Plan for replacing instance Hono route implementations with Effect HttpApi while preserving behavior, OpenAPI, and SDK output during the transition.
src/server/routes/instance/httpapi/*.HttpApiGroup, schemas, handlers, and route-level middleware.httpapi/server.ts only composes groups, instance lookup, observability, and the web handler bridge.HttpApi replacements are default, tested, and represented in the SDK/OpenAPI pipeline.HttpApi if HttpApi is the wrong abstraction.OPENCODE_EXPERIMENTAL_HTTPAPI selects the backend at server startup. Default is still hono.server/backend.ts picks one of effect-httpapi or hono; server.ts builds either a pure Effect HttpApi web handler or the legacy Hono app accordingly. The earlier in-Hono "bridge" model has been replaced by this fork-at-startup.hono backend and remain the source for hono-openapi SDK generation.HttpApi OpenAPI surface exists (OpenApi.fromApi(PublicApi) in cli/cmd/generate.ts --httpapi, OPENCODE_SDK_OPENAPI=httpapi in packages/sdk/js/script/build.ts) but is opt-in. The default SDK generation is still Hono.httpapi/public.ts carries the Hono-compat normalization for the Effect-generated OpenAPI surface (auth scheme strip, request-body required flag, optional null arms, BadRequestError / NotFoundError remap, $ref self-cycle fix, auth_token query injection). Today's Effect-generated SDK is not byte-identical to the Hono-generated SDK — see Phase 4.Config (refactor: use Effect config for HttpApi authorization, Fix HttpApi raw route authorization) rather than re-attached in each route module.auth_token query parameter through HttpApiSecurity.apiKey.httpapi/server.ts using directory, workspace, and x-opencode-directory.Observability.layer is provided in the Effect route layer and deduplicated through the shared memoMap.feat(httpapi): add CORS middleware to instance routes)..zod only as compatibility for remaining Hono/OpenAPI surfaces.describeRoute entry.Use this checklist for each small HttpApi migration PR:
HttpApiGroup, route paths, DTO schemas, and handlers in src/server/routes/instance/httpapi/*.src/server/routes/instance/index.ts only inside the OPENCODE_EXPERIMENTAL_HTTPAPI block.InstanceState.context / InstanceState.directory inside HttpApi handlers instead of Instance.directory, Instance.worktree, or Instance.project ALS globals.Schema.Struct; use Schema.Class only when handlers return actual class instances..zod compatibility in place for SDK/OpenAPI generation.InstanceRoutes, not only the raw HttpApi web handler, when the route depends on auth or instance context.bun typecheck from packages/opencode, relevant bun run test:ci ... tests from packages/opencode, and ./packages/sdk/js/script/build.ts from the repo root.Use this checklist before deleting any Hono route implementation. A route being bridged is not enough.
HttpApi parity is complete for the route path, method, auth behavior, query parameters, request body, response status, response headers, and error status.OPENCODE_EXPERIMENTAL_HTTPAPI.HttpApi path and the fallback Hono path until the fallback is removed.HttpApi route as the source for that path.describeRoute, validator, and handler for that path are removed..zod compatibility on the canonical Effect Schema.HttpApi if raw Effect HTTP is a better fit.Hono can be removed from the instance server only after all mounted Hono route groups meet this checklist and server/routes/instance/index.ts no longer depends on Hono routing for default behavior.
For the experimental route group, port read-only JSON routes before mutations:
GET /console, GET /console/orgs, GET /tool/ids, GET /resource.GET /worktree only if the handler uses InstanceState.context instead of Instance.project.POST /console/switch, worktree create/remove/reset, and GET /session to separate PRs because they mutate state or have broader pagination/session behavior.Schema.Struct(...).annotate({ identifier }) for named OpenAPI refs when handlers return plain objects.Schema.Class only when the handler returns real class instances or the constructor requirement is intentional.Schema.Struct unless a named SDK type is useful.Before porting more routes, cover the bridge behavior that every route depends on.
HttpApi bridge, not just HttpApiBuilder.layer directly.auth_token success, missing credentials, and bad credentials.directory and x-opencode-directory instance selection.HttpApi groups.Create a route inventory from the actual Hono registrations and classify each route.
Statuses:
bridged: served through the HttpApi bridge when the flag is on.implemented: HttpApi group exists but is not mounted through Hono.next: good JSON candidate for near-term porting.later: portable, but needs schema/service cleanup first.special: SSE, websocket, streaming, or UI bridge behavior that likely needs raw Effect HTTP rather than HttpApi.Port remaining JSON routes in small batches.
Good near-term candidates:
GET /path, GET /vcs, GET /vcs/diff, GET /command, GET /agent, GET /skill, GET /lsp, GET /formatterPOST /instance/disposeKeep large or stateful groups for later:
sessionsyncHono routes cannot be deleted while hono-openapi is the source of SDK generation.
Status: the Effect HttpApi OpenAPI surface is implemented and opt-in (bun dev generate --httpapi, OPENCODE_SDK_OPENAPI=httpapi). Default SDK generation still uses Hono. httpapi/public.ts applies the Hono-compat normalization layer to the Effect output. Diff against the Hono-generated spec still shows real gaps that must be closed before the SDK can flip:
pattern constraints on ID schemas are not propagated to the Effect output (~169 missing).description annotations are not propagated through Schema.Struct to the Effect output (~107 missing).Event.* and SyncEvent.* component names use dotted form in Hono and PascalCase in Effect (~50 differences, breaks SDK type names).Session9, SyncEvent.session.updated.11) that need a name-collision fix.additionalProperties: false, const vs enum, MAX_SAFE_INTEGER maximum, propertyNames) can be normalized in public.ts if they would otherwise change SDK output.Required before route deletion:
packages/sdk/js/script/build.ts default to httpapi and regenerate.dev for every route group deletion.V2 cleanup once SDK compatibility no longer needs the legacy Hono contract:
public.ts compatibility transforms that hide honest HttpApi metadata, including auth securitySchemes, per-route security, and generated 401 responses.HttpApi error schemas back to legacy Hono BadRequestError / NotFoundError components if V2 clients can consume the actual Effect error shape.HttpApi OpenAPI output for request/response bodies and named component schemas instead of rewriting it to match Hono generator quirks.auth_token as an OpenAPI security scheme rather than a hand-injected query parameter once clients can consume the V2 spec.After JSON parity and SDK generation are covered:
Delete Hono routes group-by-group after each group meets the deletion criteria.
Deletion criteria:
HttpApi route is mounted by default.After deleting a group:
.route(...) registration from instance/index.ts.Special routes need explicit designs before Hono can disappear completely.
event: SSEpty: websockettui: UI/control bridge behaviorsession endpointsUse raw Effect HTTP routes where HttpApi does not fit. The goal is deleting Hono implementations, not forcing every transport shape through HttpApi.
| Area | Status | Notes |
|---|---|---|
question |
bridged |
GET /question, reply, reject |
permission |
bridged |
list and reply |
provider |
bridged |
list, auth, OAuth authorize/callback |
config |
bridged |
read, providers, update |
project |
bridged |
list, current, git init, update |
file |
bridged partial |
find text/file/symbol, list/content/status |
mcp |
bridged |
status, add, OAuth, connect/disconnect |
workspace |
bridged |
adapter/list/status/create/remove/session-restore |
| top-level instance routes | bridged |
path, vcs, command, agent, skill, lsp, formatter, dispose |
| experimental JSON routes | bridged |
console, tool, worktree list/mutations, global session list, resource list |
session |
bridged |
read, lifecycle, prompt, message/part mutations, revert, permission reply |
sync |
bridged |
start/replay/history |
event |
bridged |
SSE via raw Effect HTTP |
pty |
special |
websocket |
tui |
special |
UI bridge |
This checklist tracks bridge parity only. Checked routes are available through the experimental HttpApi bridge; Hono deletion is tracked separately by the deletion checklist above.
POST /instance/dispose - dispose active instance after response.GET /path - current directory and worktree paths.GET /vcs - current VCS status.GET /vcs/diff - VCS diff summary.GET /command - command catalog.GET /agent - agent catalog.GET /skill - skill catalog.GET /lsp - LSP status.GET /formatter - formatter status.GET /config - read config.PATCH /config - update config and dispose active instance after response.GET /config/providers - config provider summary.GET /project - list projects.GET /project/current - current project.POST /project/git/init - initialize git and reload active instance after response.PATCH /project/:projectID - update project metadata.GET /provider - list providers.GET /provider/auth - list provider auth methods.POST /provider/:providerID/oauth/authorize - start provider OAuth.POST /provider/:providerID/oauth/callback - finish provider OAuth.GET /question - list questions.POST /question/:requestID/reply - reply to question.POST /question/:requestID/reject - reject question.GET /permission - list permission requests.POST /permission/:requestID/reply - reply to permission request.GET /find - text search.GET /find/file - file search.GET /find/symbol - symbol search.GET /file - list directory entries.GET /file/content - read file content.GET /file/status - file status.GET /mcp - MCP status.POST /mcp - add MCP server at runtime.POST /mcp/:name/auth - start MCP OAuth.POST /mcp/:name/auth/callback - finish MCP OAuth callback.POST /mcp/:name/auth/authenticate - run MCP OAuth authenticate flow.DELETE /mcp/:name/auth - remove MCP OAuth credentials.POST /mcp/:name/connect - connect MCP server.POST /mcp/:name/disconnect - disconnect MCP server.GET /experimental/console - active Console provider metadata.GET /experimental/console/orgs - switchable Console orgs.POST /experimental/console/switch - switch active Console org.GET /experimental/tool/ids - tool IDs.GET /experimental/tool - tools for provider/model.GET /experimental/worktree - list worktrees.POST /experimental/worktree - create worktree.DELETE /experimental/worktree - remove worktree.POST /experimental/worktree/reset - reset worktree.GET /experimental/session - global session list.GET /experimental/resource - MCP resources.GET /experimental/workspace/adapter - list workspace adapters.POST /experimental/workspace - create workspace.GET /experimental/workspace - list workspaces.GET /experimental/workspace/status - workspace status.DELETE /experimental/workspace/:id - remove workspace.POST /experimental/workspace/:id/session-restore - restore session into workspace.POST /sync/start - start workspace sync.POST /sync/replay - replay sync events.POST /sync/history - list sync event history.GET /session - list sessions.GET /session/status - session status map.GET /session/:sessionID - get session.GET /session/:sessionID/children - get child sessions.GET /session/:sessionID/todo - get session todos.POST /session - create session.DELETE /session/:sessionID - delete session.PATCH /session/:sessionID - update session metadata.POST /session/:sessionID/init - run project init command.POST /session/:sessionID/fork - fork session.POST /session/:sessionID/abort - abort session.POST /session/:sessionID/share - share session.GET /session/:sessionID/diff - session diff.DELETE /session/:sessionID/share - unshare session.POST /session/:sessionID/summarize - summarize session.GET /session/:sessionID/message - list session messages.GET /session/:sessionID/message/:messageID - get message.DELETE /session/:sessionID/message/:messageID - delete message.DELETE /session/:sessionID/message/:messageID/part/:partID - delete part.PATCH /session/:sessionID/message/:messageID/part/:partID - update part.POST /session/:sessionID/message - prompt with streaming response.POST /session/:sessionID/prompt_async - async prompt.POST /session/:sessionID/command - run command.POST /session/:sessionID/shell - run shell command.POST /session/:sessionID/revert - revert message.POST /session/:sessionID/unrevert - restore reverted messages.POST /session/:sessionID/permissions/:permissionID - deprecated permission response route.GET /event - SSE event stream via raw Effect HTTP.GET /pty - list PTY sessions.POST /pty - create PTY session.GET /pty/:ptyID - get PTY session.PUT /pty/:ptyID - update PTY session.DELETE /pty/:ptyID - remove PTY session.GET /pty/:ptyID/connect - PTY websocket; replace with raw Effect HTTP/websocket support.POST /tui/append-prompt - append prompt.POST /tui/open-help - open help.POST /tui/open-sessions - open sessions.POST /tui/open-themes - open themes.POST /tui/open-models - open models.POST /tui/submit-prompt - submit prompt.POST /tui/clear-prompt - clear prompt.POST /tui/execute-command - execute command.POST /tui/show-toast - show toast.POST /tui/publish - publish TUI event.POST /tui/select-session - select session.GET /tui/control/next - get next TUI request.POST /tui/control/response - submit TUI control response.Prefer smaller PRs from here so route behavior and SDK/OpenAPI fallout stays reviewable.
PATCH /project/:projectID.httpapi/event.ts; the Hono backend still uses hono/streaming streamSSE. Either port Hono /event to raw Effect HTTP for the fallback window, or skip and delete it together with Hono in step 15.pty.ts remains in the Hono backend.tui.ts remains in the Hono backend.--httpapi / OPENCODE_SDK_OPENAPI=httpapi. Close the schema-shape gaps in public.ts (branded pattern, per-property description, Event.* / SyncEvent.* naming, dedup collisions), then flip packages/sdk/js/script/build.ts default.backend.ts default from hono to effect-httpapi, keep OPENCODE_EXPERIMENTAL_HTTPAPI (or its inverse) as a short fallback flag, then delete replaced Hono route files.HttpApi JSON route slices.HttpApi routes behind OPENCODE_EXPERIMENTAL_HTTPAPI. (Now backend-fork-at-startup rather than in-Hono path mounting.)Config for the Effect backend.auth_token as a query security scheme.HttpApi OpenAPI generation behind --httpapi / OPENCODE_SDK_OPENAPI=httpapi.hono to effect-httpapi, with a short fallback flag.