You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[FEATURE]: sdk: expose the embedded server's fetch handler on PromiseSdk.Interface #54803
I have verified this feature I'm about to request hasn't been suggested before.
Describe the enhancement you want to request
PromiseSdk.create (and so OpenCodeWorkerd.create) builds an in-process HTTP handler for the embedded server, host.fetch, and only passes it to OpenCode.make:
The returned Interface has the typed client, close and plugin, but not the handler. internal/host isn't in the package's exports, so there's no supported way to reach it.
We'd like it on the interface:
exporttypeInterface=Omit<OpenCodeClient,"plugin">&{// .../** The embedded server's HTTP API, in process. */readonlyfetch: (request: Request)=>Promise<Response>}return{
...client,fetch: host.fetch,// ...}
The same field on the Effect OpenCode.Interface would keep the two in line.
Why
On workerd, OpenCode runs inside a Durable Object, and the object owns the only way in from outside: its fetch. With the handler exposed, the object can serve OpenCode's HTTP API from one of its own routes:
cloudflare/agents has an OpenCodeHarness built on @opencode/sdk/workerd. An earlier version got the handler by redefining the exported OpenCode.make from @opencode/client and catching the fetch option during create with AsyncLocalStorage. That depends on the SDK and the embedder sharing one copy of @opencode/client, and on create calling OpenCode.make with fetch set. We'd rather not ship that, so the harness has no HTTP passthrough until there's a supported field.
Related: #53577, another option we need for embedding in a Durable Object.
To restate it: you'd like PromiseSdk.create / OpenCodeWorkerd.create to return the embedded server's in-process HTTP handler (host.fetch) as a fetch field on the interface, with the same field on the Effect OpenCode.Interface. A Durable Object could then serve OpenCode's HTTP API from its own fetch, and regular clients like opencode --server ... could connect to it.
I checked dev: host.fetch is currently only passed to OpenCode.make, and internal/host isn't exported, so you're right that there's no supported way to reach it today. Maintainers will consider this.
Related:
feat(sdk): expose the embedded instance's HTTP app with password auth #53736: open PR that exposes the embedded instance's HTTP routes on the Effect SDK as opencode.http, with password auth, for serving to external clients. It uses an Effect HttpServer rather than a fetch handler, partly because a fetch handler can't accept PTY WebSocket upgrades.
I can take on the PromiseSdk/Workerd part by returning the existing host.fetch on PromiseSdk.Interface, with its exact inferred transport type. OpenCodeWorkerd already delegates to PromiseSdk.create and aliases its interface, so it would gain the same field without a second host or listener.
This would expose the same OwnedFetch transport used by the typed client, preserving request cancellation, streamed-response lifetime and rejection after close(). It would also preserve its existing Request/URL/init inputs rather than narrowing them to Request alone. The embedder would own routing and access control: the embedded host does not configure the external server's password authentication.
For a focused first change, I would keep Effect opencode.http and PTY WebSocket serving in #53736's scope. A fetch handler alone would not promise WebSocket upgrades or complete CLI compatibility. This addresses the supported HTTP passthrough requested in cloudflare/agents#2567 without the rejected OpenCode.make monkey-patch or exporting SDK internals.
After approval, I would verify that an HTTP session request through the exposed field matches the typed client's result, that Workerd exposes the same field, and that cancellation/streaming/close behavior still follows the existing transport contract. Existing typed methods, plugin registration and disposal would retain their behavior.
Could the core team approve this additive PromiseSdk/Workerd API scope? I would leave Effect API alignment to the ongoing #53736 design unless you prefer one coordinated interface change.
Activity
mattzcarey commentedon Oct 12, 2026
Tracking on our side: cloudflare/agents#2567
opencode-agent commentedon Oct 12, 2026
Thanks for the detailed request and the use case!
To restate it: you'd like
PromiseSdk.create/OpenCodeWorkerd.createto return the embedded server's in-process HTTP handler (host.fetch) as afetchfield on the interface, with the same field on the EffectOpenCode.Interface. A Durable Object could then serve OpenCode's HTTP API from its ownfetch, and regular clients likeopencode --server ...could connect to it.I checked
dev:host.fetchis currently only passed toOpenCode.make, andinternal/hostisn't exported, so you're right that there's no supported way to reach it today. Maintainers will consider this.Related:
opencode.http, withpasswordauth, for serving to external clients. It uses an EffectHttpServerrather than a fetch handler, partly because a fetch handler can't accept PTY WebSocket upgrades.Dante-dan commentedon Oct 12, 2026
I can take on the PromiseSdk/Workerd part by returning the existing
host.fetchonPromiseSdk.Interface, with its exact inferred transport type.OpenCodeWorkerdalready delegates toPromiseSdk.createand aliases its interface, so it would gain the same field without a second host or listener.This would expose the same
OwnedFetchtransport used by the typed client, preserving request cancellation, streamed-response lifetime and rejection afterclose(). It would also preserve its existing Request/URL/init inputs rather than narrowing them to Request alone. The embedder would own routing and access control: the embedded host does not configure the external server's password authentication.For a focused first change, I would keep Effect
opencode.httpand PTY WebSocket serving in #53736's scope. A fetch handler alone would not promise WebSocket upgrades or complete CLI compatibility. This addresses the supported HTTP passthrough requested in cloudflare/agents#2567 without the rejectedOpenCode.makemonkey-patch or exporting SDK internals.After approval, I would verify that an HTTP session request through the exposed field matches the typed client's result, that Workerd exposes the same field, and that cancellation/streaming/close behavior still follows the existing transport contract. Existing typed methods, plugin registration and disposal would retain their behavior.
Could the core team approve this additive PromiseSdk/Workerd API scope? I would leave Effect API alignment to the ongoing #53736 design unless you prefer one coordinated interface change.