Skip to content

Dispatch tools when code mode is not awaited directly#14437

Merged
pakrym-oai merged 5 commits intomainfrom
pakrym/code-mode-worker-dispatch
Mar 12, 2026
Merged

Dispatch tools when code mode is not awaited directly#14437
pakrym-oai merged 5 commits intomainfrom
pakrym/code-mode-worker-dispatch

Conversation

@pakrym-oai
Copy link
Collaborator

Summary

  • start a code mode worker once per turn and let it pump nested tool calls through a dedicated queue
  • simplify code mode request/response dispatch around request ids and generic runner-unavailable errors
  • clean up the code mode process API and runner protocol plumbing

Testing

  • not run yet

@pakrym-oai pakrym-oai force-pushed the pakrym/code-mode-worker-dispatch branch from c472815 to 9c4ffb8 Compare March 12, 2026 06:17
@pakrym-oai pakrym-oai changed the title Refactor code mode worker dispatch Dispatch tools when code mode is not awaited directly Mar 12, 2026
section.push_str("- Import nested tools from `tools.js`, for example `import { exec_command } from \"tools.js\"` or `import { ALL_TOOLS } from \"tools.js\"` to inspect the available `{ module, name, description }` entries. Namespaced tools are also available from `tools/<namespace...>.js`; MCP tools use `tools/mcp/<server>.js`, for example `import { append_notebook_logs_chart } from \"tools/mcp/ologs.js\"`. Nested tool calls resolve to their code-mode result values.\n");
section.push_str(&format!(
"- Import `{{ output_text, output_image, set_max_output_tokens_per_exec_call, set_yield_time, store, load }}` from `@openai/code_mode` (or `\"openai/code_mode\"`). `output_text(value)` surfaces text back to the model and stringifies non-string objects with `JSON.stringify(...)` when possible. `output_image(imageUrl)` appends an `input_image` content item for `http(s)` or `data:` URLs. `store(key, value)` persists JSON-serializable values across `{PUBLIC_TOOL_NAME}` calls in the current session, and `load(key)` returns a cloned stored value or `undefined`. `set_max_output_tokens_per_exec_call(value)` sets the token budget used to truncate direct `{PUBLIC_TOOL_NAME}` returns; `{WAIT_TOOL_NAME}` uses its own `max_tokens` argument instead and defaults to `10000`. `set_yield_time(value)` asks `{PUBLIC_TOOL_NAME}` to return early if the script is still running after that many milliseconds so `{WAIT_TOOL_NAME}` can resume it later. The returned content starts with a separate `Script completed`, `Script failed`, or `Script running with session ID …` text item that includes wall time. When truncation happens, the final text may include `Total output lines:` and the usual `…N tokens truncated…` marker.\n",
"- Import `{{ output_text, output_image, set_max_output_tokens_per_exec_call, set_yield_time, store, load, yield_control }}` from `@openai/code_mode` (or `\"openai/code_mode\"`). `output_text(value)` surfaces text back to the model and stringifies non-string objects with `JSON.stringify(...)` when possible. `output_image(imageUrl)` appends an `input_image` content item for `http(s)` or `data:` URLs. `store(key, value)` persists JSON-serializable values across `{PUBLIC_TOOL_NAME}` calls in the current session, and `load(key)` returns a cloned stored value or `undefined`. `set_max_output_tokens_per_exec_call(value)` sets the token budget used to truncate direct `{PUBLIC_TOOL_NAME}` returns; `{WAIT_TOOL_NAME}` uses its own `max_tokens` argument instead and defaults to `10000`. `set_yield_time(value)` asks `{PUBLIC_TOOL_NAME}` to return early if the script is still running after that many milliseconds so `{WAIT_TOOL_NAME}` can resume it later. `yield_control()` returns a yielded `{PUBLIC_TOOL_NAME}` response immediately while the script keeps running in the background. The returned content starts with a separate `Script completed`, `Script failed`, or `Script running with session ID …` text item that includes wall time. When truncation happens, the final text may include `Total output lines:` and the usual `…N tokens truncated…` marker.\n",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm realizing that yield doesn't actually feel quite right here, its more about "detaching" focus or backgrounding. The yield is actually just fire and forget and has no impact on the control flow in repl. This is all the more apparent when we're allowing tool calls to run backgrounded too.

Copy link
Contributor

@cconger cconger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming nit, curious your thoughts.

tool_call.name,
tool_call.input,
)
.await,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We actually have head of line blocking here in this loop. Since our tool calls are async its possible that multiple could be in flight from js at a time, and here we are serially executing so a slow one blocks the rest.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its even more exacerbated by this being shared across the process.

Copy link
Collaborator Author

@pakrym-oai pakrym-oai Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree but one step at a time. Want to keep individual changes small.

@pakrym-oai pakrym-oai merged commit 2f03b1a into main Mar 12, 2026
30 of 32 checks passed
@pakrym-oai pakrym-oai deleted the pakrym/code-mode-worker-dispatch branch March 12, 2026 16:00
@github-actions github-actions bot locked and limited conversation to collaborators Mar 12, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants