| # CodeMode Design and Status |
|
|
| This is the living design and status document for `@opencode-ai/codemode` and its existing V2 OpenCode adapter. |
| It records current behavior, intentional boundaries, durable rationale, and material remaining work. |
|
|
| Completed implementation history, branch names, test counts, and closed findings belong in git, not here. Remove |
| completed work instead of preserving checked-off chronology. |
|
|
| Detailed package API documentation lives in [README.md](./README.md). OpenAPI-specific follow-ups live in |
| [src/openapi/TODO.md](./src/openapi/TODO.md). |
|
|
| ## How CodeMode Works |
|
|
| ### Purpose |
|
|
| CodeMode gives a model one `execute` tool backed by a confined JavaScript interpreter. Inside the program, the model |
| can call an explicit tree of schema-described tools, sequence dependent work, run independent calls concurrently, |
| and filter or aggregate results before returning them to the agent loop. |
|
|
| The goals are: |
|
|
| - Reduce model context consumed by large tool catalogs. |
| - Avoid an agent round-trip between every dependent tool call. |
| - Keep large intermediate results inside the program instead of sending them through model context. |
| - Give generated code only the authority explicitly supplied by the host. |
|
|
| CodeMode is an orchestration language, not a general JavaScript runtime or an application authorization system. |
|
|
| ### Runtime |
|
|
| The generic runtime lives in `packages/codemode` and is host-neutral: |
|
|
| 1. The host builds a tree of `Tool.make(...)` definitions and calls `CodeMode.make(...)` or `CodeMode.execute(...)`. |
| 2. CodeMode generates model instructions, a budgeted inline catalog, and the internal `$codemode.search` tool. |
| 3. TypeScript syntax is transpiled away, Acorn parses the resulting JavaScript, and an owned tree-walking interpreter |
| executes it without `eval`. |
| 4. Tool inputs and outputs cross schema and plain-data boundaries before they become visible on either side. |
| 5. Execution returns `CodeMode.Result`. Expected program and tool failures are diagnostic data; host interruption |
| remains Effect interruption. |
|
|
| Effect Schemas validate and transform tool inputs and outputs. JSON Schemas render model-facing signatures but do not |
| validate values; adapter-provided values still cross the plain-data boundary. A tool without an output schema is |
| advertised as `Promise<unknown>`. |
|
|
| ### Discovery and model workflow |
|
|
| The model sees a token-budgeted catalog. Every namespace remains visible, and complete signatures are selected |
| round-robin across namespaces so one large namespace cannot starve the others. `$codemode.search` is always callable |
| and is advertised when the inline catalog is partial. |
|
|
| The intended workflow is: |
|
|
| 1. Pick an exact signature from the inline catalog, or return `$codemode.search(...)` results and use a selected path |
| in the next execution. |
| 2. Call the exact returned path without guessing or normalizing segments. |
| 3. Narrow `Promise<unknown>` results before reading fields. |
| 4. Start independent calls together and await them with `Promise.all`. |
| 5. Filter and aggregate inside the program, then return only the data needed by the model. |
|
|
| Search returns directly usable JavaScript paths, descriptions, and complete TypeScript signatures. It supports exact |
| path lookup, namespace browsing, deterministic ranking, and pagination. |
|
|
| ### Tool execution |
|
|
| Calling a tool starts its Effect eagerly on a supervised fiber. The returned sandbox promise is run-once and can be |
| awaited directly or through the supported `Promise` combinators. At most eight tool calls execute concurrently. |
| Unfinished calls are drained before successful program completion, and an unhandled call failure becomes a diagnostic. |
|
|
| The public execution-policy knobs are `timeoutMs`, `maxToolCalls`, and `maxOutputBytes`. The package supplies no |
| defaults because budgets are host policy. The interpreter also enforces fixed internal boundaries for tool-call |
| concurrency and data nesting depth. |
|
|
| ### Data, files, and failures |
|
|
| Program results and tool arguments are JSON-like data. Dates become ISO strings at host boundaries; RegExp, Map, and |
| Set values become `{}` as they do under JSON serialization. Promise and runtime reference values cannot cross the |
| boundary. |
|
|
| Unknown host failures and invalid outputs are sanitized. `ToolError` is the explicit channel for a safe message that a |
| tool wants the model to see. Diagnostic categories distinguish parsing, unsupported syntax, unknown tools, invalid |
| data, tool failures, limits, timeouts, and execution failures. |
|
|
| Files and other attachment content stay outside the interpreter. A host may collect them while child tools execute and |
| attach them to the outer result, but the program receives only the structured tool output. |
|
|
| ### V2 OpenCode adapter |
|
|
| This section describes the `v2` branch integration. On `dev`, CodeMode is integrated through |
| `packages/opencode/src/tool/code-mode.ts`, where nested MCP calls run the `tool.execute.before` and |
| `tool.execute.after` plugin hooks. |
|
|
| CodeMode is integrated into V2 through `packages/core/src/tool/registry.ts` and |
| `packages/core/src/tool/execute.ts`: |
|
|
| - Core has one canonical `Tool` representation. Location-scoped producers register direct or deferred tools through |
| `Tools.Service`. |
| - Each model step snapshots effective registrations, applies catalog visibility filtering, and exposes direct tools |
| normally. |
| - When visible deferred tools exist, Core reserves and materializes one `execute` tool. Grouped deferred tools become |
| CodeMode namespaces instead of flattened model-facing names. |
| - Each nested call checks that its captured registration is still current before dispatching it. |
| - Authorization and side-effect ordering remain responsibilities of the leaf tool. Catalog visibility is not execution |
| authorization. |
| - Structured child output enters the interpreter. File parts are collected host-side and attached to the outer result. |
| - Nested call statuses are returned as final `execute` metadata for the TUI. |
| - `execute` is the one model-facing tool invocation. Nested calls reuse its invocation context and do not independently |
| run registry hooks or model-output bounding; this keeps complete intermediate structured values available for |
| in-program filtering. The outer `execute` settlement is the single model-output bounding boundary. |
| - Core supplies no CodeMode timeout or tool-call limit. User cancellation interrupts the outer invocation and its |
| supervised children; the outer settlement applies Core's normal output-retention policy. |
|
|
| MCP tools use this canonical path: they register as grouped tools and are deferred while CodeMode is enabled. Existing |
| output schemas are preserved in generated signatures. Direct Core tools remain direct and are not ambient globals |
| inside CodeMode. |
|
|
| ## Intentionally Unsupported |
|
|
| These are product boundaries rather than DSL backlog: |
|
|
| - Ambient filesystem, process, environment, network, credential, or application access. External work must go through |
| supplied tools. |
| - Modules, imports, dynamic imports, `eval`, arbitrary host globals, npm packages, and prototype mutation. |
| - Generic permission prompts, authorization policy, durable pause/resume, replay, storage, or exactly-once external |
| side effects. Hosts and tools own those concerns. |
| - Heuristic parsing of text tool results as JSON. A result should not silently change type based on its contents. |
|
|
| The OpenAPI adapter may gain more transports and encodings, but it must continue skipping operations it cannot |
| represent accurately rather than guessing semantics. |
|
|
| ## Decisions and Rationale |
|
|
| | Decision | Rationale | |
| | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
| | Keep an owned tree-walking interpreter. | The product need is bounded tool orchestration, not arbitrary JavaScript. Owning the language surface keeps authority and behavior explicit. | |
| | Treat schemas as the model-facing interface. | Signatures drive correct calls; Effect Schema also provides the runtime validation boundary, while JSON Schema supports adapter interoperability. | |
| | Keep authority host-owned. | CodeMode can only confine programs to supplied tools. The host chooses those tools, and each tool enforces its own authorization and side-effect policy. | |
| | Use progressive catalog disclosure plus search. | Large tool sets should not consume the prompt, but every namespace must remain discoverable and speculative search calls should remain valid. | |
| | Start tool promises eagerly and supervise them. | This preserves normal call-time parallelism while giving each call run-once settlement and interruption safety. | |
| | Keep files outside the sandbox value space. | Models should compose structured data without routing binary payloads through generated code or context. | |
| | Treat `execute` as the model-facing invocation boundary. | Nested calls are implementation details of one orchestration program. Reusing the outer context and bounding only the final result preserves complete intermediate data without inventing durable child-call identities. | |
| | Return expected failures as data. | Models need actionable diagnostics without exposing private host causes; host interruption and defects must still propagate correctly. | |
| | Leave execution-limit defaults to hosts. | Appropriate budgets depend on the surrounding product and its own cancellation, retention, and output-bounding policies. | |
| | Skip unsupported OpenAPI operations. | Incorrect parameter encoding, authentication, or transport behavior is worse than a precise `skipped` reason. | |
|
|
| ## Remaining Work |
|
|
| Keep only material unresolved work here. Small isolated defects should be GitHub issues; adapter-only work belongs in |
| the adapter TODO. Delete entries when completed. |
|
|
| ### DSL expansion |
|
|
| The supported JavaScript subset should grow when common model-generated code improves tool orchestration. These are |
| current omissions to implement, not intentional product boundaries. |
|
|
| - [ ] Design proper multi-stage promise pipelines. Supporting `.then`, `.catch`, and `.finally` should preserve promise |
| assimilation, cancellation, failure handling, and concurrent per-item pipelines rather than adding syntax-only |
| shims. Consider `Promise.any` in the same pass. |
| - [ ] Support async iteration and `for await...of`. Define behavior first for the runtime's supported promise and |
| collection values, then extend it to bounded host streams when a stream boundary exists. |
| - [ ] Support callback-bearing standard-library variants that models commonly generate: the mapper argument to |
| `Array.from(...)` and replacers for `JSON.stringify(...)`, including Effect-aware callbacks where needed. |
| - [ ] Close basic `Object` parity gaps: let `Object.values`/`Object.entries` accept arrays, make `Object.assign` validate |
| and mutate its target, add `Object.is`, and let `Object.fromEntries` consume every supported iterable. |
| - [ ] Add deterministic modern collection conveniences where they improve orchestration: `Object.groupBy`, Set |
| composition methods, and `Array.prototype.toSpliced`. |
| - [ ] Complete the deterministic `Math` surface beyond the current arithmetic, rounding, root, power, and logarithm |
| helpers. Decide separately whether nondeterministic `Math.random` belongs in the runtime. |
| - [ ] Refine diagnostics so user throws, expected tool failures, unexpected host/tool defects, and genuine interpreter |
| defects are distinguishable without leaking private causes. |
| |
| ### Tool and result contracts |
|
|
| - [ ] Design explicit tagged representations and size rules before allowing Blob, File, ArrayBuffer, typed arrays, or |
| host streams to cross the sandbox boundary. |
| - [ ] Define one consistent policy for tool path segments named `__proto__`, `constructor`, or `prototype`. They must |
| either be safely callable, rejected before catalog generation, or use one documented escaping rule. |
| |