@@ -178,6 +178,46 @@ The action executes with the same privileges tool mode already grants:
178178- CLI: ` nodetool agent run <yaml> --codeact ` ; the agent YAML also takes
179179 ` execution_mode: codeact ` . Flag > YAML > setting.
180180
181+ ## Chat turns (websocket runner)
182+
183+ The chat websocket runner honors the same setting: when ` resolveExecutionMode() `
184+ says ` codeact ` , a plain chat turn presents ` execute_code ` (plus ` view_image ` ,
185+ the one channel that puts pixels into context and so cannot ride the JSON
186+ observation envelope) instead of the toolbelt, and the ToolSearch deferral
187+ machinery is replaced by the in-sandbox ` searchTools() ` . The adapter is
188+ ` createChatCodeActSession ` (` packages/agents/src/codeact/chat-codeact.ts ` ): a
189+ chat toolbelt mixes server tools with client (` ui_* ` ) tools that exist
190+ server-side only as schemas, so instead of ` buildToolBridge ` the session
191+ bridges ` tools.<name>() ` to the chat runner's own ` executeTool ` router —
192+ permission gating, client round-trips over the ToolBridge, and asset
193+ materialization all stay where they are. ` state ` persists across the turn's
194+ actions; there is no ` finish() ` — a plain assistant message ends the turn, and
195+ the prompt says so (` variant: "chat" ` of ` buildCodeActSystemPrompt ` ).
196+
197+ ## Workflow graph editing: the JS object model
198+
199+ When the belt carries the ` ui_* ` workflow document tools, code actions get an
200+ object model instead of one bridged call per mutation
201+ (` packages/agents/src/codeact/graph-model.ts ` ):
202+
203+ ``` js
204+ const wf = await openWorkflow (workflowId);
205+ const input = wf .addNode (" in1" , " nodetool.input.StringInput" , { name: " prompt" });
206+ wf .addNode (" llm1" , " nodetool.agents.Agent" , {}, { x: 400 , y: 120 });
207+ wf .connect (" in1" , " output" , " llm1" , " prompt" );
208+ wf .node (" llm1" ).setTitle (" Draft" ).set ({ system: " be brief" });
209+ await wf .commit ();
210+ ```
211+
212+ Mutators are synchronous: they update a local mirror (` wf.nodes ` , ` wf.edges ` ,
213+ ` wf.node(id) ` ) and queue the equivalent ` ui_* ` operation. ` commit() ` replays
214+ the queue through the bridged tools — the same contract the renderer and the
215+ headless document tools implement, so routing, validation, and live-editor
216+ sync are untouched. A failed commit names the failing operation and keeps it
217+ (and everything after it) queued for a retry; removing a never-committed node
218+ cancels its queued ops instead of issuing a delete. Tests:
219+ ` packages/agents/tests/chat-codeact.test.ts ` .
220+
181221## Evaluation
182222
183223` eval codeact ` (registered next to ` subtask ` ): objectives with instrumented
@@ -203,5 +243,3 @@ finalization — no network, no model.
203243 action space, not about Python specifically.
204244- Replacing planners. GraphPlanner/ScriptPlanner/CodePlanner already use
205245 code-shaped * artifacts* ; this changes the step execution loop only.
206- - The chat/websocket toolbelt. Chat turns keep tool mode; wiring codeact into
207- the websocket runner is a follow-up once step-level evals justify it.
0 commit comments