perf: complete multi-tool code-mode blocks in one exec turn instead of a wait per await#109290
Merged
Conversation
Code mode previously suspended to the model with a waiting result for every
awaited tools.search/describe/call, forcing one wait tool turn per await
(turns = tool calls + 1). Bridge calls now resolve inline within the exec
deadline and resume the QuickJS snapshot host-side, matching the namespace
auto-drain path. Restart-safe runs keep their existing no-drain semantics.
Live proof across OpenAI/Anthropic/Google: ~45% fewer turns and ~45% fewer
input tokens at equal success rate.
Also landed with the same contract:
- one shared wall-clock deadline per exec/wait call (initial worker, inline
drain rounds, and resumed guest interrupt budget all share it)
- park the snapshot instead of resuming when the remaining budget could not
survive VM restore, so near-deadline settles stay recoverable
- abort now terminates the guest worker, skips resume, and drops suspended
runs instead of pinning process-global slots until TTL, reported with the
existing aborted failure code
- exec description omits API.list/MCP/namespace guidance when the run catalog
has no MCP tools or namespaces, so models stop probing an empty surface
- API.list tolerates a trailing-slash prefix like API.list("mcp/")
steipete
force-pushed
the
claude/priceless-lumiere-3ed6f0
branch
from
July 16, 2026 19:51
877732f to
9e89830
Compare
Contributor
Author
|
Merged via squash.
|
github-actions Bot
pushed a commit
to Desicool/openclaw
that referenced
this pull request
Jul 17, 2026
openclaw#109290) Code mode previously suspended to the model with a waiting result for every awaited tools.search/describe/call, forcing one wait tool turn per await (turns = tool calls + 1). Bridge calls now resolve inline within the exec deadline and resume the QuickJS snapshot host-side, matching the namespace auto-drain path. Restart-safe runs keep their existing no-drain semantics. Live proof across OpenAI/Anthropic/Google: ~45% fewer turns and ~45% fewer input tokens at equal success rate. Also landed with the same contract: - one shared wall-clock deadline per exec/wait call (initial worker, inline drain rounds, and resumed guest interrupt budget all share it) - park the snapshot instead of resuming when the remaining budget could not survive VM restore, so near-deadline settles stay recoverable - abort now terminates the guest worker, skips resume, and drops suspended runs instead of pinning process-global slots until TTL, reported with the existing aborted failure code - exec description omits API.list/MCP/namespace guidance when the run catalog has no MCP tools or namespaces, so models stop probing an empty surface - API.list tolerates a trailing-slash prefix like API.list("mcp/")
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.
Closes #109288
What Problem This Solves
Fixes an issue where code mode agents would spend one
waittool round-trip per awaited bridge tool call: a guest block awaiting Ntools.search/describe/calloperations took N+1 model turns, making code mode ~3x slower and 2-4.5x more token-expensive than direct tool exposure on identical live tasks (measured across OpenAI, Anthropic, and Google). It also fixes three issues found while stress-testing the same surface: cancelled exec/wait calls kept hostile guest code running until the interrupt deadline and parked orphaned snapshots in the process-global 64-slot table until TTL; the exec description lured models into probing an emptyAPI/MCPsurface on runs with no MCP tools; andAPI.list("mcp/")rejected the trailing-slash prefix models actually type.Why This Change Was Made
Bridge tool calls run through the same policy-checked host executor whether the model awaits them one at a time or in a batch, so the host now resolves them inline within the exec deadline and resumes the QuickJS snapshot without a model turn - the contract namespace calls already had. That made the per-call budget the real boundary, so one shared wall-clock deadline now bounds the initial worker, inline drain rounds, and the resumed guest interrupt budget; a near-deadline settle parks the snapshot (recoverable via
wait) instead of burning the remaining budget on VM restore; and abort terminates the worker, skips resume, and drops suspended runs.maxPendingToolCallsstays a per-batch concurrency cap and no longer doubles as a sequential-round cap.User Impact
Agents running with
tools.codeModecomplete multi-tool code blocks in one exec turn instead of a wait per await: live A/B across OpenAI/Anthropic/Google shows ~45% fewer turns, ~45% fewer input tokens, and ~50% lower latency at equal task success. Cancelling a run now stops guest code promptly and cannot exhaust the shared suspended-run capacity. No config changes;waiting/waitsemantics remain foryield_control()and calls that outlive the deadline.Evidence
Live bench (real
Agentruntime, 72-tool catalog with adversarial decoys, 4 validated tasks, code mode enabled, defaults):Success-rate A/B on the weakest model (gpt-5.4-mini, 20 runs per arm): before 14/20 pass, avg 7.9 turns; after 15/20 pass, avg 4.3 turns - equal success within noise, turns nearly halved. Final live sanity with the landed code: Anthropic 4/4, Google 4/4, OpenAI 3/4 (the one miss is the model answering without checking sensors, a failure mode present in the before arm too).
Deterministic proof:
pnpm test src/agents/code-mode.test.ts src/agents/tool-search.test.ts src/agents/tool-search.stream-errors.test.ts test/scripts/mcp-code-mode-gateway-client.test.ts- 136 tests green on Blacksmith Testbox (leasetbx_01kxp6xtqf78ykjhtr0xryssjt) on the rebased head, including all replay-safe and abort-classification tests that landed on main meanwhile, plus new tests: five sequentialtools.callawaits complete in a single exec withmaxPendingToolCalls: 2; a pre-aborted exec fails fast with no parked run; a live abort terminates a hostilewhile(true)guest promptly;API.list("mcp/")equalsAPI.list("mcp"); exec description drops/keeps MCP guidance by actual catalog content.pnpm tsgoandpnpm buildgreen on the same Testbox;node scripts/report-test-temp-creations.mjsgreen locally (the delegated warning-only lane cannot difforigin/main...HEADon the Testbox's shallow sync - infra, not this change).Reviewed with autoreview (codex gpt-5.6-sol, xhigh) through seven cycles until clean; review-driven fixes included the shared deadline, abort-drop semantics, the resume-budget floor, and not misreading a missing catalog ref as an empty catalog. The change was then re-ported onto current main - which had meanwhile gained
replaySafethreading and anabortedfailure code - preserving replay-safe no-drain semantics and reusing the existing abort classification; a fresh autoreview pass on the ported diff was clean, and the live sanity re-ran green (Anthropic 4/4, Google 4/4, OpenAI within its measured flake band).