@qduc/term2 0.22.1 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.d.ts +1 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +18 -10
- package/dist/agent.js.map +1 -1
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +19 -1
- package/dist/app.js.map +1 -1
- package/dist/cli.js +26 -0
- package/dist/cli.js.map +1 -1
- package/dist/components/input/ApplicationInputSurface.js +1 -1
- package/dist/components/input/ApplicationInputSurface.js.map +1 -1
- package/dist/components/input/SettingsMenuSession.d.ts.map +1 -1
- package/dist/components/input/SettingsMenuSession.js +8 -5
- package/dist/components/input/SettingsMenuSession.js.map +1 -1
- package/dist/components/input/SubagentPoolMenuSession.d.ts +9 -0
- package/dist/components/input/SubagentPoolMenuSession.d.ts.map +1 -0
- package/dist/components/input/{MentorPoolMenuSession.js → SubagentPoolMenuSession.js} +17 -10
- package/dist/components/input/SubagentPoolMenuSession.js.map +1 -0
- package/dist/components/input/menu-controller.d.ts.map +1 -1
- package/dist/components/input/menu-controller.js +4 -3
- package/dist/components/input/menu-controller.js.map +1 -1
- package/dist/components/input/menu-registry.js +2 -2
- package/dist/components/input/menu-registry.js.map +1 -1
- package/dist/components/input/menu-types.d.ts +5 -1
- package/dist/components/input/menu-types.d.ts.map +1 -1
- package/dist/components/input/triggers.d.ts.map +1 -1
- package/dist/components/input/triggers.js +10 -7
- package/dist/components/input/triggers.js.map +1 -1
- package/dist/components/layout/Banner.d.ts.map +1 -1
- package/dist/components/layout/Banner.js +4 -1
- package/dist/components/layout/Banner.js.map +1 -1
- package/dist/components/layout/StatusBar.d.ts.map +1 -1
- package/dist/components/layout/StatusBar.js +10 -17
- package/dist/components/layout/StatusBar.js.map +1 -1
- package/dist/components/menu/SettingsSelectionMenu.d.ts.map +1 -1
- package/dist/components/menu/SettingsSelectionMenu.js +3 -1
- package/dist/components/menu/SettingsSelectionMenu.js.map +1 -1
- package/dist/components/menu/SubagentPoolSelectionMenu.d.ts +19 -0
- package/dist/components/menu/SubagentPoolSelectionMenu.d.ts.map +1 -0
- package/dist/components/menu/{MentorPoolSelectionMenu.js → SubagentPoolSelectionMenu.js} +29 -15
- package/dist/components/menu/SubagentPoolSelectionMenu.js.map +1 -0
- package/dist/context/InputContext.d.ts +1 -1
- package/dist/context/InputContext.d.ts.map +1 -1
- package/dist/context/InputContext.js +2 -2
- package/dist/context/InputContext.js.map +1 -1
- package/dist/contracts/conversation-items.d.ts +4 -0
- package/dist/contracts/conversation-items.d.ts.map +1 -1
- package/dist/contracts/provider-input.d.ts +2 -0
- package/dist/contracts/provider-input.d.ts.map +1 -1
- package/dist/contracts/provider-input.js.map +1 -1
- package/dist/gateway/assertion.d.ts +1 -1
- package/dist/gateway/assertion.d.ts.map +1 -1
- package/dist/gateway/contracts.d.ts +13 -3
- package/dist/gateway/contracts.d.ts.map +1 -1
- package/dist/gateway/contracts.js +56 -0
- package/dist/gateway/contracts.js.map +1 -1
- package/dist/gateway/gateway.d.ts +40 -1
- package/dist/gateway/gateway.d.ts.map +1 -1
- package/dist/gateway/gateway.js +1319 -76
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/persistence/admission-persistence.d.ts +3 -3
- package/dist/gateway/persistence/admission-persistence.d.ts.map +1 -1
- package/dist/gateway/persistence/admission-persistence.js +25 -11
- package/dist/gateway/persistence/admission-persistence.js.map +1 -1
- package/dist/gateway/persistence/contracts.d.ts +5 -3
- package/dist/gateway/persistence/contracts.d.ts.map +1 -1
- package/dist/gateway/persistence/contracts.js +13 -0
- package/dist/gateway/persistence/contracts.js.map +1 -1
- package/dist/gateway/persistence/interaction-checkpoint.d.ts +5 -0
- package/dist/gateway/persistence/interaction-checkpoint.d.ts.map +1 -1
- package/dist/gateway/persistence/interaction-checkpoint.js +22 -0
- package/dist/gateway/persistence/interaction-checkpoint.js.map +1 -1
- package/dist/gateway/persistence/projection.d.ts.map +1 -1
- package/dist/gateway/persistence/projection.js +4 -1
- package/dist/gateway/persistence/projection.js.map +1 -1
- package/dist/gateway/persistence/session-index.d.ts +1 -1
- package/dist/gateway/persistence/session-index.d.ts.map +1 -1
- package/dist/gateway/persistence/session-index.js +2 -2
- package/dist/gateway/persistence/session-index.js.map +1 -1
- package/dist/gateway/persistence/session-snapshot.d.ts +32 -0
- package/dist/gateway/persistence/session-snapshot.d.ts.map +1 -0
- package/dist/gateway/persistence/session-snapshot.js +50 -0
- package/dist/gateway/persistence/session-snapshot.js.map +1 -0
- package/dist/gateway/runtime-factory.d.ts +37 -1
- package/dist/gateway/runtime-factory.d.ts.map +1 -1
- package/dist/gateway/runtime-factory.js +155 -24
- package/dist/gateway/runtime-factory.js.map +1 -1
- package/dist/gateway/safe-log.d.ts +1 -1
- package/dist/gateway/safe-log.d.ts.map +1 -1
- package/dist/gateway/safe-log.js +5 -46
- package/dist/gateway/safe-log.js.map +1 -1
- package/dist/gateway/serve-args.d.ts +53 -0
- package/dist/gateway/serve-args.d.ts.map +1 -0
- package/dist/gateway/serve-args.js +172 -0
- package/dist/gateway/serve-args.js.map +1 -0
- package/dist/gateway/serve.d.ts +79 -0
- package/dist/gateway/serve.d.ts.map +1 -0
- package/dist/gateway/serve.js +375 -0
- package/dist/gateway/serve.js.map +1 -0
- package/dist/gateway/server-session.d.ts +9 -0
- package/dist/gateway/server-session.d.ts.map +1 -1
- package/dist/gateway/server-session.js +142 -26
- package/dist/gateway/server-session.js.map +1 -1
- package/dist/gateway/server.d.ts.map +1 -1
- package/dist/gateway/server.js +13 -2
- package/dist/gateway/server.js.map +1 -1
- package/dist/hooks/use-subagent-pool-selection.d.ts +115 -0
- package/dist/hooks/use-subagent-pool-selection.d.ts.map +1 -0
- package/dist/hooks/{use-mentor-pool-selection.js → use-subagent-pool-selection.js} +110 -54
- package/dist/hooks/use-subagent-pool-selection.js.map +1 -0
- package/dist/lib/agent-client.d.ts +3 -0
- package/dist/lib/agent-client.d.ts.map +1 -1
- package/dist/lib/agent-client.js +7 -0
- package/dist/lib/agent-client.js.map +1 -1
- package/dist/lib/agent-configuration.d.ts +1 -0
- package/dist/lib/agent-configuration.d.ts.map +1 -1
- package/dist/lib/agent-configuration.js +3 -0
- package/dist/lib/agent-configuration.js.map +1 -1
- package/dist/lib/agent-factory.d.ts +2 -0
- package/dist/lib/agent-factory.d.ts.map +1 -1
- package/dist/lib/agent-factory.js +5 -0
- package/dist/lib/agent-factory.js.map +1 -1
- package/dist/lib/subagent-bridge.d.ts +5 -0
- package/dist/lib/subagent-bridge.d.ts.map +1 -1
- package/dist/lib/subagent-bridge.js +5 -0
- package/dist/lib/subagent-bridge.js.map +1 -1
- package/dist/lib/tool-selection-policy.d.ts +2 -0
- package/dist/lib/tool-selection-policy.d.ts.map +1 -1
- package/dist/lib/tool-selection-policy.js +6 -1
- package/dist/lib/tool-selection-policy.js.map +1 -1
- package/dist/prompts/explorer-delegation-contract.test.ts +1 -1
- package/dist/prompts/prompt-constructor.d.ts.map +1 -1
- package/dist/prompts/prompt-constructor.js +1 -0
- package/dist/prompts/prompt-constructor.js.map +1 -1
- package/dist/prompts/prompt-constructor.test.ts +3 -0
- package/dist/prompts/prompt-constructor.ts +1 -0
- package/dist/prompts/prompt-profiles.d.ts.map +1 -1
- package/dist/prompts/prompt-profiles.js +3 -2
- package/dist/prompts/prompt-profiles.js.map +1 -1
- package/dist/prompts/prompt-profiles.ts +4 -2
- package/dist/prompts/subagent-delegation.d.ts +2 -1
- package/dist/prompts/subagent-delegation.d.ts.map +1 -1
- package/dist/prompts/subagent-delegation.js +10 -5
- package/dist/prompts/subagent-delegation.js.map +1 -1
- package/dist/prompts/subagent-delegation.test.ts +10 -3
- package/dist/prompts/subagent-delegation.ts +16 -4
- package/dist/prompts/subagents/librarian.md +26 -21
- package/dist/prompts/subagents/reviewer.md +33 -0
- package/dist/services/agent-runtime/application-run-loop.d.ts.map +1 -1
- package/dist/services/agent-runtime/application-run-loop.js +10 -5
- package/dist/services/agent-runtime/application-run-loop.js.map +1 -1
- package/dist/services/agent-runtime/context-compaction/local-context-compactor.d.ts +9 -0
- package/dist/services/agent-runtime/context-compaction/local-context-compactor.d.ts.map +1 -1
- package/dist/services/agent-runtime/context-compaction/local-context-compactor.js +6 -0
- package/dist/services/agent-runtime/context-compaction/local-context-compactor.js.map +1 -1
- package/dist/services/agent-runtime/model-resolver.d.ts +6 -0
- package/dist/services/agent-runtime/model-resolver.d.ts.map +1 -1
- package/dist/services/agent-runtime/model-resolver.js +18 -4
- package/dist/services/agent-runtime/model-resolver.js.map +1 -1
- package/dist/services/approval/non-interactive-approval-policy.d.ts.map +1 -1
- package/dist/services/approval/non-interactive-approval-policy.js +2 -1
- package/dist/services/approval/non-interactive-approval-policy.js.map +1 -1
- package/dist/services/approval/shell-auto-approval-evaluator.js +2 -2
- package/dist/services/approval/shell-auto-approval-evaluator.js.map +1 -1
- package/dist/services/conversation/conversation-service.d.ts +28 -0
- package/dist/services/conversation/conversation-service.d.ts.map +1 -1
- package/dist/services/conversation/conversation-service.js +95 -27
- package/dist/services/conversation/conversation-service.js.map +1 -1
- package/dist/services/conversation/run-item-normalizer.d.ts.map +1 -1
- package/dist/services/conversation/run-item-normalizer.js +3 -0
- package/dist/services/conversation/run-item-normalizer.js.map +1 -1
- package/dist/services/conversation/session-browser.d.ts +5 -0
- package/dist/services/conversation/session-browser.d.ts.map +1 -1
- package/dist/services/conversation/session-browser.js +94 -31
- package/dist/services/conversation/session-browser.js.map +1 -1
- package/dist/services/conversation/session-index/session-index-database.d.ts +1 -0
- package/dist/services/conversation/session-index/session-index-database.d.ts.map +1 -1
- package/dist/services/conversation/session-index/session-index-database.js +6 -4
- package/dist/services/conversation/session-index/session-index-database.js.map +1 -1
- package/dist/services/conversation/session-index/session-index-schema.d.ts +1 -1
- package/dist/services/conversation/session-index/session-index-schema.d.ts.map +1 -1
- package/dist/services/conversation/session-index/session-index-schema.js +2 -1
- package/dist/services/conversation/session-index/session-index-schema.js.map +1 -1
- package/dist/services/conversation-agent-client.d.ts +3 -0
- package/dist/services/conversation-agent-client.d.ts.map +1 -1
- package/dist/services/memory/memory-capabilities.d.ts.map +1 -1
- package/dist/services/memory/memory-capabilities.js +2 -5
- package/dist/services/memory/memory-capabilities.js.map +1 -1
- package/dist/services/plan-mode-interceptor.js +2 -2
- package/dist/services/plan-mode-interceptor.js.map +1 -1
- package/dist/services/sandboxed-code-host/host-types.d.ts +12 -1
- package/dist/services/sandboxed-code-host/host-types.d.ts.map +1 -1
- package/dist/services/sandboxed-code-host/host-types.js.map +1 -1
- package/dist/services/sandboxed-code-host/host-worker.d.ts.map +1 -1
- package/dist/services/sandboxed-code-host/host-worker.js +146 -31
- package/dist/services/sandboxed-code-host/host-worker.js.map +1 -1
- package/dist/services/sandboxed-code-host/sandbox.d.ts +6 -1
- package/dist/services/sandboxed-code-host/sandbox.d.ts.map +1 -1
- package/dist/services/sandboxed-code-host/sandbox.js +5 -0
- package/dist/services/sandboxed-code-host/sandbox.js.map +1 -1
- package/dist/services/sandboxed-code-host/sandboxed-code-host.d.ts +3 -3
- package/dist/services/sandboxed-code-host/sandboxed-code-host.d.ts.map +1 -1
- package/dist/services/sandboxed-code-host/sandboxed-code-host.js +173 -22
- package/dist/services/sandboxed-code-host/sandboxed-code-host.js.map +1 -1
- package/dist/services/session/session-access-state.d.ts +5 -1
- package/dist/services/session/session-access-state.d.ts.map +1 -1
- package/dist/services/session/session-access-state.js +8 -1
- package/dist/services/session/session-access-state.js.map +1 -1
- package/dist/services/session/session-client-factory.d.ts +1 -0
- package/dist/services/session/session-client-factory.d.ts.map +1 -1
- package/dist/services/session/session-client-factory.js +1 -1
- package/dist/services/session/session-client-factory.js.map +1 -1
- package/dist/services/session/session-composition.d.ts +22 -4
- package/dist/services/session/session-composition.d.ts.map +1 -1
- package/dist/services/session/session-composition.js +9 -5
- package/dist/services/session/session-composition.js.map +1 -1
- package/dist/services/settings/ancillary-settings-migration.d.ts.map +1 -1
- package/dist/services/settings/ancillary-settings-migration.js +8 -1
- package/dist/services/settings/ancillary-settings-migration.js.map +1 -1
- package/dist/services/settings/settings-schema.d.ts +12 -40
- package/dist/services/settings/settings-schema.d.ts.map +1 -1
- package/dist/services/settings/settings-schema.js +27 -53
- package/dist/services/settings/settings-schema.js.map +1 -1
- package/dist/services/settings/settings-sources.d.ts +0 -3
- package/dist/services/settings/settings-sources.d.ts.map +1 -1
- package/dist/services/settings/settings-sources.js +0 -3
- package/dist/services/settings/settings-sources.js.map +1 -1
- package/dist/services/settings/settings-ui-metadata.d.ts.map +1 -1
- package/dist/services/settings/settings-ui-metadata.js +3 -3
- package/dist/services/settings/settings-ui-metadata.js.map +1 -1
- package/dist/services/settings/test-helpers/settings-consumer-inventory.d.ts +1 -1
- package/dist/services/settings/test-helpers/settings-consumer-inventory.d.ts.map +1 -1
- package/dist/services/settings/test-helpers/settings-consumer-inventory.js +5 -8
- package/dist/services/settings/test-helpers/settings-consumer-inventory.js.map +1 -1
- package/dist/services/subagents/nested-runner.d.ts +3 -0
- package/dist/services/subagents/nested-runner.d.ts.map +1 -1
- package/dist/services/subagents/nested-runner.js +31 -4
- package/dist/services/subagents/nested-runner.js.map +1 -1
- package/dist/services/subagents/role-loader.d.ts.map +1 -1
- package/dist/services/subagents/role-loader.js +7 -12
- package/dist/services/subagents/role-loader.js.map +1 -1
- package/dist/services/subagents/runtime.d.ts +3 -0
- package/dist/services/subagents/runtime.d.ts.map +1 -1
- package/dist/services/subagents/runtime.js +35 -3
- package/dist/services/subagents/runtime.js.map +1 -1
- package/dist/services/subagents/subagent-async-registry.d.ts +10 -1
- package/dist/services/subagents/subagent-async-registry.d.ts.map +1 -1
- package/dist/services/subagents/subagent-async-registry.js +21 -4
- package/dist/services/subagents/subagent-async-registry.js.map +1 -1
- package/dist/services/subagents/subagent-manager.d.ts +3 -0
- package/dist/services/subagents/subagent-manager.d.ts.map +1 -1
- package/dist/services/subagents/subagent-manager.js.map +1 -1
- package/dist/services/subagents/subagent-pool-config.d.ts +39 -0
- package/dist/services/subagents/subagent-pool-config.d.ts.map +1 -0
- package/dist/services/subagents/subagent-pool-config.js +84 -0
- package/dist/services/subagents/subagent-pool-config.js.map +1 -0
- package/dist/services/subagents/subagent-role-pool-selector.d.ts +29 -0
- package/dist/services/subagents/subagent-role-pool-selector.d.ts.map +1 -0
- package/dist/services/subagents/subagent-role-pool-selector.js +56 -0
- package/dist/services/subagents/subagent-role-pool-selector.js.map +1 -0
- package/dist/services/subagents/tool-policy.d.ts +13 -0
- package/dist/services/subagents/tool-policy.d.ts.map +1 -1
- package/dist/services/subagents/tool-policy.js +36 -4
- package/dist/services/subagents/tool-policy.js.map +1 -1
- package/dist/services/subagents/types.d.ts +1 -1
- package/dist/services/subagents/types.d.ts.map +1 -1
- package/dist/services/subagents/types.js +1 -1
- package/dist/services/subagents/types.js.map +1 -1
- package/dist/test-helpers/outside-sandbox-temp-dir.d.ts +2 -0
- package/dist/test-helpers/outside-sandbox-temp-dir.d.ts.map +1 -0
- package/dist/test-helpers/outside-sandbox-temp-dir.js +38 -0
- package/dist/test-helpers/outside-sandbox-temp-dir.js.map +1 -0
- package/dist/tools/agent/configure-task-check-in.d.ts +2 -0
- package/dist/tools/agent/configure-task-check-in.d.ts.map +1 -1
- package/dist/tools/agent/configure-task-check-in.js +3 -0
- package/dist/tools/agent/configure-task-check-in.js.map +1 -1
- package/dist/tools/agent/run-explorer.d.ts +6 -0
- package/dist/tools/agent/run-explorer.d.ts.map +1 -0
- package/dist/tools/agent/run-explorer.js +58 -0
- package/dist/tools/agent/run-explorer.js.map +1 -0
- package/dist/tools/agent/run-subagent-async.d.ts +2 -0
- package/dist/tools/agent/run-subagent-async.d.ts.map +1 -1
- package/dist/tools/agent/run-subagent-async.js +3 -3
- package/dist/tools/agent/run-subagent-async.js.map +1 -1
- package/dist/tools/agent/run-subagent.d.ts +2 -2
- package/dist/tools/agent/run-subagent.d.ts.map +1 -1
- package/dist/tools/agent/run-subagent.js +8 -6
- package/dist/tools/agent/run-subagent.js.map +1 -1
- package/dist/tools/file/apply-patch.d.ts.map +1 -1
- package/dist/tools/file/apply-patch.js +7 -2
- package/dist/tools/file/apply-patch.js.map +1 -1
- package/dist/tools/file/create-file.d.ts.map +1 -1
- package/dist/tools/file/create-file.js +4 -0
- package/dist/tools/file/create-file.js.map +1 -1
- package/dist/tools/file/glob.d.ts.map +1 -1
- package/dist/tools/file/glob.js +6 -0
- package/dist/tools/file/glob.js.map +1 -1
- package/dist/tools/file/search-replace.d.ts.map +1 -1
- package/dist/tools/file/search-replace.js +6 -2
- package/dist/tools/file/search-replace.js.map +1 -1
- package/dist/tools/format-helpers.d.ts +5 -0
- package/dist/tools/format-helpers.d.ts.map +1 -1
- package/dist/tools/format-helpers.js.map +1 -1
- package/dist/tools/memory/memory-tools.d.ts +0 -1
- package/dist/tools/memory/memory-tools.d.ts.map +1 -1
- package/dist/tools/memory/memory-tools.js +1 -116
- package/dist/tools/memory/memory-tools.js.map +1 -1
- package/dist/tools/scripted-return-contract.d.ts +24 -0
- package/dist/tools/scripted-return-contract.d.ts.map +1 -0
- package/dist/tools/scripted-return-contract.js +36 -0
- package/dist/tools/scripted-return-contract.js.map +1 -0
- package/dist/tools/session-browser/session-browser-tools.d.ts.map +1 -1
- package/dist/tools/session-browser/session-browser-tools.js +21 -8
- package/dist/tools/session-browser/session-browser-tools.js.map +1 -1
- package/dist/tools/system/run-code/index.d.ts +3 -0
- package/dist/tools/system/run-code/index.d.ts.map +1 -1
- package/dist/tools/system/run-code/index.js +3 -0
- package/dist/tools/system/run-code/index.js.map +1 -1
- package/dist/tools/system/run-code/run-code-execution.d.ts +56 -0
- package/dist/tools/system/run-code/run-code-execution.d.ts.map +1 -0
- package/dist/tools/system/run-code/run-code-execution.js +68 -0
- package/dist/tools/system/run-code/run-code-execution.js.map +1 -0
- package/dist/tools/system/run-code/run-code-runtime-contract.d.ts +20 -0
- package/dist/tools/system/run-code/run-code-runtime-contract.d.ts.map +1 -0
- package/dist/tools/system/run-code/run-code-runtime-contract.js +2 -0
- package/dist/tools/system/run-code/run-code-runtime-contract.js.map +1 -0
- package/dist/tools/system/run-code/run-code-runtime.d.ts +99 -0
- package/dist/tools/system/run-code/run-code-runtime.d.ts.map +1 -0
- package/dist/tools/system/run-code/run-code-runtime.js +719 -0
- package/dist/tools/system/run-code/run-code-runtime.js.map +1 -0
- package/dist/tools/system/run-code/run-code-telemetry.d.ts +12 -10
- package/dist/tools/system/run-code/run-code-telemetry.d.ts.map +1 -1
- package/dist/tools/system/run-code/run-code-telemetry.js +48 -72
- package/dist/tools/system/run-code/run-code-telemetry.js.map +1 -1
- package/dist/tools/system/run-code/run-code.d.ts +22 -59
- package/dist/tools/system/run-code/run-code.d.ts.map +1 -1
- package/dist/tools/system/run-code/run-code.js +118 -784
- package/dist/tools/system/run-code/run-code.js.map +1 -1
- package/dist/tools/system/run-code/tools-header.d.ts +16 -4
- package/dist/tools/system/run-code/tools-header.d.ts.map +1 -1
- package/dist/tools/system/run-code/tools-header.js +32 -16
- package/dist/tools/system/run-code/tools-header.js.map +1 -1
- package/dist/tools/system/shell.d.ts +3 -0
- package/dist/tools/system/shell.d.ts.map +1 -1
- package/dist/tools/system/shell.js +13 -3
- package/dist/tools/system/shell.js.map +1 -1
- package/dist/tools/types.d.ts +12 -4
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/types.js.map +1 -1
- package/dist/utils/ai/model-settings.d.ts +6 -0
- package/dist/utils/ai/model-settings.d.ts.map +1 -1
- package/dist/utils/ai/model-settings.js +7 -1
- package/dist/utils/ai/model-settings.js.map +1 -1
- package/dist/utils/ai/token-usage.d.ts +4 -5
- package/dist/utils/ai/token-usage.d.ts.map +1 -1
- package/dist/utils/ai/token-usage.js +3 -3
- package/dist/utils/ai/token-usage.js.map +1 -1
- package/dist/utils/conversation/conversation-utils.d.ts.map +1 -1
- package/dist/utils/conversation/conversation-utils.js +3 -0
- package/dist/utils/conversation/conversation-utils.js.map +1 -1
- package/dist/utils/settings-command.d.ts.map +1 -1
- package/dist/utils/settings-command.js +2 -1
- package/dist/utils/settings-command.js.map +1 -1
- package/dist/utils/shell/sandbox/sandbox-policy.d.ts +2 -0
- package/dist/utils/shell/sandbox/sandbox-policy.d.ts.map +1 -1
- package/dist/utils/shell/sandbox/sandbox-policy.js +1 -1
- package/dist/utils/shell/sandbox/sandbox-policy.js.map +1 -1
- package/dist/utils/streaming/streaming-session-factory.d.ts.map +1 -1
- package/dist/utils/streaming/streaming-session-factory.js +22 -22
- package/dist/utils/streaming/streaming-session-factory.js.map +1 -1
- package/package.json +1 -1
- package/dist/components/input/MentorPoolMenuSession.d.ts +0 -9
- package/dist/components/input/MentorPoolMenuSession.d.ts.map +0 -1
- package/dist/components/input/MentorPoolMenuSession.js.map +0 -1
- package/dist/components/menu/MentorPoolSelectionMenu.d.ts +0 -13
- package/dist/components/menu/MentorPoolSelectionMenu.d.ts.map +0 -1
- package/dist/components/menu/MentorPoolSelectionMenu.js.map +0 -1
- package/dist/hooks/use-mentor-pool-selection.d.ts +0 -105
- package/dist/hooks/use-mentor-pool-selection.d.ts.map +0 -1
- package/dist/hooks/use-mentor-pool-selection.js.map +0 -1
|
@@ -1,76 +1,71 @@
|
|
|
1
|
-
import { appendFileSync } from 'node:fs';
|
|
2
|
-
import { lstat } from 'node:fs/promises';
|
|
3
|
-
import { randomUUID } from 'node:crypto';
|
|
4
1
|
import { z } from 'zod';
|
|
5
2
|
import { relaxedNumber } from '../../utils.js';
|
|
6
|
-
import { normalizeToolParameters } from '../../../lib/tool-invoke.js';
|
|
7
|
-
import { SandboxedCodeHostImpl } from '../../../services/sandboxed-code-host/sandboxed-code-host.js';
|
|
8
|
-
import { applyApprovalGrant } from '../../../services/approval/approval-grant-executor.js';
|
|
9
|
-
import { isZodToolParameterSchema, } from '../../types.js';
|
|
10
3
|
import { createBaseMessage, getCallIdFromItem, getOutputText, isSuccessOutput, normalizeToolArguments, } from '../../format-helpers.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
import { saveOutputArtifact, formatFullOutputSavedNote } from '../../../utils/shell/shell-output.js';
|
|
18
|
-
export const TOOL_NAME_RUN_CODE = 'run_code';
|
|
19
|
-
export const TOOL_NAME_DESCRIBE = 'describe';
|
|
4
|
+
import { renderToolsHeader } from './tools-header.js';
|
|
5
|
+
import { RUN_CODE_EXECUTION_RESULT } from './run-code-execution.js';
|
|
6
|
+
import { formatFullOutputSavedNote, saveOutputArtifact } from '../../../utils/shell/shell-output.js';
|
|
7
|
+
import { RUN_CODE_LIMITS, RUN_CODE_PROHIBITED_TOOLS, TOOL_NAME_RUN_CODE, createRunCodeRuntime, } from './run-code-runtime.js';
|
|
8
|
+
export { RUN_CODE_EXECUTION_RESULT, getRunCodeExecutionResult } from './run-code-execution.js';
|
|
9
|
+
export { RUN_CODE_LIMITS, RUN_CODE_PROHIBITED_TOOLS, TOOL_NAME_DESCRIBE, TOOL_NAME_RUN_CODE, isDirectlyCallable, } from './run-code-runtime.js';
|
|
20
10
|
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
21
11
|
/** Node `setTimeout` wraps delays above `2**31-1` to ~1 ms. */
|
|
22
12
|
const MAX_TIMEOUT_MS = 2_147_483_647;
|
|
23
13
|
const MAX_OUTPUT_CHARS = 30_000;
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
const encodeInputKeysForValidation = (value) => {
|
|
15
|
+
if (Array.isArray(value))
|
|
16
|
+
return value.map(encodeInputKeysForValidation);
|
|
17
|
+
if (value === null || typeof value !== 'object')
|
|
18
|
+
return value;
|
|
19
|
+
const encoded = Object.create(null);
|
|
20
|
+
for (const [key, item] of Object.entries(value)) {
|
|
21
|
+
Object.defineProperty(encoded, `$${key}`, {
|
|
22
|
+
value: encodeInputKeysForValidation(item),
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
writable: true,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return encoded;
|
|
29
|
+
};
|
|
30
|
+
const decodeValidatedInputKeys = (value) => {
|
|
31
|
+
const decode = (item) => {
|
|
32
|
+
if (Array.isArray(item))
|
|
33
|
+
return item.map(decode);
|
|
34
|
+
if (item === null || typeof item !== 'object')
|
|
35
|
+
return item;
|
|
36
|
+
const decoded = Object.create(null);
|
|
37
|
+
for (const [key, nested] of Object.entries(item)) {
|
|
38
|
+
Object.defineProperty(decoded, key.slice(1), {
|
|
39
|
+
value: decode(nested),
|
|
40
|
+
enumerable: true,
|
|
41
|
+
configurable: true,
|
|
42
|
+
writable: true,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return decoded;
|
|
46
|
+
};
|
|
47
|
+
return decode(value);
|
|
48
|
+
};
|
|
49
|
+
const runCodeInputsSchema = z
|
|
50
|
+
.preprocess(encodeInputKeysForValidation, z.record(z.string(), z.json()))
|
|
51
|
+
// Zod records omit `__proto__`; prefixing every key before validation and
|
|
52
|
+
// removing exactly one prefix afterward keeps all JSON object keys inert.
|
|
53
|
+
.overwrite(decodeValidatedInputKeys);
|
|
26
54
|
/**
|
|
27
55
|
* Script-shaped limits. They are deliberately not the workflow's: a workflow
|
|
28
56
|
* spawns a handful of agents, while a script's whole point is looping over many
|
|
29
57
|
* cheap tool calls.
|
|
30
58
|
*/
|
|
31
|
-
export const RUN_CODE_LIMITS = {
|
|
32
|
-
/** Total `tools.*` calls one script may make. */
|
|
33
|
-
maxCalls: 200,
|
|
34
|
-
/** Parallel-safe calls that may overlap; others take a serial lane of one. */
|
|
35
|
-
maxConcurrency: 8,
|
|
36
|
-
/** Per-result cap. A larger result is truncated with an explicit marker. */
|
|
37
|
-
maxResultChars: 100_000,
|
|
38
|
-
/** Maximum raw bytes in one media attachment retained outside the script. */
|
|
39
|
-
maxMediaBytes: 8 * 1024 * 1024,
|
|
40
|
-
/** Maximum raw bytes and attachment count retained for one script run. */
|
|
41
|
-
maxMediaTotalBytes: 32 * 1024 * 1024,
|
|
42
|
-
maxMediaAttachments: 32,
|
|
43
|
-
maxCodeBytes: 65_536,
|
|
44
|
-
maxOutputBytes: 262_144,
|
|
45
|
-
maxConsoleBytes: 262_144,
|
|
46
|
-
};
|
|
47
|
-
/**
|
|
48
|
-
* Tools a script may never call.
|
|
49
|
-
*
|
|
50
|
-
* `run_code` excludes itself because each run owns its own call budget, and
|
|
51
|
-
* nesting would let one run spend many. The rest are the workflow's prohibited
|
|
52
|
-
* set: `run_subagent` in particular spawns agents outside any run budget, so a
|
|
53
|
-
* script could loop on it indefinitely. Shell tools are excluded because their
|
|
54
|
-
* complete approval state also lives in the interactive batch coordinator.
|
|
55
|
-
*/
|
|
56
|
-
export const RUN_CODE_PROHIBITED_TOOLS = new Set([
|
|
57
|
-
...WORKFLOW_PROHIBITED_TOOLS,
|
|
58
|
-
TOOL_NAME_RUN_CODE,
|
|
59
|
-
'ask_mentor',
|
|
60
|
-
'session_rollover',
|
|
61
|
-
// Shell approval also depends on coordinator-owned Docker/session state that
|
|
62
|
-
// the raw policy registry cannot express for an out-of-band script call.
|
|
63
|
-
'shell',
|
|
64
|
-
'bash',
|
|
65
|
-
'enter_worktree',
|
|
66
|
-
'exit_worktree',
|
|
67
|
-
]);
|
|
68
59
|
export const runCodeParametersSchema = z.object({
|
|
69
60
|
code: z
|
|
70
61
|
.string()
|
|
71
62
|
.min(1)
|
|
72
63
|
.describe('JavaScript (not TypeScript) executed as the body of an async function. Top-level await is available. ' +
|
|
73
64
|
'Return the value you want the model to receive. Use console.log only for debugging.'),
|
|
65
|
+
inputs: runCodeInputsSchema
|
|
66
|
+
.optional()
|
|
67
|
+
.describe('JSON data exposed to the script as the global `inputs`. Use this for patch or file text. ' +
|
|
68
|
+
'Code and serialized inputs share the 65,536-byte admission limit.'),
|
|
74
69
|
include_console: z
|
|
75
70
|
.boolean()
|
|
76
71
|
.optional()
|
|
@@ -106,59 +101,21 @@ const RUN_CODE_DESCRIPTION = 'Write a JavaScript program and execute it. Return
|
|
|
106
101
|
'If the final result says `Full output saved to`, read that exact path with `read_file` rather than repeating completed calls; ' +
|
|
107
102
|
'for a large artifact, use line ranges or a focused `grep` projection. A scripted `read_file` result may itself have ' +
|
|
108
103
|
'`truncated: true` and a `fullOutputPath`; follow that path or narrow the projection before returning it. ' +
|
|
109
|
-
'Use `tools.describe` before guessing parameters or returned fields. If a patch
|
|
110
|
-
'
|
|
104
|
+
'Use `tools.describe` before guessing parameters or returned fields. If a patch or file body contains JavaScript ' +
|
|
105
|
+
'syntax such as backticks or `${...}`, pass it through the `inputs` parameter and read it from the global `inputs` ' +
|
|
106
|
+
'object instead of embedding it in code. Code and serialized inputs share the 65,536-byte admission limit. ' +
|
|
111
107
|
'Example: `const r = await Promise.allSettled([tools.read_file({path:"a.ts"}), tools.read_file({path:"b.ts"})]); ' +
|
|
112
108
|
'return r.map(x => x.status === "fulfilled" ? {content:(typeof x.value === "string" ? x.value : x.value.content).slice(0,2000)} : {error:x.reason.message});`';
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
if (parsed.ok === true)
|
|
125
|
-
return { outcome: 'applied' };
|
|
126
|
-
const detail = typeof parsed.error === 'string' && parsed.error ? parsed.error : 'action reported ok:false';
|
|
127
|
-
return { outcome: 'not_applied', reason: detail };
|
|
128
|
-
},
|
|
129
|
-
cancel_run: (raw) => {
|
|
130
|
-
const parsed = parseActionPayload(raw);
|
|
131
|
-
if (!isRecord(parsed) || typeof parsed.ok !== 'boolean') {
|
|
132
|
-
return { outcome: 'unknown', reason: 'unrecognized action result shape' };
|
|
133
|
-
}
|
|
134
|
-
if (parsed.ok === true &&
|
|
135
|
-
typeof parsed.runId === 'string' &&
|
|
136
|
-
parsed.runId.length > 0 &&
|
|
137
|
-
parsed.status === 'cancelling') {
|
|
138
|
-
return { outcome: 'applied', reason: 'cancellation requested and accepted; settlement is reported separately' };
|
|
139
|
-
}
|
|
140
|
-
if (parsed.ok === false &&
|
|
141
|
-
parsed.code === 'not_active' &&
|
|
142
|
-
typeof parsed.target === 'string' &&
|
|
143
|
-
parsed.target.length > 0) {
|
|
144
|
-
return { outcome: 'not_applied', reason: `not_active (target: ${parsed.target})` };
|
|
145
|
-
}
|
|
146
|
-
return { outcome: 'unknown', reason: 'unrecognized action result shape' };
|
|
147
|
-
},
|
|
148
|
-
};
|
|
149
|
-
const isActionTool = (name) => ACTION_SEMANTICS[name] !== undefined;
|
|
150
|
-
const parseActionPayload = (raw) => {
|
|
151
|
-
if (typeof raw !== 'string')
|
|
152
|
-
return raw;
|
|
153
|
-
try {
|
|
154
|
-
return JSON.parse(raw);
|
|
155
|
-
}
|
|
156
|
-
catch {
|
|
157
|
-
return raw;
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
const MAX_RECEIPT_REASON_CHARS = 280;
|
|
161
|
-
const clipReason = (reason) => reason.length <= MAX_RECEIPT_REASON_CHARS ? reason : `${reason.slice(0, MAX_RECEIPT_REASON_CHARS)}…`;
|
|
109
|
+
function attachRunCodeExecution(value, execution) {
|
|
110
|
+
if (Array.isArray(value)) {
|
|
111
|
+
const result = [...value];
|
|
112
|
+
Object.defineProperty(result, RUN_CODE_EXECUTION_RESULT, { value: execution });
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
const result = new String(value);
|
|
116
|
+
Object.defineProperty(result, RUN_CODE_EXECUTION_RESULT, { value: execution });
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
162
119
|
/**
|
|
163
120
|
* Marks a definition as accepting the final, wrapped tool registry.
|
|
164
121
|
*
|
|
@@ -181,45 +138,6 @@ export function bindRunCodeNestedApprovalOwner(tools, owner) {
|
|
|
181
138
|
tool[NESTED_OWNER_BINDER]?.(owner, tools);
|
|
182
139
|
}
|
|
183
140
|
}
|
|
184
|
-
/**
|
|
185
|
-
* Direct calls are the tools a script structurally cannot reach, including
|
|
186
|
-
* `run_code` itself. Visibility does not depend on `canRequireApproval`.
|
|
187
|
-
*/
|
|
188
|
-
export function isDirectlyCallable(tool) {
|
|
189
|
-
return RUN_CODE_PROHIBITED_TOOLS.has(tool.name);
|
|
190
|
-
}
|
|
191
|
-
function unknownToolMessage(name, registry) {
|
|
192
|
-
return `Unknown tool "${name}". Available: ${registry.map((entry) => entry.name).join(', ')}`;
|
|
193
|
-
}
|
|
194
|
-
function describeTool(tool) {
|
|
195
|
-
let parameters;
|
|
196
|
-
const targetSchema = tool.canonicalParameters ?? tool.parameters;
|
|
197
|
-
if (isZodToolParameterSchema(targetSchema)) {
|
|
198
|
-
try {
|
|
199
|
-
parameters = z.toJSONSchema(targetSchema, { io: 'input' });
|
|
200
|
-
}
|
|
201
|
-
catch {
|
|
202
|
-
parameters = { unconvertible: true };
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
else if (targetSchema && typeof targetSchema === 'object') {
|
|
206
|
-
parameters = targetSchema;
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
parameters = { unconvertible: true };
|
|
210
|
-
}
|
|
211
|
-
return {
|
|
212
|
-
name: tool.name,
|
|
213
|
-
description: tool.description,
|
|
214
|
-
parameters,
|
|
215
|
-
// The scripted-path return contract. Agents discover it here before
|
|
216
|
-
// calling, and the tools header renders the same string for essential
|
|
217
|
-
// tools, so both surfaces stay consistent.
|
|
218
|
-
...(typeof tool.scriptedReturnShape === 'string' && tool.scriptedReturnShape
|
|
219
|
-
? { scriptedReturnShape: tool.scriptedReturnShape }
|
|
220
|
-
: {}),
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
141
|
const summarizeCalls = (calls) => {
|
|
224
142
|
const describeCalls = calls.filter((call) => call.outcome === 'describe');
|
|
225
143
|
const executionCalls = calls.filter((call) => call.outcome !== 'describe');
|
|
@@ -263,112 +181,23 @@ const clip = async (text, protectedAction) => {
|
|
|
263
181
|
prefix = prefix.slice(0, -1);
|
|
264
182
|
return `${prefix}${marker}${action}`;
|
|
265
183
|
};
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
];
|
|
273
|
-
/**
|
|
274
|
-
* Whether a rendered run_code result reads as unsuccessful. Script-level
|
|
275
|
-
* failures start with a known prefix; a script can also complete at the JS
|
|
276
|
-
* level while its nested result is an error string (status-string tools such as
|
|
277
|
-
* apply_patch), which the renderer places under a `Result:` heading. Lifecycle
|
|
278
|
-
* status stays 'completed' in both cases — this is the call-level success bit,
|
|
279
|
-
* kept separate from script success (visible in the text) and task success
|
|
280
|
-
* (conversation-level).
|
|
281
|
-
*/
|
|
282
|
-
const isUnsuccessfulRunCodeOutput = (output) => {
|
|
283
|
-
if (FAILURE_PREFIXES.some((prefix) => output.startsWith(prefix)))
|
|
284
|
-
return true;
|
|
184
|
+
/** Compatibility-only reader for old persisted messages. New executions carry
|
|
185
|
+
* a typed marker and never use rendered text for their success bit. */
|
|
186
|
+
const legacyRunCodeSuccess = (output) => {
|
|
187
|
+
if (/^(?:Error:|Script failed|Script timed out|Script was cancelled|Script exceeded its deadline)/.test(output)) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
285
190
|
if (output.startsWith('Result:')) {
|
|
286
|
-
// The renderer joins the rendered value and the trailing tool-call summary
|
|
287
|
-
// with a blank line. Evaluate only the value block, using the shared
|
|
288
|
-
// success heuristic: an 'Error:' text prefix or a JSON value carrying an
|
|
289
|
-
// 'error' key (string or structured envelope) both read as unsuccessful.
|
|
290
191
|
const valueBlock = output.slice('Result:'.length).trimStart().split('\n\n')[0];
|
|
291
|
-
return
|
|
192
|
+
return isSuccessOutput(valueBlock);
|
|
292
193
|
}
|
|
293
|
-
return
|
|
194
|
+
return true;
|
|
294
195
|
};
|
|
295
|
-
/**
|
|
296
|
-
* Truncation is a display concern, but a script may branch on the result, so
|
|
297
|
-
* the marker has to be unmistakable rather than a silent cut.
|
|
298
|
-
*/
|
|
299
|
-
const truncate = (text, limit) => text.length <= limit ? text : `${text.slice(0, limit)}\n[truncated: result exceeded ${limit} characters]`;
|
|
300
196
|
const isRecord = (value) => typeof value === 'object' && value !== null;
|
|
301
197
|
const isMediaContentPart = (value) => isRecord(value) && ((value.type === 'image' && 'image' in value) || (value.type === 'file' && 'file' in value));
|
|
302
198
|
const isContentPartArray = (value) => Array.isArray(value) &&
|
|
303
199
|
value.length > 0 &&
|
|
304
200
|
value.every((part) => isRecord(part) && (part.type === 'text' || isMediaContentPart(part)));
|
|
305
|
-
const MEDIA_REFERENCE_KEY = '__term2_run_code_media_reference__';
|
|
306
|
-
const isMediaReference = (value) => isRecord(value) && typeof value[MEDIA_REFERENCE_KEY] === 'string' && Object.keys(value).length === 1;
|
|
307
|
-
/** Count the bytes that will eventually be sent to the model, not its JSON wrapper. */
|
|
308
|
-
function mediaBytes(value) {
|
|
309
|
-
const payload = value.type === 'image' ? value.image : value.type === 'file' ? value.file : undefined;
|
|
310
|
-
if (isRecord(payload) && typeof payload.data === 'string') {
|
|
311
|
-
try {
|
|
312
|
-
return Buffer.from(payload.data, 'base64').byteLength;
|
|
313
|
-
}
|
|
314
|
-
catch {
|
|
315
|
-
return Buffer.byteLength(payload.data, 'utf8');
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
try {
|
|
319
|
-
return Buffer.byteLength(JSON.stringify(payload) ?? '', 'utf8');
|
|
320
|
-
}
|
|
321
|
-
catch {
|
|
322
|
-
return Number.POSITIVE_INFINITY;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
/**
|
|
326
|
-
* Keep large media out of both the worker transport and its generic result
|
|
327
|
-
* budget. The reference is deliberately scoped to one execute call: a token
|
|
328
|
-
* from another run is just an ordinary object and cannot recover its bytes.
|
|
329
|
-
*/
|
|
330
|
-
function createMediaReferenceStore() {
|
|
331
|
-
const attachments = new Map();
|
|
332
|
-
const byValue = new WeakMap();
|
|
333
|
-
let totalBytes = 0;
|
|
334
|
-
const capture = (value) => {
|
|
335
|
-
if (isMediaContentPart(value)) {
|
|
336
|
-
const existing = byValue.get(value);
|
|
337
|
-
if (existing !== undefined)
|
|
338
|
-
return existing ? { [MEDIA_REFERENCE_KEY]: existing } : '[media content omitted]';
|
|
339
|
-
const size = mediaBytes(value);
|
|
340
|
-
if (!Number.isFinite(size) ||
|
|
341
|
-
size > RUN_CODE_LIMITS.maxMediaBytes ||
|
|
342
|
-
attachments.size >= RUN_CODE_LIMITS.maxMediaAttachments ||
|
|
343
|
-
totalBytes + size > RUN_CODE_LIMITS.maxMediaTotalBytes) {
|
|
344
|
-
byValue.set(value, null);
|
|
345
|
-
return (`[media content omitted: attachment exceeds the ${RUN_CODE_LIMITS.maxMediaBytes}-byte per-image or ` +
|
|
346
|
-
`${RUN_CODE_LIMITS.maxMediaTotalBytes}-byte per-run limit]`);
|
|
347
|
-
}
|
|
348
|
-
const token = `run_code_media_${randomUUID()}`;
|
|
349
|
-
byValue.set(value, token);
|
|
350
|
-
attachments.set(token, value);
|
|
351
|
-
totalBytes += size;
|
|
352
|
-
return { [MEDIA_REFERENCE_KEY]: token };
|
|
353
|
-
}
|
|
354
|
-
if (Array.isArray(value))
|
|
355
|
-
return value.map(capture);
|
|
356
|
-
if (isRecord(value)) {
|
|
357
|
-
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, capture(entry)]));
|
|
358
|
-
}
|
|
359
|
-
return value;
|
|
360
|
-
};
|
|
361
|
-
const resolve = (value) => {
|
|
362
|
-
if (isMediaReference(value))
|
|
363
|
-
return attachments.get(value[MEDIA_REFERENCE_KEY]) ?? value;
|
|
364
|
-
if (Array.isArray(value))
|
|
365
|
-
return value.map(resolve);
|
|
366
|
-
if (isRecord(value))
|
|
367
|
-
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, resolve(entry)]));
|
|
368
|
-
return value;
|
|
369
|
-
};
|
|
370
|
-
return { capture, resolve };
|
|
371
|
-
}
|
|
372
201
|
/** Look through script wrappers without mistaking ordinary arrays for media. */
|
|
373
202
|
function containsMediaContent(value, ancestors = new Set()) {
|
|
374
203
|
if (isMediaContentPart(value))
|
|
@@ -384,41 +213,6 @@ function containsMediaContent(value, ancestors = new Set()) {
|
|
|
384
213
|
ancestors.delete(value);
|
|
385
214
|
return found;
|
|
386
215
|
}
|
|
387
|
-
const serializeResult = async (result, limit, toolName, callsCompleted) => {
|
|
388
|
-
if (typeof result === 'string')
|
|
389
|
-
return { ok: true, result: truncate(result, limit) };
|
|
390
|
-
try {
|
|
391
|
-
const encoded = JSON.stringify(result);
|
|
392
|
-
if (encoded === undefined)
|
|
393
|
-
return { ok: true, result: null };
|
|
394
|
-
if (encoded.length <= limit)
|
|
395
|
-
return { ok: true, result: result };
|
|
396
|
-
// Never turn a multimodal result into a partial JSON string: that both
|
|
397
|
-
// corrupts image data and makes the failure look like a successful text
|
|
398
|
-
// result to the script. A clear value lets the script continue (or catch
|
|
399
|
-
// the omission) without sending an unusable image to the worker.
|
|
400
|
-
if (containsMediaContent(result)) {
|
|
401
|
-
return {
|
|
402
|
-
ok: true,
|
|
403
|
-
result: `[truncated: media result exceeded ${limit} characters; media content omitted]`,
|
|
404
|
-
};
|
|
405
|
-
}
|
|
406
|
-
let retrieval = '';
|
|
407
|
-
try {
|
|
408
|
-
const artifactPath = await saveOutputArtifact(encoded, { filenamePrefix: 'tool-overflow' });
|
|
409
|
-
retrieval = `Full output saved to: ${artifactPath}. `;
|
|
410
|
-
}
|
|
411
|
-
catch {
|
|
412
|
-
retrieval = 'Full output could not be saved; the omitted result is unavailable. ';
|
|
413
|
-
}
|
|
414
|
-
const callUnit = callsCompleted === 1 ? 'call' : 'calls';
|
|
415
|
-
const message = `Tool "${toolName}" result exceeded ${limit} characters and could not be delivered to script. ${retrieval}${callsCompleted} nested tool ${callUnit} completed — inspect state before retrying; tool effects have already completed.`;
|
|
416
|
-
return { ok: false, error: message };
|
|
417
|
-
}
|
|
418
|
-
catch {
|
|
419
|
-
return { ok: true, result: truncate(String(result), limit) };
|
|
420
|
-
}
|
|
421
|
-
};
|
|
422
216
|
/** Replace embedded media with a text marker while retaining the real parts. */
|
|
423
217
|
function stripMediaContent(value, media) {
|
|
424
218
|
if (isMediaContentPart(value)) {
|
|
@@ -432,8 +226,6 @@ function stripMediaContent(value, media) {
|
|
|
432
226
|
}
|
|
433
227
|
return value;
|
|
434
228
|
}
|
|
435
|
-
/** Renders one console.log's arguments the way a terminal would. */
|
|
436
|
-
const renderConsoleValues = (values) => values.map((value) => (typeof value === 'string' ? value : JSON.stringify(value))).join(' ');
|
|
437
229
|
export const formatRunCodeCommandMessage = (item, index, toolCallArgumentsById) => {
|
|
438
230
|
const callId = getCallIdFromItem(item);
|
|
439
231
|
const fallbackArgs = callId && toolCallArgumentsById.has(callId) ? toolCallArgumentsById.get(callId) : null;
|
|
@@ -441,36 +233,17 @@ export const formatRunCodeCommandMessage = (item, index, toolCallArgumentsById)
|
|
|
441
233
|
const description = typeof args?.description === 'string' ? args.description : undefined;
|
|
442
234
|
const code = typeof args?.code === 'string' ? args.code : '';
|
|
443
235
|
const output = getOutputText(item) || 'No output';
|
|
236
|
+
const semantic = item.runCodeExecution;
|
|
444
237
|
return [
|
|
445
238
|
createBaseMessage(item, index, 0, false, {
|
|
446
239
|
command: description ? `run_code — ${description}` : 'run_code',
|
|
447
240
|
output,
|
|
448
|
-
success:
|
|
241
|
+
success: semantic ? semantic.success : legacyRunCodeSuccess(output),
|
|
449
242
|
toolName: TOOL_NAME_RUN_CODE,
|
|
450
243
|
toolArgs: { ...args, code },
|
|
451
244
|
}),
|
|
452
245
|
];
|
|
453
246
|
};
|
|
454
|
-
function getConversationSessionId(context) {
|
|
455
|
-
if (!context || typeof context !== 'object')
|
|
456
|
-
return undefined;
|
|
457
|
-
const runContext = context.context;
|
|
458
|
-
if (!runContext || typeof runContext !== 'object')
|
|
459
|
-
return undefined;
|
|
460
|
-
const sessionId = runContext.sessionId;
|
|
461
|
-
return typeof sessionId === 'string' && sessionId.length > 0 ? sessionId : undefined;
|
|
462
|
-
}
|
|
463
|
-
function writeNestedCallRecord(tool, sessionId, outcome) {
|
|
464
|
-
const logPath = process.env.TERM2_NESTED_CALL_LOG;
|
|
465
|
-
if (!logPath || !sessionId)
|
|
466
|
-
return;
|
|
467
|
-
try {
|
|
468
|
-
appendFileSync(logPath, `${JSON.stringify({ tool, sessionId, timestamp: new Date().toISOString(), outcome })}\n`, 'utf8');
|
|
469
|
-
}
|
|
470
|
-
catch {
|
|
471
|
-
// Experiment-only logging must never affect tool execution.
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
247
|
export function createRunCodeToolDefinition(options) {
|
|
475
248
|
const { loggingService, getCwd = () => options.executionContext?.getCwd() || process.cwd() } = options;
|
|
476
249
|
const approvalRegistry = options.approvalPolicyRegistry;
|
|
@@ -478,372 +251,48 @@ export function createRunCodeToolDefinition(options) {
|
|
|
478
251
|
let boundRegistry;
|
|
479
252
|
let nestedApprovalOwner = options.nestedApprovalOwner;
|
|
480
253
|
const exposedTools = () => (options.getToolRegistry?.() ?? boundRegistry ?? []).filter((tool) => !RUN_CODE_PROHIBITED_TOOLS.has(tool.name));
|
|
254
|
+
const createRuntime = (registry) => createRunCodeRuntime({
|
|
255
|
+
registry,
|
|
256
|
+
graphIdentity: boundRegistry ?? registry,
|
|
257
|
+
loggingService,
|
|
258
|
+
approvalPolicyRegistry: approvalRegistry,
|
|
259
|
+
getCwd,
|
|
260
|
+
executionContext: options.executionContext,
|
|
261
|
+
nestedApprovalOwner,
|
|
262
|
+
sessionAccess: options.sessionAccess,
|
|
263
|
+
nestedCompatibility: options.nestedCompatibility,
|
|
264
|
+
});
|
|
481
265
|
const definition = {
|
|
482
266
|
name: TOOL_NAME_RUN_CODE,
|
|
483
267
|
// Read late, after bindRunCodeRegistry, so the model is told which tools
|
|
484
268
|
// the script can actually reach rather than a guess made before wrapping.
|
|
485
269
|
get description() {
|
|
486
|
-
const header = renderToolsHeader(exposedTools());
|
|
270
|
+
const header = renderToolsHeader(createRuntime(exposedTools()).discovery());
|
|
487
271
|
return header ? `${RUN_CODE_DESCRIPTION}\n\n${header}` : RUN_CODE_DESCRIPTION;
|
|
488
272
|
},
|
|
489
273
|
parameters: runCodeParametersSchema,
|
|
490
274
|
effect: 'mutating',
|
|
491
275
|
needsApproval: () => false,
|
|
492
|
-
execute: async (params, context) => {
|
|
493
|
-
const { code, timeout_ms, description, include_console = false } = params;
|
|
276
|
+
execute: async (params, context, details) => {
|
|
277
|
+
const { code, inputs = {}, timeout_ms, description, include_console = false } = params;
|
|
494
278
|
const timeout = timeout_ms ?? DEFAULT_TIMEOUT_MS;
|
|
495
279
|
const callerSignal = context?.signal;
|
|
496
280
|
const registry = exposedTools();
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
const pendingReceiptByCallId = new Map();
|
|
505
|
-
let rejectedSeq = 0;
|
|
506
|
-
const output = [];
|
|
507
|
-
const sessionId = getConversationSessionId(context);
|
|
508
|
-
const mediaReferences = createMediaReferenceStore();
|
|
509
|
-
const record = (tool, outcome, started, directlyCallable) => {
|
|
510
|
-
calls.push({ tool, outcome, durationMs: Date.now() - started, directlyCallable });
|
|
511
|
-
if (outcome === 'describe')
|
|
512
|
-
return;
|
|
513
|
-
writeNestedCallRecord(tool, sessionId, outcome === 'ok' ? 'success' : outcome === 'approval_required' ? 'denied-by-approval' : 'failure');
|
|
514
|
-
};
|
|
515
|
-
const failed = (message) => ({
|
|
516
|
-
kind: 'result',
|
|
517
|
-
result: { ok: false, error: message },
|
|
518
|
-
});
|
|
519
|
-
const recordReceipt = (callId, toolName, outcome, reason) => {
|
|
520
|
-
const pendingIndex = pendingReceiptByCallId.get(callId);
|
|
521
|
-
if (pendingIndex !== undefined) {
|
|
522
|
-
receipts[pendingIndex] = {
|
|
523
|
-
callId,
|
|
524
|
-
tool: toolName,
|
|
525
|
-
outcome,
|
|
526
|
-
...(reason ? { reason: clipReason(reason) } : {}),
|
|
527
|
-
};
|
|
528
|
-
pendingReceiptByCallId.delete(callId);
|
|
529
|
-
return;
|
|
530
|
-
}
|
|
531
|
-
receipts.push({
|
|
532
|
-
callId,
|
|
533
|
-
tool: toolName,
|
|
534
|
-
outcome,
|
|
535
|
-
...(reason ? { reason: clipReason(reason) } : {}),
|
|
536
|
-
});
|
|
537
|
-
};
|
|
538
|
-
const tools = {
|
|
539
|
-
binding: {
|
|
540
|
-
name: 'tools',
|
|
541
|
-
kind: 'namespace',
|
|
542
|
-
members: [...new Set([...registry.map((tool) => tool.name), TOOL_NAME_DESCRIBE])],
|
|
543
|
-
},
|
|
544
|
-
limits: {
|
|
545
|
-
maxCalls: RUN_CODE_LIMITS.maxCalls,
|
|
546
|
-
maxConcurrency: RUN_CODE_LIMITS.maxConcurrency,
|
|
547
|
-
limitExceededMessage: `Tool call limit reached (${RUN_CODE_LIMITS.maxCalls} calls per script run).`,
|
|
548
|
-
},
|
|
549
|
-
// A budget-exhausted call is the script's problem, not a reason to
|
|
550
|
-
// discard the work it has already printed.
|
|
551
|
-
overBudget: ({ usedCalls, maxCalls }, rejected) => {
|
|
552
|
-
// The host supplies the exact prepared call that was rejected.
|
|
553
|
-
// Preparation is async and concurrent, so a local FIFO would be
|
|
554
|
-
// unable to attribute a budget rejection safely.
|
|
555
|
-
if (isActionTool(rejected.tool.name)) {
|
|
556
|
-
rejectedSeq += 1;
|
|
557
|
-
recordReceipt(`${bridgeRunId}:rejected-${rejectedSeq}`, rejected.tool.name, 'unknown', 'call budget exhausted before dispatch');
|
|
558
|
-
}
|
|
559
|
-
return failed(`Tool call limit reached (${maxCalls} calls per script run; ${usedCalls} calls admitted, ${Math.max(0, maxCalls - usedCalls)} remaining). ` +
|
|
560
|
-
'Return the partial results you collected and start another run_code call only for unattempted work; ' +
|
|
561
|
-
'Do not repeat completed tool effects.');
|
|
562
|
-
},
|
|
563
|
-
onAdmitted: (prepared, callContext) => {
|
|
564
|
-
if (!isActionTool(prepared.tool.name))
|
|
565
|
-
return;
|
|
566
|
-
const callId = `${bridgeRunId}:${callContext.callId}`;
|
|
567
|
-
pendingReceiptByCallId.set(callId, receipts.length);
|
|
568
|
-
receipts.push({ callId, tool: prepared.tool.name, outcome: 'unknown' });
|
|
569
|
-
},
|
|
570
|
-
prepare: async (payload) => {
|
|
571
|
-
const started = Date.now();
|
|
572
|
-
const name = typeof payload.member === 'string' ? payload.member : '';
|
|
573
|
-
const tool = registry.find((candidate) => candidate.name === name);
|
|
574
|
-
if (name === TOOL_NAME_DESCRIBE && typeof payload.params === 'string') {
|
|
575
|
-
const described = registry.find((candidate) => candidate.name === payload.params);
|
|
576
|
-
if (!described)
|
|
577
|
-
return failed(unknownToolMessage(payload.params, registry));
|
|
578
|
-
record(name, 'describe', started);
|
|
579
|
-
return {
|
|
580
|
-
kind: 'result',
|
|
581
|
-
result: { ok: true, result: describeTool(described) },
|
|
582
|
-
};
|
|
583
|
-
}
|
|
584
|
-
if (!tool) {
|
|
585
|
-
record(name || '(unnamed)', 'unknown_tool', started);
|
|
586
|
-
if (isActionTool(name)) {
|
|
587
|
-
rejectedSeq += 1;
|
|
588
|
-
recordReceipt(`${bridgeRunId}:rejected-${rejectedSeq}`, name, 'failed', unknownToolMessage(name, registry));
|
|
589
|
-
}
|
|
590
|
-
return failed(unknownToolMessage(name, registry));
|
|
591
|
-
}
|
|
592
|
-
const targetSchema = tool.canonicalParameters ?? tool.parameters;
|
|
593
|
-
let normalized;
|
|
594
|
-
try {
|
|
595
|
-
normalized = normalizeToolParameters(payload.params ?? {}, targetSchema);
|
|
596
|
-
}
|
|
597
|
-
catch {
|
|
598
|
-
normalized = payload.params ?? {};
|
|
599
|
-
}
|
|
600
|
-
if (isZodToolParameterSchema(targetSchema)) {
|
|
601
|
-
const parsed = targetSchema.safeParse(normalized);
|
|
602
|
-
if (!parsed.success) {
|
|
603
|
-
record(name, 'invalid_params', started);
|
|
604
|
-
const issues = parsed.error.issues
|
|
605
|
-
.map((issue) => `${issue.path.join('.') || '(root)'}: ${issue.message}`)
|
|
606
|
-
.join('; ');
|
|
607
|
-
if (isActionTool(name)) {
|
|
608
|
-
rejectedSeq += 1;
|
|
609
|
-
recordReceipt(`${bridgeRunId}:rejected-${rejectedSeq}`, name, 'failed', `Invalid parameters: ${issues}`);
|
|
610
|
-
}
|
|
611
|
-
return failed(`Invalid parameters for "${name}": ${issues}\nSignature: ${renderCompactSignature(tool)}`);
|
|
612
|
-
}
|
|
613
|
-
normalized = parsed.data;
|
|
614
|
-
}
|
|
615
|
-
const authorityRoot = getCwd();
|
|
616
|
-
const authority = await bindPreparedAuthority(name, normalized, authorityRoot, options.executionContext);
|
|
617
|
-
if (authority.kind === 'denied') {
|
|
618
|
-
record(name, 'approval_required', started, isDirectlyCallable(tool));
|
|
619
|
-
if (isActionTool(name)) {
|
|
620
|
-
rejectedSeq += 1;
|
|
621
|
-
recordReceipt(`${bridgeRunId}:rejected-${rejectedSeq}`, name, 'not_applied', authority.message);
|
|
622
|
-
}
|
|
623
|
-
return failed(authority.message);
|
|
624
|
-
}
|
|
625
|
-
const prepared = {
|
|
626
|
-
tool,
|
|
627
|
-
params: authority.params,
|
|
628
|
-
originalParams: normalized,
|
|
629
|
-
authorityRoot,
|
|
630
|
-
authorityMeaning: fingerprint(authority),
|
|
631
|
-
parallelSafe: await isParallelSafe(tool, normalized, context),
|
|
632
|
-
started,
|
|
633
|
-
};
|
|
634
|
-
return prepared;
|
|
635
|
-
},
|
|
636
|
-
// Mirrors the run loop: only a definition that declares itself
|
|
637
|
-
// parallel-safe may overlap another call, because tools such as
|
|
638
|
-
// enter_worktree mutate shared execution context.
|
|
639
|
-
lane: (prepared) => (prepared.parallelSafe ? 'default' : 'serial'),
|
|
640
|
-
invoke: async (prepared, callContext) => {
|
|
641
|
-
const decision = await approvalRegistry.evaluate({
|
|
642
|
-
toolName: prepared.tool.name,
|
|
643
|
-
args: prepared.params,
|
|
644
|
-
context,
|
|
645
|
-
});
|
|
646
|
-
if (decision.kind !== 'auto_approve') {
|
|
647
|
-
if (decision.kind === 'prompt' && nestedApprovalOwner) {
|
|
648
|
-
const nestedCallId = bridgeRunId + ':' + callContext.callId;
|
|
649
|
-
const nestedContext = withAbortSignal(context, mergeAbortSignals(callerSignal, callContext.signal), {
|
|
650
|
-
scripted: true,
|
|
651
|
-
});
|
|
652
|
-
tools.onWaiting?.(callContext);
|
|
653
|
-
try {
|
|
654
|
-
const resolution = await nestedApprovalOwner.request({
|
|
655
|
-
requestId: nestedCallId,
|
|
656
|
-
sessionId: sessionId ?? 'unknown',
|
|
657
|
-
graphIdentity: boundRegistry ?? registry,
|
|
658
|
-
outerRunId: bridgeRunId,
|
|
659
|
-
nestedCallId,
|
|
660
|
-
toolName: prepared.tool.name,
|
|
661
|
-
preparedArguments: prepared.params,
|
|
662
|
-
authorityContext: context,
|
|
663
|
-
approval: {
|
|
664
|
-
agentName: 'Nested run_code',
|
|
665
|
-
toolName: prepared.tool.name,
|
|
666
|
-
argumentsText: JSON.stringify(prepared.params),
|
|
667
|
-
rawInterruption: null,
|
|
668
|
-
callId: nestedCallId,
|
|
669
|
-
outsideWorkspaceEdit: resolveOutsideWorkspaceEdit(prepared.tool.name, prepared.params, prepared.authorityRoot),
|
|
670
|
-
},
|
|
671
|
-
signal: nestedContext.signal,
|
|
672
|
-
revalidateAuthority: async () => {
|
|
673
|
-
const currentRoot = getCwd();
|
|
674
|
-
const authority = await bindPreparedAuthority(prepared.tool.name, prepared.originalParams, currentRoot, options.executionContext);
|
|
675
|
-
return (authority.kind === 'bound' &&
|
|
676
|
-
currentRoot === prepared.authorityRoot &&
|
|
677
|
-
fingerprint(authority) === prepared.authorityMeaning);
|
|
678
|
-
},
|
|
679
|
-
revalidate: async () => {
|
|
680
|
-
const latest = await approvalRegistry.evaluate({
|
|
681
|
-
toolName: prepared.tool.name,
|
|
682
|
-
args: prepared.params,
|
|
683
|
-
context,
|
|
684
|
-
});
|
|
685
|
-
return latest.kind;
|
|
686
|
-
},
|
|
687
|
-
grant: (nestedDecision) => {
|
|
688
|
-
if (callContext.signal.aborted)
|
|
689
|
-
throw new Error('Tool execution was not approved.');
|
|
690
|
-
const applied = applyApprovalGrant({
|
|
691
|
-
sessionId: sessionId ?? 'unknown',
|
|
692
|
-
sessionAccess: options.sessionAccess,
|
|
693
|
-
nestedCompatibility: options.nestedCompatibility,
|
|
694
|
-
logger: loggingService,
|
|
695
|
-
}, {
|
|
696
|
-
answer: nestedDecision.answer,
|
|
697
|
-
toolName: prepared.tool.name,
|
|
698
|
-
rawArguments: prepared.params,
|
|
699
|
-
callId: nestedCallId,
|
|
700
|
-
});
|
|
701
|
-
if (!applied.isApproved)
|
|
702
|
-
throw new Error('Tool execution was not approved.');
|
|
703
|
-
},
|
|
704
|
-
dispatch: async () => prepared.tool.execute(prepared.params, nestedContext, { toolCall: { callId: nestedCallId } }),
|
|
705
|
-
});
|
|
706
|
-
if (resolution.kind === 'approved') {
|
|
707
|
-
record(prepared.tool.name, 'ok', prepared.started);
|
|
708
|
-
if (isActionTool(prepared.tool.name)) {
|
|
709
|
-
const semantic = ACTION_SEMANTICS[prepared.tool.name](resolution.result);
|
|
710
|
-
recordReceipt(`${bridgeRunId}:${callContext.callId}`, prepared.tool.name, semantic.outcome, semantic.reason);
|
|
711
|
-
}
|
|
712
|
-
const serialized = await serializeResult(mediaReferences.capture(resolution.result), RUN_CODE_LIMITS.maxResultChars, prepared.tool.name, calls.filter((c) => c.outcome !== 'describe').length);
|
|
713
|
-
return {
|
|
714
|
-
kind: 'result',
|
|
715
|
-
result: serialized,
|
|
716
|
-
};
|
|
717
|
-
}
|
|
718
|
-
if (resolution.kind === 'failed') {
|
|
719
|
-
record(prepared.tool.name, 'error', prepared.started);
|
|
720
|
-
const message = resolution.error instanceof Error ? resolution.error.message : String(resolution.error);
|
|
721
|
-
if (isActionTool(prepared.tool.name)) {
|
|
722
|
-
recordReceipt(`${bridgeRunId}:${callContext.callId}`, prepared.tool.name, 'failed', message);
|
|
723
|
-
}
|
|
724
|
-
return failed(message);
|
|
725
|
-
}
|
|
726
|
-
record(prepared.tool.name, 'approval_required', prepared.started, isDirectlyCallable(prepared.tool));
|
|
727
|
-
if (isActionTool(prepared.tool.name)) {
|
|
728
|
-
recordReceipt(`${bridgeRunId}:${callContext.callId}`, prepared.tool.name, 'not_applied', resolution.message);
|
|
729
|
-
}
|
|
730
|
-
return failed(resolution.message);
|
|
731
|
-
}
|
|
732
|
-
finally {
|
|
733
|
-
tools.onResumed?.(callContext);
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
const outcome = decision.kind === 'unknown'
|
|
737
|
-
? 'unknown_policy'
|
|
738
|
-
: decision.kind === 'error'
|
|
739
|
-
? 'policy_error'
|
|
740
|
-
: decision.kind === 'interceptor_denied'
|
|
741
|
-
? 'interceptor_denied'
|
|
742
|
-
: 'approval_required';
|
|
743
|
-
const directlyCallable = isDirectlyCallable(prepared.tool);
|
|
744
|
-
record(prepared.tool.name, outcome, prepared.started, directlyCallable);
|
|
745
|
-
if (isActionTool(prepared.tool.name)) {
|
|
746
|
-
const denialReason = decision.kind === 'unknown'
|
|
747
|
-
? 'no registered approval policy'
|
|
748
|
-
: decision.kind === 'interceptor_denied'
|
|
749
|
-
? 'refused by an approval interceptor'
|
|
750
|
-
: decision.kind === 'error'
|
|
751
|
-
? 'approval policy failed'
|
|
752
|
-
: 'requires approval and is unavailable from inside a script';
|
|
753
|
-
recordReceipt(`${bridgeRunId}:${callContext.callId}`, prepared.tool.name, 'not_applied', denialReason);
|
|
754
|
-
}
|
|
755
|
-
return failed(decision.kind === 'unknown'
|
|
756
|
-
? `"${prepared.tool.name}" has no registered approval policy and is unavailable from inside a script.`
|
|
757
|
-
: decision.kind === 'interceptor_denied'
|
|
758
|
-
? `"${prepared.tool.name}" was refused by an approval interceptor and is unavailable from inside a script.`
|
|
759
|
-
: decision.kind === 'error'
|
|
760
|
-
? `"${prepared.tool.name}" approval policy failed and is unavailable from inside a script.`
|
|
761
|
-
: `"${prepared.tool.name}" requires approval and is unavailable from inside a script.`);
|
|
762
|
-
}
|
|
763
|
-
const started = Date.now();
|
|
764
|
-
const callId = `${bridgeRunId}:${callContext.callId}`;
|
|
765
|
-
try {
|
|
766
|
-
// Marks the call as originating inside a script: the result goes to the
|
|
767
|
-
// script, not into model context, so context-protection caps do not apply.
|
|
768
|
-
const nestedContext = withAbortSignal(context, mergeAbortSignals(callerSignal, callContext.signal), {
|
|
769
|
-
scripted: true,
|
|
770
|
-
});
|
|
771
|
-
const result = await prepared.tool.execute(prepared.params, nestedContext, { toolCall: { callId } });
|
|
772
|
-
record(prepared.tool.name, 'ok', started);
|
|
773
|
-
if (isActionTool(prepared.tool.name)) {
|
|
774
|
-
const semantic = ACTION_SEMANTICS[prepared.tool.name](result);
|
|
775
|
-
recordReceipt(callId, prepared.tool.name, semantic.outcome, semantic.reason);
|
|
776
|
-
}
|
|
777
|
-
const serialized = await serializeResult(mediaReferences.capture(result), RUN_CODE_LIMITS.maxResultChars, prepared.tool.name, calls.filter((c) => c.outcome !== 'describe').length);
|
|
778
|
-
return {
|
|
779
|
-
kind: 'result',
|
|
780
|
-
result: serialized,
|
|
781
|
-
};
|
|
782
|
-
}
|
|
783
|
-
catch (error) {
|
|
784
|
-
record(prepared.tool.name, 'error', started);
|
|
785
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
786
|
-
if (isActionTool(prepared.tool.name)) {
|
|
787
|
-
recordReceipt(callId, prepared.tool.name, 'failed', message);
|
|
788
|
-
}
|
|
789
|
-
return failed(message);
|
|
790
|
-
}
|
|
791
|
-
},
|
|
792
|
-
};
|
|
793
|
-
loggingService.debug('run_code execution started', {
|
|
794
|
-
cwd: getCwd(),
|
|
281
|
+
// The runtime is the single owner of the nested-call lifecycle. The
|
|
282
|
+
// definition remains responsible for model parameters and presentation;
|
|
283
|
+
// in particular, it must not expose a partially wrapped registry.
|
|
284
|
+
const runtime = createRuntime(registry);
|
|
285
|
+
const runtimeResult = await runtime.execute({
|
|
286
|
+
code,
|
|
287
|
+
inputs,
|
|
795
288
|
timeout,
|
|
796
|
-
exposedTools: registry.length,
|
|
797
289
|
description,
|
|
798
|
-
|
|
799
|
-
const result = await new SandboxedCodeHostImpl().run({
|
|
800
|
-
code,
|
|
801
|
-
capabilities: { tools },
|
|
802
|
-
limits: {
|
|
803
|
-
timeoutMs: timeout,
|
|
804
|
-
maxCodeBytes: RUN_CODE_LIMITS.maxCodeBytes,
|
|
805
|
-
maxOutputBytes: RUN_CODE_LIMITS.maxOutputBytes,
|
|
806
|
-
maxConsoleBytes: RUN_CODE_LIMITS.maxConsoleBytes,
|
|
807
|
-
},
|
|
808
|
-
subject: 'Script',
|
|
809
|
-
allowVoidOutput: true,
|
|
290
|
+
context,
|
|
810
291
|
signal: callerSignal,
|
|
811
|
-
onConsole: (values) => output.push(renderConsoleValues(values)),
|
|
812
|
-
});
|
|
813
|
-
const describeCalls = calls.filter((call) => call.outcome === 'describe');
|
|
814
|
-
const executionCalls = calls.filter((call) => call.outcome !== 'describe');
|
|
815
|
-
loggingService.debug('run_code execution finished', {
|
|
816
|
-
ok: result.ok,
|
|
817
|
-
toolCalls: executionCalls.length,
|
|
818
|
-
schemaLookups: describeCalls.length,
|
|
819
292
|
});
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
for (const [callId, index] of pendingReceiptByCallId) {
|
|
824
|
-
receipts[index] = {
|
|
825
|
-
...receipts[index],
|
|
826
|
-
outcome: 'unknown',
|
|
827
|
-
reason: 'did not settle before the script run ended',
|
|
828
|
-
};
|
|
829
|
-
pendingReceiptByCallId.delete(callId);
|
|
830
|
-
}
|
|
831
|
-
// Emitted before rendering: a script whose result cannot be rendered has
|
|
832
|
-
// still produced an outcome worth counting.
|
|
833
|
-
emitRunCodeCompletionTelemetry(loggingService, {
|
|
834
|
-
code,
|
|
835
|
-
result,
|
|
836
|
-
durationMs: Date.now() - startedAt,
|
|
837
|
-
timeoutMs: timeout,
|
|
838
|
-
sessionId,
|
|
839
|
-
runId: bridgeRunId,
|
|
840
|
-
calls,
|
|
841
|
-
receipts,
|
|
842
|
-
});
|
|
843
|
-
const resolvedResult = result.ok && !result.voidOutput
|
|
844
|
-
? { ...result, output: mediaReferences.resolve(result.output) }
|
|
845
|
-
: result;
|
|
846
|
-
return renderResult(resolvedResult, output, calls, receipts, include_console);
|
|
293
|
+
const runtimeRendered = await renderResult(runtimeResult.execution, runtimeResult.output, include_console);
|
|
294
|
+
const runtimeTransportCall = details && typeof details === 'object' && 'toolCall' in details && typeof details.toolCall === 'object';
|
|
295
|
+
return runtimeTransportCall ? attachRunCodeExecution(runtimeRendered, runtimeResult.execution) : runtimeRendered;
|
|
847
296
|
},
|
|
848
297
|
formatCommandMessage: formatRunCodeCommandMessage,
|
|
849
298
|
};
|
|
@@ -858,125 +307,6 @@ export function createRunCodeToolDefinition(options) {
|
|
|
858
307
|
};
|
|
859
308
|
return definition;
|
|
860
309
|
}
|
|
861
|
-
const PATH_TOOLS = new Set([
|
|
862
|
-
'read_file',
|
|
863
|
-
'create_file',
|
|
864
|
-
'search_replace',
|
|
865
|
-
'apply_patch',
|
|
866
|
-
'grep',
|
|
867
|
-
'glob',
|
|
868
|
-
'read_code_outline',
|
|
869
|
-
'code_context_search',
|
|
870
|
-
]);
|
|
871
|
-
async function bindPreparedAuthority(toolName, params, cwd, executionContext) {
|
|
872
|
-
const isRemote = executionContext?.isRemote() ?? false;
|
|
873
|
-
if (isRemote && PATH_TOOLS.has(toolName)) {
|
|
874
|
-
return { kind: 'denied', message: 'Cannot establish remote physical path authority for a nested tool call.' };
|
|
875
|
-
}
|
|
876
|
-
try {
|
|
877
|
-
const physicalRoot = isRemote ? cwd : await requirePhysicalPath(cwd, cwd);
|
|
878
|
-
return { kind: 'bound', physicalRoot, params: await bindPreparedArguments(toolName, params, cwd) };
|
|
879
|
-
}
|
|
880
|
-
catch (error) {
|
|
881
|
-
return {
|
|
882
|
-
kind: 'denied',
|
|
883
|
-
message: `Cannot establish physical path authority: ${error instanceof Error ? error.message : String(error)}`,
|
|
884
|
-
};
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
async function requirePhysicalPath(value, cwd, semantics = 'referent') {
|
|
888
|
-
const physicalPath = await resolveWorkspacePathPhysically(value, cwd);
|
|
889
|
-
if (physicalPath === undefined)
|
|
890
|
-
throw new Error(`Unresolved path: ${value}`);
|
|
891
|
-
if (semantics === 'unlink-source') {
|
|
892
|
-
// Move sources are both read and unlinked. Following a leaf symlink would
|
|
893
|
-
// preserve the read but delete its referent; reject rather than change meaning.
|
|
894
|
-
const lexicalPath = resolveWorkspacePath(value, cwd, { allowOutsideWorkspace: true });
|
|
895
|
-
const stats = await lstat(lexicalPath);
|
|
896
|
-
if (stats.isSymbolicLink())
|
|
897
|
-
throw new Error(`Symlink unlink source is unsupported: ${value}`);
|
|
898
|
-
}
|
|
899
|
-
return physicalPath;
|
|
900
|
-
}
|
|
901
|
-
async function bindPreparedArguments(toolName, params, cwd) {
|
|
902
|
-
if (!params || typeof params !== 'object' || Array.isArray(params) || !PATH_TOOLS.has(toolName))
|
|
903
|
-
return params;
|
|
904
|
-
const record = params;
|
|
905
|
-
const bindPath = async (value, semantics = 'referent') => typeof value === 'string' ? requirePhysicalPath(value, cwd, semantics) : value;
|
|
906
|
-
if (toolName === 'apply_patch' && typeof record.patch === 'string') {
|
|
907
|
-
return { ...record, patch: await bindPatchPaths(record.patch, bindPath) };
|
|
908
|
-
}
|
|
909
|
-
return 'path' in record ? { ...record, path: await bindPath(record.path) } : params;
|
|
910
|
-
}
|
|
911
|
-
async function bindPatchPaths(patch, bindPath) {
|
|
912
|
-
let parsed;
|
|
913
|
-
try {
|
|
914
|
-
parsed = parseUpstreamApplyPatch(patch);
|
|
915
|
-
}
|
|
916
|
-
catch {
|
|
917
|
-
return patch;
|
|
918
|
-
}
|
|
919
|
-
const targets = parsed.operations.flatMap((operation) => {
|
|
920
|
-
const moveTo = 'moveTo' in operation ? operation.moveTo : undefined;
|
|
921
|
-
const semantics = operation.type === 'delete_file' || moveTo ? 'unlink-source' : 'referent';
|
|
922
|
-
return [{ path: operation.path, semantics }, ...(moveTo ? [{ path: moveTo, semantics: 'referent' }] : [])];
|
|
923
|
-
});
|
|
924
|
-
let targetIndex = 0;
|
|
925
|
-
const lines = patch.replace(/\r\n?/g, '\n').split('\n');
|
|
926
|
-
const prefixes = ['*** Add File: ', '*** Update File: ', '*** Delete File: ', '*** Move to: '];
|
|
927
|
-
for (let index = 0; index < lines.length; index += 1) {
|
|
928
|
-
const prefix = prefixes.find((candidate) => lines[index].startsWith(candidate));
|
|
929
|
-
if (!prefix)
|
|
930
|
-
continue;
|
|
931
|
-
const target = targets[targetIndex++];
|
|
932
|
-
const bound = await bindPath(target.path, target.semantics);
|
|
933
|
-
lines[index] = prefix + String(bound);
|
|
934
|
-
}
|
|
935
|
-
return lines.join('\n');
|
|
936
|
-
}
|
|
937
|
-
function fingerprint(value) {
|
|
938
|
-
try {
|
|
939
|
-
return JSON.stringify(value) ?? String(value);
|
|
940
|
-
}
|
|
941
|
-
catch {
|
|
942
|
-
return String(value);
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
function withAbortSignal(context, signal, extra = {}) {
|
|
946
|
-
return context && typeof context === 'object'
|
|
947
|
-
? { ...context, signal, ...extra }
|
|
948
|
-
: { signal, ...extra };
|
|
949
|
-
}
|
|
950
|
-
function mergeAbortSignals(callerSignal, hostSignal) {
|
|
951
|
-
if (!callerSignal || callerSignal === hostSignal)
|
|
952
|
-
return callerSignal ?? hostSignal;
|
|
953
|
-
const controller = new AbortController();
|
|
954
|
-
const abort = () => {
|
|
955
|
-
callerSignal.removeEventListener('abort', abort);
|
|
956
|
-
hostSignal.removeEventListener('abort', abort);
|
|
957
|
-
controller.abort();
|
|
958
|
-
};
|
|
959
|
-
if (callerSignal.aborted || hostSignal.aborted) {
|
|
960
|
-
controller.abort();
|
|
961
|
-
return controller.signal;
|
|
962
|
-
}
|
|
963
|
-
callerSignal.addEventListener('abort', abort, { once: true });
|
|
964
|
-
hostSignal.addEventListener('abort', abort, { once: true });
|
|
965
|
-
return controller.signal;
|
|
966
|
-
}
|
|
967
|
-
async function isParallelSafe(tool, params, context) {
|
|
968
|
-
const declared = tool.parallelSafe;
|
|
969
|
-
if (declared === undefined || declared === false)
|
|
970
|
-
return false;
|
|
971
|
-
if (declared === true)
|
|
972
|
-
return true;
|
|
973
|
-
try {
|
|
974
|
-
return (await declared(params, context)) === true;
|
|
975
|
-
}
|
|
976
|
-
catch {
|
|
977
|
-
return false;
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
310
|
const formatActionOutcome = (outcome) => outcome === 'not_applied' ? 'not applied' : outcome;
|
|
981
311
|
/**
|
|
982
312
|
* Host-rendered action section. Present only when at least one covered action
|
|
@@ -1007,28 +337,32 @@ const renderActionReceipts = (receipts, includeDetails = true) => {
|
|
|
1007
337
|
'they describe tool behavior, not user-request or task outcomes.',
|
|
1008
338
|
].join('\n');
|
|
1009
339
|
};
|
|
1010
|
-
function renderResult(
|
|
340
|
+
function renderResult(execution, output, includeConsole) {
|
|
1011
341
|
const sections = [];
|
|
1012
|
-
const media =
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
342
|
+
const media = execution.attachments;
|
|
343
|
+
const calls = execution.calls;
|
|
344
|
+
const receipts = execution.actions;
|
|
345
|
+
if (execution.script.status === 'failed') {
|
|
346
|
+
const { code, message } = execution.script.diagnostic;
|
|
347
|
+
sections.push(code === 'timeout'
|
|
348
|
+
? `Script timed out. ${message}`
|
|
349
|
+
: code === 'deadline'
|
|
350
|
+
? `Script exceeded its deadline. ${message}`
|
|
351
|
+
: code === 'cancellation'
|
|
352
|
+
? `Script was cancelled. ${message}`
|
|
353
|
+
: `Script failed: ${message}`);
|
|
354
|
+
}
|
|
355
|
+
else if (execution.script.voidOutput) {
|
|
1023
356
|
sections.push('Script returned no result. Return a value from the script to send it to the model.');
|
|
1024
357
|
}
|
|
1025
358
|
else {
|
|
1026
|
-
const
|
|
359
|
+
const value = execution.script.value;
|
|
360
|
+
const stripped = containsMediaContent(value) ? stripMediaContent(value, media) : value;
|
|
1027
361
|
// A top-level content-part array already has a useful text projection;
|
|
1028
362
|
// wrappers (Promise.all, object fields, etc.) retain their shape in JSON,
|
|
1029
363
|
// with media replaced by markers so base64 never leaks into text.
|
|
1030
|
-
const rendered = media.length > 0 && isContentPartArray(
|
|
1031
|
-
?
|
|
364
|
+
const rendered = media.length > 0 && isContentPartArray(value)
|
|
365
|
+
? value
|
|
1032
366
|
.filter((part) => part.type === 'text' && typeof part.text === 'string')
|
|
1033
367
|
.map((part) => part.text)
|
|
1034
368
|
.join('\n')
|
|
@@ -1038,7 +372,7 @@ function renderResult(result, output, calls, receipts, includeConsole) {
|
|
|
1038
372
|
sections.push(`Result:\n${rendered}`);
|
|
1039
373
|
}
|
|
1040
374
|
const printed = output.join('\n').trim();
|
|
1041
|
-
if (printed && (
|
|
375
|
+
if (printed && (execution.script.status === 'failed' || includeConsole))
|
|
1042
376
|
sections.push(`Console trace (debug):\n${printed}`);
|
|
1043
377
|
const refused = calls.filter((call) => call.outcome === 'approval_required');
|
|
1044
378
|
const directlyRefused = refused.filter((call) => call.directlyCallable === true);
|