@namzu/sdk 1.4.0 → 3.0.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 +2284 -0
- package/README.md +49 -53
- package/dist/__tests__/default-path-conformance.test.d.ts +2 -0
- package/dist/__tests__/default-path-conformance.test.d.ts.map +1 -0
- package/dist/__tests__/default-path-conformance.test.js +262 -0
- package/dist/__tests__/default-path-conformance.test.js.map +1 -0
- package/dist/__tests__/shipped-defaults.test.d.ts +2 -0
- package/dist/__tests__/shipped-defaults.test.d.ts.map +1 -0
- package/dist/__tests__/shipped-defaults.test.js +97 -0
- package/dist/__tests__/shipped-defaults.test.js.map +1 -0
- package/dist/advisory/__tests__/budget-enforcement.test.d.ts +2 -0
- package/dist/advisory/__tests__/budget-enforcement.test.d.ts.map +1 -0
- package/dist/advisory/__tests__/budget-enforcement.test.js +159 -0
- package/dist/advisory/__tests__/budget-enforcement.test.js.map +1 -0
- package/dist/advisory/__tests__/structured-response.test.d.ts +2 -0
- package/dist/advisory/__tests__/structured-response.test.d.ts.map +1 -0
- package/dist/advisory/__tests__/structured-response.test.js +99 -0
- package/dist/advisory/__tests__/structured-response.test.js.map +1 -0
- package/dist/advisory/budget.d.ts +15 -0
- package/dist/advisory/budget.d.ts.map +1 -0
- package/dist/advisory/budget.js +21 -0
- package/dist/advisory/budget.js.map +1 -0
- package/dist/advisory/context.d.ts +2 -0
- package/dist/advisory/context.d.ts.map +1 -1
- package/dist/advisory/context.js +16 -0
- package/dist/advisory/context.js.map +1 -1
- package/dist/advisory/executor.d.ts +14 -11
- package/dist/advisory/executor.d.ts.map +1 -1
- package/dist/advisory/executor.js +36 -24
- package/dist/advisory/executor.js.map +1 -1
- package/dist/advisory/executor.test.d.ts +8 -6
- package/dist/advisory/executor.test.d.ts.map +1 -1
- package/dist/advisory/executor.test.js +12 -8
- package/dist/advisory/executor.test.js.map +1 -1
- package/dist/advisory/index.d.ts +2 -0
- package/dist/advisory/index.d.ts.map +1 -1
- package/dist/advisory/index.js +2 -0
- package/dist/advisory/index.js.map +1 -1
- package/dist/advisory/parse.d.ts +23 -0
- package/dist/advisory/parse.d.ts.map +1 -0
- package/dist/advisory/parse.js +56 -0
- package/dist/advisory/parse.js.map +1 -0
- package/dist/agents/AbstractAgent.d.ts +48 -0
- package/dist/agents/AbstractAgent.d.ts.map +1 -1
- package/dist/agents/AbstractAgent.js +77 -0
- package/dist/agents/AbstractAgent.js.map +1 -1
- package/dist/agents/PipelineAgent.d.ts +10 -0
- package/dist/agents/PipelineAgent.d.ts.map +1 -1
- package/dist/agents/PipelineAgent.js +12 -0
- package/dist/agents/PipelineAgent.js.map +1 -1
- package/dist/agents/ReactiveAgent.d.ts +10 -0
- package/dist/agents/ReactiveAgent.d.ts.map +1 -1
- package/dist/agents/ReactiveAgent.js +40 -0
- package/dist/agents/ReactiveAgent.js.map +1 -1
- package/dist/agents/RouterAgent.d.ts +10 -0
- package/dist/agents/RouterAgent.d.ts.map +1 -1
- package/dist/agents/RouterAgent.js +23 -2
- package/dist/agents/RouterAgent.js.map +1 -1
- package/dist/agents/SupervisorAgent.d.ts +11 -1
- package/dist/agents/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/SupervisorAgent.js +23 -1
- package/dist/agents/SupervisorAgent.js.map +1 -1
- package/dist/agents/__tests__/idempotency-key.test.d.ts +2 -0
- package/dist/agents/__tests__/idempotency-key.test.d.ts.map +1 -0
- package/dist/agents/__tests__/idempotency-key.test.js +149 -0
- package/dist/agents/__tests__/idempotency-key.test.js.map +1 -0
- package/dist/agents/__tests__/invocation-lock.test.d.ts +2 -0
- package/dist/agents/__tests__/invocation-lock.test.d.ts.map +1 -0
- package/dist/agents/__tests__/invocation-lock.test.js +126 -0
- package/dist/agents/__tests__/invocation-lock.test.js.map +1 -0
- package/dist/agents/__tests__/reachability.test.d.ts +2 -0
- package/dist/agents/__tests__/reachability.test.d.ts.map +1 -0
- package/dist/agents/__tests__/reachability.test.js +173 -0
- package/dist/agents/__tests__/reachability.test.js.map +1 -0
- package/dist/agents/__tests__/supervisor-ledger.test.js +1 -1
- package/dist/agents/__tests__/supervisor-ledger.test.js.map +1 -1
- package/dist/agents/index.d.ts +1 -1
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/lock.d.ts +0 -1
- package/dist/agents/lock.d.ts.map +1 -1
- package/dist/agents/lock.js.map +1 -1
- package/dist/bridge/a2a/late-events.test.d.ts +2 -0
- package/dist/bridge/a2a/late-events.test.d.ts.map +1 -0
- package/dist/bridge/a2a/late-events.test.js +70 -0
- package/dist/bridge/a2a/late-events.test.js.map +1 -0
- package/dist/bridge/a2a/mapper.d.ts.map +1 -1
- package/dist/bridge/a2a/mapper.js +55 -3
- package/dist/bridge/a2a/mapper.js.map +1 -1
- package/dist/bridge/a2a/mapper.test.js +1 -1
- package/dist/bridge/a2a/mapper.test.js.map +1 -1
- package/dist/bridge/a2a/message.d.ts.map +1 -1
- package/dist/bridge/a2a/message.js +5 -1
- package/dist/bridge/a2a/message.js.map +1 -1
- package/dist/bridge/sse/late-events.test.d.ts +2 -0
- package/dist/bridge/sse/late-events.test.d.ts.map +1 -0
- package/dist/bridge/sse/late-events.test.js +163 -0
- package/dist/bridge/sse/late-events.test.js.map +1 -0
- package/dist/bridge/sse/mapper.d.ts.map +1 -1
- package/dist/bridge/sse/mapper.js +100 -0
- package/dist/bridge/sse/mapper.js.map +1 -1
- package/dist/compaction/__tests__/cleared-result-recovery.test.d.ts +2 -0
- package/dist/compaction/__tests__/cleared-result-recovery.test.d.ts.map +1 -0
- package/dist/compaction/__tests__/cleared-result-recovery.test.js +101 -0
- package/dist/compaction/__tests__/cleared-result-recovery.test.js.map +1 -0
- package/dist/compaction/__tests__/context-window.test.d.ts +2 -0
- package/dist/compaction/__tests__/context-window.test.d.ts.map +1 -0
- package/dist/compaction/__tests__/context-window.test.js +62 -0
- package/dist/compaction/__tests__/context-window.test.js.map +1 -0
- package/dist/compaction/__tests__/dangling.test.js +16 -5
- package/dist/compaction/__tests__/dangling.test.js.map +1 -1
- package/dist/compaction/__tests__/lossiness.test.d.ts +2 -0
- package/dist/compaction/__tests__/lossiness.test.d.ts.map +1 -0
- package/dist/compaction/__tests__/lossiness.test.js +163 -0
- package/dist/compaction/__tests__/lossiness.test.js.map +1 -0
- package/dist/compaction/__tests__/state-across-resume.test.d.ts +2 -0
- package/dist/compaction/__tests__/state-across-resume.test.d.ts.map +1 -0
- package/dist/compaction/__tests__/state-across-resume.test.js +88 -0
- package/dist/compaction/__tests__/state-across-resume.test.js.map +1 -0
- package/dist/compaction/__tests__/tool-result-editing.test.d.ts +2 -0
- package/dist/compaction/__tests__/tool-result-editing.test.d.ts.map +1 -0
- package/dist/compaction/__tests__/tool-result-editing.test.js +184 -0
- package/dist/compaction/__tests__/tool-result-editing.test.js.map +1 -0
- package/dist/compaction/__tests__/trim-opens-on-user.test.d.ts +2 -0
- package/dist/compaction/__tests__/trim-opens-on-user.test.d.ts.map +1 -0
- package/dist/compaction/__tests__/trim-opens-on-user.test.js +109 -0
- package/dist/compaction/__tests__/trim-opens-on-user.test.js.map +1 -0
- package/dist/compaction/__tests__/verifier-model.test.d.ts +2 -0
- package/dist/compaction/__tests__/verifier-model.test.d.ts.map +1 -0
- package/dist/compaction/__tests__/verifier-model.test.js +58 -0
- package/dist/compaction/__tests__/verifier-model.test.js.map +1 -0
- package/dist/compaction/context-window.d.ts +44 -0
- package/dist/compaction/context-window.d.ts.map +1 -0
- package/dist/compaction/context-window.js +97 -0
- package/dist/compaction/context-window.js.map +1 -0
- package/dist/compaction/dangling.js +48 -1
- package/dist/compaction/dangling.js.map +1 -1
- package/dist/compaction/extractor.d.ts.map +1 -1
- package/dist/compaction/extractor.js +55 -1
- package/dist/compaction/extractor.js.map +1 -1
- package/dist/compaction/index.d.ts +5 -0
- package/dist/compaction/index.d.ts.map +1 -1
- package/dist/compaction/index.js +3 -0
- package/dist/compaction/index.js.map +1 -1
- package/dist/compaction/manager.d.ts +25 -0
- package/dist/compaction/manager.d.ts.map +1 -1
- package/dist/compaction/manager.js +42 -8
- package/dist/compaction/manager.js.map +1 -1
- package/dist/compaction/managers/structured.d.ts.map +1 -1
- package/dist/compaction/managers/structured.js +7 -3
- package/dist/compaction/managers/structured.js.map +1 -1
- package/dist/compaction/retention.d.ts +15 -0
- package/dist/compaction/retention.d.ts.map +1 -0
- package/dist/compaction/retention.js +89 -0
- package/dist/compaction/retention.js.map +1 -0
- package/dist/compaction/serializer.d.ts.map +1 -1
- package/dist/compaction/serializer.js +28 -11
- package/dist/compaction/serializer.js.map +1 -1
- package/dist/compaction/tool-result-editing.d.ts +54 -0
- package/dist/compaction/tool-result-editing.d.ts.map +1 -0
- package/dist/compaction/tool-result-editing.js +164 -0
- package/dist/compaction/tool-result-editing.js.map +1 -0
- package/dist/compaction/types.d.ts +10 -0
- package/dist/compaction/types.d.ts.map +1 -1
- package/dist/compaction/verifier.d.ts +20 -1
- package/dist/compaction/verifier.d.ts.map +1 -1
- package/dist/compaction/verifier.js +11 -2
- package/dist/compaction/verifier.js.map +1 -1
- package/dist/compaction/wire.d.ts +54 -0
- package/dist/compaction/wire.d.ts.map +1 -0
- package/dist/compaction/wire.js +45 -0
- package/dist/compaction/wire.js.map +1 -0
- package/dist/config/runtime.d.ts +161 -45
- package/dist/config/runtime.d.ts.map +1 -1
- package/dist/config/runtime.js +61 -19
- package/dist/config/runtime.js.map +1 -1
- package/dist/connector/builtins/http.d.ts.map +1 -1
- package/dist/connector/builtins/http.js +14 -1
- package/dist/connector/builtins/http.js.map +1 -1
- package/dist/connector/builtins/http.test.js +12 -0
- package/dist/connector/builtins/http.test.js.map +1 -1
- package/dist/connector/mcp/__tests__/discovery-policy.test.d.ts +2 -0
- package/dist/connector/mcp/__tests__/discovery-policy.test.d.ts.map +1 -0
- package/dist/connector/mcp/__tests__/discovery-policy.test.js +109 -0
- package/dist/connector/mcp/__tests__/discovery-policy.test.js.map +1 -0
- package/dist/connector/mcp/__tests__/list-pagination.test.d.ts +2 -0
- package/dist/connector/mcp/__tests__/list-pagination.test.d.ts.map +1 -0
- package/dist/connector/mcp/__tests__/list-pagination.test.js +81 -0
- package/dist/connector/mcp/__tests__/list-pagination.test.js.map +1 -0
- package/dist/connector/mcp/__tests__/policy.test.d.ts +2 -0
- package/dist/connector/mcp/__tests__/policy.test.d.ts.map +1 -0
- package/dist/connector/mcp/__tests__/policy.test.js +86 -0
- package/dist/connector/mcp/__tests__/policy.test.js.map +1 -0
- package/dist/connector/mcp/__tests__/protocol-negotiation.test.d.ts +2 -0
- package/dist/connector/mcp/__tests__/protocol-negotiation.test.d.ts.map +1 -0
- package/dist/connector/mcp/__tests__/protocol-negotiation.test.js +81 -0
- package/dist/connector/mcp/__tests__/protocol-negotiation.test.js.map +1 -0
- package/dist/connector/mcp/__tests__/schema-fidelity.test.d.ts +2 -0
- package/dist/connector/mcp/__tests__/schema-fidelity.test.d.ts.map +1 -0
- package/dist/connector/mcp/__tests__/schema-fidelity.test.js +206 -0
- package/dist/connector/mcp/__tests__/schema-fidelity.test.js.map +1 -0
- package/dist/connector/mcp/__tests__/structured-result.test.d.ts +2 -0
- package/dist/connector/mcp/__tests__/structured-result.test.d.ts.map +1 -0
- package/dist/connector/mcp/__tests__/structured-result.test.js +112 -0
- package/dist/connector/mcp/__tests__/structured-result.test.js.map +1 -0
- package/dist/connector/mcp/adapter.d.ts +14 -0
- package/dist/connector/mcp/adapter.d.ts.map +1 -1
- package/dist/connector/mcp/adapter.js +292 -18
- package/dist/connector/mcp/adapter.js.map +1 -1
- package/dist/connector/mcp/client.d.ts +38 -0
- package/dist/connector/mcp/client.d.ts.map +1 -1
- package/dist/connector/mcp/client.js +138 -12
- package/dist/connector/mcp/client.js.map +1 -1
- package/dist/connector/mcp/client.test.d.ts +2 -0
- package/dist/connector/mcp/client.test.d.ts.map +1 -0
- package/dist/connector/mcp/client.test.js +105 -0
- package/dist/connector/mcp/client.test.js.map +1 -0
- package/dist/connector/mcp/discovery.d.ts +32 -1
- package/dist/connector/mcp/discovery.d.ts.map +1 -1
- package/dist/connector/mcp/discovery.js +41 -4
- package/dist/connector/mcp/discovery.js.map +1 -1
- package/dist/connector/mcp/http-sse.d.ts.map +1 -1
- package/dist/connector/mcp/http-sse.js +15 -1
- package/dist/connector/mcp/http-sse.js.map +1 -1
- package/dist/connector/mcp/index.d.ts +3 -0
- package/dist/connector/mcp/index.d.ts.map +1 -1
- package/dist/connector/mcp/index.js +1 -0
- package/dist/connector/mcp/index.js.map +1 -1
- package/dist/connector/mcp/policy.d.ts +57 -0
- package/dist/connector/mcp/policy.d.ts.map +1 -0
- package/dist/connector/mcp/policy.js +84 -0
- package/dist/connector/mcp/policy.js.map +1 -0
- package/dist/connector/mcp/schema-refs.d.ts +26 -0
- package/dist/connector/mcp/schema-refs.d.ts.map +1 -0
- package/dist/connector/mcp/schema-refs.js +111 -0
- package/dist/connector/mcp/schema-refs.js.map +1 -0
- package/dist/constants/agent/index.d.ts.map +1 -1
- package/dist/constants/agent/index.js +3 -0
- package/dist/constants/agent/index.js.map +1 -1
- package/dist/constants/compaction/index.js +1 -1
- package/dist/constants/compaction/index.js.map +1 -1
- package/dist/constants/continuation.d.ts +4 -4
- package/dist/constants/continuation.js +4 -4
- package/dist/constants/mcp/index.d.ts +31 -0
- package/dist/constants/mcp/index.d.ts.map +1 -1
- package/dist/constants/mcp/index.js +31 -0
- package/dist/constants/mcp/index.js.map +1 -1
- package/dist/constants/plugin/index.d.ts +23 -2
- package/dist/constants/plugin/index.d.ts.map +1 -1
- package/dist/constants/plugin/index.js +23 -2
- package/dist/constants/plugin/index.js.map +1 -1
- package/dist/constants/sandbox/index.d.ts +2 -3
- package/dist/constants/sandbox/index.d.ts.map +1 -1
- package/dist/constants/sandbox/index.js +6 -5
- package/dist/constants/sandbox/index.js.map +1 -1
- package/dist/constants/telemetry/index.d.ts +7 -0
- package/dist/constants/telemetry/index.d.ts.map +1 -1
- package/dist/constants/telemetry/index.js +7 -0
- package/dist/constants/telemetry/index.js.map +1 -1
- package/dist/constants/tools/index.d.ts +16 -0
- package/dist/constants/tools/index.d.ts.map +1 -1
- package/dist/constants/tools/index.js +16 -0
- package/dist/constants/tools/index.js.map +1 -1
- package/dist/contracts/a2a.d.ts +94 -94
- package/dist/contracts/api.d.ts +40 -2
- package/dist/contracts/api.d.ts.map +1 -1
- package/dist/contracts/run-status.d.ts +17 -0
- package/dist/contracts/run-status.d.ts.map +1 -0
- package/dist/contracts/run-status.js +41 -0
- package/dist/contracts/run-status.js.map +1 -0
- package/dist/contracts/schemas.d.ts +6 -6
- package/dist/eval/__tests__/case-deadline.test.d.ts +2 -0
- package/dist/eval/__tests__/case-deadline.test.d.ts.map +1 -0
- package/dist/eval/__tests__/case-deadline.test.js +128 -0
- package/dist/eval/__tests__/case-deadline.test.js.map +1 -0
- package/dist/eval/__tests__/end-to-end.test.d.ts +2 -0
- package/dist/eval/__tests__/end-to-end.test.d.ts.map +1 -0
- package/dist/eval/__tests__/end-to-end.test.js +223 -0
- package/dist/eval/__tests__/end-to-end.test.js.map +1 -0
- package/dist/eval/__tests__/harness-honesty.test.d.ts +2 -0
- package/dist/eval/__tests__/harness-honesty.test.d.ts.map +1 -0
- package/dist/eval/__tests__/harness-honesty.test.js +85 -0
- package/dist/eval/__tests__/harness-honesty.test.js.map +1 -0
- package/dist/eval/__tests__/harness.test.d.ts +2 -0
- package/dist/eval/__tests__/harness.test.d.ts.map +1 -0
- package/dist/eval/__tests__/harness.test.js +207 -0
- package/dist/eval/__tests__/harness.test.js.map +1 -0
- package/dist/eval/__tests__/judge.test.d.ts +2 -0
- package/dist/eval/__tests__/judge.test.d.ts.map +1 -0
- package/dist/eval/__tests__/judge.test.js +255 -0
- package/dist/eval/__tests__/judge.test.js.map +1 -0
- package/dist/eval/__tests__/scorer-gates.test.d.ts +2 -0
- package/dist/eval/__tests__/scorer-gates.test.d.ts.map +1 -0
- package/dist/eval/__tests__/scorer-gates.test.js +136 -0
- package/dist/eval/__tests__/scorer-gates.test.js.map +1 -0
- package/dist/eval/experiment.d.ts +54 -0
- package/dist/eval/experiment.d.ts.map +1 -0
- package/dist/eval/experiment.js +263 -0
- package/dist/eval/experiment.js.map +1 -0
- package/dist/eval/from-run.d.ts +30 -0
- package/dist/eval/from-run.d.ts.map +1 -0
- package/dist/eval/from-run.js +46 -0
- package/dist/eval/from-run.js.map +1 -0
- package/dist/eval/index.d.ts +8 -0
- package/dist/eval/index.d.ts.map +1 -0
- package/dist/eval/index.js +5 -0
- package/dist/eval/index.js.map +1 -0
- package/dist/eval/judge.d.ts +63 -0
- package/dist/eval/judge.d.ts.map +1 -0
- package/dist/eval/judge.js +136 -0
- package/dist/eval/judge.js.map +1 -0
- package/dist/eval/scorers.d.ts +40 -0
- package/dist/eval/scorers.d.ts.map +1 -0
- package/dist/eval/scorers.js +151 -0
- package/dist/eval/scorers.js.map +1 -0
- package/dist/eval/types.d.ts +150 -0
- package/dist/eval/types.d.ts.map +1 -0
- package/dist/eval/types.js +2 -0
- package/dist/eval/types.js.map +1 -0
- package/dist/execution/local.d.ts.map +1 -1
- package/dist/execution/local.js +6 -1
- package/dist/execution/local.js.map +1 -1
- package/dist/execution/local.test.d.ts +2 -0
- package/dist/execution/local.test.d.ts.map +1 -0
- package/dist/execution/local.test.js +37 -0
- package/dist/execution/local.test.js.map +1 -0
- package/dist/gateway/__tests__/list-survives-eviction.test.d.ts +2 -0
- package/dist/gateway/__tests__/list-survives-eviction.test.d.ts.map +1 -0
- package/dist/gateway/__tests__/list-survives-eviction.test.js +102 -0
- package/dist/gateway/__tests__/list-survives-eviction.test.js.map +1 -0
- package/dist/gateway/local.d.ts +42 -2
- package/dist/gateway/local.d.ts.map +1 -1
- package/dist/gateway/local.js +110 -3
- package/dist/gateway/local.js.map +1 -1
- package/dist/manager/agent/__tests__/dispose-cancels.test.d.ts +2 -0
- package/dist/manager/agent/__tests__/dispose-cancels.test.d.ts.map +1 -0
- package/dist/manager/agent/__tests__/dispose-cancels.test.js +88 -0
- package/dist/manager/agent/__tests__/dispose-cancels.test.js.map +1 -0
- package/dist/manager/agent/__tests__/lifecycle.test.js +230 -0
- package/dist/manager/agent/__tests__/lifecycle.test.js.map +1 -1
- package/dist/manager/agent/lifecycle.d.ts +35 -0
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +117 -7
- package/dist/manager/agent/lifecycle.js.map +1 -1
- package/dist/manager/run/persistence.d.ts +90 -1
- package/dist/manager/run/persistence.d.ts.map +1 -1
- package/dist/manager/run/persistence.js +120 -0
- package/dist/manager/run/persistence.js.map +1 -1
- package/dist/plugin/__tests__/enable-contributions.test.js +3 -3
- package/dist/plugin/__tests__/enable-contributions.test.js.map +1 -1
- package/dist/plugin/__tests__/hook-ordering.test.d.ts +2 -0
- package/dist/plugin/__tests__/hook-ordering.test.d.ts.map +1 -0
- package/dist/plugin/__tests__/hook-ordering.test.js +227 -0
- package/dist/plugin/__tests__/hook-ordering.test.js.map +1 -0
- package/dist/plugin/__tests__/install-time-refusal.test.d.ts +2 -0
- package/dist/plugin/__tests__/install-time-refusal.test.d.ts.map +1 -0
- package/dist/plugin/__tests__/install-time-refusal.test.js +49 -0
- package/dist/plugin/__tests__/install-time-refusal.test.js.map +1 -0
- package/dist/plugin/__tests__/lifecycle.test.js +95 -68
- package/dist/plugin/__tests__/lifecycle.test.js.map +1 -1
- package/dist/plugin/lifecycle.d.ts +15 -1
- package/dist/plugin/lifecycle.d.ts.map +1 -1
- package/dist/plugin/lifecycle.js +67 -20
- package/dist/plugin/lifecycle.js.map +1 -1
- package/dist/plugin/loader.d.ts +15 -0
- package/dist/plugin/loader.d.ts.map +1 -1
- package/dist/plugin/loader.js +33 -1
- package/dist/plugin/loader.js.map +1 -1
- package/dist/probe/registry.d.ts.map +1 -1
- package/dist/probe/registry.js +19 -1
- package/dist/probe/registry.js.map +1 -1
- package/dist/probe/registry.test.d.ts +9 -3
- package/dist/probe/registry.test.d.ts.map +1 -1
- package/dist/probe/registry.test.js +44 -6
- package/dist/probe/registry.test.js.map +1 -1
- package/dist/provider/__tests__/mock-script.test.d.ts +2 -0
- package/dist/provider/__tests__/mock-script.test.d.ts.map +1 -0
- package/dist/provider/__tests__/mock-script.test.js +151 -0
- package/dist/provider/__tests__/mock-script.test.js.map +1 -0
- package/dist/provider/__tests__/retry-visibility.test.d.ts +2 -0
- package/dist/provider/__tests__/retry-visibility.test.d.ts.map +1 -0
- package/dist/provider/__tests__/retry-visibility.test.js +137 -0
- package/dist/provider/__tests__/retry-visibility.test.js.map +1 -0
- package/dist/provider/__tests__/retry.test.d.ts +2 -0
- package/dist/provider/__tests__/retry.test.d.ts.map +1 -0
- package/dist/provider/__tests__/retry.test.js +259 -0
- package/dist/provider/__tests__/retry.test.js.map +1 -0
- package/dist/provider/capabilities.d.ts +1 -0
- package/dist/provider/capabilities.d.ts.map +1 -1
- package/dist/provider/capabilities.js +2 -0
- package/dist/provider/capabilities.js.map +1 -1
- package/dist/provider/collect.d.ts +1 -1
- package/dist/provider/collect.js +1 -1
- package/dist/provider/collect.test.d.ts +1 -1
- package/dist/provider/collect.test.js +1 -1
- package/dist/provider/index.d.ts +3 -0
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +2 -0
- package/dist/provider/index.js.map +1 -1
- package/dist/provider/mock-register.d.ts.map +1 -1
- package/dist/provider/mock-register.js +5 -2
- package/dist/provider/mock-register.js.map +1 -1
- package/dist/provider/mock.d.ts +35 -5
- package/dist/provider/mock.d.ts.map +1 -1
- package/dist/provider/mock.js +104 -32
- package/dist/provider/mock.js.map +1 -1
- package/dist/provider/retry.d.ts +42 -0
- package/dist/provider/retry.d.ts.map +1 -0
- package/dist/provider/retry.js +161 -0
- package/dist/provider/retry.js.map +1 -0
- package/dist/public-runtime.d.ts +26 -7
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +50 -7
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-types.d.ts +5 -1
- package/dist/public-types.d.ts.map +1 -1
- package/dist/rag/__tests__/retrieval-math.test.d.ts +2 -0
- package/dist/rag/__tests__/retrieval-math.test.d.ts.map +1 -0
- package/dist/rag/__tests__/retrieval-math.test.js +46 -0
- package/dist/rag/__tests__/retrieval-math.test.js.map +1 -0
- package/dist/rag/chunking.d.ts +12 -0
- package/dist/rag/chunking.d.ts.map +1 -1
- package/dist/rag/chunking.js +32 -1
- package/dist/rag/chunking.js.map +1 -1
- package/dist/rag/embedding.d.ts +4 -4
- package/dist/rag/embedding.d.ts.map +1 -1
- package/dist/rag/embedding.js +23 -3
- package/dist/rag/embedding.js.map +1 -1
- package/dist/rag/embedding.test.d.ts +1 -1
- package/dist/rag/embedding.test.js +59 -10
- package/dist/rag/embedding.test.js.map +1 -1
- package/dist/rag/index.d.ts +1 -1
- package/dist/rag/index.d.ts.map +1 -1
- package/dist/rag/index.js +1 -1
- package/dist/rag/index.js.map +1 -1
- package/dist/rag/retriever.d.ts +0 -1
- package/dist/rag/retriever.d.ts.map +1 -1
- package/dist/rag/retriever.js +75 -22
- package/dist/rag/retriever.js.map +1 -1
- package/dist/rag/vector-store.test.d.ts +1 -1
- package/dist/rag/vector-store.test.js +2 -2
- package/dist/rag/vector-store.test.js.map +1 -1
- package/dist/registry/ManagedRegistry.test.d.ts +1 -1
- package/dist/registry/ManagedRegistry.test.js +1 -1
- package/dist/registry/Registry.test.d.ts +1 -1
- package/dist/registry/Registry.test.js +1 -1
- package/dist/registry/connector/definitions.test.d.ts +1 -1
- package/dist/registry/connector/definitions.test.js +1 -1
- package/dist/registry/tool/__tests__/schema.test.d.ts +2 -0
- package/dist/registry/tool/__tests__/schema.test.d.ts.map +1 -0
- package/dist/registry/tool/__tests__/schema.test.js +176 -0
- package/dist/registry/tool/__tests__/schema.test.js.map +1 -0
- package/dist/registry/tool/__tests__/tool-name-validation.test.d.ts +2 -0
- package/dist/registry/tool/__tests__/tool-name-validation.test.d.ts.map +1 -0
- package/dist/registry/tool/__tests__/tool-name-validation.test.js +70 -0
- package/dist/registry/tool/__tests__/tool-name-validation.test.js.map +1 -0
- package/dist/registry/tool/execute.d.ts +37 -0
- package/dist/registry/tool/execute.d.ts.map +1 -1
- package/dist/registry/tool/execute.js +116 -16
- package/dist/registry/tool/execute.js.map +1 -1
- package/dist/registry/tool/execute.test.js +93 -7
- package/dist/registry/tool/execute.test.js.map +1 -1
- package/dist/registry/tool/schema.d.ts +26 -0
- package/dist/registry/tool/schema.d.ts.map +1 -0
- package/dist/registry/tool/schema.js +77 -0
- package/dist/registry/tool/schema.js.map +1 -0
- package/dist/registry/toolset/catalog.d.ts.map +1 -1
- package/dist/registry/toolset/catalog.js +17 -5
- package/dist/registry/toolset/catalog.js.map +1 -1
- package/dist/registry/toolset/catalog.test.js +101 -1
- package/dist/registry/toolset/catalog.test.js.map +1 -1
- package/dist/run/reporter.d.ts.map +1 -1
- package/dist/run/reporter.js +76 -0
- package/dist/run/reporter.js.map +1 -1
- package/dist/runtime/bidi/__tests__/session.test.d.ts +2 -0
- package/dist/runtime/bidi/__tests__/session.test.d.ts.map +1 -0
- package/dist/runtime/bidi/__tests__/session.test.js +201 -0
- package/dist/runtime/bidi/__tests__/session.test.js.map +1 -0
- package/dist/runtime/bidi/index.d.ts +5 -0
- package/dist/runtime/bidi/index.d.ts.map +1 -0
- package/dist/runtime/bidi/index.js +3 -0
- package/dist/runtime/bidi/index.js.map +1 -0
- package/dist/runtime/bidi/mock.d.ts +34 -0
- package/dist/runtime/bidi/mock.d.ts.map +1 -0
- package/dist/runtime/bidi/mock.js +60 -0
- package/dist/runtime/bidi/mock.js.map +1 -0
- package/dist/runtime/bidi/session.d.ts +44 -0
- package/dist/runtime/bidi/session.d.ts.map +1 -0
- package/dist/runtime/bidi/session.js +154 -0
- package/dist/runtime/bidi/session.js.map +1 -0
- package/dist/runtime/decision/parser.d.ts.map +1 -1
- package/dist/runtime/decision/parser.js +8 -1
- package/dist/runtime/decision/parser.js.map +1 -1
- package/dist/runtime/decision/parser.test.d.ts +2 -0
- package/dist/runtime/decision/parser.test.d.ts.map +1 -0
- package/dist/runtime/decision/parser.test.js +37 -0
- package/dist/runtime/decision/parser.test.js.map +1 -0
- package/dist/runtime/query/__tests__/answer-review.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/answer-review.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/answer-review.test.js +141 -0
- package/dist/runtime/query/__tests__/answer-review.test.js.map +1 -0
- package/dist/runtime/query/__tests__/capability-negotiation.test.js +24 -32
- package/dist/runtime/query/__tests__/capability-negotiation.test.js.map +1 -1
- package/dist/runtime/query/__tests__/checkpoint-store.test.js +12 -44
- package/dist/runtime/query/__tests__/checkpoint-store.test.js.map +1 -1
- package/dist/runtime/query/__tests__/checkpoint.test.js +2 -10
- package/dist/runtime/query/__tests__/checkpoint.test.js.map +1 -1
- package/dist/runtime/query/__tests__/citations.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/citations.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/citations.test.js +85 -0
- package/dist/runtime/query/__tests__/citations.test.js.map +1 -0
- package/dist/runtime/query/__tests__/content-budget.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/content-budget.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/content-budget.test.js +69 -0
- package/dist/runtime/query/__tests__/content-budget.test.js.map +1 -0
- package/dist/runtime/query/__tests__/context.test.js +3 -2
- package/dist/runtime/query/__tests__/context.test.js.map +1 -1
- package/dist/runtime/query/__tests__/deferred-tools.test.js +21 -30
- package/dist/runtime/query/__tests__/deferred-tools.test.js.map +1 -1
- package/dist/runtime/query/__tests__/delegation-resume.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/delegation-resume.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/delegation-resume.test.js +111 -0
- package/dist/runtime/query/__tests__/delegation-resume.test.js.map +1 -0
- package/dist/runtime/query/__tests__/document-attachments.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/document-attachments.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/document-attachments.test.js +92 -0
- package/dist/runtime/query/__tests__/document-attachments.test.js.map +1 -0
- package/dist/runtime/query/__tests__/durable-park-and-trace.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/durable-park-and-trace.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/durable-park-and-trace.test.js +194 -0
- package/dist/runtime/query/__tests__/durable-park-and-trace.test.js.map +1 -0
- package/dist/runtime/query/__tests__/durable-question-park.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/durable-question-park.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/durable-question-park.test.js +253 -0
- package/dist/runtime/query/__tests__/durable-question-park.test.js.map +1 -0
- package/dist/runtime/query/__tests__/durable-run-state.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/durable-run-state.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/durable-run-state.test.js +158 -0
- package/dist/runtime/query/__tests__/durable-run-state.test.js.map +1 -0
- package/dist/runtime/query/__tests__/executor-plugin-hooks.test.js +24 -20
- package/dist/runtime/query/__tests__/executor-plugin-hooks.test.js.map +1 -1
- package/dist/runtime/query/__tests__/finish-reason-vs-tool-calls.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/finish-reason-vs-tool-calls.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/finish-reason-vs-tool-calls.test.js +187 -0
- package/dist/runtime/query/__tests__/finish-reason-vs-tool-calls.test.js.map +1 -0
- package/dist/runtime/query/__tests__/grant-cannot-overrule-the-gate.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/grant-cannot-overrule-the-gate.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/grant-cannot-overrule-the-gate.test.js +95 -0
- package/dist/runtime/query/__tests__/grant-cannot-overrule-the-gate.test.js.map +1 -0
- package/dist/runtime/query/__tests__/guardrails-e2e.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/guardrails-e2e.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/guardrails-e2e.test.js +132 -0
- package/dist/runtime/query/__tests__/guardrails-e2e.test.js.map +1 -0
- package/dist/runtime/query/__tests__/guardrails.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/guardrails.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/guardrails.test.js +163 -0
- package/dist/runtime/query/__tests__/guardrails.test.js.map +1 -0
- package/dist/runtime/query/__tests__/hook-model-visibility.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/hook-model-visibility.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/hook-model-visibility.test.js +119 -0
- package/dist/runtime/query/__tests__/hook-model-visibility.test.js.map +1 -0
- package/dist/runtime/query/__tests__/long-document-flow.test.js +21 -14
- package/dist/runtime/query/__tests__/long-document-flow.test.js.map +1 -1
- package/dist/runtime/query/__tests__/memory-promotion.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/memory-promotion.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/memory-promotion.test.js +119 -0
- package/dist/runtime/query/__tests__/memory-promotion.test.js.map +1 -0
- package/dist/runtime/query/__tests__/orphan-task-turn-end.test.js +5 -27
- package/dist/runtime/query/__tests__/orphan-task-turn-end.test.js.map +1 -1
- package/dist/runtime/query/__tests__/overflow-relief.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/overflow-relief.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/overflow-relief.test.js +119 -0
- package/dist/runtime/query/__tests__/overflow-relief.test.js.map +1 -0
- package/dist/runtime/query/__tests__/partial-batch-resume.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/partial-batch-resume.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/partial-batch-resume.test.js +205 -0
- package/dist/runtime/query/__tests__/partial-batch-resume.test.js.map +1 -0
- package/dist/runtime/query/__tests__/plugin-hooks.test.js +0 -1
- package/dist/runtime/query/__tests__/plugin-hooks.test.js.map +1 -1
- package/dist/runtime/query/__tests__/prepare-step-chain.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/prepare-step-chain.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/prepare-step-chain.test.js +104 -0
- package/dist/runtime/query/__tests__/prepare-step-chain.test.js.map +1 -0
- package/dist/runtime/query/__tests__/prepare-step.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/prepare-step.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/prepare-step.test.js +194 -0
- package/dist/runtime/query/__tests__/prepare-step.test.js.map +1 -0
- package/dist/runtime/query/__tests__/probe-veto-is-error.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/probe-veto-is-error.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/probe-veto-is-error.test.js +111 -0
- package/dist/runtime/query/__tests__/probe-veto-is-error.test.js.map +1 -0
- package/dist/runtime/query/__tests__/prompt.test.js +1 -1
- package/dist/runtime/query/__tests__/prompt.test.js.map +1 -1
- package/dist/runtime/query/__tests__/provider-fault-pauses.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/provider-fault-pauses.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/provider-fault-pauses.test.js +86 -0
- package/dist/runtime/query/__tests__/provider-fault-pauses.test.js.map +1 -0
- package/dist/runtime/query/__tests__/reasoning-stream.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/reasoning-stream.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/reasoning-stream.test.js +146 -0
- package/dist/runtime/query/__tests__/reasoning-stream.test.js.map +1 -0
- package/dist/runtime/query/__tests__/recoverable-settle.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/recoverable-settle.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/recoverable-settle.test.js +119 -0
- package/dist/runtime/query/__tests__/recoverable-settle.test.js.map +1 -0
- package/dist/runtime/query/__tests__/resume-budget.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/resume-budget.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/resume-budget.test.js +130 -0
- package/dist/runtime/query/__tests__/resume-budget.test.js.map +1 -0
- package/dist/runtime/query/__tests__/resume-pending.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/resume-pending.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/resume-pending.test.js +226 -0
- package/dist/runtime/query/__tests__/resume-pending.test.js.map +1 -0
- package/dist/runtime/query/__tests__/rich-content-budget-wiring.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/rich-content-budget-wiring.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/rich-content-budget-wiring.test.js +89 -0
- package/dist/runtime/query/__tests__/rich-content-budget-wiring.test.js.map +1 -0
- package/dist/runtime/query/__tests__/stop-condition.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/stop-condition.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/stop-condition.test.js +205 -0
- package/dist/runtime/query/__tests__/stop-condition.test.js.map +1 -0
- package/dist/runtime/query/__tests__/stream-recovery.test.js +1 -1
- package/dist/runtime/query/__tests__/stream-recovery.test.js.map +1 -1
- package/dist/runtime/query/__tests__/structured-output.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/structured-output.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/structured-output.test.js +219 -0
- package/dist/runtime/query/__tests__/structured-output.test.js.map +1 -0
- package/dist/runtime/query/__tests__/structured-run-failure.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/structured-run-failure.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/structured-run-failure.test.js +117 -0
- package/dist/runtime/query/__tests__/structured-run-failure.test.js.map +1 -0
- package/dist/runtime/query/__tests__/terminal-tool.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/terminal-tool.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/terminal-tool.test.js +119 -0
- package/dist/runtime/query/__tests__/terminal-tool.test.js.map +1 -0
- package/dist/runtime/query/__tests__/tool-grants.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/tool-grants.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/tool-grants.test.js +85 -0
- package/dist/runtime/query/__tests__/tool-grants.test.js.map +1 -0
- package/dist/runtime/query/__tests__/tool-output-budget.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/tool-output-budget.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/tool-output-budget.test.js +0 -0
- package/dist/runtime/query/__tests__/tool-output-budget.test.js.map +1 -0
- package/dist/runtime/query/__tests__/tool-pause.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/tool-pause.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/tool-pause.test.js +165 -0
- package/dist/runtime/query/__tests__/tool-pause.test.js.map +1 -0
- package/dist/runtime/query/__tests__/tool-progress.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/tool-progress.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/tool-progress.test.js +122 -0
- package/dist/runtime/query/__tests__/tool-progress.test.js.map +1 -0
- package/dist/runtime/query/__tests__/tool-repair-retry.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/tool-repair-retry.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/tool-repair-retry.test.js +196 -0
- package/dist/runtime/query/__tests__/tool-repair-retry.test.js.map +1 -0
- package/dist/runtime/query/__tests__/tool-result-wire.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/tool-result-wire.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/tool-result-wire.test.js +106 -0
- package/dist/runtime/query/__tests__/tool-result-wire.test.js.map +1 -0
- package/dist/runtime/query/__tests__/tool-timeout.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/tool-timeout.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/tool-timeout.test.js +184 -0
- package/dist/runtime/query/__tests__/tool-timeout.test.js.map +1 -0
- package/dist/runtime/query/__tests__/trace-parenting.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/trace-parenting.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/trace-parenting.test.js +111 -0
- package/dist/runtime/query/__tests__/trace-parenting.test.js.map +1 -0
- package/dist/runtime/query/__tests__/wiring-seams.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/wiring-seams.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/wiring-seams.test.js +237 -0
- package/dist/runtime/query/__tests__/wiring-seams.test.js.map +1 -0
- package/dist/runtime/query/checkpoint.d.ts +124 -6
- package/dist/runtime/query/checkpoint.d.ts.map +1 -1
- package/dist/runtime/query/checkpoint.js +207 -6
- package/dist/runtime/query/checkpoint.js.map +1 -1
- package/dist/runtime/query/events.js +2 -2
- package/dist/runtime/query/events.js.map +1 -1
- package/dist/runtime/query/executor.d.ts +224 -7
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +664 -37
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/guard.d.ts +18 -0
- package/dist/runtime/query/guard.d.ts.map +1 -1
- package/dist/runtime/query/guard.js +16 -1
- package/dist/runtime/query/guard.js.map +1 -1
- package/dist/runtime/query/guardrail-presets.d.ts +36 -0
- package/dist/runtime/query/guardrail-presets.d.ts.map +1 -0
- package/dist/runtime/query/guardrail-presets.js +96 -0
- package/dist/runtime/query/guardrail-presets.js.map +1 -0
- package/dist/runtime/query/guardrails.d.ts +35 -0
- package/dist/runtime/query/guardrails.d.ts.map +1 -0
- package/dist/runtime/query/guardrails.js +102 -0
- package/dist/runtime/query/guardrails.js.map +1 -0
- package/dist/runtime/query/index.d.ts +213 -2
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +458 -10
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/__tests__/cancel-settles-turn.test.d.ts +2 -0
- package/dist/runtime/query/iteration/__tests__/cancel-settles-turn.test.d.ts.map +1 -0
- package/dist/runtime/query/iteration/__tests__/cancel-settles-turn.test.js +131 -0
- package/dist/runtime/query/iteration/__tests__/cancel-settles-turn.test.js.map +1 -0
- package/dist/runtime/query/iteration/index.d.ts +76 -1
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +578 -25
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/advisory.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/advisory.js +14 -2
- package/dist/runtime/query/iteration/phases/advisory.js.map +1 -1
- package/dist/runtime/query/iteration/phases/advisory.test.d.ts +1 -1
- package/dist/runtime/query/iteration/phases/advisory.test.js +4 -1
- package/dist/runtime/query/iteration/phases/advisory.test.js.map +1 -1
- package/dist/runtime/query/iteration/phases/checkpoint.js +2 -2
- package/dist/runtime/query/iteration/phases/checkpoint.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction-safe-cut.test.js +1 -1
- package/dist/runtime/query/iteration/phases/compaction-safe-cut.test.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.d.ts +22 -1
- package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +297 -64
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context-measurement.test.d.ts +21 -0
- package/dist/runtime/query/iteration/phases/context-measurement.test.d.ts.map +1 -0
- package/dist/runtime/query/iteration/phases/context-measurement.test.js +161 -0
- package/dist/runtime/query/iteration/phases/context-measurement.test.js.map +1 -0
- package/dist/runtime/query/iteration/phases/context.d.ts +62 -2
- package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/context.js +87 -0
- package/dist/runtime/query/iteration/phases/context.js.map +1 -1
- package/dist/runtime/query/iteration/phases/hitl-answer-question.test.js +3 -1
- package/dist/runtime/query/iteration/phases/hitl-answer-question.test.js.map +1 -1
- package/dist/runtime/query/iteration/phases/overflow-escalation.test.d.ts +15 -0
- package/dist/runtime/query/iteration/phases/overflow-escalation.test.d.ts.map +1 -0
- package/dist/runtime/query/iteration/phases/overflow-escalation.test.js +130 -0
- package/dist/runtime/query/iteration/phases/overflow-escalation.test.js.map +1 -0
- package/dist/runtime/query/iteration/phases/plan.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/plan.js +8 -2
- package/dist/runtime/query/iteration/phases/plan.js.map +1 -1
- package/dist/runtime/query/iteration/phases/retained-messages.test.d.ts +2 -0
- package/dist/runtime/query/iteration/phases/retained-messages.test.d.ts.map +1 -0
- package/dist/runtime/query/iteration/phases/retained-messages.test.js +180 -0
- package/dist/runtime/query/iteration/phases/retained-messages.test.js.map +1 -0
- package/dist/runtime/query/iteration/phases/tool-review-denials.test.d.ts +2 -0
- package/dist/runtime/query/iteration/phases/tool-review-denials.test.d.ts.map +1 -0
- package/dist/runtime/query/iteration/phases/tool-review-denials.test.js +224 -0
- package/dist/runtime/query/iteration/phases/tool-review-denials.test.js.map +1 -0
- package/dist/runtime/query/iteration/phases/tool-review.d.ts +29 -1
- package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.js +114 -44
- package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
- package/dist/runtime/query/iteration/phases/working-memory-compaction.test.js +20 -9
- package/dist/runtime/query/iteration/phases/working-memory-compaction.test.js.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.d.ts +2 -26
- package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.js +254 -16
- package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
- package/dist/runtime/query/plugin-hooks.d.ts.map +1 -1
- package/dist/runtime/query/plugin-hooks.js +6 -2
- package/dist/runtime/query/plugin-hooks.js.map +1 -1
- package/dist/runtime/query/prompt.d.ts.map +1 -1
- package/dist/runtime/query/prompt.js +25 -1
- package/dist/runtime/query/prompt.js.map +1 -1
- package/dist/runtime/query/question-park.d.ts +85 -0
- package/dist/runtime/query/question-park.d.ts.map +1 -0
- package/dist/runtime/query/question-park.js +71 -0
- package/dist/runtime/query/question-park.js.map +1 -0
- package/dist/runtime/query/result.d.ts +9 -0
- package/dist/runtime/query/result.d.ts.map +1 -1
- package/dist/runtime/query/result.js +49 -0
- package/dist/runtime/query/result.js.map +1 -1
- package/dist/runtime/query/resume-pending.d.ts +106 -0
- package/dist/runtime/query/resume-pending.d.ts.map +1 -0
- package/dist/runtime/query/resume-pending.js +312 -0
- package/dist/runtime/query/resume-pending.js.map +1 -0
- package/dist/runtime/query/run-state.d.ts +52 -0
- package/dist/runtime/query/run-state.d.ts.map +1 -0
- package/dist/runtime/query/run-state.js +87 -0
- package/dist/runtime/query/run-state.js.map +1 -0
- package/dist/runtime/query/tool-grants.d.ts +44 -0
- package/dist/runtime/query/tool-grants.d.ts.map +1 -0
- package/dist/runtime/query/tool-grants.js +63 -0
- package/dist/runtime/query/tool-grants.js.map +1 -0
- package/dist/runtime/query/tool-output-budget.d.ts +83 -0
- package/dist/runtime/query/tool-output-budget.d.ts.map +1 -0
- package/dist/runtime/query/tool-output-budget.js +147 -0
- package/dist/runtime/query/tool-output-budget.js.map +1 -0
- package/dist/runtime/query/tool-pause.d.ts +41 -0
- package/dist/runtime/query/tool-pause.d.ts.map +1 -0
- package/dist/runtime/query/tool-pause.js +94 -0
- package/dist/runtime/query/tool-pause.js.map +1 -0
- package/dist/runtime/query/tooling.d.ts +10 -1
- package/dist/runtime/query/tooling.d.ts.map +1 -1
- package/dist/runtime/query/tooling.js +13 -0
- package/dist/runtime/query/tooling.js.map +1 -1
- package/dist/sandbox/__tests__/isolation.test.d.ts +2 -0
- package/dist/sandbox/__tests__/isolation.test.d.ts.map +1 -0
- package/dist/sandbox/__tests__/isolation.test.js +139 -0
- package/dist/sandbox/__tests__/isolation.test.js.map +1 -0
- package/dist/sandbox/__tests__/output-truncation.test.d.ts +2 -0
- package/dist/sandbox/__tests__/output-truncation.test.d.ts.map +1 -0
- package/dist/sandbox/__tests__/output-truncation.test.js +88 -0
- package/dist/sandbox/__tests__/output-truncation.test.js.map +1 -0
- package/dist/sandbox/__tests__/resource-limits.test.d.ts +2 -0
- package/dist/sandbox/__tests__/resource-limits.test.d.ts.map +1 -0
- package/dist/sandbox/__tests__/resource-limits.test.js +86 -0
- package/dist/sandbox/__tests__/resource-limits.test.js.map +1 -0
- package/dist/sandbox/factory.js +1 -1
- package/dist/sandbox/factory.js.map +1 -1
- package/dist/sandbox/index.d.ts +2 -0
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/index.js +1 -0
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/isolation.d.ts +15 -0
- package/dist/sandbox/isolation.d.ts.map +1 -0
- package/dist/sandbox/isolation.js +53 -0
- package/dist/sandbox/isolation.js.map +1 -0
- package/dist/sandbox/provider/local.d.ts +51 -2
- package/dist/sandbox/provider/local.d.ts.map +1 -1
- package/dist/sandbox/provider/local.js +139 -69
- package/dist/sandbox/provider/local.js.map +1 -1
- package/dist/session/__tests__/integration/spawn-rollback.test.d.ts +1 -1
- package/dist/session/__tests__/integration/spawn-rollback.test.js +1 -1
- package/dist/session/handoff/broadcast.js +3 -3
- package/dist/session/handoff/broadcast.js.map +1 -1
- package/dist/session/migration/__tests__/filesystem.test.js +5 -1
- package/dist/session/migration/__tests__/filesystem.test.js.map +1 -1
- package/dist/session/migration/filesystem.d.ts.map +1 -1
- package/dist/session/migration/filesystem.js +7 -13
- package/dist/session/migration/filesystem.js.map +1 -1
- package/dist/session/migration/marker.d.ts.map +1 -1
- package/dist/session/migration/marker.js +5 -10
- package/dist/session/migration/marker.js.map +1 -1
- package/dist/session/retention/disk-backend.d.ts.map +1 -1
- package/dist/session/retention/disk-backend.js +5 -12
- package/dist/session/retention/disk-backend.js.map +1 -1
- package/dist/session/workspace/__tests__/git-worktree.test.js +6 -2
- package/dist/session/workspace/__tests__/git-worktree.test.js.map +1 -1
- package/dist/session/workspace/__tests__/shared-run.test.js +15 -0
- package/dist/session/workspace/__tests__/shared-run.test.js.map +1 -1
- package/dist/session/workspace/shared-run.js +8 -1
- package/dist/session/workspace/shared-run.js.map +1 -1
- package/dist/skills/__tests__/frontmatter.test.d.ts +2 -0
- package/dist/skills/__tests__/frontmatter.test.d.ts.map +1 -0
- package/dist/skills/__tests__/frontmatter.test.js +106 -0
- package/dist/skills/__tests__/frontmatter.test.js.map +1 -0
- package/dist/skills/loader.d.ts.map +1 -1
- package/dist/skills/loader.js +42 -3
- package/dist/skills/loader.js.map +1 -1
- package/dist/store/__tests__/checkpoint-integrity.test.d.ts +2 -0
- package/dist/store/__tests__/checkpoint-integrity.test.d.ts.map +1 -0
- package/dist/store/__tests__/checkpoint-integrity.test.js +112 -0
- package/dist/store/__tests__/checkpoint-integrity.test.js.map +1 -0
- package/dist/store/__tests__/checkpoint-versioning.test.d.ts +2 -0
- package/dist/store/__tests__/checkpoint-versioning.test.d.ts.map +1 -0
- package/dist/store/__tests__/checkpoint-versioning.test.js +151 -0
- package/dist/store/__tests__/checkpoint-versioning.test.js.map +1 -0
- package/dist/store/__tests__/schema-adoption.test.d.ts +2 -0
- package/dist/store/__tests__/schema-adoption.test.d.ts.map +1 -0
- package/dist/store/__tests__/schema-adoption.test.js +90 -0
- package/dist/store/__tests__/schema-adoption.test.js.map +1 -0
- package/dist/store/__tests__/schema.test.d.ts +2 -0
- package/dist/store/__tests__/schema.test.d.ts.map +1 -0
- package/dist/store/__tests__/schema.test.js +93 -0
- package/dist/store/__tests__/schema.test.js.map +1 -0
- package/dist/store/memory/disk.d.ts.map +1 -1
- package/dist/store/memory/disk.js +16 -16
- package/dist/store/memory/disk.js.map +1 -1
- package/dist/store/run/disk.d.ts +23 -0
- package/dist/store/run/disk.d.ts.map +1 -1
- package/dist/store/run/disk.js +148 -23
- package/dist/store/run/disk.js.map +1 -1
- package/dist/store/schema.d.ts +73 -0
- package/dist/store/schema.d.ts.map +1 -0
- package/dist/store/schema.js +103 -0
- package/dist/store/schema.js.map +1 -0
- package/dist/store/session/disk.d.ts.map +1 -1
- package/dist/store/session/disk.js +25 -13
- package/dist/store/session/disk.js.map +1 -1
- package/dist/store/task/__tests__/lookup-across-runs.test.d.ts +2 -0
- package/dist/store/task/__tests__/lookup-across-runs.test.d.ts.map +1 -0
- package/dist/store/task/__tests__/lookup-across-runs.test.js +60 -0
- package/dist/store/task/__tests__/lookup-across-runs.test.js.map +1 -0
- package/dist/store/task/disk.d.ts +18 -0
- package/dist/store/task/disk.d.ts.map +1 -1
- package/dist/store/task/disk.js +61 -16
- package/dist/store/task/disk.js.map +1 -1
- package/dist/store/thread/disk.d.ts.map +1 -1
- package/dist/store/thread/disk.js +20 -11
- package/dist/store/thread/disk.js.map +1 -1
- package/dist/streaming/coalesce.d.ts +1 -1
- package/dist/streaming/coalesce.js +1 -1
- package/dist/telemetry/__tests__/latency-instruments.test.d.ts +2 -0
- package/dist/telemetry/__tests__/latency-instruments.test.d.ts.map +1 -0
- package/dist/telemetry/__tests__/latency-instruments.test.js +82 -0
- package/dist/telemetry/__tests__/latency-instruments.test.js.map +1 -0
- package/dist/telemetry/__tests__/metrics.test.d.ts +2 -0
- package/dist/telemetry/__tests__/metrics.test.d.ts.map +1 -0
- package/dist/telemetry/__tests__/metrics.test.js +112 -0
- package/dist/telemetry/__tests__/metrics.test.js.map +1 -0
- package/dist/telemetry/__tests__/span-hierarchy.test.d.ts +2 -0
- package/dist/telemetry/__tests__/span-hierarchy.test.d.ts.map +1 -0
- package/dist/telemetry/__tests__/span-hierarchy.test.js +84 -0
- package/dist/telemetry/__tests__/span-hierarchy.test.js.map +1 -0
- package/dist/telemetry/attributes.d.ts +44 -0
- package/dist/telemetry/attributes.d.ts.map +1 -1
- package/dist/telemetry/attributes.js +80 -0
- package/dist/telemetry/attributes.js.map +1 -1
- package/dist/telemetry/metrics.d.ts +41 -0
- package/dist/telemetry/metrics.d.ts.map +1 -0
- package/dist/telemetry/metrics.js +160 -0
- package/dist/telemetry/metrics.js.map +1 -0
- package/dist/test-setup.d.ts +3 -3
- package/dist/test-setup.js +3 -3
- package/dist/test-support/paths.d.ts +14 -0
- package/dist/test-support/paths.d.ts.map +1 -0
- package/dist/test-support/paths.js +16 -0
- package/dist/test-support/paths.js.map +1 -0
- package/dist/tools/__tests__/path-containment.test.d.ts +2 -0
- package/dist/tools/__tests__/path-containment.test.d.ts.map +1 -0
- package/dist/tools/__tests__/path-containment.test.js +133 -0
- package/dist/tools/__tests__/path-containment.test.js.map +1 -0
- package/dist/tools/__tests__/posix-path.test.d.ts +2 -0
- package/dist/tools/__tests__/posix-path.test.d.ts.map +1 -0
- package/dist/tools/__tests__/posix-path.test.js +67 -0
- package/dist/tools/__tests__/posix-path.test.js.map +1 -0
- package/dist/tools/__tests__/sandboxed-search.test.d.ts +2 -0
- package/dist/tools/__tests__/sandboxed-search.test.d.ts.map +1 -0
- package/dist/tools/__tests__/sandboxed-search.test.js +114 -0
- package/dist/tools/__tests__/sandboxed-search.test.js.map +1 -0
- package/dist/tools/builtins/__tests__/computer-use.test.js +12 -2
- package/dist/tools/builtins/__tests__/computer-use.test.js.map +1 -1
- package/dist/tools/builtins/__tests__/edit.test.js +0 -54
- package/dist/tools/builtins/__tests__/edit.test.js.map +1 -1
- package/dist/tools/builtins/__tests__/read-file.test.js +23 -1
- package/dist/tools/builtins/__tests__/read-file.test.js.map +1 -1
- package/dist/tools/builtins/atomic-write-file.d.ts +13 -0
- package/dist/tools/builtins/atomic-write-file.d.ts.map +1 -0
- package/dist/tools/builtins/atomic-write-file.js +46 -0
- package/dist/tools/builtins/atomic-write-file.js.map +1 -0
- package/dist/tools/builtins/bash.d.ts.map +1 -1
- package/dist/tools/builtins/bash.js +31 -2
- package/dist/tools/builtins/bash.js.map +1 -1
- package/dist/tools/builtins/computer-use.d.ts +22 -22
- package/dist/tools/builtins/computer-use.d.ts.map +1 -1
- package/dist/tools/builtins/computer-use.js +7 -1
- package/dist/tools/builtins/computer-use.js.map +1 -1
- package/dist/tools/builtins/edit.d.ts +1 -1
- package/dist/tools/builtins/edit.d.ts.map +1 -1
- package/dist/tools/builtins/edit.js +13 -11
- package/dist/tools/builtins/edit.js.map +1 -1
- package/dist/tools/builtins/file-mutation-lock.d.ts +7 -0
- package/dist/tools/builtins/file-mutation-lock.d.ts.map +1 -0
- package/dist/tools/builtins/file-mutation-lock.js +25 -0
- package/dist/tools/builtins/file-mutation-lock.js.map +1 -0
- package/dist/tools/builtins/glob.d.ts +1 -1
- package/dist/tools/builtins/glob.d.ts.map +1 -1
- package/dist/tools/builtins/glob.js +63 -6
- package/dist/tools/builtins/glob.js.map +1 -1
- package/dist/tools/builtins/grep.d.ts.map +1 -1
- package/dist/tools/builtins/grep.js +61 -8
- package/dist/tools/builtins/grep.js.map +1 -1
- package/dist/tools/builtins/index.js +6 -6
- package/dist/tools/builtins/index.js.map +1 -1
- package/dist/tools/builtins/ls.d.ts.map +1 -1
- package/dist/tools/builtins/ls.js +4 -2
- package/dist/tools/builtins/ls.js.map +1 -1
- package/dist/tools/builtins/read-file.d.ts +1 -1
- package/dist/tools/builtins/read-file.d.ts.map +1 -1
- package/dist/tools/builtins/read-file.js +38 -4
- package/dist/tools/builtins/read-file.js.map +1 -1
- package/dist/tools/coordinator/__tests__/agent.test.d.ts +1 -1
- package/dist/tools/coordinator/__tests__/agent.test.js +2 -2
- package/dist/tools/coordinator/__tests__/agent.test.js.map +1 -1
- package/dist/tools/coordinator/agent.d.ts +20 -9
- package/dist/tools/coordinator/agent.d.ts.map +1 -1
- package/dist/tools/coordinator/agent.js +2 -1
- package/dist/tools/coordinator/agent.js.map +1 -1
- package/dist/tools/coordinator/index.d.ts +20 -0
- package/dist/tools/coordinator/index.d.ts.map +1 -1
- package/dist/tools/coordinator/index.js +80 -32
- package/dist/tools/coordinator/index.js.map +1 -1
- package/dist/tools/defineTool.d.ts +18 -0
- package/dist/tools/defineTool.d.ts.map +1 -1
- package/dist/tools/defineTool.js +6 -0
- package/dist/tools/defineTool.js.map +1 -1
- package/dist/tools/glob-match.d.ts +18 -0
- package/dist/tools/glob-match.d.ts.map +1 -0
- package/dist/tools/glob-match.js +50 -0
- package/dist/tools/glob-match.js.map +1 -0
- package/dist/tools/paths.d.ts +20 -0
- package/dist/tools/paths.d.ts.map +1 -0
- package/dist/tools/paths.js +41 -0
- package/dist/tools/paths.js.map +1 -0
- package/dist/tools/posix-path.d.ts +24 -0
- package/dist/tools/posix-path.d.ts.map +1 -0
- package/dist/tools/posix-path.js +67 -0
- package/dist/tools/posix-path.js.map +1 -0
- package/dist/types/advisory/config.d.ts +21 -3
- package/dist/types/advisory/config.d.ts.map +1 -1
- package/dist/types/advisory/result.d.ts +12 -10
- package/dist/types/advisory/result.d.ts.map +1 -1
- package/dist/types/agent/base.d.ts +20 -2
- package/dist/types/agent/base.d.ts.map +1 -1
- package/dist/types/agent/factory.d.ts +15 -9
- package/dist/types/agent/factory.d.ts.map +1 -1
- package/dist/types/agent/gateway.d.ts +18 -0
- package/dist/types/agent/gateway.d.ts.map +1 -1
- package/dist/types/agent/manager.d.ts +19 -0
- package/dist/types/agent/manager.d.ts.map +1 -1
- package/dist/types/agent/reactive.d.ts +48 -0
- package/dist/types/agent/reactive.d.ts.map +1 -1
- package/dist/types/agent/router.d.ts +11 -0
- package/dist/types/agent/router.d.ts.map +1 -1
- package/dist/types/agent/supervisor.d.ts +2 -2
- package/dist/types/agent/task.d.ts +24 -0
- package/dist/types/agent/task.d.ts.map +1 -1
- package/dist/types/bidi/index.d.ts +140 -0
- package/dist/types/bidi/index.d.ts.map +1 -0
- package/dist/types/bidi/index.js +2 -0
- package/dist/types/bidi/index.js.map +1 -0
- package/dist/types/common/__tests__/numeric-correctness.test.d.ts +2 -0
- package/dist/types/common/__tests__/numeric-correctness.test.d.ts.map +1 -0
- package/dist/types/common/__tests__/numeric-correctness.test.js +108 -0
- package/dist/types/common/__tests__/numeric-correctness.test.js.map +1 -0
- package/dist/types/common/index.d.ts +1 -1
- package/dist/types/common/index.d.ts.map +1 -1
- package/dist/types/common/index.js +15 -4
- package/dist/types/common/index.js.map +1 -1
- package/dist/types/connector/mcp.d.ts +29 -0
- package/dist/types/connector/mcp.d.ts.map +1 -1
- package/dist/types/errors/__tests__/catalog.test.d.ts +2 -0
- package/dist/types/errors/__tests__/catalog.test.d.ts.map +1 -0
- package/dist/types/errors/__tests__/catalog.test.js +106 -0
- package/dist/types/errors/__tests__/catalog.test.js.map +1 -0
- package/dist/types/errors/__tests__/errors.test.d.ts +2 -0
- package/dist/types/errors/__tests__/errors.test.d.ts.map +1 -0
- package/dist/types/errors/__tests__/errors.test.js +104 -0
- package/dist/types/errors/__tests__/errors.test.js.map +1 -0
- package/dist/types/errors/catalog.d.ts +83 -0
- package/dist/types/errors/catalog.d.ts.map +1 -0
- package/dist/types/errors/catalog.js +148 -0
- package/dist/types/errors/catalog.js.map +1 -0
- package/dist/types/errors/index.d.ts +73 -0
- package/dist/types/errors/index.d.ts.map +1 -0
- package/dist/types/errors/index.js +70 -0
- package/dist/types/errors/index.js.map +1 -0
- package/dist/types/guardrail/index.d.ts +57 -0
- package/dist/types/guardrail/index.d.ts.map +1 -0
- package/dist/types/guardrail/index.js +2 -0
- package/dist/types/guardrail/index.js.map +1 -0
- package/dist/types/hitl/index.d.ts +97 -14
- package/dist/types/hitl/index.d.ts.map +1 -1
- package/dist/types/hitl/index.js.map +1 -1
- package/dist/types/ids/index.d.ts +2 -2
- package/dist/types/message/content.d.ts +22 -0
- package/dist/types/message/content.d.ts.map +1 -0
- package/dist/types/message/content.js +49 -0
- package/dist/types/message/content.js.map +1 -0
- package/dist/types/message/index.d.ts +193 -8
- package/dist/types/message/index.d.ts.map +1 -1
- package/dist/types/message/index.js +19 -4
- package/dist/types/message/index.js.map +1 -1
- package/dist/types/plugin/index.d.ts +78 -3
- package/dist/types/plugin/index.d.ts.map +1 -1
- package/dist/types/plugin/index.js +0 -1
- package/dist/types/plugin/index.js.map +1 -1
- package/dist/types/provider/__tests__/cause-chain.test.d.ts +2 -0
- package/dist/types/provider/__tests__/cause-chain.test.d.ts.map +1 -0
- package/dist/types/provider/__tests__/cause-chain.test.js +108 -0
- package/dist/types/provider/__tests__/cause-chain.test.js.map +1 -0
- package/dist/types/provider/__tests__/declared-retryable.test.d.ts +2 -0
- package/dist/types/provider/__tests__/declared-retryable.test.d.ts.map +1 -0
- package/dist/types/provider/__tests__/declared-retryable.test.js +70 -0
- package/dist/types/provider/__tests__/declared-retryable.test.js.map +1 -0
- package/dist/types/provider/__tests__/overflow-classification.test.d.ts +2 -0
- package/dist/types/provider/__tests__/overflow-classification.test.d.ts.map +1 -0
- package/dist/types/provider/__tests__/overflow-classification.test.js +80 -0
- package/dist/types/provider/__tests__/overflow-classification.test.js.map +1 -0
- package/dist/types/provider/chat.d.ts +36 -1
- package/dist/types/provider/chat.d.ts.map +1 -1
- package/dist/types/provider/config.d.ts +83 -2
- package/dist/types/provider/config.d.ts.map +1 -1
- package/dist/types/provider/errors.d.ts +102 -0
- package/dist/types/provider/errors.d.ts.map +1 -0
- package/dist/types/provider/errors.js +393 -0
- package/dist/types/provider/errors.js.map +1 -0
- package/dist/types/provider/index.d.ts +3 -2
- package/dist/types/provider/index.d.ts.map +1 -1
- package/dist/types/provider/stream.d.ts +59 -3
- package/dist/types/provider/stream.d.ts.map +1 -1
- package/dist/types/rag/embedding.d.ts +11 -2
- package/dist/types/rag/embedding.d.ts.map +1 -1
- package/dist/types/run/__tests__/step.test.d.ts +2 -0
- package/dist/types/run/__tests__/step.test.d.ts.map +1 -0
- package/dist/types/run/__tests__/step.test.js +107 -0
- package/dist/types/run/__tests__/step.test.js.map +1 -0
- package/dist/types/run/answer-review.d.ts +46 -0
- package/dist/types/run/answer-review.d.ts.map +1 -0
- package/dist/types/run/answer-review.js +2 -0
- package/dist/types/run/answer-review.js.map +1 -0
- package/dist/types/run/config.d.ts +33 -0
- package/dist/types/run/config.d.ts.map +1 -1
- package/dist/types/run/derive-status.d.ts +29 -0
- package/dist/types/run/derive-status.d.ts.map +1 -0
- package/dist/types/run/derive-status.js +49 -0
- package/dist/types/run/derive-status.js.map +1 -0
- package/dist/types/run/entity.d.ts +16 -0
- package/dist/types/run/entity.d.ts.map +1 -1
- package/dist/types/run/events.d.ts +228 -4
- package/dist/types/run/events.d.ts.map +1 -1
- package/dist/types/run/events.js +7 -0
- package/dist/types/run/events.js.map +1 -1
- package/dist/types/run/index.d.ts +5 -0
- package/dist/types/run/index.d.ts.map +1 -1
- package/dist/types/run/index.js +3 -0
- package/dist/types/run/index.js.map +1 -1
- package/dist/types/run/memory-promotion.d.ts +90 -0
- package/dist/types/run/memory-promotion.d.ts.map +1 -0
- package/dist/types/run/memory-promotion.js +38 -0
- package/dist/types/run/memory-promotion.js.map +1 -0
- package/dist/types/run/prepare-step.d.ts +100 -0
- package/dist/types/run/prepare-step.d.ts.map +1 -0
- package/dist/types/run/prepare-step.js +2 -0
- package/dist/types/run/prepare-step.js.map +1 -0
- package/dist/types/run/state.d.ts +85 -0
- package/dist/types/run/state.d.ts.map +1 -0
- package/dist/types/run/state.js +33 -0
- package/dist/types/run/state.js.map +1 -0
- package/dist/types/run/step.d.ts +77 -0
- package/dist/types/run/step.d.ts.map +1 -0
- package/dist/types/run/step.js +26 -0
- package/dist/types/run/step.js.map +1 -0
- package/dist/types/run/stop-reason.d.ts +31 -3
- package/dist/types/run/stop-reason.d.ts.map +1 -1
- package/dist/types/sandbox/index.d.ts +84 -8
- package/dist/types/sandbox/index.d.ts.map +1 -1
- package/dist/types/sandbox/index.js +13 -1
- package/dist/types/sandbox/index.js.map +1 -1
- package/dist/types/structured-output/index.d.ts +6 -5
- package/dist/types/structured-output/index.d.ts.map +1 -1
- package/dist/types/thread/entity.d.ts +2 -3
- package/dist/types/thread/entity.d.ts.map +1 -1
- package/dist/types/tool/index.d.ts +207 -1
- package/dist/types/tool/index.d.ts.map +1 -1
- package/dist/types/tool/index.js +1 -1
- package/dist/types/tool/index.js.map +1 -1
- package/dist/types/tool/repair.d.ts +49 -0
- package/dist/types/tool/repair.d.ts.map +1 -0
- package/dist/types/tool/repair.js +2 -0
- package/dist/types/tool/repair.js.map +1 -0
- package/dist/types/toolset/index.d.ts +2 -5
- package/dist/types/toolset/index.d.ts.map +1 -1
- package/dist/utils/__tests__/atomic-write.test.d.ts +2 -0
- package/dist/utils/__tests__/atomic-write.test.d.ts.map +1 -0
- package/dist/utils/__tests__/atomic-write.test.js +91 -0
- package/dist/utils/__tests__/atomic-write.test.js.map +1 -0
- package/dist/utils/__tests__/shell-compress.test.d.ts +2 -0
- package/dist/utils/__tests__/shell-compress.test.d.ts.map +1 -0
- package/dist/utils/__tests__/shell-compress.test.js +41 -0
- package/dist/utils/__tests__/shell-compress.test.js.map +1 -0
- package/dist/utils/atomic-write.d.ts +10 -0
- package/dist/utils/atomic-write.d.ts.map +1 -0
- package/dist/utils/atomic-write.js +83 -0
- package/dist/utils/atomic-write.js.map +1 -0
- package/dist/utils/shell-compress.d.ts.map +1 -1
- package/dist/utils/shell-compress.js +12 -1
- package/dist/utils/shell-compress.js.map +1 -1
- package/dist/verification/presets.d.ts +6 -3
- package/dist/verification/presets.d.ts.map +1 -1
- package/dist/verification/presets.js +6 -3
- package/dist/verification/presets.js.map +1 -1
- package/package.json +4 -3
- package/src/__tests__/default-path-conformance.test.ts +306 -0
- package/src/__tests__/shipped-defaults.test.ts +111 -0
- package/src/advisory/__tests__/budget-enforcement.test.ts +208 -0
- package/src/advisory/__tests__/structured-response.test.ts +128 -0
- package/src/advisory/budget.ts +27 -0
- package/src/advisory/context.ts +19 -0
- package/src/advisory/executor.test.ts +12 -8
- package/src/advisory/executor.ts +37 -25
- package/src/advisory/index.ts +2 -0
- package/src/advisory/parse.ts +63 -0
- package/src/agents/AbstractAgent.ts +82 -0
- package/src/agents/PipelineAgent.ts +19 -0
- package/src/agents/ReactiveAgent.ts +47 -0
- package/src/agents/RouterAgent.ts +32 -2
- package/src/agents/SupervisorAgent.ts +31 -1
- package/src/agents/__tests__/idempotency-key.test.ts +177 -0
- package/src/agents/__tests__/invocation-lock.test.ts +154 -0
- package/src/agents/__tests__/reachability.test.ts +224 -0
- package/src/agents/__tests__/supervisor-ledger.test.ts +1 -1
- package/src/agents/index.ts +1 -1
- package/src/agents/lock.ts +0 -2
- package/src/bridge/a2a/late-events.test.ts +84 -0
- package/src/bridge/a2a/mapper.test.ts +1 -1
- package/src/bridge/a2a/mapper.ts +70 -5
- package/src/bridge/a2a/message.ts +5 -1
- package/src/bridge/sse/late-events.test.ts +182 -0
- package/src/bridge/sse/mapper.ts +109 -0
- package/src/compaction/__tests__/cleared-result-recovery.test.ts +118 -0
- package/src/compaction/__tests__/context-window.test.ts +77 -0
- package/src/compaction/__tests__/dangling.test.ts +16 -6
- package/src/compaction/__tests__/lossiness.test.ts +187 -0
- package/src/compaction/__tests__/state-across-resume.test.ts +99 -0
- package/src/compaction/__tests__/tool-result-editing.test.ts +213 -0
- package/src/compaction/__tests__/trim-opens-on-user.test.ts +118 -0
- package/src/compaction/__tests__/verifier-model.test.ts +83 -0
- package/src/compaction/context-window.ts +107 -0
- package/src/compaction/dangling.ts +48 -1
- package/src/compaction/extractor.ts +56 -1
- package/src/compaction/index.ts +16 -0
- package/src/compaction/manager.ts +53 -8
- package/src/compaction/managers/structured.ts +7 -6
- package/src/compaction/retention.ts +89 -0
- package/src/compaction/serializer.ts +42 -11
- package/src/compaction/tool-result-editing.ts +229 -0
- package/src/compaction/types.ts +10 -0
- package/src/compaction/verifier.ts +24 -1
- package/src/compaction/wire.ts +91 -0
- package/src/config/runtime.ts +63 -19
- package/src/connector/builtins/http.test.ts +13 -0
- package/src/connector/builtins/http.ts +15 -1
- package/src/connector/mcp/__tests__/discovery-policy.test.ts +133 -0
- package/src/connector/mcp/__tests__/list-pagination.test.ts +98 -0
- package/src/connector/mcp/__tests__/policy.test.ts +100 -0
- package/src/connector/mcp/__tests__/protocol-negotiation.test.ts +105 -0
- package/src/connector/mcp/__tests__/schema-fidelity.test.ts +250 -0
- package/src/connector/mcp/__tests__/structured-result.test.ts +143 -0
- package/src/connector/mcp/adapter.ts +306 -19
- package/src/connector/mcp/client.test.ts +159 -0
- package/src/connector/mcp/client.ts +160 -13
- package/src/connector/mcp/discovery.ts +74 -5
- package/src/connector/mcp/http-sse.ts +16 -1
- package/src/connector/mcp/index.ts +4 -0
- package/src/connector/mcp/policy.ts +134 -0
- package/src/connector/mcp/schema-refs.ts +126 -0
- package/src/constants/agent/index.ts +3 -0
- package/src/constants/compaction/index.ts +1 -1
- package/src/constants/continuation.ts +4 -4
- package/src/constants/mcp/index.ts +34 -0
- package/src/constants/plugin/index.ts +24 -2
- package/src/constants/sandbox/index.ts +6 -5
- package/src/constants/telemetry/index.ts +8 -0
- package/src/constants/tools/index.ts +20 -0
- package/src/contracts/api.ts +40 -0
- package/src/contracts/run-status.ts +43 -0
- package/src/eval/__tests__/case-deadline.test.ts +147 -0
- package/src/eval/__tests__/end-to-end.test.ts +259 -0
- package/src/eval/__tests__/harness-honesty.test.ts +99 -0
- package/src/eval/__tests__/harness.test.ts +249 -0
- package/src/eval/__tests__/judge.test.ts +306 -0
- package/src/eval/__tests__/scorer-gates.test.ts +156 -0
- package/src/eval/experiment.ts +335 -0
- package/src/eval/from-run.ts +48 -0
- package/src/eval/index.ts +21 -0
- package/src/eval/judge.ts +240 -0
- package/src/eval/scorers.ts +169 -0
- package/src/eval/types.ts +157 -0
- package/src/execution/local.test.ts +46 -0
- package/src/execution/local.ts +6 -1
- package/src/gateway/__tests__/list-survives-eviction.test.ts +124 -0
- package/src/gateway/local.ts +117 -3
- package/src/manager/agent/__tests__/dispose-cancels.test.ts +104 -0
- package/src/manager/agent/__tests__/lifecycle.test.ts +297 -0
- package/src/manager/agent/lifecycle.ts +127 -7
- package/src/manager/run/persistence.ts +132 -1
- package/src/plugin/__tests__/enable-contributions.test.ts +3 -3
- package/src/plugin/__tests__/hook-ordering.test.ts +252 -0
- package/src/plugin/__tests__/install-time-refusal.test.ts +68 -0
- package/src/plugin/__tests__/lifecycle.test.ts +95 -73
- package/src/plugin/lifecycle.ts +76 -22
- package/src/plugin/loader.ts +37 -1
- package/src/probe/registry.test.ts +49 -6
- package/src/probe/registry.ts +21 -1
- package/src/provider/__tests__/mock-script.test.ts +179 -0
- package/src/provider/__tests__/registry.test.ts +1 -1
- package/src/provider/__tests__/retry-visibility.test.ts +160 -0
- package/src/provider/__tests__/retry.test.ts +296 -0
- package/src/provider/capabilities.ts +4 -0
- package/src/provider/collect.test.ts +1 -1
- package/src/provider/collect.ts +1 -1
- package/src/provider/index.ts +8 -0
- package/src/provider/mock-register.ts +5 -2
- package/src/provider/mock.ts +117 -34
- package/src/provider/retry.ts +199 -0
- package/src/public-runtime.ts +111 -7
- package/src/public-types.ts +5 -1
- package/src/rag/__tests__/retrieval-math.test.ts +54 -0
- package/src/rag/chunking.ts +32 -1
- package/src/rag/embedding.test.ts +65 -10
- package/src/rag/embedding.ts +26 -5
- package/src/rag/index.ts +1 -1
- package/src/rag/retriever.ts +92 -21
- package/src/rag/vector-store.test.ts +2 -2
- package/src/registry/ManagedRegistry.test.ts +1 -1
- package/src/registry/Registry.test.ts +1 -1
- package/src/registry/connector/definitions.test.ts +1 -1
- package/src/registry/tool/__tests__/schema.test.ts +208 -0
- package/src/registry/tool/__tests__/tool-name-validation.test.ts +88 -0
- package/src/registry/tool/execute.test.ts +107 -7
- package/src/registry/tool/execute.ts +133 -16
- package/src/registry/tool/schema.ts +80 -0
- package/src/registry/toolset/catalog.test.ts +118 -1
- package/src/registry/toolset/catalog.ts +26 -5
- package/src/run/reporter.ts +84 -0
- package/src/runtime/bidi/__tests__/session.test.ts +232 -0
- package/src/runtime/bidi/index.ts +4 -0
- package/src/runtime/bidi/mock.ts +90 -0
- package/src/runtime/bidi/session.ts +205 -0
- package/src/runtime/decision/parser.test.ts +46 -0
- package/src/runtime/decision/parser.ts +8 -1
- package/src/runtime/query/__tests__/answer-review.test.ts +172 -0
- package/src/runtime/query/__tests__/capability-negotiation.test.ts +25 -41
- package/src/runtime/query/__tests__/checkpoint-store.test.ts +13 -50
- package/src/runtime/query/__tests__/checkpoint.test.ts +2 -10
- package/src/runtime/query/__tests__/citations.test.ts +111 -0
- package/src/runtime/query/__tests__/content-budget.test.ts +83 -0
- package/src/runtime/query/__tests__/context.test.ts +3 -2
- package/src/runtime/query/__tests__/deferred-tools.test.ts +23 -37
- package/src/runtime/query/__tests__/delegation-resume.test.ts +159 -0
- package/src/runtime/query/__tests__/document-attachments.test.ts +125 -0
- package/src/runtime/query/__tests__/durable-park-and-trace.test.ts +245 -0
- package/src/runtime/query/__tests__/durable-question-park.test.ts +305 -0
- package/src/runtime/query/__tests__/durable-run-state.test.ts +194 -0
- package/src/runtime/query/__tests__/executor-plugin-hooks.test.ts +24 -20
- package/src/runtime/query/__tests__/finish-reason-vs-tool-calls.test.ts +206 -0
- package/src/runtime/query/__tests__/grant-cannot-overrule-the-gate.test.ts +115 -0
- package/src/runtime/query/__tests__/guardrails-e2e.test.ts +164 -0
- package/src/runtime/query/__tests__/guardrails.test.ts +236 -0
- package/src/runtime/query/__tests__/hook-model-visibility.test.ts +143 -0
- package/src/runtime/query/__tests__/long-document-flow.test.ts +21 -14
- package/src/runtime/query/__tests__/memory-promotion.test.ts +148 -0
- package/src/runtime/query/__tests__/orphan-task-turn-end.test.ts +5 -31
- package/src/runtime/query/__tests__/overflow-relief.test.ts +139 -0
- package/src/runtime/query/__tests__/partial-batch-resume.test.ts +264 -0
- package/src/runtime/query/__tests__/plugin-hooks.test.ts +0 -1
- package/src/runtime/query/__tests__/prepare-step-chain.test.ts +127 -0
- package/src/runtime/query/__tests__/prepare-step.test.ts +233 -0
- package/src/runtime/query/__tests__/probe-veto-is-error.test.ts +145 -0
- package/src/runtime/query/__tests__/prompt.test.ts +1 -1
- package/src/runtime/query/__tests__/provider-fault-pauses.test.ts +104 -0
- package/src/runtime/query/__tests__/reasoning-stream.test.ts +177 -0
- package/src/runtime/query/__tests__/recoverable-settle.test.ts +145 -0
- package/src/runtime/query/__tests__/resume-budget.test.ts +158 -0
- package/src/runtime/query/__tests__/resume-pending.test.ts +273 -0
- package/src/runtime/query/__tests__/rich-content-budget-wiring.test.ts +109 -0
- package/src/runtime/query/__tests__/stop-condition.test.ts +265 -0
- package/src/runtime/query/__tests__/stream-recovery.test.ts +1 -1
- package/src/runtime/query/__tests__/structured-output.test.ts +268 -0
- package/src/runtime/query/__tests__/structured-run-failure.test.ts +144 -0
- package/src/runtime/query/__tests__/terminal-tool.test.ts +148 -0
- package/src/runtime/query/__tests__/tool-grants.test.ts +102 -0
- package/src/runtime/query/__tests__/tool-output-budget.test.ts +0 -0
- package/src/runtime/query/__tests__/tool-pause.test.ts +209 -0
- package/src/runtime/query/__tests__/tool-progress.test.ts +154 -0
- package/src/runtime/query/__tests__/tool-repair-retry.test.ts +255 -0
- package/src/runtime/query/__tests__/tool-result-wire.test.ts +135 -0
- package/src/runtime/query/__tests__/tool-timeout.test.ts +230 -0
- package/src/runtime/query/__tests__/trace-parenting.test.ts +128 -0
- package/src/runtime/query/__tests__/wiring-seams.test.ts +288 -0
- package/src/runtime/query/checkpoint.ts +242 -10
- package/src/runtime/query/events.ts +2 -2
- package/src/runtime/query/executor.ts +849 -41
- package/src/runtime/query/guard.ts +26 -1
- package/src/runtime/query/guardrail-presets.ts +124 -0
- package/src/runtime/query/guardrails.ts +144 -0
- package/src/runtime/query/index.ts +759 -12
- package/src/runtime/query/iteration/__tests__/cancel-settles-turn.test.ts +177 -0
- package/src/runtime/query/iteration/index.ts +658 -27
- package/src/runtime/query/iteration/phases/advisory.test.ts +4 -1
- package/src/runtime/query/iteration/phases/advisory.ts +14 -2
- package/src/runtime/query/iteration/phases/checkpoint.ts +2 -2
- package/src/runtime/query/iteration/phases/compaction-safe-cut.test.ts +3 -1
- package/src/runtime/query/iteration/phases/compaction.ts +329 -64
- package/src/runtime/query/iteration/phases/context-measurement.test.ts +196 -0
- package/src/runtime/query/iteration/phases/context.ts +155 -2
- package/src/runtime/query/iteration/phases/hitl-answer-question.test.ts +3 -1
- package/src/runtime/query/iteration/phases/overflow-escalation.test.ts +167 -0
- package/src/runtime/query/iteration/phases/plan.ts +10 -2
- package/src/runtime/query/iteration/phases/retained-messages.test.ts +226 -0
- package/src/runtime/query/iteration/phases/tool-review-denials.test.ts +270 -0
- package/src/runtime/query/iteration/phases/tool-review.ts +140 -51
- package/src/runtime/query/iteration/phases/working-memory-compaction.test.ts +21 -9
- package/src/runtime/query/iteration/stream-turn.ts +285 -15
- package/src/runtime/query/plugin-hooks.ts +6 -2
- package/src/runtime/query/prompt.ts +26 -1
- package/src/runtime/query/question-park.ts +120 -0
- package/src/runtime/query/result.ts +64 -0
- package/src/runtime/query/resume-pending.ts +394 -0
- package/src/runtime/query/run-state.ts +124 -0
- package/src/runtime/query/tool-grants.ts +83 -0
- package/src/runtime/query/tool-output-budget.ts +183 -0
- package/src/runtime/query/tool-pause.ts +119 -0
- package/src/runtime/query/tooling.ts +23 -1
- package/src/sandbox/__tests__/isolation.test.ts +164 -0
- package/src/sandbox/__tests__/output-truncation.test.ts +106 -0
- package/src/sandbox/__tests__/resource-limits.test.ts +106 -0
- package/src/sandbox/factory.ts +1 -1
- package/src/sandbox/index.ts +2 -0
- package/src/sandbox/isolation.ts +72 -0
- package/src/sandbox/provider/local.ts +173 -74
- package/src/session/__tests__/integration/spawn-rollback.test.ts +1 -1
- package/src/session/handoff/broadcast.ts +3 -3
- package/src/session/migration/__tests__/filesystem.test.ts +26 -19
- package/src/session/migration/filesystem.ts +7 -12
- package/src/session/migration/marker.ts +5 -9
- package/src/session/retention/disk-backend.ts +5 -11
- package/src/session/workspace/__tests__/git-worktree.test.ts +6 -2
- package/src/session/workspace/__tests__/shared-run.test.ts +18 -0
- package/src/session/workspace/shared-run.ts +7 -1
- package/src/skills/__tests__/frontmatter.test.ts +135 -0
- package/src/skills/loader.ts +47 -3
- package/src/store/__tests__/checkpoint-integrity.test.ts +134 -0
- package/src/store/__tests__/checkpoint-versioning.test.ts +194 -0
- package/src/store/__tests__/schema-adoption.test.ts +118 -0
- package/src/store/__tests__/schema.test.ts +119 -0
- package/src/store/memory/disk.ts +17 -16
- package/src/store/run/disk.ts +169 -22
- package/src/store/schema.ts +135 -0
- package/src/store/session/disk.ts +30 -21
- package/src/store/task/__tests__/lookup-across-runs.test.ts +77 -0
- package/src/store/task/disk.ts +59 -15
- package/src/store/thread/disk.ts +21 -10
- package/src/streaming/coalesce.ts +1 -1
- package/src/telemetry/__tests__/latency-instruments.test.ts +120 -0
- package/src/telemetry/__tests__/metrics.test.ts +160 -0
- package/src/telemetry/__tests__/span-hierarchy.test.ts +94 -0
- package/src/telemetry/attributes.ts +109 -0
- package/src/telemetry/metrics.ts +190 -0
- package/src/test-setup.ts +3 -3
- package/src/test-support/paths.ts +17 -0
- package/src/tools/__tests__/path-containment.test.ts +167 -0
- package/src/tools/__tests__/posix-path.test.ts +83 -0
- package/src/tools/__tests__/sandboxed-search.test.ts +140 -0
- package/src/tools/builtins/__tests__/computer-use.test.ts +15 -2
- package/src/tools/builtins/__tests__/edit.test.ts +0 -76
- package/src/tools/builtins/__tests__/read-file.test.ts +29 -1
- package/src/tools/builtins/atomic-write-file.ts +58 -0
- package/src/tools/builtins/bash.ts +32 -2
- package/src/tools/builtins/computer-use.ts +7 -1
- package/src/tools/builtins/edit.ts +12 -12
- package/src/tools/builtins/file-mutation-lock.ts +27 -0
- package/src/tools/builtins/glob.ts +71 -6
- package/src/tools/builtins/grep.ts +82 -8
- package/src/tools/builtins/index.ts +6 -6
- package/src/tools/builtins/ls.ts +4 -2
- package/src/tools/builtins/read-file.ts +40 -4
- package/src/tools/coordinator/__tests__/agent.test.ts +2 -2
- package/src/tools/coordinator/agent.ts +23 -10
- package/src/tools/coordinator/index.ts +111 -31
- package/src/tools/defineTool.ts +24 -0
- package/src/tools/glob-match.ts +50 -0
- package/src/tools/paths.ts +44 -0
- package/src/tools/posix-path.ts +73 -0
- package/src/types/advisory/config.ts +21 -3
- package/src/types/advisory/result.ts +12 -7
- package/src/types/agent/base.ts +22 -2
- package/src/types/agent/factory.ts +15 -9
- package/src/types/agent/gateway.ts +20 -0
- package/src/types/agent/manager.ts +19 -0
- package/src/types/agent/reactive.ts +50 -0
- package/src/types/agent/router.ts +11 -0
- package/src/types/agent/supervisor.ts +2 -2
- package/src/types/agent/task.ts +26 -0
- package/src/types/bidi/index.ts +119 -0
- package/src/types/common/__tests__/numeric-correctness.test.ts +123 -0
- package/src/types/common/index.ts +16 -4
- package/src/types/computer-use/index.ts +1 -1
- package/src/types/connector/mcp.ts +29 -0
- package/src/types/errors/__tests__/catalog.test.ts +130 -0
- package/src/types/errors/__tests__/errors.test.ts +118 -0
- package/src/types/errors/catalog.ts +211 -0
- package/src/types/errors/index.ts +121 -0
- package/src/types/guardrail/index.ts +62 -0
- package/src/types/hitl/index.ts +102 -20
- package/src/types/ids/index.ts +2 -2
- package/src/types/message/content.ts +50 -0
- package/src/types/message/index.ts +210 -9
- package/src/types/plugin/index.ts +82 -2
- package/src/types/provider/__tests__/cause-chain.test.ts +132 -0
- package/src/types/provider/__tests__/declared-retryable.test.ts +85 -0
- package/src/types/provider/__tests__/overflow-classification.test.ts +113 -0
- package/src/types/provider/chat.ts +43 -2
- package/src/types/provider/config.ts +83 -2
- package/src/types/provider/errors.ts +472 -0
- package/src/types/provider/index.ts +5 -0
- package/src/types/provider/stream.ts +63 -3
- package/src/types/rag/embedding.ts +11 -2
- package/src/types/run/__tests__/step.test.ts +123 -0
- package/src/types/run/answer-review.ts +51 -0
- package/src/types/run/config.ts +35 -0
- package/src/types/run/derive-status.ts +62 -0
- package/src/types/run/entity.ts +18 -0
- package/src/types/run/events.ts +230 -3
- package/src/types/run/index.ts +5 -0
- package/src/types/run/memory-promotion.ts +112 -0
- package/src/types/run/prepare-step.ts +109 -0
- package/src/types/run/state.ts +111 -0
- package/src/types/run/step.ts +95 -0
- package/src/types/run/stop-reason.ts +29 -1
- package/src/types/sandbox/index.ts +95 -10
- package/src/types/structured-output/index.ts +6 -5
- package/src/types/thread/entity.ts +2 -3
- package/src/types/tool/index.ts +218 -1
- package/src/types/tool/repair.ts +54 -0
- package/src/types/toolset/index.ts +2 -6
- package/src/utils/__tests__/atomic-write.test.ts +108 -0
- package/src/utils/__tests__/shell-compress.test.ts +49 -0
- package/src/utils/atomic-write.ts +85 -0
- package/src/utils/shell-compress.ts +12 -1
- package/src/verification/presets.ts +6 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,2289 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 935b8f3: Retire the declarations that promised behaviour nothing implemented, and implement the ones worth keeping.
|
|
8
|
+
|
|
9
|
+
Seven fields were declared on exported types and read by nothing. Each was a contract a host could satisfy and get no result from — the worst kind of gap, because the only signal is that nothing happens.
|
|
10
|
+
|
|
11
|
+
**Implemented**
|
|
12
|
+
|
|
13
|
+
- `maxToolContentBytes` capped the rich channel of a tool result, and no caller could set it: `ToolingBootstrapConfig` had no such field, so the cap was always `0` and the capping branch was unreachable. It is now settable on `ReactiveAgentConfig` and on query params, and reaches the executor through the same chain `maxToolOutputChars` already had.
|
|
14
|
+
- `AdvisoryResult.warnings` and `.decisions` had two consumers each — the advisory phase folds decisions into working state so they survive compaction, and renders warnings back to the executing agent — and no producer at all. Advisors are now told the convention their answer is read with, and `parseAdvisoryResponse` lifts `<warnings>` / `<decisions>` blocks out of the prose. The contract is appended to a host-written prompt and a persona-assembled one too, not only the default; an advisor never told the convention would have had its warnings silently discarded.
|
|
15
|
+
- `AdvisoryBudget.maxCostPerRun` is enforced before each call against real accumulated spend, and `maxTokensPerCall` clamps the advisor's own response ceiling. Cost is now computed from a new optional `AdvisorDefinition.pricing`, and a run that sets a cost cap over unpriced advisors is **refused at construction** rather than left with a cap that could never be reached.
|
|
16
|
+
|
|
17
|
+
**Removed** — declared, never read, and not worth building:
|
|
18
|
+
|
|
19
|
+
- `AdvisoryBudget.maxCallsPerSession` and `maxCostPerSession`: the advisory stack is built once per run, so no accumulator outlived one and a per-session cap could only ever be decoration. `maxCostPerCall` went with them — a per-call cap can only be checked after the spend, which is a log line, not a budget.
|
|
20
|
+
- `AdvisoryResult.plan`, `.modelSuggestion`, `.toolGuidance`: no producer and no consumer.
|
|
21
|
+
- `ToolsetDefinition.toolPolicies`: stored on the toolset and never consulted, so a per-tool `{ enabled: false }` override was inert.
|
|
22
|
+
- `SandboxConfig.cleanupOnDestroy`: defaulted to `true` and read by nothing; `destroy()` removes unconditionally either way.
|
|
23
|
+
- `StructuredOutputConfig.enforceToolChoice`: documented a tool-choice mechanism no code implemented.
|
|
24
|
+
- `RuntimeConfig.promptCache`: caching is unconditional at both model calls, and no surface accepts a `RuntimeConfig`, so nothing could set it even in principle.
|
|
25
|
+
|
|
26
|
+
Also ports the telemetry provider to the current tracing API — `Resource` became a type with a factory, and span processors moved to the provider constructor — and lifts a run deadline inside the long-document flow test that aborted the run at 5s and read as a broken flow rather than a busy machine.
|
|
27
|
+
|
|
28
|
+
- 935b8f3: Three controls a caller could set that the runtime then quietly declined to apply.
|
|
29
|
+
|
|
30
|
+
- **`toolChoice: 'none'` permitted tool calls on two drivers.** It means the model must not call a tool. One driver mapped it to the wire's "auto" and the other to `{ type: 'auto' }` — both of which say the model _may_. A caller that had forbidden tool use got a request that allowed it, with nothing in the response to say so. The runtime depends on the guarantee: an advisory consultation passes `'none'` so the advisor answers in prose, into a turn where no executor is waiting for a tool call. Both drivers now answer `'none'` by sending no tools at all, which no wire format can misread.
|
|
31
|
+
|
|
32
|
+
- **`memoryLimitMb` and `maxProcesses` were dropped by the stronger isolation tiers.** They were applied inside the unconfined tier's branch only, so asking for namespace or profile isolation silently removed the blast-radius caps — a control failing in the one direction nobody checks. They are the same shell builtin on every tier; the stronger tiers now apply them one level in, inside the wrapper they already spawn through, and keep doing their own job. The sibling backend in the sandbox package already refuses per-sandbox controls it cannot enforce rather than ignoring them; this is the same rule, satisfied by enforcing.
|
|
33
|
+
|
|
34
|
+
- **`AgentManager.dispose()` cancelled nothing.** It called `cancelAll('' as RunId)`, and `cancelAll` filters by parent run — no task has an empty parent, so it matched nothing, and the next lines cleared the instance map. Every live child was released without its abort controller firing: the work kept running, the budget kept draining, and nothing was left holding a reference to stop it. It now cancels every live child before dropping them. `cancelAll` stays scoped to one parent, which is its actual job.
|
|
35
|
+
|
|
36
|
+
`toBedrockToolConfig` and `buildLimitedSpawn` are exported so the mapping and the spawn shape can be asserted directly rather than through a live process.
|
|
37
|
+
|
|
38
|
+
- 935b8f3: Three public identifiers named a vendor where the code was generic. Renamed,
|
|
39
|
+
and in two cases the naming was hiding a design problem worth fixing.
|
|
40
|
+
|
|
41
|
+
**`OpenRouterEmbeddingProvider` → `HttpEmbeddingProvider`** (config type
|
|
42
|
+
likewise). Nothing about the class was vendor-specific: it POSTs to
|
|
43
|
+
`{baseUrl}/embeddings` with a bearer key and reads back
|
|
44
|
+
`{ data: [{ index, embedding }] }` — the shape every hosted embeddings
|
|
45
|
+
service speaks. Only the name and a default host said otherwise.
|
|
46
|
+
|
|
47
|
+
`baseUrl` is now **required**. It defaulted to one vendor's host, which
|
|
48
|
+
meant a caller who never named an endpoint still shipped its text to one. A
|
|
49
|
+
default network destination is a decision the caller has to make out loud.
|
|
50
|
+
A trailing slash is now tolerated rather than producing a doubled path.
|
|
51
|
+
|
|
52
|
+
**`AgentFactoryOptions.provider`** was `'openrouter' | 'bedrock'` — a closed
|
|
53
|
+
two-member union in a generic factory, naming two specific services that the
|
|
54
|
+
provider registry has never been limited to and that no caller could extend.
|
|
55
|
+
It is now `string`: any registered provider type.
|
|
56
|
+
|
|
57
|
+
**`AgentFactoryOptions.bedrockConfig`** is replaced by
|
|
58
|
+
`providerConfig?: Record<string, unknown>`, passed through untouched. The
|
|
59
|
+
old field existed for exactly one service and had no construction site
|
|
60
|
+
anywhere in the workspace.
|
|
61
|
+
|
|
62
|
+
**`StorageProviderId`**: the `'anthropic-files'` member is now
|
|
63
|
+
`'provider-files'`.
|
|
64
|
+
|
|
65
|
+
- 935b8f3: A model-graded judge, and a failed measurement that stops reading as a zero.
|
|
66
|
+
|
|
67
|
+
Every scorer in the harness was a pure function over the run, which is what
|
|
68
|
+
makes them reproducible — and what makes them unable to say whether an
|
|
69
|
+
answer is _good_. `containsScorer` can check that a required phrase appears;
|
|
70
|
+
it cannot tell a correct explanation from a fluent wrong one. The dimension
|
|
71
|
+
most worth guarding had no scorer behind it.
|
|
72
|
+
|
|
73
|
+
**`judgeScorer`** grades an open-ended answer with a model. Four choices in
|
|
74
|
+
it are deliberate, because each is where these usually go wrong:
|
|
75
|
+
|
|
76
|
+
- The **rubric is required**. A judge asked to rate "quality" rates fluency,
|
|
77
|
+
which correlates with little worth measuring and drifts whenever the judge
|
|
78
|
+
model changes. It throws rather than run without one.
|
|
79
|
+
- An **ordinal scale, not a 0..1 float**. Models place a continuous score
|
|
80
|
+
poorly and cluster on round numbers; a short scale against a written
|
|
81
|
+
rubric is a judgement they can make. The default of 4 is even on purpose —
|
|
82
|
+
an odd scale has a midpoint, and a midpoint is where an uncertain judge
|
|
83
|
+
parks.
|
|
84
|
+
- **Temperature 0**, because sampling noise is indistinguishable from a
|
|
85
|
+
regression.
|
|
86
|
+
- **Truncation disclosed in the prompt**, so the judge does not mark an
|
|
87
|
+
answer down for an ending the harness removed.
|
|
88
|
+
|
|
89
|
+
A grade outside the scale it was given is an error rather than a clamp: a
|
|
90
|
+
judge that misread the scale did not apply the rubric either.
|
|
91
|
+
`details.judgeTokens` carries what the judging cost.
|
|
92
|
+
|
|
93
|
+
**A failed measurement is no longer a measurement of zero.** A judge is a
|
|
94
|
+
network call, so it can fail to answer at all — and scoring that `0` says
|
|
95
|
+
"the run was bad" when the truth is "we do not know". One rate limit would
|
|
96
|
+
turn a green suite red and send somebody hunting a regression that never
|
|
97
|
+
happened.
|
|
98
|
+
|
|
99
|
+
- `Score.unavailable` marks a judgement that could not be produced. It is
|
|
100
|
+
excluded from the case mean's numerator **and** denominator.
|
|
101
|
+
- `CaseResult.status` is `'passed' | 'failed' | 'inconclusive'`, and a case
|
|
102
|
+
where every scorer was unavailable is inconclusive rather than failed.
|
|
103
|
+
`CaseResult.passed` remains, true only for `'passed'`.
|
|
104
|
+
- `ExperimentReport.inconclusive` counts them, and `formatReport` surfaces
|
|
105
|
+
that count **above** the failures — it means every number below covers
|
|
106
|
+
less evidence than it appears to.
|
|
107
|
+
- `byScorer` averages each scorer over the cases it actually judged, and
|
|
108
|
+
omits one that was never available rather than reporting it as `0`.
|
|
109
|
+
|
|
110
|
+
A run that **threw** still scores zero: that is a real failure of the thing
|
|
111
|
+
under test, not of the measurement.
|
|
112
|
+
|
|
113
|
+
Breaking: `CaseResult` gains a required `status`, `ExperimentReport` gains a
|
|
114
|
+
required `inconclusive`, and a scorer that throws now reports as unavailable
|
|
115
|
+
instead of scoring zero.
|
|
116
|
+
|
|
117
|
+
- 935b8f3: **Breaking:** `ActiveNodeInfo` and `BranchStackEntry`, and the `activeNode` / `branchStack` checkpoint fields that carried them, are removed.
|
|
118
|
+
|
|
119
|
+
Both types described where a multi-node run stood — which agent was active, how deep, what each branch decided — and nothing ever wrote either one. `CheckpointManager.save` accepted them as optional `extra` arguments no caller passed, so every checkpoint ever written left both `undefined`, and a resume that consulted them would have found nothing to consult.
|
|
120
|
+
|
|
121
|
+
Resuming a fan-out is already covered, and by a general mechanism rather than a topology-specific one: delegation blocks and returns the worker's output as its own tool result, so a delegation is an ordinary tool call whose completion the transcript records — and the crash-resume path that answers already-executed tool calls answers delegations too. A worker that already ran does not run twice. That behaviour is now pinned by tests, so if delegation ever stops blocking, they fail.
|
|
122
|
+
|
|
123
|
+
- 935b8f3: **Breaking:** three public types that promised behaviour the runtime does
|
|
124
|
+
not have are removed.
|
|
125
|
+
|
|
126
|
+
A public type that describes an absent capability is a worse defect than an
|
|
127
|
+
absent type. It reads as a feature, gets designed around, and the discovery
|
|
128
|
+
that it does nothing happens at runtime — usually in the one code path
|
|
129
|
+
nobody exercised until production.
|
|
130
|
+
|
|
131
|
+
- **`PluginHookResult`'s `{ action: 'resume' }`** — declared as a hook
|
|
132
|
+
outcome and rejected with "unsupported action" at every one of its three
|
|
133
|
+
consumers: the lifecycle-event applier, the `pre_tool_use` path and the
|
|
134
|
+
`post_tool_use` path. A plugin author reading the union had every reason
|
|
135
|
+
to think a hook could resume something. Nothing could.
|
|
136
|
+
- **`ConcurrencyMode`** (`'throw' | 'queue'`) — no API accepts it, nothing
|
|
137
|
+
calls the lock it was meant to configure, and the `queue` half describes
|
|
138
|
+
a mode that was never built. It promised a choice about concurrent
|
|
139
|
+
invocation where there is exactly one behaviour.
|
|
140
|
+
- **`ToolPermissionPolicy`** and `ToolsetPolicy.permissionPolicy` — written
|
|
141
|
+
once with a constant `'default'` and read by no runtime code. A host
|
|
142
|
+
setting `'always_ask'` on a toolset got no prompt and no error.
|
|
143
|
+
|
|
144
|
+
Migration: nothing consumed any of them, so nothing should break. If your
|
|
145
|
+
code sets `permissionPolicy`, delete the field — it never did anything; the
|
|
146
|
+
verification gate (`allow_by_name`, `custom_pattern`, `target: 'args'`) is
|
|
147
|
+
the surface that actually decides. If a hook returns `{ action: 'resume' }`,
|
|
148
|
+
it was already throwing at every call site.
|
|
149
|
+
|
|
150
|
+
Kept, and documented instead of removed: `AgentManager.continueTask` /
|
|
151
|
+
`queueMessage` / `drainMessages`. The queue they maintain is read by
|
|
152
|
+
nothing in the iteration loop — the consumer that once drained it was
|
|
153
|
+
removed — so a caller who assumes `continueTask` reaches a running agent is
|
|
154
|
+
filling a buffer only `drainMessages` empties. That is now stated on the
|
|
155
|
+
interface, along with the two mid-run routes that DO work (feedback inside
|
|
156
|
+
a tool result; `prepareStep`'s `system` string). Deleting `drainMessages`
|
|
157
|
+
would have removed the only way a host can pick those messages up and left
|
|
158
|
+
the trap in place.
|
|
159
|
+
|
|
160
|
+
- 935b8f3: Close every open code-scanning finding
|
|
161
|
+
|
|
162
|
+
**Breaking:** `LocalExecutionContext.executeCommand` no longer interprets its arguments as shell syntax. `shell` defaulted to `true`, and spawning with a shell re-joins the command and its argument array into a single `sh -c` string — so every metacharacter inside an argument became syntax. An `args` array reads argv-safe and was not. The default is now `false`; `shell: true` remains available where a caller genuinely wants a pipeline. A consumer passing `"ls -la"` as one command string, or relying on glob expansion without asking for a shell, must now pass `shell: true`.
|
|
163
|
+
|
|
164
|
+
**A sandbox timeout is bounded, and an out-of-range one is refused.** The bash tool's `timeout` argument is a number the model writes, with no ceiling of its own, and it reached both sandbox transports unmodified — so a single call could pin a container or a guest for as long as the platform's timer honours. Both transports now refuse a non-finite, non-positive or over-thirty-minute request rather than clamping it: running under a deadline the caller never chose, and never learns about, is the "accepted and silently not applied" failure this codebase treats as worse than not offering the control at all.
|
|
165
|
+
|
|
166
|
+
**Seven quadratic-backtracking regexes are now linear scans**, each on a path an attacker can reach: shell output the agent captured, a tenant-supplied connector URL, a host-supplied workspace root, a model completion, and three endpoint strings that cross the same trust boundary. The worst measured over thirty seconds on a single pathological input, on a shared event loop. Three of the seven were not flagged by the scanner — the same pattern, the same boundary — and were fixed with the rest rather than left to be rediscovered.
|
|
167
|
+
|
|
168
|
+
- 935b8f3: namzu's own vocabulary, everywhere.
|
|
169
|
+
|
|
170
|
+
Comments across the kernel explained namzu's design by naming another
|
|
171
|
+
product: "mirrors X's container architecture", "reference: X's
|
|
172
|
+
`normalizePathForSandbox()`", "which is what Y and Z both do", "Claude Code
|
|
173
|
+
uses 2000 for the same reason". Behaviour was correct throughout — this is
|
|
174
|
+
about what the code says it is. A kernel that explains itself by citation
|
|
175
|
+
reads as a reimplementation of something else, and namzu is not one.
|
|
176
|
+
|
|
177
|
+
Every such comment now states the reason directly. Where a rule exists
|
|
178
|
+
because a provider requires it, the comment says what the requirement is
|
|
179
|
+
rather than whose it is — which is also more useful, since the same
|
|
180
|
+
requirement usually holds for more than one provider, and a reader who has
|
|
181
|
+
never used the named one can still follow it.
|
|
182
|
+
|
|
183
|
+
**Breaking (types only, no runtime behaviour):**
|
|
184
|
+
|
|
185
|
+
- `ToolCatalogSurface`: the `'cowork'` member is now `'supervised'`.
|
|
186
|
+
- `ToolSource.skill.type`: `'anthropic' | 'custom'` is now
|
|
187
|
+
`'published' | 'custom'`.
|
|
188
|
+
|
|
189
|
+
Both are descriptive metadata with no construction site anywhere in the
|
|
190
|
+
workspace, so nothing internal moved. An external consumer that names
|
|
191
|
+
either value gets a compile error pointing at the line.
|
|
192
|
+
|
|
193
|
+
**Deliberately unchanged**, because these are addresses rather than
|
|
194
|
+
borrowed naming: model-id prefixes in the context-window table (data the
|
|
195
|
+
runtime matches against), API-key detection patterns in the guardrail
|
|
196
|
+
presets (a pattern is worthless if you cannot tell what it detects),
|
|
197
|
+
namzu's own provider package names, and the credential-store integration in
|
|
198
|
+
the CLI, whose service name and file path are literally the other tool's.
|
|
199
|
+
|
|
200
|
+
- 935b8f3: Tool names are validated, and a paged remote catalogue is read to the end.
|
|
201
|
+
|
|
202
|
+
**Every plugin-contributed tool name was illegal.** A tool name reaches the
|
|
203
|
+
provider verbatim and the major message APIs accept `[a-zA-Z0-9_-]` up to 64
|
|
204
|
+
characters — but the plugin namespace separator was `:`, so every tool a
|
|
205
|
+
plugin contributed carried a name the wire rejects. Nothing checked: names
|
|
206
|
+
are derived by concatenation at three separate construction sites and none
|
|
207
|
+
validated the result.
|
|
208
|
+
|
|
209
|
+
The rejection is a 400 on the **whole request**, not on that tool. Those
|
|
210
|
+
tools are registered deferred, so it fired the moment something activated
|
|
211
|
+
one, with nothing naming the culprit.
|
|
212
|
+
|
|
213
|
+
- `assertToolName` runs at registration, where a bad name can still be
|
|
214
|
+
attributed and costs the run nothing.
|
|
215
|
+
- **Breaking:** `PLUGIN_NAMESPACE_SEPARATOR` is now `__`, which renames every
|
|
216
|
+
plugin-contributed tool id — `fs-plugin:mcp__fs__read_file` becomes
|
|
217
|
+
`fs-plugin__mcp__fs__read_file`. A host that names one of these in an
|
|
218
|
+
allowlist, a permission rule or a preserve-list must update it. The two
|
|
219
|
+
changes have to land together: adding the check without the rename would
|
|
220
|
+
refuse every plugin tool.
|
|
221
|
+
|
|
222
|
+
One driver had already ratified passing names through untouched, on the
|
|
223
|
+
grounds that a confusing name is "a naming problem to fix in the registry,
|
|
224
|
+
not something to paper over" — which is precisely why the registry has to be
|
|
225
|
+
the one that checks.
|
|
226
|
+
|
|
227
|
+
**A paged remote catalogue is now read to the end.** `tools/list`,
|
|
228
|
+
`resources/list` and `resources/templates/list` each sent an empty params
|
|
229
|
+
object and returned the first page — never sending a cursor, never reading
|
|
230
|
+
the one that came back. A server that pages its catalogue contributed only
|
|
231
|
+
its first page: the rest were never registered, never namespaced, never
|
|
232
|
+
advertised, with no error and no warning. Drift detection did not help
|
|
233
|
+
either, since it compared page one against page one.
|
|
234
|
+
|
|
235
|
+
The symptom is a model that never uses a tool it was told about, which reads
|
|
236
|
+
as model incompetence rather than a client bug. Both clients — the SDK's and
|
|
237
|
+
the CLI's — now thread the cursor. A server whose cursor never ends is
|
|
238
|
+
refused after 100 pages rather than looping forever or stopping silently,
|
|
239
|
+
since stopping silently is the failure being fixed.
|
|
240
|
+
|
|
241
|
+
### Minor Changes
|
|
242
|
+
|
|
243
|
+
- 935b8f3: An answer can cite the document it came from
|
|
244
|
+
|
|
245
|
+
Sending a document buys the provider's native handling of it — page structure, built-in OCR, and the ability to say which passage an answer rests on. namzu could send the document and could not receive the third: an answer about a contract arrived as prose, and checking it meant reading the contract again by hand. A citation is the difference between an answer you trust and one you verify.
|
|
246
|
+
|
|
247
|
+
`citations: true` on a document attachment asks for them; they come back on the assistant message as `Citation[]`. Opt-in per document, because the provider splits the document into citable units and the answer carries the passages it leaned on — tokens a turn that never wanted a citation should not pay.
|
|
248
|
+
|
|
249
|
+
The location is a union — `page`, `char` or `block` — rather than a page number, because providers segment differently and the segmentation is theirs. Flattening all three would invent a page number for the two that have none. Web-search and search-result citations are deliberately dropped: they point at something that was never in the request, so there is no attachment to resolve them against, and a citation the reader cannot go and look at is worse than none.
|
|
250
|
+
|
|
251
|
+
Citations ride with the turn that made them, like reasoning blocks, so compaction takes a turn's evidence with it rather than leaving citations pointing at prose that is gone.
|
|
252
|
+
|
|
253
|
+
- 935b8f3: A finished run can leave something behind
|
|
254
|
+
|
|
255
|
+
The SDK could store a memory and could not form one. `MemoryStore` and its disk implementation have been here all along, and the only path into them was the model calling `save_memory` — so a run that worked out a durable fact and never thought to write it down lost it at settle, along with everything the compaction pass had already extracted and structured on the way.
|
|
256
|
+
|
|
257
|
+
The extraction was already built: compaction distils the transcript into decisions, discoveries, requirements and failures precisely because a list of facts is worth more than a summary of prose. That structure was serialized into one system message and then dropped when the run ended. `promoteMemory` is called once, at settle, with it.
|
|
258
|
+
|
|
259
|
+
A callback rather than a store the runtime writes into — what is worth remembering is a policy question the host owns, and a runtime that decided it would write a row for every run whether or not anything happened. It is called for a failed run too (the approach that failed is exactly what a later run should not pay for twice), it is awaited rather than fire-and-forget (a one-shot process exits as soon as the run returns), and a throw is swallowed and logged, because a memory that failed to form must not retract an answer that was already produced.
|
|
260
|
+
|
|
261
|
+
- 935b8f3: A host can judge the **answer**, and one agent instance runs one thing at a
|
|
262
|
+
time.
|
|
263
|
+
|
|
264
|
+
**`reviewAnswer` closes the verify-then-fix loop.** `stopWhen` is evaluated
|
|
265
|
+
after each step's _tools_ have run, so it had nothing to say at the moment
|
|
266
|
+
the model stopped calling them — the run finalized with whatever it had
|
|
267
|
+
produced. Running the build, feeding the failure back, and letting the
|
|
268
|
+
model try again meant starting a whole new run and re-supplying the context
|
|
269
|
+
the first one had already assembled.
|
|
270
|
+
|
|
271
|
+
The reviewer sees the answer and the history, and either accepts or returns
|
|
272
|
+
feedback that becomes the next user turn. Three properties carry it:
|
|
273
|
+
|
|
274
|
+
- **Bounded** by `maxAnswerReviews` (default 3), stopping with
|
|
275
|
+
`stopReason: 'answer_rejected'`. The distinct reason matters: without it
|
|
276
|
+
a reviewer that never accepts ends the run on `max_iterations`, naming
|
|
277
|
+
the resource it exhausted rather than the judgement that exhausted it,
|
|
278
|
+
and the reader goes looking for a loop instead of at the reviewer.
|
|
279
|
+
- **Never on the forced-final turn**, which exists to extract a closing
|
|
280
|
+
summary under pressure. Rejecting it would spend budget the run has
|
|
281
|
+
already run out of.
|
|
282
|
+
- **A reviewer that throws ACCEPTS** — the opposite of the safety gates,
|
|
283
|
+
deliberately. Those are asked "is this dangerous", where failing closed
|
|
284
|
+
costs one refused operation; this is asked "is this good enough", where
|
|
285
|
+
failing closed hands the answer back forever and turns every run into a
|
|
286
|
+
loop. One unreviewed answer is the cheaper failure, and the throw is
|
|
287
|
+
logged at `error` so it is never mistaken for approval.
|
|
288
|
+
|
|
289
|
+
Shaped after the structured-output re-prompt directly above it in the loop,
|
|
290
|
+
which solves the same problem for one specific judge.
|
|
291
|
+
|
|
292
|
+
**The invocation lock now has a caller.** `InvocationLock`,
|
|
293
|
+
`ConcurrentInvocationError` and `acquireInvocationLock` were all defined and
|
|
294
|
+
exported, and no agent ever acquired the lock — so concurrent invocations of
|
|
295
|
+
one instance were not prevented and the error type could not be thrown by
|
|
296
|
+
anything.
|
|
297
|
+
|
|
298
|
+
They are genuinely unsafe: `abortController` and `currentRunId` are
|
|
299
|
+
_instance_ state. Two overlapping runs share one abort controller, so
|
|
300
|
+
cancelling either kills both, and the second clobbers the first's run id, so
|
|
301
|
+
a later `cancel()` cancels the wrong run. Neither failure announces itself —
|
|
302
|
+
the first run simply stops, or the wrong one does. A host that wants
|
|
303
|
+
parallelism constructs a second instance, which is cheap; sharing one was
|
|
304
|
+
never the supported shape, it merely was not refused.
|
|
305
|
+
|
|
306
|
+
This is the other half of `ConcurrencyMode`, removed earlier in this release
|
|
307
|
+
as an unreachable type promising a `queue` mode that was never built.
|
|
308
|
+
|
|
309
|
+
- 935b8f3: Any tool can raise a durable pause
|
|
310
|
+
|
|
311
|
+
The pause-for-a-human machinery is durable and complete, and it was reachable from exactly four kernel-owned points: the plan gate, the tool-review gate, the iteration cadence, and the built-in question tool. A host-authored tool had no seam to it — the operations that most want their own confirmation with their own wording, a spend, an outbound post, a destructive migration, had to settle for the generic tool-review gate or hand-thread a recorder and a resume callback into a private builder, which nothing in `ToolContext` suggested was possible.
|
|
312
|
+
|
|
313
|
+
`context.requestPause({ name, prompt, options })` is that machinery behind one function. The pause is written as a real checkpoint, so it appears on every surface a tool-review park appears on and survives the process dying, and on resume the answer routes back **by name** — several tools pausing in one batch each get their own, and one call may pause more than once.
|
|
314
|
+
|
|
315
|
+
The outcome is `answered`, `unanswered`, or `aborted`. Silence is deliberately not a variant of `answered` with an empty selection: a tool that asks "may I charge this card" and reads silence as yes is worse than one that never asked, so the absence of an answer has its own shape and cannot be destructured into consent. An option id the tool never offered is dropped for the same reason.
|
|
316
|
+
|
|
317
|
+
`requestPause` is optional on the context, because a host calling a tool directly provides no route to a human.
|
|
318
|
+
|
|
319
|
+
- 935b8f3: Four places where namzu knew something and told no one.
|
|
320
|
+
|
|
321
|
+
**A backoff is now visible.** `withProviderRetry` logged and slept. There
|
|
322
|
+
was no run event, no wire event, and — worse than that — the sole
|
|
323
|
+
production call site never passed a logger, and every warn in the decorator
|
|
324
|
+
is guarded behind it, so the log lines were dead code too. A run could sit
|
|
325
|
+
silent for the better part of a minute between `iteration_started` and the
|
|
326
|
+
next event, or up to the 60s server-directed cap, with no signal and no
|
|
327
|
+
keepalive: a backoff was indistinguishable from a hang, and a host's
|
|
328
|
+
watchdog would cancel a run that was about to succeed.
|
|
329
|
+
|
|
330
|
+
A `provider_retry` run event now carries the attempt, the ceiling, the
|
|
331
|
+
delay, the classified code and whether the server asked for it, mapped to
|
|
332
|
+
`provider.retry` on the SSE wire and to a `running` status update over A2A.
|
|
333
|
+
It is emitted **before** the sleep, so the delay it names is still ahead —
|
|
334
|
+
which is also why it rides the stream as a delta-less chunk rather than an
|
|
335
|
+
out-of-band callback: the consumer is blocked inside the provider's
|
|
336
|
+
iterator, so a callback could not reach it until the wait was already over.
|
|
337
|
+
The omission was never principled; `tool_progress` exists to answer "is it
|
|
338
|
+
still working?" and the wire contract justifies the reasoning events on
|
|
339
|
+
exactly the same grounds.
|
|
340
|
+
|
|
341
|
+
**Two latency measurements that could not be recovered from the data.**
|
|
342
|
+
`gen_ai.client.time_to_first_token` is recorded at the first delta of any
|
|
343
|
+
kind. namzu streams, so perceived latency is dominated by that number, and
|
|
344
|
+
the one existing latency histogram measures the whole request — it cannot
|
|
345
|
+
tell a fast-first-token long generation from a stalled one, and no host
|
|
346
|
+
could reconstruct the difference in any form.
|
|
347
|
+
`gen_ai.tool.call.duration` records what the executor has measured since
|
|
348
|
+
its first version: the value was already in scope one frame above the call
|
|
349
|
+
site, emitted per call on `tool_completed`, and had no instrument. It
|
|
350
|
+
carries the same attributes as the tool-call counter, so "which tool is
|
|
351
|
+
slow" and "which tool fails" are one query rather than two that cannot be
|
|
352
|
+
joined.
|
|
353
|
+
|
|
354
|
+
**`run_failed` carries the classification it always had.** The event was a
|
|
355
|
+
bare string, and the run boundary flattened the throwable into it,
|
|
356
|
+
discarding `code`, `status`, `retryAfterMs`, `retryable`, `details` and the
|
|
357
|
+
cause chain. This was never a missing taxonomy: the provider-boundary
|
|
358
|
+
classifier already walks all of that, so a fully-populated error arrived at
|
|
359
|
+
the boundary and was thrown away one line later — and `toPlatformError`,
|
|
360
|
+
the projection written for exactly this, had no callers outside its own
|
|
361
|
+
test. `run_failed` now carries `failure` alongside `error`; the A2A bridge
|
|
362
|
+
sends it as event metadata (a peer deciding whether to retry needs the
|
|
363
|
+
flag, not prose to pattern-match) and the CLI prefixes the code. Nothing
|
|
364
|
+
had to change at the hundreds of `throw` sites.
|
|
365
|
+
|
|
366
|
+
Not fixed, and worth naming: the advisory `on_error` trigger still
|
|
367
|
+
substring-matches. Its input is tool output from the message history, which
|
|
368
|
+
has no structured code to preserve — that needs a tool-side error catalog,
|
|
369
|
+
not this change.
|
|
370
|
+
|
|
371
|
+
**The published attribute constants can no longer drift.**
|
|
372
|
+
`@namzu/telemetry/attributes` restated the attribute bags by hand and had
|
|
373
|
+
already lost `GENAI.TOKEN_TYPE`, the dimension that splits the token
|
|
374
|
+
counter by kind. The consequence was narrow — namzu emits through the
|
|
375
|
+
canonical module, so the dimension is on the data regardless — but this is
|
|
376
|
+
the entry point the observability docs steer consumers to, the package had
|
|
377
|
+
no tests at all, and the public-surface verifier only loads the SDK bundle.
|
|
378
|
+
It is now a re-export, with a parity test so a future hand-copy fails
|
|
379
|
+
immediately.
|
|
380
|
+
|
|
381
|
+
- 935b8f3: Add programmable stop conditions and a per-step record.
|
|
382
|
+
|
|
383
|
+
`GuardCoordinator` was the loop's only halt, and it consumes
|
|
384
|
+
`{aborted, totalTokens, totalCost, currentIteration, startTime}` — it never
|
|
385
|
+
sees messages, tool calls or results. So a terminal `submit_answer` /
|
|
386
|
+
`verify_outputs` tool could not end a run: the model had to be prompt-begged to
|
|
387
|
+
stop, with `maxIterations: 200` or the token budget as the only backstop, which
|
|
388
|
+
meant a finished task still burned its whole envelope. "Stop after three steps
|
|
389
|
+
without progress" and "stop when the plan is complete" were inexpressible.
|
|
390
|
+
|
|
391
|
+
**`StepResult`** records what each iteration did — model, message id, content,
|
|
392
|
+
tool calls, tool results, finish reason, per-step usage and cost delta, start
|
|
393
|
+
time, total duration and time spent inside tools. Every field was already
|
|
394
|
+
computed somewhere in the loop; none of it was reachable, because neither `Run`
|
|
395
|
+
nor `BaseAgentResult` had a `steps[]`. A host that persisted the returned `Run`
|
|
396
|
+
— the natural thing — permanently lost per-step attribution, and answering
|
|
397
|
+
"which step cost the most" meant correlating raw `RunEvent`s by iteration number
|
|
398
|
+
and diffing cumulative counters.
|
|
399
|
+
|
|
400
|
+
- `Run.steps` carries the record, including on a failed run.
|
|
401
|
+
- `onStepFinish(step)` fires as each step completes.
|
|
402
|
+
- `stopWhen` is evaluated **after** the step's tools have run, so a predicate
|
|
403
|
+
sees what they returned. That ordering is what lets a terminal tool end the
|
|
404
|
+
run _after_ executing rather than instead of executing — its output is still
|
|
405
|
+
recorded and still reaches the model's history.
|
|
406
|
+
- Helpers: `stepCountIs(n)`, `hasToolCall(...names)`, `anyOf(...conditions)`.
|
|
407
|
+
Conditions may be async.
|
|
408
|
+
- A predicate that throws is logged and treated as "do not stop": failing open
|
|
409
|
+
leaves the existing budgets in charge rather than killing a healthy run.
|
|
410
|
+
- New `StopReason: 'stop_condition'`.
|
|
411
|
+
|
|
412
|
+
`runToolReview` now returns its tool outcomes alongside its decision, so the
|
|
413
|
+
loop builds the step record from what actually ran instead of re-deriving it
|
|
414
|
+
from the messages it just pushed.
|
|
415
|
+
|
|
416
|
+
- 935b8f3: Durable run state: a parked approval now survives a process boundary.
|
|
417
|
+
|
|
418
|
+
A HITL park used to exist only as a suspended `await` inside one process.
|
|
419
|
+
The checkpoint written just before it looked identical to any mid-run
|
|
420
|
+
checkpoint, so nothing in durable state said a human owed the run an
|
|
421
|
+
answer — an approval queue could not be rebuilt, and a serverless host
|
|
422
|
+
could not park a run at all, because the container holding the promise had
|
|
423
|
+
to stay alive.
|
|
424
|
+
|
|
425
|
+
- `IterationCheckpoint.pending` records the `HITLDecisionRequest` verbatim,
|
|
426
|
+
plus the answer once it arrives (kept as evidence, not erased).
|
|
427
|
+
- `findPendingCheckpoint(store, scope)` — the read an approval queue is
|
|
428
|
+
built from, in any process.
|
|
429
|
+
- `RunState` + `captureRunState` / `loadRunState` / `parseRunState`: a
|
|
430
|
+
flat, JSON-safe snapshot with a version guard, so a snapshot written by
|
|
431
|
+
one deployment cannot silently half-restore in another.
|
|
432
|
+
- `QueryParams.pendingDecision` applies a decision collected out-of-band to
|
|
433
|
+
the exact tool calls the human was shown. Without it a resumed run
|
|
434
|
+
repaired the unanswered `tool_use` blocks away and let the model
|
|
435
|
+
re-decide, so "yes, delete that row" degraded into "ask the model again
|
|
436
|
+
and hope it asks for the same thing". The decision is ignored (and the
|
|
437
|
+
repair path runs) when the checkpoint's calls no longer match the
|
|
438
|
+
recorded request — consent to one batch is not consent to another.
|
|
439
|
+
- A `pause` decision keeps the park outstanding; every other action
|
|
440
|
+
resolves it. A host that cannot block answers `pause` immediately and
|
|
441
|
+
comes back in another process.
|
|
442
|
+
- Park recording is lazy (`parkRecordDelayMs`, default 250ms) so a
|
|
443
|
+
programmatic handler never pays for it — except `pause`, which is always
|
|
444
|
+
recorded because it means the decision is still owed.
|
|
445
|
+
|
|
446
|
+
- 935b8f3: Retry works on a wrapped error, and the runtime actually emits metrics.
|
|
447
|
+
|
|
448
|
+
**Every error signal is now read across the whole cause chain.** It was read
|
|
449
|
+
off the error handed in, so one layer of wrapping hid it — and wrapping is
|
|
450
|
+
the normal case, not an edge one: a vendor SDK wraps its transport error and
|
|
451
|
+
the runtime wraps again on the way out. A rate limit wrapped **once**
|
|
452
|
+
classified as `unknown`, which is treated as non-retryable, so the retry
|
|
453
|
+
policy was dead for every failure that was not the outermost throwable. A
|
|
454
|
+
socket reset two levels down was likewise unknown — the one class of failure
|
|
455
|
+
where retrying is almost always right.
|
|
456
|
+
|
|
457
|
+
Status, transport errno, `Retry-After`, and message text are all searched
|
|
458
|
+
along the chain now, outermost first, with a `seen` set so a cause cycle
|
|
459
|
+
(easy to build by accident when errors are re-wrapped in a retry loop)
|
|
460
|
+
terminates instead of hanging. Precedence is unchanged — status, then errno,
|
|
461
|
+
then message — and an unwrapped error classifies exactly as before.
|
|
462
|
+
|
|
463
|
+
**The runtime emitted spans and not one measurement.** Metrics lived in a
|
|
464
|
+
bag a host was expected to construct, and nothing in the workspace ever
|
|
465
|
+
constructed one. Worse, the bag bound its instruments eagerly, so one built
|
|
466
|
+
before `registerTelemetry()` captured the no-op meter and discarded every
|
|
467
|
+
write for the rest of its life — silently, forever, from a line of call
|
|
468
|
+
order.
|
|
469
|
+
|
|
470
|
+
- The instruments now live beside the code that records them, and the
|
|
471
|
+
runtime records token usage and model latency per call, tool outcomes per
|
|
472
|
+
call, and run duration per run.
|
|
473
|
+
- Instruments resolve **lazily** and re-resolve when a real provider is
|
|
474
|
+
installed, so registration order no longer decides whether anything is
|
|
475
|
+
measured.
|
|
476
|
+
- One token metric split by `gen_ai.token.type`, not two under two names
|
|
477
|
+
with the second invented — a dashboard aggregating the conventional name
|
|
478
|
+
was getting input tokens only and under-reporting usage by roughly half.
|
|
479
|
+
- Cache reads and writes are recorded as their own token types. They bill
|
|
480
|
+
differently, so a total that hides them cannot explain a bill.
|
|
481
|
+
- Tool calls carry an error type, so a broken tool can be told apart from
|
|
482
|
+
one whose input the model keeps getting wrong.
|
|
483
|
+
- `createPlatformMetrics()` still works and now delegates to the same
|
|
484
|
+
instruments, so host and runtime measurements aggregate instead of
|
|
485
|
+
describing the same events under two names.
|
|
486
|
+
|
|
487
|
+
- 935b8f3: A delegated run now joins the trace it belongs to.
|
|
488
|
+
|
|
489
|
+
Every run started its own ROOT span, including a spawned sub-agent's. A
|
|
490
|
+
supervisor delegating to three children produced four disconnected traces
|
|
491
|
+
instead of one tree — the same defect that made a 20-turn run show up as 21
|
|
492
|
+
roots before iterations were parented, except across the spawn boundary,
|
|
493
|
+
where the delegation structure is exactly what a trace is for.
|
|
494
|
+
|
|
495
|
+
`QueryParams.parentSpan` (and `ReactiveAgentConfig.parentSpan`) parents the
|
|
496
|
+
run span when a caller supplies one. The spawning tool passes its own span,
|
|
497
|
+
so a child run lands inside the turn that asked for it:
|
|
498
|
+
|
|
499
|
+
tool span → child run → child iterations → child tool spans
|
|
500
|
+
|
|
501
|
+
A top-level run with no parent still starts its own root, which is correct:
|
|
502
|
+
it IS the root, and forcing one would be wrong.
|
|
503
|
+
|
|
504
|
+
The parent is stamped onto the child config after `configBuilder` runs
|
|
505
|
+
rather than relying on every builder to forward an option it may not know
|
|
506
|
+
about.
|
|
507
|
+
|
|
508
|
+
- 935b8f3: A conversation with no turn boundary
|
|
509
|
+
|
|
510
|
+
Every other seam in this kernel is turn-based by construction: a run has iterations, an iteration sends a complete message list and reads a stream back, and a checkpoint is taken between two of them. That shape cannot describe a duplex session, where input keeps arriving while output is still being produced and "the turn" is not something either side can point at.
|
|
511
|
+
|
|
512
|
+
`BidiProvider` / `BidiSession` is a second contract rather than a widening of the first — bending `chatStream` to accept a live input channel would put a half-duplex assumption inside every consumer of the turn-based path in exchange for a duplex path that still would not fit. `startBidiRun` is the loop that runs tools against it.
|
|
513
|
+
|
|
514
|
+
Two properties matter here that the turn-based loop never needs. A tool must not block the stream: awaiting one inline would stall the very events an interruption arrives on, so calls start and are not awaited. And an interruption invalidates work in flight: a call still running when the human speaks over the model is abandoned rather than delivered, because a stale answer in a conversation that has moved on is worse than no answer.
|
|
515
|
+
|
|
516
|
+
Audio capture and playback are not here — the types carry audio, but the microphone belongs to the host. Neither is checkpoint/resume: a duplex session's state lives on the far side of a socket with no boundary to snapshot at, and checkpoints that cannot restore would be worse than none. The contract ships with a scripted driver, which is how the turn-based path is developed too.
|
|
517
|
+
|
|
518
|
+
- 935b8f3: Seven places where state, consent or a verdict did not survive the boundary
|
|
519
|
+
it needed to cross.
|
|
520
|
+
|
|
521
|
+
**A checkpoint is versioned on disk, and its budgets are checked.** It was
|
|
522
|
+
written bare and read with a cast. Unstamped is read as version 1 by
|
|
523
|
+
definition, which is correct only while version 1 is the only version there
|
|
524
|
+
has ever been — the moment a second exists, a file written by the newer
|
|
525
|
+
build is read by the older one as if it were the older shape, and the
|
|
526
|
+
refusal that exists to prevent exactly that never fires. There was no chain
|
|
527
|
+
to hang a migration on. Separately, the read validated `id`, `iteration`,
|
|
528
|
+
`createdAt` and `messages` and skipped `tokenUsage` / `costInfo` /
|
|
529
|
+
`guardState` — which a resume dereferences before its first iteration. A
|
|
530
|
+
run recalled at $4.80 of a $5 cap whose cost came back malformed continued
|
|
531
|
+
with `NaN`, which compares false against every limit, so the guard that
|
|
532
|
+
exists to stop it never stopped it. Both read paths now refuse.
|
|
533
|
+
|
|
534
|
+
**A resumed run joins the trace it crashed inside.** `parentContext`
|
|
535
|
+
accepted only a live in-memory span, so a parent that had to survive a
|
|
536
|
+
process boundary could not be expressed. A run that crashed at iteration 12
|
|
537
|
+
and resumed produced two traces with different ids and no link. The run id
|
|
538
|
+
correlated them well enough to find both by query and not well enough to
|
|
539
|
+
see one waterfall — and for a replay fork, which mints a new run id, not
|
|
540
|
+
even that. Checkpoints now record a serialized span context, read back
|
|
541
|
+
_before_ the root span is minted because a parent can only be set at
|
|
542
|
+
creation. An all-zero or malformed id is refused rather than emitted, since
|
|
543
|
+
an exporter drops those silently and that would be worse than the
|
|
544
|
+
disconnected traces it replaces.
|
|
545
|
+
|
|
546
|
+
**A park can expire.** `runConfig.hitlParkTtlMs` writes an ABSOLUTE
|
|
547
|
+
deadline. Every timer in the SDK is an in-process `setTimeout` and the
|
|
548
|
+
park-record delay is deliberately `unref`'d, so nothing in memory outlives
|
|
549
|
+
a redeploy: a run parked for approval, the worker was replaced, nobody
|
|
550
|
+
answered, and the checkpoint stayed outstanding forever with every
|
|
551
|
+
approval-queue reader serving it. The run timeout cannot cover it — it is
|
|
552
|
+
checked between iterations and a park suspends mid-iteration, so a
|
|
553
|
+
long-lived process hard-stops the run immediately _after_ the human
|
|
554
|
+
approves, while across a restart the restored clock excludes parked time
|
|
555
|
+
entirely. `findPendingCheckpoint` skips an expired park, `listExpiredParks`
|
|
556
|
+
lets a host sweep, and `expire` records the expiry rather than deleting the
|
|
557
|
+
evidence.
|
|
558
|
+
|
|
559
|
+
**Two reserved statuses finally have producers.** `deriveRunStatus`
|
|
560
|
+
projects a run plus its park onto the session-layer `RunStatus`, which was
|
|
561
|
+
consumed by session derivation and handoff gating and produced by nothing —
|
|
562
|
+
`awaiting_hitl_resolution` in particular documented a "persisted wait after
|
|
563
|
+
a HITL timeout" for a timeout nothing could raise. `toWireRunStatus`
|
|
564
|
+
implements the domain→wire collapse that `WireRunStatus` had documented and
|
|
565
|
+
never had as code.
|
|
566
|
+
|
|
567
|
+
**An approval can be remembered, at a scope the approver chooses.**
|
|
568
|
+
Approving recorded nothing anywhere. `bash` is unconditionally
|
|
569
|
+
non-read-only and in no allowlist, so `bash: git status` re-prompted on
|
|
570
|
+
every batch forever, and the only escape was a blanket session grant
|
|
571
|
+
covering every destructive call. `approve_tools` now takes `remember`,
|
|
572
|
+
`toolGrantKeys(call)` offers a narrow (this exact invocation) and a wide
|
|
573
|
+
(this tool) key, and a batch fully covered by recorded grants skips the
|
|
574
|
+
park. Non-reuse stays the default — nothing is remembered unless an
|
|
575
|
+
explicit approval says so, and grants are run-scoped, never persisted.
|
|
576
|
+
Argument key order is normalised so the same call is not asked about twice.
|
|
577
|
+
|
|
578
|
+
**An eval case can fail on a gate, not just on an average.** The verdict
|
|
579
|
+
was one unweighted mean against one suite-wide threshold. At the default of
|
|
580
|
+
1 the harness never reports a false pass, but a trajectory F1 and a graded
|
|
581
|
+
judge can essentially never reach 1 — so every real suite lowers it, and
|
|
582
|
+
every step down buys the deterministic scorers the same tolerance as the
|
|
583
|
+
fuzzy ones. At 0.75, trajectory 0 alongside three perfect scores averages
|
|
584
|
+
to 0.75 and reports **passed**. `Scorer.severity: 'gate'` fails the case
|
|
585
|
+
outright; `threshold` is per-scorer. `completionScorer` and
|
|
586
|
+
`containsScorer` ship as gates. An unavailable gate does not fail a case —
|
|
587
|
+
it did not judge the run, which is the inconclusive path.
|
|
588
|
+
|
|
589
|
+
**A provider's own retryable flag is listened to, and a plugin that cannot
|
|
590
|
+
enable is refused at install.** Retryability was derived solely from
|
|
591
|
+
namzu's code set, a second-hand inference that necessarily lags every new
|
|
592
|
+
failure shape a vendor invents; a flag declared anywhere on the cause chain
|
|
593
|
+
now decides, while the code still decides what the failure _is_. And the
|
|
594
|
+
plugin manifest accepted `skills` / `connectors` / `personas` with per-type
|
|
595
|
+
caps that enabling then refused wholesale — so a plugin shipping four tools
|
|
596
|
+
and one skill validated clean, installed clean, was persisted as
|
|
597
|
+
`installed`, and contributed zero tools. The refusal moved to load time,
|
|
598
|
+
with the enable-time check kept as a backstop that transitions the plugin
|
|
599
|
+
to `error` rather than leaving a status that says it is fine.
|
|
600
|
+
|
|
601
|
+
- 935b8f3: The two gaps that were deferred as needing their own design session.
|
|
602
|
+
|
|
603
|
+
**A question raised inside a tool is now durable, and the answer reaches
|
|
604
|
+
the tool that asked.** `ask_user_question` parked through the raw handler
|
|
605
|
+
under a synthetic `cp_question_<toolUseId>` id that was never written
|
|
606
|
+
anywhere. The checkpoint did not exist: nothing on disk said a human owed
|
|
607
|
+
this run an answer, the pending-checkpoint lookup could never return it,
|
|
608
|
+
and a remote host could not even _observe_ the question except through the
|
|
609
|
+
in-process callback. Kill the process while somebody is looking at the card
|
|
610
|
+
and the answer could never be applied — the restore path stripped the whole
|
|
611
|
+
assistant turn, discarding work that sibling tools in the same batch had
|
|
612
|
+
already finished, and re-billed the turn.
|
|
613
|
+
|
|
614
|
+
The park is now a real checkpoint, with `user_question_asked` /
|
|
615
|
+
`user_question_answered` on the event stream, `question.asked` /
|
|
616
|
+
`question.answered` on the SSE wire, and an `input-required` A2A status —
|
|
617
|
+
the same surfaces a tool-review park has always had.
|
|
618
|
+
|
|
619
|
+
The re-entry contract was the deferred half, and it turned out to reuse
|
|
620
|
+
machinery that already exists. A question checkpoint is written
|
|
621
|
+
mid-execution, so it holds the assistant turn with its `tool_use` blocks
|
|
622
|
+
unanswered — the same shape a tool-review park leaves. Re-executing that
|
|
623
|
+
batch is _how_ the asking tool gets re-entered; a carried-answer registry
|
|
624
|
+
is what makes the re-entry return the recorded answer instead of parking a
|
|
625
|
+
second time; and every sibling that already completed is answered from the
|
|
626
|
+
transcript by the crash-resume recovery, so nothing runs twice. An answer
|
|
627
|
+
that does not name a call in this turn is refused rather than delivered to
|
|
628
|
+
whichever tool now holds that slot.
|
|
629
|
+
|
|
630
|
+
**The egress policy has a boundary to be enforced at.** Two of its four
|
|
631
|
+
shapes were honourable nowhere: the container backend refused a host
|
|
632
|
+
allowlist outright because it had nothing to filter through, so `deny-all`
|
|
633
|
+
and `allow-all` were the whole spectrum — all or nothing.
|
|
634
|
+
|
|
635
|
+
`EgressProxy` enforces the other two. Matching has exactly two forms —
|
|
636
|
+
exact host, and `.example.com` for a domain and its subdomains — and
|
|
637
|
+
substring is deliberately not one of them: `host.includes(entry)` would
|
|
638
|
+
admit `example.com.attacker.net`, and plain suffix matching would admit
|
|
639
|
+
`notexample.com`. A policy that cannot be read denies, because an allowlist
|
|
640
|
+
that fails open is not an allowlist. A request addressed to the proxy
|
|
641
|
+
itself is refused rather than forwarded — found by a test that hung instead
|
|
642
|
+
of failing, which is exactly the shape that failure takes in production.
|
|
643
|
+
|
|
644
|
+
`Sandbox.setNetworkPolicy` narrows or widens a **live** sandbox, so "clone
|
|
645
|
+
with a token, then drop to deny-all before running untrusted build scripts"
|
|
646
|
+
is expressible; it was not, because the policy was frozen at provider
|
|
647
|
+
construction. A backend that cannot enforce it throws.
|
|
648
|
+
|
|
649
|
+
And `brokeredCredentials` settles where the token lives. Any credential the
|
|
650
|
+
agent needed to reach an allowed host had to be inside the sandbox, in the
|
|
651
|
+
environment, readable by the untrusted code it is meant to be isolated from
|
|
652
|
+
— via `/proc/self/environ`, or via a prompt injection that exfiltrates it
|
|
653
|
+
over the very egress the policy permits. The real value is now held
|
|
654
|
+
host-side and applied at the boundary, scoped per host: a credential
|
|
655
|
+
attached to every request is a credential handed to whichever host the
|
|
656
|
+
agent was talked into contacting.
|
|
657
|
+
|
|
658
|
+
One limit, stated rather than hidden: a credential cannot be injected into
|
|
659
|
+
a CONNECT tunnel, because reading those bytes would mean terminating TLS
|
|
660
|
+
with a CA the sandbox trusts — a strictly larger risk than the one being
|
|
661
|
+
mitigated. A workload that needs brokering speaks plain HTTP to the proxy
|
|
662
|
+
and lets it upgrade upstream. The allowlist is enforced on CONNECT either
|
|
663
|
+
way, since the target names the host in clear text.
|
|
664
|
+
|
|
665
|
+
- 935b8f3: Two findings from a fit-gap against another agent SDK.
|
|
666
|
+
|
|
667
|
+
**A tool veto that throws now denies.** namzu has several places that can
|
|
668
|
+
stop a run, and they disagreed on what happens when the check _itself_
|
|
669
|
+
throws: a content guardrail that threw blocked the run — with a comment
|
|
670
|
+
saying why, "safety is unknown" — while a tool veto that threw was skipped
|
|
671
|
+
and the call proceeded. The same policy inverted its security posture
|
|
672
|
+
depending on which surface it was written on.
|
|
673
|
+
|
|
674
|
+
An observer probe that throws is still skipped, and that asymmetry IS
|
|
675
|
+
deliberate: an observer was never asked a question, so it has no answer to
|
|
676
|
+
withhold, and taking a run down because a metrics handler crashed would be
|
|
677
|
+
the same mistake pointing the other way.
|
|
678
|
+
|
|
679
|
+
The exposure this trades against is real and is the one the guardrail
|
|
680
|
+
already accepted: a buggy veto can refuse every call. The refusal names the
|
|
681
|
+
probe, so it is diagnosable; a wrongly permitted destructive call is not
|
|
682
|
+
recoverable at all. `docs/sdk/architecture/safety.md` now states the rule
|
|
683
|
+
for all four surfaces in one table.
|
|
684
|
+
|
|
685
|
+
The old behaviour was pinned by a test that described it and never argued
|
|
686
|
+
for it, under a header pointing at a design document that had since been
|
|
687
|
+
frozen and removed — so the instruction to "update it first" could not be
|
|
688
|
+
followed, and the fail-open kept its ratified status with no surviving
|
|
689
|
+
justification. The pointer now names a document a reader can open.
|
|
690
|
+
|
|
691
|
+
**A truncated tool result says what it took with it.** The output budget
|
|
692
|
+
takes `output: string` only, so the rich channel was never bounded — and
|
|
693
|
+
when the text half truncated, the rich half was dropped with it, silently.
|
|
694
|
+
Dropping is right, since the preview is no longer the tool's own payload
|
|
695
|
+
and an image alongside it would be illustrating something the model can no
|
|
696
|
+
longer read. Doing it silently is not: the model saw a preview with no way
|
|
697
|
+
to know an image had ever existed, and reasoned as though the tool returned
|
|
698
|
+
text only. The result now names what went, so the agent can ask for a
|
|
699
|
+
smaller region instead of retrying the same call.
|
|
700
|
+
|
|
701
|
+
`maxToolContentBytes` caps the rich channel, and is **off by default** on
|
|
702
|
+
purpose. The right number depends on what a host's tools return and on the
|
|
703
|
+
model's own image budget; inventing one here would either break screenshot
|
|
704
|
+
workflows or be so generous it bounds nothing. Over the cap the channel is
|
|
705
|
+
refused whole rather than trimmed — half a base64 payload is not a smaller
|
|
706
|
+
image, it is a corrupt one.
|
|
707
|
+
|
|
708
|
+
- 935b8f3: Reclaim context by clearing stale tool output, before summarizing
|
|
709
|
+
destructively.
|
|
710
|
+
|
|
711
|
+
Compaction was all-or-nothing: once the threshold hit, every older message
|
|
712
|
+
became a summary and the agent's own reasoning — the decisions, the false
|
|
713
|
+
starts it learned from, the exact wording of a plan — was paraphrased away
|
|
714
|
+
with it. That is a heavy price for a context problem usually caused by
|
|
715
|
+
something much dumber: a handful of enormous tool outputs the agent already
|
|
716
|
+
read, took what it needed from, and moved past.
|
|
717
|
+
|
|
718
|
+
`clearStaleToolResults` replaces the OUTPUT of old, large tool results with
|
|
719
|
+
a short placeholder that names the tool and its original size, so a result
|
|
720
|
+
that turns out to still be needed is one tool call away rather than lost.
|
|
721
|
+
It is safe where trimming is not, because nothing moves — the `tool` message
|
|
722
|
+
keeps its position and its `toolCallId`, so `tool_use` ↔ `tool_result`
|
|
723
|
+
pairing is intact by construction.
|
|
724
|
+
|
|
725
|
+
It runs first in `runCompactionCheck`; if it gets the context back under
|
|
726
|
+
`triggerThreshold`, summarization is skipped entirely and the history stays
|
|
727
|
+
verbatim. New `CompactionConfig` fields: `clearToolResults` (default
|
|
728
|
+
`true`), `keepRecentToolResults` (3), `minToolResultCharsToClear` (1000),
|
|
729
|
+
`preserveToolResultsFrom`.
|
|
730
|
+
|
|
731
|
+
Never clears an error result (the error is what steers the next turn), the
|
|
732
|
+
most recent N results (still in use), or anything below the size floor
|
|
733
|
+
(the placeholder would cost as much). Image payloads are measured by their
|
|
734
|
+
base64 size — a screenshot is the largest thing a tool result can carry and
|
|
735
|
+
exactly the kind of output an agent reads once.
|
|
736
|
+
|
|
737
|
+
- 935b8f3: Wire up emergency crash-save, and correct the README's claim about it.
|
|
738
|
+
|
|
739
|
+
`EmergencySaveManager.attach()` had zero call sites: the handler that writes
|
|
740
|
+
`emergency/<runId>.json` was never installed, so `replay({ fromCheckpoint:
|
|
741
|
+
'emergency' })` read a file nothing ever produced — while the README marketed
|
|
742
|
+
"Emergency save on signal" as a differentiator against six competitors and
|
|
743
|
+
stated "there is no reliance on the user remembering to catch signals; the
|
|
744
|
+
kernel does it."
|
|
745
|
+
|
|
746
|
+
`query()` now installs the handlers when you pass `emergencySave: true`, and
|
|
747
|
+
removes them when the run settles.
|
|
748
|
+
|
|
749
|
+
It is opt-in rather than automatic, which is a deliberate narrowing of the old
|
|
750
|
+
README claim. `attach()` calls `process.on('SIGINT' | 'SIGTERM' |
|
|
751
|
+
'uncaughtException')` with handlers that `process.exit()`; a library must not
|
|
752
|
+
seize its host's termination path by default, and an API server has its own
|
|
753
|
+
drain sequence. The manager is also a singleton whose `attach` detaches
|
|
754
|
+
whoever held it before, so under concurrent runs an automatic attach would
|
|
755
|
+
silently make the last-started run the only one ever saved. Both READMEs now
|
|
756
|
+
say so.
|
|
757
|
+
|
|
758
|
+
The `namzu` CLI opts in — it owns its process end to end, so Ctrl-C mid-run
|
|
759
|
+
leaves a dump under `.namzu/emergency/` instead of losing the turn.
|
|
760
|
+
|
|
761
|
+
- 935b8f3: Five fixes where a subsystem reported more than it delivered.
|
|
762
|
+
|
|
763
|
+
**A sandbox tier now says what it actually enforces.** The local provider
|
|
764
|
+
reported `id = 'local'` / `name = 'Local Sandbox'` and logged at `info` at
|
|
765
|
+
every detected tier, but the tiers are not equivalent: one installs a
|
|
766
|
+
deny-default, deny-network profile; one unshares namespaces without
|
|
767
|
+
remounting anything, so the child still sees the whole host filesystem; and
|
|
768
|
+
one confines nothing at all. A host that deliberately turned isolation
|
|
769
|
+
**on** got a tier-dependent amount of it under one undifferentiated name,
|
|
770
|
+
and no guard, test or doc anywhere keyed on the weakest tier.
|
|
771
|
+
|
|
772
|
+
- `isolationOf(environment)` states per-tier what is enforced —
|
|
773
|
+
`filesystem`, `network`, `process` — deliberately pessimistic. The
|
|
774
|
+
namespace tier reports `filesystem: false`, because a private mount table
|
|
775
|
+
is not confinement.
|
|
776
|
+
- `sandbox.requireIsolation` (also `new LocalSandboxProvider(log, {…})`)
|
|
777
|
+
**throws** when the host cannot supply a control the caller named.
|
|
778
|
+
Refusing is the point: a control that is accepted and then not applied is
|
|
779
|
+
worse than one never offered, because the caller stops looking. Empty by
|
|
780
|
+
default, so best-effort callers are unaffected.
|
|
781
|
+
- Detection now runs the flags it will spawn under instead of checking that
|
|
782
|
+
a binary exists — a host with unprivileged user namespaces disabled
|
|
783
|
+
answers `unshare --version` happily and then fails every spawn. The
|
|
784
|
+
other platform's probe already ran its sandbox for real.
|
|
785
|
+
- The namespace tier also unshares the network, which it previously left
|
|
786
|
+
wide open while the other tier denied it unconditionally.
|
|
787
|
+
- Constructing at the unconfined tier logs a **warning** naming it as such.
|
|
788
|
+
|
|
789
|
+
**Compaction stopped measuring the context one turn late.** The provider's
|
|
790
|
+
prompt measurement describes the request as it was sent, so the assistant
|
|
791
|
+
message and every tool result the turn appended fell outside it — and the
|
|
792
|
+
reading was taken verbatim. Separately, the tool catalogue is assembled
|
|
793
|
+
apart from the message array and never entered the fallback estimate at
|
|
794
|
+
all; a 30-tool registry is easily 10-20k tokens of JSON Schema. Both errors
|
|
795
|
+
point the same way, under-count, so the trigger did not jitter around the
|
|
796
|
+
threshold — it sat systematically late, worst on the turns that grew the
|
|
797
|
+
context the most.
|
|
798
|
+
|
|
799
|
+
**A remote tool's schema keeps its shape.** `$ref` reached the converter's
|
|
800
|
+
permissive branch and became "anything": no type, no shape. Since that node
|
|
801
|
+
is inherently optional in Zod, a `$ref`'d field the server listed as
|
|
802
|
+
`required` stopped being enforced too — an empty payload validated clean and
|
|
803
|
+
was forwarded to the server instead of being rejected with the hint the
|
|
804
|
+
executor already builds. `$defs` + `$ref` is the default output of several
|
|
805
|
+
common schema generators, so a server that did everything right had its
|
|
806
|
+
main argument shown to the model as `{}`. Local pointers are now inlined
|
|
807
|
+
first (cycles cut at the repeat, dangling and non-local pointers left
|
|
808
|
+
permissive), `allOf` is flattened, and `pattern`, the length and range
|
|
809
|
+
bounds, `multipleOf` and the `email`/`uri`/`uuid`/`date-time` formats are
|
|
810
|
+
carried onto the converted node — shown to the model _and_ enforced. The
|
|
811
|
+
conversion is also depth-bounded: a remote schema is untrusted input.
|
|
812
|
+
|
|
813
|
+
**A declared return shape reaches the model, and a structured result is not
|
|
814
|
+
lost.** Servers publish `outputSchema` on a tool listing regardless of
|
|
815
|
+
negotiated protocol revision and it had no slot in the type, so the return
|
|
816
|
+
shape never reached the model at all. It is now carried verbatim —
|
|
817
|
+
shown, never validated — and appended to the description, since no
|
|
818
|
+
provider's tool format has a field for it. `ToolDefinition.outputSchema`
|
|
819
|
+
takes JSON Schema for the same reason. A server that answers with
|
|
820
|
+
`structuredContent` and omits the compatibility text block previously
|
|
821
|
+
produced an EMPTY tool result for a call that succeeded, with no diagnostic
|
|
822
|
+
anywhere; that payload is now serialized into the output, with the raw pair
|
|
823
|
+
available on `result.data`.
|
|
824
|
+
|
|
825
|
+
**A tool batch killed part-way through is resumed, not repeated.** Results
|
|
826
|
+
reach the history only when the whole batch settles, so a hard kill lost
|
|
827
|
+
everything that had already come back and the resumed run re-executed those
|
|
828
|
+
calls — for a `write_file` that is waste, for a payment or an email it is a
|
|
829
|
+
second one. Nothing new had to be recorded: the executor already awaits a
|
|
830
|
+
`tool_completed` per tool, inline, and the transcript already persists it.
|
|
831
|
+
`RunDiskStore.readCompletedTools()` reads it back and `executeBatch` accepts
|
|
832
|
+
those results, so an already-executed call is answered from the record
|
|
833
|
+
while the calls that never ran execute for the first time through the
|
|
834
|
+
ordinary executor — every guard and permission check still applies. The
|
|
835
|
+
discriminator is whether the transcript holds any completion for the turn:
|
|
836
|
+
a tool-review park records its checkpoint _before_ execution, so it has
|
|
837
|
+
none and keeps the existing repair, where re-deciding costs only a round
|
|
838
|
+
trip.
|
|
839
|
+
|
|
840
|
+
- 935b8f3: Show extensions the model call they fire around
|
|
841
|
+
|
|
842
|
+
`pre_llm_call` and `post_llm_call` fired directly beside the request and the reply and were handed neither — only a run id and an iteration number. An extension could observe THAT a call was happening and nothing about what it was, so a prompt audit, a redaction pass, or a per-tenant token ledger had no way to do its job from a hook.
|
|
843
|
+
|
|
844
|
+
`PluginHookContext` now carries `request` on `pre_llm_call` (`model`, `messages`, `toolNames`, `temperature`, `maxTokens`) and `response` on `post_llm_call` (`content`, `toolNames`, `finishReason`, `usage`). Both are projections rather than the wire objects, so driver-specific parameters do not become part of the plugin contract by accident, and tools appear as names because an audit asks which capabilities were offered, not what their schemas look like.
|
|
845
|
+
|
|
846
|
+
Both are read-only and frozen, and the messages are frozen copies. A hook that reshaped the request would change what every later hook sees, making the outcome depend on installation order — shaping a call stays with `prepareStep`, which has one writer by contract.
|
|
847
|
+
|
|
848
|
+
- 935b8f3: Hook order is declared, and a hook deadline stops holding the process open.
|
|
849
|
+
|
|
850
|
+
**Order was install order** — neither declared nor stable, since it depends
|
|
851
|
+
on when each plugin happened to be installed. That is fine for a hook that
|
|
852
|
+
only observes and wrong for one that decides: `executeHooks` short-circuits
|
|
853
|
+
on `skip` and `error`, so a hook that denies a dangerous command only gets
|
|
854
|
+
to deny it if it runs before whatever else stops the chain. A guard that
|
|
855
|
+
fires depending on installation history is not a guard.
|
|
856
|
+
|
|
857
|
+
`PluginHookDefinition.priority` — lower runs first, default `100`, ties
|
|
858
|
+
keeping registration order so a plugin that sets nothing behaves exactly as
|
|
859
|
+
before. Convention: guards below 100, observers above. `post_*` hooks still
|
|
860
|
+
unwind, so a guard at priority 1 runs first on `pre_tool_use` and last on
|
|
861
|
+
`post_tool_use` — the wrapping order a guard needs.
|
|
862
|
+
|
|
863
|
+
**The deadline timer was never cleared.** `setTimeout` was armed per hook
|
|
864
|
+
invocation and left running after the hook resolved, and an armed timer
|
|
865
|
+
keeps the Node event loop alive. Hooks fire on every tool call and every
|
|
866
|
+
model call, so a run of twenty tool calls left twenty live timers and the
|
|
867
|
+
process could not exit until the last one expired. Nothing failed — it just
|
|
868
|
+
hung, for up to the timeout, every time.
|
|
869
|
+
|
|
870
|
+
**`PluginHookContext.signal`** aborts when that deadline expires. The
|
|
871
|
+
runtime stops waiting on a slow hook either way, but without a signal the
|
|
872
|
+
hook never learns it was abandoned: a request inside it keeps a socket open
|
|
873
|
+
and its eventual result is written into a run that moved on.
|
|
874
|
+
|
|
875
|
+
**`registerHook(pluginId, hook)`** attaches a hook without installing a
|
|
876
|
+
plugin from disk. Registration was reachable only through `enable()`, which
|
|
877
|
+
loads a manifest and imports modules by path, so a host that wanted one
|
|
878
|
+
in-process guard had to lay out a plugin directory to get it — and this
|
|
879
|
+
class's own tests were reaching into a private map to work around it,
|
|
880
|
+
constructing entries the real path would never produce.
|
|
881
|
+
|
|
882
|
+
- 935b8f3: A retried invocation can be deduplicated
|
|
883
|
+
|
|
884
|
+
A request goes out, the connection drops, the client retries. Without a key that retry is a second full run — a second set of model calls, and a second set of whatever the tools did. The invocation lock does not help: refusing the retry with `ConcurrentInvocationError` is not what the caller wanted either, because they wanted the answer.
|
|
885
|
+
|
|
886
|
+
`AgentRunConfig.idempotencyKey` makes a duplicate arriving while the first is still running await it and receive its result — the error included, because both callers asked the same question once and telling one of them something different would make the key a lie.
|
|
887
|
+
|
|
888
|
+
In-flight only. A retry that arrives after the first has settled runs again: keeping the answer would turn deduplication into caching, and how stale an answer may be is the host's judgement, not the SDK's. Instance-scoped, like the lock — deduplicating across processes needs somewhere durable to record the key, which is a store the host owns.
|
|
889
|
+
|
|
890
|
+
- 935b8f3: Widen the message model to content blocks: multimodal tool results, `is_error`,
|
|
891
|
+
and reasoning replay.
|
|
892
|
+
|
|
893
|
+
`ToolMessage.content` was `string` and `AssistantMessage` had no slot for
|
|
894
|
+
reasoning, so three separate things died at the provider boundary. Doing them
|
|
895
|
+
as one migration is deliberate — all three need the same widening, and every
|
|
896
|
+
stored transcript, checkpoint and `messages.json` is written in the narrow
|
|
897
|
+
shape, so the cost only grows.
|
|
898
|
+
|
|
899
|
+
**Tool results can carry non-text content.** `ToolResultContent` is
|
|
900
|
+
`string | ToolResultBlock[]`, where a block is text, image or document. String
|
|
901
|
+
stays first-class: the common case is unchanged and every existing tool and
|
|
902
|
+
driver compiles untouched. `@namzu/computer-use`'s `screenshot` returned
|
|
903
|
+
~400 KB–2.7 MB of base64 **as text** — roughly 100k–670k tokens of characters
|
|
904
|
+
no model can decode — so computer use was effectively non-functional; it now
|
|
905
|
+
returns an image block with a short textual description. MCP `image` and
|
|
906
|
+
inline `resource` blocks are passed through instead of being filtered out.
|
|
907
|
+
|
|
908
|
+
**Failures are marked on the wire.** The executor computed `isError`, routed it
|
|
909
|
+
to the SSE bridge, the A2A bridge and the TUI, then dropped it at the provider
|
|
910
|
+
boundary — so the model's trained tool-failure recovery never fired. The
|
|
911
|
+
Anthropic driver now sends `is_error: true`, and the value survives the
|
|
912
|
+
executor's result tuple, which previously narrowed to `{toolCallId, output}`
|
|
913
|
+
before the message was built.
|
|
914
|
+
|
|
915
|
+
**Reasoning is representable and replayed verbatim.** `AssistantMessage.reasoning`
|
|
916
|
+
holds opaque `ReasoningBlock`s (thinking / redacted, with signature or encrypted
|
|
917
|
+
payload). The Anthropic driver used to rebuild every assistant turn as
|
|
918
|
+
`[text?, ...tool_use]` — precisely the pattern the verbatim-echo contract
|
|
919
|
+
prohibits when a `tool_result` follows — and now emits stored reasoning blocks
|
|
920
|
+
first, signature intact.
|
|
921
|
+
|
|
922
|
+
Drivers that cannot express non-text tool results (`@namzu/openai`,
|
|
923
|
+
`@namzu/ollama`) degrade through `toolResultToText`, which renders an explicit
|
|
924
|
+
`[image: …]` placeholder rather than dumping base64 or silently dropping it.
|
|
925
|
+
|
|
926
|
+
This is the outbound half. The Anthropic driver does not yet parse thinking
|
|
927
|
+
blocks out of the stream and `ChatCompletionParams` has no `thinking` field,
|
|
928
|
+
so `reasoning` is populated only when a caller supplies it.
|
|
929
|
+
|
|
930
|
+
- 935b8f3: Make `MockLLMProvider` a scriptable test model that can emit tool calls.
|
|
931
|
+
|
|
932
|
+
The mock accepted `{ model, responseText, responseDelayMs }` and emitted 8-char
|
|
933
|
+
text slices. It never yielded `delta.toolCalls`, and `MOCK_CAPABILITIES`
|
|
934
|
+
declared `supportsTools: false`, so capability negotiation stripped the tool
|
|
935
|
+
surface before a request was even built. A consumer writing a custom tool had
|
|
936
|
+
no supported way to test that the agent loop calls it, that its error string
|
|
937
|
+
comes back as a `tool_result`, or that the model retries — and namzu's own
|
|
938
|
+
maintainers hand-rolled **eight** `implements LLMProvider` fakes across seven
|
|
939
|
+
test files to work around it, each re-implementing the delta bucketing and
|
|
940
|
+
`toolCallEnd` framing that `streamProviderTurn` exists to hide.
|
|
941
|
+
|
|
942
|
+
`MockProviderConfig` now takes `turns: MockTurn[]`, where a turn carries text,
|
|
943
|
+
tool calls, a finish reason, usage, and failure injection. Tool calls are
|
|
944
|
+
emitted with the frame sequence a real driver produces — per-tool `index`, id
|
|
945
|
+
and name first, then argument fragments, then the block-close signal — so a
|
|
946
|
+
test exercises the real consumer path instead of a shortcut through it.
|
|
947
|
+
|
|
948
|
+
- `truncateArguments` reproduces a tool call cut off mid-JSON at `max_tokens`.
|
|
949
|
+
- `error` fails the request with a status (for retry tests);
|
|
950
|
+
`throwAfterChunks` fails mid-stream (for recovery tests).
|
|
951
|
+
- `nextTurn(params, i)` decides each turn from the request that triggered it;
|
|
952
|
+
`onRequest` and `provider.requests` capture what the runtime actually sent,
|
|
953
|
+
so a test can assert on `tools`, `toolChoice` or `cacheControl`.
|
|
954
|
+
- A script shorter than the run repeats its last turn, so a loop bug reads as
|
|
955
|
+
repetition rather than an exhausted-script crash.
|
|
956
|
+
- `supportsTools` / `supportsFunctionCalling` are now `true`.
|
|
957
|
+
|
|
958
|
+
The old `responseText` shorthand still works and becomes a one-turn script.
|
|
959
|
+
|
|
960
|
+
- 935b8f3: Parse reasoning out of the stream, and let a run request extended thinking.
|
|
961
|
+
|
|
962
|
+
This completes the reasoning work: the previous release added storage and
|
|
963
|
+
verbatim replay, but nothing populated it. `StreamChunk.delta` carried only
|
|
964
|
+
`content` and `toolCalls`, so the Anthropic driver's `thinking_delta` and
|
|
965
|
+
`signature_delta` events fell through its `default: // ignore` — the blocks
|
|
966
|
+
could not be captured even in principle. Two consequences: the verbatim-echo
|
|
967
|
+
contract was unsatisfiable in practice, and a streaming UI showed a
|
|
968
|
+
multi-second stall with zero events while the model was demonstrably working.
|
|
969
|
+
|
|
970
|
+
- `StreamChunk.delta.reasoning` carries fragments bucketed by block index,
|
|
971
|
+
exactly like `toolCalls[].index`, closed by `done`.
|
|
972
|
+
- `streamProviderTurn` accumulates them and attaches the finished blocks to
|
|
973
|
+
the response in **stream-index order**, not arrival order — a provider may
|
|
974
|
+
interleave blocks, and the echo contract is about the original ordering.
|
|
975
|
+
- New `reasoning_started` / `reasoning_delta` / `reasoning_completed` run
|
|
976
|
+
events, wire-mapped as `reasoning.*`. The delta is ephemeral, so the
|
|
977
|
+
transcript records the completed block rather than every fragment.
|
|
978
|
+
- The Anthropic driver handles `content_block_start` for
|
|
979
|
+
`thinking`/`redacted_thinking`, forwards `thinking_delta` and
|
|
980
|
+
`signature_delta`, and closes the block on `content_block_stop`.
|
|
981
|
+
- `AgentRunConfig.thinking` (`ThinkingConfig`) is forwarded on every model
|
|
982
|
+
call. The Anthropic driver maps it to `thinking` and **omits
|
|
983
|
+
temperature/top_p/top_k while it is enabled**, because the API rejects them
|
|
984
|
+
together — sending a request known to 400 is worse than dropping a sampling
|
|
985
|
+
knob the caller did not prioritise.
|
|
986
|
+
|
|
987
|
+
Reasoning rides on the assistant message it belongs to, so the replay contract
|
|
988
|
+
holds automatically: trimming or compacting that message takes its thinking
|
|
989
|
+
blocks with it, and no separate atomicity rule is needed in `findSafeTrimIndex`.
|
|
990
|
+
|
|
991
|
+
- 935b8f3: Add a provider failure taxonomy and retry transient model-call failures.
|
|
992
|
+
|
|
993
|
+
No driver in the estate retried anything: a single `429`, `503` or dropped
|
|
994
|
+
socket terminated the run. Nor could one be added, because every driver threw
|
|
995
|
+
its vendor SDK's raw error and the runtime had no way to tell a rate limit
|
|
996
|
+
from a malformed request — classification is the substrate a retry policy
|
|
997
|
+
stands on.
|
|
998
|
+
|
|
999
|
+
`ProviderError` gives failures a `code` (`rate_limit`, `overloaded`,
|
|
1000
|
+
`server_error`, `timeout`, `network`, `auth`, `invalid_request`,
|
|
1001
|
+
`context_length_exceeded`, `content_filter`, `not_found`, `unknown`), a
|
|
1002
|
+
`retryable` flag, the HTTP `status`, and a server-directed `retryAfterMs`
|
|
1003
|
+
parsed from `Retry-After` (both delta-seconds and HTTP-date forms).
|
|
1004
|
+
`classifyProviderError` derives it from status, then transport errno, then
|
|
1005
|
+
message text — so a window overflow arriving as a `400` is filed as
|
|
1006
|
+
`context_length_exceeded` rather than a generic invalid request, because the
|
|
1007
|
+
caller can act on one and not the other.
|
|
1008
|
+
|
|
1009
|
+
`withProviderRetry` wraps any `LLMProvider` with exponential backoff and full
|
|
1010
|
+
jitter, honouring `Retry-After` up to a sanity cap. It retries **only before
|
|
1011
|
+
the first content chunk**: once a delta has been yielded the consumer has
|
|
1012
|
+
already emitted `text_delta` events, so restarting would duplicate output.
|
|
1013
|
+
Aborts propagate untouched, so a Stop still settles the run as `cancelled`.
|
|
1014
|
+
|
|
1015
|
+
`query()` wraps its provider by default; pass `retry: false` to opt out, or a
|
|
1016
|
+
partial config to tune it. The wrapper is transparent to `id`, `name` and
|
|
1017
|
+
`capabilities`, so capability negotiation is unaffected.
|
|
1018
|
+
|
|
1019
|
+
- 935b8f3: Make compaction actually fire, and make it observable.
|
|
1020
|
+
|
|
1021
|
+
The trigger divided the current context size by `runConfig.tokenBudget` whenever
|
|
1022
|
+
`contextWindowTokens` was absent — which was always, since nothing in the estate
|
|
1023
|
+
ever set it. Those are different quantities: `tokenBudget` is a cumulative spend
|
|
1024
|
+
cap, and comparing a live window against it is self-defeating, because the guard
|
|
1025
|
+
force-finalizes at 0.9x that number while compaction needs 0.7x of it. With the
|
|
1026
|
+
shipped CLI's `tokenBudget: 1_000_000` the trigger sat at ~700k. The entire
|
|
1027
|
+
subsystem — working state, extractor, serializer, dangling repair, verifier —
|
|
1028
|
+
was armed and never fired.
|
|
1029
|
+
|
|
1030
|
+
- The divisor is now always a context **window**: `contextWindowTokens` when the
|
|
1031
|
+
host sets one, otherwise resolved from the model id via a new
|
|
1032
|
+
`resolveContextWindow` / `lookupContextWindow`, otherwise a conservative
|
|
1033
|
+
128k default. `tokenBudget` is never the divisor.
|
|
1034
|
+
- Context size prefers the provider's own `promptTokens` from the last turn — a
|
|
1035
|
+
measurement that includes tool schemas, system blocks and image tokens — over
|
|
1036
|
+
the chars/4 heuristic, which remains the fallback before the first turn
|
|
1037
|
+
reports. `RunPersistence.recordTurnUsage()` records it; side-channel calls
|
|
1038
|
+
keep using `accumulateUsage()` so they cannot corrupt the signal.
|
|
1039
|
+
- Two guards (the thrash guard and prior-summary replacement) were gated behind
|
|
1040
|
+
`contextWindowTokens != null` to preserve the legacy path byte-for-byte. That
|
|
1041
|
+
path's actual behavior was "never fires", so the gates are removed — otherwise
|
|
1042
|
+
a consumer that now compacts would accumulate one redundant summary per pass.
|
|
1043
|
+
- New `compaction_completed` run event (wire: `compaction.completed`) carrying
|
|
1044
|
+
before/after message counts and token sizes, whether the size was measured or
|
|
1045
|
+
estimated, and which window was used. Compaction deletes history
|
|
1046
|
+
irrecoverably and previously emitted nothing at all.
|
|
1047
|
+
|
|
1048
|
+
- 935b8f3: Let `MockLLMProvider` declare capabilities and fail mid-tool-arguments.
|
|
1049
|
+
|
|
1050
|
+
Two small additions that let the scriptable mock absorb the last of the
|
|
1051
|
+
hand-rolled test providers:
|
|
1052
|
+
|
|
1053
|
+
- `capabilities` overrides the declaration for one instance. Capability
|
|
1054
|
+
negotiation degrades a run when a driver says it cannot do something, and
|
|
1055
|
+
testing that path means being able to _say_ it — a fixed registry-level
|
|
1056
|
+
declaration cannot express "a driver with no vision".
|
|
1057
|
+
- `rawArguments` emits a raw string instead of serializing `args`, and
|
|
1058
|
+
`throwAfterArguments` throws mid-tool-block. Together they script a provider
|
|
1059
|
+
going idle while streaming tool JSON, which is precisely the failure the
|
|
1060
|
+
truncated-tool-input recovery path exists for — otherwise that path can only
|
|
1061
|
+
be tested by hand-rolling a provider, which is what everyone was doing.
|
|
1062
|
+
|
|
1063
|
+
Six of the eight `implements LLMProvider` fakes across the test suite are now
|
|
1064
|
+
gone. The two that remain are in `registry.test.ts`, which checks that the
|
|
1065
|
+
registry accepts arbitrary provider _constructors_; collapsing those would
|
|
1066
|
+
defeat what they test.
|
|
1067
|
+
|
|
1068
|
+
- 935b8f3: Persisted state carries a schema version, and a record from the future is
|
|
1069
|
+
refused instead of half-read.
|
|
1070
|
+
|
|
1071
|
+
Every read from disk was `JSON.parse(raw) as T` — an unchecked cast with no
|
|
1072
|
+
idea which version of the shape it was looking at. Three things followed,
|
|
1073
|
+
all of them silent:
|
|
1074
|
+
|
|
1075
|
+
- A record written by an **older** build was read as the current shape.
|
|
1076
|
+
Fields added since arrived as `undefined` and flowed into the runtime as
|
|
1077
|
+
though they had been there.
|
|
1078
|
+
- A record written by a **newer** build was read by an older one, which
|
|
1079
|
+
understood some fields and dropped the rest. Write it back and the rest
|
|
1080
|
+
are gone — the only one of these that destroys data.
|
|
1081
|
+
- None of it produced an error, a warning, or a log line. A resumed session
|
|
1082
|
+
that quietly lost half its state looked exactly like one that never had
|
|
1083
|
+
it.
|
|
1084
|
+
|
|
1085
|
+
The version is stamped as a field on the record rather than wrapping it in
|
|
1086
|
+
an envelope, so **every file already on disk stays readable**: a record with
|
|
1087
|
+
no stamp _is_ version 1, which is exactly what those files are.
|
|
1088
|
+
|
|
1089
|
+
- `defineSchema` / `stamp` / `migrate` in `store/schema.ts`, adopted by the
|
|
1090
|
+
session, thread, run, task and memory disk stores. Each store versions its
|
|
1091
|
+
on-disk format as a unit, so no call site carries schema plumbing.
|
|
1092
|
+
- A record from a version this build does not understand throws
|
|
1093
|
+
`SchemaVersionError` naming what it found and what is supported. Refusing
|
|
1094
|
+
is recoverable by upgrading; a partial read that gets written back is not.
|
|
1095
|
+
- A gap in the migration chain is rejected when the schema is **declared**,
|
|
1096
|
+
not when a stale file finally shows up — a gap found at read time is found
|
|
1097
|
+
in production, by a user whose session will not open.
|
|
1098
|
+
- Each line of the append-only message log carries its own stamp: such a log
|
|
1099
|
+
is written by many builds over its lifetime and its lines can legitimately
|
|
1100
|
+
differ in version. A line the build cannot read is refused rather than
|
|
1101
|
+
skipped, because silently dropping one hands the model a conversation with
|
|
1102
|
+
a hole in it.
|
|
1103
|
+
|
|
1104
|
+
Known limitation, stated rather than papered over: a file whose top level is
|
|
1105
|
+
an array has nowhere to put a stamp that survives `JSON.stringify`, so it
|
|
1106
|
+
stays unversioned. A store that needs to migrate one has to move it under an
|
|
1107
|
+
object first.
|
|
1108
|
+
|
|
1109
|
+
- 935b8f3: `prepareStep` — shape each step before the model is called.
|
|
1110
|
+
|
|
1111
|
+
`stopWhen` let a run decide TO STOP from what its steps produced. This is
|
|
1112
|
+
the other half: deciding how the next step should look. Without it, the
|
|
1113
|
+
tool surface and the model were fixed at `query()` time, so a phased agent
|
|
1114
|
+
— research with search tools, write with file tools, verify with a cheaper
|
|
1115
|
+
model — had to be built as three separate runs, each starting blind to the
|
|
1116
|
+
last one's context.
|
|
1117
|
+
|
|
1118
|
+
The hook receives the run id, the step number, the full message history and
|
|
1119
|
+
every completed `StepResult`, and may return `activeTools`, `model`,
|
|
1120
|
+
`system` (one-step guidance), `temperature` and `maxResponseTokens`. Any
|
|
1121
|
+
omitted field keeps the run's configured value.
|
|
1122
|
+
|
|
1123
|
+
- `system` guidance is appended to the REQUEST, never pushed onto the run's
|
|
1124
|
+
history — otherwise a long run accumulates one stale phase instruction
|
|
1125
|
+
per iteration.
|
|
1126
|
+
- `activeTools` does NOT touch `tool_choice`. Anthropic has no
|
|
1127
|
+
`allowed_tools`, and moving `tool_choice` invalidates cached MESSAGE
|
|
1128
|
+
blocks as well — a strictly worse trade for the same effect. Narrowing
|
|
1129
|
+
still costs the prompt-cache prefix, since tools render at position 0;
|
|
1130
|
+
that is inherent, and worth paying at a real phase boundary rather than
|
|
1131
|
+
every step.
|
|
1132
|
+
- Unregistered tool names are dropped with a warning: a phase list that
|
|
1133
|
+
outlives a tool rename should narrow the surface, not kill the agent
|
|
1134
|
+
mid-run.
|
|
1135
|
+
- Fails OPEN. A throwing hook leaves the step with the run's configuration
|
|
1136
|
+
— same reasoning as `stopWhen`, and deliberately opposite to a guardrail,
|
|
1137
|
+
because nothing unsafe gets through when step shaping is skipped.
|
|
1138
|
+
|
|
1139
|
+
- 935b8f3: Give runtime failures a code a host can branch on.
|
|
1140
|
+
|
|
1141
|
+
`PlatformError` was declared and never constructed — a shape nothing
|
|
1142
|
+
produced and nothing consumed — while the runtime threw bare `Error`
|
|
1143
|
+
everywhere. A caller catching a failure from `query()` could not tell "the
|
|
1144
|
+
model rate-limited us" from "the run was configured wrong" from "that
|
|
1145
|
+
checkpoint does not exist"; matching on message text was the only recourse,
|
|
1146
|
+
and message text is not an interface.
|
|
1147
|
+
|
|
1148
|
+
- `NamzuError` implements `PlatformError` and extends `Error`, so it still
|
|
1149
|
+
behaves like one everywhere that only knows about `Error` — stack,
|
|
1150
|
+
`instanceof`, `cause`.
|
|
1151
|
+
- `NamzuErrorCode` stays small on purpose: each member exists because a
|
|
1152
|
+
caller does something different about it (`invalid_config`,
|
|
1153
|
+
`provider_error`, `tool_error`, `not_found`, `plugin_error`,
|
|
1154
|
+
`capability_unavailable`, `storage_error`, `unknown`).
|
|
1155
|
+
- `toPlatformError(unknown)` normalizes ANYTHING thrown into the declared
|
|
1156
|
+
shape — a `NamzuError`, a `ProviderError`, a plain `Error` from a
|
|
1157
|
+
dependency, or a thrown string. Without it, "handle errors from the SDK"
|
|
1158
|
+
means writing the same `instanceof` ladder in every caller. A
|
|
1159
|
+
`ProviderError` keeps its own classification (its code lands in
|
|
1160
|
+
`details.providerCode` and its `retryable` verdict is preserved, not
|
|
1161
|
+
recomputed).
|
|
1162
|
+
|
|
1163
|
+
Adopted at the runtime sites a host would actually branch on: strict
|
|
1164
|
+
capability failures, provider stream errors, checkpoint-not-found, and
|
|
1165
|
+
plugin hook errors. Exhaustiveness guards stay plain `Error` — those are
|
|
1166
|
+
programmer bugs, not conditions to handle.
|
|
1167
|
+
|
|
1168
|
+
- 935b8f3: Step shaping composes
|
|
1169
|
+
|
|
1170
|
+
`prepareStep` was a single slot: enough for one concern and no help with two. A host with a per-tenant system prefix _and_ a cost-based model downgrade had to hand-compose them into one callback, which puts the ordering in the host's own code where nothing can see it and makes each concern's failure the other's problem.
|
|
1171
|
+
|
|
1172
|
+
It now accepts an array. Stages run in **declaration order** — not registration order, and that distinction is the whole reason this is safe where a plugin-style fan-out would not be: the author writes the order down, so "who wins" is a line of their code rather than an accident of install history. Each stage sees what the ones before it decided through `context.prepared`, which is how a later stage refines an earlier one instead of guessing at it.
|
|
1173
|
+
|
|
1174
|
+
A stage that throws is skipped and the rest still run, because one broken concern must not silently disable the others it was declared beside. A single function behaves exactly as before.
|
|
1175
|
+
|
|
1176
|
+
- 935b8f3: Add input/output guardrails to `query()`.
|
|
1177
|
+
|
|
1178
|
+
namzu had three gates on tool calls — probe veto, `VerificationGate`, HITL
|
|
1179
|
+
review — and all three point the same way: they protect the world from the
|
|
1180
|
+
agent. Nothing protected the user from the agent's own output, and nothing
|
|
1181
|
+
looked at the prompt before a run started.
|
|
1182
|
+
|
|
1183
|
+
- `inputGuardrails` run before the first model call. A block settles the run
|
|
1184
|
+
as `input_guardrail` having spent nothing.
|
|
1185
|
+
- `outputGuardrails` run against the final result. A block settles as
|
|
1186
|
+
`output_guardrail`; a `rewrite` replaces the text, so a redaction policy
|
|
1187
|
+
can clean an answer instead of discarding it. Rewrites compose.
|
|
1188
|
+
- A guardrail that throws **fails closed** — deliberately the opposite of
|
|
1189
|
+
`stopWhen`. A broken halt predicate should not kill a healthy run; a broken
|
|
1190
|
+
safety check must not wave content through.
|
|
1191
|
+
- New `guardrail_triggered` run event (wire: `guardrail.triggered`).
|
|
1192
|
+
- Presets: `secretRedactionGuardrail` (prefix-anchored credential patterns,
|
|
1193
|
+
redact or block) and `promptInjectionGuardrail` (partial, by design).
|
|
1194
|
+
|
|
1195
|
+
These gate the result, not the stream: `text_delta` events have already
|
|
1196
|
+
reached the host, so a rewrite arrives as a correction alongside the event.
|
|
1197
|
+
|
|
1198
|
+
- 935b8f3: Make the loop-control surface reachable from the Agent classes, and stop
|
|
1199
|
+
gating environment context on built-in tool names.
|
|
1200
|
+
|
|
1201
|
+
Found by auditing the one application in the estate that actually consumes
|
|
1202
|
+
`@namzu/sdk`, rather than by reading the SDK again.
|
|
1203
|
+
|
|
1204
|
+
- **`ReactiveAgent` forwarded none of the loop-control seams.** It is what
|
|
1205
|
+
`AgentManager` spawns and what real applications call, and it passed only
|
|
1206
|
+
provider/tools/runConfig — so `toolTimeoutMs`, `retry`, `emergencySave`,
|
|
1207
|
+
`stopWhen`, `onStepFinish`, `prepareStep`, `structuredOutput`,
|
|
1208
|
+
guardrails, `repairToolCall`, `maxToolConcurrency`, `maxToolOutputChars`,
|
|
1209
|
+
`resumeHandler` and `checkpointStore` were reachable only by dropping to
|
|
1210
|
+
`query()` and rebuilding the run wiring by hand. A feature a consumer
|
|
1211
|
+
cannot reach is a feature that does not exist for them.
|
|
1212
|
+
|
|
1213
|
+
- **The `<env>` block keyed on four hardcoded tool names.** A host
|
|
1214
|
+
registering a filesystem tool called `read_file` — declaring
|
|
1215
|
+
`category: 'filesystem'` and `permissions: ['file_read']` correctly — got
|
|
1216
|
+
no environment context at all, so the model was never told its working
|
|
1217
|
+
directory and the host hand-encoded paths into its system prompt. The
|
|
1218
|
+
gate now reads what a tool declares, keeping the name set as a fallback.
|
|
1219
|
+
|
|
1220
|
+
- **Providers were handed the run's live message array.** `runMgr.messages`
|
|
1221
|
+
is the live array and the loop pushes onto it after the call returns, so
|
|
1222
|
+
a driver that retained its input — to log it, cache it, or replay it on
|
|
1223
|
+
retry — watched it grow new turns underneath. A capture provider in the
|
|
1224
|
+
estate recorded every turn as identical to the last for exactly this
|
|
1225
|
+
reason. The array is now copied at the provider boundary.
|
|
1226
|
+
|
|
1227
|
+
- 935b8f3: Five places where namzu gave up, or claimed to recover, too early.
|
|
1228
|
+
|
|
1229
|
+
**A transient failure now pauses instead of failing.** A 503 that survived
|
|
1230
|
+
every in-turn recovery — retry with jitter, the one-shot compaction relief,
|
|
1231
|
+
mid-stream salvage — settled the run as `failed`, identically to a bad API
|
|
1232
|
+
key. The host could not tell them apart, and recovering meant knowing about
|
|
1233
|
+
checkpoints and driving replay itself. The state was never the problem:
|
|
1234
|
+
checkpoints are written every iteration by default and the failed run is
|
|
1235
|
+
persisted with full messages. Only the settle and the signal were missing.
|
|
1236
|
+
|
|
1237
|
+
A retryable failure with a checkpoint to resume from now emits `run_paused`
|
|
1238
|
+
naming that checkpoint, leaves the span OK rather than ERROR, and sets
|
|
1239
|
+
`stopReason: 'paused'`. Both conditions are required — pausing on a
|
|
1240
|
+
permanent error would invite a resume that cannot work, and pausing with
|
|
1241
|
+
nowhere to resume from produces a run nobody can ever pick up.
|
|
1242
|
+
|
|
1243
|
+
**A forced compaction pass can no longer decline to do anything.** A forced
|
|
1244
|
+
pass runs because the provider _rejected_ the prompt as too long, and two
|
|
1245
|
+
things let it treat that as advisory. It re-applied the chars/4 estimate
|
|
1246
|
+
after clearing stale tool results — the estimate the provider had just
|
|
1247
|
+
refuted — and returned early if that said the context was fine. And relief
|
|
1248
|
+
reported success on ANY positive shed, so clearing one short result counted
|
|
1249
|
+
and the retry burned a whole model call to be told the same thing. The
|
|
1250
|
+
early return is now force-gated, and a shed has to clear a floor (a
|
|
1251
|
+
fraction of the prompt, at least a couple of thousand characters) to count.
|
|
1252
|
+
|
|
1253
|
+
Separately, the relief latch is per **stuck point**, not per run. It exists
|
|
1254
|
+
to stop a second overflow immediately after a successful compaction from
|
|
1255
|
+
looping; as a run-scoped flag it meant one relief at iteration 3 disarmed
|
|
1256
|
+
the mechanism for the rest of the run, leaving iteration 40 to die with
|
|
1257
|
+
obvious moves left. It is now cleared by a turn that actually succeeded.
|
|
1258
|
+
|
|
1259
|
+
**An eval case can no longer hang the suite.** `executeCase` was a bare
|
|
1260
|
+
await, so a `run` closure that never settled blocked its worker and
|
|
1261
|
+
`runExperiment` never returned — no report, no partial results, nothing to
|
|
1262
|
+
read. `ExperimentConfig.timeoutMs` bounds a case and hands `run` an
|
|
1263
|
+
`AbortSignal` as a third argument; a timed-out case is reported and the
|
|
1264
|
+
suite continues, exactly like a case that threw, with its real elapsed time
|
|
1265
|
+
rather than zero. Unset means no deadline, which is today's behaviour. The
|
|
1266
|
+
documented path already inherits deadlines from the runtime it drives; this
|
|
1267
|
+
covers what those cannot see — a closure that does not go through
|
|
1268
|
+
`query()`, and a mid-iteration provider stall.
|
|
1269
|
+
|
|
1270
|
+
**A malformed content block is named, not smuggled.** One driver built an
|
|
1271
|
+
image block by calling `String()` on whatever `data` and `mediaType`
|
|
1272
|
+
happened to be, behind only a truthiness check — so a non-string `data`
|
|
1273
|
+
became the literal `"[object Object]"` as the base64 payload, and the wire
|
|
1274
|
+
rejected the whole request with nothing naming the block at fault. That is
|
|
1275
|
+
reachable: a remote tool result is cast without validation on the way in.
|
|
1276
|
+
It now type- and media-type-guards and degrades to a named placeholder,
|
|
1277
|
+
matching the sibling driver that already did, and without inlining the
|
|
1278
|
+
payload it refused to send.
|
|
1279
|
+
|
|
1280
|
+
**Failures have somewhere to grow remediation.** A stale API key surfaced
|
|
1281
|
+
as whatever prose the vendor SDK happened to write: no id to grep in logs,
|
|
1282
|
+
no instruction on what to change, and no growth point — a newly-observed
|
|
1283
|
+
failure shape could only be given curated copy by editing the classifier.
|
|
1284
|
+
`explainError` adds an ordered, id-keyed rule layer matching on
|
|
1285
|
+
**structural** signals (code, status, an explicit hint) rather than
|
|
1286
|
+
volatile vendor prose. `run_failed` carries the result as `explanation`;
|
|
1287
|
+
`withHint(err, '…')` lets a throw site attach what only it knows, and
|
|
1288
|
+
outranks every generic rule. It returns `null` when no rule claims the
|
|
1289
|
+
failure — inventing advice for something uncharacterised is worse than
|
|
1290
|
+
saying nothing, because it sends the reader somewhere specific and wrong.
|
|
1291
|
+
The container backend's readiness, port-mapping and worker-fetch failures
|
|
1292
|
+
now carry hints.
|
|
1293
|
+
|
|
1294
|
+
- 935b8f3: A context overflow now shortens the prompt and retries instead of killing
|
|
1295
|
+
the run.
|
|
1296
|
+
|
|
1297
|
+
`context_length_exceeded` was classified precisely and consumed by nothing.
|
|
1298
|
+
It is correctly non-retryable — resending the identical prompt cannot help
|
|
1299
|
+
— so the run died, holding a compaction subsystem that could have made
|
|
1300
|
+
room.
|
|
1301
|
+
|
|
1302
|
+
This is not a hypothetical failure. Compaction fires on an ESTIMATE of how
|
|
1303
|
+
full the context is, and an estimate can read low: a run carrying images,
|
|
1304
|
+
or text in a language the chars-per-token ratio does not fit, reaches the
|
|
1305
|
+
real window while still looking comfortable. The provider then reports
|
|
1306
|
+
exactly what is wrong, which is stronger evidence than the estimate that
|
|
1307
|
+
was just proven wrong.
|
|
1308
|
+
|
|
1309
|
+
- `relieveOverflow` forces a compaction pass, bypassing the threshold.
|
|
1310
|
+
- It reports whether anything was actually shed. When nothing was — no
|
|
1311
|
+
compaction configured, or nothing left to compact — the error proceeds,
|
|
1312
|
+
because retrying would send the same prompt and reach the same error.
|
|
1313
|
+
- Relief is attempted once per run. A second overflow after a successful
|
|
1314
|
+
compaction means the prompt is irreducible, and looping would burn the
|
|
1315
|
+
budget to arrive at the same place.
|
|
1316
|
+
|
|
1317
|
+
- 935b8f3: A message can be pinned against eviction
|
|
1318
|
+
|
|
1319
|
+
Everything a run protected from compaction was protected by **position**: the leading system messages, the working-memory slot, the last N turns, the most recent tool results. A standing constraint stated in the middle of a conversation — "the account id is 4471; never bill a different one" — therefore aged out at the same rate as chatter. No positional rule could express it, and the working-memory slot could not either: it is host-rendered each turn and does not know what the user said.
|
|
1320
|
+
|
|
1321
|
+
`retain: true` on a message says it directly. The summarization rebuild carries pinned turns over verbatim, in order, between the summary and the recent window, and the in-place tool-result clearing pass leaves their content alone — clearing keeps the message and replaces its content, which is exactly the loss the marker was asked to prevent.
|
|
1322
|
+
|
|
1323
|
+
Protection is transitive across a tool pair: pinning a `tool_result` pins the assistant turn that issued the call, and pinning that turn pins every result answering it. Half a pair is not a smaller history, it is one the provider rejects.
|
|
1324
|
+
|
|
1325
|
+
Nothing caps how much may be pinned. Pinned turns are exempt from the reclaim that keeps a long run alive, so this is a budget the setter spends — a cap would have to guess which pin mattered, and dropping the wrong one quietly is worse than a run that overflows in the open.
|
|
1326
|
+
|
|
1327
|
+
- 935b8f3: Add structured final output — and fix two bugs it uncovered on the tool-result
|
|
1328
|
+
wire path.
|
|
1329
|
+
|
|
1330
|
+
**Structured output.** Both leaf pieces already shipped and neither was
|
|
1331
|
+
reachable: `createStructuredOutputTool` is excluded from `getBuiltinTools()`
|
|
1332
|
+
because it needs a schema, and `StructuredOutputConfig` was referenced by
|
|
1333
|
+
exactly one non-test line — the barrel re-export. A host needing
|
|
1334
|
+
`{verdict, findings}` from an agent that also uses tools had to register the
|
|
1335
|
+
tool by hand and hope: nothing forced the call, nothing stopped the loop when
|
|
1336
|
+
it came, and a schema mismatch surfaced as a `ZodError` _after_ the run had
|
|
1337
|
+
paid for itself.
|
|
1338
|
+
|
|
1339
|
+
`query({ structuredOutput: { schema } })` registers the tool **from iteration
|
|
1340
|
+
zero** — tools render at prefix position 0, so late injection would invalidate
|
|
1341
|
+
the prompt cache for the rest of the run — validates the call, lands the parsed
|
|
1342
|
+
value on `Run.structuredOutput`, and ends the run there rather than paying for
|
|
1343
|
+
another turn that would only restate it. A model that answers in prose is
|
|
1344
|
+
re-prompted, bounded by `maxRetries` (default 2), after which the run settles
|
|
1345
|
+
with the new `StopReason: 'structured_output_failed'` instead of grinding
|
|
1346
|
+
against `maxIterations`.
|
|
1347
|
+
|
|
1348
|
+
**Two bugs found while testing it**, both on the path between what a tool
|
|
1349
|
+
returns and what reaches the provider, and both introduced by the content-block
|
|
1350
|
+
migration:
|
|
1351
|
+
|
|
1352
|
+
- `ToolExecutor`'s final return omitted `isError`, so a failed tool was never
|
|
1353
|
+
marked as failed on the message and `is_error` could not reach the wire.
|
|
1354
|
+
- The executor's local `result` was typed as a narrowed literal that dropped
|
|
1355
|
+
`content`, so a tool returning an image block had it discarded before the
|
|
1356
|
+
mapper built to carry it ever saw it.
|
|
1357
|
+
|
|
1358
|
+
The mapper tests passed throughout because they set those fields by hand. A new
|
|
1359
|
+
suite covers the executor→message seam directly, which is where both lived.
|
|
1360
|
+
|
|
1361
|
+
- 935b8f3: A fan-out can now declare what a failed child means for its siblings.
|
|
1362
|
+
|
|
1363
|
+
The primitive to stop them already existed — every child holds an abort
|
|
1364
|
+
controller chained to the parent's, and `AgentManager.cancel` uses it — but
|
|
1365
|
+
nothing connected a failure to it. A supervisor that fanned out five tasks
|
|
1366
|
+
and watched one die had no way to say the other four were now pointless:
|
|
1367
|
+
they ran to completion, spending budget on work whose premise had gone.
|
|
1368
|
+
|
|
1369
|
+
`LocalTaskGateway` takes a `SiblingFailurePolicy`:
|
|
1370
|
+
|
|
1371
|
+
- `'continue'` — the default, and deliberately unchanged. Partial results
|
|
1372
|
+
are usually worth having, and cancelling healthy siblings on any failure
|
|
1373
|
+
would let one flaky child waste four good ones.
|
|
1374
|
+
- `'cancel-siblings'` — for a fan-out whose parts only mean something
|
|
1375
|
+
together, where one dead leg makes the rest an answer nobody can use.
|
|
1376
|
+
|
|
1377
|
+
Failure is judged from the result as well as the task state. A child whose
|
|
1378
|
+
spawn machinery threw lands in state `'failed'`, but a child that RAN and
|
|
1379
|
+
returned `status: 'failed'` is marked completed and carries the failure in
|
|
1380
|
+
its result — so reading only the state would have caught the exceptional
|
|
1381
|
+
case and missed the ordinary one.
|
|
1382
|
+
|
|
1383
|
+
- 935b8f3: Cap model-visible tool output, and spill the overflow instead of losing it.
|
|
1384
|
+
|
|
1385
|
+
Nothing bounded tool output. `read` returned a whole file when `limit` was
|
|
1386
|
+
omitted, `bash` allowed a 100 MB buffer, and the MCP adapter joined every text
|
|
1387
|
+
block uncapped — so a `read` of a 2 MB lockfile became ~500k tokens in one
|
|
1388
|
+
`tool_result`, the provider rejected the request, and with no retry the run
|
|
1389
|
+
died with everything lost. The one existing reducer, `compressShellOutput`,
|
|
1390
|
+
early-returns for any tool whose category is not `shell` and has no absolute
|
|
1391
|
+
size cap at all.
|
|
1392
|
+
|
|
1393
|
+
- `maxToolOutputChars` (default 40k ≈ 10k tokens), overridable per run. Output
|
|
1394
|
+
over budget is written to `<runDir>/tool-output/<toolUseId>.txt` and replaced
|
|
1395
|
+
with a head+tail preview naming the path. Spilling beats truncating on every
|
|
1396
|
+
axis: nothing is lost, tokens are paid only if the agent decides the rest is
|
|
1397
|
+
worth re-reading, and retrieval uses `read`/`grep` — tools it already has.
|
|
1398
|
+
Without a run directory it degrades to middle-elision rather than being
|
|
1399
|
+
unbounded.
|
|
1400
|
+
- `read` defaults to a 2000-line window instead of the entire file, and any
|
|
1401
|
+
partial read now ends with a `[PARTIAL view — lines X-Y of Z]` notice naming
|
|
1402
|
+
the exact next call. A truncated read used to be indistinguishable from a
|
|
1403
|
+
short file, so the agent reasoned about a fragment as if it were the whole
|
|
1404
|
+
thing.
|
|
1405
|
+
- `bash` surfaces the sandbox's `stdoutTruncated` / `stderrTruncated` flags,
|
|
1406
|
+
which were computed by the backend and dropped at the `SandboxExecResult`
|
|
1407
|
+
type boundary — the model saw a complete-looking result that had silently
|
|
1408
|
+
lost its tail. Both flags are now part of the contract, along with
|
|
1409
|
+
`SandboxExecOptions.signal` so a cancelled run can reach the process.
|
|
1410
|
+
- `tool_completed` carries `durationMs` (computed since the first version of
|
|
1411
|
+
the executor but only ever logged), plus `outputLength`, `outputTruncated`
|
|
1412
|
+
and `outputSpillPath`.
|
|
1413
|
+
|
|
1414
|
+
- 935b8f3: Compaction's working state now rides the checkpoint, so a resumed run stops
|
|
1415
|
+
deleting its own history.
|
|
1416
|
+
|
|
1417
|
+
Compaction replaces older messages with a summary and drops any prior
|
|
1418
|
+
`[COMPACTED CONTEXT]` block, on the grounds that `serializeState` is
|
|
1419
|
+
cumulative so the newer summary supersedes it. That holds inside one
|
|
1420
|
+
process. Across a resume it did not: `WorkingStateManager` was constructed
|
|
1421
|
+
fresh on every `query()` with no restore path, so the second compaction of
|
|
1422
|
+
a resumed run produced a summary covering only post-resume activity — and
|
|
1423
|
+
deleted the block that held everything before it.
|
|
1424
|
+
|
|
1425
|
+
The restore path deliberately carries that block forward, calling it the
|
|
1426
|
+
only surviving record of the history the first pass deleted. The next pass
|
|
1427
|
+
then destroyed it. This is what made the two halves agree.
|
|
1428
|
+
|
|
1429
|
+
- `IterationCheckpoint.workingState` — optional, so checkpoints written
|
|
1430
|
+
before this field exists restore exactly as they do today.
|
|
1431
|
+
- `snapshotWorkingState` / `restoreWorkingState` handle the wire shape.
|
|
1432
|
+
`WorkingState.files` is a `Map`, which JSON renders as `{}`, so a naive
|
|
1433
|
+
snapshot would have silently lost every tracked file. Eviction counters
|
|
1434
|
+
round-trip too: a resumed summary that forgot what it had already dropped
|
|
1435
|
+
would claim a completeness it does not have.
|
|
1436
|
+
- State is restored directly rather than by replaying extractors over the
|
|
1437
|
+
restored messages — the messages the first pass compacted away are gone,
|
|
1438
|
+
so re-extraction is both lossy and non-idempotent.
|
|
1439
|
+
|
|
1440
|
+
- 935b8f3: Harden the MCP boundary: the host decides what enters the tool registry,
|
|
1441
|
+
and a server that changes its mind is noticed.
|
|
1442
|
+
|
|
1443
|
+
- `MCPToolDiscovery` takes per-server `allow`/`deny` policies (`'*'` for
|
|
1444
|
+
servers without an entry). Discovery previously admitted whatever the
|
|
1445
|
+
server offered, which put the REMOTE side in charge of what the agent
|
|
1446
|
+
can call — the exact inversion of least privilege. Deny beats allow, so
|
|
1447
|
+
a self-contradicting config resolves restrictively.
|
|
1448
|
+
- Drift detection: the admitted tool set is fingerprinted (name +
|
|
1449
|
+
description + input schema) and compared on each discovery, with an
|
|
1450
|
+
`onDrift` callback reporting `added` / `removed` / `changed`. The
|
|
1451
|
+
fingerprint covers descriptions and schemas, not just names, because the
|
|
1452
|
+
attack shape is advertising something benign at approval time and
|
|
1453
|
+
swapping its meaning afterwards — the name never moves. Reported rather
|
|
1454
|
+
than blocked: a dev server legitimately changes between runs, and only
|
|
1455
|
+
the host knows which kind it is looking at.
|
|
1456
|
+
- Protocol negotiation is checked. A server answers `initialize` with the
|
|
1457
|
+
version IT will speak; the client ignored that answer entirely, so a
|
|
1458
|
+
version it could not speak looked like a healthy connection until
|
|
1459
|
+
something downstream broke oddly. It now refuses a version outside
|
|
1460
|
+
`MCP_SUPPORTED_PROTOCOL_VERSIONS` and names what it can speak. An
|
|
1461
|
+
ABSENT version is still tolerated — a missing field is a sloppy server,
|
|
1462
|
+
an unsupported version is a real incompatibility.
|
|
1463
|
+
|
|
1464
|
+
`MCP_PROTOCOL_VERSION` deliberately stays at the version namzu actually
|
|
1465
|
+
implements. Advertising a newer one whose requirements are unimplemented is
|
|
1466
|
+
worse than advertising an older one honestly, because the server tailors
|
|
1467
|
+
its behavior to the claim. Raising it is a conformance task.
|
|
1468
|
+
|
|
1469
|
+
Hosts that configure no policy see no behavior change.
|
|
1470
|
+
|
|
1471
|
+
- 935b8f3: Recover from a bad tool call without spending a model round trip on it.
|
|
1472
|
+
|
|
1473
|
+
- `QueryParams.repairToolCall` — a last chance to fix a call the model got
|
|
1474
|
+
wrong, before the error reaches it. A malformed call otherwise costs a
|
|
1475
|
+
full round trip: the error goes back as a `tool_result`, the model
|
|
1476
|
+
re-reads the whole context, and issues a second inference to add a
|
|
1477
|
+
missing brace. The hook sees the reason (`invalid_json`,
|
|
1478
|
+
`schema_validation`, `unknown_tool`), the tool's JSON Schema and every
|
|
1479
|
+
registered tool name, and may rewrite the arguments and the tool name —
|
|
1480
|
+
nothing else. It is tried exactly once, a throw is caught, and declining
|
|
1481
|
+
is normal: the original error simply proceeds as before.
|
|
1482
|
+
- `ToolDefinition.maxRetries` (default `0`) + `ToolResult.retryable` — a
|
|
1483
|
+
transient tool failure can now be retried in-loop instead of going back
|
|
1484
|
+
to the model to be re-decided. Strictly opt-in per tool, because the SDK
|
|
1485
|
+
cannot know a tool is idempotent, and only for failures the tool marked
|
|
1486
|
+
retryable.
|
|
1487
|
+
- `PluginHookResult` `{action:'retry'}` finally does something. It was a
|
|
1488
|
+
declared variant that threw at every site that consumed it; in
|
|
1489
|
+
`post_tool_use` it now re-runs the tool, bounded by the same per-tool
|
|
1490
|
+
budget so a plugin cannot spin the executor. It remains an error in
|
|
1491
|
+
`pre_tool_use`, where nothing has run yet for it to mean anything.
|
|
1492
|
+
|
|
1493
|
+
With no repairer configured and no tool opting into retries, behavior is
|
|
1494
|
+
unchanged.
|
|
1495
|
+
|
|
1496
|
+
- 935b8f3: Bound tool execution: per-tool deadlines, real cancellation, and a fan-out cap.
|
|
1497
|
+
|
|
1498
|
+
`ToolContext.abortSignal` was produced by the executor and consumed by nothing —
|
|
1499
|
+
a repo-wide grep found only the two producer sites. A Stop tore down the model
|
|
1500
|
+
stream and then parked inside `Promise.all` waiting for a tool that had no idea
|
|
1501
|
+
it should quit, and there was no framework-level deadline at all: `bash`
|
|
1502
|
+
defaulted to **one hour**, and the MCP stdio transport to forever.
|
|
1503
|
+
|
|
1504
|
+
- `ToolDefinition.timeoutMs` and `ToolExecutorConfig.toolTimeoutMs` (default
|
|
1505
|
+
120s). On expiry the executor stops waiting and returns a model-visible
|
|
1506
|
+
error result, so a slow dependency becomes something the agent can route
|
|
1507
|
+
around rather than a turn that never comes back.
|
|
1508
|
+
- The tool's `context.abortSignal` now really fires — on the deadline and on a
|
|
1509
|
+
run abort — so cooperative tools stop working instead of merely being
|
|
1510
|
+
detached. `bash` passes it to the child process.
|
|
1511
|
+
- `bash`'s default timeout drops from 1 hour to 2 minutes. The model can still
|
|
1512
|
+
request longer through the tool's own `timeout` argument.
|
|
1513
|
+
- `ToolExecutorConfig.maxToolConcurrency` (default 8) bounds the parallel
|
|
1514
|
+
branch of `executeBatch`, which previously fanned out without limit.
|
|
1515
|
+
- MCP: `MCPClientConfig.requestTimeoutMs` (default 30s) bounds every JSON-RPC
|
|
1516
|
+
round trip; in-flight requests are now rejected when the transport closes or
|
|
1517
|
+
errors, not only on an explicit `disconnect()`; and a server-initiated
|
|
1518
|
+
request (`sampling/createMessage`, `elicitation/create`, `roots/list`,
|
|
1519
|
+
`ping`) gets a `-32601` reply instead of being silently discarded, which
|
|
1520
|
+
used to leave the server waiting forever.
|
|
1521
|
+
|
|
1522
|
+
- 935b8f3: A tool can declare that its output IS the answer
|
|
1523
|
+
|
|
1524
|
+
Every delegation path is blocking: the worker's final text comes back as the dispatching call's result. The loop then went round once more purely to restate what the worker had already said — a full model call at the parent's context size, the most expensive call in the run. It is also lossy, because the parent paraphrases the worker's answer through its own compacted view, so the caller receives the summary rather than the answer. For a router agent, whose entire job is to pick a specialist, that doubled the cost of every request.
|
|
1525
|
+
|
|
1526
|
+
`terminal: true` on a tool settles the run with that tool's output — the rule `structured_output` has always had, now available to any tool. `buildAgentTool({ terminal: true })` sets it on the built-in delegation tool.
|
|
1527
|
+
|
|
1528
|
+
It is honoured only when the terminal call is the only call in the turn and it did not fail. A model that asked for other work in the same turn meant to see those results, and settling would discard answers it requested; an error is not an answer either, and the model is the one that should read it. Both cases take the ordinary path and log the reason rather than quietly costing the relay the flag was set to avoid.
|
|
1529
|
+
|
|
1530
|
+
`defineTool` also gained `maxRetries` and `outputSchema` passthrough. Both fields were already read by the runtime, and the sanctioned way to author a tool had no way to set either — the documented "the tool author opts in, per tool" was reachable only by hand-writing the interface.
|
|
1531
|
+
|
|
1532
|
+
- 935b8f3: A long-running tool can report progress.
|
|
1533
|
+
|
|
1534
|
+
Tools get a deadline of up to two minutes by default, and before this they
|
|
1535
|
+
were silent for all of it: a host could show that a build, a test run or a
|
|
1536
|
+
long fetch had started, and then nothing at all until it finished or timed
|
|
1537
|
+
out.
|
|
1538
|
+
|
|
1539
|
+
- `ToolContext.report(message, fraction?)` — fire-and-forget, returns void,
|
|
1540
|
+
never throws back into the tool, so it can be called without wrapping.
|
|
1541
|
+
- `tool_progress` run event (wire: `tool.progress`), carrying the tool name
|
|
1542
|
+
and `toolUseId` so a host rendering a concurrent batch knows whose
|
|
1543
|
+
progress it is. A `fraction` outside [0,1] is clamped rather than passed
|
|
1544
|
+
on.
|
|
1545
|
+
- Ephemeral, like `text_delta` — excluded from `transcript.jsonl`, so a
|
|
1546
|
+
tool reporting every file it compiles cannot bloat the durable record.
|
|
1547
|
+
|
|
1548
|
+
The model never sees these. Progress answers "is it still working?", which
|
|
1549
|
+
is a question only a human asks, and putting it in the conversation would
|
|
1550
|
+
spend tokens telling the model something it cannot act on.
|
|
1551
|
+
|
|
1552
|
+
- 935b8f3: Add an evaluation harness with trajectory scoring.
|
|
1553
|
+
|
|
1554
|
+
There was no evaluation harness of any kind — no dataset, no scorer, no judge,
|
|
1555
|
+
no trajectory assertion. So namzu's most load-bearing behavior was tuned by
|
|
1556
|
+
constants nobody could measure: `search_tools` activates the top 5 deferred
|
|
1557
|
+
tools, compaction fires at 0.7 of the window, six state lists cap at 25. Change
|
|
1558
|
+
any of them, or a builtin tool description, or the deferred-tools prompt block,
|
|
1559
|
+
and there was no way to learn the agent now takes four tool calls where it took
|
|
1560
|
+
one — short of a user hitting it.
|
|
1561
|
+
|
|
1562
|
+
```ts
|
|
1563
|
+
import {
|
|
1564
|
+
runExperiment,
|
|
1565
|
+
trajectoryScorer,
|
|
1566
|
+
completionScorer,
|
|
1567
|
+
evalRunFromQuery,
|
|
1568
|
+
} from "@namzu/sdk";
|
|
1569
|
+
|
|
1570
|
+
const report = await runExperiment({
|
|
1571
|
+
name: "file-editing",
|
|
1572
|
+
cases: [
|
|
1573
|
+
{
|
|
1574
|
+
name: "edits after reading",
|
|
1575
|
+
input: msgs,
|
|
1576
|
+
expectedTools: ["read", "edit"],
|
|
1577
|
+
},
|
|
1578
|
+
],
|
|
1579
|
+
scorers: [trajectoryScorer(), completionScorer()],
|
|
1580
|
+
run: (input) =>
|
|
1581
|
+
evalRunFromQuery(query({ provider, tools, messages: input /* … */ })),
|
|
1582
|
+
});
|
|
1583
|
+
```
|
|
1584
|
+
|
|
1585
|
+
- **`trajectoryScorer`** scores the tool sequence as F1 over the longest common
|
|
1586
|
+
_subsequence_. Subsequence, not set intersection: reading a file before
|
|
1587
|
+
editing it is not the same run as editing then reading. Extra calls cut
|
|
1588
|
+
precision, missing calls cut recall — so "did the right thing wastefully" and
|
|
1589
|
+
"skipped a step" get different scores, which a final-answer assertion
|
|
1590
|
+
collapses into one.
|
|
1591
|
+
- `completionScorer`, `stepBudgetScorer`, `containsScorer`, and `customScorer`
|
|
1592
|
+
for anything else — including a model-graded judge, which is just an async
|
|
1593
|
+
predicate that calls a provider.
|
|
1594
|
+
- **Every `Score` carries a required `reason`.** A bare number tells you a run
|
|
1595
|
+
got worse without telling you how, which is exactly when you need to know;
|
|
1596
|
+
`formatReport` prints those reasons for failures rather than a bare mean.
|
|
1597
|
+
- A case that throws is a _result_, not a crash: a suite whose first broken
|
|
1598
|
+
case aborts tells you nothing about the other forty. Same for a scorer that
|
|
1599
|
+
throws.
|
|
1600
|
+
- `evalRunFromRun` / `evalRunFromQuery` bridge a finished `Run` into the shape
|
|
1601
|
+
scorers consume. That bridge is three lines of mapping only because
|
|
1602
|
+
`Run.steps` exists — otherwise a trajectory scorer would have to correlate
|
|
1603
|
+
raw `RunEvent`s by iteration number and diff cumulative counters.
|
|
1604
|
+
|
|
1605
|
+
- 935b8f3: A user message can carry a document
|
|
1606
|
+
|
|
1607
|
+
Documents existed in the type system only in the tool-result direction, and both first-party drivers mapped images only on the input side. So "here is the contract, answer questions about it" — a mainstream workload — was reachable only by having a tool read the file and stringify it. That loses the provider's native document handling (page structure, built-in OCR, citations) and pays the text cost instead.
|
|
1608
|
+
|
|
1609
|
+
`UserMessage.attachments` is now `MessageAttachment[]`: an image or a document. The discriminant is optional and stays optional — an attachment without one is an image, which is what every attachment was before, so no existing caller changes.
|
|
1610
|
+
|
|
1611
|
+
`supportsDocuments` sits beside `supportsVision` in the driver capability declaration, and the runtime checks it the same way: a document sent to a driver that declares `false` warns before the request, or throws under `strictCapabilities`, instead of letting the model answer about a file it never saw. The two are counted separately because they are separate wire shapes and a driver can map one without the other.
|
|
1612
|
+
|
|
1613
|
+
The two first-party drivers map documents natively. The remaining five map images only and now say so; a document reaching them degrades to a named placeholder that says which kind was dropped, rather than one that calls a document an image.
|
|
1614
|
+
|
|
1615
|
+
- 935b8f3: Stop compaction from quietly degrading the state it produces, and implement
|
|
1616
|
+
`resetThreshold`.
|
|
1617
|
+
|
|
1618
|
+
What survives compaction is the only record of the history it replaced, so
|
|
1619
|
+
silently shrinking it is the one thing that structure must not do. Three fixes:
|
|
1620
|
+
|
|
1621
|
+
**Capped lists keep their head.** Eviction used `shift()` — oldest-first — so
|
|
1622
|
+
on a long run the 26th assistant note deleted the 1st, and "the structured
|
|
1623
|
+
state that survives compaction" degraded into a rolling window over recent
|
|
1624
|
+
activity. The early entries are the load-bearing ones (the original
|
|
1625
|
+
requirement, the decision that set the approach); the recent ones are still in
|
|
1626
|
+
the un-compacted tail of the conversation. The first `keepFirstEntries`
|
|
1627
|
+
(default 3) are now pinned and eviction takes from the middle. Tool results
|
|
1628
|
+
keep oldest-first eviction, because there recency genuinely wins: an old `read`
|
|
1629
|
+
of a since-edited file is worse than useless.
|
|
1630
|
+
|
|
1631
|
+
**The summary admits what it lost.** Evictions are counted per slot and
|
|
1632
|
+
rendered as `_(N entries dropped to stay within the state budget)_`. A summary
|
|
1633
|
+
that presents a gap as complete is worse than one that admits the gap — the
|
|
1634
|
+
model reasons about a fragment as if it were the whole record.
|
|
1635
|
+
|
|
1636
|
+
**Unrecognised tools get a useful summary.** Every MCP tool, custom tool and
|
|
1637
|
+
connector-bridged tool fell into a flat 120-character head slice, which on JSON
|
|
1638
|
+
spends the entire budget on syntax: `Ran: {"results":[{"id":"a1b2` and nothing
|
|
1639
|
+
else. Unknown tools are the ones a summary can say least about from the name,
|
|
1640
|
+
so they now get 400 characters and a structure-aware slice — array length and
|
|
1641
|
+
element shape, or object keys — falling back to head-and-tail for plain text.
|
|
1642
|
+
|
|
1643
|
+
**`resetThreshold` is implemented rather than deleted.** It was declared, set
|
|
1644
|
+
by the shipped CLI, and read by nothing. It is hysteresis: a pass that only
|
|
1645
|
+
moves the context from 0.72 to 0.71 of the window leaves the trigger armed, so
|
|
1646
|
+
the next iteration compacts again, paying a summarization call and busting the
|
|
1647
|
+
prompt-cache prefix each time for nothing. A pass that cannot reach the reset
|
|
1648
|
+
level now logs the shortfall, and `compaction_completed` carries
|
|
1649
|
+
`reachedResetThreshold`.
|
|
1650
|
+
|
|
1651
|
+
### Patch Changes
|
|
1652
|
+
|
|
1653
|
+
- 935b8f3: Atomic writes stop sharing one scratch file.
|
|
1654
|
+
|
|
1655
|
+
The rename is what makes a write atomic — a reader sees the old file or the
|
|
1656
|
+
new one, never a half-written one. The sidecar it renames _from_ has to be
|
|
1657
|
+
private to that write, and in seven places it was a fixed `${path}.tmp`.
|
|
1658
|
+
|
|
1659
|
+
Two writers of the same record then shared one scratch file: both opened it,
|
|
1660
|
+
both wrote into it, and the first rename published whatever mixture had
|
|
1661
|
+
landed while the second renamed a file that was no longer there. That is the
|
|
1662
|
+
exact failure atomic writes exist to prevent, reached through the mechanism
|
|
1663
|
+
meant to prevent it.
|
|
1664
|
+
|
|
1665
|
+
Not hypothetical for this SDK: the cross-process park and unpark handoff —
|
|
1666
|
+
one process suspending a run, another resuming it — is a design where two
|
|
1667
|
+
processes legitimately touch the same records, and it is the feature these
|
|
1668
|
+
stores exist to serve. One store already picked a private name; the other
|
|
1669
|
+
seven inherited the fixed one.
|
|
1670
|
+
|
|
1671
|
+
- One `atomicWriteFile` in `utils/`, used by the session, thread, run, task
|
|
1672
|
+
and memory stores, the retention backend and both migration writers. The
|
|
1673
|
+
sidecar carries the process id, a per-process counter and random bytes —
|
|
1674
|
+
distinct within a millisecond, within a process, and across hosts sharing
|
|
1675
|
+
a network mount.
|
|
1676
|
+
- It lives in `utils/` rather than `store/` because one of those writers was
|
|
1677
|
+
_deliberately_ duplicated to avoid an inbound dependency on the store
|
|
1678
|
+
layer. That instinct was right, and it is also why that copy kept the
|
|
1679
|
+
fixed name after the others were fixed; somewhere everything may depend on
|
|
1680
|
+
leaves nothing to duplicate.
|
|
1681
|
+
- A rename contended by a concurrent writer is retried briefly. Replacing an
|
|
1682
|
+
existing file by rename is unconditional on POSIX and not on Windows,
|
|
1683
|
+
where a concurrent writer holding the target fails the call for as long as
|
|
1684
|
+
the other rename takes — and two processes writing one record is precisely
|
|
1685
|
+
what this helper is for. Bounded to five attempts, so a genuine permission
|
|
1686
|
+
error still fails immediately instead of hanging.
|
|
1687
|
+
|
|
1688
|
+
- 935b8f3: The third-party-name audit now covers prose, not just source
|
|
1689
|
+
|
|
1690
|
+
The rule namzu holds is that nothing here takes its naming from another system and no brand appears in prose. The guard that enforces it scanned `.ts` only — so the largest prose surface in the repository, every README and published page, was never checked, and it had accumulated exactly what the rule refuses: a competitor feature grid, a scoring table, "in the spirit of X", "our tool names mirror Y's table verbatim", and a sandbox tier matrix written as market positioning.
|
|
1691
|
+
|
|
1692
|
+
Markdown is scanned now, with the same distinction the source side already draws. An inline code span, a fenced block, a link target and YAML frontmatter are values a reader types verbatim — a package path, a model id, a keychain item — and they are exempt. A published page may also name a service namzu ships a driver for, because telling an operator what it connects to is the page's job; source comments get no such licence, since a vendor is never the reason namzu's own code has its shape.
|
|
1693
|
+
|
|
1694
|
+
- 935b8f3: Four defects an adversarial audit confirmed
|
|
1695
|
+
|
|
1696
|
+
**A task could be created and then never found again.** `DiskTaskStore` writes under the run that created it and read only under the store's default run, so every lookup missed as soon as the two differed — the normal case, since the task tools are built with the live run id while a long-lived host constructs the store once with a fixed default. `create` succeeded, `list` succeeded, and `update`, `delete`, `claim` and every dependency link answered "not found" for a task the caller could see. The in-memory store keys by task id alone, which is why nothing caught it.
|
|
1697
|
+
|
|
1698
|
+
**A sub-agent's token reservation was never returned.** The debit at spawn reserves headroom so siblings cannot each be promised the same tokens, and nothing credited back the unused part — so a pool shrank by the full allocation on every spawn no matter what the child used. At a half-pool fraction, ten delegations left a parent with a thousandth of its budget and the next spawn was refused for a budget that had barely been spent. The debit also ran before provisioning, so a spawn rejected for capacity still burned its allocation — the one state change the comment there promised would not happen.
|
|
1699
|
+
|
|
1700
|
+
**A failed sandbox create leaked a proxy holding real credentials.** The egress proxy starts before the container and its only close was in `destroy()`, which a create that never returned can never reach. Every failure in between left a listening server on loopback stamping credential headers, plus a retained event-loop handle, one per retry.
|
|
1701
|
+
|
|
1702
|
+
**A remembered approval could overrule the operator.** The grant check ran before the verification gate and returned, so a remembered approval skipped the gate entirely — and because a tool-scoped grant matches any arguments, approving one harmless invocation authorised every other one, past a rule written to stop exactly that. The gate now runs first, and a grant can satisfy a review but never a denial.
|
|
1703
|
+
|
|
1704
|
+
- 935b8f3: Retry now works on the bedrock driver, and the shared classifier reads a
|
|
1705
|
+
status wherever a vendor hides it.
|
|
1706
|
+
|
|
1707
|
+
An unclassified error is treated as non-retryable, which is the right
|
|
1708
|
+
default — but it meant the retry policy was effectively dead on this
|
|
1709
|
+
driver, and the one failure most worth backing off from was the one that
|
|
1710
|
+
killed the run. The service reports failures as named exception classes,
|
|
1711
|
+
and the classifier looked at neither the name nor the status, because the
|
|
1712
|
+
status lives in a metadata bag rather than on the error.
|
|
1713
|
+
|
|
1714
|
+
- `classifyProviderError` now also reads `$metadata.httpStatusCode`. A
|
|
1715
|
+
status is a status wherever it hides, and this helps any driver — first
|
|
1716
|
+
or third party — whose SDK reports it that way.
|
|
1717
|
+
- The bedrock driver maps its own exception vocabulary to provider error
|
|
1718
|
+
codes: throttling and quota to `rate_limit`, unavailable and not-ready to
|
|
1719
|
+
`overloaded`, internal and stream faults to `server_error`, and the
|
|
1720
|
+
non-retryable ones (`ValidationException`, `AccessDeniedException`,
|
|
1721
|
+
`ResourceNotFoundException`) to their exact codes so they fail fast
|
|
1722
|
+
instead of burning the retry budget.
|
|
1723
|
+
|
|
1724
|
+
The vocabulary lives in the driver rather than the shared classifier: a
|
|
1725
|
+
driver knows its own vendor's error names, and the classifier should stay
|
|
1726
|
+
generic. An unrecognised exception passes through untouched — an honest
|
|
1727
|
+
unknown beats a confident wrong classification.
|
|
1728
|
+
|
|
1729
|
+
- 935b8f3: A cancelled turn records what it spent before it stopped.
|
|
1730
|
+
|
|
1731
|
+
Cancel re-threw from inside the chunk loop, so everything past that point
|
|
1732
|
+
was unreachable — and everything past that point is the turn's bookkeeping.
|
|
1733
|
+
|
|
1734
|
+
- **Silent cost under-reporting**, the load-bearing one: the usage the
|
|
1735
|
+
stream had already merged was discarded wholesale, so `Run.tokenUsage`
|
|
1736
|
+
and `costInfo` under-reported every cancelled turn. A cancelled turn is
|
|
1737
|
+
not a free turn; the tokens were spent.
|
|
1738
|
+
- The `chat {model}` span opened for the call was started and never ended,
|
|
1739
|
+
so it never exported at all.
|
|
1740
|
+
- The message the turn announced never got a terminator, so a host
|
|
1741
|
+
consuming the message lifecycle saw a message begin and never end.
|
|
1742
|
+
- The streamed text was absent from the run's messages and steps.
|
|
1743
|
+
|
|
1744
|
+
The stream-**error** path a few lines away already settled all of this.
|
|
1745
|
+
Cancel was the one exit that skipped it, which is the opposite of what its
|
|
1746
|
+
frequency deserves.
|
|
1747
|
+
|
|
1748
|
+
`MessageStopReason` gains `'cancelled'` so the terminator can be
|
|
1749
|
+
well-formed. Settling is best-effort and never replaces the reason the turn
|
|
1750
|
+
ended: the cancellation still propagates, so the run loop still settles as
|
|
1751
|
+
cancelled.
|
|
1752
|
+
|
|
1753
|
+
- 935b8f3: A damaged checkpoint is refused instead of skipped.
|
|
1754
|
+
|
|
1755
|
+
A checkpoint file is the **only** durable record of a park — there is no
|
|
1756
|
+
separate approval store. So an unreadable one that gets logged and skipped
|
|
1757
|
+
does not merely lose a resume point: `findPendingCheckpoint` reports "not
|
|
1758
|
+
parked" and drops an approval a human already granted.
|
|
1759
|
+
|
|
1760
|
+
`listCheckpoints` wrapped every per-file read in a `catch` that warned and
|
|
1761
|
+
continued, returning a silently short list that four callers treat as
|
|
1762
|
+
complete:
|
|
1763
|
+
|
|
1764
|
+
- `'latest'` resolution and `newest()` quietly resume from an **older**
|
|
1765
|
+
checkpoint, so the run re-executes a full iteration of tool calls;
|
|
1766
|
+
- `findPendingCheckpoint` loses the park, as above;
|
|
1767
|
+
- `prune` under-deletes, because a file the keep-count cannot see is
|
|
1768
|
+
immortal.
|
|
1769
|
+
|
|
1770
|
+
The only signal was a `log.warn` on a line nobody watches — and the by-id
|
|
1771
|
+
read next door was already strict. Two read paths disagreeing about whether
|
|
1772
|
+
damage matters is how the lenient one gets trusted.
|
|
1773
|
+
|
|
1774
|
+
Both paths now refuse. Both also **check** the parsed shape rather than
|
|
1775
|
+
casting it: `JSON.parse(content) as IterationCheckpoint` let `{}` through
|
|
1776
|
+
and failed much later at the point of use, where the message names a
|
|
1777
|
+
missing property rather than a damaged file.
|
|
1778
|
+
|
|
1779
|
+
Absent stays distinguishable from damaged: no checkpoints still returns an
|
|
1780
|
+
empty list, and an unknown id still returns `null`.
|
|
1781
|
+
|
|
1782
|
+
- 935b8f3: Compaction no longer leaves the conversation opening on an assistant turn.
|
|
1783
|
+
|
|
1784
|
+
After compaction the kept tail **is** the conversation: the summary is
|
|
1785
|
+
written as a system message and every driver hoists system messages into
|
|
1786
|
+
their own request parameter, so the first kept message becomes the first
|
|
1787
|
+
message on the wire. A conversation that opens on an assistant turn is
|
|
1788
|
+
rejected.
|
|
1789
|
+
|
|
1790
|
+
`findSafeTrimIndex` advanced past an orphaned `tool` message and never past
|
|
1791
|
+
an `assistant` one. How often that bit depends on the shape of the history,
|
|
1792
|
+
and the shape that matters most is the worst: in a **multi-step turn** — the
|
|
1793
|
+
agent working through several tool calls without the user speaking in
|
|
1794
|
+
between — the tail alternates assistant and tool with no user message in it
|
|
1795
|
+
at all, so essentially every boundary landed wrong.
|
|
1796
|
+
|
|
1797
|
+
The failure was unrecoverable. The resulting rejection is not classified as
|
|
1798
|
+
an overflow, so relief never fires and the run dies — compaction, whose
|
|
1799
|
+
entire job is keeping a long run alive, becoming the thing that ends it.
|
|
1800
|
+
|
|
1801
|
+
The boundary now advances to a `user` turn. Where none lies ahead it falls
|
|
1802
|
+
back to the nearest one behind **whose own tail is free of dangling tool
|
|
1803
|
+
pairs**: two wire invariants are in play, and satisfying one by breaking the
|
|
1804
|
+
other is not a fix. Where no boundary satisfies both, the input was already
|
|
1805
|
+
unsendable and no cut makes it otherwise, so the prior behaviour stands
|
|
1806
|
+
rather than a different invalid conversation being invented to replace it.
|
|
1807
|
+
|
|
1808
|
+
Also fixed alongside: the structured manager took
|
|
1809
|
+
`Math.min(safeTrimIndex, desiredTrimPoint)`, and since the safe index only
|
|
1810
|
+
ever moves forward of the desired one, that minimum resolved back to the
|
|
1811
|
+
desired point every time — discarding the entire safety search. Whatever the
|
|
1812
|
+
guard was reaching for, what it did was undo the line above it.
|
|
1813
|
+
|
|
1814
|
+
- 935b8f3: Four arithmetic defects, each pinned by a computed counterexample.
|
|
1815
|
+
|
|
1816
|
+
- **`mergeTokenUsage` maxed `totalTokens` as an independent field.** It is
|
|
1817
|
+
derived (`input + output`), and Anthropic reports the input on
|
|
1818
|
+
`message_start` and the output on `message_delta` — so the two frames
|
|
1819
|
+
carry totals of 1200 and 350, and the max returns the larger _component_
|
|
1820
|
+
rather than the sum. Merged: 1200. Correct: 1550. Every completion token
|
|
1821
|
+
was invisible to the token-budget hard stop, which reads only
|
|
1822
|
+
`totalTokens`. The merge now also takes `prompt + completion`, so it is
|
|
1823
|
+
monotone and can never under-report.
|
|
1824
|
+
|
|
1825
|
+
- **The compaction estimator counted array-shaped tool results by block
|
|
1826
|
+
count.** `msg.content.length` on `ToolResultBlock[]` is the number of
|
|
1827
|
+
blocks, so a tool result carrying a 400 KB screenshot contributed **1**
|
|
1828
|
+
character — and the estimate that decides when to compact read near zero
|
|
1829
|
+
for exactly the runs that need compacting most.
|
|
1830
|
+
|
|
1831
|
+
- **`toolsHash` omitted `annotations`.** Those carry `readOnlyHint` and
|
|
1832
|
+
`destructiveHint`, which become `isReadOnly` / `isDestructive` and drive
|
|
1833
|
+
whether a human reviews the call. A server could flip a tool from
|
|
1834
|
+
destructive to read-only — same name, same schema, silently removed from
|
|
1835
|
+
review — and the fingerprint built to catch that rug-pull produced an
|
|
1836
|
+
identical hash.
|
|
1837
|
+
|
|
1838
|
+
- **Sub-agent budget exhaustion inverted into no budget.**
|
|
1839
|
+
`floor(remaining * maxBudgetFraction)` reaches 0 once the parent drops
|
|
1840
|
+
below `1 / maxBudgetFraction`, and `tokenBudget: 0` means _uncapped_
|
|
1841
|
+
downstream (`LimitChecker`: `tokenBudget > 0 && …`). So the most depleted
|
|
1842
|
+
parent in the tree was the one that spawned an unlimited child. Spawning
|
|
1843
|
+
now refuses with a clear error; a caller who wants an uncapped child says
|
|
1844
|
+
so explicitly.
|
|
1845
|
+
|
|
1846
|
+
- 935b8f3: Parent the OpenTelemetry spans, and emit the missing `chat` span.
|
|
1847
|
+
|
|
1848
|
+
Every span was a root. A repo-wide grep for `context.with` / `trace.setSpan`
|
|
1849
|
+
returned zero hits, so a single 20-iteration run landed in Honeycomb as 21
|
|
1850
|
+
disconnected root spans plus N orphan tool spans — no waterfall, no way to see
|
|
1851
|
+
which iteration a slow tool belonged to. There was no span around the model
|
|
1852
|
+
call at all: `chatSpanName` existed with zero call sites, so traces carried no
|
|
1853
|
+
LLM latency, and the token counts were stamped on the iteration span instead of
|
|
1854
|
+
the operation that produced them.
|
|
1855
|
+
|
|
1856
|
+
The fix is explicit parent contexts rather than `startActiveSpan`. Every
|
|
1857
|
+
span-owning body in the run loop is an async **generator**, and a generator
|
|
1858
|
+
resumes on its consumer's async context — so the ambient parent is already gone
|
|
1859
|
+
by the time a child span is created, and the naive conversion silently parents
|
|
1860
|
+
nothing. `parentContext(span)` threads it as a value instead.
|
|
1861
|
+
|
|
1862
|
+
- Iteration spans parent to the run span; tool spans parent to the iteration
|
|
1863
|
+
that requested them, via a new optional `ToolContext.parentSpan` (already
|
|
1864
|
+
threaded to exactly the right place).
|
|
1865
|
+
- A `chat {model}` span carries `gen_ai.operation.name`, request model,
|
|
1866
|
+
temperature and max tokens, and on completion the response model, id,
|
|
1867
|
+
finish reasons, token usage and the cache-read/write counts.
|
|
1868
|
+
- `@namzu/telemetry` switches from `SimpleSpanProcessor` to
|
|
1869
|
+
`BatchSpanProcessor`, so exporting a span no longer puts network latency
|
|
1870
|
+
inline on the agent loop.
|
|
1871
|
+
|
|
1872
|
+
Adds the first telemetry tests in the repo.
|
|
1873
|
+
|
|
1874
|
+
- 935b8f3: Carry budgets across a checkpoint resume, and count the side-channel model calls.
|
|
1875
|
+
|
|
1876
|
+
Budget enforcement was neither durable nor total.
|
|
1877
|
+
|
|
1878
|
+
**Durable.** `IterationCheckpoint` faithfully persisted `tokenUsage`, `costInfo`
|
|
1879
|
+
and `guardState`, and the resume path replayed messages only — the numbers were
|
|
1880
|
+
written and then discarded on the way back in. A run checkpointed at $4.80 of a
|
|
1881
|
+
$5 cap came back with a brand-new $5 and a brand-new timeout clock, so a task
|
|
1882
|
+
that parked five times spent 5x its cap while every invocation truthfully
|
|
1883
|
+
reported itself in budget. `RunPersistence.restoreUsage()` and
|
|
1884
|
+
`GuardCoordinator.restoreElapsed()` (also available as `elapsedMsOffset` at
|
|
1885
|
+
construction) seed both from the checkpoint before the first iteration, so a
|
|
1886
|
+
resumed run that is already over budget stops immediately.
|
|
1887
|
+
|
|
1888
|
+
**Total.** Three `chatStream` call sites bypassed `accumulateUsage` entirely, so
|
|
1889
|
+
a run with `tokenBudget: 200_000` could send well past 200k and never trip
|
|
1890
|
+
`token_budget`:
|
|
1891
|
+
|
|
1892
|
+
- the advisory phase — its usage was already captured for reporting and simply
|
|
1893
|
+
never reached the accountant;
|
|
1894
|
+
- the compaction verifier — the worst offender, since it fires exactly when the
|
|
1895
|
+
context is largest. It now takes an optional `UsageSink`;
|
|
1896
|
+
- `RouterAgent` — routing runs before any `RunPersistence` exists, so
|
|
1897
|
+
`RoutingDecision` now carries the routing call's `usage` (summed across
|
|
1898
|
+
retries) and the router folds it into the result instead of reporting the
|
|
1899
|
+
delegate's usage alone.
|
|
1900
|
+
|
|
1901
|
+
- 935b8f3: namzu takes its naming from nobody, and now there is a gate that proves it.
|
|
1902
|
+
|
|
1903
|
+
`scripts/audit-external-names.mjs` refuses a third-party product name in a
|
|
1904
|
+
comment or an identifier, and runs in CI. It found 31 real ones — most of
|
|
1905
|
+
them in the TUI, where the design was being explained as "modelled on how X
|
|
1906
|
+
presents text", "X-style grouping", "like X / Y".
|
|
1907
|
+
|
|
1908
|
+
That is the failure the rule exists for. A design explained by reference to
|
|
1909
|
+
somebody else's product has handed over its rationale: the next reader
|
|
1910
|
+
reaches for that product's model instead of asking what namzu is trying to
|
|
1911
|
+
achieve, and when the reference changes the comment becomes a claim nobody
|
|
1912
|
+
can check. Each one now states the same decision on its own terms — what it
|
|
1913
|
+
accomplishes, and what breaks without it.
|
|
1914
|
+
|
|
1915
|
+
The kernel had eleven, all in prose explaining a wire behaviour by naming
|
|
1916
|
+
the vendor whose endpoint exhibits it. A 400 for an unanswered `tool_use`
|
|
1917
|
+
is a property of the protocol, not of a company; several function-calling
|
|
1918
|
+
endpoints report `stop` alongside populated tool calls, and which ones is
|
|
1919
|
+
not the point.
|
|
1920
|
+
|
|
1921
|
+
The identity prompt named the products it told the model not to be. It now
|
|
1922
|
+
says the stronger thing without them: the underlying model is an
|
|
1923
|
+
implementation detail of how namzu runs, not who it is.
|
|
1924
|
+
|
|
1925
|
+
What the audit deliberately does NOT flag, because a rule that cries wolf
|
|
1926
|
+
gets switched off: wire values and the files that carry them. A
|
|
1927
|
+
context-window table keyed by model id must contain real model ids or it
|
|
1928
|
+
resolves nothing; a driver package is named after the service it drives.
|
|
1929
|
+
The exemption is per path and narrow, and the script says where the line
|
|
1930
|
+
falls. Scanning string literals was tried and rejected in the same spirit —
|
|
1931
|
+
it flagged driver ids in switch statements and model ids in test fixtures
|
|
1932
|
+
everywhere, which would have meant exempting half the tree.
|
|
1933
|
+
|
|
1934
|
+
Two matcher details worth keeping: the camelCase check is case-SENSITIVE,
|
|
1935
|
+
because an `i` flag turns `[A-Z]` into `[A-Za-z]` and the rule starts
|
|
1936
|
+
rejecting `coherent` for `cohere` and `strands` for the English verb. And
|
|
1937
|
+
`cursor` is absent from the list entirely — it collides with the pagination
|
|
1938
|
+
cursor this codebase threads through every list call.
|
|
1939
|
+
|
|
1940
|
+
- 935b8f3: `glob`, `grep` and `ls` stay inside the working directory, and inside the
|
|
1941
|
+
sandbox when there is one.
|
|
1942
|
+
|
|
1943
|
+
Two independent failures, both in tools that are in the default set.
|
|
1944
|
+
|
|
1945
|
+
**The path escape needed no sandbox at all.** All three resolved a
|
|
1946
|
+
caller-supplied `path` against the working directory bare, so
|
|
1947
|
+
`path: "../../.."` landed wherever that pointed and the tool read it
|
|
1948
|
+
happily. The containment rule already existed — in one private function
|
|
1949
|
+
inside the local sandbox provider — and these never reached it. `grep`
|
|
1950
|
+
returns file **content**, so what escaped was not a listing. For `glob` the
|
|
1951
|
+
same escape also rides in on the _pattern_, since the base directory lifted
|
|
1952
|
+
out of `"../../**/*.pem"` is caller-supplied too.
|
|
1953
|
+
|
|
1954
|
+
A refusal now reaches the model as a failed tool result carrying the reason,
|
|
1955
|
+
rather than a throw, so it can correct itself.
|
|
1956
|
+
|
|
1957
|
+
**The sandbox was not a read boundary.** `glob` and `grep` called
|
|
1958
|
+
`node:fs/promises` against the host working directory and referenced
|
|
1959
|
+
`context.sandbox` nowhere, while every sibling builtin already remembered
|
|
1960
|
+
the branch. With a container backend wired in they read the SDK process's
|
|
1961
|
+
own filesystem. The paths they returned were host-relative too, while
|
|
1962
|
+
`read` resolves what it is handed _inside_ the sandbox — so every
|
|
1963
|
+
search-to-read handoff either failed or opened a different file. The two
|
|
1964
|
+
roots genuinely diverge: the executor passes `workingDirectory` through
|
|
1965
|
+
unchanged alongside the sandbox.
|
|
1966
|
+
|
|
1967
|
+
Both now route through `context.sandbox` when present. `grep` abstracts only
|
|
1968
|
+
the file _source_ — enumerate and read — so matching, context lines and the
|
|
1969
|
+
caps stay one implementation; duplicating the substantive half is how the
|
|
1970
|
+
two paths would drift, and the sandboxed one is the one nobody runs by
|
|
1971
|
+
accident.
|
|
1972
|
+
|
|
1973
|
+
**Sandbox paths are no longer run through the host's path module.** A
|
|
1974
|
+
sandbox is a POSIX filesystem whatever the host runs, so resolving its paths
|
|
1975
|
+
host-side rewrites them whenever the two disagree — on a Windows host
|
|
1976
|
+
`resolve('/workspace')` becomes `C:\workspace`, and a container path stops
|
|
1977
|
+
being a container path. This was found by the new tests, which returned no
|
|
1978
|
+
results at all until it was fixed.
|
|
1979
|
+
|
|
1980
|
+
- 935b8f3: Fix five wiring defects found by auditing the previous wave rather than
|
|
1981
|
+
trusting it. All five had passing unit tests, because those tests
|
|
1982
|
+
constructed the internal class directly and so proved the helper worked
|
|
1983
|
+
while proving nothing about whether `query()` ever reached it.
|
|
1984
|
+
|
|
1985
|
+
- **`query({ repairToolCall })` was a no-op.** The field was spread into
|
|
1986
|
+
`ToolingBootstrap.init`, whose config type has no such field and whose
|
|
1987
|
+
`init` enumerates what it forwards. Object spread bypasses excess-property
|
|
1988
|
+
checking, so it type-checked and did nothing.
|
|
1989
|
+
- **A truncated tool-input stream never reached the repairer** — the case
|
|
1990
|
+
the hook exists for. `executeSingle` answered `inputTruncated` with a
|
|
1991
|
+
generic hint and returned before repair ran. The partial buffer is now
|
|
1992
|
+
preserved (`ToolCall.metadata.partialArguments`) and offered to the
|
|
1993
|
+
repairer, because one handed an empty object has nothing to work from.
|
|
1994
|
+
- **`{action:'retry'}` from `post_tool_use` was silently discarded.** It was
|
|
1995
|
+
read inside a loop bounded by the tool's `maxRetries`, which defaults to
|
|
1996
|
+
0, so the loop body never ran. Hook-requested retries now get their own
|
|
1997
|
+
bounded budget (`HOOK_RETRY_BUDGET`): the hook is host code reacting to
|
|
1998
|
+
one specific result, a more specific signal than the tool's blanket
|
|
1999
|
+
idempotency declaration.
|
|
2000
|
+
- **A cross-process HITL resume never cleared the park.** The approved batch
|
|
2001
|
+
executed and the checkpoint kept `pending` with no `resolvedAt`, so an
|
|
2002
|
+
approval queue re-served a destructive call that had already run — the
|
|
2003
|
+
exact failure recording the park exists to prevent.
|
|
2004
|
+
- **Configuring an output guardrail rewrote the run's outcome.** The branch
|
|
2005
|
+
called `markCompleted()` purely to materialize the produced text, so a
|
|
2006
|
+
cancelled run reported `completed` merely because a safety check was
|
|
2007
|
+
present. Reading and settling are now separate (`materializeResult`), and
|
|
2008
|
+
`setResult` is sticky so the later `resolveResult` cannot re-expand a
|
|
2009
|
+
redaction back to the raw model output.
|
|
2010
|
+
|
|
2011
|
+
- 935b8f3: Fix five defects in the eval harness and RAG retrieval — all plain bugs
|
|
2012
|
+
with correct answers, not design trade-offs.
|
|
2013
|
+
|
|
2014
|
+
**Eval harness — it could report green on a broken suite.**
|
|
2015
|
+
|
|
2016
|
+
- A case whose run THREW scored 1.0. `executeCase` catches the failure and
|
|
2017
|
+
returns an empty run, and an empty run walks into every scorer's happy
|
|
2018
|
+
path: `stepBudgetScorer` sees 0 steps against its allowance and returns
|
|
2019
|
+
1, `trajectoryScorer` sees "no tools expected, none called" and returns
|
|
2020
|
+
1. The failure was recorded on `run.error` and nothing consulted it. Any
|
|
2021
|
+
run that failed now scores 0, with the error as the reason.
|
|
2022
|
+
- Two scorers sharing a name silently collapsed. Scores are keyed by name,
|
|
2023
|
+
so a second `containsScorer(...)` — also called `contains` — overwrote
|
|
2024
|
+
the first, and the case mean's denominator became the count of distinct
|
|
2025
|
+
NAMES rather than scorers run. With one scoring 0 and one scoring 1 the
|
|
2026
|
+
suite reported 1.0 where the honest answer is 0.5. Duplicate names now
|
|
2027
|
+
throw.
|
|
2028
|
+
|
|
2029
|
+
**RAG retrieval.**
|
|
2030
|
+
|
|
2031
|
+
- `bm25Score` implemented only the term-frequency saturation half and no
|
|
2032
|
+
IDF at all — the half that discriminates. Without it every matched term
|
|
2033
|
+
weighs the same, so a chunk matching three common words outranks the one
|
|
2034
|
+
chunk containing the rare term the query was about. It also normalized
|
|
2035
|
+
document length against a hardcoded `avgDl = 256` rather than the corpus
|
|
2036
|
+
in front of it. Both now computed from the candidate set.
|
|
2037
|
+
- `hybridSearch` blended bounded cosine with unbounded BM25 linearly, so
|
|
2038
|
+
`hybridAlpha` did not weight the two halves — whichever scale happened to
|
|
2039
|
+
be larger won. Each ranking is normalized to [0,1] first.
|
|
2040
|
+
- The recursive chunker used `text.split(sep)`, which DELETES the
|
|
2041
|
+
separator: splitting on `'. '` stripped every sentence terminator and
|
|
2042
|
+
`'\n\n'` stripped every paragraph break, so the chunk shown to the model
|
|
2043
|
+
was not what the document said.
|
|
2044
|
+
|
|
2045
|
+
- 935b8f3: A tool call a probe vetoed now says it failed.
|
|
2046
|
+
|
|
2047
|
+
The probe-veto branch was the only result-producing branch in the executor
|
|
2048
|
+
that left `isError` off, and `isError` being optional meant the compiler
|
|
2049
|
+
could not catch it. Five lines above, the `tool_completed` event for the
|
|
2050
|
+
same veto carried `isError: true` — so a run's event stream and the result
|
|
2051
|
+
it returned disagreed about the same call, in the same function.
|
|
2052
|
+
|
|
2053
|
+
Four things degraded off that one omission:
|
|
2054
|
+
|
|
2055
|
+
- Two drivers emit their failure marker only when this is true, so the model
|
|
2056
|
+
read a **successful** result whose body begins `Error: Probe "x" vetoed…`
|
|
2057
|
+
and the failure-recovery path it was trained on never fired.
|
|
2058
|
+
- The persisted step recorded a literal `isError: false`, so the run record
|
|
2059
|
+
contradicted its own event stream.
|
|
2060
|
+
- Compaction guards error results from being cleared; a vetoed result was
|
|
2061
|
+
silently excluded from that protection.
|
|
2062
|
+
|
|
2063
|
+
- 935b8f3: A provider fault keeps the classification its driver produced
|
|
2064
|
+
|
|
2065
|
+
The stream turn flattened a classified `ProviderError` to its message and threw a fresh error in its place, so `retryable`, `status` and `retryAfterMs` were all discarded — and `NamzuError`'s default for `provider_error` is not-retryable. A 429 or 529 that had exhausted its backoff therefore settled the run **failed**, where the documented behaviour is a **pause** with a checkpoint to resume from. `toPlatformError` already projects the right shape; it was simply never handed one.
|
|
2066
|
+
|
|
2067
|
+
The asymmetry was visible in the codebase: the same fault raised inside the compaction verifier propagates untouched and does pause, so identical faults settled oppositely depending on whether compaction happened to run that iteration. A classified failure is now rethrown as itself, and an unclassified one keeps its cause.
|
|
2068
|
+
|
|
2069
|
+
- 935b8f3: Four places where the runtime lost information it was holding, or admitted a limit it had already exceeded.
|
|
2070
|
+
|
|
2071
|
+
- **A clipped sandbox stream said nothing.** `SandboxExecResult` carries `stdoutTruncated` / `stderrTruncated`, added when the other backend needed to report a clipped stream. The local backend clipped at the same cap and never set them, so the model read a complete-looking result whose tail was gone — against the contract's own note that the kernel does not truncate silently. The tool layer already renders the flag; nothing raised it. The accumulator is now a small `CappedStream` that reports hitting its cap, and reports it at the first byte past it rather than at exactly the cap.
|
|
2072
|
+
|
|
2073
|
+
- **Two concurrent spawns could both take the last delegation slot.** The width cap counted a parent's children and then created one, with every other provisioning step in between. Two spawns under the same parent both read the same count, both saw room, and both created, so a cap of N admitted N+1. Provisioning is now serialized per parent session — the narrowest key that makes the check and the write one critical section; spawns under different parents never contend. In-process only, which is the honest scope: cross-process capacity belongs to the store.
|
|
2074
|
+
|
|
2075
|
+
- **`agent_task_list` forgot finished workers.** Terminal tasks leave the manager 30 seconds after they settle, and the gateway's list was rebuilt by looking each tracked id back up — so a task that finished a minute ago vanished from the exact tool whose description says to call it before declaring multi-worker work done. A supervisor could not tell an evicted task from one that never launched; both read as absence. The gateway now snapshots each task's settled summary while the manager still holds it, and prefers the live record whenever there is one.
|
|
2076
|
+
|
|
2077
|
+
- **The compaction summary hid its dropped tool results.** Every capped section in the working-state summary appends a line naming what it evicted — except tool results, which counted their evictions and rendered without them. The section carrying the most volume was the only one presenting a fragment as the whole record.
|
|
2078
|
+
|
|
2079
|
+
- 935b8f3: Clearing a tool result no longer destroys the way back to it, and skill
|
|
2080
|
+
frontmatter fails loudly instead of quietly.
|
|
2081
|
+
|
|
2082
|
+
**A cleared tool result kept its recovery pointer.** When a result exceeds
|
|
2083
|
+
the output budget its full text is written to disk and a line pointing at
|
|
2084
|
+
the file is embedded _in_ the result. Compaction then replaced the whole
|
|
2085
|
+
content with a placeholder — deleting that line for exactly the largest
|
|
2086
|
+
outputs, and advising the model to "call the tool again", which is advice to
|
|
2087
|
+
re-run something that returned megabytes. The spill line now survives, along
|
|
2088
|
+
with the `read`/`grep` instruction that goes with it.
|
|
2089
|
+
|
|
2090
|
+
A head and tail survive too. Clearing was total, so a result just over the
|
|
2091
|
+
1,000-character minimum lost 100% of itself — including the few lines the
|
|
2092
|
+
agent was actively reasoning from — to reclaim a few hundred characters. A
|
|
2093
|
+
result shorter than the head and tail together is kept whole, since eliding
|
|
2094
|
+
it would drop content while saving nothing.
|
|
2095
|
+
|
|
2096
|
+
**The skill frontmatter fence is anchored to a line.** An unanchored search
|
|
2097
|
+
for `---` cut the frontmatter at the first occurrence anywhere — inside a
|
|
2098
|
+
quoted value, inside a URL — which both truncated the metadata and spilled
|
|
2099
|
+
the remainder into the body, where it reaches the system prompt verbatim.
|
|
2100
|
+
|
|
2101
|
+
**YAML this reader does not implement is refused rather than mangled.** The
|
|
2102
|
+
reader is a flat key/value splitter and the documented contract says "YAML
|
|
2103
|
+
frontmatter" with no restriction, so an author has every reason to write a
|
|
2104
|
+
block scalar or a flow sequence. `description: >-` produced the literal
|
|
2105
|
+
string `">-"`, which passed validation and registered with no warning — the
|
|
2106
|
+
skill existed and was never selected, because its description said nothing.
|
|
2107
|
+
`[Read, Grep]` became that literal text and was interpolated into the
|
|
2108
|
+
prompt. Both now name the file and the field.
|
|
2109
|
+
|
|
2110
|
+
That is worse for exactly one skill — the one already silently broken — and
|
|
2111
|
+
better for everyone looking for it.
|
|
2112
|
+
|
|
2113
|
+
- 935b8f3: **Breaking:** `@namzu/sandbox` declares only the backends it has.
|
|
2114
|
+
|
|
2115
|
+
Four of the shapes this package offered could type-check and then throw: a `process` tier, a `passthrough` tier, and two adapters to third-party managed schedulers, none of which was ever written. Each demanded required configuration for a call that was never made — the `self-hosted` microvm arm went further and required three fields belonging to a local-daemon path that does not exist, while the two fields the working path needs were optional. So the only configuration that ran had to supply three values nothing reads, and omitting the two that matter compiled its way to a runtime throw.
|
|
2116
|
+
|
|
2117
|
+
`SandboxTier` is now `container | microvm`. `MicroVMBackendConfig` is one shape whose `orchestratorEndpoint` and `getToken` are required. `SandboxBackendNotImplementedError` stays exported and thrown: a JS host that invents a tier gets a named refusal rather than a provider that confines nothing.
|
|
2118
|
+
|
|
2119
|
+
The `sandbox.platform` health check now asks the provider what this host enforces instead of answering from a table keyed on the OS name. That table had drifted both ways — it called the Linux probe unimplemented long after the provider began probing real flags, and it told a Windows operator that sandboxing is "not supported", which is true of the in-process tier and silent about the container tier that runs there. Every non-passing result now names the missing controls and what to do about them.
|
|
2120
|
+
|
|
2121
|
+
`SANDBOX_ISOLATION_CONTROLS` is exported as a value from `@namzu/sdk`. It was reachable only through `export type *`, so importing it type-checked and then failed on the first line of a built binary.
|
|
2122
|
+
|
|
2123
|
+
- 29f35c8: Constrain `ask_user_question` to its canonical JSON object-array input on
|
|
2124
|
+
supported providers and reject malformed compatibility shapes at runtime.
|
|
2125
|
+
- 935b8f3: Overflow reaches the rescue that exists for it.
|
|
2126
|
+
|
|
2127
|
+
Overflow is the one 4xx the runtime can act on: it sheds history and
|
|
2128
|
+
retries. Everything else in the 400 family is surfaced. So the rescue is
|
|
2129
|
+
gated on the code being **exactly** `context_length_exceeded`, and anything
|
|
2130
|
+
that misses that gate dies holding the remedy.
|
|
2131
|
+
|
|
2132
|
+
Three things missed it. Measured before and after, five of six realistic
|
|
2133
|
+
overflow shapes never reached relief; now all six do.
|
|
2134
|
+
|
|
2135
|
+
- **The structural code was extracted and then discarded.** The cause-chain
|
|
2136
|
+
walk returned the first `code` it found and fed it only to the two
|
|
2137
|
+
transport-errno sets, so a provider that said `context_length_exceeded` in
|
|
2138
|
+
the one field designed to say it was answered with a substring search that
|
|
2139
|
+
did not match. A structural code is now consulted **before** the status,
|
|
2140
|
+
because it is strictly more specific: a 400 is a category, the code is the
|
|
2141
|
+
diagnosis. The gateway `type` discriminator and a nested error envelope
|
|
2142
|
+
are read the same way.
|
|
2143
|
+
- **The phrase list missed the common wordings.** "too long for", "maximum
|
|
2144
|
+
length", "exceeds the maximum", "input is too large" all fell through to a
|
|
2145
|
+
plain non-retryable invalid request.
|
|
2146
|
+
- **The Converse driver pre-filed `ValidationException` as
|
|
2147
|
+
`invalid_request`.** That name covers both a malformed request and a
|
|
2148
|
+
prompt past the model's window, and only one of those is recoverable — so
|
|
2149
|
+
guessing from the name made the recoverable case unrecoverable by
|
|
2150
|
+
construction, because the shared classifier short-circuits on an error
|
|
2151
|
+
that already carries a code and never read the body. It now hands that one
|
|
2152
|
+
name to the classifier. The result is still a `ProviderError`, so the
|
|
2153
|
+
driver's contract is unchanged; it just stops answering a question it
|
|
2154
|
+
cannot answer from the name alone.
|
|
2155
|
+
|
|
2156
|
+
The rate-limit half of the same class is fixed alongside: a provider that
|
|
2157
|
+
reports `rate_limit_exceeded` structurally under a 400 is now retryable
|
|
2158
|
+
instead of being filed as a bad request.
|
|
2159
|
+
|
|
2160
|
+
- 935b8f3: Answer every `tool_use` block, and stop a human approval from overriding a gate denial.
|
|
2161
|
+
|
|
2162
|
+
Four tool-review paths — verification-gate all-deny, human `reject_tools`,
|
|
2163
|
+
`modify_tools` with everything denied, and `modify_tools` with a _partial_
|
|
2164
|
+
deny — returned without producing a `tool_result` for the calls they refused.
|
|
2165
|
+
The assistant turn stayed unanswered, so the next provider request was
|
|
2166
|
+
malformed (`400 … Did not find 1 tool_result block(s)`) and the run died.
|
|
2167
|
+
Any host wiring a rejection decision (including the `namzu` TUI's permission
|
|
2168
|
+
prompt) hit this on the first decline.
|
|
2169
|
+
|
|
2170
|
+
`ToolExecutor.executeBatch` now takes an optional denial map and answers
|
|
2171
|
+
_every_ call in the batch: refused calls get a synthetic error `tool_result`
|
|
2172
|
+
carrying the reason instead of being executed. Because there is one place
|
|
2173
|
+
that turns tool calls into messages, the invariant now holds by construction.
|
|
2174
|
+
The refusal reason travels inside the `tool_result` rather than as a trailing
|
|
2175
|
+
`[SYSTEM]` user message, so a rejection steers the model instead of only
|
|
2176
|
+
stopping it.
|
|
2177
|
+
|
|
2178
|
+
Alongside it, a policy-bypass fix: on the gate's _mixed_-decision path a human
|
|
2179
|
+
`approve_tools` replayed the full, unfiltered response and executed the calls
|
|
2180
|
+
the gate had denied. Gate denials are now threaded through every downstream
|
|
2181
|
+
execution, and a `modify_tools` rewrite can no longer resurrect a denied call.
|
|
2182
|
+
|
|
2183
|
+
Checkpoint resume repairs unanswered tool calls (`removeDanglingMessages`)
|
|
2184
|
+
before replaying history, so a run parked at a tool-review checkpoint and
|
|
2185
|
+
resumed in a fresh process no longer fails on its first model call.
|
|
2186
|
+
|
|
2187
|
+
- 935b8f3: Fix three defects in delegation and compaction that unit tests could not
|
|
2188
|
+
see, because the numbers involved stay plausible-looking until you check
|
|
2189
|
+
their units and their object identity.
|
|
2190
|
+
|
|
2191
|
+
- **A child agent's wall-clock deadline was a TOKEN count.** The fallback
|
|
2192
|
+
was `context.budgetTracker.remaining` read as `timeoutMs`. It hid because
|
|
2193
|
+
a six-figure token budget lands in a plausible range of milliseconds; it
|
|
2194
|
+
bit at the edges, where an unlimited budget (`0`) produced a child that
|
|
2195
|
+
was out of time on arrival. There is now an explicit
|
|
2196
|
+
`AgentManagerConfig.childTimeoutMs` (default 5 minutes).
|
|
2197
|
+
- **Sibling sub-agents each got a full share of the same pool.**
|
|
2198
|
+
`LocalTaskGateway` handed every spawn a _cloned_ budget tracker, so
|
|
2199
|
+
`AgentManager.spawn`'s `remaining -= allocatedTokens` debited a throwaway
|
|
2200
|
+
object. N children were each allocated `maxBudgetFraction` of the
|
|
2201
|
+
untouched parent total — N × 50% of a budget that only had 100% in it.
|
|
2202
|
+
The tracker is shared, as the debit always assumed.
|
|
2203
|
+
- **The compaction verifier sent `model: ''`.** Some drivers quietly
|
|
2204
|
+
substitute a default and others reject outright — on Bedrock the model id
|
|
2205
|
+
IS the endpoint. So compaction's LLM verifier failed exactly on the
|
|
2206
|
+
providers where a long run most needs it, and the failure surfaced as
|
|
2207
|
+
compaction killing the run it exists to save. It now receives the run's
|
|
2208
|
+
model.
|
|
2209
|
+
|
|
2210
|
+
Each fix ships with a test that was confirmed to fail against the old code.
|
|
2211
|
+
|
|
2212
|
+
- 935b8f3: Normalize and memoize the tool schema that goes on the wire, and stop
|
|
2213
|
+
losing MCP schemas in translation.
|
|
2214
|
+
|
|
2215
|
+
- `$schema` (`http://json-schema.org/draft-07/schema#`) was stamped on
|
|
2216
|
+
every tool's parameters and sent on every request. No provider reads it,
|
|
2217
|
+
and it rides in the tools block — position 0, inside the cached prefix.
|
|
2218
|
+
Stripped.
|
|
2219
|
+
- `toLLMTools` re-walked every registered tool's Zod tree once per
|
|
2220
|
+
iteration. Rendering is now memoized on the schema object and deeply
|
|
2221
|
+
frozen, so it is both free and byte-identical across iterations — the
|
|
2222
|
+
tools block heads the prompt-cache prefix, and a single reordered key
|
|
2223
|
+
invalidates the whole run's cache.
|
|
2224
|
+
- `mcpJsonSchemaToZod` collapsed `array` to `z.array(z.unknown())` and
|
|
2225
|
+
`object` to `z.record(z.unknown())`. Because a bridged tool's schema
|
|
2226
|
+
round-trips (server JSON Schema → Zod → JSON Schema on the wire), every
|
|
2227
|
+
MCP tool taking a structured argument was shown to the model as "an
|
|
2228
|
+
array of anything" or "an object with any keys" — nested properties,
|
|
2229
|
+
item types, enums and descriptions all gone. It is now recursive and
|
|
2230
|
+
faithful: nested objects, array items, enums, `const`, `anyOf`/`oneOf`,
|
|
2231
|
+
nullable (`type: ['string','null']`), descriptions and defaults survive.
|
|
2232
|
+
- MCP objects default to closed (`additionalProperties: false`) instead of
|
|
2233
|
+
`.passthrough()`, so the model is no longer told it may invent arguments
|
|
2234
|
+
the server never declared. A server that explicitly sets
|
|
2235
|
+
`additionalProperties: true` is still honored.
|
|
2236
|
+
|
|
2237
|
+
- 935b8f3: A turn that asked for tools no longer ends because the provider said it
|
|
2238
|
+
didn't.
|
|
2239
|
+
|
|
2240
|
+
The iteration loop ended the turn on `finishReason === 'stop'` **before**
|
|
2241
|
+
looking at whether the model had asked for tools. Endpoints on the OpenAI
|
|
2242
|
+
wire shape — gateways and local servers especially — routinely report `stop`
|
|
2243
|
+
on the same response that carries a populated `tool_calls`, and three of
|
|
2244
|
+
this repo's drivers passed that value straight through.
|
|
2245
|
+
|
|
2246
|
+
The damage was total and silent: every requested call skipped, an assistant
|
|
2247
|
+
turn left carrying `tool_use` blocks nothing ever answered, and the run
|
|
2248
|
+
settling as though it had finished the work it never started.
|
|
2249
|
+
|
|
2250
|
+
- **The runtime now treats tool calls as the fact and the finish reason as
|
|
2251
|
+
the summary.** When they disagree, the calls win. This is the load-bearing
|
|
2252
|
+
fix: it protects every driver, including ones this repo does not ship.
|
|
2253
|
+
- **The three drivers that cast the reason raw now report it honestly** —
|
|
2254
|
+
a stream that produced a tool call reports `tool_calls`, whatever the
|
|
2255
|
+
endpoint called it. Defence in depth, and it makes the reported reason
|
|
2256
|
+
true for anyone else reading it.
|
|
2257
|
+
|
|
2258
|
+
The existing suite could not catch this: the scripted mock reports
|
|
2259
|
+
`tool_calls` whenever it emits one, which is what an honest provider does
|
|
2260
|
+
and therefore never the case that breaks.
|
|
2261
|
+
|
|
2262
|
+
- 935b8f3: The question a run asked and the answer that resolved it match on the same key
|
|
2263
|
+
|
|
2264
|
+
`user_question_asked` carried a `question_id` and `user_question_answered` did not, so a client that keyed on the question id — the natural key, since it is what routes an answer back on resume — could not match the two halves without also having stored the checkpoint id. The answered event now carries it whenever the resolution named one.
|
|
2265
|
+
|
|
2266
|
+
Twelve event mappings across the SSE and agent-to-agent bridges shipped with no test: the nine event kinds added since those mappers were first written, plus the failure-classification and message-role paths. A wire transform with no test is a contract nobody checked — the field names are what a remote consumer parses, and the transforms return `Record<string, unknown>`, so renaming one is a break type-checking cannot see.
|
|
2267
|
+
|
|
2268
|
+
## 2.0.0
|
|
2269
|
+
|
|
2270
|
+
### Major Changes
|
|
2271
|
+
|
|
2272
|
+
- 6b0fbfd: Replace the built-in filesystem mutation contracts with one strict canonical
|
|
2273
|
+
shape per tool: `edit` accepts `path`, `old_string`, `new_string`, and optional
|
|
2274
|
+
`replace_all`; `write` accepts `path` and `content`. Remove line insertion and
|
|
2275
|
+
legacy aliases, serialize same-process mutations by resolved path, and document
|
|
2276
|
+
replay-safe marker advancement for bounded long-document writes. Local writes
|
|
2277
|
+
commit through same-directory temp files and atomic rename; sandbox
|
|
2278
|
+
implementations are required to provide the same atomic replacement contract.
|
|
2279
|
+
|
|
2280
|
+
### Minor Changes
|
|
2281
|
+
|
|
2282
|
+
- 11167dd: Separate runtime tool validation from canonical model-facing JSON Schema,
|
|
2283
|
+
propagate constrained-input hints through the agent loop, and map reviewed
|
|
2284
|
+
schemas to Anthropic strict tool use with capability-aware overrides. The
|
|
2285
|
+
built-in edit tool advertises only canonical arguments.
|
|
2286
|
+
|
|
3
2287
|
## 1.4.0
|
|
4
2288
|
|
|
5
2289
|
### Minor Changes
|