Skip to content

fix(agents): name the export and the empty value the sandbox refused - #5299

Merged
georgi merged 1 commit into
mainfrom
claude/sandbox-api-failures-jg8io9
Aug 27, 2026
Merged

fix(agents): name the export and the empty value the sandbox refused#5299
georgi merged 1 commit into
mainfrom
claude/sandbox-api-failures-jg8io9

Conversation

@georgi

@georgi georgi commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

What changed

Three sandbox refusals named the failure but not the remedy, and two of them sent the caller after the wrong cause. An unknown named import from a capability module fell through to QuickJS as Could not find export 'generateImage' — no export list, no near match — so a model that reaches for the camelCase spelling just guesses again; mountCapabilityModules already parses the body for its specifiers, so it now checks the imported names too and refuses with the module's export list and the wire name that matches (staticImportBindings in node-sdk is the AST half). missingArgsMessage listed Object.keys(args) under "Got" including keys whose value was null, so edit_storyboard({storyboard_id, ops}) with an unset variable read missing required argument storyboard_id. Got: storyboard_id, ops — a report that contradicts itself and points at a spelling that was already right (withSnakeCaseAliases accepts storyboardId too); it now says the key is right and the value is missing. And the dynamic-import refusal names the static import to use instead.

Verification

  • npm run test:affected — passes except @nodetool-ai/image-nodes, which fails identically on a clean stash: it is the documented headless-WebGPU gap (Dawn/Vulkan, no ICD installed), not this diff.
  • npm run typecheck — web and electron clean; mobile fails on uninstalled Expo/React Native deps, unchanged by this diff and untouched by it.
  • npm run lint — exit 0.
  • npm run nodetool -- harness gate --base origin/mainGate: 12/12 selfchecks passed.
  • npm run test --workspace=packages/node-sdk — 1185 passed, 2 skipped.
  • packages/agents: capability-module-exports, capabilities-args, capabilities-dispatcher, codeact-executor, chat-codeact (91 passed), plus js-sandbox (137), js-sandbox-modules (38), js-scripts-capabilities (41) for the dynamic-import message.

The import check is pinned in both directions rather than only in the failing one: serves an import that names a real export and leaves a default import alone pass through, and the four refusal cases fail. The registry cases derive the wire name from the live module spec, so the check is proven against media/models as they actually declare themselves rather than against a literal that could go stale.

Agent capabilities

No capability is added and no declared contract changes.

  • npm run capabilities:checkcapability table is current (224 capabilities)

Worth noting for reviewers: the first draft of the new test spelled generate_image and find_model as literals, and capabilities:sync then attributed the file as coverage for generate_image, displacing mcp-tools.test.ts from that entry — a test that only mentions a name reading as a test that exercises it. The test now derives every registry name it asserts on and names its session-graft exports do_a_thing / find_a_thing, so the table is untouched.

New checks

  • The import check was observed failing: the four refusal cases in packages/agents/tests/capability-module-exports.test.ts are the inverted direction, and the two pass-through cases prove it does not refuse everything. staticImportBindings is pinned separately in packages/node-sdk/tests/static-import-bindings.test.ts (5 cases, including a default/namespace import contributing no names).
  • The arg-message change carries both directions too: one case asserts the empty-value sentence appears when a required key is present but null, and one asserts it does not appear when the key is simply absent.

Generated by Claude Code

Three sandbox refusals named the failure but not the remedy, and two of them
sent a caller after the wrong cause.

An unknown named import from a capability module fell through to QuickJS as
"Could not find export 'generateImage'" — no export list, no near match — so a
model that reaches for the camelCase spelling guesses again. `mountCapabilityModules`
already parses the body for its specifiers; it now checks the names too, and
refuses with the module's exports and the wire name that matches. `staticImportBindings`
in node-sdk is the AST half.

`missingArgsMessage` listed `Object.keys(args)` under "Got", including keys
whose value was null. `edit_storyboard({storyboard_id, ops})` with an unset
variable therefore read "missing required argument storyboard_id. Got:
storyboard_id, ops" — a report that contradicts itself and points at the
spelling, which was already right. It now says the key is right and the value
is missing.

The dynamic-import refusal names the static import to use instead.
@georgi
georgi merged commit 059a97f into main Aug 27, 2026
21 checks passed
@georgi
georgi deleted the claude/sandbox-api-failures-jg8io9 branch August 27, 2026 08:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants