Summary
Track the agreed CodeMode direction from the Kit + Aiden design grill.
Source Discord thread: https://discord.com/channels/1391832426048651334/1521953032408141944
Related PR context: #34677
Locked decisions
- Split the generic CodeMode runtime into its own package, following the current experiments package structure/export style.
- Use CodeMode/tool naming:
CodeMode, Tool, ToolError, UnknownTool, $codemode.
- Keep OpenCode-specific MCP integration in OpenCode, not in the generic runtime.
- MCP/user tools are adapted into ordinary CodeMode
Tool.make(...) definitions.
- CodeMode owns generic
$codemode.search over the final tool tree.
- Search only; no separate
describe.
$codemode.search({ query?, path?, limit? }) returns an array of full entries: path, description, signature, input, output.
- Default search limit is about 10; exact
path lookup returns the matching tool entry.
- Search result signatures use native payloads:
Promise<Issue>, not Promise<Result<Issue>>.
- Tools without output schema render output as
unknown.
- Tool metadata should support Effect Schema and JSON Schema now, with room for Standard Schema later.
- Effect schema validation is first-class in v1; JSON Schema / Standard Schema can initially be render-only unless an adapter validates.
- Tool implementations are Effect-based for v1; promise normalization can come later for plugin ergonomics.
- Existing core OpenCode tools stay outside CodeMode for v1; focus CodeMode on MCP/user/deferred tools.
- Permissions stay in OpenCode adapters/tools. CodeMode runtime stays permission-dumb.
- Namespace collisions are not over-engineered: the final tool tree can use normal override behavior for now.
- Tool-call progress uses simple hooks, e.g.
onToolCallStart / onToolCallEnd; no CurrentToolCall context for v1.
- Public limit knobs should be simple:
timeoutMs, maxToolCalls, maxOutputBytes; derive internal guardrails.
- Keep the hand-rolled interpreter; do not embed QuickJS/V8 for v1.
- Add pure JS built-ins:
Date, RegExp, Map, Set, and more Promise helpers.
- Keep ambient authority out: no
fs, child processes, network/fetch, process/env, or timers for v1.
- Port forgiving JS semantics: missing property reads return
undefined, typeof undeclared works, null/undefined object spread is a no-op, builtin callbacks like filter(Boolean) work, and NaN/Infinity can exist inside the sandbox while boundary handling stays safe.
- Support
console.* capture and append logs to the model-facing result; prompt should still prefer return for intended output.
- Do not include
output.text/file/image in OpenCode v1.
- Attachments/files/images emitted by child tools are host-collected outside CodeMode, never base64-inlined into the program value.
- Tool calls return their native structured payload to CodeMode; emitted attachments are accumulated by the outer OpenCode layer and returned as normal tool-result attachments with existing size/count handling.
- Error reporting should use clear categories: parse errors, unsupported syntax, invalid tool input, unknown tool, tool failure, invalid tool output, execution failure, timeout, and operation limits. Messages should be specific when safe and avoid leaking host internals/secrets.
Suggested implementation order
- Create the standalone CodeMode runtime package from the experiments structure.
- Port the locked interpreter/runtime changes.
- Implement generic
$codemode.search with the simple array/full-entry shape.
- Add the OpenCode MCP adapter that produces
Tool.make(...) definitions.
- Wire host-side attachment accumulation.
- Wire the OpenCode
execute tool and simple progress hooks.
- Leave existing core OpenCode tools outside CodeMode for v1.
Summary
Track the agreed CodeMode direction from the Kit + Aiden design grill.
Source Discord thread: https://discord.com/channels/1391832426048651334/1521953032408141944
Related PR context: #34677
Locked decisions
CodeMode,Tool,ToolError,UnknownTool,$codemode.Tool.make(...)definitions.$codemode.searchover the final tool tree.describe.$codemode.search({ query?, path?, limit? })returns an array of full entries:path,description,signature,input,output.pathlookup returns the matching tool entry.Promise<Issue>, notPromise<Result<Issue>>.unknown.onToolCallStart/onToolCallEnd; noCurrentToolCallcontext for v1.timeoutMs,maxToolCalls,maxOutputBytes; derive internal guardrails.Date,RegExp,Map,Set, and more Promise helpers.fs, child processes, network/fetch, process/env, or timers for v1.undefined,typeof undeclaredworks, null/undefined object spread is a no-op, builtin callbacks likefilter(Boolean)work, andNaN/Infinitycan exist inside the sandbox while boundary handling stays safe.console.*capture and append logs to the model-facing result; prompt should still preferreturnfor intended output.output.text/file/imagein OpenCode v1.Suggested implementation order
$codemode.searchwith the simple array/full-entry shape.Tool.make(...)definitions.executetool and simple progress hooks.