feat(graph): codemode — a node whose action space is code, not tool calls - #998
Merged
Conversation
…alls A `codemode` node asks the model once for a program written against the operations it grants, then runs it: one model turn instead of one per step. The example measures it at 1 turn against 8 for the same job and the same answer. The three properties a prompt-and-skill version cannot have are the point. The API shown to the model is generated from the same table the runner binds. The host owns where code runs, through a declared `codeRunner` effect the engine refuses to proceed without. And spend from every operation totals into the settlement the kernel journals, which is what stops authored code spending outside the budget. `assertAuthoredCode` generalizes strategy-author.ts's contract lint rather than duplicating it. It is a lint, not a sandbox, and says so. Version 0.169.0.
tangletools
approved these changes
Aug 22, 2026
tangletools
left a comment
Contributor
There was a problem hiding this comment.
✅ Auto-approved drewstone PR — bfac142c
This PR was opened by the trusted drewstone account.
This approval is provisional and was applied by the local stand-in because the pr-reviewer webhook host is unreachable (2026-08-21). CI on this head is fully green. The full PR reviewer audit re-runs via the resweep when the service returns and will publish findings if it detects issues.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Answers "can an agent easily do code mode on agent-runtime, or is it a profile + skill?" — with the short answer being no, and here is why, and here is the piece that was missing.
What code mode is
An
agentnode acts one JSON tool call per turn: N steps cost N model round trips, and every tool's schema occupies context. Code mode changes the action space — the model writes a program against a typed API of the tools it is allowed to use, and the runtime runs it. Loops, branches and fan-out happen inside one turn; intermediates live in program memory instead of the transcript.examples/engine/codemode.tsruns one job both ways over the same operations table:What agent-runtime already had
One instance, hard-wired.
src/runtime/strategy-author.tshas an LLM read a contract, write an optimization strategy as a TS module, lints it against banned constructs, writes it to disk, imports it and runs it. It is safe for a reason worth naming: the authored body composesshot()/critique(), so everything it spends goes through the Supervisor's conserved pool. Code that calls tools outside the seams makes the budget and the journal lie — that is the actual hazard, not the code execution.Why a profile + skill cannot get you there
A skill can describe an API. Three things must be runtime-owned, and
codemodeKind()owns them:renderCodeApigenerates what the model is shown from the sameoperationstable the runner binds. A documented-but-ungranted call cannot exist; a granted-but-undocumented one cannot hide. Prose drifts from the grant; generated text cannot.codeRunnereffect and executes nothing itself — the engine refuses before spending when no runner was supplied (tested). A host picks in-process or jailed, and the graph is identical either way.inlineCodeRunner()is offered for development and labelled honestly:assertAuthoredCodeis a lint, not a sandbox, and in-process code reaches whatever the process reaches.ExecutorunderScope.spawn, so reservation, settlement, journal record, completion gate and replay are the kernel's. Each operation reports its spend and the node totals it. The test asserts this off the journal, not the return value: 100 input tokens from the one model call plus 1 from each of three metered operation calls arrive as103in the kernel'ssettledrecord.Where it sits
codemodeis a sibling ofscriptin the same registry:scriptis developer-authored code as a node,codemodeis model-authored code as a node. Both inherit the executor contract, the conserved pool, the journal,onCrashand the deliverable gate for free — which is the argument for it being a node kind rather than a new subsystem.assertAuthoredCode(code, { allowedImports })isstrategy-author.ts's contract check, generalized and exported rather than duplicated: same banned constructs, with the allowed-import list a parameter instead of one hardcoded specifier.Verification
pnpm tsx examples/engine/codemode.tsoutput above ·tests/graph/codemode.test.ts7/7 (grant projection, one-turn loop, journal-backed spend, pre-execution refusal offetchwith zero operation calls made, missing-runner refusal, config refusals, lint behaviour) · full affected suites 1119 passed, 1 failed (the127.0.0.53bind, macOS baseline) · typecheck 0 · lint clean · docs:api 0/0 · docs:freshness green · 0.169.0.Not done, deliberately
No
codemodeexecutor for plainsupervise()leaves, and no MCP-tools-to-operations projection. Both are real and both are speculative until something asks for them; the node kind is the smallest thing that proves the shape end to end.