@namzu/sdk 51.1.0 → 52.1.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/CHANGELOG.md +119 -0
- package/dist/agents/QueryAgent.d.ts.map +1 -1
- package/dist/agents/QueryAgent.js +2 -0
- package/dist/agents/QueryAgent.js.map +1 -1
- package/dist/agents/SupervisorAgent.d.ts +1 -1
- package/dist/agents/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/SupervisorAgent.js.map +1 -1
- package/dist/agents/examples/SupervisorAgent.d.ts +42 -0
- package/dist/agents/examples/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/examples/SupervisorAgent.js +80 -4
- package/dist/agents/examples/SupervisorAgent.js.map +1 -1
- package/dist/agents/index.d.ts +1 -1
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js.map +1 -1
- package/dist/authorization/protected-paths.d.ts +36 -0
- package/dist/authorization/protected-paths.d.ts.map +1 -0
- package/dist/authorization/protected-paths.js +107 -0
- package/dist/authorization/protected-paths.js.map +1 -0
- package/dist/authorization/readonly-commands.d.ts +39 -0
- package/dist/authorization/readonly-commands.d.ts.map +1 -0
- package/dist/authorization/readonly-commands.js +797 -0
- package/dist/authorization/readonly-commands.js.map +1 -0
- package/dist/authorization/readonly-flags.d.ts +19 -0
- package/dist/authorization/readonly-flags.d.ts.map +1 -0
- package/dist/authorization/readonly-flags.js +178 -0
- package/dist/authorization/readonly-flags.js.map +1 -0
- package/dist/authorization/removal.d.ts +52 -0
- package/dist/authorization/removal.d.ts.map +1 -0
- package/dist/authorization/removal.js +283 -0
- package/dist/authorization/removal.js.map +1 -0
- package/dist/authorization/shell-paths.d.ts +66 -0
- package/dist/authorization/shell-paths.d.ts.map +1 -0
- package/dist/authorization/shell-paths.js +697 -0
- package/dist/authorization/shell-paths.js.map +1 -0
- package/dist/bridge/acp/update.d.ts.map +1 -1
- package/dist/bridge/acp/update.js +1 -0
- package/dist/bridge/acp/update.js.map +1 -1
- package/dist/compaction/context-window.d.ts +24 -0
- package/dist/compaction/context-window.d.ts.map +1 -1
- package/dist/compaction/context-window.js +34 -0
- package/dist/compaction/context-window.js.map +1 -1
- package/dist/compaction/manual.d.ts.map +1 -1
- package/dist/compaction/manual.js +10 -2
- package/dist/compaction/manual.js.map +1 -1
- package/dist/compaction/plan.d.ts +4 -0
- package/dist/compaction/plan.d.ts.map +1 -1
- package/dist/compaction/plan.js +6 -2
- package/dist/compaction/plan.js.map +1 -1
- package/dist/compaction/verifier.d.ts +11 -1
- package/dist/compaction/verifier.d.ts.map +1 -1
- package/dist/compaction/verifier.js +75 -23
- package/dist/compaction/verifier.js.map +1 -1
- package/dist/config/runtime.d.ts +55 -7
- package/dist/config/runtime.d.ts.map +1 -1
- package/dist/config/runtime.js +23 -2
- package/dist/config/runtime.js.map +1 -1
- package/dist/connector/web/guarded-fetch.d.ts.map +1 -1
- package/dist/connector/web/guarded-fetch.js +24 -0
- package/dist/connector/web/guarded-fetch.js.map +1 -1
- package/dist/connector/web/index.d.ts +1 -1
- package/dist/connector/web/index.d.ts.map +1 -1
- package/dist/connector/web/page.d.ts +59 -0
- package/dist/connector/web/page.d.ts.map +1 -0
- package/dist/connector/web/page.js +112 -0
- package/dist/connector/web/page.js.map +1 -0
- package/dist/connector/web/types.d.ts +47 -0
- package/dist/connector/web/types.d.ts.map +1 -1
- package/dist/connector/web/types.js.map +1 -1
- package/dist/constants/continuation.d.ts +8 -0
- package/dist/constants/continuation.d.ts.map +1 -1
- package/dist/constants/continuation.js +8 -0
- package/dist/constants/continuation.js.map +1 -1
- package/dist/constants/plugin/index.d.ts +1 -1
- package/dist/constants/plugin/index.d.ts.map +1 -1
- package/dist/constants/plugin/index.js +1 -1
- package/dist/constants/plugin/index.js.map +1 -1
- package/dist/contracts/a2a.d.ts +2 -2
- package/dist/manager/agent/lifecycle.d.ts +7 -0
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +11 -2
- package/dist/manager/agent/lifecycle.js.map +1 -1
- package/dist/manager/session/turn-recorder.d.ts.map +1 -1
- package/dist/manager/session/turn-recorder.js +4 -2
- package/dist/manager/session/turn-recorder.js.map +1 -1
- package/dist/plugin/index.d.ts +1 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +1 -1
- package/dist/plugin/index.js.map +1 -1
- package/dist/plugin/lifecycle.d.ts +6 -0
- package/dist/plugin/lifecycle.d.ts.map +1 -1
- package/dist/plugin/lifecycle.js +98 -114
- package/dist/plugin/lifecycle.js.map +1 -1
- package/dist/plugin/shell-hook.d.ts +90 -22
- package/dist/plugin/shell-hook.d.ts.map +1 -1
- package/dist/plugin/shell-hook.js +434 -41
- package/dist/plugin/shell-hook.js.map +1 -1
- package/dist/provider/capabilities.d.ts +9 -0
- package/dist/provider/capabilities.d.ts.map +1 -1
- package/dist/provider/capabilities.js +15 -0
- package/dist/provider/capabilities.js.map +1 -1
- package/dist/provider/errors.d.ts.map +1 -1
- package/dist/provider/errors.js +13 -0
- package/dist/provider/errors.js.map +1 -1
- package/dist/provider/fallback.d.ts.map +1 -1
- package/dist/provider/fallback.js +10 -0
- package/dist/provider/fallback.js.map +1 -1
- package/dist/provider/idle-timeout.d.ts.map +1 -1
- package/dist/provider/idle-timeout.js +5 -0
- package/dist/provider/idle-timeout.js.map +1 -1
- package/dist/provider/image-input-refusal.d.ts +9 -0
- package/dist/provider/image-input-refusal.d.ts.map +1 -0
- package/dist/provider/image-input-refusal.js +54 -0
- package/dist/provider/image-input-refusal.js.map +1 -0
- package/dist/provider/index.d.ts +2 -1
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +2 -1
- package/dist/provider/index.js.map +1 -1
- package/dist/provider/retry.d.ts +7 -2
- package/dist/provider/retry.d.ts.map +1 -1
- package/dist/provider/retry.js +38 -4
- package/dist/provider/retry.js.map +1 -1
- package/dist/public-runtime.d.ts +5 -2
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +5 -2
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-types.d.ts +2 -2
- package/dist/public-types.d.ts.map +1 -1
- package/dist/runtime/jobs/registry.d.ts +21 -0
- package/dist/runtime/jobs/registry.d.ts.map +1 -1
- package/dist/runtime/jobs/registry.js +15 -2
- package/dist/runtime/jobs/registry.js.map +1 -1
- package/dist/runtime/query/deferred-sandbox.d.ts +54 -0
- package/dist/runtime/query/deferred-sandbox.d.ts.map +1 -0
- package/dist/runtime/query/deferred-sandbox.js +75 -0
- package/dist/runtime/query/deferred-sandbox.js.map +1 -0
- package/dist/runtime/query/executor/tool-call-admission.d.ts +10 -0
- package/dist/runtime/query/executor/tool-call-admission.d.ts.map +1 -1
- package/dist/runtime/query/executor/tool-call-admission.js +74 -13
- package/dist/runtime/query/executor/tool-call-admission.js.map +1 -1
- package/dist/runtime/query/executor.d.ts +51 -2
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +214 -9
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/finalize-turn.d.ts +6 -0
- package/dist/runtime/query/finalize-turn.d.ts.map +1 -1
- package/dist/runtime/query/finalize-turn.js +1 -1
- package/dist/runtime/query/finalize-turn.js.map +1 -1
- package/dist/runtime/query/index.d.ts +29 -0
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +108 -28
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts +57 -0
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +380 -28
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts +61 -0
- package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts.map +1 -0
- package/dist/runtime/query/iteration/phases/compaction-breaker.js +94 -0
- package/dist/runtime/query/iteration/phases/compaction-breaker.js.map +1 -0
- package/dist/runtime/query/iteration/phases/compaction.d.ts +42 -1
- package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +203 -23
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context.d.ts +10 -0
- package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/context.js.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.js +165 -2
- package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
- package/dist/runtime/query/iteration/provider-rejected-image.d.ts +21 -1
- package/dist/runtime/query/iteration/provider-rejected-image.d.ts.map +1 -1
- package/dist/runtime/query/iteration/provider-rejected-image.js +53 -7
- package/dist/runtime/query/iteration/provider-rejected-image.js.map +1 -1
- package/dist/runtime/query/iteration/request-repair.d.ts +108 -0
- package/dist/runtime/query/iteration/request-repair.d.ts.map +1 -0
- package/dist/runtime/query/iteration/request-repair.js +593 -0
- package/dist/runtime/query/iteration/request-repair.js.map +1 -0
- package/dist/runtime/query/iteration/stream-turn.d.ts +6 -0
- package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.js +2 -2
- package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
- package/dist/runtime/query/path-escalation.d.ts +36 -0
- package/dist/runtime/query/path-escalation.d.ts.map +1 -0
- package/dist/runtime/query/path-escalation.js +184 -0
- package/dist/runtime/query/path-escalation.js.map +1 -0
- package/dist/runtime/query/plugin-hooks.d.ts.map +1 -1
- package/dist/runtime/query/plugin-hooks.js +8 -0
- package/dist/runtime/query/plugin-hooks.js.map +1 -1
- package/dist/runtime/query/reminders.d.ts +35 -0
- package/dist/runtime/query/reminders.d.ts.map +1 -0
- package/dist/runtime/query/reminders.js +7 -0
- package/dist/runtime/query/reminders.js.map +1 -0
- package/dist/runtime/query/request-rich-content.d.ts +5 -1
- package/dist/runtime/query/request-rich-content.d.ts.map +1 -1
- package/dist/runtime/query/request-rich-content.js +67 -25
- package/dist/runtime/query/request-rich-content.js.map +1 -1
- package/dist/runtime/query/resume-pending.d.ts.map +1 -1
- package/dist/runtime/query/resume-pending.js +26 -0
- package/dist/runtime/query/resume-pending.js.map +1 -1
- package/dist/runtime/query/review-policy.d.ts +26 -0
- package/dist/runtime/query/review-policy.d.ts.map +1 -1
- package/dist/runtime/query/review-policy.js +79 -5
- package/dist/runtime/query/review-policy.js.map +1 -1
- package/dist/runtime/query/tool-output-budget.d.ts +46 -0
- package/dist/runtime/query/tool-output-budget.d.ts.map +1 -1
- package/dist/runtime/query/tool-output-budget.js +85 -1
- package/dist/runtime/query/tool-output-budget.js.map +1 -1
- package/dist/runtime/query/tooling.d.ts +1 -0
- package/dist/runtime/query/tooling.d.ts.map +1 -1
- package/dist/runtime/query/tooling.js +5 -0
- package/dist/runtime/query/tooling.js.map +1 -1
- package/dist/session/paths.d.ts +5 -1
- package/dist/session/paths.d.ts.map +1 -1
- package/dist/session/paths.js +8 -0
- package/dist/session/paths.js.map +1 -1
- package/dist/store/memory/disk.d.ts.map +1 -1
- package/dist/store/memory/disk.js +2 -2
- package/dist/store/memory/disk.js.map +1 -1
- package/dist/store/memory/markdown.d.ts.map +1 -1
- package/dist/store/memory/markdown.js +2 -2
- package/dist/store/memory/markdown.js.map +1 -1
- package/dist/store/memory/naming.d.ts +9 -0
- package/dist/store/memory/naming.d.ts.map +1 -1
- package/dist/store/memory/naming.js +12 -0
- package/dist/store/memory/naming.js.map +1 -1
- package/dist/tools/builtins/bash.d.ts +30 -1
- package/dist/tools/builtins/bash.d.ts.map +1 -1
- package/dist/tools/builtins/bash.js +170 -26
- package/dist/tools/builtins/bash.js.map +1 -1
- package/dist/tools/builtins/computer-use.d.ts +1 -1
- package/dist/tools/builtins/computer-use.d.ts.map +1 -1
- package/dist/tools/builtins/computer-use.js +12 -5
- package/dist/tools/builtins/computer-use.js.map +1 -1
- package/dist/tools/builtins/content-fingerprint.d.ts +2 -0
- package/dist/tools/builtins/content-fingerprint.d.ts.map +1 -1
- package/dist/tools/builtins/content-fingerprint.js +4 -0
- package/dist/tools/builtins/content-fingerprint.js.map +1 -1
- package/dist/tools/builtins/edit.d.ts.map +1 -1
- package/dist/tools/builtins/edit.js +60 -18
- package/dist/tools/builtins/edit.js.map +1 -1
- package/dist/tools/builtins/grep.d.ts +8 -1
- package/dist/tools/builtins/grep.d.ts.map +1 -1
- package/dist/tools/builtins/grep.js +152 -33
- package/dist/tools/builtins/grep.js.map +1 -1
- package/dist/tools/builtins/read-file.d.ts +1 -0
- package/dist/tools/builtins/read-file.d.ts.map +1 -1
- package/dist/tools/builtins/read-file.js +44 -5
- package/dist/tools/builtins/read-file.js.map +1 -1
- package/dist/tools/builtins/read-render.d.ts +52 -0
- package/dist/tools/builtins/read-render.d.ts.map +1 -1
- package/dist/tools/builtins/read-render.js +143 -0
- package/dist/tools/builtins/read-render.js.map +1 -1
- package/dist/tools/builtins/web.d.ts +13 -2
- package/dist/tools/builtins/web.d.ts.map +1 -1
- package/dist/tools/builtins/web.js +225 -32
- package/dist/tools/builtins/web.js.map +1 -1
- package/dist/tools/coordinator/ask-user-question.d.ts +2 -0
- package/dist/tools/coordinator/ask-user-question.d.ts.map +1 -1
- package/dist/tools/coordinator/ask-user-question.js +256 -92
- package/dist/tools/coordinator/ask-user-question.js.map +1 -1
- package/dist/tools/coordinator/question-options.d.ts +1 -0
- package/dist/tools/coordinator/question-options.d.ts.map +1 -1
- package/dist/tools/coordinator/question-options.js +6 -1
- package/dist/tools/coordinator/question-options.js.map +1 -1
- package/dist/tools/defineTool.d.ts +4 -1
- package/dist/tools/defineTool.d.ts.map +1 -1
- package/dist/tools/defineTool.js +6 -1
- package/dist/tools/defineTool.js.map +1 -1
- package/dist/tools/paths.d.ts +6 -0
- package/dist/tools/paths.d.ts.map +1 -1
- package/dist/tools/paths.js +1 -1
- package/dist/tools/paths.js.map +1 -1
- package/dist/types/agent/query.d.ts +3 -0
- package/dist/types/agent/query.d.ts.map +1 -1
- package/dist/types/errors/catalog.js +1 -1
- package/dist/types/errors/catalog.js.map +1 -1
- package/dist/types/hitl/index.d.ts +104 -0
- package/dist/types/hitl/index.d.ts.map +1 -1
- package/dist/types/hitl/index.js.map +1 -1
- package/dist/types/message/index.d.ts +1 -1
- package/dist/types/message/index.d.ts.map +1 -1
- package/dist/types/message/index.js +2 -0
- package/dist/types/message/index.js.map +1 -1
- package/dist/types/plugin/index.d.ts +93 -19
- package/dist/types/plugin/index.d.ts.map +1 -1
- package/dist/types/plugin/index.js +3 -0
- package/dist/types/plugin/index.js.map +1 -1
- package/dist/types/provider/errors.d.ts +19 -0
- package/dist/types/provider/errors.d.ts.map +1 -1
- package/dist/types/provider/errors.js +54 -5
- package/dist/types/provider/errors.js.map +1 -1
- package/dist/types/provider/interface.d.ts +12 -0
- package/dist/types/provider/interface.d.ts.map +1 -1
- package/dist/types/provider/stream.d.ts +1 -1
- package/dist/types/session/config.d.ts +14 -0
- package/dist/types/session/config.d.ts.map +1 -1
- package/dist/types/session/events.d.ts +27 -4
- package/dist/types/session/events.d.ts.map +1 -1
- package/dist/types/session/events.js.map +1 -1
- package/dist/types/session/records.d.ts +9 -9
- package/dist/types/session/records.d.ts.map +1 -1
- package/dist/types/session/records.js +1 -0
- package/dist/types/session/records.js.map +1 -1
- package/dist/types/session/stop-reason.d.ts +5 -0
- package/dist/types/session/stop-reason.d.ts.map +1 -1
- package/dist/types/tool/index.d.ts +52 -0
- package/dist/types/tool/index.d.ts.map +1 -1
- package/dist/types/tool/index.js.map +1 -1
- package/package.json +4 -2
- package/src/agents/QueryAgent.ts +5 -1
- package/src/agents/SupervisorAgent.ts +1 -1
- package/src/agents/examples/SupervisorAgent.ts +217 -106
- package/src/agents/index.ts +1 -1
- package/src/authorization/protected-paths.ts +110 -0
- package/src/authorization/readonly-commands.ts +771 -0
- package/src/authorization/readonly-flags.ts +196 -0
- package/src/authorization/removal.ts +326 -0
- package/src/authorization/shell-paths.ts +927 -0
- package/src/bridge/acp/update.ts +1 -0
- package/src/compaction/context-window.ts +41 -0
- package/src/compaction/manual.ts +10 -2
- package/src/compaction/plan.ts +11 -2
- package/src/compaction/verifier.ts +84 -22
- package/src/config/runtime.ts +23 -2
- package/src/connector/web/guarded-fetch.ts +26 -0
- package/src/connector/web/index.ts +3 -0
- package/src/connector/web/page.ts +130 -0
- package/src/connector/web/types.ts +47 -0
- package/src/constants/continuation.ts +10 -0
- package/src/constants/plugin/index.ts +1 -1
- package/src/manager/agent/lifecycle.ts +12 -4
- package/src/manager/session/turn-recorder.ts +10 -2
- package/src/plugin/index.ts +1 -0
- package/src/plugin/lifecycle.ts +107 -119
- package/src/plugin/shell-hook.ts +477 -43
- package/src/provider/capabilities.ts +17 -0
- package/src/provider/errors.ts +13 -0
- package/src/provider/fallback.ts +10 -0
- package/src/provider/idle-timeout.ts +5 -0
- package/src/provider/image-input-refusal.ts +54 -0
- package/src/provider/index.ts +2 -0
- package/src/provider/retry.ts +42 -4
- package/src/public-runtime.ts +11 -0
- package/src/public-types.ts +4 -0
- package/src/runtime/jobs/registry.ts +39 -2
- package/src/runtime/query/deferred-sandbox.ts +110 -0
- package/src/runtime/query/executor/tool-call-admission.ts +79 -13
- package/src/runtime/query/executor.ts +267 -11
- package/src/runtime/query/finalize-turn.ts +7 -1
- package/src/runtime/query/index.ts +169 -52
- package/src/runtime/query/iteration/index.ts +439 -35
- package/src/runtime/query/iteration/phases/compaction-breaker.ts +108 -0
- package/src/runtime/query/iteration/phases/compaction.ts +241 -38
- package/src/runtime/query/iteration/phases/context.ts +11 -0
- package/src/runtime/query/iteration/phases/tool-review.ts +177 -2
- package/src/runtime/query/iteration/provider-rejected-image.ts +71 -5
- package/src/runtime/query/iteration/request-repair.ts +743 -0
- package/src/runtime/query/iteration/stream-turn.ts +11 -1
- package/src/runtime/query/path-escalation.ts +204 -0
- package/src/runtime/query/plugin-hooks.ts +8 -0
- package/src/runtime/query/reminders.ts +45 -0
- package/src/runtime/query/request-rich-content.ts +77 -27
- package/src/runtime/query/resume-pending.ts +27 -0
- package/src/runtime/query/review-policy.ts +106 -5
- package/src/runtime/query/tool-output-budget.ts +115 -1
- package/src/runtime/query/tooling.ts +6 -0
- package/src/session/paths.ts +11 -1
- package/src/store/memory/disk.ts +2 -1
- package/src/store/memory/markdown.ts +2 -1
- package/src/store/memory/naming.ts +13 -0
- package/src/tools/builtins/bash.ts +203 -34
- package/src/tools/builtins/computer-use.ts +13 -4
- package/src/tools/builtins/content-fingerprint.ts +5 -0
- package/src/tools/builtins/edit.ts +69 -19
- package/src/tools/builtins/grep.ts +159 -38
- package/src/tools/builtins/read-file.ts +48 -5
- package/src/tools/builtins/read-render.ts +193 -0
- package/src/tools/builtins/web.ts +258 -33
- package/src/tools/coordinator/ask-user-question.ts +309 -103
- package/src/tools/coordinator/question-options.ts +8 -1
- package/src/tools/defineTool.ts +11 -2
- package/src/tools/paths.ts +1 -1
- package/src/types/agent/query.ts +3 -0
- package/src/types/errors/catalog.ts +1 -1
- package/src/types/hitl/index.ts +104 -0
- package/src/types/message/index.ts +2 -0
- package/src/types/plugin/index.ts +101 -18
- package/src/types/provider/errors.ts +66 -5
- package/src/types/provider/interface.ts +13 -0
- package/src/types/provider/stream.ts +1 -1
- package/src/types/session/config.ts +14 -0
- package/src/types/session/events.ts +32 -4
- package/src/types/session/records.ts +1 -0
- package/src/types/session/stop-reason.ts +5 -0
- package/src/types/tool/index.ts +53 -2
package/src/provider/errors.ts
CHANGED
|
@@ -260,6 +260,19 @@ const OVERFLOW_BODY_PATTERNS: readonly RegExp[] = [
|
|
|
260
260
|
// length" — so a pattern anchored on "exceeded" misses it entirely.
|
|
261
261
|
/exceeds?\s+(?:the\s+)?(?:model'?s?\s+)?context\s+(?:length|window|size)/i,
|
|
262
262
|
/context\s+(?:length|window|size)\s+exceed/i,
|
|
263
|
+
// The reference terminal's own list matches the bare phrase "context
|
|
264
|
+
// window". Here it must sit next to a verb that says the request did not
|
|
265
|
+
// fit ("larger than the model context window of N tokens"), so a 400 that
|
|
266
|
+
// only names the window ("does not support this parameter with the 1M
|
|
267
|
+
// context window beta") stays an invalid request. The same pattern lives in
|
|
268
|
+
// `types/provider/errors.ts` (`codeFromMessage`), which also gates it on the
|
|
269
|
+
// status; keep them equal.
|
|
270
|
+
/(?:exceed\w*|too (?:long|large|big)|larger than|longer than|over the|beyond|maximum|overflow\w*)\b[^.]{0,60}context window|context window[^.]{0,60}\b(?:exceed\w*|too (?:long|large|big)|larger than|longer than|over the|beyond|maximum|overflow\w*)/i,
|
|
271
|
+
// "input length and `max_tokens` exceed context limit: A + B > C". The
|
|
272
|
+
// prompt may fit and only the requested output not; the loop tries a smaller
|
|
273
|
+
// `max_tokens` first (readOutputOverflow) and falls back to shedding history
|
|
274
|
+
// when there is no room for a useful reply, which needs this to classify.
|
|
275
|
+
/input length and `?max_tokens`? exceed context limit/i,
|
|
263
276
|
]
|
|
264
277
|
|
|
265
278
|
/** Does this response body say the request did not fit the window? */
|
package/src/provider/fallback.ts
CHANGED
|
@@ -512,6 +512,16 @@ export function withProviderFallback(
|
|
|
512
512
|
member.provider.capabilities?.supportsHostedWebSearch === true &&
|
|
513
513
|
(member.provider.supportsHostedWebSearchFor?.(member.model ?? model, mode) ?? true),
|
|
514
514
|
),
|
|
515
|
+
// Answered for the member the next request goes to first, the one the
|
|
516
|
+
// cursor rests on (it never rewinds). Unlike the head's other
|
|
517
|
+
// declarations this costs nothing to keep current, and the head's answer
|
|
518
|
+
// would strip images from a vision member serving behind a text-only head.
|
|
519
|
+
// A request that falls over mid-call to a member that cannot read images is
|
|
520
|
+
// refused there, and the runtime sends it again without them.
|
|
521
|
+
supportsImageInputFor: (model: string) => {
|
|
522
|
+
const serving = members[cursor] ?? first
|
|
523
|
+
return serving.provider.supportsImageInputFor?.(serving.model ?? model) ?? true
|
|
524
|
+
},
|
|
515
525
|
reasoningEffortLevelsFor: (model: string, thinking?: ThinkingConfig) =>
|
|
516
526
|
commonReasoningEffortLevels(members, model, thinking),
|
|
517
527
|
// Present on the chain wrapper for the same reason the menu method is:
|
|
@@ -218,6 +218,11 @@ export function withStreamIdleTimeout(
|
|
|
218
218
|
provider.supportsHostedWebSearchFor?.(model, mode) ?? false,
|
|
219
219
|
}
|
|
220
220
|
: {}),
|
|
221
|
+
...(provider.supportsImageInputFor
|
|
222
|
+
? {
|
|
223
|
+
supportsImageInputFor: (model: string) => provider.supportsImageInputFor?.(model) ?? true,
|
|
224
|
+
}
|
|
225
|
+
: {}),
|
|
221
226
|
...(provider.reasoningEffortLevelsFor
|
|
222
227
|
? {
|
|
223
228
|
reasoningEffortLevelsFor: (
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { isProviderRequestError } from './errors.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wording providers use when they refuse a request because the MODEL reads text
|
|
5
|
+
* only. There is no shared error code for this: each vendor writes its own
|
|
6
|
+
* sentence, so the match is by phrase.
|
|
7
|
+
*
|
|
8
|
+
* Every pattern names the model's input as text-only, or says the model does not
|
|
9
|
+
* take image input at all. A 400 about one image (its size, its format, its URL)
|
|
10
|
+
* or about text somewhere else in the request (a tool message, an output
|
|
11
|
+
* format) is not this, and must not match: the caller drops every image and
|
|
12
|
+
* stops sending any to the model for the rest of the conversation.
|
|
13
|
+
*/
|
|
14
|
+
const IMAGE_INPUT_REFUSAL_PATTERNS: readonly RegExp[] = [
|
|
15
|
+
// Zen: "Beam supports text input only. This conversation contains an image."
|
|
16
|
+
/\btext\s+inputs?\s+only\b/i,
|
|
17
|
+
/\bonly\s+(?:supports?|accepts?)\s+text\s+inputs?\b/i,
|
|
18
|
+
/\bonly\s+text\s+inputs?\s+(?:is|are)\s+(?:supported|accepted|allowed)\b/i,
|
|
19
|
+
// "This model is text-only", "a text-only model"
|
|
20
|
+
/\btext[- ]only\s+model\b/i,
|
|
21
|
+
/\bmodel\b[^.;]{0,40}\bis\s+(?:a\s+)?text[- ]only\b/i,
|
|
22
|
+
// Local model servers: "<model> does not support images", "Model does not
|
|
23
|
+
// support images. Please use a model that does." Also "doesn't support vision",
|
|
24
|
+
// "does not support image input", "does not support multimodal inputs".
|
|
25
|
+
/\b(?:does\s?n[o'’]t|do\s+not|cannot|can[’']t)\s+(?:support|accept)\s+(?:images|vision)\b(?![/\w-])/i,
|
|
26
|
+
/\b(?:does\s?n[o'’]t|do\s+not|cannot|can[’']t)\s+(?:support|accept|process|handle)\s+(?:image|vision|multimodal|visual)\s+inputs?\b/i,
|
|
27
|
+
// A local inference server: "image input is not supported - hint: ... mmproj". The
|
|
28
|
+
// sentence must end there or name the model: "image input is not supported
|
|
29
|
+
// on streaming" is a restriction of the endpoint, not of the model.
|
|
30
|
+
/\b(?:image|vision|multimodal)\s+inputs?\s+(?:is|are)\s+(?:not\s+supported|unsupported)(?:\s+(?:by|for)\s+(?:this|the)\s+model)?\s*(?:$|[.;:,!)\-])/i,
|
|
31
|
+
/\bimages\s+(?:is|are)\s+not\s+supported(?:\s+(?:by|for)\s+(?:this|the)\s+model)?\s*(?:$|[.;:,!)\-])/i,
|
|
32
|
+
// A serving engine: "<model> is not a multimodal model"
|
|
33
|
+
/\bis\s+not\s+a\s+(?:multimodal|vision)\s+model\b/i,
|
|
34
|
+
// A chat-completions API: "Invalid content type. image_url is only supported by certain models."
|
|
35
|
+
/\bimage_url\b.{0,40}\bonly\s+supported\s+by\s+certain\s+models\b/i,
|
|
36
|
+
// A chat-completions server that only knows text parts: "unknown variant `image_url`, expected `text`"
|
|
37
|
+
/\bunknown\s+variant\s+\W?image_url\W?,?\s+expected\s+\W?text\b/i,
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
/** Whether a provider's refusal text says "this model cannot take images". */
|
|
41
|
+
export function isImageInputRefusalMessage(text: string | undefined): boolean {
|
|
42
|
+
if (!text) return false
|
|
43
|
+
return IMAGE_INPUT_REFUSAL_PATTERNS.some((pattern) => pattern.test(text))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Whether `error` is a provider refusing the request because of an image it
|
|
48
|
+
* holds that the model cannot read. Resending the same request can never help;
|
|
49
|
+
* only removing the image or choosing a model that reads images does.
|
|
50
|
+
*/
|
|
51
|
+
export function isImageInputRefusal(error: unknown): boolean {
|
|
52
|
+
if (!isProviderRequestError(error) || error.kind !== 'bad_request') return false
|
|
53
|
+
return isImageInputRefusalMessage(error.detail ?? error.message)
|
|
54
|
+
}
|
package/src/provider/index.ts
CHANGED
|
@@ -25,7 +25,9 @@ export { registerMock, MOCK_CAPABILITIES } from './mock-register.js'
|
|
|
25
25
|
export {
|
|
26
26
|
PERMISSIVE_PROVIDER_CAPABILITIES,
|
|
27
27
|
resolveProviderCapabilities,
|
|
28
|
+
modelReadsImages,
|
|
28
29
|
} from './capabilities.js'
|
|
30
|
+
export { isImageInputRefusal, isImageInputRefusalMessage } from './image-input-refusal.js'
|
|
29
31
|
export type { ResolvedProviderCapabilities } from './capabilities.js'
|
|
30
32
|
export {
|
|
31
33
|
ProviderError,
|
package/src/provider/retry.ts
CHANGED
|
@@ -54,7 +54,8 @@ export interface ProviderRetryConfig extends BackoffPolicy {
|
|
|
54
54
|
*/
|
|
55
55
|
readonly retryAfter?: 'exact' | 'floor'
|
|
56
56
|
/**
|
|
57
|
-
* Also retry a stream that failed AFTER it produced text
|
|
57
|
+
* Also retry a stream that failed AFTER it produced text.
|
|
58
|
+
* (A stream that produced only reasoning is always retried.)
|
|
58
59
|
*
|
|
59
60
|
* Off by default, because the decorator cannot un-emit: the consumer has
|
|
60
61
|
* already seen those deltas. When on, the retry notice carries
|
|
@@ -82,11 +83,34 @@ export interface WithProviderRetryOptions {
|
|
|
82
83
|
readonly sleepFn?: (ms: number, signal?: AbortSignal) => Promise<void>
|
|
83
84
|
}
|
|
84
85
|
|
|
86
|
+
/**
|
|
87
|
+
* A chunk that carries a fragment of a reasoning block and nothing a consumer
|
|
88
|
+
* would have to take back: no text, tool call, hosted tool, citation or finish.
|
|
89
|
+
*/
|
|
90
|
+
function isReasoningOnly(chunk: StreamChunk): boolean {
|
|
91
|
+
const d = chunk.delta
|
|
92
|
+
return (
|
|
93
|
+
d.reasoning !== undefined &&
|
|
94
|
+
!d.content &&
|
|
95
|
+
!d.toolCalls?.length &&
|
|
96
|
+
d.toolCallEnd === undefined &&
|
|
97
|
+
d.hostedTool === undefined &&
|
|
98
|
+
d.citation === undefined &&
|
|
99
|
+
d.textPart === undefined &&
|
|
100
|
+
chunk.textParts === undefined &&
|
|
101
|
+
chunk.finishReason === undefined
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
85
105
|
/**
|
|
86
106
|
* Wrap a provider so transient failures are retried with exponential
|
|
87
107
|
* backoff and full jitter.
|
|
88
108
|
*
|
|
89
|
-
* **Retry only before the first content chunk.**
|
|
109
|
+
* **Retry only before the first content chunk.** Reasoning-only chunks do
|
|
110
|
+
* not count as content: a stream that fails after yielding nothing but
|
|
111
|
+
* reasoning is retried, and the notice carries `restart: true` so the consumer
|
|
112
|
+
* drops the thinking the failed attempt showed (the turn replaces reasoning
|
|
113
|
+
* blocks by index, and clears them on a restart). Once a text or tool-call delta has been
|
|
90
114
|
* yielded the consumer has already emitted `text_delta` events and
|
|
91
115
|
* appended to its buffer, so restarting the request would duplicate
|
|
92
116
|
* output. A mid-stream failure is therefore surfaced, not retried — the
|
|
@@ -129,6 +153,11 @@ export function withProviderRetry(
|
|
|
129
153
|
async function* chatStream(params: ChatCompletionParams): AsyncIterable<StreamChunk> {
|
|
130
154
|
for (let attempt = 0; ; attempt++) {
|
|
131
155
|
let produced = false
|
|
156
|
+
// Reasoning has streamed and nothing else has. The consumer holds
|
|
157
|
+
// reasoning by block index and has no text or tool call to repeat, so
|
|
158
|
+
// this is retryable without `retryMidStream`; the notice says
|
|
159
|
+
// `restart` so a host that showed the thinking drops it.
|
|
160
|
+
let reasoned = false
|
|
132
161
|
// A tool call or hosted tool began: past this point a restart could
|
|
133
162
|
// repeat work that is already underway, so it is never offered.
|
|
134
163
|
let acted = false
|
|
@@ -140,7 +169,10 @@ export function withProviderRetry(
|
|
|
140
169
|
const isErrorOnly =
|
|
141
170
|
chunk.error !== undefined && !chunk.delta.content && !chunk.delta.toolCalls?.length
|
|
142
171
|
|
|
143
|
-
if (!isErrorOnly)
|
|
172
|
+
if (!isErrorOnly) {
|
|
173
|
+
if (isReasoningOnly(chunk)) reasoned = true
|
|
174
|
+
else produced = true
|
|
175
|
+
}
|
|
144
176
|
if (chunk.delta.toolCalls?.length || chunk.delta.toolCallEnd || chunk.delta.hostedTool) {
|
|
145
177
|
acted = true
|
|
146
178
|
}
|
|
@@ -170,7 +202,8 @@ export function withProviderRetry(
|
|
|
170
202
|
const classified = classifyProviderError(err, provider.id)
|
|
171
203
|
const exhausted = attempt >= config.maxRetries
|
|
172
204
|
|
|
173
|
-
const restartable =
|
|
205
|
+
const restartable =
|
|
206
|
+
(produced && config.retryMidStream === true && !acted) || (!produced && reasoned)
|
|
174
207
|
if ((produced && !restartable) || !classified.retryable || exhausted) {
|
|
175
208
|
log?.warn('Provider call failed', {
|
|
176
209
|
[GENAI.SYSTEM]: provider.id,
|
|
@@ -326,6 +359,11 @@ export function withProviderRetry(
|
|
|
326
359
|
provider.supportsHostedWebSearchFor?.(model, mode) ?? false,
|
|
327
360
|
}
|
|
328
361
|
: {}),
|
|
362
|
+
...(provider.supportsImageInputFor
|
|
363
|
+
? {
|
|
364
|
+
supportsImageInputFor: (model: string) => provider.supportsImageInputFor?.(model) ?? true,
|
|
365
|
+
}
|
|
366
|
+
: {}),
|
|
329
367
|
...(provider.reasoningEffortLevelsFor
|
|
330
368
|
? {
|
|
331
369
|
reasoningEffortLevelsFor: (
|
package/src/public-runtime.ts
CHANGED
|
@@ -318,6 +318,12 @@ export {
|
|
|
318
318
|
formatSteeringNote,
|
|
319
319
|
} from './runtime/query/steering.js'
|
|
320
320
|
export type { SteeringChannel } from './runtime/query/steering.js'
|
|
321
|
+
// Hidden mid-conversation reminders a host feeds per model request (opt-in).
|
|
322
|
+
export { isReminderMessage } from './runtime/query/reminders.js'
|
|
323
|
+
export type {
|
|
324
|
+
ReminderContext,
|
|
325
|
+
ReminderSource,
|
|
326
|
+
} from './runtime/query/reminders.js'
|
|
321
327
|
export {
|
|
322
328
|
BidiSessionCloseTimeoutError,
|
|
323
329
|
createMockBidiProvider,
|
|
@@ -628,6 +634,7 @@ export {
|
|
|
628
634
|
runShellHook,
|
|
629
635
|
SHELL_HOOK_EVENTS,
|
|
630
636
|
SHELL_HOOKS_PLUGIN_ID,
|
|
637
|
+
shellHookIfMatches,
|
|
631
638
|
shellHookMatches,
|
|
632
639
|
shellHookVerdict,
|
|
633
640
|
} from './plugin/index.js'
|
|
@@ -718,8 +725,11 @@ export {
|
|
|
718
725
|
isProviderError,
|
|
719
726
|
LazyProviderLoadError,
|
|
720
727
|
LazyProviderSyncCreateError,
|
|
728
|
+
isImageInputRefusal,
|
|
729
|
+
isImageInputRefusalMessage,
|
|
721
730
|
MOCK_CAPABILITIES,
|
|
722
731
|
MockLLMProvider,
|
|
732
|
+
modelReadsImages,
|
|
723
733
|
parseRetryAfterMs,
|
|
724
734
|
PERMISSIVE_PROVIDER_CAPABILITIES,
|
|
725
735
|
ProviderError,
|
|
@@ -1505,6 +1515,7 @@ export {
|
|
|
1505
1515
|
// consumer (`SpanProcessorLike`, then `CompactNowInput` and `CompactionResult`).
|
|
1506
1516
|
// The gate is the reason this is a list rather than a fourth accident.
|
|
1507
1517
|
export type { ResolvedContextWindow } from './compaction/context-window.js'
|
|
1518
|
+
export { resolveTriggerThreshold } from './compaction/context-window.js'
|
|
1508
1519
|
export type { CompactRegionInput } from './compaction/manual.js'
|
|
1509
1520
|
export type {
|
|
1510
1521
|
CompactionVerificationOptions,
|
package/src/public-types.ts
CHANGED
|
@@ -204,6 +204,7 @@ export type {
|
|
|
204
204
|
Disposable,
|
|
205
205
|
RunAgentOptions,
|
|
206
206
|
RunAgentResult,
|
|
207
|
+
SupervisorResumeTarget,
|
|
207
208
|
} from './agents/index.js'
|
|
208
209
|
|
|
209
210
|
export type {
|
|
@@ -457,7 +458,10 @@ export type {
|
|
|
457
458
|
|
|
458
459
|
export type {
|
|
459
460
|
GuardedFetchConfig,
|
|
461
|
+
WebContentExtractor,
|
|
462
|
+
WebExtractRequest,
|
|
460
463
|
WebFetchProvider,
|
|
464
|
+
WebFetchRedirectPolicy,
|
|
461
465
|
WebFetchRefusalReason,
|
|
462
466
|
WebFetchRequest,
|
|
463
467
|
WebFetchResult,
|
|
@@ -99,6 +99,21 @@ export interface StartJobParams {
|
|
|
99
99
|
* expect stdin.
|
|
100
100
|
*/
|
|
101
101
|
readonly spawn?: () => JobProcess
|
|
102
|
+
/**
|
|
103
|
+
* Output the process already wrote before the registry took it over, in
|
|
104
|
+
* the order it was written. Used when a foreground command is moved to the
|
|
105
|
+
* background, so a later `read` starts with what the model was shown.
|
|
106
|
+
*/
|
|
107
|
+
readonly initialOutput?: readonly {
|
|
108
|
+
readonly stream: 'stdout' | 'stderr'
|
|
109
|
+
readonly text: string
|
|
110
|
+
}[]
|
|
111
|
+
/**
|
|
112
|
+
* `spawn` returned a process on THIS host that the host shell started (a
|
|
113
|
+
* foreground command being moved to the background), so its bytes are
|
|
114
|
+
* decoded the way a foreground command's are, not as a Linux guest's.
|
|
115
|
+
*/
|
|
116
|
+
readonly hostOutput?: boolean
|
|
102
117
|
}
|
|
103
118
|
|
|
104
119
|
export interface BackgroundJobRegistryConfig {
|
|
@@ -333,8 +348,12 @@ export class BackgroundJobRegistry {
|
|
|
333
348
|
// Raw bytes, decoded here: a Windows console writes its OEM code page.
|
|
334
349
|
// A process a sandbox started runs a Linux guest, so it is UTF-8.
|
|
335
350
|
const decoders = {
|
|
336
|
-
stdout: createHostOutputDecoder(
|
|
337
|
-
|
|
351
|
+
stdout: createHostOutputDecoder(
|
|
352
|
+
params.spawn && !params.hostOutput ? { platform: 'linux' } : {},
|
|
353
|
+
),
|
|
354
|
+
stderr: createHostOutputDecoder(
|
|
355
|
+
params.spawn && !params.hostOutput ? { platform: 'linux' } : {},
|
|
356
|
+
),
|
|
338
357
|
}
|
|
339
358
|
let startError: string | undefined
|
|
340
359
|
let settleStarted!: () => void
|
|
@@ -473,6 +492,7 @@ export class BackgroundJobRegistry {
|
|
|
473
492
|
const text = typeof data === 'string' ? data : decoders[stream].write(data)
|
|
474
493
|
if (text) append(text, stream)
|
|
475
494
|
}
|
|
495
|
+
for (const seed of params.initialOutput ?? []) append(seed.text, seed.stream)
|
|
476
496
|
child.stdout?.on('data', onData('stdout'))
|
|
477
497
|
child.stderr?.on('data', onData('stderr'))
|
|
478
498
|
|
|
@@ -855,6 +875,23 @@ export function bindOwner(
|
|
|
855
875
|
: {}),
|
|
856
876
|
})
|
|
857
877
|
},
|
|
878
|
+
// A foreground command that ran out of time, kept instead of killed.
|
|
879
|
+
// The same owner and cap as `start`; the process is the one already
|
|
880
|
+
// running, with what it has written so far.
|
|
881
|
+
adopt: (params: {
|
|
882
|
+
command: string
|
|
883
|
+
child: ReturnType<typeof spawn>
|
|
884
|
+
workingDirectory?: string
|
|
885
|
+
initialOutput?: StartJobParams['initialOutput']
|
|
886
|
+
}) =>
|
|
887
|
+
registry.start({
|
|
888
|
+
owner,
|
|
889
|
+
command: params.command,
|
|
890
|
+
workingDirectory: params.workingDirectory ?? defaults.workingDirectory ?? process.cwd(),
|
|
891
|
+
spawn: () => ({ child: params.child }),
|
|
892
|
+
hostOutput: true,
|
|
893
|
+
...(params.initialOutput ? { initialOutput: params.initialOutput } : {}),
|
|
894
|
+
}),
|
|
858
895
|
get: (id: string) => mine(id),
|
|
859
896
|
awaitStarted: (id: string) => {
|
|
860
897
|
mine(id)
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { Sandbox } from '../../types/sandbox/index.js'
|
|
2
|
+
import type { SandboxAcquisitionResult } from './sandbox-lifecycle.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* When a turn starts acquiring its sandbox.
|
|
6
|
+
*
|
|
7
|
+
* - `'eager'`: before the input guardrails and the first model request, and the
|
|
8
|
+
* turn waits for it. This is the behaviour every sandboxed turn had before
|
|
9
|
+
* the option existed.
|
|
10
|
+
* - `'concurrent'`: at the same point, but the turn does not wait. The first
|
|
11
|
+
* model request goes out while the provider allocates, and the first tool
|
|
12
|
+
* call waits for the handle.
|
|
13
|
+
* - `'on-first-use'`: not until the first tool call needs one.
|
|
14
|
+
*/
|
|
15
|
+
export type SandboxAcquireMode = 'eager' | 'concurrent' | 'on-first-use'
|
|
16
|
+
|
|
17
|
+
export type SandboxAcquisitionStop = Exclude<SandboxAcquisitionResult, { kind: 'created' }>
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A deferred acquisition ended without a sandbox for a reason the turn settles
|
|
21
|
+
* with a stop reason (`cancelled`, `timeout`), exactly as an eager acquisition
|
|
22
|
+
* does. A provider failure is not wrapped: it propagates as the provider's own
|
|
23
|
+
* error, as it does when the turn waits.
|
|
24
|
+
*/
|
|
25
|
+
export class SandboxAcquisitionStopped extends Error {
|
|
26
|
+
readonly acquisition: SandboxAcquisitionStop
|
|
27
|
+
|
|
28
|
+
constructor(acquisition: SandboxAcquisitionStop) {
|
|
29
|
+
super(
|
|
30
|
+
acquisition.kind === 'timed_out'
|
|
31
|
+
? acquisition.error.message
|
|
32
|
+
: 'Sandbox acquisition was cancelled before it finished',
|
|
33
|
+
)
|
|
34
|
+
this.name = 'SandboxAcquisitionStopped'
|
|
35
|
+
this.acquisition = acquisition
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type Outcome = SandboxAcquisitionResult | { readonly kind: 'failed'; readonly error: unknown }
|
|
40
|
+
|
|
41
|
+
export interface DeferredSandbox {
|
|
42
|
+
/** Start acquiring if nothing has yet. Idempotent. */
|
|
43
|
+
start(): void
|
|
44
|
+
/** Start if needed, then wait for the handle to be published. Throws when none will be. */
|
|
45
|
+
ensure(): Promise<void>
|
|
46
|
+
/**
|
|
47
|
+
* Wait for an acquisition that was started. A no-op when none was, and when
|
|
48
|
+
* the turn has already been cancelled (the loop settles that on its own).
|
|
49
|
+
*/
|
|
50
|
+
settle(): Promise<void>
|
|
51
|
+
/** Withdraw authority from a still-pending acquisition and wait until it has settled. Never throws. */
|
|
52
|
+
cancel(): Promise<void>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* A sandbox acquisition that outlives the point that started it.
|
|
57
|
+
*
|
|
58
|
+
* `acquire` is the same lifecycle function an eager turn uses, so a late
|
|
59
|
+
* handle, a timeout and a cancellation are released by the machinery that
|
|
60
|
+
* already owns them. `publish` runs once, when (and only when) the handle
|
|
61
|
+
* arrived in time, so `sandbox_created` marks the moment of resolution.
|
|
62
|
+
*/
|
|
63
|
+
export function createDeferredSandbox(options: {
|
|
64
|
+
readonly turnSignal: AbortSignal
|
|
65
|
+
readonly acquire: (signal: AbortSignal) => Promise<SandboxAcquisitionResult>
|
|
66
|
+
readonly publish: (sandbox: Sandbox) => Promise<void>
|
|
67
|
+
}): DeferredSandbox {
|
|
68
|
+
const controller = new AbortController()
|
|
69
|
+
const relay = (): void => controller.abort(options.turnSignal.reason)
|
|
70
|
+
if (options.turnSignal.aborted) relay()
|
|
71
|
+
else options.turnSignal.addEventListener('abort', relay, { once: true })
|
|
72
|
+
|
|
73
|
+
let outcome: Promise<Outcome> | undefined
|
|
74
|
+
const start = (): Promise<Outcome> => {
|
|
75
|
+
outcome ??= (async (): Promise<Outcome> => {
|
|
76
|
+
try {
|
|
77
|
+
const acquired = await options.acquire(controller.signal)
|
|
78
|
+
if (acquired.kind === 'created') await options.publish(acquired.sandbox)
|
|
79
|
+
return acquired
|
|
80
|
+
} catch (error) {
|
|
81
|
+
return { kind: 'failed', error }
|
|
82
|
+
}
|
|
83
|
+
})()
|
|
84
|
+
return outcome
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const rethrow = (settled: Outcome): void => {
|
|
88
|
+
if (settled.kind === 'created') return
|
|
89
|
+
if (settled.kind === 'failed') throw settled.error
|
|
90
|
+
throw new SandboxAcquisitionStopped(settled)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
start: () => {
|
|
95
|
+
void start()
|
|
96
|
+
},
|
|
97
|
+
ensure: async () => rethrow(await start()),
|
|
98
|
+
settle: async () => {
|
|
99
|
+
if (outcome === undefined) return
|
|
100
|
+
const settled = await outcome
|
|
101
|
+
if (options.turnSignal.aborted) return
|
|
102
|
+
rethrow(settled)
|
|
103
|
+
},
|
|
104
|
+
cancel: async () => {
|
|
105
|
+
options.turnSignal.removeEventListener('abort', relay)
|
|
106
|
+
controller.abort()
|
|
107
|
+
if (outcome !== undefined) await outcome
|
|
108
|
+
},
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -2,7 +2,7 @@ import { GENAI, NAMZU } from '../../../constants/telemetry/index.js'
|
|
|
2
2
|
import { callableToolNames, formatToolNames } from '../../../registry/tool/callable.js'
|
|
3
3
|
import { renderToolSchema } from '../../../registry/tool/schema.js'
|
|
4
4
|
import type { ToolCall, ToolInputError } from '../../../types/message/index.js'
|
|
5
|
-
import type { PluginHookResult } from '../../../types/plugin/index.js'
|
|
5
|
+
import type { PluginHookPermission, PluginHookResult } from '../../../types/plugin/index.js'
|
|
6
6
|
import type { ToolDefinition } from '../../../types/tool/index.js'
|
|
7
7
|
import type { ToolCallRepair, ToolCallRepairReason } from '../../../types/tool/repair.js'
|
|
8
8
|
import { awaitWithAbort } from '../../../utils/await-with-abort.js'
|
|
@@ -10,6 +10,7 @@ import { toErrorMessage } from '../../../utils/error.js'
|
|
|
10
10
|
import type { Logger } from '../../../utils/logger.js'
|
|
11
11
|
import type {
|
|
12
12
|
EmitEvent,
|
|
13
|
+
PreToolHookNotes,
|
|
13
14
|
PreToolHookOutcome,
|
|
14
15
|
PreparedDirectCall,
|
|
15
16
|
ToolExecutorConfig,
|
|
@@ -49,6 +50,11 @@ export interface ToolAdmissionHost {
|
|
|
49
50
|
* step, which is the answer that sent a model round in a circle.
|
|
50
51
|
*/
|
|
51
52
|
readonly allowedTools: readonly string[] | undefined
|
|
53
|
+
/**
|
|
54
|
+
* A hook asked for the turn to stop after the current step. Optional so a
|
|
55
|
+
* host that builds an admission by hand (a test) need not say anything.
|
|
56
|
+
*/
|
|
57
|
+
readonly requestHalt?: (reason: string) => void
|
|
52
58
|
}
|
|
53
59
|
|
|
54
60
|
/**
|
|
@@ -89,7 +95,7 @@ export async function runPreToolHook(
|
|
|
89
95
|
signal,
|
|
90
96
|
)
|
|
91
97
|
signal.throwIfAborted()
|
|
92
|
-
return interpretPreToolResults(toolName, input, results)
|
|
98
|
+
return interpretPreToolResults(toolName, input, results, host.requestHalt)
|
|
93
99
|
}
|
|
94
100
|
|
|
95
101
|
export async function prepareDirectCall(
|
|
@@ -169,6 +175,7 @@ export async function prepareDirectCall(
|
|
|
169
175
|
...(providerInput && !truncationRepair && !preOutcome.modified && resolved.inputFailure
|
|
170
176
|
? { inputFailure: resolved.inputFailure }
|
|
171
177
|
: {}),
|
|
178
|
+
...hookNotesOf(preOutcome),
|
|
172
179
|
}
|
|
173
180
|
}
|
|
174
181
|
return {
|
|
@@ -321,6 +328,20 @@ export async function prepareDirectCall(
|
|
|
321
328
|
toolName,
|
|
322
329
|
input: preparation.prepared.input,
|
|
323
330
|
prepared: preparation.prepared,
|
|
331
|
+
...hookNotesOf(preOutcome),
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** The hook answers a ready or legacy call carries forward, or nothing. */
|
|
336
|
+
function hookNotesOf(outcome: Extract<PreToolHookOutcome, { kind: 'continue' }>): {
|
|
337
|
+
hook?: PreToolHookNotes
|
|
338
|
+
} {
|
|
339
|
+
if (outcome.permission === undefined && outcome.context === undefined) return {}
|
|
340
|
+
return {
|
|
341
|
+
hook: {
|
|
342
|
+
...(outcome.permission ? { permission: outcome.permission } : {}),
|
|
343
|
+
...(outcome.context !== undefined ? { context: outcome.context } : {}),
|
|
344
|
+
},
|
|
324
345
|
}
|
|
325
346
|
}
|
|
326
347
|
|
|
@@ -328,29 +349,49 @@ function interpretPreToolResults(
|
|
|
328
349
|
toolName: string,
|
|
329
350
|
initialInput: unknown,
|
|
330
351
|
results: readonly PluginHookResult[],
|
|
352
|
+
requestHalt?: (reason: string) => void,
|
|
331
353
|
): PreToolHookOutcome {
|
|
332
354
|
let currentInput = initialInput
|
|
333
355
|
let modified = false
|
|
356
|
+
let permission: PluginHookPermission | undefined
|
|
357
|
+
const contexts: string[] = []
|
|
358
|
+
/** `ask` outranks `allow`: the stricter answer is the one that stands. */
|
|
359
|
+
const takePermission = (next: PluginHookPermission | undefined) => {
|
|
360
|
+
if (next === undefined) return
|
|
361
|
+
if (permission === undefined || (permission.decision === 'allow' && next.decision === 'ask')) {
|
|
362
|
+
permission = next
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
const withContext = (text: string): string =>
|
|
366
|
+
contexts.length === 0 ? text : `${text}\n\n${hookNote(contexts)}`
|
|
367
|
+
// The hooks ran side by side on the original input, and their answers are
|
|
368
|
+
// combined here. The first refusal (or failure) in registration order is the
|
|
369
|
+
// answer, whatever the others said; of several rewrites the last wins;
|
|
370
|
+
// `ask` outranks `allow`; every context is kept; every request to stop is
|
|
371
|
+
// passed on.
|
|
372
|
+
let refusal: { kind: 'skip' | 'error'; text: string } | undefined
|
|
334
373
|
for (const result of results) {
|
|
335
374
|
switch (result.action) {
|
|
336
375
|
case 'continue':
|
|
376
|
+
takePermission(result.permission)
|
|
377
|
+
if (result.context) contexts.push(result.context)
|
|
378
|
+
if (result.halt) requestHalt?.(result.halt.reason)
|
|
337
379
|
continue
|
|
338
380
|
case 'modify':
|
|
339
381
|
currentInput = result.input
|
|
340
382
|
modified = true
|
|
383
|
+
takePermission(result.permission)
|
|
384
|
+
if (result.context) contexts.push(result.context)
|
|
385
|
+
if (result.halt) requestHalt?.(result.halt.reason)
|
|
341
386
|
continue
|
|
342
387
|
case 'skip':
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
388
|
+
if (result.context) contexts.push(result.context)
|
|
389
|
+
if (result.halt) requestHalt?.(result.halt.reason)
|
|
390
|
+
refusal ??= { kind: 'skip', text: skippedToolResultText(toolName, result.reason) }
|
|
391
|
+
continue
|
|
348
392
|
case 'error':
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
input: currentInput,
|
|
352
|
-
output: `Error: ${result.message}`,
|
|
353
|
-
}
|
|
393
|
+
refusal ??= { kind: 'error', text: `Error: ${result.message}` }
|
|
394
|
+
continue
|
|
354
395
|
case 'retry':
|
|
355
396
|
case 'annotate':
|
|
356
397
|
// There is no result to replace yet. Rejecting loudly beats
|
|
@@ -367,9 +408,34 @@ function interpretPreToolResults(
|
|
|
367
408
|
}
|
|
368
409
|
}
|
|
369
410
|
}
|
|
370
|
-
|
|
411
|
+
if (refusal) {
|
|
412
|
+
return {
|
|
413
|
+
kind: refusal.kind,
|
|
414
|
+
input: currentInput,
|
|
415
|
+
output: refusal.kind === 'skip' ? withContext(refusal.text) : refusal.text,
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return {
|
|
419
|
+
kind: 'continue',
|
|
420
|
+
input: currentInput,
|
|
421
|
+
modified,
|
|
422
|
+
...(permission ? { permission } : {}),
|
|
423
|
+
...(contexts.length > 0 ? { context: contexts.join('\n') } : {}),
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Hook text, framed so the model reads it as what a configured hook said and
|
|
429
|
+
* not as something the tool returned.
|
|
430
|
+
*/
|
|
431
|
+
export function hookNote(texts: readonly string[]): string {
|
|
432
|
+
const joined = texts.join('\n')
|
|
433
|
+
return `[Hook note]\n${joined.length > MAX_HOOK_NOTE_CHARS ? `${joined.slice(0, MAX_HOOK_NOTE_CHARS)}\n[truncated]` : joined}`
|
|
371
434
|
}
|
|
372
435
|
|
|
436
|
+
/** A hook's text is one more thing for the model to read; it gets a bound. */
|
|
437
|
+
const MAX_HOOK_NOTE_CHARS = 8_000
|
|
438
|
+
|
|
373
439
|
/**
|
|
374
440
|
* Turn the call the model issued into a name and a parsed input, giving
|
|
375
441
|
* a configured repairer one chance to fix it first.
|