@sema-agent/core 1.279.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/LICENSE +104 -0
- package/NOTICE.md +30 -0
- package/README.md +143 -0
- package/README.zh-CN.md +138 -0
- package/dist/agents/agent-definition.d.ts +3 -0
- package/dist/agents/agent-definition.d.ts.map +1 -0
- package/dist/agents/agent-definition.js +9 -0
- package/dist/agents/agent-definition.js.map +1 -0
- package/dist/agents/builtin-agents.d.ts +9 -0
- package/dist/agents/builtin-agents.d.ts.map +1 -0
- package/dist/agents/builtin-agents.js +114 -0
- package/dist/agents/builtin-agents.js.map +1 -0
- package/dist/agents/cascade.d.ts +44 -0
- package/dist/agents/cascade.d.ts.map +1 -0
- package/dist/agents/cascade.js +153 -0
- package/dist/agents/cascade.js.map +1 -0
- package/dist/agents/observer.d.ts +122 -0
- package/dist/agents/observer.d.ts.map +1 -0
- package/dist/agents/observer.js +365 -0
- package/dist/agents/observer.js.map +1 -0
- package/dist/agents/repair-loop.d.ts +49 -0
- package/dist/agents/repair-loop.d.ts.map +1 -0
- package/dist/agents/repair-loop.js +147 -0
- package/dist/agents/repair-loop.js.map +1 -0
- package/dist/agents/session-util.d.ts +3 -0
- package/dist/agents/session-util.d.ts.map +1 -0
- package/dist/agents/session-util.js +4 -0
- package/dist/agents/session-util.js.map +1 -0
- package/dist/agents/subagent-steps.d.ts +31 -0
- package/dist/agents/subagent-steps.d.ts.map +1 -0
- package/dist/agents/subagent-steps.js +167 -0
- package/dist/agents/subagent-steps.js.map +1 -0
- package/dist/agents/subagent.d.ts +152 -0
- package/dist/agents/subagent.d.ts.map +1 -0
- package/dist/agents/subagent.js +1698 -0
- package/dist/agents/subagent.js.map +1 -0
- package/dist/agents/suspend-guard.d.ts +6 -0
- package/dist/agents/suspend-guard.d.ts.map +1 -0
- package/dist/agents/suspend-guard.js +19 -0
- package/dist/agents/suspend-guard.js.map +1 -0
- package/dist/agents/teacher.d.ts +70 -0
- package/dist/agents/teacher.d.ts.map +1 -0
- package/dist/agents/teacher.js +462 -0
- package/dist/agents/teacher.js.map +1 -0
- package/dist/agents/team.d.ts +92 -0
- package/dist/agents/team.d.ts.map +1 -0
- package/dist/agents/team.js +231 -0
- package/dist/agents/team.js.map +1 -0
- package/dist/agents/tool-filter.d.ts +4 -0
- package/dist/agents/tool-filter.d.ts.map +1 -0
- package/dist/agents/tool-filter.js +18 -0
- package/dist/agents/tool-filter.js.map +1 -0
- package/dist/agents/verify.d.ts +46 -0
- package/dist/agents/verify.d.ts.map +1 -0
- package/dist/agents/verify.js +177 -0
- package/dist/agents/verify.js.map +1 -0
- package/dist/bench/metrics.d.ts +259 -0
- package/dist/bench/metrics.d.ts.map +1 -0
- package/dist/bench/metrics.js +439 -0
- package/dist/bench/metrics.js.map +1 -0
- package/dist/bin/sema-tb.d.ts +3 -0
- package/dist/bin/sema-tb.d.ts.map +1 -0
- package/dist/bin/sema-tb.js +373 -0
- package/dist/bin/sema-tb.js.map +1 -0
- package/dist/bin/tb-env.d.ts +2 -0
- package/dist/bin/tb-env.d.ts.map +1 -0
- package/dist/bin/tb-env.js +15 -0
- package/dist/bin/tb-env.js.map +1 -0
- package/dist/brain/anthropic.d.ts +16 -0
- package/dist/brain/anthropic.d.ts.map +1 -0
- package/dist/brain/anthropic.js +635 -0
- package/dist/brain/anthropic.js.map +1 -0
- package/dist/brain/circuit-breaker.d.ts +26 -0
- package/dist/brain/circuit-breaker.d.ts.map +1 -0
- package/dist/brain/circuit-breaker.js +118 -0
- package/dist/brain/circuit-breaker.js.map +1 -0
- package/dist/brain/degrading.d.ts +33 -0
- package/dist/brain/degrading.d.ts.map +1 -0
- package/dist/brain/degrading.js +175 -0
- package/dist/brain/degrading.js.map +1 -0
- package/dist/brain/errors.d.ts +14 -0
- package/dist/brain/errors.d.ts.map +1 -0
- package/dist/brain/errors.js +43 -0
- package/dist/brain/errors.js.map +1 -0
- package/dist/brain/failover.d.ts +3 -0
- package/dist/brain/failover.d.ts.map +1 -0
- package/dist/brain/failover.js +85 -0
- package/dist/brain/failover.js.map +1 -0
- package/dist/brain/model-presets.d.ts +5 -0
- package/dist/brain/model-presets.d.ts.map +1 -0
- package/dist/brain/model-presets.js +30 -0
- package/dist/brain/model-presets.js.map +1 -0
- package/dist/brain/openai.d.ts +13 -0
- package/dist/brain/openai.d.ts.map +1 -0
- package/dist/brain/openai.js +581 -0
- package/dist/brain/openai.js.map +1 -0
- package/dist/brain/reasoning.d.ts +33 -0
- package/dist/brain/reasoning.d.ts.map +1 -0
- package/dist/brain/reasoning.js +84 -0
- package/dist/brain/reasoning.js.map +1 -0
- package/dist/brain/repetition.d.ts +16 -0
- package/dist/brain/repetition.d.ts.map +1 -0
- package/dist/brain/repetition.js +96 -0
- package/dist/brain/repetition.js.map +1 -0
- package/dist/brain/request-params.d.ts +6 -0
- package/dist/brain/request-params.d.ts.map +1 -0
- package/dist/brain/request-params.js +44 -0
- package/dist/brain/request-params.js.map +1 -0
- package/dist/brain/retry.d.ts +3 -0
- package/dist/brain/retry.d.ts.map +1 -0
- package/dist/brain/retry.js +23 -0
- package/dist/brain/retry.js.map +1 -0
- package/dist/brain/routing.d.ts +7 -0
- package/dist/brain/routing.d.ts.map +1 -0
- package/dist/brain/routing.js +34 -0
- package/dist/brain/routing.js.map +1 -0
- package/dist/brain/status-sink.d.ts +27 -0
- package/dist/brain/status-sink.d.ts.map +1 -0
- package/dist/brain/status-sink.js +20 -0
- package/dist/brain/status-sink.js.map +1 -0
- package/dist/brain/stream-engine.d.ts +37 -0
- package/dist/brain/stream-engine.d.ts.map +1 -0
- package/dist/brain/stream-engine.js +295 -0
- package/dist/brain/stream-engine.js.map +1 -0
- package/dist/brain/timeout.d.ts +12 -0
- package/dist/brain/timeout.d.ts.map +1 -0
- package/dist/brain/timeout.js +34 -0
- package/dist/brain/timeout.js.map +1 -0
- package/dist/brain/tool-call-repair.d.ts +6 -0
- package/dist/brain/tool-call-repair.d.ts.map +1 -0
- package/dist/brain/tool-call-repair.js +58 -0
- package/dist/brain/tool-call-repair.js.map +1 -0
- package/dist/brain/walltime.d.ts +2 -0
- package/dist/brain/walltime.d.ts.map +1 -0
- package/dist/brain/walltime.js +2 -0
- package/dist/brain/walltime.js.map +1 -0
- package/dist/core/arg-summary.d.ts +3 -0
- package/dist/core/arg-summary.d.ts.map +1 -0
- package/dist/core/arg-summary.js +76 -0
- package/dist/core/arg-summary.js.map +1 -0
- package/dist/core/ask-question.d.ts +36 -0
- package/dist/core/ask-question.d.ts.map +1 -0
- package/dist/core/ask-question.js +171 -0
- package/dist/core/ask-question.js.map +1 -0
- package/dist/core/auto-compaction.d.ts +88 -0
- package/dist/core/auto-compaction.d.ts.map +1 -0
- package/dist/core/auto-compaction.js +253 -0
- package/dist/core/auto-compaction.js.map +1 -0
- package/dist/core/auto-mode-prompt-assets.d.ts +3 -0
- package/dist/core/auto-mode-prompt-assets.d.ts.map +1 -0
- package/dist/core/auto-mode-prompt-assets.js +3 -0
- package/dist/core/auto-mode-prompt-assets.js.map +1 -0
- package/dist/core/auto-mode-prompt.d.ts +22 -0
- package/dist/core/auto-mode-prompt.d.ts.map +1 -0
- package/dist/core/auto-mode-prompt.js +84 -0
- package/dist/core/auto-mode-prompt.js.map +1 -0
- package/dist/core/auto-mode.d.ts +35 -0
- package/dist/core/auto-mode.d.ts.map +1 -0
- package/dist/core/auto-mode.js +100 -0
- package/dist/core/auto-mode.js.map +1 -0
- package/dist/core/auto-promote.d.ts +26 -0
- package/dist/core/auto-promote.d.ts.map +1 -0
- package/dist/core/auto-promote.js +93 -0
- package/dist/core/auto-promote.js.map +1 -0
- package/dist/core/background-shell.d.ts +41 -0
- package/dist/core/background-shell.d.ts.map +1 -0
- package/dist/core/background-shell.js +21 -0
- package/dist/core/background-shell.js.map +1 -0
- package/dist/core/cache-break-detector.d.ts +29 -0
- package/dist/core/cache-break-detector.d.ts.map +1 -0
- package/dist/core/cache-break-detector.js +80 -0
- package/dist/core/cache-break-detector.js.map +1 -0
- package/dist/core/canonical-json.d.ts +3 -0
- package/dist/core/canonical-json.d.ts.map +1 -0
- package/dist/core/canonical-json.js +68 -0
- package/dist/core/canonical-json.js.map +1 -0
- package/dist/core/checkpoint-store.d.ts +257 -0
- package/dist/core/checkpoint-store.d.ts.map +1 -0
- package/dist/core/checkpoint-store.js +292 -0
- package/dist/core/checkpoint-store.js.map +1 -0
- package/dist/core/consolidate-scope.d.ts +19 -0
- package/dist/core/consolidate-scope.d.ts.map +1 -0
- package/dist/core/consolidate-scope.js +96 -0
- package/dist/core/consolidate-scope.js.map +1 -0
- package/dist/core/context-edit.d.ts +18 -0
- package/dist/core/context-edit.d.ts.map +1 -0
- package/dist/core/context-edit.js +98 -0
- package/dist/core/context-edit.js.map +1 -0
- package/dist/core/context-guard.d.ts +14 -0
- package/dist/core/context-guard.d.ts.map +1 -0
- package/dist/core/context-guard.js +110 -0
- package/dist/core/context-guard.js.map +1 -0
- package/dist/core/dynamic-recall.d.ts +33 -0
- package/dist/core/dynamic-recall.d.ts.map +1 -0
- package/dist/core/dynamic-recall.js +77 -0
- package/dist/core/dynamic-recall.js.map +1 -0
- package/dist/core/exec-gate.d.ts +30 -0
- package/dist/core/exec-gate.d.ts.map +1 -0
- package/dist/core/exec-gate.js +106 -0
- package/dist/core/exec-gate.js.map +1 -0
- package/dist/core/exec-output-tail.d.ts +15 -0
- package/dist/core/exec-output-tail.d.ts.map +1 -0
- package/dist/core/exec-output-tail.js +39 -0
- package/dist/core/exec-output-tail.js.map +1 -0
- package/dist/core/file-snapshot-store.d.ts +53 -0
- package/dist/core/file-snapshot-store.d.ts.map +1 -0
- package/dist/core/file-snapshot-store.js +244 -0
- package/dist/core/file-snapshot-store.js.map +1 -0
- package/dist/core/git-worktree-env.d.ts +16 -0
- package/dist/core/git-worktree-env.d.ts.map +1 -0
- package/dist/core/git-worktree-env.js +40 -0
- package/dist/core/git-worktree-env.js.map +1 -0
- package/dist/core/hooks.d.ts +128 -0
- package/dist/core/hooks.d.ts.map +1 -0
- package/dist/core/hooks.js +184 -0
- package/dist/core/hooks.js.map +1 -0
- package/dist/core/lsp-diagnostics.d.ts +31 -0
- package/dist/core/lsp-diagnostics.d.ts.map +1 -0
- package/dist/core/lsp-diagnostics.js +79 -0
- package/dist/core/lsp-diagnostics.js.map +1 -0
- package/dist/core/lsp-protocol.d.ts +10 -0
- package/dist/core/lsp-protocol.d.ts.map +1 -0
- package/dist/core/lsp-protocol.js +136 -0
- package/dist/core/lsp-protocol.js.map +1 -0
- package/dist/core/lsp-session.d.ts +27 -0
- package/dist/core/lsp-session.d.ts.map +1 -0
- package/dist/core/lsp-session.js +134 -0
- package/dist/core/lsp-session.js.map +1 -0
- package/dist/core/lsp.d.ts +56 -0
- package/dist/core/lsp.d.ts.map +1 -0
- package/dist/core/lsp.js +130 -0
- package/dist/core/lsp.js.map +1 -0
- package/dist/core/mcp.d.ts +108 -0
- package/dist/core/mcp.d.ts.map +1 -0
- package/dist/core/mcp.js +692 -0
- package/dist/core/mcp.js.map +1 -0
- package/dist/core/media-byte-cap.d.ts +13 -0
- package/dist/core/media-byte-cap.d.ts.map +1 -0
- package/dist/core/media-byte-cap.js +51 -0
- package/dist/core/media-byte-cap.js.map +1 -0
- package/dist/core/memory-engine/data-plane.d.ts +60 -0
- package/dist/core/memory-engine/data-plane.d.ts.map +1 -0
- package/dist/core/memory-engine/data-plane.js +198 -0
- package/dist/core/memory-engine/data-plane.js.map +1 -0
- package/dist/core/memory-engine/dual-root.d.ts +16 -0
- package/dist/core/memory-engine/dual-root.d.ts.map +1 -0
- package/dist/core/memory-engine/dual-root.js +75 -0
- package/dist/core/memory-engine/dual-root.js.map +1 -0
- package/dist/core/memory-engine/engine.d.ts +67 -0
- package/dist/core/memory-engine/engine.d.ts.map +1 -0
- package/dist/core/memory-engine/engine.js +828 -0
- package/dist/core/memory-engine/engine.js.map +1 -0
- package/dist/core/memory-engine/file-backend.d.ts +55 -0
- package/dist/core/memory-engine/file-backend.d.ts.map +1 -0
- package/dist/core/memory-engine/file-backend.js +752 -0
- package/dist/core/memory-engine/file-backend.js.map +1 -0
- package/dist/core/memory-engine/frontmatter.d.ts +13 -0
- package/dist/core/memory-engine/frontmatter.d.ts.map +1 -0
- package/dist/core/memory-engine/frontmatter.js +119 -0
- package/dist/core/memory-engine/frontmatter.js.map +1 -0
- package/dist/core/memory-engine/index.d.ts +13 -0
- package/dist/core/memory-engine/index.d.ts.map +1 -0
- package/dist/core/memory-engine/index.js +12 -0
- package/dist/core/memory-engine/index.js.map +1 -0
- package/dist/core/memory-engine/layout.d.ts +44 -0
- package/dist/core/memory-engine/layout.d.ts.map +1 -0
- package/dist/core/memory-engine/layout.js +459 -0
- package/dist/core/memory-engine/layout.js.map +1 -0
- package/dist/core/memory-engine/memory-backend-contract.d.ts +10 -0
- package/dist/core/memory-engine/memory-backend-contract.d.ts.map +1 -0
- package/dist/core/memory-engine/memory-backend-contract.js +269 -0
- package/dist/core/memory-engine/memory-backend-contract.js.map +1 -0
- package/dist/core/memory-engine/migrate.d.ts +8 -0
- package/dist/core/memory-engine/migrate.d.ts.map +1 -0
- package/dist/core/memory-engine/migrate.js +23 -0
- package/dist/core/memory-engine/migrate.js.map +1 -0
- package/dist/core/memory-engine/scan.d.ts +8 -0
- package/dist/core/memory-engine/scan.d.ts.map +1 -0
- package/dist/core/memory-engine/scan.js +49 -0
- package/dist/core/memory-engine/scan.js.map +1 -0
- package/dist/core/memory-engine/scope-contract.d.ts +44 -0
- package/dist/core/memory-engine/scope-contract.d.ts.map +1 -0
- package/dist/core/memory-engine/scope-contract.js +205 -0
- package/dist/core/memory-engine/scope-contract.js.map +1 -0
- package/dist/core/memory-engine/sync-client.d.ts +57 -0
- package/dist/core/memory-engine/sync-client.d.ts.map +1 -0
- package/dist/core/memory-engine/sync-client.js +257 -0
- package/dist/core/memory-engine/sync-client.js.map +1 -0
- package/dist/core/memory-engine/sync.d.ts +37 -0
- package/dist/core/memory-engine/sync.d.ts.map +1 -0
- package/dist/core/memory-engine/sync.js +68 -0
- package/dist/core/memory-engine/sync.js.map +1 -0
- package/dist/core/memory-engine/types.d.ts +128 -0
- package/dist/core/memory-engine/types.d.ts.map +1 -0
- package/dist/core/memory-engine/types.js +2 -0
- package/dist/core/memory-engine/types.js.map +1 -0
- package/dist/core/memory-recall.d.ts +82 -0
- package/dist/core/memory-recall.d.ts.map +1 -0
- package/dist/core/memory-recall.js +352 -0
- package/dist/core/memory-recall.js.map +1 -0
- package/dist/core/memory-vector.d.ts +4 -0
- package/dist/core/memory-vector.d.ts.map +1 -0
- package/dist/core/memory-vector.js +31 -0
- package/dist/core/memory-vector.js.map +1 -0
- package/dist/core/memory.d.ts +112 -0
- package/dist/core/memory.d.ts.map +1 -0
- package/dist/core/memory.js +512 -0
- package/dist/core/memory.js.map +1 -0
- package/dist/core/message-utils.d.ts +4 -0
- package/dist/core/message-utils.d.ts.map +1 -0
- package/dist/core/message-utils.js +7 -0
- package/dist/core/message-utils.js.map +1 -0
- package/dist/core/oracle-isolation.d.ts +16 -0
- package/dist/core/oracle-isolation.d.ts.map +1 -0
- package/dist/core/oracle-isolation.js +50 -0
- package/dist/core/oracle-isolation.js.map +1 -0
- package/dist/core/permission-rules.d.ts +35 -0
- package/dist/core/permission-rules.d.ts.map +1 -0
- package/dist/core/permission-rules.js +270 -0
- package/dist/core/permission-rules.js.map +1 -0
- package/dist/core/present-plan-tool.d.ts +8 -0
- package/dist/core/present-plan-tool.d.ts.map +1 -0
- package/dist/core/present-plan-tool.js +82 -0
- package/dist/core/present-plan-tool.js.map +1 -0
- package/dist/core/pricing.d.ts +22 -0
- package/dist/core/pricing.d.ts.map +1 -0
- package/dist/core/pricing.js +23 -0
- package/dist/core/pricing.js.map +1 -0
- package/dist/core/property-harness.d.ts +40 -0
- package/dist/core/property-harness.d.ts.map +1 -0
- package/dist/core/property-harness.js +121 -0
- package/dist/core/property-harness.js.map +1 -0
- package/dist/core/push-queue.d.ts +9 -0
- package/dist/core/push-queue.d.ts.map +1 -0
- package/dist/core/push-queue.js +41 -0
- package/dist/core/push-queue.js.map +1 -0
- package/dist/core/remote-env.d.ts +105 -0
- package/dist/core/remote-env.d.ts.map +1 -0
- package/dist/core/remote-env.js +27 -0
- package/dist/core/remote-env.js.map +1 -0
- package/dist/core/roles.d.ts +35 -0
- package/dist/core/roles.d.ts.map +1 -0
- package/dist/core/roles.js +182 -0
- package/dist/core/roles.js.map +1 -0
- package/dist/core/runner/active-skill-scope.d.ts +26 -0
- package/dist/core/runner/active-skill-scope.d.ts.map +1 -0
- package/dist/core/runner/active-skill-scope.js +116 -0
- package/dist/core/runner/active-skill-scope.js.map +1 -0
- package/dist/core/runner/assemble-result.d.ts +89 -0
- package/dist/core/runner/assemble-result.d.ts.map +1 -0
- package/dist/core/runner/assemble-result.js +141 -0
- package/dist/core/runner/assemble-result.js.map +1 -0
- package/dist/core/runner/call-cap.d.ts +38 -0
- package/dist/core/runner/call-cap.d.ts.map +1 -0
- package/dist/core/runner/call-cap.js +76 -0
- package/dist/core/runner/call-cap.js.map +1 -0
- package/dist/core/runner/grounding-signal.d.ts +2 -0
- package/dist/core/runner/grounding-signal.d.ts.map +1 -0
- package/dist/core/runner/grounding-signal.js +32 -0
- package/dist/core/runner/grounding-signal.js.map +1 -0
- package/dist/core/runner/image.d.ts +6 -0
- package/dist/core/runner/image.d.ts.map +1 -0
- package/dist/core/runner/image.js +116 -0
- package/dist/core/runner/image.js.map +1 -0
- package/dist/core/runner/memory-consolidation.d.ts +52 -0
- package/dist/core/runner/memory-consolidation.d.ts.map +1 -0
- package/dist/core/runner/memory-consolidation.js +253 -0
- package/dist/core/runner/memory-consolidation.js.map +1 -0
- package/dist/core/runner/prepare-task.d.ts +219 -0
- package/dist/core/runner/prepare-task.d.ts.map +1 -0
- package/dist/core/runner/prepare-task.js +2087 -0
- package/dist/core/runner/prepare-task.js.map +1 -0
- package/dist/core/runner/prompt-suggestions.d.ts +26 -0
- package/dist/core/runner/prompt-suggestions.d.ts.map +1 -0
- package/dist/core/runner/prompt-suggestions.js +104 -0
- package/dist/core/runner/prompt-suggestions.js.map +1 -0
- package/dist/core/runner/runtask.d.ts +53 -0
- package/dist/core/runner/runtask.d.ts.map +1 -0
- package/dist/core/runner/runtask.js +2703 -0
- package/dist/core/runner/runtask.js.map +1 -0
- package/dist/core/runner/session-rule-policy.d.ts +11 -0
- package/dist/core/runner/session-rule-policy.d.ts.map +1 -0
- package/dist/core/runner/session-rule-policy.js +76 -0
- package/dist/core/runner/session-rule-policy.js.map +1 -0
- package/dist/core/runner/strict-output-schema.d.ts +21 -0
- package/dist/core/runner/strict-output-schema.d.ts.map +1 -0
- package/dist/core/runner/strict-output-schema.js +304 -0
- package/dist/core/runner/strict-output-schema.js.map +1 -0
- package/dist/core/runner/synthetic-tools.d.ts +30 -0
- package/dist/core/runner/synthetic-tools.d.ts.map +1 -0
- package/dist/core/runner/synthetic-tools.js +124 -0
- package/dist/core/runner/synthetic-tools.js.map +1 -0
- package/dist/core/runner/tool-disclosure.d.ts +46 -0
- package/dist/core/runner/tool-disclosure.d.ts.map +1 -0
- package/dist/core/runner/tool-disclosure.js +220 -0
- package/dist/core/runner/tool-disclosure.js.map +1 -0
- package/dist/core/runner/turn-attachments.d.ts +110 -0
- package/dist/core/runner/turn-attachments.d.ts.map +1 -0
- package/dist/core/runner/turn-attachments.js +334 -0
- package/dist/core/runner/turn-attachments.js.map +1 -0
- package/dist/core/runner/usage-accounting.d.ts +19 -0
- package/dist/core/runner/usage-accounting.d.ts.map +1 -0
- package/dist/core/runner/usage-accounting.js +30 -0
- package/dist/core/runner/usage-accounting.js.map +1 -0
- package/dist/core/runtime.d.ts +4 -0
- package/dist/core/runtime.d.ts.map +1 -0
- package/dist/core/runtime.js +12 -0
- package/dist/core/runtime.js.map +1 -0
- package/dist/core/safety-axis-vocab.d.ts +10 -0
- package/dist/core/safety-axis-vocab.d.ts.map +1 -0
- package/dist/core/safety-axis-vocab.js +12 -0
- package/dist/core/safety-axis-vocab.js.map +1 -0
- package/dist/core/safety-merge-corpus.d.ts +11 -0
- package/dist/core/safety-merge-corpus.d.ts.map +1 -0
- package/dist/core/safety-merge-corpus.js +195 -0
- package/dist/core/safety-merge-corpus.js.map +1 -0
- package/dist/core/scheduler.d.ts +55 -0
- package/dist/core/scheduler.d.ts.map +1 -0
- package/dist/core/scheduler.js +32 -0
- package/dist/core/scheduler.js.map +1 -0
- package/dist/core/secret-env.d.ts +3 -0
- package/dist/core/secret-env.d.ts.map +1 -0
- package/dist/core/secret-env.js +13 -0
- package/dist/core/secret-env.js.map +1 -0
- package/dist/core/select-model.d.ts +11 -0
- package/dist/core/select-model.d.ts.map +1 -0
- package/dist/core/select-model.js +44 -0
- package/dist/core/select-model.js.map +1 -0
- package/dist/core/sensitive-path-policy.d.ts +9 -0
- package/dist/core/sensitive-path-policy.d.ts.map +1 -0
- package/dist/core/sensitive-path-policy.js +61 -0
- package/dist/core/sensitive-path-policy.js.map +1 -0
- package/dist/core/session-policy-store.d.ts +38 -0
- package/dist/core/session-policy-store.d.ts.map +1 -0
- package/dist/core/session-policy-store.js +112 -0
- package/dist/core/session-policy-store.js.map +1 -0
- package/dist/core/session-reconcile.d.ts +15 -0
- package/dist/core/session-reconcile.d.ts.map +1 -0
- package/dist/core/session-reconcile.js +48 -0
- package/dist/core/session-reconcile.js.map +1 -0
- package/dist/core/session-store.d.ts +36 -0
- package/dist/core/session-store.d.ts.map +1 -0
- package/dist/core/session-store.js +192 -0
- package/dist/core/session-store.js.map +1 -0
- package/dist/core/session.d.ts +44 -0
- package/dist/core/session.d.ts.map +1 -0
- package/dist/core/session.js +27 -0
- package/dist/core/session.js.map +1 -0
- package/dist/core/spec-contract.d.ts +58 -0
- package/dist/core/spec-contract.d.ts.map +1 -0
- package/dist/core/spec-contract.js +256 -0
- package/dist/core/spec-contract.js.map +1 -0
- package/dist/core/strategy-store.d.ts +25 -0
- package/dist/core/strategy-store.d.ts.map +1 -0
- package/dist/core/strategy-store.js +81 -0
- package/dist/core/strategy-store.js.map +1 -0
- package/dist/core/stub-env.d.ts +28 -0
- package/dist/core/stub-env.d.ts.map +1 -0
- package/dist/core/stub-env.js +61 -0
- package/dist/core/stub-env.js.map +1 -0
- package/dist/core/task-notification.d.ts +56 -0
- package/dist/core/task-notification.d.ts.map +1 -0
- package/dist/core/task-notification.js +200 -0
- package/dist/core/task-notification.js.map +1 -0
- package/dist/core/task-outcome.d.ts +27 -0
- package/dist/core/task-outcome.d.ts.map +1 -0
- package/dist/core/task-outcome.js +12 -0
- package/dist/core/task-outcome.js.map +1 -0
- package/dist/core/task-registry.d.ts +188 -0
- package/dist/core/task-registry.d.ts.map +1 -0
- package/dist/core/task-registry.js +1443 -0
- package/dist/core/task-registry.js.map +1 -0
- package/dist/core/tighten-task-spec.d.ts +6 -0
- package/dist/core/tighten-task-spec.d.ts.map +1 -0
- package/dist/core/tighten-task-spec.js +50 -0
- package/dist/core/tighten-task-spec.js.map +1 -0
- package/dist/core/tool-detach.d.ts +10 -0
- package/dist/core/tool-detach.d.ts.map +1 -0
- package/dist/core/tool-detach.js +41 -0
- package/dist/core/tool-detach.js.map +1 -0
- package/dist/core/tool-errors.d.ts +8 -0
- package/dist/core/tool-errors.d.ts.map +1 -0
- package/dist/core/tool-errors.js +132 -0
- package/dist/core/tool-errors.js.map +1 -0
- package/dist/core/tool-name-aliases.d.ts +3 -0
- package/dist/core/tool-name-aliases.d.ts.map +1 -0
- package/dist/core/tool-name-aliases.js +60 -0
- package/dist/core/tool-name-aliases.js.map +1 -0
- package/dist/core/tool-policy.d.ts +75 -0
- package/dist/core/tool-policy.d.ts.map +1 -0
- package/dist/core/tool-policy.js +363 -0
- package/dist/core/tool-policy.js.map +1 -0
- package/dist/core/tool-result-budget.d.ts +17 -0
- package/dist/core/tool-result-budget.d.ts.map +1 -0
- package/dist/core/tool-result-budget.js +103 -0
- package/dist/core/tool-result-budget.js.map +1 -0
- package/dist/core/tool-result-store.d.ts +55 -0
- package/dist/core/tool-result-store.d.ts.map +1 -0
- package/dist/core/tool-result-store.js +154 -0
- package/dist/core/tool-result-store.js.map +1 -0
- package/dist/core/tools.d.ts +6 -0
- package/dist/core/tools.d.ts.map +1 -0
- package/dist/core/tools.js +61 -0
- package/dist/core/tools.js.map +1 -0
- package/dist/core/trace.d.ts +208 -0
- package/dist/core/trace.d.ts.map +1 -0
- package/dist/core/trace.js +10 -0
- package/dist/core/trace.js.map +1 -0
- package/dist/core/types.d.ts +684 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/untrusted-egress.d.ts +7 -0
- package/dist/core/untrusted-egress.d.ts.map +1 -0
- package/dist/core/untrusted-egress.js +23 -0
- package/dist/core/untrusted-egress.js.map +1 -0
- package/dist/core/untrusted-text.d.ts +5 -0
- package/dist/core/untrusted-text.d.ts.map +1 -0
- package/dist/core/untrusted-text.js +36 -0
- package/dist/core/untrusted-text.js.map +1 -0
- package/dist/core/warm-resume.d.ts +6 -0
- package/dist/core/warm-resume.d.ts.map +1 -0
- package/dist/core/warm-resume.js +11 -0
- package/dist/core/warm-resume.js.map +1 -0
- package/dist/core/with-retry.d.ts +19 -0
- package/dist/core/with-retry.d.ts.map +1 -0
- package/dist/core/with-retry.js +36 -0
- package/dist/core/with-retry.js.map +1 -0
- package/dist/core/workflow-journal-store.d.ts +16 -0
- package/dist/core/workflow-journal-store.d.ts.map +1 -0
- package/dist/core/workflow-journal-store.js +34 -0
- package/dist/core/workflow-journal-store.js.map +1 -0
- package/dist/core/workflow-run-store-contract.d.ts +6 -0
- package/dist/core/workflow-run-store-contract.d.ts.map +1 -0
- package/dist/core/workflow-run-store-contract.js +144 -0
- package/dist/core/workflow-run-store-contract.js.map +1 -0
- package/dist/core/workflow-run-store.d.ts +54 -0
- package/dist/core/workflow-run-store.d.ts.map +1 -0
- package/dist/core/workflow-run-store.js +86 -0
- package/dist/core/workflow-run-store.js.map +1 -0
- package/dist/engine/compaction/compaction.d.ts +63 -0
- package/dist/engine/compaction/compaction.d.ts.map +1 -0
- package/dist/engine/compaction/compaction.js +770 -0
- package/dist/engine/compaction/compaction.js.map +1 -0
- package/dist/engine/compaction/utils.d.ts +19 -0
- package/dist/engine/compaction/utils.d.ts.map +1 -0
- package/dist/engine/compaction/utils.js +167 -0
- package/dist/engine/compaction/utils.js.map +1 -0
- package/dist/engine/execution-env/kill-tree.d.ts +10 -0
- package/dist/engine/execution-env/kill-tree.d.ts.map +1 -0
- package/dist/engine/execution-env/kill-tree.js +143 -0
- package/dist/engine/execution-env/kill-tree.js.map +1 -0
- package/dist/engine/execution-env/node-execution-env.d.ts +98 -0
- package/dist/engine/execution-env/node-execution-env.d.ts.map +1 -0
- package/dist/engine/execution-env/node-execution-env.js +945 -0
- package/dist/engine/execution-env/node-execution-env.js.map +1 -0
- package/dist/engine/harness/agent-harness.d.ts +112 -0
- package/dist/engine/harness/agent-harness.d.ts.map +1 -0
- package/dist/engine/harness/agent-harness.js +932 -0
- package/dist/engine/harness/agent-harness.js.map +1 -0
- package/dist/engine/harness/messages.d.ts +12 -0
- package/dist/engine/harness/messages.d.ts.map +1 -0
- package/dist/engine/harness/messages.js +102 -0
- package/dist/engine/harness/messages.js.map +1 -0
- package/dist/engine/harness/types.d.ts +484 -0
- package/dist/engine/harness/types.d.ts.map +1 -0
- package/dist/engine/harness/types.js +91 -0
- package/dist/engine/harness/types.js.map +1 -0
- package/dist/engine/llm/diagnostics.d.ts +19 -0
- package/dist/engine/llm/diagnostics.d.ts.map +1 -0
- package/dist/engine/llm/diagnostics.js +28 -0
- package/dist/engine/llm/diagnostics.js.map +1 -0
- package/dist/engine/llm/event-stream.d.ts +22 -0
- package/dist/engine/llm/event-stream.d.ts.map +1 -0
- package/dist/engine/llm/event-stream.js +91 -0
- package/dist/engine/llm/event-stream.js.map +1 -0
- package/dist/engine/llm/index.d.ts +5 -0
- package/dist/engine/llm/index.d.ts.map +1 -0
- package/dist/engine/llm/index.js +5 -0
- package/dist/engine/llm/index.js.map +1 -0
- package/dist/engine/llm/types.d.ts +359 -0
- package/dist/engine/llm/types.d.ts.map +1 -0
- package/dist/engine/llm/types.js +2 -0
- package/dist/engine/llm/types.js.map +1 -0
- package/dist/engine/llm/validation.d.ts +5 -0
- package/dist/engine/llm/validation.d.ts.map +1 -0
- package/dist/engine/llm/validation.js +293 -0
- package/dist/engine/llm/validation.js.map +1 -0
- package/dist/engine/loop/agent-loop.d.ts +19 -0
- package/dist/engine/loop/agent-loop.d.ts.map +1 -0
- package/dist/engine/loop/agent-loop.js +1087 -0
- package/dist/engine/loop/agent-loop.js.map +1 -0
- package/dist/engine/loop/runtime-deps.d.ts +10 -0
- package/dist/engine/loop/runtime-deps.d.ts.map +1 -0
- package/dist/engine/loop/runtime-deps.js +19 -0
- package/dist/engine/loop/runtime-deps.js.map +1 -0
- package/dist/engine/loop/types.d.ts +209 -0
- package/dist/engine/loop/types.d.ts.map +1 -0
- package/dist/engine/loop/types.js +2 -0
- package/dist/engine/loop/types.js.map +1 -0
- package/dist/engine/lsp/frame-decoder.d.ts +3 -0
- package/dist/engine/lsp/frame-decoder.d.ts.map +1 -0
- package/dist/engine/lsp/frame-decoder.js +36 -0
- package/dist/engine/lsp/frame-decoder.js.map +1 -0
- package/dist/engine/lsp/node-lsp-manager.d.ts +49 -0
- package/dist/engine/lsp/node-lsp-manager.d.ts.map +1 -0
- package/dist/engine/lsp/node-lsp-manager.js +267 -0
- package/dist/engine/lsp/node-lsp-manager.js.map +1 -0
- package/dist/engine/lsp/stdio-lsp-transport.d.ts +32 -0
- package/dist/engine/lsp/stdio-lsp-transport.d.ts.map +1 -0
- package/dist/engine/lsp/stdio-lsp-transport.js +154 -0
- package/dist/engine/lsp/stdio-lsp-transport.js.map +1 -0
- package/dist/engine/session/import-validate.d.ts +13 -0
- package/dist/engine/session/import-validate.d.ts.map +1 -0
- package/dist/engine/session/import-validate.js +56 -0
- package/dist/engine/session/import-validate.js.map +1 -0
- package/dist/engine/session/memory-repo.d.ts +20 -0
- package/dist/engine/session/memory-repo.d.ts.map +1 -0
- package/dist/engine/session/memory-repo.js +54 -0
- package/dist/engine/session/memory-repo.js.map +1 -0
- package/dist/engine/session/memory-storage.d.ts +12 -0
- package/dist/engine/session/memory-storage.d.ts.map +1 -0
- package/dist/engine/session/memory-storage.js +16 -0
- package/dist/engine/session/memory-storage.js.map +1 -0
- package/dist/engine/session/repo-utils.d.ts +11 -0
- package/dist/engine/session/repo-utils.d.ts.map +1 -0
- package/dist/engine/session/repo-utils.js +40 -0
- package/dist/engine/session/repo-utils.js.map +1 -0
- package/dist/engine/session/session.d.ts +27 -0
- package/dist/engine/session/session.d.ts.map +1 -0
- package/dist/engine/session/session.js +197 -0
- package/dist/engine/session/session.js.map +1 -0
- package/dist/engine/session/storage-base.d.ts +30 -0
- package/dist/engine/session/storage-base.d.ts.map +1 -0
- package/dist/engine/session/storage-base.js +138 -0
- package/dist/engine/session/storage-base.js.map +1 -0
- package/dist/engine/session/timestamps.d.ts +3 -0
- package/dist/engine/session/timestamps.d.ts.map +1 -0
- package/dist/engine/session/timestamps.js +15 -0
- package/dist/engine/session/timestamps.js.map +1 -0
- package/dist/engine/session/uuid.d.ts +2 -0
- package/dist/engine/session/uuid.d.ts.map +1 -0
- package/dist/engine/session/uuid.js +50 -0
- package/dist/engine/session/uuid.js.map +1 -0
- package/dist/index.d.ts +156 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +141 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/harness.d.ts +19 -0
- package/dist/internal/harness.d.ts.map +1 -0
- package/dist/internal/harness.js +13 -0
- package/dist/internal/harness.js.map +1 -0
- package/dist/internal/llm.d.ts +3 -0
- package/dist/internal/llm.d.ts.map +1 -0
- package/dist/internal/llm.js +2 -0
- package/dist/internal/llm.js.map +1 -0
- package/dist/orchestration/builtin-workflows.d.ts +11 -0
- package/dist/orchestration/builtin-workflows.d.ts.map +1 -0
- package/dist/orchestration/builtin-workflows.js +134 -0
- package/dist/orchestration/builtin-workflows.js.map +1 -0
- package/dist/orchestration/dev-vm-script-runner.d.ts +5 -0
- package/dist/orchestration/dev-vm-script-runner.d.ts.map +1 -0
- package/dist/orchestration/dev-vm-script-runner.js +68 -0
- package/dist/orchestration/dev-vm-script-runner.js.map +1 -0
- package/dist/orchestration/goal.d.ts +40 -0
- package/dist/orchestration/goal.d.ts.map +1 -0
- package/dist/orchestration/goal.js +190 -0
- package/dist/orchestration/goal.js.map +1 -0
- package/dist/orchestration/run-spec.d.ts +21 -0
- package/dist/orchestration/run-spec.d.ts.map +1 -0
- package/dist/orchestration/run-spec.js +201 -0
- package/dist/orchestration/run-spec.js.map +1 -0
- package/dist/orchestration/run-workflow-tool.d.ts +80 -0
- package/dist/orchestration/run-workflow-tool.d.ts.map +1 -0
- package/dist/orchestration/run-workflow-tool.js +431 -0
- package/dist/orchestration/run-workflow-tool.js.map +1 -0
- package/dist/orchestration/workflow-governance.d.ts +30 -0
- package/dist/orchestration/workflow-governance.d.ts.map +1 -0
- package/dist/orchestration/workflow-governance.js +125 -0
- package/dist/orchestration/workflow-governance.js.map +1 -0
- package/dist/orchestration/workflow-meta.d.ts +13 -0
- package/dist/orchestration/workflow-meta.d.ts.map +1 -0
- package/dist/orchestration/workflow-meta.js +367 -0
- package/dist/orchestration/workflow-meta.js.map +1 -0
- package/dist/orchestration/workflow-observe.d.ts +18 -0
- package/dist/orchestration/workflow-observe.d.ts.map +1 -0
- package/dist/orchestration/workflow-observe.js +73 -0
- package/dist/orchestration/workflow-observe.js.map +1 -0
- package/dist/orchestration/workflow-primitives.d.ts +12 -0
- package/dist/orchestration/workflow-primitives.d.ts.map +1 -0
- package/dist/orchestration/workflow-primitives.js +56 -0
- package/dist/orchestration/workflow-primitives.js.map +1 -0
- package/dist/orchestration/workflow-sandbox-conformance.d.ts +5 -0
- package/dist/orchestration/workflow-sandbox-conformance.d.ts.map +1 -0
- package/dist/orchestration/workflow-sandbox-conformance.js +220 -0
- package/dist/orchestration/workflow-sandbox-conformance.js.map +1 -0
- package/dist/orchestration/workflow-script-runner.d.ts +39 -0
- package/dist/orchestration/workflow-script-runner.d.ts.map +1 -0
- package/dist/orchestration/workflow-script-runner.js +23 -0
- package/dist/orchestration/workflow-script-runner.js.map +1 -0
- package/dist/orchestration/workflow-script-store.d.ts +20 -0
- package/dist/orchestration/workflow-script-store.d.ts.map +1 -0
- package/dist/orchestration/workflow-script-store.js +73 -0
- package/dist/orchestration/workflow-script-store.js.map +1 -0
- package/dist/orchestration/workflow-size-guideline.d.ts +10 -0
- package/dist/orchestration/workflow-size-guideline.d.ts.map +1 -0
- package/dist/orchestration/workflow-size-guideline.js +38 -0
- package/dist/orchestration/workflow-size-guideline.js.map +1 -0
- package/dist/orchestration/workflow.d.ts +281 -0
- package/dist/orchestration/workflow.d.ts.map +1 -0
- package/dist/orchestration/workflow.js +1095 -0
- package/dist/orchestration/workflow.js.map +1 -0
- package/dist/prompts/default.d.ts +104 -0
- package/dist/prompts/default.d.ts.map +1 -0
- package/dist/prompts/default.js +397 -0
- package/dist/prompts/default.js.map +1 -0
- package/dist/prompts/supervisor.d.ts +5 -0
- package/dist/prompts/supervisor.d.ts.map +1 -0
- package/dist/prompts/supervisor.js +107 -0
- package/dist/prompts/supervisor.js.map +1 -0
- package/dist/scenarios/env.d.ts +13 -0
- package/dist/scenarios/env.d.ts.map +1 -0
- package/dist/scenarios/env.js +40 -0
- package/dist/scenarios/env.js.map +1 -0
- package/dist/scenarios/full-body.d.ts +13 -0
- package/dist/scenarios/full-body.d.ts.map +1 -0
- package/dist/scenarios/full-body.js +20 -0
- package/dist/scenarios/full-body.js.map +1 -0
- package/dist/scenarios/scenario-registry.d.ts +47 -0
- package/dist/scenarios/scenario-registry.d.ts.map +1 -0
- package/dist/scenarios/scenario-registry.js +139 -0
- package/dist/scenarios/scenario-registry.js.map +1 -0
- package/dist/scenarios/teacher-quickstart.d.ts +14 -0
- package/dist/scenarios/teacher-quickstart.d.ts.map +1 -0
- package/dist/scenarios/teacher-quickstart.js +13 -0
- package/dist/scenarios/teacher-quickstart.js.map +1 -0
- package/dist/server/http.d.ts +19 -0
- package/dist/server/http.d.ts.map +1 -0
- package/dist/server/http.js +115 -0
- package/dist/server/http.js.map +1 -0
- package/dist/stores/file/checkpoint-store.d.ts +40 -0
- package/dist/stores/file/checkpoint-store.d.ts.map +1 -0
- package/dist/stores/file/checkpoint-store.js +198 -0
- package/dist/stores/file/checkpoint-store.js.map +1 -0
- package/dist/stores/file/file-snapshot-store.d.ts +25 -0
- package/dist/stores/file/file-snapshot-store.d.ts.map +1 -0
- package/dist/stores/file/file-snapshot-store.js +313 -0
- package/dist/stores/file/file-snapshot-store.js.map +1 -0
- package/dist/stores/file/fs-atomic.d.ts +24 -0
- package/dist/stores/file/fs-atomic.d.ts.map +1 -0
- package/dist/stores/file/fs-atomic.js +276 -0
- package/dist/stores/file/fs-atomic.js.map +1 -0
- package/dist/stores/file/index.d.ts +45 -0
- package/dist/stores/file/index.d.ts.map +1 -0
- package/dist/stores/file/index.js +77 -0
- package/dist/stores/file/index.js.map +1 -0
- package/dist/stores/file/memory-store.d.ts +35 -0
- package/dist/stores/file/memory-store.d.ts.map +1 -0
- package/dist/stores/file/memory-store.js +300 -0
- package/dist/stores/file/memory-store.js.map +1 -0
- package/dist/stores/file/session-policy-store.d.ts +11 -0
- package/dist/stores/file/session-policy-store.d.ts.map +1 -0
- package/dist/stores/file/session-policy-store.js +96 -0
- package/dist/stores/file/session-policy-store.js.map +1 -0
- package/dist/stores/file/session-store.d.ts +24 -0
- package/dist/stores/file/session-store.d.ts.map +1 -0
- package/dist/stores/file/session-store.js +134 -0
- package/dist/stores/file/session-store.js.map +1 -0
- package/dist/stores/file/tool-result-store.d.ts +12 -0
- package/dist/stores/file/tool-result-store.d.ts.map +1 -0
- package/dist/stores/file/tool-result-store.js +44 -0
- package/dist/stores/file/tool-result-store.js.map +1 -0
- package/dist/stores/file/workflow-journal-store.d.ts +17 -0
- package/dist/stores/file/workflow-journal-store.d.ts.map +1 -0
- package/dist/stores/file/workflow-journal-store.js +104 -0
- package/dist/stores/file/workflow-journal-store.js.map +1 -0
- package/dist/stores/file/workflow-run-store.d.ts +41 -0
- package/dist/stores/file/workflow-run-store.d.ts.map +1 -0
- package/dist/stores/file/workflow-run-store.js +147 -0
- package/dist/stores/file/workflow-run-store.js.map +1 -0
- package/dist/stores/pg.d.ts +96 -0
- package/dist/stores/pg.d.ts.map +1 -0
- package/dist/stores/pg.js +451 -0
- package/dist/stores/pg.js.map +1 -0
- package/dist/tools/fs/encoding.d.ts +16 -0
- package/dist/tools/fs/encoding.d.ts.map +1 -0
- package/dist/tools/fs/encoding.js +44 -0
- package/dist/tools/fs/encoding.js.map +1 -0
- package/dist/tools/fs/gh-rate-limit.d.ts +3 -0
- package/dist/tools/fs/gh-rate-limit.d.ts.map +1 -0
- package/dist/tools/fs/gh-rate-limit.js +16 -0
- package/dist/tools/fs/gh-rate-limit.js.map +1 -0
- package/dist/tools/fs/index.d.ts +74 -0
- package/dist/tools/fs/index.d.ts.map +1 -0
- package/dist/tools/fs/index.js +1576 -0
- package/dist/tools/fs/index.js.map +1 -0
- package/dist/tools/fs/pdf.d.ts +47 -0
- package/dist/tools/fs/pdf.d.ts.map +1 -0
- package/dist/tools/fs/pdf.js +124 -0
- package/dist/tools/fs/pdf.js.map +1 -0
- package/dist/tools/fs/repo-map.d.ts +5 -0
- package/dist/tools/fs/repo-map.d.ts.map +1 -0
- package/dist/tools/fs/repo-map.js +209 -0
- package/dist/tools/fs/repo-map.js.map +1 -0
- package/dist/tools/fs/safety.d.ts +52 -0
- package/dist/tools/fs/safety.d.ts.map +1 -0
- package/dist/tools/fs/safety.js +314 -0
- package/dist/tools/fs/safety.js.map +1 -0
- package/dist/tools/fs/search.d.ts +57 -0
- package/dist/tools/fs/search.d.ts.map +1 -0
- package/dist/tools/fs/search.js +574 -0
- package/dist/tools/fs/search.js.map +1 -0
- package/dist/tools/gitea-issue.d.ts +14 -0
- package/dist/tools/gitea-issue.d.ts.map +1 -0
- package/dist/tools/gitea-issue.js +74 -0
- package/dist/tools/gitea-issue.js.map +1 -0
- package/dist/tools/monitor.d.ts +20 -0
- package/dist/tools/monitor.d.ts.map +1 -0
- package/dist/tools/monitor.js +90 -0
- package/dist/tools/monitor.js.map +1 -0
- package/dist/tools/scheduler-tools.d.ts +15 -0
- package/dist/tools/scheduler-tools.d.ts.map +1 -0
- package/dist/tools/scheduler-tools.js +338 -0
- package/dist/tools/scheduler-tools.js.map +1 -0
- package/dist/tools/sql-adapters.d.ts +14 -0
- package/dist/tools/sql-adapters.d.ts.map +1 -0
- package/dist/tools/sql-adapters.js +13 -0
- package/dist/tools/sql-adapters.js.map +1 -0
- package/dist/tools/sql.d.ts +11 -0
- package/dist/tools/sql.d.ts.map +1 -0
- package/dist/tools/sql.js +104 -0
- package/dist/tools/sql.js.map +1 -0
- package/dist/tools/task-list.d.ts +14 -0
- package/dist/tools/task-list.d.ts.map +1 -0
- package/dist/tools/task-list.js +318 -0
- package/dist/tools/task-list.js.map +1 -0
- package/dist/tools/todo.d.ts +3 -0
- package/dist/tools/todo.d.ts.map +1 -0
- package/dist/tools/todo.js +97 -0
- package/dist/tools/todo.js.map +1 -0
- package/dist/tools/web.d.ts +26 -0
- package/dist/tools/web.d.ts.map +1 -0
- package/dist/tools/web.js +498 -0
- package/dist/tools/web.js.map +1 -0
- package/dist/tools/worktree.d.ts +18 -0
- package/dist/tools/worktree.d.ts.map +1 -0
- package/dist/tools/worktree.js +288 -0
- package/dist/tools/worktree.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,2703 @@
|
|
|
1
|
+
import { AgentHarness, DEFAULT_COMPACTION_SETTINGS, uuidv7 } from "../../internal/harness.js";
|
|
2
|
+
import { CheckpointError, BINDING_CHECKPOINT_VERSION, checkpointVersionOf, MAX_SUPPORTED_CHECKPOINT_VERSION, remainingBudgetMicroUsd, winnerFromOutcome, } from "../checkpoint-store.js";
|
|
3
|
+
import { estimateCycleMs, recordCallSample, recordToolSample, writeoutCushionMs } from "./call-cap.js";
|
|
4
|
+
import { DEFAULT_COMPACTION_INSTRUCTIONS, createRapidRefillState, maybeCompact, nextTrimForceBackoff, recordCompactionAndCheckRapidRefill } from "../auto-compaction.js";
|
|
5
|
+
import { ASK_USER_QUESTION_TOOL_NAME } from "../ask-question.js";
|
|
6
|
+
import { computeCostMicroUsd, modelCostToPricing } from "../pricing.js";
|
|
7
|
+
import { emitTrace } from "../trace.js";
|
|
8
|
+
import { emitTaskOutcome } from "../task-outcome.js";
|
|
9
|
+
import { DEGENERATE_MESSAGE } from "../../brain/repetition.js";
|
|
10
|
+
import { WALLTIME_CUTOFF_MESSAGE } from "../../brain/walltime.js";
|
|
11
|
+
import { primaryActivityArg } from "../arg-summary.js";
|
|
12
|
+
import { resolveReasoning } from "../../brain/reasoning.js";
|
|
13
|
+
import { readDegradation } from "../../brain/degrading.js";
|
|
14
|
+
import { runWithBrainTelemetry, runWithStatusSink } from "../../brain/status-sink.js";
|
|
15
|
+
import { expandTiers, resolveModel, resolveTaskModel } from "../roles.js";
|
|
16
|
+
import { generatePromptSuggestions } from "./prompt-suggestions.js";
|
|
17
|
+
import { runMemoryConsolidation } from "./memory-consolidation.js";
|
|
18
|
+
import { advanceCursorAfterInline } from "../consolidate-scope.js";
|
|
19
|
+
import { PushQueue } from "../push-queue.js";
|
|
20
|
+
import { TtlSessionStore } from "../session-store.js";
|
|
21
|
+
import { toImageContent } from "./image.js";
|
|
22
|
+
import { OUTPUT_TOOL_NAME, resolveOutputRetries } from "./synthetic-tools.js";
|
|
23
|
+
import { cacheFamilyOf, usageCostMicroUsd } from "./usage-accounting.js";
|
|
24
|
+
import { assembleResult, errorCodeOf } from "./assemble-result.js";
|
|
25
|
+
import { ATTACHMENT_BYTE_CAP, CHANGED_FILES_MAX, advanceCadenceClock, agentListingDeltaHeader, agentListingInitialHeader, collectDueAttachments, commitAgentListing, createAttachmentState, rebaseCadenceWindows, reduceToolEnd, renderAgentListingDelta, renderMcpInstructionsDelta, renderOrphanedBackgroundTasks, renderToolsDelta, stampWriteAnchor } from "./turn-attachments.js";
|
|
26
|
+
import { prepareTask } from "./prepare-task.js";
|
|
27
|
+
import { hasVerifiableStructureSignal } from "./grounding-signal.js";
|
|
28
|
+
import { hasDestroy, isIsolated } from "../remote-env.js";
|
|
29
|
+
import { hasBackgroundShell } from "../background-shell.js";
|
|
30
|
+
import { formatHookFeedback } from "../hooks.js";
|
|
31
|
+
import { delimitUntrusted, inlineUntrusted, sanitizeUntrustedText } from "../untrusted-text.js";
|
|
32
|
+
import { reconcileInterruptedSession } from "../session-reconcile.js";
|
|
33
|
+
import { RunnerSharedToolResultStore } from "../tool-result-store.js";
|
|
34
|
+
import { formatDiagnosticsBlock } from "../lsp-diagnostics.js";
|
|
35
|
+
import { canonicalToolName } from "../tool-name-aliases.js";
|
|
36
|
+
import { defaultTaskRegistry } from "../task-registry.js";
|
|
37
|
+
import { discloseDroppedPending, PendingSessionNotifications, renderTaskNotificationXml, SystemInjectionQueue, taskNotificationDedupKey } from "../task-notification.js";
|
|
38
|
+
import { ToolDetachHub } from "../tool-detach.js";
|
|
39
|
+
import { createBrainMemorySelector } from "../memory-recall.js";
|
|
40
|
+
import { buildRecallQuery, runDynamicRecall } from "../dynamic-recall.js";
|
|
41
|
+
import { guardedMemoryStore } from "../memory.js";
|
|
42
|
+
import { releaseSession } from "../../agents/session-util.js";
|
|
43
|
+
export const DEFAULT_MAX_TURNS = 500;
|
|
44
|
+
const MAX_CONSECUTIVE_COMPACTION_FAILURES = 3;
|
|
45
|
+
const STOP_HOOK_BLOCK_CAP = 8;
|
|
46
|
+
const COMPACTION_REGROWTH_FACTOR = 1.5;
|
|
47
|
+
const COMPACTION_FREED_EPSILON = 256;
|
|
48
|
+
const MAX_TOOL_END_OUTPUT_CHARS = 16384;
|
|
49
|
+
const BATCH_RESPONSE_MAX_CHARS = 500;
|
|
50
|
+
function batchResponseDigest(result) {
|
|
51
|
+
const content = result !== null && typeof result === "object" ? result.content : result;
|
|
52
|
+
if (content === undefined || content === null)
|
|
53
|
+
return undefined;
|
|
54
|
+
let text;
|
|
55
|
+
if (typeof content === "string") {
|
|
56
|
+
text = content;
|
|
57
|
+
}
|
|
58
|
+
else if (Array.isArray(content)) {
|
|
59
|
+
text = content
|
|
60
|
+
.map((c) => (c !== null && typeof c === "object" && typeof c.text === "string" ? c.text : ""))
|
|
61
|
+
.filter((t) => t.length > 0)
|
|
62
|
+
.join("\n");
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
try {
|
|
66
|
+
text = JSON.stringify(content) ?? "";
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (text.length === 0)
|
|
73
|
+
return undefined;
|
|
74
|
+
return text.length > BATCH_RESPONSE_MAX_CHARS
|
|
75
|
+
? `${text.slice(0, BATCH_RESPONSE_MAX_CHARS)}…[+${text.length - BATCH_RESPONSE_MAX_CHARS} chars truncated]`
|
|
76
|
+
: text;
|
|
77
|
+
}
|
|
78
|
+
function clipToByteBudget(s, maxBytes) {
|
|
79
|
+
if (Buffer.byteLength(s, "utf8") <= maxBytes)
|
|
80
|
+
return s;
|
|
81
|
+
let lo = 0;
|
|
82
|
+
let hi = s.length;
|
|
83
|
+
while (lo < hi) {
|
|
84
|
+
const mid = (lo + hi + 1) >> 1;
|
|
85
|
+
if (Buffer.byteLength(s.slice(0, mid), "utf8") <= maxBytes)
|
|
86
|
+
lo = mid;
|
|
87
|
+
else
|
|
88
|
+
hi = mid - 1;
|
|
89
|
+
}
|
|
90
|
+
return s.slice(0, lo);
|
|
91
|
+
}
|
|
92
|
+
const BATCH_CONTEXT_MIN_KEEP_BYTES = 160;
|
|
93
|
+
const BATCH_TRUNCATION_MARKER = "\n…[truncated]";
|
|
94
|
+
const SUGGESTIONS_DEFAULT_COUNT = 3;
|
|
95
|
+
const SUGGESTIONS_MAX_COUNT = 8;
|
|
96
|
+
const SUGGESTIONS_TIMEOUT_MS = 30_000;
|
|
97
|
+
const SUGGESTIONS_TRANSCRIPT_MESSAGES = 12;
|
|
98
|
+
const SNAPSHOT_TIMEOUT_MS = 30_000;
|
|
99
|
+
const SNAPSHOT_TOO_LARGE_TTL_MS = 30 * 60_000;
|
|
100
|
+
function resolveMaxTurns(limits) {
|
|
101
|
+
if (limits?.maxTurns !== undefined) {
|
|
102
|
+
return limits.maxTurns;
|
|
103
|
+
}
|
|
104
|
+
if (limits?.timeoutSec !== undefined && limits.timeoutSec > 0) {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
return DEFAULT_MAX_TURNS;
|
|
108
|
+
}
|
|
109
|
+
const DEFERRED_REISSUE = "[DEFERRED] This tool call shared a batch with a call that suspended for durable approval, so it was " +
|
|
110
|
+
"NOT executed on resume. If you still need it, issue it again now.";
|
|
111
|
+
function deepJsonEqual(a, b) {
|
|
112
|
+
if (a === b)
|
|
113
|
+
return true;
|
|
114
|
+
if (typeof a !== typeof b || a === null || b === null || typeof a !== "object") {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
const aArr = Array.isArray(a);
|
|
118
|
+
const bArr = Array.isArray(b);
|
|
119
|
+
if (aArr !== bArr)
|
|
120
|
+
return false;
|
|
121
|
+
if (aArr && bArr) {
|
|
122
|
+
if (a.length !== b.length)
|
|
123
|
+
return false;
|
|
124
|
+
return a.every((v, i) => deepJsonEqual(v, b[i]));
|
|
125
|
+
}
|
|
126
|
+
const ao = a;
|
|
127
|
+
const bo = b;
|
|
128
|
+
const aKeys = Object.keys(ao);
|
|
129
|
+
const bKeys = Object.keys(bo);
|
|
130
|
+
if (aKeys.length !== bKeys.length)
|
|
131
|
+
return false;
|
|
132
|
+
return aKeys.every((k) => Object.prototype.hasOwnProperty.call(bo, k) && deepJsonEqual(ao[k], bo[k]));
|
|
133
|
+
}
|
|
134
|
+
function sameWinner(a, b) {
|
|
135
|
+
return a.boundCallId === b.boundCallId && a.decision === b.decision && deepJsonEqual(a.updatedInput, b.updatedInput);
|
|
136
|
+
}
|
|
137
|
+
function writeFamilyOfCanonical(name) {
|
|
138
|
+
if (name === "TaskCreate" || name === "TaskUpdate")
|
|
139
|
+
return "task";
|
|
140
|
+
if (name === "TodoWrite")
|
|
141
|
+
return "todo";
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
function toolResultMsg(toolCallId, toolName, text, isError) {
|
|
145
|
+
return {
|
|
146
|
+
role: "toolResult",
|
|
147
|
+
toolCallId,
|
|
148
|
+
toolName,
|
|
149
|
+
content: [{ type: "text", text }],
|
|
150
|
+
isError,
|
|
151
|
+
timestamp: Date.now(),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function resumeContinuation(resume) {
|
|
155
|
+
if (resume.outcome.gate === "resource_limit") {
|
|
156
|
+
return formatHookFeedback("You were resumed after a pause. Before continuing, re-orient from the workspace: run `git status` and " +
|
|
157
|
+
"review your recent changes / last commits to confirm what is already done, then continue the " +
|
|
158
|
+
"remaining work. Do NOT restart the task or re-run work that is already committed.");
|
|
159
|
+
}
|
|
160
|
+
if (resume.outcome.gate === "dry_run_review") {
|
|
161
|
+
const verdict = resume.outcome.decision === "approve"
|
|
162
|
+
? "Your predicted change was REVIEWED and APPROVED; it has been applied"
|
|
163
|
+
: `Your predicted change was REVIEWED and REJECTED${resume.outcome.reason ? `: ${delimitUntrusted("reviewer note", resume.outcome.reason)}` : ""}; it was NOT applied`;
|
|
164
|
+
return formatHookFeedback(`You were resumed after a dry-run review. ${verdict}. Before continuing, re-orient from the workspace ` +
|
|
165
|
+
"(run `git status` and review your recent changes) to confirm the current state, then continue the " +
|
|
166
|
+
"remaining work. Do NOT restart the task or re-run work that is already done.");
|
|
167
|
+
}
|
|
168
|
+
if (resume.outcome.gate === "plan_review") {
|
|
169
|
+
if (resume.outcome.decision === "approve") {
|
|
170
|
+
return formatHookFeedback("Your proposed PLAN was REVIEWED and APPROVED. Proceed with that plan now — begin executing it. " +
|
|
171
|
+
"This is a RESUMED task; do NOT re-plan or restart from scratch, just carry out the approved plan.");
|
|
172
|
+
}
|
|
173
|
+
if (resume.outcome.decision === "edit") {
|
|
174
|
+
if (!resume.outcome.editedPlan) {
|
|
175
|
+
return formatHookFeedback("Your proposed PLAN was REVIEWED and EDITED, but no revised plan text was supplied — proceed with your " +
|
|
176
|
+
"ORIGINAL plan as-is, begin executing it now. This is a RESUMED task; do NOT re-plan or restart from scratch.");
|
|
177
|
+
}
|
|
178
|
+
return formatHookFeedback("Your proposed PLAN was REVIEWED and EDITED by a human reviewer. Proceed with the REVISED plan below " +
|
|
179
|
+
"(it supersedes your earlier plan); begin executing it now. This is a RESUMED task; do NOT re-plan " +
|
|
180
|
+
"or restart from scratch, just carry out the revised plan.\n\nThe REVISED plan to follow is:\n" +
|
|
181
|
+
delimitUntrusted("revised plan", resume.outcome.editedPlan));
|
|
182
|
+
}
|
|
183
|
+
const why = resume.outcome.reason ? ` Reviewer note: ${delimitUntrusted("reviewer note", resume.outcome.reason)}` : "";
|
|
184
|
+
return formatHookFeedback(`Your proposed PLAN was REVIEWED and REJECTED; it was NOT executed.${why} Produce a NEW plan that ` +
|
|
185
|
+
"addresses the concern, then continue. This is a RESUMED task — re-plan from the current state; do " +
|
|
186
|
+
"NOT execute the rejected plan.");
|
|
187
|
+
}
|
|
188
|
+
const { pendingAction } = resume.cp;
|
|
189
|
+
if (pendingAction.kind !== "tool_approval") {
|
|
190
|
+
return formatHookFeedback("This is a RESUMED task — continue from where you left off without restarting or re-running prior work.");
|
|
191
|
+
}
|
|
192
|
+
const decided = resume.outcome.decision === "allow"
|
|
193
|
+
? "was APPROVED and has now been executed — its result is in the tool results above"
|
|
194
|
+
: `was DENIED${resume.outcome.reason ? `: ${delimitUntrusted("reviewer note", resume.outcome.reason)}` : ""}`;
|
|
195
|
+
const remaining = pendingAction.batchToolCallIds.filter((id) => id !== pendingAction.toolCallId && !pendingAction.completedCallIds.includes(id)).length;
|
|
196
|
+
const batchNote = remaining > 0
|
|
197
|
+
? ` ${remaining} other tool call(s) from that batch were returned as deferred and not run — re-issue any you still need.`
|
|
198
|
+
: "";
|
|
199
|
+
return formatHookFeedback(`The pending tool call "${pendingAction.toolName}" ${decided}.${batchNote} ` +
|
|
200
|
+
`This is a RESUMED task — every step before this point is already done and its results are in the ` +
|
|
201
|
+
`conversation above. Do NOT restart the task or re-run any tool you already ran; continue from this ` +
|
|
202
|
+
`exact point, building on the existing results, and finish the remaining work.`);
|
|
203
|
+
}
|
|
204
|
+
export class Runner {
|
|
205
|
+
deps;
|
|
206
|
+
sessions;
|
|
207
|
+
sessionLocks = new Map();
|
|
208
|
+
snapshotTooLargeRoots = new Map();
|
|
209
|
+
pendingSessionNotifications = new PendingSessionNotifications();
|
|
210
|
+
constructor(deps) {
|
|
211
|
+
this.deps = deps.memoryStore
|
|
212
|
+
? { ...deps, memoryStore: guardedMemoryStore(deps.memoryStore, deps.utilityGate) }
|
|
213
|
+
: deps;
|
|
214
|
+
if (this.deps.tiers) {
|
|
215
|
+
this.deps = { ...this.deps, models: expandTiers(this.deps.models, this.deps.tiers) };
|
|
216
|
+
}
|
|
217
|
+
if (!this.deps.toolResultStore) {
|
|
218
|
+
this.deps = { ...this.deps, toolResultStore: new RunnerSharedToolResultStore({ maxTotalChars: 64_000_000 }) };
|
|
219
|
+
}
|
|
220
|
+
this.sessions = deps.sessionStore ?? new TtlSessionStore();
|
|
221
|
+
}
|
|
222
|
+
emitTaskOutcome(outcome) {
|
|
223
|
+
emitTaskOutcome(this.deps, outcome);
|
|
224
|
+
}
|
|
225
|
+
get agentCatalog() {
|
|
226
|
+
return {
|
|
227
|
+
...(this.deps.agents ? { agents: [...this.deps.agents] } : {}),
|
|
228
|
+
...(this.deps.builtinAgents !== undefined ? { builtinAgents: this.deps.builtinAgents } : {}),
|
|
229
|
+
...(this.deps.models ? { models: { ...this.deps.models } } : {}),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
get gateBaseline() {
|
|
233
|
+
const deps = this.deps;
|
|
234
|
+
const h = deps.hooks;
|
|
235
|
+
const hooks = h
|
|
236
|
+
? {
|
|
237
|
+
...(typeof h.preToolUse === "function" ? { preToolUse: (t, i, c) => h.preToolUse(t, i, c) } : {}),
|
|
238
|
+
...(typeof h.postToolUse === "function" ? { postToolUse: (t, i, o, c) => h.postToolUse(t, i, o, c) } : {}),
|
|
239
|
+
...(typeof h.userPromptSubmit === "function" ? { userPromptSubmit: (p) => h.userPromptSubmit(p) } : {}),
|
|
240
|
+
...(typeof h.stop === "function" ? { stop: (c) => h.stop(c) } : {}),
|
|
241
|
+
...(typeof h.postToolUseFailure === "function"
|
|
242
|
+
? { postToolUseFailure: (t, i, f, c) => h.postToolUseFailure(t, i, f, c) }
|
|
243
|
+
: {}),
|
|
244
|
+
...(typeof h.postToolBatch === "function" ? { postToolBatch: (b) => h.postToolBatch(b) } : {}),
|
|
245
|
+
...(typeof h.preCompact === "function" ? { preCompact: (c) => h.preCompact(c) } : {}),
|
|
246
|
+
...(typeof h.postCompact === "function" ? { postCompact: (c) => h.postCompact(c) } : {}),
|
|
247
|
+
...(typeof h.stopFailure === "function" ? { stopFailure: (c) => h.stopFailure(c) } : {}),
|
|
248
|
+
...(typeof h.permissionDenied === "function" ? { permissionDenied: (p) => h.permissionDenied(p) } : {}),
|
|
249
|
+
}
|
|
250
|
+
: undefined;
|
|
251
|
+
return {
|
|
252
|
+
...(deps.toolPolicy !== undefined
|
|
253
|
+
? { toolPolicy: { check: (req, signal) => deps.toolPolicy.check(req, signal) } }
|
|
254
|
+
: {}),
|
|
255
|
+
...(hooks !== undefined ? { hooks } : {}),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
async acquireSessionLock(sessionId) {
|
|
259
|
+
const prev = this.sessionLocks.get(sessionId) ?? Promise.resolve();
|
|
260
|
+
let release;
|
|
261
|
+
const mine = new Promise((r) => {
|
|
262
|
+
release = r;
|
|
263
|
+
});
|
|
264
|
+
const chain = prev.then(() => mine);
|
|
265
|
+
this.sessionLocks.set(sessionId, chain);
|
|
266
|
+
await prev.catch(() => { });
|
|
267
|
+
return () => {
|
|
268
|
+
release();
|
|
269
|
+
void chain.then(() => {
|
|
270
|
+
if (this.sessionLocks.get(sessionId) === chain) {
|
|
271
|
+
this.sessionLocks.delete(sessionId);
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
runTaskStream(spec, resume, internals) {
|
|
277
|
+
const queue = new PushQueue();
|
|
278
|
+
const detachHub = new ToolDetachHub();
|
|
279
|
+
let resultValue;
|
|
280
|
+
let consolidationDone = Promise.resolve();
|
|
281
|
+
let suggestionsDone = Promise.resolve([]);
|
|
282
|
+
let handle;
|
|
283
|
+
let publishReady;
|
|
284
|
+
const ready = new Promise((res) => {
|
|
285
|
+
publishReady = res;
|
|
286
|
+
});
|
|
287
|
+
const READY_TIMEOUT_MS = 30_000;
|
|
288
|
+
const orTimeout = (p) => {
|
|
289
|
+
let t;
|
|
290
|
+
const timeout = new Promise((res2) => {
|
|
291
|
+
t = setTimeout(() => res2(undefined), READY_TIMEOUT_MS);
|
|
292
|
+
t.unref?.();
|
|
293
|
+
});
|
|
294
|
+
return Promise.race([p, timeout]).finally(() => clearTimeout(t));
|
|
295
|
+
};
|
|
296
|
+
let reapHandle;
|
|
297
|
+
const manualCompactRef = { requested: false, waiters: [] };
|
|
298
|
+
const drainManualCompactWaiters = (outcome) => {
|
|
299
|
+
manualCompactRef.requested = false;
|
|
300
|
+
for (const w of manualCompactRef.waiters.splice(0))
|
|
301
|
+
w(outcome);
|
|
302
|
+
};
|
|
303
|
+
const run = (async () => {
|
|
304
|
+
const releaseLock = spec.sessionId
|
|
305
|
+
? await this.acquireSessionLock(spec.sessionId)
|
|
306
|
+
: undefined;
|
|
307
|
+
try {
|
|
308
|
+
await this.runLocked(spec, queue, (r) => {
|
|
309
|
+
resultValue = r;
|
|
310
|
+
}, (p) => {
|
|
311
|
+
consolidationDone = p;
|
|
312
|
+
}, (p) => {
|
|
313
|
+
suggestionsDone = p;
|
|
314
|
+
}, (h) => {
|
|
315
|
+
handle = h;
|
|
316
|
+
publishReady(h);
|
|
317
|
+
}, (s) => {
|
|
318
|
+
reapHandle = s;
|
|
319
|
+
}, manualCompactRef, resume, { ...(internals ?? {}), detachHub });
|
|
320
|
+
}
|
|
321
|
+
finally {
|
|
322
|
+
releaseLock?.();
|
|
323
|
+
publishReady(handle);
|
|
324
|
+
drainManualCompactWaiters("mooted");
|
|
325
|
+
}
|
|
326
|
+
})();
|
|
327
|
+
const settled = run.catch(async (err) => {
|
|
328
|
+
const code = errorCodeOf(err);
|
|
329
|
+
const reopenReason = code === "resume.env_failed"
|
|
330
|
+
? "env_failed"
|
|
331
|
+
: code === "resume.tool_unavailable"
|
|
332
|
+
? "tool_unavailable"
|
|
333
|
+
: undefined;
|
|
334
|
+
const reopenable = reopenReason !== undefined;
|
|
335
|
+
if (reopenable && resume?.onEnvRestoreFailed) {
|
|
336
|
+
try {
|
|
337
|
+
await resume.onEnvRestoreFailed(reopenReason);
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
else if (resume && !reopenable) {
|
|
343
|
+
try {
|
|
344
|
+
await this.sessions.unpin?.(resume.cp.sessionId);
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
resultValue = {
|
|
350
|
+
taskId: spec.taskId ?? "unknown",
|
|
351
|
+
sessionId: spec.sessionId ?? "unknown",
|
|
352
|
+
status: "failed",
|
|
353
|
+
result: "",
|
|
354
|
+
errorMessage: err instanceof Error ? err.message : String(err),
|
|
355
|
+
errorCode: code,
|
|
356
|
+
stats: { turns: 0, tokens: 0, toolCalls: 0, cachedTokens: 0, costUsd: 0 },
|
|
357
|
+
};
|
|
358
|
+
queue.push({ type: "done", result: resultValue });
|
|
359
|
+
queue.close();
|
|
360
|
+
});
|
|
361
|
+
const steeringError = (msg, code = "steering.not_running") => {
|
|
362
|
+
const e = new Error(`cannot steer: ${msg}`);
|
|
363
|
+
e.code = code;
|
|
364
|
+
return e;
|
|
365
|
+
};
|
|
366
|
+
let destroyOnce;
|
|
367
|
+
const reapSuspended = async () => {
|
|
368
|
+
const rh = reapHandle;
|
|
369
|
+
if (!rh)
|
|
370
|
+
return;
|
|
371
|
+
const reportErr = (err) => {
|
|
372
|
+
try {
|
|
373
|
+
this.deps.onError?.(err, { phase: "config", sessionId: rh.sessionId });
|
|
374
|
+
}
|
|
375
|
+
catch {
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
let won;
|
|
379
|
+
try {
|
|
380
|
+
won = await rh.store.expire(rh.token, rh.scope);
|
|
381
|
+
}
|
|
382
|
+
catch (err) {
|
|
383
|
+
reportErr(err);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
if (!won)
|
|
387
|
+
return;
|
|
388
|
+
if (rh.env && hasDestroy(rh.env)) {
|
|
389
|
+
try {
|
|
390
|
+
await rh.env.destroy();
|
|
391
|
+
}
|
|
392
|
+
catch (err) {
|
|
393
|
+
reportErr(err);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
return {
|
|
398
|
+
[Symbol.asyncIterator]: () => queue[Symbol.asyncIterator](),
|
|
399
|
+
result: async () => {
|
|
400
|
+
await settled;
|
|
401
|
+
return resultValue;
|
|
402
|
+
},
|
|
403
|
+
consolidation: async () => {
|
|
404
|
+
await run.catch(() => { });
|
|
405
|
+
await consolidationDone.catch(() => { });
|
|
406
|
+
},
|
|
407
|
+
suggestions: async () => {
|
|
408
|
+
await run.catch(() => { });
|
|
409
|
+
return suggestionsDone.catch(() => []);
|
|
410
|
+
},
|
|
411
|
+
steer: async (text, options) => {
|
|
412
|
+
if (resultValue)
|
|
413
|
+
throw steeringError("the task has already finished");
|
|
414
|
+
const h = handle ?? (await orTimeout(ready));
|
|
415
|
+
if (!h)
|
|
416
|
+
throw steeringError("the task is not running");
|
|
417
|
+
if (options?.trusted && sanitizeUntrustedText(text) !== text) {
|
|
418
|
+
throw steeringError("trusted steering text must not contain a </system-reminder> tag", "steering.invalid_content");
|
|
419
|
+
}
|
|
420
|
+
const payload = options?.trusted ? formatHookFeedback(text) : text;
|
|
421
|
+
try {
|
|
422
|
+
await h.harness.steer(payload);
|
|
423
|
+
}
|
|
424
|
+
catch (e) {
|
|
425
|
+
if (e instanceof Error && e.code === "invalid_state") {
|
|
426
|
+
throw steeringError("the task is no longer running");
|
|
427
|
+
}
|
|
428
|
+
throw e;
|
|
429
|
+
}
|
|
430
|
+
},
|
|
431
|
+
compact: async () => {
|
|
432
|
+
if (resultValue)
|
|
433
|
+
throw steeringError("the task has already finished");
|
|
434
|
+
const h = handle ?? (await orTimeout(ready));
|
|
435
|
+
if (!h)
|
|
436
|
+
throw steeringError("the task is not running");
|
|
437
|
+
return new Promise((resolve) => {
|
|
438
|
+
manualCompactRef.requested = true;
|
|
439
|
+
manualCompactRef.waiters.push(resolve);
|
|
440
|
+
});
|
|
441
|
+
},
|
|
442
|
+
detach: (toolCallId) => {
|
|
443
|
+
detachHub.request(toolCallId);
|
|
444
|
+
},
|
|
445
|
+
interrupt: async () => {
|
|
446
|
+
const h = handle ?? (await orTimeout(ready));
|
|
447
|
+
if (!h)
|
|
448
|
+
return;
|
|
449
|
+
h.abortController.abort();
|
|
450
|
+
void h.harness.abort();
|
|
451
|
+
},
|
|
452
|
+
destroy: () => (destroyOnce ??= (async () => {
|
|
453
|
+
const h = handle ?? (await orTimeout(ready));
|
|
454
|
+
if (h) {
|
|
455
|
+
h.abortController.abort();
|
|
456
|
+
void h.harness.abort();
|
|
457
|
+
}
|
|
458
|
+
await orTimeout(run.catch(() => { }));
|
|
459
|
+
await reapSuspended();
|
|
460
|
+
})()),
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
async runLocked(spec, queue, setResult, onConsolidation, onSuggestions, onReady, onSuspend, manualCompactRef, resume, internals) {
|
|
464
|
+
const prepareResume = resume
|
|
465
|
+
? {
|
|
466
|
+
leafId: resume.cp.leafId,
|
|
467
|
+
suspendedBatch: new Set(resume.cp.pendingAction.kind === "tool_approval" ? resume.cp.pendingAction.batchToolCallIds : []),
|
|
468
|
+
seed: resume.cp.state,
|
|
469
|
+
priorSuspendCount: resume.cp.suspendCount,
|
|
470
|
+
priorLedger: resume.cp.resourceLedger,
|
|
471
|
+
priorHumanReview: resume.cp.humanReview,
|
|
472
|
+
workspaceHandle: resume.cp.state.workspaceHandle,
|
|
473
|
+
}
|
|
474
|
+
: undefined;
|
|
475
|
+
const memorySelector = this.deps.memoryRecall?.enabled || this.deps.dynamicRecall?.enabled
|
|
476
|
+
? (this.deps.memoryRecall?.select ?? createBrainMemorySelector(this, { model: this.deps.memoryRecall?.selectorModel, release: (id) => releaseSession(this, id) }))
|
|
477
|
+
: undefined;
|
|
478
|
+
const taskNotificationQueue = new SystemInjectionQueue();
|
|
479
|
+
const detachHub = internals?.detachHub ?? new ToolDetachHub();
|
|
480
|
+
let notificationHarness;
|
|
481
|
+
let notificationIdent = () => ({});
|
|
482
|
+
let notificationLaneLive = true;
|
|
483
|
+
let notificationSessionId;
|
|
484
|
+
const unsubscribeTaskNotifications = taskNotificationQueue.subscribe((item) => {
|
|
485
|
+
queue.push({ type: "task_notification", notification: item.payload, ...notificationIdent() });
|
|
486
|
+
if (notificationHarness) {
|
|
487
|
+
const xml = renderTaskNotificationXml(item.payload);
|
|
488
|
+
const deliver = item.priority === "later" ? notificationHarness.followUp(xml) : notificationHarness.steer(xml);
|
|
489
|
+
void deliver.catch(() => {
|
|
490
|
+
if (notificationSessionId !== undefined)
|
|
491
|
+
this.pendingSessionNotifications.pend(notificationSessionId, item.payload);
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
const upstreamTaskNotification = internals?.onTaskNotification;
|
|
496
|
+
const prepared = await prepareTask(spec, this.deps, this.sessions, prepareResume, memorySelector, {
|
|
497
|
+
...(internals ?? {}),
|
|
498
|
+
detachHub,
|
|
499
|
+
onTaskNotification: (notification, opts) => {
|
|
500
|
+
try {
|
|
501
|
+
upstreamTaskNotification?.(notification, opts);
|
|
502
|
+
}
|
|
503
|
+
catch {
|
|
504
|
+
}
|
|
505
|
+
if (!notificationLaneLive) {
|
|
506
|
+
if (notificationSessionId !== undefined)
|
|
507
|
+
this.pendingSessionNotifications.pend(notificationSessionId, notification);
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
taskNotificationQueue.enqueue({
|
|
511
|
+
kind: "task_notification",
|
|
512
|
+
priority: opts?.priority ?? "later",
|
|
513
|
+
dedupKey: taskNotificationDedupKey(notification),
|
|
514
|
+
payload: notification,
|
|
515
|
+
});
|
|
516
|
+
},
|
|
517
|
+
}, this);
|
|
518
|
+
notificationHarness = prepared.harness;
|
|
519
|
+
notificationSessionId = prepared.sessionId;
|
|
520
|
+
{
|
|
521
|
+
const pendingIdle = this.pendingSessionNotifications.drain(prepared.sessionId);
|
|
522
|
+
if (pendingIdle !== undefined) {
|
|
523
|
+
for (const payload of discloseDroppedPending(pendingIdle)) {
|
|
524
|
+
queue.push({ type: "task_notification", notification: payload });
|
|
525
|
+
void prepared.harness.nextTurn(renderTaskNotificationXml(payload)).catch(() => {
|
|
526
|
+
this.pendingSessionNotifications.pend(prepared.sessionId, payload);
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
onReady({ harness: prepared.harness, abortController: prepared.abortController });
|
|
532
|
+
const stats = { turns: 0, tokens: 0, toolCalls: 0, promptTokens: 0, cachedTokens: 0, cacheWriteTokens: 0, cacheWriteTokensLong: 0, outputTokens: 0, costUsd: 0, costMicroUsd: 0 };
|
|
533
|
+
prepared.liveSpendRef.get = () => ({ costMicroUsd: stats.costMicroUsd, tokens: stats.tokens, turns: stats.turns });
|
|
534
|
+
if (resume &&
|
|
535
|
+
resume.cp.suspendedAt !== undefined &&
|
|
536
|
+
(resume.cp.gate.kind === "human" ||
|
|
537
|
+
resume.cp.gate.kind === "irreversible_ask" ||
|
|
538
|
+
resume.cp.gate.kind === "needs_review" ||
|
|
539
|
+
resume.cp.gate.kind === "plan_review")) {
|
|
540
|
+
const waitMs = Math.max(0, prepared.now() - resume.cp.suspendedAt);
|
|
541
|
+
const decision = resume.outcome.decision;
|
|
542
|
+
prepared.humanReviewRef.count += 1;
|
|
543
|
+
prepared.humanReviewRef.totalWaitMs += waitMs;
|
|
544
|
+
const gateToolName = "toolName" in resume.cp.gate ? resume.cp.gate.toolName : undefined;
|
|
545
|
+
prepared.humanReviewRef.gates.push({ kind: resume.cp.gate.kind, waitMs, ...(decision !== undefined ? { decision } : {}), ...(gateToolName !== undefined ? { toolName: gateToolName } : {}) });
|
|
546
|
+
}
|
|
547
|
+
let cacheFamily = cacheFamilyOf(prepared.model);
|
|
548
|
+
let pricing = this.deps.pricing?.[prepared.model.id] ?? modelCostToPricing(prepared.model.cost);
|
|
549
|
+
let degradeToModel;
|
|
550
|
+
if (spec.degrade) {
|
|
551
|
+
try {
|
|
552
|
+
degradeToModel = resolveModel(spec.degrade.to, this.deps.models);
|
|
553
|
+
}
|
|
554
|
+
catch (e) {
|
|
555
|
+
this.deps.onError?.(e, { phase: "degraded", sessionId: prepared.sessionId });
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
const famOverride = prepared.model.params?.promptCacheFamily;
|
|
559
|
+
if (famOverride && !["input-includes-cached", "input-excludes-cached", "openai", "anthropic"].includes(famOverride)) {
|
|
560
|
+
this.deps.onError?.(new Error(`prompt-cache: unrecognized model.params.promptCacheFamily "${famOverride}" — ignored (fell back to model.api inference). Use "input-includes-cached" | "input-excludes-cached" (aliases "openai" | "anthropic").`), { phase: "prompt-cache", sessionId: prepared.sessionId });
|
|
561
|
+
}
|
|
562
|
+
let turnsExceeded = false;
|
|
563
|
+
let budgetHit;
|
|
564
|
+
let degraded;
|
|
565
|
+
let outputErrorStreak = 0;
|
|
566
|
+
let outputInvalid = false;
|
|
567
|
+
let cacheBreakReported = false;
|
|
568
|
+
const outputRetryCap = resolveOutputRetries(spec.outputRetries);
|
|
569
|
+
const effectiveMaxTurns = resolveMaxTurns(spec.limits);
|
|
570
|
+
const tracer = spec.tracer ?? this.deps.tracer;
|
|
571
|
+
const taskId = spec.taskId ?? prepared.sessionId;
|
|
572
|
+
const taskStart = Date.now();
|
|
573
|
+
void Promise.resolve(this.sessions.noteTaskRun?.(prepared.sessionId, taskId)).catch(() => { });
|
|
574
|
+
const recordDegraded = (info, toModel) => {
|
|
575
|
+
if (degraded !== undefined)
|
|
576
|
+
return;
|
|
577
|
+
let m = toModel;
|
|
578
|
+
if (!m) {
|
|
579
|
+
try {
|
|
580
|
+
m = resolveModel(info.to, this.deps.models);
|
|
581
|
+
}
|
|
582
|
+
catch {
|
|
583
|
+
m = undefined;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
if (m) {
|
|
587
|
+
pricing = this.deps.pricing?.[m.id] ?? modelCostToPricing(m.cost);
|
|
588
|
+
cacheFamily = cacheFamilyOf(m);
|
|
589
|
+
}
|
|
590
|
+
else if (this.deps.pricing?.[info.to]) {
|
|
591
|
+
pricing = this.deps.pricing[info.to];
|
|
592
|
+
}
|
|
593
|
+
degraded = info;
|
|
594
|
+
emitTrace(tracer, () => ({ kind: "task.degraded", version: 1, taskId, from: info.from, to: info.to, reason: info.reason, atTurn: info.atTurn, ts: Date.now() }));
|
|
595
|
+
this.deps.onError?.(new Error(`degraded to a cheaper model: ${info.from} → ${info.to} (${info.reason}) at turn ${info.atTurn}`), { phase: "degraded", sessionId: prepared.sessionId });
|
|
596
|
+
};
|
|
597
|
+
const sliceWindowMicroUsd = spec.maxCostUsd !== undefined ? Math.round(spec.maxCostUsd * 1e6) : undefined;
|
|
598
|
+
const remainingMicroUsd = prepared.resourceLedger
|
|
599
|
+
? remainingBudgetMicroUsd(prepared.resourceLedger)
|
|
600
|
+
: spec.resourceSuspend?.totalBudgetUsd !== undefined
|
|
601
|
+
? Math.round(spec.resourceSuspend.totalBudgetUsd * 1e6)
|
|
602
|
+
: undefined;
|
|
603
|
+
const maxCostMicroUsd = sliceWindowMicroUsd !== undefined && remainingMicroUsd !== undefined
|
|
604
|
+
? Math.min(sliceWindowMicroUsd, remainingMicroUsd)
|
|
605
|
+
: (sliceWindowMicroUsd ?? remainingMicroUsd);
|
|
606
|
+
const overBudget = () => (maxCostMicroUsd !== undefined && stats.costMicroUsd > maxCostMicroUsd) ||
|
|
607
|
+
(spec.maxTokens !== undefined && stats.tokens > spec.maxTokens);
|
|
608
|
+
const streamCancel = (spec.budgetStreamCancel ?? maxCostMicroUsd !== undefined) && prepared.suspendForResource === undefined;
|
|
609
|
+
let callOutputChars = 0;
|
|
610
|
+
let lastStreamBudgetCheck = 0;
|
|
611
|
+
const projectedOverBudget = () => {
|
|
612
|
+
const estOut = Math.ceil(callOutputChars / 4);
|
|
613
|
+
if (maxCostMicroUsd !== undefined) {
|
|
614
|
+
const estOutMicro = computeCostMicroUsd({ totalInputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, cacheWriteTokensLong: 0, outputTokens: estOut }, pricing);
|
|
615
|
+
if (stats.costMicroUsd + estOutMicro > maxCostMicroUsd)
|
|
616
|
+
return true;
|
|
617
|
+
}
|
|
618
|
+
return spec.maxTokens !== undefined && stats.tokens + estOut > spec.maxTokens;
|
|
619
|
+
};
|
|
620
|
+
let callStartAt;
|
|
621
|
+
let firstTokenAt;
|
|
622
|
+
let turnUsage;
|
|
623
|
+
let turnUsageMissing = false;
|
|
624
|
+
let turnStopReason;
|
|
625
|
+
let nudgesSent = 0;
|
|
626
|
+
let callCutoffs = 0;
|
|
627
|
+
let repetitionCuts = 0;
|
|
628
|
+
let repetitionSpared = 0;
|
|
629
|
+
const repetitionEvents = [];
|
|
630
|
+
const REPETITION_EVENTS_CAP = 20;
|
|
631
|
+
let preemptIgnoredReported = false;
|
|
632
|
+
let wroteThisRun = false;
|
|
633
|
+
let finalVerifyInjections = 0;
|
|
634
|
+
let groundingSignalPreR9 = false;
|
|
635
|
+
let groundingSignalPostR9 = false;
|
|
636
|
+
const attachmentsCfg = spec.attachments;
|
|
637
|
+
const attachState = attachmentsCfg !== undefined ? createAttachmentState() : undefined;
|
|
638
|
+
let cadenceTurns = 0;
|
|
639
|
+
let lastTurnHadToolCalls = false;
|
|
640
|
+
if (attachState !== undefined && attachmentsCfg?.backgroundTasks === true) {
|
|
641
|
+
try {
|
|
642
|
+
const branch = await prepared.session.getBranch();
|
|
643
|
+
for (let i = branch.length - 1; i >= 0; i--) {
|
|
644
|
+
const e = branch[i];
|
|
645
|
+
if (e.type === "compaction") {
|
|
646
|
+
attachState.postCompactPending = true;
|
|
647
|
+
break;
|
|
648
|
+
}
|
|
649
|
+
if (e.type === "message" && e.message.role === "assistant")
|
|
650
|
+
break;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
catch {
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
if (attachState !== undefined && attachmentsCfg?.agentListing === true && prepared.agentListing?.seedAnnounced === true) {
|
|
657
|
+
const listing = prepared.agentListing;
|
|
658
|
+
try {
|
|
659
|
+
const headers = [agentListingInitialHeader(listing.toolName), agentListingDeltaHeader(listing.toolName)];
|
|
660
|
+
const textOf = (m) => {
|
|
661
|
+
const c = m.content;
|
|
662
|
+
if (typeof c === "string")
|
|
663
|
+
return c;
|
|
664
|
+
if (Array.isArray(c)) {
|
|
665
|
+
return c
|
|
666
|
+
.map((b) => (b !== null && typeof b === "object" && b.type === "text" ? String(b.text ?? "") : ""))
|
|
667
|
+
.join("\n");
|
|
668
|
+
}
|
|
669
|
+
return "";
|
|
670
|
+
};
|
|
671
|
+
const branch = await prepared.session.getBranch();
|
|
672
|
+
const delivered = branch.some((e) => {
|
|
673
|
+
if (e.type !== "message")
|
|
674
|
+
return false;
|
|
675
|
+
const text = textOf(e.message);
|
|
676
|
+
return headers.some((h) => text.includes(h));
|
|
677
|
+
});
|
|
678
|
+
if (delivered) {
|
|
679
|
+
attachState.announcedAgentTypes = new Map(listing.entries.map((e) => [e.name, e.description]));
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
catch {
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
if (spec.finalVerification === true && resume !== undefined) {
|
|
686
|
+
try {
|
|
687
|
+
const branch = await prepared.session.getBranch();
|
|
688
|
+
for (const e of branch) {
|
|
689
|
+
if (e.type !== "message")
|
|
690
|
+
continue;
|
|
691
|
+
const m = e.message;
|
|
692
|
+
if (m.role === "user") {
|
|
693
|
+
const text = typeof m.content === "string"
|
|
694
|
+
? m.content
|
|
695
|
+
: m.content.map((b) => (b !== null && typeof b === "object" && b.type === "text" ? String(b.text ?? "") : "")).join("\n");
|
|
696
|
+
if (text.includes("<system-reminder>[final verification]") && finalVerifyInjections < 2)
|
|
697
|
+
finalVerifyInjections += 1;
|
|
698
|
+
}
|
|
699
|
+
else if (m.role === "toolResult" && !wroteThisRun && (prepared.toolEffects.get(m.toolName) ?? "write") !== "read") {
|
|
700
|
+
wroteThisRun = true;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
catch {
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
let attachmentsInjected = 0;
|
|
708
|
+
const todoToolMounted = attachState !== undefined && prepared.tools.some((t) => canonicalToolName(t.name) === "TodoWrite");
|
|
709
|
+
const taskToolsMounted = attachState !== undefined && prepared.tools.some((t) => canonicalToolName(t.name) === "TaskCreate");
|
|
710
|
+
const writeFamilyByName = new Map();
|
|
711
|
+
if (attachState !== undefined) {
|
|
712
|
+
for (const t of prepared.tools) {
|
|
713
|
+
const family = writeFamilyOfCanonical(canonicalToolName(t.name));
|
|
714
|
+
if (family !== undefined)
|
|
715
|
+
for (const n of [t.name, ...(t.aliases ?? [])])
|
|
716
|
+
writeFamilyByName.set(n, family);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
const writeFamilyOf = (name) => writeFamilyByName.get(name) ?? writeFamilyOfCanonical(canonicalToolName(name));
|
|
720
|
+
const toolStartAt = new Map();
|
|
721
|
+
emitTrace(tracer, () => ({ kind: "task.start", version: 1, taskId, model: prepared.model.id, ts: taskStart }));
|
|
722
|
+
emitTrace(tracer, () => ({
|
|
723
|
+
kind: "prompt.assembled",
|
|
724
|
+
version: 1,
|
|
725
|
+
taskId,
|
|
726
|
+
constitution: prepared.promptManifest.constitution,
|
|
727
|
+
blocks: prepared.promptManifest.blocks,
|
|
728
|
+
totalChars: prepared.promptManifest.blocks.reduce((n, b) => n + b.chars, 0),
|
|
729
|
+
ts: taskStart,
|
|
730
|
+
}));
|
|
731
|
+
if (prepared.thinking && prepared.thinking !== "off" && prepared.model.reasoning === true) {
|
|
732
|
+
emitTrace(tracer, () => {
|
|
733
|
+
const r = resolveReasoning(prepared.thinking, prepared.model);
|
|
734
|
+
return {
|
|
735
|
+
kind: "reasoning.resolved",
|
|
736
|
+
version: 1,
|
|
737
|
+
taskId,
|
|
738
|
+
model: prepared.model.id,
|
|
739
|
+
requested: r.requested,
|
|
740
|
+
effective: r.effective,
|
|
741
|
+
graded: r.graded,
|
|
742
|
+
clamped: r.clamped,
|
|
743
|
+
format: r.format,
|
|
744
|
+
endpoint: r.endpoint,
|
|
745
|
+
ts: Date.now(),
|
|
746
|
+
};
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
const walltimeDeadlineMs = spec.limits?.timeoutSec ? taskStart + spec.limits.timeoutSec * 1000 : undefined;
|
|
750
|
+
if (prepared.callCapRef && prepared.suspendForResource === undefined && walltimeDeadlineMs !== undefined) {
|
|
751
|
+
prepared.callCapRef.deadlineMs = walltimeDeadlineMs;
|
|
752
|
+
if (spec.limits?.gracefulFinalize !== false) {
|
|
753
|
+
prepared.callCapRef.cushionMs = writeoutCushionMs(spec.limits.timeoutSec * 1000);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
let finalizeInjected = false;
|
|
757
|
+
const nudgeSchedule = [];
|
|
758
|
+
{
|
|
759
|
+
const conf = spec.limits?.deadlineNudge;
|
|
760
|
+
const budgetSec = spec.limits?.timeoutSec;
|
|
761
|
+
if (conf !== false && budgetSec && budgetSec > 0) {
|
|
762
|
+
const [a, b] = conf?.at ?? [0.8, 0.95];
|
|
763
|
+
if (a > 0 && a < 1) {
|
|
764
|
+
const t1 = taskStart + a * budgetSec * 1000;
|
|
765
|
+
const remainMin = Math.max(1, Math.round((budgetSec * 1000 * (1 - a)) / 60_000));
|
|
766
|
+
const budgetMin = Math.max(1, Math.round(budgetSec / 60));
|
|
767
|
+
nudgeSchedule.push({
|
|
768
|
+
atMs: t1,
|
|
769
|
+
text: `<system-reminder>[deadline] ~${remainMin} min of the ~${budgetMin}-min wall-clock budget remain. ` +
|
|
770
|
+
`Start converging: persist finished work (write files / commit), prefer completing in-flight items over opening new ones.</system-reminder>`,
|
|
771
|
+
});
|
|
772
|
+
const t2 = taskStart + Math.max(Math.min(b * budgetSec * 1000, budgetSec * 1000 - 120_000), a * budgetSec * 1000 + 1);
|
|
773
|
+
if (b > a && b < 1) {
|
|
774
|
+
nudgeSchedule.push({
|
|
775
|
+
atMs: t2,
|
|
776
|
+
text: `<system-reminder>[deadline] the wall-clock budget is nearly exhausted. STOP new work — ` +
|
|
777
|
+
`write out deliverables and give your final answer NOW; an unfinished answer beats a hard timeout.</system-reminder>`,
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
let nudgeIdx = 0;
|
|
784
|
+
const timeout = startTimeout(prepared.harness, prepared.abortController, spec.limits?.timeoutSec, prepared.suspendForResource !== undefined);
|
|
785
|
+
const parentToolCallId = internals?.parentToolCallId;
|
|
786
|
+
const ident = () => parentToolCallId !== undefined ? { eventId: uuidv7(), parentToolCallId, sourceTaskId: taskId } : { eventId: uuidv7() };
|
|
787
|
+
notificationIdent = ident;
|
|
788
|
+
const pushContent = (e) => {
|
|
789
|
+
queue.push(e);
|
|
790
|
+
if (parentToolCallId !== undefined && internals?.onForwardEvent) {
|
|
791
|
+
try {
|
|
792
|
+
internals.onForwardEvent(e);
|
|
793
|
+
}
|
|
794
|
+
catch {
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
const emitCommitted = (entryId, role, toolCallId) => {
|
|
799
|
+
queue.push({ type: "message_committed", entryId, role, ...(toolCallId !== undefined ? { toolCallId } : {}), ...ident() });
|
|
800
|
+
};
|
|
801
|
+
const subagentName = parentToolCallId !== undefined && internals?.agentName !== undefined ? inlineUntrusted(internals.agentName.slice(0, 320), 80) : undefined;
|
|
802
|
+
let lastWorkspaceCwd = prepared.cwdRef?.current;
|
|
803
|
+
const statusEmit = (s) => {
|
|
804
|
+
queue.push({
|
|
805
|
+
type: "status",
|
|
806
|
+
phase: s.phase,
|
|
807
|
+
...(s.detail !== undefined ? { detail: s.detail } : {}),
|
|
808
|
+
...(s.retryInSec !== undefined ? { retryInSec: s.retryInSec } : {}),
|
|
809
|
+
...ident(),
|
|
810
|
+
});
|
|
811
|
+
};
|
|
812
|
+
const telemetryEmit = (t) => {
|
|
813
|
+
emitTrace(tracer, () => t.kind === "failover"
|
|
814
|
+
? {
|
|
815
|
+
kind: "brain.failover",
|
|
816
|
+
version: 1,
|
|
817
|
+
taskId,
|
|
818
|
+
servedIndex: t.servedIndex,
|
|
819
|
+
total: t.total,
|
|
820
|
+
...(t.errorCode !== undefined ? { errorCode: t.errorCode } : {}),
|
|
821
|
+
ts: Date.now(),
|
|
822
|
+
}
|
|
823
|
+
: t.kind === "breaker"
|
|
824
|
+
? { kind: "breaker.transition", version: 1, taskId, key: t.key, phase: t.phase, failures: t.failures, ts: Date.now() }
|
|
825
|
+
: t.kind === "retry"
|
|
826
|
+
? { kind: "brain.retry", version: 1, taskId, attempt: t.attempt, phase: t.phase, ts: Date.now() }
|
|
827
|
+
: { kind: "vision.placeholder", version: 1, taskId, count: t.count, ts: Date.now() });
|
|
828
|
+
};
|
|
829
|
+
const withBrainSinks = (fn) => runWithStatusSink(statusEmit, () => runWithBrainTelemetry(telemetryEmit, fn));
|
|
830
|
+
const trunc = (s) => s.slice(0, MAX_TOOL_END_OUTPUT_CHARS) + `…[+${s.length - MAX_TOOL_END_OUTPUT_CHARS} chars truncated]`;
|
|
831
|
+
const toolOutputFrom = (result) => {
|
|
832
|
+
const content = result !== null && typeof result === "object" ? result.content : result;
|
|
833
|
+
if (content === undefined)
|
|
834
|
+
return undefined;
|
|
835
|
+
if (typeof content === "string") {
|
|
836
|
+
return content.length > MAX_TOOL_END_OUTPUT_CHARS ? { output: trunc(content), truncated: true } : { output: content, truncated: false };
|
|
837
|
+
}
|
|
838
|
+
let serialized;
|
|
839
|
+
try {
|
|
840
|
+
serialized = JSON.stringify(content) ?? "";
|
|
841
|
+
}
|
|
842
|
+
catch {
|
|
843
|
+
return { output: "[unserializable tool output]", truncated: false };
|
|
844
|
+
}
|
|
845
|
+
return serialized.length > MAX_TOOL_END_OUTPUT_CHARS ? { output: trunc(serialized), truncated: true } : { output: content, truncated: false };
|
|
846
|
+
};
|
|
847
|
+
const CC_DETAIL_TYPES = new Set([
|
|
848
|
+
"edit", "multiedit", "create", "update", "bash", "notebook-edit", "text", "grep", "glob", "mcp",
|
|
849
|
+
"agent", "task", "task-list", "task-output", "memory-saved", "workflow-run",
|
|
850
|
+
"web-fetch", "web-search", "todo", "cron-create", "cron-delete", "cron-list", "image",
|
|
851
|
+
"task-stop", "tool-search", "memory-recall", "repo-map", "fork", "enter-plan-mode", "exit-plan-mode",
|
|
852
|
+
]);
|
|
853
|
+
const structuredFrom = (result) => {
|
|
854
|
+
const details = result !== null && typeof result === "object" ? result.details : undefined;
|
|
855
|
+
if (details === null || typeof details !== "object")
|
|
856
|
+
return undefined;
|
|
857
|
+
const t = details.type;
|
|
858
|
+
if (typeof t !== "string" || !CC_DETAIL_TYPES.has(t))
|
|
859
|
+
return undefined;
|
|
860
|
+
try {
|
|
861
|
+
const serialized = JSON.stringify(details) ?? "";
|
|
862
|
+
if (serialized.length > MAX_TOOL_END_OUTPUT_CHARS * 4)
|
|
863
|
+
return undefined;
|
|
864
|
+
}
|
|
865
|
+
catch {
|
|
866
|
+
return undefined;
|
|
867
|
+
}
|
|
868
|
+
return details;
|
|
869
|
+
};
|
|
870
|
+
const onMessageUpdate = (event) => {
|
|
871
|
+
const ev = event.assistantMessageEvent;
|
|
872
|
+
if (callStartAt === undefined)
|
|
873
|
+
callStartAt = Date.now();
|
|
874
|
+
if (ev.type === "text_delta" && ev.delta) {
|
|
875
|
+
if (firstTokenAt === undefined)
|
|
876
|
+
firstTokenAt = Date.now();
|
|
877
|
+
pushContent({ type: "text_delta", delta: ev.delta, ...ident() });
|
|
878
|
+
}
|
|
879
|
+
else if (ev.type === "thinking_delta" && ev.delta) {
|
|
880
|
+
pushContent({ type: "reasoning_delta", delta: ev.delta, ...ident() });
|
|
881
|
+
}
|
|
882
|
+
if (streamCancel && !budgetHit && (ev.type === "text_delta" || ev.type === "thinking_delta") && ev.delta) {
|
|
883
|
+
callOutputChars += ev.delta.length;
|
|
884
|
+
if (callOutputChars - lastStreamBudgetCheck >= 256) {
|
|
885
|
+
lastStreamBudgetCheck = callOutputChars;
|
|
886
|
+
if (projectedOverBudget()) {
|
|
887
|
+
budgetHit = "exceeded";
|
|
888
|
+
void prepared.harness.abort();
|
|
889
|
+
prepared.abortController.abort();
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
};
|
|
894
|
+
const onMessageEnd = (event) => {
|
|
895
|
+
const m = event.message;
|
|
896
|
+
if (event.entryId !== undefined && (m.role === "user" || m.role === "assistant" || m.role === "toolResult")) {
|
|
897
|
+
emitCommitted(event.entryId, m.role, m.role === "toolResult" ? m.toolCallId : undefined);
|
|
898
|
+
}
|
|
899
|
+
if (m.role === "assistant" && "usage" in m && m.usage) {
|
|
900
|
+
if (m.usageMissing)
|
|
901
|
+
turnUsageMissing = true;
|
|
902
|
+
const u = m.usage;
|
|
903
|
+
stats.tokens += u.totalTokens || 0;
|
|
904
|
+
const { totalInputTokens: turnInput, costMicroUsd: turnCostMicroUsd } = usageCostMicroUsd(cacheFamily, u, pricing);
|
|
905
|
+
stats.promptTokens += turnInput;
|
|
906
|
+
stats.cachedTokens += u.cacheRead || 0;
|
|
907
|
+
stats.cacheWriteTokens += u.cacheWrite || 0;
|
|
908
|
+
stats.outputTokens += u.output || 0;
|
|
909
|
+
stats.costUsd += u.cost?.total || 0;
|
|
910
|
+
stats.costMicroUsd += turnCostMicroUsd;
|
|
911
|
+
turnUsage ??= { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, costMicroUsd: 0 };
|
|
912
|
+
turnUsage.inputTokens += turnInput;
|
|
913
|
+
turnUsage.outputTokens += u.output || 0;
|
|
914
|
+
turnUsage.cacheReadTokens += u.cacheRead || 0;
|
|
915
|
+
turnUsage.cacheWriteTokens += u.cacheWrite || 0;
|
|
916
|
+
turnUsage.costMicroUsd += turnCostMicroUsd;
|
|
917
|
+
const callEndAt = Date.now();
|
|
918
|
+
const capLast = prepared.callCapRef?.state.last;
|
|
919
|
+
if (prepared.callCapRef) {
|
|
920
|
+
recordCallSample(prepared.callCapRef.state, u.output || 0, callStartAt !== undefined ? callEndAt - callStartAt : 0);
|
|
921
|
+
prepared.callCapRef.state.last = undefined;
|
|
922
|
+
}
|
|
923
|
+
emitTrace(tracer, () => ({
|
|
924
|
+
kind: "brain.call",
|
|
925
|
+
version: 1,
|
|
926
|
+
taskId,
|
|
927
|
+
model: m.model,
|
|
928
|
+
provider: m.provider,
|
|
929
|
+
promptTokens: turnInput,
|
|
930
|
+
completionTokens: u.output || 0,
|
|
931
|
+
cacheRead: u.cacheRead || 0,
|
|
932
|
+
cacheWrite: u.cacheWrite || 0,
|
|
933
|
+
latencyMs: callStartAt !== undefined ? callEndAt - callStartAt : 0,
|
|
934
|
+
firstTokenMs: callStartAt !== undefined && firstTokenAt !== undefined ? firstTokenAt - callStartAt : undefined,
|
|
935
|
+
costMicroUsd: turnCostMicroUsd,
|
|
936
|
+
...(capLast !== undefined ? { callCap: capLast.cap, capThinkingSkipped: capLast.thinkingSkipped } : {}),
|
|
937
|
+
...(m.role === "assistant" && typeof m.stopReason === "string"
|
|
938
|
+
? { stopReason: m.stopReason }
|
|
939
|
+
: {}),
|
|
940
|
+
ts: callEndAt,
|
|
941
|
+
}));
|
|
942
|
+
if (prepared.callCapRef &&
|
|
943
|
+
prepared.callCapRef.reasoningObserved !== true &&
|
|
944
|
+
capLast !== undefined &&
|
|
945
|
+
m.role === "assistant" &&
|
|
946
|
+
m.stopReason === "length") {
|
|
947
|
+
const blocks = Array.isArray(m.content)
|
|
948
|
+
? (m.content)
|
|
949
|
+
: [];
|
|
950
|
+
const productive = blocks.some((c) => c?.type === "toolCall") ||
|
|
951
|
+
blocks.some((c) => c?.type === "text" && typeof c.text === "string" && c.text.trim() !== "");
|
|
952
|
+
if (!productive)
|
|
953
|
+
prepared.callCapRef.reasoningObserved = true;
|
|
954
|
+
}
|
|
955
|
+
if (prepared.cacheBreakDetector && prepared.cacheFingerprint) {
|
|
956
|
+
const finding = prepared.cacheBreakDetector.observe({
|
|
957
|
+
turn: stats.turns + 1,
|
|
958
|
+
systemPrompt: prepared.cacheFingerprint.systemPrompt,
|
|
959
|
+
tools: prepared.cacheFingerprint.tools,
|
|
960
|
+
modelKey: `${m.provider}:${m.model}`,
|
|
961
|
+
cacheRead: u.cacheRead || 0,
|
|
962
|
+
});
|
|
963
|
+
if (finding) {
|
|
964
|
+
cacheBreakReported = true;
|
|
965
|
+
this.deps.onError?.(new Error(`prompt-cache: break at turn ${finding.turn} — cache_read ${finding.cacheReadBefore}→${finding.cacheReadAfter}; ${finding.detail}. See design/31.`), { phase: "prompt-cache", sessionId: prepared.sessionId, classification: finding.cause });
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
if (m.role === "assistant" && typeof m.stopReason === "string") {
|
|
970
|
+
turnStopReason = m.stopReason;
|
|
971
|
+
}
|
|
972
|
+
if (m.role === "assistant" && m.errorMessage === WALLTIME_CUTOFF_MESSAGE) {
|
|
973
|
+
callCutoffs += 1;
|
|
974
|
+
}
|
|
975
|
+
if (m.role === "assistant") {
|
|
976
|
+
const rep = m.repetition;
|
|
977
|
+
if (rep !== undefined) {
|
|
978
|
+
const observed = [
|
|
979
|
+
...(rep.cut !== undefined ? [{ action: "cut", e: rep.cut }] : []),
|
|
980
|
+
...(rep.spared ?? []).map((e) => ({ action: "spared", e })),
|
|
981
|
+
];
|
|
982
|
+
for (const { action, e } of observed) {
|
|
983
|
+
if (action === "cut")
|
|
984
|
+
repetitionCuts += 1;
|
|
985
|
+
else
|
|
986
|
+
repetitionSpared += 1;
|
|
987
|
+
if (repetitionEvents.length < REPETITION_EVENTS_CAP) {
|
|
988
|
+
repetitionEvents.push({ turn: stats.turns + 1, action, rule: e.rule, period: e.period, reps: e.reps, segment: e.segment });
|
|
989
|
+
}
|
|
990
|
+
emitTrace(tracer, () => ({
|
|
991
|
+
kind: "repetition.detected",
|
|
992
|
+
version: 1,
|
|
993
|
+
taskId,
|
|
994
|
+
turn: stats.turns + 1,
|
|
995
|
+
action,
|
|
996
|
+
rule: e.rule,
|
|
997
|
+
period: e.period,
|
|
998
|
+
reps: e.reps,
|
|
999
|
+
segment: e.segment,
|
|
1000
|
+
ts: Date.now(),
|
|
1001
|
+
}));
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
if (prepared.callCapRef &&
|
|
1006
|
+
prepared.callCapRef.reasoningObserved !== true &&
|
|
1007
|
+
m.role === "assistant" &&
|
|
1008
|
+
Array.isArray(m.content) &&
|
|
1009
|
+
m.content.some((c) => c?.type === "thinking")) {
|
|
1010
|
+
prepared.callCapRef.reasoningObserved = true;
|
|
1011
|
+
}
|
|
1012
|
+
if (attachState !== undefined && m.role === "assistant") {
|
|
1013
|
+
const content = m.content;
|
|
1014
|
+
cadenceTurns = advanceCadenceClock(attachState, cadenceTurns, content, writeFamilyOf);
|
|
1015
|
+
lastTurnHadToolCalls = Array.isArray(content) && content.some((c) => c?.type === "toolCall");
|
|
1016
|
+
}
|
|
1017
|
+
if (degraded === undefined && m.role === "assistant") {
|
|
1018
|
+
const deg = readDegradation(m);
|
|
1019
|
+
if (deg)
|
|
1020
|
+
recordDegraded({ from: deg.from, to: deg.to, reason: deg.reason, atTurn: stats.turns + 1, ...(deg.chain && { chain: deg.chain }) });
|
|
1021
|
+
}
|
|
1022
|
+
callStartAt = undefined;
|
|
1023
|
+
firstTokenAt = undefined;
|
|
1024
|
+
callOutputChars = 0;
|
|
1025
|
+
lastStreamBudgetCheck = 0;
|
|
1026
|
+
};
|
|
1027
|
+
const toolLabels = new Map(prepared.tools.flatMap((t) => (t.label !== undefined && t.label !== t.name ? [[t.name, t.label]] : [])));
|
|
1028
|
+
const postToolBatchHook = (spec.hooks ?? this.deps.hooks)?.postToolBatch;
|
|
1029
|
+
const batchArgs = postToolBatchHook ? new Map() : undefined;
|
|
1030
|
+
let toolBatch = [];
|
|
1031
|
+
const onToolStart = (event) => {
|
|
1032
|
+
toolStartAt.set(event.toolCallId, Date.now());
|
|
1033
|
+
batchArgs?.set(event.toolCallId, event.args);
|
|
1034
|
+
stats.toolCalls += 1;
|
|
1035
|
+
if (spec.finalVerification === true && !(finalVerifyInjections === 0 ? groundingSignalPreR9 : groundingSignalPostR9) && hasVerifiableStructureSignal(event.args)) {
|
|
1036
|
+
if (finalVerifyInjections === 0)
|
|
1037
|
+
groundingSignalPreR9 = true;
|
|
1038
|
+
else
|
|
1039
|
+
groundingSignalPostR9 = true;
|
|
1040
|
+
}
|
|
1041
|
+
const activityArg = primaryActivityArg(event.args);
|
|
1042
|
+
internals?.onActivity?.({ phase: "start", toolCallId: event.toolCallId, toolName: event.toolName, ...(activityArg !== undefined ? { arg: activityArg } : {}) });
|
|
1043
|
+
pushContent({
|
|
1044
|
+
type: "tool_start",
|
|
1045
|
+
toolCallId: event.toolCallId,
|
|
1046
|
+
toolName: event.toolName,
|
|
1047
|
+
...(toolLabels.get(event.toolName) !== undefined ? { label: toolLabels.get(event.toolName) } : {}),
|
|
1048
|
+
args: event.args,
|
|
1049
|
+
...ident(),
|
|
1050
|
+
});
|
|
1051
|
+
};
|
|
1052
|
+
const onToolEnd = (event) => {
|
|
1053
|
+
const toolStarted = toolStartAt.get(event.toolCallId);
|
|
1054
|
+
toolStartAt.delete(event.toolCallId);
|
|
1055
|
+
const toolNow = Date.now();
|
|
1056
|
+
if (prepared.callCapRef && toolStarted !== undefined) {
|
|
1057
|
+
recordToolSample(prepared.callCapRef.state, toolNow - toolStarted);
|
|
1058
|
+
}
|
|
1059
|
+
if (!wroteThisRun && (prepared.toolEffects.get(event.toolName) ?? "write") !== "read") {
|
|
1060
|
+
wroteThisRun = true;
|
|
1061
|
+
}
|
|
1062
|
+
if (attachState !== undefined && !event.isError) {
|
|
1063
|
+
const det = event.result?.details;
|
|
1064
|
+
if (det !== undefined)
|
|
1065
|
+
reduceToolEnd(attachState, det);
|
|
1066
|
+
}
|
|
1067
|
+
if (postToolBatchHook !== undefined) {
|
|
1068
|
+
if (prepared.blockedToolCalls.delete(event.toolCallId)) {
|
|
1069
|
+
batchArgs?.delete(event.toolCallId);
|
|
1070
|
+
}
|
|
1071
|
+
else {
|
|
1072
|
+
const digest = batchResponseDigest(event.result);
|
|
1073
|
+
toolBatch.push({
|
|
1074
|
+
toolName: event.toolName,
|
|
1075
|
+
input: batchArgs?.get(event.toolCallId),
|
|
1076
|
+
toolCallId: event.toolCallId,
|
|
1077
|
+
...(digest !== undefined ? { response: digest } : {}),
|
|
1078
|
+
isError: event.isError,
|
|
1079
|
+
});
|
|
1080
|
+
batchArgs?.delete(event.toolCallId);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
emitTrace(tracer, () => ({
|
|
1084
|
+
kind: "tool.call",
|
|
1085
|
+
version: 1,
|
|
1086
|
+
taskId,
|
|
1087
|
+
name: event.toolName,
|
|
1088
|
+
durationMs: toolStarted !== undefined ? toolNow - toolStarted : 0,
|
|
1089
|
+
ok: !event.isError,
|
|
1090
|
+
ts: toolNow,
|
|
1091
|
+
}));
|
|
1092
|
+
internals?.onActivity?.({ phase: "end", toolCallId: event.toolCallId, toolName: event.toolName, isError: event.isError });
|
|
1093
|
+
if (prepared.lspDiagnostics && !event.isError) {
|
|
1094
|
+
const name = canonicalToolName(event.toolName);
|
|
1095
|
+
if (name === "Edit" || name === "Write" || name === "NotebookEdit") {
|
|
1096
|
+
const d = event.result?.details;
|
|
1097
|
+
const p = typeof d?.filePath === "string" ? d.filePath : typeof d?.notebookPath === "string" ? d.notebookPath : undefined;
|
|
1098
|
+
if (p)
|
|
1099
|
+
prepared.lspDiagnostics.nudge(p);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
pushContent({
|
|
1103
|
+
type: "tool_end",
|
|
1104
|
+
toolCallId: event.toolCallId,
|
|
1105
|
+
toolName: event.toolName,
|
|
1106
|
+
...(toolLabels.get(event.toolName) !== undefined ? { label: toolLabels.get(event.toolName) } : {}),
|
|
1107
|
+
isError: event.isError,
|
|
1108
|
+
...(() => {
|
|
1109
|
+
const o = toolOutputFrom(event.result);
|
|
1110
|
+
return o !== undefined ? { output: o.output, ...(o.truncated ? { truncated: true } : {}) } : {};
|
|
1111
|
+
})(),
|
|
1112
|
+
...(() => {
|
|
1113
|
+
const st = structuredFrom(event.result);
|
|
1114
|
+
return st !== undefined ? { structured: st } : {};
|
|
1115
|
+
})(),
|
|
1116
|
+
...ident(),
|
|
1117
|
+
});
|
|
1118
|
+
const cwdNow = prepared.cwdRef?.current;
|
|
1119
|
+
if (cwdNow !== undefined && cwdNow !== lastWorkspaceCwd) {
|
|
1120
|
+
lastWorkspaceCwd = cwdNow;
|
|
1121
|
+
queue.push({ type: "workspace_changed", cwd: cwdNow, ...ident() });
|
|
1122
|
+
}
|
|
1123
|
+
if (spec.outputSchema && event.toolName === OUTPUT_TOOL_NAME) {
|
|
1124
|
+
if (event.isError) {
|
|
1125
|
+
outputErrorStreak += 1;
|
|
1126
|
+
if (outputErrorStreak > outputRetryCap) {
|
|
1127
|
+
outputInvalid = true;
|
|
1128
|
+
void prepared.harness.abort();
|
|
1129
|
+
prepared.abortController.abort();
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
else {
|
|
1133
|
+
outputErrorStreak = 0;
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
const onTurnEnd = () => {
|
|
1138
|
+
stats.turns += 1;
|
|
1139
|
+
const stopExtra = turnStopReason !== undefined ? { stopReason: turnStopReason } : {};
|
|
1140
|
+
queue.push(turnUsage
|
|
1141
|
+
? { type: "turn_end", usage: turnUsage, ...(turnUsageMissing ? { usageMissing: true } : {}), ...stopExtra, ...ident() }
|
|
1142
|
+
: { type: "turn_end", usageMissing: true, ...stopExtra, ...ident() });
|
|
1143
|
+
turnUsageMissing = false;
|
|
1144
|
+
turnStopReason = undefined;
|
|
1145
|
+
if (parentToolCallId !== undefined) {
|
|
1146
|
+
const progress = {
|
|
1147
|
+
type: "task_progress",
|
|
1148
|
+
taskId,
|
|
1149
|
+
...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
|
|
1150
|
+
...(subagentName ? { name: subagentName } : {}),
|
|
1151
|
+
usage: { totalTokens: stats.tokens, toolUses: stats.toolCalls, durationMs: Date.now() - taskStart },
|
|
1152
|
+
status: "running",
|
|
1153
|
+
...ident(),
|
|
1154
|
+
};
|
|
1155
|
+
queue.push(progress);
|
|
1156
|
+
try {
|
|
1157
|
+
internals?.onForwardEvent?.(progress);
|
|
1158
|
+
}
|
|
1159
|
+
catch {
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
emitTrace(tracer, () => ({ kind: "turn.end", version: 1, taskId, turn: stats.turns, ts: Date.now() }));
|
|
1163
|
+
turnUsage = undefined;
|
|
1164
|
+
callStartAt = undefined;
|
|
1165
|
+
firstTokenAt = undefined;
|
|
1166
|
+
callOutputChars = 0;
|
|
1167
|
+
lastStreamBudgetCheck = 0;
|
|
1168
|
+
toolStartAt.clear();
|
|
1169
|
+
if (effectiveMaxTurns !== undefined && effectiveMaxTurns > 0 && stats.turns >= effectiveMaxTurns) {
|
|
1170
|
+
turnsExceeded = true;
|
|
1171
|
+
if (prepared.suspendForResource === undefined) {
|
|
1172
|
+
void prepared.harness.abort();
|
|
1173
|
+
prepared.abortController.abort();
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
else {
|
|
1177
|
+
if (degraded === undefined &&
|
|
1178
|
+
spec.degrade &&
|
|
1179
|
+
degradeToModel &&
|
|
1180
|
+
maxCostMicroUsd !== undefined &&
|
|
1181
|
+
stats.costMicroUsd >= spec.degrade.atCostFraction * maxCostMicroUsd) {
|
|
1182
|
+
void prepared.harness.setModel(degradeToModel);
|
|
1183
|
+
recordDegraded({ from: prepared.model.id, to: degradeToModel.id, reason: "budget", atTurn: stats.turns }, degradeToModel);
|
|
1184
|
+
if (prepared.callCapRef &&
|
|
1185
|
+
spec.limits?.maxOutputTokens === undefined &&
|
|
1186
|
+
degradeToModel.maxTokens !== undefined &&
|
|
1187
|
+
(prepared.callCapRef.staticCapTokens === undefined || degradeToModel.maxTokens < prepared.callCapRef.staticCapTokens)) {
|
|
1188
|
+
prepared.callCapRef.staticCapTokens = degradeToModel.maxTokens;
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
if (!budgetHit && overBudget()) {
|
|
1192
|
+
budgetHit = "exceeded";
|
|
1193
|
+
if (prepared.suspendForResource === undefined) {
|
|
1194
|
+
void prepared.harness.abort();
|
|
1195
|
+
prepared.abortController.abort();
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
};
|
|
1200
|
+
const recordCompactionUsage = (m, msg) => {
|
|
1201
|
+
const u = msg?.usage;
|
|
1202
|
+
if (!u)
|
|
1203
|
+
return;
|
|
1204
|
+
const fam = cacheFamilyOf(m);
|
|
1205
|
+
const price = this.deps.pricing?.[m.id] ?? modelCostToPricing(m.cost);
|
|
1206
|
+
const { totalInputTokens, costMicroUsd } = usageCostMicroUsd(fam, u, price);
|
|
1207
|
+
stats.tokens += u.totalTokens || 0;
|
|
1208
|
+
stats.promptTokens += totalInputTokens;
|
|
1209
|
+
stats.cachedTokens += u.cacheRead || 0;
|
|
1210
|
+
stats.cacheWriteTokens += u.cacheWrite || 0;
|
|
1211
|
+
stats.outputTokens += u.output || 0;
|
|
1212
|
+
stats.costUsd += u.cost?.total || 0;
|
|
1213
|
+
stats.costMicroUsd += costMicroUsd;
|
|
1214
|
+
stats.compactionMicroUsd = (stats.compactionMicroUsd ?? 0) + costMicroUsd;
|
|
1215
|
+
emitTrace(tracer, () => ({
|
|
1216
|
+
kind: "brain.call", version: 1, taskId, model: m.id, provider: m.provider,
|
|
1217
|
+
promptTokens: totalInputTokens, completionTokens: u.output || 0,
|
|
1218
|
+
cacheRead: u.cacheRead || 0, cacheWrite: u.cacheWrite || 0,
|
|
1219
|
+
latencyMs: 0, costMicroUsd, ...(typeof msg?.stopReason === "string" ? { stopReason: msg.stopReason } : {}), ts: Date.now(),
|
|
1220
|
+
}));
|
|
1221
|
+
};
|
|
1222
|
+
const compactionBrain = {
|
|
1223
|
+
stream: this.deps.brain.stream,
|
|
1224
|
+
complete: async (m, c, o) => {
|
|
1225
|
+
const msg = await runWithStatusSink(() => { }, async () => this.deps.brain.complete
|
|
1226
|
+
? await this.deps.brain.complete(m, c, o)
|
|
1227
|
+
: await (await Promise.resolve(this.deps.brain.stream(m, c, o))).result());
|
|
1228
|
+
recordCompactionUsage(m, msg);
|
|
1229
|
+
return msg;
|
|
1230
|
+
},
|
|
1231
|
+
};
|
|
1232
|
+
const withinTaskCompaction = (spec.compaction?.enabled ?? true) && (spec.compaction?.withinTask ?? true);
|
|
1233
|
+
const compactionBreaker = { failures: 0 };
|
|
1234
|
+
const stopHook = (spec.hooks ?? this.deps.hooks)?.stop;
|
|
1235
|
+
const finalVerificationOn = spec.finalVerification === true;
|
|
1236
|
+
if (stopHook || finalVerificationOn) {
|
|
1237
|
+
let consecutiveBlocks = 0;
|
|
1238
|
+
prepared.harness.setStopGate(async () => {
|
|
1239
|
+
if (prepared.abortController.signal.aborted || prepared.suspendRef.token !== undefined)
|
|
1240
|
+
return [];
|
|
1241
|
+
if (finalVerificationOn &&
|
|
1242
|
+
(finalVerifyInjections === 0 || (finalVerifyInjections === 1 && groundingSignalPreR9 && !groundingSignalPostR9)) &&
|
|
1243
|
+
wroteThisRun &&
|
|
1244
|
+
prepared.outputRef.set !== true &&
|
|
1245
|
+
!finalizeInjected &&
|
|
1246
|
+
!(effectiveMaxTurns !== undefined && effectiveMaxTurns > 0 && stats.turns >= effectiveMaxTurns - 1)) {
|
|
1247
|
+
finalVerifyInjections += 1;
|
|
1248
|
+
if (finalVerifyInjections === 2) {
|
|
1249
|
+
return [
|
|
1250
|
+
{
|
|
1251
|
+
role: "user",
|
|
1252
|
+
content: "<system-reminder>[final verification] Your tool calls in this run worked with raw bytes, structural parsing, " +
|
|
1253
|
+
"or checksum/digest computation — the deliverable very likely embeds verifiable structure (structural fields, an " +
|
|
1254
|
+
"embedded checksum-family value, reference data it must match, or a replayable deterministic path). You MUST " +
|
|
1255
|
+
"execute the grounding check that structure supports — recompute the embedded value and compare it against the " +
|
|
1256
|
+
"declared one, re-parse the structure from the raw bytes and reconcile it with your output, compare against the " +
|
|
1257
|
+
"reference data, or replay the deterministic path — and REPORT the check's concrete result before finishing. " +
|
|
1258
|
+
"A closing statement without a reported check result is not verification. If you already ran such a check, state " +
|
|
1259
|
+
"its concrete result now; if the check mismatches, fix the deliverable first. This is the final reminder from " +
|
|
1260
|
+
"this verification gate — it will not intervene again.</system-reminder>",
|
|
1261
|
+
timestamp: Date.now(),
|
|
1262
|
+
},
|
|
1263
|
+
];
|
|
1264
|
+
}
|
|
1265
|
+
return [
|
|
1266
|
+
{
|
|
1267
|
+
role: "user",
|
|
1268
|
+
content: "<system-reminder>[final verification] Before finishing: re-verify the FINAL deliverable through its REAL entry point, " +
|
|
1269
|
+
"exactly as the acceptance criteria would exercise it — execute the binary/function/endpoint directly and read the ACTUAL " +
|
|
1270
|
+
"output and exit code. Do NOT rely on earlier self-tests, shell redirections, or assumptions (a program that prints to " +
|
|
1271
|
+
"stdout is not a program that writes the required file). If anything mismatches the task's requirements, fix it before " +
|
|
1272
|
+
"finishing. " +
|
|
1273
|
+
"Treat verification writes as state-harmless: when the deliverable itself is a persisted final " +
|
|
1274
|
+
"state (for example a committed or pushed file, a deployed artifact, or a required output file), " +
|
|
1275
|
+
"do not change that state merely to test it. This constrains HOW you verify — it is never a " +
|
|
1276
|
+
"license to skip the real acceptance path or to check a substitute of your own making: expected " +
|
|
1277
|
+
"values must come from the task's requirements, never from content you generated. If the real " +
|
|
1278
|
+
"acceptance path requires a write, use disposable inputs or an isolated target, end in the exact " +
|
|
1279
|
+
"required final state, and verify that final state before finishing. " +
|
|
1280
|
+
"If the work relied on a third-party API, library, or model, check the usage contract the object itself declares " +
|
|
1281
|
+
"(docstrings, metadata, configuration — e.g. prompt conventions shipped with a model) and confirm your calls follow " +
|
|
1282
|
+
"it rather than a default symmetric usage. Verify not only that the deliverable EXISTS but that the METHOD that " +
|
|
1283
|
+
"produced it matches the task's requirements. " +
|
|
1284
|
+
"Choose the verification SURFACE deliberately: check against the reference data, oracle, or evaluation tooling the " +
|
|
1285
|
+
"task itself provides — re-running your own implementation and getting the same answer is self-consistency, not " +
|
|
1286
|
+
"correctness — and cross-check through an independent second path where the task or environment offers one " +
|
|
1287
|
+
"(checksums, runtime artifacts). Verify the PERSISTED artifact — re-read what is actually on disk or committed, " +
|
|
1288
|
+
"not in-memory state — against every hard constraint from the original task text (numeric bounds, allowed-value " +
|
|
1289
|
+
"lists, naming semantics, required files), reconciling whole-set completeness: nothing missing, nothing duplicated. " +
|
|
1290
|
+
"If the deliverable embeds verifiable structure — structural fields, an embedded checksum-family value, " +
|
|
1291
|
+
"reference data it must match, or a replayable deterministic path — you MUST execute the grounding check " +
|
|
1292
|
+
"that structure supports and REPORT its concrete result in your closing summary: for such a deliverable, " +
|
|
1293
|
+
"no reported check result means the work is not finished. " +
|
|
1294
|
+
"If the task produced neither an executable deliverable nor any verifiable structure or acceptance " +
|
|
1295
|
+
"oracle to check against, briefly confirm completion and stop.</system-reminder>",
|
|
1296
|
+
timestamp: Date.now(),
|
|
1297
|
+
},
|
|
1298
|
+
];
|
|
1299
|
+
}
|
|
1300
|
+
if (!stopHook)
|
|
1301
|
+
return [];
|
|
1302
|
+
let result;
|
|
1303
|
+
try {
|
|
1304
|
+
result = await stopHook({ stopHookActive: consecutiveBlocks > 0, consecutiveBlocks });
|
|
1305
|
+
}
|
|
1306
|
+
catch (err) {
|
|
1307
|
+
this.deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "hook", sessionId: prepared.sessionId });
|
|
1308
|
+
return [];
|
|
1309
|
+
}
|
|
1310
|
+
const messages = [];
|
|
1311
|
+
if (result?.additionalContext) {
|
|
1312
|
+
messages.push({
|
|
1313
|
+
role: "user",
|
|
1314
|
+
content: formatHookFeedback(`Stop hook additional context: ${result.additionalContext}`),
|
|
1315
|
+
timestamp: Date.now(),
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
if (!result?.block) {
|
|
1319
|
+
consecutiveBlocks = 0;
|
|
1320
|
+
return messages;
|
|
1321
|
+
}
|
|
1322
|
+
consecutiveBlocks++;
|
|
1323
|
+
const cap = STOP_HOOK_BLOCK_CAP;
|
|
1324
|
+
if (consecutiveBlocks > cap) {
|
|
1325
|
+
this.deps.onError?.(new Error(`a Stop hook blocked the turn from ending ${consecutiveBlocks} consecutive times — overriding and ending the run. ` +
|
|
1326
|
+
`Check ctx.stopHookActive in the hook and return success while it's true.`), { phase: "hook", sessionId: prepared.sessionId });
|
|
1327
|
+
return [];
|
|
1328
|
+
}
|
|
1329
|
+
messages.push({ role: "user", content: formatHookFeedback(`Stop hook stopped continuation: ${result.block}`), timestamp: Date.now() });
|
|
1330
|
+
return messages;
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
prepared.harness.setLoopRecovery({
|
|
1334
|
+
truncatedOutput: {},
|
|
1335
|
+
malformedToolUse: {},
|
|
1336
|
+
thinkingOnly: {},
|
|
1337
|
+
degenerateOutput: { detect: (m) => m.errorMessage === DEGENERATE_MESSAGE },
|
|
1338
|
+
walltimeCutoff: { detect: (m) => m.errorMessage === WALLTIME_CUTOFF_MESSAGE },
|
|
1339
|
+
promptTooLong: {
|
|
1340
|
+
recover: async () => {
|
|
1341
|
+
if (!(spec.compaction?.enabled ?? true))
|
|
1342
|
+
return false;
|
|
1343
|
+
if (compactionBreaker.failures >= MAX_CONSECUTIVE_COMPACTION_FAILURES)
|
|
1344
|
+
return false;
|
|
1345
|
+
if (prepared.abortController.signal.aborted || prepared.suspendRef.token !== undefined)
|
|
1346
|
+
return false;
|
|
1347
|
+
try {
|
|
1348
|
+
const comp = await maybeCompact({
|
|
1349
|
+
session: prepared.session,
|
|
1350
|
+
model: prepared.harness.getModel(),
|
|
1351
|
+
compactionModel: prepared.compModel,
|
|
1352
|
+
brain: compactionBrain,
|
|
1353
|
+
getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
|
|
1354
|
+
thinking: prepared.thinking,
|
|
1355
|
+
settings: { ...DEFAULT_COMPACTION_SETTINGS, ...spec.compaction },
|
|
1356
|
+
customInstructions: spec.compaction?.instructions ?? DEFAULT_COMPACTION_INSTRUCTIONS,
|
|
1357
|
+
signal: prepared.abortController.signal,
|
|
1358
|
+
minTokens: 0,
|
|
1359
|
+
force: true,
|
|
1360
|
+
overheadTokens: prepared.promptOverheadTokens,
|
|
1361
|
+
onInputTruncated: (info) => emitTrace(tracer, () => ({ kind: "compaction.input_truncated", version: 1, taskId, label: info.label, droppedChars: info.droppedChars, keptChars: info.keptChars, ts: Date.now() })),
|
|
1362
|
+
workingFileAttachments: spec.compaction?.attachWorkingFiles !== false && prepared.readTaskFile
|
|
1363
|
+
? {
|
|
1364
|
+
readFile: prepared.readTaskFile,
|
|
1365
|
+
...(prepared.recentlyReadFiles ? { recentlyReadFiles: prepared.recentlyReadFiles } : {}),
|
|
1366
|
+
...(typeof spec.compaction?.attachWorkingFiles === "object" ? spec.compaction.attachWorkingFiles : undefined),
|
|
1367
|
+
}
|
|
1368
|
+
: undefined,
|
|
1369
|
+
...this.seamCCompactionOptions(prepared),
|
|
1370
|
+
...this.compactionHookOptions(spec, prepared.sessionId, "forced"),
|
|
1371
|
+
});
|
|
1372
|
+
if (comp.compacted) {
|
|
1373
|
+
compactionBreaker.failures = 0;
|
|
1374
|
+
this.recordCompactionReuse(prepared, comp);
|
|
1375
|
+
if ((comp.freedTokens ?? 0) >= COMPACTION_FREED_EPSILON) {
|
|
1376
|
+
const postSize = comp.postTriggerTokens ?? Math.max(0, (comp.triggerTokens ?? comp.tokensBefore ?? 0) - (comp.freedTokens ?? 0));
|
|
1377
|
+
compactionFloor = Math.ceil(postSize * COMPACTION_REGROWTH_FACTOR);
|
|
1378
|
+
}
|
|
1379
|
+
queue.push({
|
|
1380
|
+
type: "compacted",
|
|
1381
|
+
trigger: "auto",
|
|
1382
|
+
tokensBefore: comp.tokensBefore ?? 0,
|
|
1383
|
+
...(comp.postTriggerTokens !== undefined ? { tokensAfter: comp.postTriggerTokens } : {}),
|
|
1384
|
+
...(comp.triggerTokens !== undefined ? { triggerTokensBefore: comp.triggerTokens } : {}),
|
|
1385
|
+
...(comp.durationMs !== undefined ? { durationMs: comp.durationMs } : {}),
|
|
1386
|
+
...(comp.firstKeptEntryId !== undefined ? { preserved_segment: { firstKeptEntryId: comp.firstKeptEntryId } } : {}),
|
|
1387
|
+
...(comp.attachedFiles !== undefined ? { attachedFiles: comp.attachedFiles } : {}),
|
|
1388
|
+
});
|
|
1389
|
+
prepared.cacheBreakDetector?.notifyCompaction();
|
|
1390
|
+
if (attachState !== undefined) {
|
|
1391
|
+
attachState.postCompactPending = true;
|
|
1392
|
+
rebaseCadenceWindows(attachState, cadenceTurns);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
else {
|
|
1396
|
+
compactionBreaker.failures += 1;
|
|
1397
|
+
}
|
|
1398
|
+
return comp.compacted === true;
|
|
1399
|
+
}
|
|
1400
|
+
catch {
|
|
1401
|
+
compactionBreaker.failures += 1;
|
|
1402
|
+
return false;
|
|
1403
|
+
}
|
|
1404
|
+
},
|
|
1405
|
+
},
|
|
1406
|
+
});
|
|
1407
|
+
const rapidRefill = createRapidRefillState();
|
|
1408
|
+
let compactionFloor = 0;
|
|
1409
|
+
let trimForceBackoff = false;
|
|
1410
|
+
const drainManualCompact = (outcome) => {
|
|
1411
|
+
manualCompactRef.requested = false;
|
|
1412
|
+
for (const w of manualCompactRef.waiters.splice(0))
|
|
1413
|
+
w(outcome);
|
|
1414
|
+
};
|
|
1415
|
+
const onTurnBoundary = async (event) => {
|
|
1416
|
+
let boundarySteered = false;
|
|
1417
|
+
if (prepared.suspendForResource === undefined) {
|
|
1418
|
+
const nowMs = Date.now();
|
|
1419
|
+
if (!finalizeInjected &&
|
|
1420
|
+
spec.limits?.gracefulFinalize !== false &&
|
|
1421
|
+
walltimeDeadlineMs !== undefined &&
|
|
1422
|
+
prepared.callCapRef !== undefined) {
|
|
1423
|
+
const remainingMs = walltimeDeadlineMs - nowMs;
|
|
1424
|
+
if (remainingMs > 0 && remainingMs < estimateCycleMs(prepared.callCapRef.state) + (prepared.callCapRef.cushionMs ?? 0)) {
|
|
1425
|
+
finalizeInjected = true;
|
|
1426
|
+
prepared.callCapRef.finalizeMode = true;
|
|
1427
|
+
nudgeIdx = nudgeSchedule.length;
|
|
1428
|
+
const finalizeText = `<system-reminder>[deadline] the wall-clock budget is exhausted — this is the FINAL turn. ` +
|
|
1429
|
+
`Write your best current work to the deliverable path NOW, even if incomplete — persist to disk FIRST; everything else comes after. ` +
|
|
1430
|
+
`If a better version exists anywhere else (a draft, scratch or temp file), copy it to the deliverable path now. ` +
|
|
1431
|
+
`Do NOT open new work, new investigations, or long commands; write out and stop.` +
|
|
1432
|
+
(finalVerificationOn
|
|
1433
|
+
? ` After writing, if seconds permit, confirm the deliverable file actually EXISTS at the required path with your latest content.`
|
|
1434
|
+
: "") +
|
|
1435
|
+
`</system-reminder>`;
|
|
1436
|
+
void prepared.harness.steer(finalizeText).catch(() => { });
|
|
1437
|
+
boundarySteered = true;
|
|
1438
|
+
queue.push({ type: "steering_injected", source: "finalize", preview: finalizeText.slice(0, 220), ...ident() });
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
let due;
|
|
1442
|
+
while (nudgeIdx < nudgeSchedule.length && nowMs >= nudgeSchedule[nudgeIdx].atMs) {
|
|
1443
|
+
due = nudgeSchedule[nudgeIdx++];
|
|
1444
|
+
}
|
|
1445
|
+
if (due) {
|
|
1446
|
+
void prepared.harness.steer(due.text).catch(() => { });
|
|
1447
|
+
nudgesSent += 1;
|
|
1448
|
+
boundarySteered = true;
|
|
1449
|
+
queue.push({ type: "steering_injected", source: "deadline_nudge", preview: due.text.slice(0, 220), ...ident() });
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
const walltimeHit = walltimeDeadlineMs !== undefined && Date.now() >= walltimeDeadlineMs;
|
|
1453
|
+
const preemptHit = spec.preemptSignal?.aborted === true;
|
|
1454
|
+
if (preemptHit && !prepared.suspendForResource && !preemptIgnoredReported) {
|
|
1455
|
+
preemptIgnoredReported = true;
|
|
1456
|
+
emitTrace(tracer, () => ({ kind: "preempt.ignored", version: 1, taskId, ts: Date.now() }));
|
|
1457
|
+
}
|
|
1458
|
+
if (prepared.suspendForResource &&
|
|
1459
|
+
(turnsExceeded || budgetHit !== undefined || walltimeHit || preemptHit) &&
|
|
1460
|
+
prepared.suspendRef.token === undefined) {
|
|
1461
|
+
const reason = preemptHit
|
|
1462
|
+
? "preempt"
|
|
1463
|
+
: budgetHit !== undefined
|
|
1464
|
+
? "budget"
|
|
1465
|
+
: walltimeHit
|
|
1466
|
+
? "walltime"
|
|
1467
|
+
: "turns";
|
|
1468
|
+
const sliceSpend = { costMicroUsd: stats.costMicroUsd, tokens: stats.tokens, turns: stats.turns };
|
|
1469
|
+
if (await prepared.suspendForResource(reason, sliceSpend)) {
|
|
1470
|
+
turnsExceeded = false;
|
|
1471
|
+
budgetHit = undefined;
|
|
1472
|
+
}
|
|
1473
|
+
else {
|
|
1474
|
+
void prepared.harness.abort();
|
|
1475
|
+
prepared.abortController.abort();
|
|
1476
|
+
}
|
|
1477
|
+
return undefined;
|
|
1478
|
+
}
|
|
1479
|
+
if (prepared.reviewRequestRef.pending !== undefined) {
|
|
1480
|
+
const { reason } = prepared.reviewRequestRef.pending;
|
|
1481
|
+
prepared.reviewRequestRef.pending = undefined;
|
|
1482
|
+
const honorable = prepared.suspendForReview !== undefined &&
|
|
1483
|
+
!prepared.abortController.signal.aborted &&
|
|
1484
|
+
prepared.suspendRef.token === undefined &&
|
|
1485
|
+
prepared.reviewRef.token === undefined;
|
|
1486
|
+
if (honorable && prepared.suspendForReview) {
|
|
1487
|
+
await prepared.suspendForReview(reason);
|
|
1488
|
+
return undefined;
|
|
1489
|
+
}
|
|
1490
|
+
emitTrace(tracer, () => ({ kind: "review.dropped", version: 1, taskId, ts: Date.now() }));
|
|
1491
|
+
}
|
|
1492
|
+
if (prepared.lspDiagnostics && !prepared.lspDiagnostics.registry.isEmpty() && !prepared.abortController.signal.aborted) {
|
|
1493
|
+
const files = prepared.lspDiagnostics.registry.drain();
|
|
1494
|
+
if (files.length > 0) {
|
|
1495
|
+
queue.push({ type: "diagnostics", files, isNew: true, ...ident() });
|
|
1496
|
+
const block = formatDiagnosticsBlock(files);
|
|
1497
|
+
void prepared.harness.steer(block).catch(() => undefined);
|
|
1498
|
+
boundarySteered = true;
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
let boundaryAttachmentBytes = 0;
|
|
1502
|
+
let attachmentsPayload;
|
|
1503
|
+
if (attachState !== undefined &&
|
|
1504
|
+
attachmentsCfg !== undefined &&
|
|
1505
|
+
!boundarySteered &&
|
|
1506
|
+
!finalizeInjected &&
|
|
1507
|
+
finalVerifyInjections === 0 &&
|
|
1508
|
+
!prepared.abortController.signal.aborted) {
|
|
1509
|
+
const changedFilesOn = Boolean(attachmentsCfg.changedFiles);
|
|
1510
|
+
let changed;
|
|
1511
|
+
if (changedFilesOn && prepared.detectExternalChanges !== undefined) {
|
|
1512
|
+
const maxFiles = typeof attachmentsCfg.changedFiles === "object"
|
|
1513
|
+
? (attachmentsCfg.changedFiles.maxFiles ?? CHANGED_FILES_MAX)
|
|
1514
|
+
: CHANGED_FILES_MAX;
|
|
1515
|
+
const SCAN_CEILING_MS = 2_000;
|
|
1516
|
+
const scan = await Promise.race([
|
|
1517
|
+
prepared.detectExternalChanges(maxFiles),
|
|
1518
|
+
new Promise((resolve) => {
|
|
1519
|
+
const t = setTimeout(() => resolve({ changed: [], evicted: [] }), SCAN_CEILING_MS);
|
|
1520
|
+
t.unref?.();
|
|
1521
|
+
}),
|
|
1522
|
+
]);
|
|
1523
|
+
for (const p of scan.evicted)
|
|
1524
|
+
attachState.surfacedMtime.delete(p);
|
|
1525
|
+
changed = scan.changed;
|
|
1526
|
+
}
|
|
1527
|
+
const backgroundTasksOn = attachmentsCfg.backgroundTasks === true;
|
|
1528
|
+
const bgTasks = backgroundTasksOn && attachState.postCompactPending ? prepared.listBackgroundTasks() : undefined;
|
|
1529
|
+
const toolsDeltaOn = attachmentsCfg.toolsDelta === true;
|
|
1530
|
+
const pendingTools = toolsDeltaOn && prepared.toolsDeltaRef !== undefined && prepared.toolsDeltaRef.pending.length > 0
|
|
1531
|
+
? [...prepared.toolsDeltaRef.pending]
|
|
1532
|
+
: undefined;
|
|
1533
|
+
const agentListingOn = attachmentsCfg.agentListing === true;
|
|
1534
|
+
const mcpInstructionsOn = attachmentsCfg.mcpInstructions === true;
|
|
1535
|
+
const mcpDelta = prepared.mcp.instructionsDelta;
|
|
1536
|
+
const mcpAdds = mcpInstructionsOn && mcpDelta.pendingAdds.length > 0 ? [...mcpDelta.pendingAdds] : undefined;
|
|
1537
|
+
const mcpRemovals = mcpInstructionsOn && mcpDelta.pendingRemovals.length > 0 ? [...mcpDelta.pendingRemovals] : undefined;
|
|
1538
|
+
const budgetUsdOn = attachmentsCfg.budgetUsd === true && maxCostMicroUsd !== undefined && lastTurnHadToolCalls;
|
|
1539
|
+
const due = collectDueAttachments(attachState, {
|
|
1540
|
+
turn: stats.turns,
|
|
1541
|
+
cadenceTurn: cadenceTurns,
|
|
1542
|
+
todoMounted: todoToolMounted,
|
|
1543
|
+
taskListMounted: taskToolsMounted,
|
|
1544
|
+
planActive: prepared.planModeRef.active,
|
|
1545
|
+
config: {
|
|
1546
|
+
todoReminder: attachmentsCfg.todoReminder === true,
|
|
1547
|
+
...(attachmentsCfg.todoReminderMode !== undefined ? { todoReminderMode: attachmentsCfg.todoReminderMode } : {}),
|
|
1548
|
+
changedFiles: changedFilesOn,
|
|
1549
|
+
planModeReminder: attachmentsCfg.planModeReminder === true,
|
|
1550
|
+
budgetUsd: budgetUsdOn,
|
|
1551
|
+
backgroundTasks: backgroundTasksOn,
|
|
1552
|
+
toolsDelta: toolsDeltaOn,
|
|
1553
|
+
agentListing: agentListingOn,
|
|
1554
|
+
mcpInstructions: mcpInstructionsOn,
|
|
1555
|
+
},
|
|
1556
|
+
...(changed !== undefined ? { changedFiles: changed } : {}),
|
|
1557
|
+
...(budgetUsdOn && maxCostMicroUsd !== undefined
|
|
1558
|
+
? { budgetUsd: { used: stats.costMicroUsd / 1e6, total: maxCostMicroUsd / 1e6 } }
|
|
1559
|
+
: {}),
|
|
1560
|
+
...(bgTasks !== undefined ? { backgroundTasks: bgTasks } : {}),
|
|
1561
|
+
...(pendingTools !== undefined ? { newTools: pendingTools } : {}),
|
|
1562
|
+
...(agentListingOn && prepared.agentListing !== undefined
|
|
1563
|
+
? { agentListing: prepared.agentListing.entries, agentToolName: prepared.agentListing.toolName }
|
|
1564
|
+
: {}),
|
|
1565
|
+
...(mcpAdds !== undefined || mcpRemovals !== undefined
|
|
1566
|
+
? { mcpInstructions: { added: mcpAdds ?? [], removed: mcpRemovals ?? [] } }
|
|
1567
|
+
: {}),
|
|
1568
|
+
});
|
|
1569
|
+
if (pendingTools !== undefined && due.some((a) => a.source === "tools_delta" && a.body === renderToolsDelta(pendingTools))) {
|
|
1570
|
+
prepared.toolsDeltaRef.pending.splice(0, pendingTools.length);
|
|
1571
|
+
}
|
|
1572
|
+
if ((mcpAdds !== undefined || mcpRemovals !== undefined) &&
|
|
1573
|
+
due.some((a) => a.source === "mcp_instructions" && a.body === renderMcpInstructionsDelta(mcpAdds ?? [], mcpRemovals ?? []))) {
|
|
1574
|
+
if (mcpAdds !== undefined)
|
|
1575
|
+
mcpDelta.pendingAdds.splice(0, mcpAdds.length);
|
|
1576
|
+
if (mcpRemovals !== undefined)
|
|
1577
|
+
mcpDelta.pendingRemovals.splice(0, mcpRemovals.length);
|
|
1578
|
+
}
|
|
1579
|
+
if (agentListingOn && prepared.agentListing !== undefined && due.some((a) => a.source === "agent_listing")) {
|
|
1580
|
+
const listing = prepared.agentListing;
|
|
1581
|
+
const intactBody = renderAgentListingDelta(attachState, listing.entries, listing.toolName);
|
|
1582
|
+
if (intactBody !== undefined && due.some((a) => a.source === "agent_listing" && a.body === intactBody)) {
|
|
1583
|
+
commitAgentListing(attachState, listing.entries);
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
if (due.length > 0) {
|
|
1587
|
+
attachmentsPayload = due.map((a) => `<system-reminder>\n${sanitizeUntrustedText(a.body)}\n</system-reminder>`).join("\n");
|
|
1588
|
+
for (const a of due)
|
|
1589
|
+
boundaryAttachmentBytes += Buffer.byteLength(a.body, "utf8");
|
|
1590
|
+
attachmentsInjected += due.length;
|
|
1591
|
+
for (const a of due) {
|
|
1592
|
+
queue.push({ type: "steering_injected", source: a.source, preview: a.body.slice(0, 220), ...ident() });
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
let batchContextBlock;
|
|
1597
|
+
if (postToolBatchHook !== undefined && toolBatch.length > 0) {
|
|
1598
|
+
const batch = toolBatch;
|
|
1599
|
+
toolBatch = [];
|
|
1600
|
+
batchArgs?.clear();
|
|
1601
|
+
if (!boundarySteered && !finalizeInjected && finalVerifyInjections === 0 && !prepared.abortController.signal.aborted) {
|
|
1602
|
+
try {
|
|
1603
|
+
const r = await postToolBatchHook(batch);
|
|
1604
|
+
if (r?.additionalContext) {
|
|
1605
|
+
const body = sanitizeUntrustedText(r.additionalContext);
|
|
1606
|
+
const budget = ATTACHMENT_BYTE_CAP - boundaryAttachmentBytes;
|
|
1607
|
+
if (budget >= BATCH_CONTEXT_MIN_KEEP_BYTES) {
|
|
1608
|
+
const clipped = clipToByteBudget(body, budget) === body
|
|
1609
|
+
? body
|
|
1610
|
+
: `${clipToByteBudget(body, budget - Buffer.byteLength(BATCH_TRUNCATION_MARKER, "utf8"))}${BATCH_TRUNCATION_MARKER}`;
|
|
1611
|
+
batchContextBlock = `<system-reminder>\n${clipped}\n</system-reminder>`;
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
catch (err) {
|
|
1616
|
+
try {
|
|
1617
|
+
this.deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "hook", sessionId: prepared.sessionId });
|
|
1618
|
+
}
|
|
1619
|
+
catch {
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
if (attachmentsPayload !== undefined || batchContextBlock !== undefined) {
|
|
1625
|
+
const payload = attachmentsPayload !== undefined && batchContextBlock !== undefined ? `${attachmentsPayload}\n${batchContextBlock}` : (attachmentsPayload ?? batchContextBlock);
|
|
1626
|
+
void prepared.harness.steer(payload).catch(() => { });
|
|
1627
|
+
}
|
|
1628
|
+
const forceManual = manualCompactRef.requested;
|
|
1629
|
+
if ((!withinTaskCompaction && !forceManual) ||
|
|
1630
|
+
(finalizeInjected && !forceManual) ||
|
|
1631
|
+
prepared.abortController.signal.aborted ||
|
|
1632
|
+
prepared.suspendRef.token !== undefined ||
|
|
1633
|
+
prepared.reviewRef.token !== undefined) {
|
|
1634
|
+
if (forceManual) {
|
|
1635
|
+
emitTrace(tracer, () => ({ kind: "compaction.mooted", version: 1, taskId, reason: "task_ending", ts: Date.now() }));
|
|
1636
|
+
drainManualCompact("mooted");
|
|
1637
|
+
}
|
|
1638
|
+
return undefined;
|
|
1639
|
+
}
|
|
1640
|
+
if (compactionBreaker.failures >= MAX_CONSECUTIVE_COMPACTION_FAILURES) {
|
|
1641
|
+
if (forceManual) {
|
|
1642
|
+
emitTrace(tracer, () => ({
|
|
1643
|
+
kind: "compaction.failed",
|
|
1644
|
+
version: 1,
|
|
1645
|
+
taskId,
|
|
1646
|
+
trigger: "manual",
|
|
1647
|
+
reason: "compaction circuit breaker open (3 consecutive failures this task) — manual request drained without an attempt",
|
|
1648
|
+
ts: Date.now(),
|
|
1649
|
+
}));
|
|
1650
|
+
drainManualCompact("failed");
|
|
1651
|
+
}
|
|
1652
|
+
return undefined;
|
|
1653
|
+
}
|
|
1654
|
+
const trimPressureArmed = prepared.trimPressureRef.droppedMessages;
|
|
1655
|
+
if (trimPressureArmed)
|
|
1656
|
+
prepared.trimPressureRef.droppedMessages = false;
|
|
1657
|
+
const trimPressure = trimPressureArmed && !trimForceBackoff;
|
|
1658
|
+
let manualOutcome = "failed";
|
|
1659
|
+
try {
|
|
1660
|
+
const comp = await maybeCompact({
|
|
1661
|
+
session: prepared.session,
|
|
1662
|
+
model: event.model,
|
|
1663
|
+
compactionModel: prepared.compModel,
|
|
1664
|
+
brain: compactionBrain,
|
|
1665
|
+
getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
|
|
1666
|
+
thinking: prepared.thinking,
|
|
1667
|
+
settings: { ...DEFAULT_COMPACTION_SETTINGS, ...spec.compaction },
|
|
1668
|
+
customInstructions: spec.compaction?.instructions ?? DEFAULT_COMPACTION_INSTRUCTIONS,
|
|
1669
|
+
signal: prepared.abortController.signal,
|
|
1670
|
+
minTokens: forceManual ? 0 : compactionFloor,
|
|
1671
|
+
force: forceManual,
|
|
1672
|
+
onInputTruncated: (info) => emitTrace(tracer, () => ({ kind: "compaction.input_truncated", version: 1, taskId, label: info.label, droppedChars: info.droppedChars, keptChars: info.keptChars, ts: Date.now() })),
|
|
1673
|
+
forceUnderThreshold: trimPressure,
|
|
1674
|
+
overheadTokens: prepared.promptOverheadTokens,
|
|
1675
|
+
workingFileAttachments: spec.compaction?.attachWorkingFiles !== false && prepared.readTaskFile
|
|
1676
|
+
? {
|
|
1677
|
+
readFile: prepared.readTaskFile,
|
|
1678
|
+
...(prepared.recentlyReadFiles ? { recentlyReadFiles: prepared.recentlyReadFiles } : {}),
|
|
1679
|
+
...(typeof spec.compaction?.attachWorkingFiles === "object" ? spec.compaction.attachWorkingFiles : undefined),
|
|
1680
|
+
}
|
|
1681
|
+
: undefined,
|
|
1682
|
+
...this.seamCCompactionOptions(prepared),
|
|
1683
|
+
...this.compactionHookOptions(spec, prepared.sessionId, trimPressure ? "forced" : forceManual ? "manual" : "auto"),
|
|
1684
|
+
});
|
|
1685
|
+
if (comp.blocked) {
|
|
1686
|
+
emitTrace(tracer, () => ({
|
|
1687
|
+
kind: "compaction.blocked",
|
|
1688
|
+
version: 1,
|
|
1689
|
+
taskId,
|
|
1690
|
+
trigger: forceManual ? "manual" : "auto",
|
|
1691
|
+
ts: Date.now(),
|
|
1692
|
+
}));
|
|
1693
|
+
}
|
|
1694
|
+
else {
|
|
1695
|
+
compactionBreaker.failures = 0;
|
|
1696
|
+
}
|
|
1697
|
+
trimForceBackoff = nextTrimForceBackoff(trimForceBackoff, comp, trimPressure);
|
|
1698
|
+
if (comp.suppressedByFloor) {
|
|
1699
|
+
const s = comp.suppressedByFloor;
|
|
1700
|
+
emitTrace(tracer, () => ({
|
|
1701
|
+
kind: "compaction.suppressed",
|
|
1702
|
+
version: 1,
|
|
1703
|
+
taskId,
|
|
1704
|
+
estTokens: s.estTokens,
|
|
1705
|
+
floor: s.floor,
|
|
1706
|
+
ts: Date.now(),
|
|
1707
|
+
}));
|
|
1708
|
+
}
|
|
1709
|
+
manualOutcome = comp.compacted ? "compacted" : comp.blocked ? "blocked" : comp.disabled ? "disabled" : "noop";
|
|
1710
|
+
if (forceManual && !comp.compacted && !comp.blocked) {
|
|
1711
|
+
if (comp.disabled) {
|
|
1712
|
+
emitTrace(tracer, () => ({ kind: "compaction.disabled", version: 1, taskId, ts: Date.now() }));
|
|
1713
|
+
}
|
|
1714
|
+
else {
|
|
1715
|
+
emitTrace(tracer, () => ({ kind: "compaction.noop", version: 1, taskId, ts: Date.now() }));
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
if (comp.compacted && !forceManual) {
|
|
1719
|
+
if (recordCompactionAndCheckRapidRefill(rapidRefill, stats.turns)) {
|
|
1720
|
+
compactionBreaker.failures = MAX_CONSECUTIVE_COMPACTION_FAILURES;
|
|
1721
|
+
this.deps.onError?.(new Error("compaction.rapid_refill: the context refilled within <3 turns of compaction 3 times in a row — compaction disabled for the rest of this task (thrash spiral; the transcript is dominated by incompressible content)"), { phase: "compaction", sessionId: prepared.sessionId });
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
this.recordCompactionReuse(prepared, comp);
|
|
1725
|
+
if (comp.compacted) {
|
|
1726
|
+
queue.push({
|
|
1727
|
+
type: "compacted",
|
|
1728
|
+
trigger: forceManual ? "manual" : "auto",
|
|
1729
|
+
tokensBefore: comp.tokensBefore ?? 0,
|
|
1730
|
+
...(comp.postTriggerTokens !== undefined ? { tokensAfter: comp.postTriggerTokens } : {}),
|
|
1731
|
+
...(comp.triggerTokens !== undefined ? { triggerTokensBefore: comp.triggerTokens } : {}),
|
|
1732
|
+
...(comp.durationMs !== undefined ? { durationMs: comp.durationMs } : {}),
|
|
1733
|
+
...(comp.firstKeptEntryId !== undefined ? { preserved_segment: { firstKeptEntryId: comp.firstKeptEntryId } } : {}),
|
|
1734
|
+
...(comp.attachedFiles !== undefined ? { attachedFiles: comp.attachedFiles } : {}),
|
|
1735
|
+
});
|
|
1736
|
+
prepared.cacheBreakDetector?.notifyCompaction();
|
|
1737
|
+
if (attachState !== undefined) {
|
|
1738
|
+
attachState.postCompactPending = true;
|
|
1739
|
+
rebaseCadenceWindows(attachState, cadenceTurns);
|
|
1740
|
+
}
|
|
1741
|
+
if ((comp.freedTokens ?? 0) >= COMPACTION_FREED_EPSILON) {
|
|
1742
|
+
const postSize = comp.postTriggerTokens ?? Math.max(0, (comp.triggerTokens ?? comp.tokensBefore ?? 0) - (comp.freedTokens ?? 0));
|
|
1743
|
+
compactionFloor = Math.ceil(postSize * COMPACTION_REGROWTH_FACTOR);
|
|
1744
|
+
}
|
|
1745
|
+
await this.maybeDynamicRecall(prepared);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
catch (err) {
|
|
1749
|
+
if (!prepared.abortController.signal.aborted) {
|
|
1750
|
+
compactionBreaker.failures += 1;
|
|
1751
|
+
this.deps.onError?.(err, { phase: "compaction", sessionId: prepared.sessionId });
|
|
1752
|
+
const msg = String(err instanceof Error ? err.message : err);
|
|
1753
|
+
emitTrace(tracer, () => ({
|
|
1754
|
+
kind: "compaction.failed",
|
|
1755
|
+
version: 1,
|
|
1756
|
+
taskId,
|
|
1757
|
+
trigger: trimPressure ? "forced" : forceManual ? "manual" : "auto",
|
|
1758
|
+
reason: msg.length > 512 ? `${msg.slice(0, 512)}…` : msg,
|
|
1759
|
+
ts: Date.now(),
|
|
1760
|
+
}));
|
|
1761
|
+
manualOutcome = "failed";
|
|
1762
|
+
}
|
|
1763
|
+
else {
|
|
1764
|
+
manualOutcome = "mooted";
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
if (forceManual)
|
|
1768
|
+
drainManualCompact(manualOutcome);
|
|
1769
|
+
return undefined;
|
|
1770
|
+
};
|
|
1771
|
+
const unsubBoundary = prepared.harness.on("turn_boundary", onTurnBoundary);
|
|
1772
|
+
const unsub = prepared.harness.subscribe((event) => {
|
|
1773
|
+
switch (event.type) {
|
|
1774
|
+
case "message_update":
|
|
1775
|
+
onMessageUpdate(event);
|
|
1776
|
+
break;
|
|
1777
|
+
case "message_end":
|
|
1778
|
+
onMessageEnd(event);
|
|
1779
|
+
break;
|
|
1780
|
+
case "tool_execution_start":
|
|
1781
|
+
onToolStart(event);
|
|
1782
|
+
break;
|
|
1783
|
+
case "tool_execution_end":
|
|
1784
|
+
onToolEnd(event);
|
|
1785
|
+
break;
|
|
1786
|
+
case "turn_end":
|
|
1787
|
+
onTurnEnd();
|
|
1788
|
+
break;
|
|
1789
|
+
default:
|
|
1790
|
+
break;
|
|
1791
|
+
}
|
|
1792
|
+
});
|
|
1793
|
+
let final;
|
|
1794
|
+
let threw;
|
|
1795
|
+
let abortedLive = false;
|
|
1796
|
+
try {
|
|
1797
|
+
if (prepared.abortController.signal.aborted) {
|
|
1798
|
+
const e = new Error("run aborted");
|
|
1799
|
+
e.name = "AbortError";
|
|
1800
|
+
throw e;
|
|
1801
|
+
}
|
|
1802
|
+
if (resume) {
|
|
1803
|
+
await this.applyResumeDecision(prepared, resume, (e) => queue.push(e), emitCommitted, (toolName, details) => {
|
|
1804
|
+
if (attachState === undefined)
|
|
1805
|
+
return;
|
|
1806
|
+
const family = writeFamilyOf(toolName);
|
|
1807
|
+
if (family !== undefined)
|
|
1808
|
+
stampWriteAnchor(attachState, family, cadenceTurns);
|
|
1809
|
+
if (details !== undefined)
|
|
1810
|
+
reduceToolEnd(attachState, details);
|
|
1811
|
+
});
|
|
1812
|
+
if (maxCostMicroUsd !== undefined && maxCostMicroUsd <= 0) {
|
|
1813
|
+
budgetHit = "exceeded";
|
|
1814
|
+
}
|
|
1815
|
+
else {
|
|
1816
|
+
let continuation = resumeContinuation(resume);
|
|
1817
|
+
const bgSnapshot = resume.cp.state.runningBackgroundTasks;
|
|
1818
|
+
if (bgSnapshot !== undefined && bgSnapshot.length > 0) {
|
|
1819
|
+
const alive = new Set(prepared.listBackgroundTasks().map((t) => t.id));
|
|
1820
|
+
const orphans = bgSnapshot.filter((t) => !alive.has(t.id));
|
|
1821
|
+
if (orphans.length > 0) {
|
|
1822
|
+
continuation += "\n\n" + formatHookFeedback(renderOrphanedBackgroundTasks(orphans));
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
const steer = resume.cp.state.pendingSteer;
|
|
1826
|
+
if (steer) {
|
|
1827
|
+
continuation +=
|
|
1828
|
+
"\n\n" +
|
|
1829
|
+
(steer.trusted ? formatHookFeedback(steer.text) : delimitUntrusted("supervisor steering message", steer.text));
|
|
1830
|
+
}
|
|
1831
|
+
final = await withBrainSinks(() => prepared.harness.prompt(continuation));
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
else {
|
|
1835
|
+
let effectiveObjective = spec.objective;
|
|
1836
|
+
let promptBlocked = false;
|
|
1837
|
+
const userPromptSubmit = (spec.hooks ?? this.deps.hooks)?.userPromptSubmit;
|
|
1838
|
+
if (userPromptSubmit) {
|
|
1839
|
+
const decision = await userPromptSubmit(spec.objective);
|
|
1840
|
+
if (decision?.block) {
|
|
1841
|
+
prepared.blockedRef.reason = formatHookFeedback(decision.block);
|
|
1842
|
+
promptBlocked = true;
|
|
1843
|
+
}
|
|
1844
|
+
else if (decision?.additionalContext) {
|
|
1845
|
+
effectiveObjective = `${formatHookFeedback(decision.additionalContext)}\n\n${spec.objective}`;
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
const precallMicroUsd = maxCostMicroUsd === undefined
|
|
1849
|
+
? 0
|
|
1850
|
+
: computeCostMicroUsd({
|
|
1851
|
+
totalInputTokens: Math.ceil(effectiveObjective.length / 4),
|
|
1852
|
+
cacheReadTokens: 0,
|
|
1853
|
+
cacheWriteTokens: 0,
|
|
1854
|
+
cacheWriteTokensLong: 0,
|
|
1855
|
+
outputTokens: prepared.model.maxTokens ?? 4096,
|
|
1856
|
+
}, pricing);
|
|
1857
|
+
const precallCeilingMicroUsd = prepared.suspendForResource !== undefined ? remainingMicroUsd : maxCostMicroUsd;
|
|
1858
|
+
if (promptBlocked) {
|
|
1859
|
+
}
|
|
1860
|
+
else if (precallCeilingMicroUsd !== undefined && precallMicroUsd > precallCeilingMicroUsd) {
|
|
1861
|
+
budgetHit = "precall";
|
|
1862
|
+
}
|
|
1863
|
+
else {
|
|
1864
|
+
const images = spec.images
|
|
1865
|
+
? await Promise.all(spec.images.map((img) => toImageContent(img, this.deps.allowImageUrl)))
|
|
1866
|
+
: undefined;
|
|
1867
|
+
final = await withBrainSinks(() => prepared.harness.prompt(effectiveObjective, images && images.length > 0 ? { images } : undefined));
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
abortedLive = prepared.abortController.signal.aborted;
|
|
1871
|
+
}
|
|
1872
|
+
catch (err) {
|
|
1873
|
+
if (errorCodeOf(err) === "resume.tool_unavailable") {
|
|
1874
|
+
try {
|
|
1875
|
+
await prepared.mcp.dispose();
|
|
1876
|
+
}
|
|
1877
|
+
catch {
|
|
1878
|
+
}
|
|
1879
|
+
try {
|
|
1880
|
+
if (prepared.ownedEnv && hasDestroy(prepared.ownedEnv))
|
|
1881
|
+
await prepared.ownedEnv.destroy();
|
|
1882
|
+
}
|
|
1883
|
+
catch {
|
|
1884
|
+
}
|
|
1885
|
+
throw err;
|
|
1886
|
+
}
|
|
1887
|
+
threw = err;
|
|
1888
|
+
abortedLive = prepared.abortController.signal.aborted;
|
|
1889
|
+
}
|
|
1890
|
+
finally {
|
|
1891
|
+
timeout.clear();
|
|
1892
|
+
prepared.abortController.abort();
|
|
1893
|
+
prepared.releaseSignal();
|
|
1894
|
+
notificationLaneLive = false;
|
|
1895
|
+
unsubscribeTaskNotifications();
|
|
1896
|
+
unsubBoundary();
|
|
1897
|
+
unsub();
|
|
1898
|
+
}
|
|
1899
|
+
const suspended = prepared.suspendRef.token !== undefined && prepared.suspendRef.gate !== undefined;
|
|
1900
|
+
const reviewPaused = prepared.reviewRef.token !== undefined && prepared.reviewRef.gate !== undefined;
|
|
1901
|
+
const durablyPaused = suspended || reviewPaused;
|
|
1902
|
+
const interrupted = !durablyPaused &&
|
|
1903
|
+
(threw !== undefined || timeout.fired || turnsExceeded || budgetHit !== undefined || abortedLive || final?.stopReason === "aborted");
|
|
1904
|
+
if (interrupted) {
|
|
1905
|
+
try {
|
|
1906
|
+
const report = await reconcileInterruptedSession(prepared.session, prepared.toolEffects);
|
|
1907
|
+
for (const orphan of report.recovered) {
|
|
1908
|
+
queue.push({ type: "tool_end", toolCallId: orphan.toolCallId, toolName: orphan.toolName, isError: true, ...ident() });
|
|
1909
|
+
emitCommitted(orphan.entryId, "toolResult", orphan.toolCallId);
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
catch (reconcileErr) {
|
|
1913
|
+
this.deps.onError?.(reconcileErr instanceof Error ? reconcileErr : new Error(String(reconcileErr)), {
|
|
1914
|
+
phase: "interrupt-reconcile",
|
|
1915
|
+
sessionId: prepared.sessionId,
|
|
1916
|
+
});
|
|
1917
|
+
}
|
|
1918
|
+
try {
|
|
1919
|
+
await prepared.harness.flushQueuedSessionWrites();
|
|
1920
|
+
}
|
|
1921
|
+
catch (flushErr) {
|
|
1922
|
+
this.deps.onError?.(flushErr instanceof Error ? flushErr : new Error(String(flushErr)), {
|
|
1923
|
+
phase: "interrupt-reconcile",
|
|
1924
|
+
sessionId: prepared.sessionId,
|
|
1925
|
+
});
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
if (attachState?.postCompactPending === true && attachmentsCfg?.backgroundTasks === true) {
|
|
1929
|
+
emitTrace(tracer, () => ({ kind: "compaction.announce_dropped", version: 1, taskId, ts: Date.now() }));
|
|
1930
|
+
}
|
|
1931
|
+
const comp = await this.finish(spec, prepared, {
|
|
1932
|
+
skipCompaction: durablyPaused || compactionBreaker.failures >= MAX_CONSECUTIVE_COMPACTION_FAILURES,
|
|
1933
|
+
minTokens: compactionFloor,
|
|
1934
|
+
brain: compactionBrain,
|
|
1935
|
+
});
|
|
1936
|
+
try {
|
|
1937
|
+
if (comp?.compacted) {
|
|
1938
|
+
queue.push({
|
|
1939
|
+
type: "compacted",
|
|
1940
|
+
trigger: "auto",
|
|
1941
|
+
tokensBefore: comp.tokensBefore ?? 0,
|
|
1942
|
+
...(comp.postTriggerTokens !== undefined ? { tokensAfter: comp.postTriggerTokens } : {}),
|
|
1943
|
+
...(comp.triggerTokens !== undefined ? { triggerTokensBefore: comp.triggerTokens } : {}),
|
|
1944
|
+
...(comp.durationMs !== undefined ? { durationMs: comp.durationMs } : {}),
|
|
1945
|
+
...(comp.firstKeptEntryId !== undefined ? { preserved_segment: { firstKeptEntryId: comp.firstKeptEntryId } } : {}),
|
|
1946
|
+
...(comp.attachedFiles !== undefined ? { attachedFiles: comp.attachedFiles } : {}),
|
|
1947
|
+
});
|
|
1948
|
+
prepared.cacheBreakDetector?.notifyCompaction();
|
|
1949
|
+
}
|
|
1950
|
+
if (prepared.nestedStats.tasks > 0) {
|
|
1951
|
+
stats.nested = {
|
|
1952
|
+
tokens: prepared.nestedStats.tokens,
|
|
1953
|
+
turns: prepared.nestedStats.turns,
|
|
1954
|
+
tasks: prepared.nestedStats.tasks,
|
|
1955
|
+
costUsd: prepared.nestedStats.costUsd || undefined,
|
|
1956
|
+
costMicroUsd: prepared.nestedStats.costMicroUsd || undefined,
|
|
1957
|
+
};
|
|
1958
|
+
}
|
|
1959
|
+
if (stats.promptTokens > 0) {
|
|
1960
|
+
const rawHit = stats.cachedTokens / stats.promptTokens;
|
|
1961
|
+
if (stats.cachedTokens > stats.promptTokens) {
|
|
1962
|
+
this.deps.onError?.(new Error(`prompt-cache: usage/api mismatch — cachedTokens (${stats.cachedTokens}) > promptTokens (${stats.promptTokens}); the cache family is likely wrong. Does model.api match the brain serving it? Override with model.params.promptCacheFamily = "input-includes-cached" | "input-excludes-cached".`), { phase: "prompt-cache", sessionId: prepared.sessionId });
|
|
1963
|
+
}
|
|
1964
|
+
stats.cacheHitRate = Math.min(1, rawHit);
|
|
1965
|
+
if (!cacheBreakReported && stats.turns >= 2 && stats.promptTokens >= 8000 && stats.cacheHitRate < 0.15) {
|
|
1966
|
+
const cause = degraded
|
|
1967
|
+
? `a mid-task model switch (${degraded.from} → ${degraded.to}, degradation) reset the prefix cache — this is the likely cause`
|
|
1968
|
+
: `the cacheable prefix looks unstable. Keep volatile content (memory/timestamps/ids) out of the prompt prefix and tool order stable, or it's a server-side/TTL miss`;
|
|
1969
|
+
this.deps.onError?.(new Error(`prompt-cache: low prefix-cache hit rate ${(stats.cacheHitRate * 100).toFixed(0)}% over ${stats.turns} turns (${stats.promptTokens} prompt tokens) — ${cause}. See design/09.`), { phase: "prompt-cache", sessionId: prepared.sessionId });
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
if (prepared.resourceLedger &&
|
|
1973
|
+
(resume?.cp.gate.kind === "resource_limit" || prepared.suspendRef.gate?.kind === "resource_limit")) {
|
|
1974
|
+
stats.costMicroUsd += prepared.resourceLedger.spentMicroUsd;
|
|
1975
|
+
stats.costUsd += prepared.resourceLedger.spentMicroUsd / 1e6;
|
|
1976
|
+
stats.tokens += prepared.resourceLedger.spentTokens;
|
|
1977
|
+
stats.turns += prepared.resourceLedger.spentTurns;
|
|
1978
|
+
}
|
|
1979
|
+
if (prepared.humanReviewRef.count > 0) {
|
|
1980
|
+
stats.humanReview = {
|
|
1981
|
+
count: prepared.humanReviewRef.count,
|
|
1982
|
+
totalWaitMs: prepared.humanReviewRef.totalWaitMs,
|
|
1983
|
+
gates: [...prepared.humanReviewRef.gates],
|
|
1984
|
+
};
|
|
1985
|
+
}
|
|
1986
|
+
const capShrinks = prepared.callCapRef?.shrinks ?? 0;
|
|
1987
|
+
const toolClamps = prepared.callCapRef?.toolClamps ?? 0;
|
|
1988
|
+
if (finalizeInjected || nudgesSent > 0 || capShrinks > 0 || callCutoffs > 0 || toolClamps > 0 || finalVerifyInjections > 0 || attachmentsInjected > 0 || repetitionCuts > 0 || repetitionSpared > 0) {
|
|
1989
|
+
stats.mechanisms = {
|
|
1990
|
+
...(finalizeInjected ? { finalizeInjected: true } : {}),
|
|
1991
|
+
...(nudgesSent > 0 ? { nudgesSent } : {}),
|
|
1992
|
+
...(capShrinks > 0 ? { capShrinks } : {}),
|
|
1993
|
+
...(callCutoffs > 0 ? { callCutoffs } : {}),
|
|
1994
|
+
...(toolClamps > 0 ? { toolClamps } : {}),
|
|
1995
|
+
...(finalVerifyInjections > 0 ? { finalVerifyInjected: true } : {}),
|
|
1996
|
+
...(finalVerifyInjections > 0 ? { finalVerifyInjections } : {}),
|
|
1997
|
+
...(attachmentsInjected > 0 ? { attachmentsInjected } : {}),
|
|
1998
|
+
...(repetitionCuts > 0 ? { repetitionCuts } : {}),
|
|
1999
|
+
...(repetitionSpared > 0 ? { repetitionSpared } : {}),
|
|
2000
|
+
...(repetitionEvents.length > 0 ? { repetitionEvents } : {}),
|
|
2001
|
+
};
|
|
2002
|
+
}
|
|
2003
|
+
const result = assembleResult(spec, prepared.sessionId, final, stats, {
|
|
2004
|
+
threw,
|
|
2005
|
+
model: prepared.model.id,
|
|
2006
|
+
abortedForTimeout: timeout.fired,
|
|
2007
|
+
abortedForTurns: turnsExceeded,
|
|
2008
|
+
abortedLive,
|
|
2009
|
+
budgetHit,
|
|
2010
|
+
blockedReason: prepared.blockedRef.reason,
|
|
2011
|
+
conflict: prepared.conflictRef.hit,
|
|
2012
|
+
outputInvalid,
|
|
2013
|
+
suspendLoop: prepared.suspendLoopRef.hit,
|
|
2014
|
+
suspendRef: prepared.suspendRef.token !== undefined && prepared.suspendRef.gate !== undefined
|
|
2015
|
+
? { token: prepared.suspendRef.token, gate: prepared.suspendRef.gate }
|
|
2016
|
+
: undefined,
|
|
2017
|
+
reviewRef: prepared.reviewRef.token !== undefined && prepared.reviewRef.gate !== undefined
|
|
2018
|
+
? { token: prepared.reviewRef.token, gate: prepared.reviewRef.gate }
|
|
2019
|
+
: undefined,
|
|
2020
|
+
});
|
|
2021
|
+
const stopFailureHook = (spec.hooks ?? this.deps.hooks)?.stopFailure;
|
|
2022
|
+
if (stopFailureHook !== undefined &&
|
|
2023
|
+
result.status === "failed" &&
|
|
2024
|
+
final?.stopReason === "error" &&
|
|
2025
|
+
final.errorMessage !== DEGENERATE_MESSAGE &&
|
|
2026
|
+
final.errorMessage !== WALLTIME_CUTOFF_MESSAGE &&
|
|
2027
|
+
threw === undefined &&
|
|
2028
|
+
budgetHit === undefined &&
|
|
2029
|
+
!outputInvalid &&
|
|
2030
|
+
!prepared.suspendLoopRef.hit &&
|
|
2031
|
+
!abortedLive &&
|
|
2032
|
+
result.errorCode !== "conflict") {
|
|
2033
|
+
try {
|
|
2034
|
+
await stopFailureHook({
|
|
2035
|
+
error: result.errorMessage ?? "model error",
|
|
2036
|
+
...(result.errorCode !== undefined ? { errorKind: result.errorCode } : {}),
|
|
2037
|
+
turns: stats.turns,
|
|
2038
|
+
});
|
|
2039
|
+
}
|
|
2040
|
+
catch (err) {
|
|
2041
|
+
try {
|
|
2042
|
+
this.deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "hook", sessionId: prepared.sessionId });
|
|
2043
|
+
}
|
|
2044
|
+
catch {
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
if (degraded)
|
|
2049
|
+
result.degraded = degraded;
|
|
2050
|
+
if (prepared.outputRef.set)
|
|
2051
|
+
result.structuredOutput = prepared.outputRef.value;
|
|
2052
|
+
const committedToken = prepared.suspendRef.token ?? prepared.reviewRef.token;
|
|
2053
|
+
const committedScope = prepared.suspendRef.scope ?? prepared.reviewRef.scope;
|
|
2054
|
+
if (committedToken !== undefined) {
|
|
2055
|
+
const store = spec.checkpointStore ?? this.deps.checkpointStore;
|
|
2056
|
+
if (store && committedScope !== undefined) {
|
|
2057
|
+
onSuspend({
|
|
2058
|
+
env: prepared.ownedEnv,
|
|
2059
|
+
token: committedToken,
|
|
2060
|
+
scope: committedScope,
|
|
2061
|
+
store,
|
|
2062
|
+
sessionId: prepared.sessionId,
|
|
2063
|
+
});
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
if (resume && committedToken === undefined) {
|
|
2067
|
+
try {
|
|
2068
|
+
await this.sessions.unpin?.(resume.cp.sessionId);
|
|
2069
|
+
}
|
|
2070
|
+
catch (err) {
|
|
2071
|
+
try {
|
|
2072
|
+
this.deps.onError?.(err, { phase: "config", sessionId: prepared.sessionId });
|
|
2073
|
+
}
|
|
2074
|
+
catch {
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
if (prepared.memoryEngineSession)
|
|
2079
|
+
await prepared.memoryEngineSession.harvest();
|
|
2080
|
+
emitTrace(tracer, () => ({
|
|
2081
|
+
kind: "task.end",
|
|
2082
|
+
version: 1,
|
|
2083
|
+
taskId,
|
|
2084
|
+
status: result.status,
|
|
2085
|
+
errorCode: result.errorCode,
|
|
2086
|
+
turns: stats.turns,
|
|
2087
|
+
tokens: stats.tokens,
|
|
2088
|
+
costMicroUsd: stats.costMicroUsd,
|
|
2089
|
+
durationMs: Date.now() - taskStart,
|
|
2090
|
+
...(prepared.outputRef.set ? { hasStructuredOutput: true } : {}),
|
|
2091
|
+
ts: Date.now(),
|
|
2092
|
+
}));
|
|
2093
|
+
setResult(result);
|
|
2094
|
+
queue.push({ type: "done", result });
|
|
2095
|
+
queue.close();
|
|
2096
|
+
if (spec.rewindFiles && result.status === "completed") {
|
|
2097
|
+
await this.snapshotTurn(prepared);
|
|
2098
|
+
}
|
|
2099
|
+
if (prepared.consolidation && prepared.consolidation.notes.length > 0) {
|
|
2100
|
+
onConsolidation(this.consolidateMemory(spec, prepared, result));
|
|
2101
|
+
}
|
|
2102
|
+
if (spec.suggestNextPrompts && result.status === "completed") {
|
|
2103
|
+
onSuggestions(this.suggestNextPrompts(spec, prepared, result));
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
finally {
|
|
2107
|
+
const ownedEnvDying = prepared.ownedEnv !== undefined &&
|
|
2108
|
+
hasDestroy(prepared.ownedEnv) &&
|
|
2109
|
+
isIsolated(prepared.ownedEnv) &&
|
|
2110
|
+
prepared.suspendRef.token === undefined &&
|
|
2111
|
+
prepared.reviewRef.token === undefined;
|
|
2112
|
+
try {
|
|
2113
|
+
await defaultTaskRegistry.settleKilledForOwner({ owner: spec.taskId ?? prepared.sessionId, scope: spec.principal ?? "default" }, {
|
|
2114
|
+
source: abortedLive &&
|
|
2115
|
+
!timeout.fired &&
|
|
2116
|
+
!turnsExceeded &&
|
|
2117
|
+
budgetHit === undefined &&
|
|
2118
|
+
prepared.suspendRef.token === undefined &&
|
|
2119
|
+
prepared.reviewRef.token === undefined
|
|
2120
|
+
? "user"
|
|
2121
|
+
: "parent",
|
|
2122
|
+
skipSessionScoped: true,
|
|
2123
|
+
envDying: ownedEnvDying,
|
|
2124
|
+
});
|
|
2125
|
+
}
|
|
2126
|
+
catch (e) {
|
|
2127
|
+
try {
|
|
2128
|
+
this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId });
|
|
2129
|
+
}
|
|
2130
|
+
catch {
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
const bgEnv = prepared.ownedEnv ?? this.deps.executionEnv;
|
|
2134
|
+
if (bgEnv && hasBackgroundShell(bgEnv)) {
|
|
2135
|
+
try {
|
|
2136
|
+
const keepAlive = ownedEnvDying && bgEnv === prepared.ownedEnv
|
|
2137
|
+
? []
|
|
2138
|
+
: [
|
|
2139
|
+
...defaultTaskRegistry.sessionResidentShellIds(prepared.sessionId, bgEnv),
|
|
2140
|
+
...defaultTaskRegistry.timeoutResidentShellIds(bgEnv),
|
|
2141
|
+
];
|
|
2142
|
+
await bgEnv.disposeBackgroundShells(keepAlive.length > 0 ? { except: keepAlive } : undefined);
|
|
2143
|
+
}
|
|
2144
|
+
catch (e) {
|
|
2145
|
+
this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId });
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
defaultTaskRegistry.clearBackgroundForOwner({
|
|
2149
|
+
owner: spec.taskId ?? prepared.sessionId,
|
|
2150
|
+
scope: spec.principal ?? "default",
|
|
2151
|
+
});
|
|
2152
|
+
defaultTaskRegistry.abortBackgroundAgentsForOwner({
|
|
2153
|
+
owner: spec.taskId ?? prepared.sessionId,
|
|
2154
|
+
scope: spec.principal ?? "default",
|
|
2155
|
+
}, { skipSessionScoped: true });
|
|
2156
|
+
if (prepared.subagentRetain) {
|
|
2157
|
+
try {
|
|
2158
|
+
await prepared.subagentRetain.disposeAll();
|
|
2159
|
+
}
|
|
2160
|
+
catch (e) {
|
|
2161
|
+
this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId });
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
await this.teardownOwnedEnv(prepared);
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
async consolidateMemory(spec, prepared, result) {
|
|
2168
|
+
const consolidation = prepared.consolidation;
|
|
2169
|
+
if (!consolidation || !this.deps.memoryStore) {
|
|
2170
|
+
return;
|
|
2171
|
+
}
|
|
2172
|
+
const { scope, notes, settings } = consolidation;
|
|
2173
|
+
const onWarn = (err) => {
|
|
2174
|
+
try {
|
|
2175
|
+
this.deps.onError?.(err, { phase: "memory", sessionId: prepared.sessionId });
|
|
2176
|
+
}
|
|
2177
|
+
catch {
|
|
2178
|
+
}
|
|
2179
|
+
};
|
|
2180
|
+
const ac = new AbortController();
|
|
2181
|
+
const timer = settings.timeoutSec > 0 ? setTimeout(() => ac.abort(), settings.timeoutSec * 1000) : undefined;
|
|
2182
|
+
timer?.unref?.();
|
|
2183
|
+
try {
|
|
2184
|
+
let model = prepared.model;
|
|
2185
|
+
let thinking = prepared.thinking;
|
|
2186
|
+
try {
|
|
2187
|
+
const resolved = resolveTaskModel({ modelRole: settings.role, roles: spec.roles }, this.deps);
|
|
2188
|
+
model = resolved.model;
|
|
2189
|
+
thinking = resolved.thinking;
|
|
2190
|
+
}
|
|
2191
|
+
catch {
|
|
2192
|
+
}
|
|
2193
|
+
const pricing = this.deps.pricing?.[model.id] ?? modelCostToPricing(model.cost);
|
|
2194
|
+
const stats = await runMemoryConsolidation({
|
|
2195
|
+
store: this.deps.memoryStore,
|
|
2196
|
+
scope,
|
|
2197
|
+
notes,
|
|
2198
|
+
band: settings.band,
|
|
2199
|
+
searchLimit: settings.searchLimit,
|
|
2200
|
+
maxNotes: settings.maxNotes,
|
|
2201
|
+
onWarn,
|
|
2202
|
+
llm: {
|
|
2203
|
+
brain: this.deps.brain,
|
|
2204
|
+
model,
|
|
2205
|
+
pricing,
|
|
2206
|
+
thinking,
|
|
2207
|
+
getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
|
|
2208
|
+
signal: ac.signal,
|
|
2209
|
+
},
|
|
2210
|
+
});
|
|
2211
|
+
await advanceCursorAfterInline(this.deps.memoryStore, scope, stats?.consolidatedIds ?? [], stats?.addedIds ?? [], stats?.failedIds ?? [], onWarn);
|
|
2212
|
+
if (stats) {
|
|
2213
|
+
result.stats.memory = { tokens: stats.tokens, costMicroUsd: stats.costMicroUsd, applied: stats.applied };
|
|
2214
|
+
if (result.stats.costBreakdown) {
|
|
2215
|
+
result.stats.costBreakdown.memoryConsolidationMicroUsd = stats.costMicroUsd;
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
catch (err) {
|
|
2220
|
+
onWarn(err);
|
|
2221
|
+
}
|
|
2222
|
+
finally {
|
|
2223
|
+
if (timer) {
|
|
2224
|
+
clearTimeout(timer);
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
async suggestNextPrompts(spec, prepared, result) {
|
|
2229
|
+
const cfg = typeof spec.suggestNextPrompts === "object" ? spec.suggestNextPrompts : {};
|
|
2230
|
+
const count = cfg.count && cfg.count > 0 ? Math.min(cfg.count, SUGGESTIONS_MAX_COUNT) : SUGGESTIONS_DEFAULT_COUNT;
|
|
2231
|
+
const ac = new AbortController();
|
|
2232
|
+
let timer;
|
|
2233
|
+
const timeout = new Promise((resolve) => {
|
|
2234
|
+
timer = setTimeout(() => {
|
|
2235
|
+
ac.abort();
|
|
2236
|
+
resolve([]);
|
|
2237
|
+
}, SUGGESTIONS_TIMEOUT_MS);
|
|
2238
|
+
timer.unref?.();
|
|
2239
|
+
});
|
|
2240
|
+
const work = (async () => {
|
|
2241
|
+
let model = prepared.model;
|
|
2242
|
+
let thinking = prepared.thinking;
|
|
2243
|
+
try {
|
|
2244
|
+
const resolved = resolveTaskModel({ modelRole: cfg.role ?? "summarize", roles: spec.roles }, this.deps);
|
|
2245
|
+
model = resolved.model;
|
|
2246
|
+
thinking = resolved.thinking;
|
|
2247
|
+
}
|
|
2248
|
+
catch {
|
|
2249
|
+
}
|
|
2250
|
+
const pricing = this.deps.pricing?.[model.id] ?? modelCostToPricing(model.cost);
|
|
2251
|
+
const ctx = await prepared.session.buildContext();
|
|
2252
|
+
const transcript = ctx.messages.slice(-SUGGESTIONS_TRANSCRIPT_MESSAGES);
|
|
2253
|
+
const out = await generatePromptSuggestions({ brain: this.deps.brain, model, pricing, thinking, getApiKeyAndHeaders: spec.getApiKeyAndHeaders, signal: ac.signal }, transcript, count);
|
|
2254
|
+
if (out.tokens > 0 || out.costMicroUsd > 0) {
|
|
2255
|
+
result.stats.suggestions = { tokens: out.tokens, costMicroUsd: out.costMicroUsd };
|
|
2256
|
+
if (result.stats.costBreakdown) {
|
|
2257
|
+
result.stats.costBreakdown.suggestionsMicroUsd = out.costMicroUsd;
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
return out.suggestions;
|
|
2261
|
+
})();
|
|
2262
|
+
work.catch(() => { });
|
|
2263
|
+
try {
|
|
2264
|
+
return await Promise.race([work, timeout]);
|
|
2265
|
+
}
|
|
2266
|
+
catch (err) {
|
|
2267
|
+
try {
|
|
2268
|
+
this.deps.onError?.(err, { phase: "suggestions", sessionId: prepared.sessionId });
|
|
2269
|
+
}
|
|
2270
|
+
catch {
|
|
2271
|
+
}
|
|
2272
|
+
return [];
|
|
2273
|
+
}
|
|
2274
|
+
finally {
|
|
2275
|
+
clearTimeout(timer);
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
async snapshotTurn(prepared) {
|
|
2279
|
+
const store = this.deps.fileSnapshotStore;
|
|
2280
|
+
const env = prepared.ownedEnv ?? this.deps.executionEnv;
|
|
2281
|
+
if (!store || !env)
|
|
2282
|
+
return;
|
|
2283
|
+
try {
|
|
2284
|
+
const leafId = await prepared.session.getLeafId();
|
|
2285
|
+
if (!leafId)
|
|
2286
|
+
return;
|
|
2287
|
+
const root = prepared.taskRootPath;
|
|
2288
|
+
const refusedAt = this.snapshotTooLargeRoots.get(root);
|
|
2289
|
+
if (refusedAt !== undefined) {
|
|
2290
|
+
if (Date.now() - refusedAt < SNAPSHOT_TOO_LARGE_TTL_MS)
|
|
2291
|
+
return;
|
|
2292
|
+
this.snapshotTooLargeRoots.delete(root);
|
|
2293
|
+
}
|
|
2294
|
+
const ac = new AbortController();
|
|
2295
|
+
let timer;
|
|
2296
|
+
const timeout = new Promise((resolve) => {
|
|
2297
|
+
timer = setTimeout(() => {
|
|
2298
|
+
ac.abort();
|
|
2299
|
+
resolve({ ok: false, error: { code: "aborted", message: "snapshot timed out" } });
|
|
2300
|
+
}, SNAPSHOT_TIMEOUT_MS);
|
|
2301
|
+
timer.unref?.();
|
|
2302
|
+
});
|
|
2303
|
+
const work = store.snapshot(prepared.sessionId, leafId, env, root, ac.signal);
|
|
2304
|
+
work.catch(() => { });
|
|
2305
|
+
let r;
|
|
2306
|
+
try {
|
|
2307
|
+
r = await Promise.race([work, timeout]);
|
|
2308
|
+
}
|
|
2309
|
+
finally {
|
|
2310
|
+
clearTimeout(timer);
|
|
2311
|
+
}
|
|
2312
|
+
if (!r.ok) {
|
|
2313
|
+
const tooLarge = r.error.code === "too_large";
|
|
2314
|
+
if (tooLarge)
|
|
2315
|
+
this.snapshotTooLargeRoots.set(root, Date.now());
|
|
2316
|
+
try {
|
|
2317
|
+
this.deps.onError?.(new Error(`rewind-files snapshot failed (${r.error.code}): ${r.error.message}` +
|
|
2318
|
+
(tooLarge ? " — skipping further snapshots for this root (this process); raise the store's snapshotBounds or shrink the tree to re-enable rewind" : "")), { phase: "rewind", sessionId: prepared.sessionId, ...(tooLarge ? { classification: "too_large" } : {}) });
|
|
2319
|
+
}
|
|
2320
|
+
catch {
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
catch (err) {
|
|
2325
|
+
try {
|
|
2326
|
+
this.deps.onError?.(err, { phase: "rewind", sessionId: prepared.sessionId });
|
|
2327
|
+
}
|
|
2328
|
+
catch {
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
async runTask(spec, internals) {
|
|
2333
|
+
const stream = this.runTaskStream(spec, undefined, internals);
|
|
2334
|
+
for await (const _ of stream) {
|
|
2335
|
+
void _;
|
|
2336
|
+
}
|
|
2337
|
+
return stream.result();
|
|
2338
|
+
}
|
|
2339
|
+
async resume(token, outcome, taskConfig) {
|
|
2340
|
+
const stream = await this.resumeStream(token, outcome, taskConfig);
|
|
2341
|
+
for await (const _ of stream) {
|
|
2342
|
+
void _;
|
|
2343
|
+
}
|
|
2344
|
+
return stream.result();
|
|
2345
|
+
}
|
|
2346
|
+
async resumeStream(token, outcome, taskConfig, internals) {
|
|
2347
|
+
const store = taskConfig.checkpointStore ?? this.deps.checkpointStore;
|
|
2348
|
+
if (!store) {
|
|
2349
|
+
throw new CheckpointError("checkpoint.not_found", "no CheckpointStore wired — cannot resume (set RunnerDeps.checkpointStore or taskConfig.checkpointStore)");
|
|
2350
|
+
}
|
|
2351
|
+
const cp = await store.get(token);
|
|
2352
|
+
if (!cp) {
|
|
2353
|
+
throw new CheckpointError("checkpoint.not_found", "no checkpoint found for the supplied token");
|
|
2354
|
+
}
|
|
2355
|
+
const gateMatch = (cp.gate.kind === "human" && outcome.gate === "policy_ask") ||
|
|
2356
|
+
(cp.gate.kind === "irreversible_ask" && outcome.gate === "policy_ask") ||
|
|
2357
|
+
(cp.gate.kind === "resource_limit" && outcome.gate === "resource_limit") ||
|
|
2358
|
+
(cp.gate.kind === "needs_review" && outcome.gate === "dry_run_review") ||
|
|
2359
|
+
(cp.gate.kind === "plan_review" && outcome.gate === "plan_review");
|
|
2360
|
+
if (!gateMatch) {
|
|
2361
|
+
throw new CheckpointError("checkpoint.gate_mismatch", `resume outcome (gate "${outcome.gate}") does not match checkpoint gate "${cp.gate.kind}" — resume serves human/policy_ask, irreversible_ask/policy_ask, resource_limit/resource_limit, needs_review/dry_run_review, and plan_review/plan_review`);
|
|
2362
|
+
}
|
|
2363
|
+
const reasonBearing = (outcome.gate === "policy_ask" && outcome.decision === "deny") ||
|
|
2364
|
+
(outcome.gate === "dry_run_review" && outcome.decision === "reject") ||
|
|
2365
|
+
(outcome.gate === "plan_review" && outcome.decision === "reject");
|
|
2366
|
+
if (reasonBearing && outcome.reason && sanitizeUntrustedText(outcome.reason) !== outcome.reason) {
|
|
2367
|
+
throw new CheckpointError("checkpoint.invalid_outcome", "resume deny/reject reason must not contain a </system-reminder> tag");
|
|
2368
|
+
}
|
|
2369
|
+
if (outcome.gate === "plan_review" &&
|
|
2370
|
+
outcome.decision === "edit" &&
|
|
2371
|
+
outcome.editedPlan &&
|
|
2372
|
+
sanitizeUntrustedText(outcome.editedPlan) !== outcome.editedPlan) {
|
|
2373
|
+
throw new CheckpointError("checkpoint.invalid_outcome", "resume editedPlan must not contain a </system-reminder> tag");
|
|
2374
|
+
}
|
|
2375
|
+
if (outcome.gate === "policy_ask") {
|
|
2376
|
+
const boundTo = cp.pendingAction.kind === "tool_approval" ? cp.pendingAction.toolCallId : undefined;
|
|
2377
|
+
if (outcome.boundCallId !== boundTo) {
|
|
2378
|
+
throw new CheckpointError("checkpoint.invalid_outcome", `resume boundCallId "${outcome.boundCallId}" does not match the checkpoint's pending tool call "${boundTo ?? "(none)"}" — the decision-action binding (design/80 D-1) failed; refusing to apply a decision bound to a different action`);
|
|
2379
|
+
}
|
|
2380
|
+
const boundHash = cp.pendingAction.kind === "tool_approval" ? cp.pendingAction.boundInputHash : undefined;
|
|
2381
|
+
if (boundHash !== undefined) {
|
|
2382
|
+
if (outcome.boundInputHash !== boundHash) {
|
|
2383
|
+
throw new CheckpointError("checkpoint.invalid_outcome", "resume boundInputHash does not match the checkpoint's pending tool call input — the decision-action input binding (design/80 D-1 §2) failed; refusing to apply an approval bound to a different input (TOCTOU re-mint guard)");
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
else if (checkpointVersionOf(cp) >= BINDING_CHECKPOINT_VERSION) {
|
|
2387
|
+
throw new CheckpointError("checkpoint.invalid_outcome", `resume of a binding-version (v${checkpointVersionOf(cp)}) checkpoint whose pending tool call has no persisted boundInputHash — refusing to resume an unbound binding-version checkpoint (corruption / downgrade guard)`);
|
|
2388
|
+
}
|
|
2389
|
+
if (cp.reopenReason === "env_failed") {
|
|
2390
|
+
if (cp.resolvedOutcome === undefined) {
|
|
2391
|
+
throw new CheckpointError("checkpoint.reopen_revote", "checkpoint was reopened after an env-restore failure (env_failed) but carries no persisted winner to replay — refusing to resume (inconsistent row, fail-closed)");
|
|
2392
|
+
}
|
|
2393
|
+
const incoming = winnerFromOutcome(outcome);
|
|
2394
|
+
if (incoming === undefined || !sameWinner(incoming, cp.resolvedOutcome)) {
|
|
2395
|
+
throw new CheckpointError("checkpoint.reopen_revote", `this checkpoint was reopened after an env-restore failure (env_failed) — a re-resume is a system retry of the already-approved action, not a re-vote; the supplied decision must replay the persisted winner (boundCallId "${cp.resolvedOutcome.boundCallId}", decision "${cp.resolvedOutcome.decision}"), refusing a different decision`);
|
|
2396
|
+
}
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
if (checkpointVersionOf(cp) > MAX_SUPPORTED_CHECKPOINT_VERSION) {
|
|
2400
|
+
throw new CheckpointError("checkpoint.unsupported_version", `checkpoint version ${checkpointVersionOf(cp)} is newer than this worker supports (max ${MAX_SUPPORTED_CHECKPOINT_VERSION})`);
|
|
2401
|
+
}
|
|
2402
|
+
if (cp.state.workspaceHandle !== undefined && this.deps.executionEnvFactory === undefined) {
|
|
2403
|
+
throw new CheckpointError("checkpoint.unsupported_version", "checkpoint has a remote workspaceHandle but no RunnerDeps.executionEnvFactory is wired to rebuild the env");
|
|
2404
|
+
}
|
|
2405
|
+
const won = await store.resolve(token, cp.scope, outcome, { rev: cp.rev ?? 0 });
|
|
2406
|
+
if (!won) {
|
|
2407
|
+
const live = await store.get(token);
|
|
2408
|
+
if (live?.status === "pending") {
|
|
2409
|
+
throw new CheckpointError("checkpoint.reopened_concurrently", "checkpoint changed concurrently (its revision advanced via a resolve/reopen cycle since this resume validated) — not executed; re-resume against the current state");
|
|
2410
|
+
}
|
|
2411
|
+
throw new CheckpointError("checkpoint.already_resolved", "checkpoint already resolved or expired — not re-executed (idempotent)");
|
|
2412
|
+
}
|
|
2413
|
+
const spec = {
|
|
2414
|
+
...taskConfig,
|
|
2415
|
+
objective: "",
|
|
2416
|
+
sessionId: cp.sessionId,
|
|
2417
|
+
preemptSignal: taskConfig.preemptSignal?.aborted ? undefined : taskConfig.preemptSignal,
|
|
2418
|
+
};
|
|
2419
|
+
const onEnvRestoreFailed = store.reopen
|
|
2420
|
+
? async (reason) => {
|
|
2421
|
+
await store.reopen?.(token, cp.scope, reason);
|
|
2422
|
+
}
|
|
2423
|
+
: undefined;
|
|
2424
|
+
return this.runTaskStream(spec, { cp, outcome, onEnvRestoreFailed }, internals);
|
|
2425
|
+
}
|
|
2426
|
+
async applyResumeDecision(prepared, resume, emit, emitCommitted, onResolvedToolSuccess) {
|
|
2427
|
+
const { pendingAction } = resume.cp;
|
|
2428
|
+
if (resume.outcome.gate === "dry_run_review")
|
|
2429
|
+
return;
|
|
2430
|
+
if (resume.outcome.gate === "plan_review")
|
|
2431
|
+
return;
|
|
2432
|
+
if (pendingAction.kind !== "tool_approval")
|
|
2433
|
+
return;
|
|
2434
|
+
if (resume.outcome.gate === "policy_ask" &&
|
|
2435
|
+
resume.outcome.decision === "allow" &&
|
|
2436
|
+
!prepared.tools.some((t) => t.name === canonicalToolName(pendingAction.toolName))) {
|
|
2437
|
+
const e = new Error(`the approved tool "${pendingAction.toolName}" is no longer available on resume — refusing to continue as if it ran; the checkpoint is reopened for a retry with the tool present`);
|
|
2438
|
+
e.code = "resume.tool_unavailable";
|
|
2439
|
+
throw e;
|
|
2440
|
+
}
|
|
2441
|
+
const completed = new Set(pendingAction.completedCallIds);
|
|
2442
|
+
const deferredIds = pendingAction.batchToolCallIds.filter((id) => id !== pendingAction.toolCallId && !completed.has(id));
|
|
2443
|
+
const names = new Map();
|
|
2444
|
+
if (deferredIds.length > 0) {
|
|
2445
|
+
const { messages } = await prepared.session.buildContext();
|
|
2446
|
+
for (const m of messages) {
|
|
2447
|
+
if (m.role === "assistant") {
|
|
2448
|
+
for (const c of m.content) {
|
|
2449
|
+
if (c.type === "toolCall")
|
|
2450
|
+
names.set(c.id, c.name);
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
for (const id of pendingAction.batchToolCallIds) {
|
|
2456
|
+
if (id === pendingAction.toolCallId) {
|
|
2457
|
+
await this.resolvePendingCall(prepared, resume, emit, emitCommitted, onResolvedToolSuccess);
|
|
2458
|
+
}
|
|
2459
|
+
else if (!completed.has(id)) {
|
|
2460
|
+
const name = names.get(id) ?? "unknown";
|
|
2461
|
+
emit({ type: "tool_end", toolCallId: id, toolName: name, isError: true });
|
|
2462
|
+
const eid = await prepared.session.appendMessage(toolResultMsg(id, name, formatHookFeedback(DEFERRED_REISSUE), true));
|
|
2463
|
+
emitCommitted(eid, "toolResult", id);
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
async resolvePendingCall(prepared, resume, emit, emitCommitted, onResolvedToolSuccess) {
|
|
2468
|
+
const { pendingAction } = resume.cp;
|
|
2469
|
+
const { outcome } = resume;
|
|
2470
|
+
if (pendingAction.kind !== "tool_approval" || outcome.gate !== "policy_ask")
|
|
2471
|
+
return;
|
|
2472
|
+
const resolvedArgs = pendingAction.toolName === ASK_USER_QUESTION_TOOL_NAME ? pendingAction.args : (outcome.updatedInput ?? pendingAction.args);
|
|
2473
|
+
emit({ type: "tool_start", toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName, args: resolvedArgs });
|
|
2474
|
+
const emitEnd = (isError) => emit({ type: "tool_end", toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName, isError });
|
|
2475
|
+
if (outcome.decision === "deny") {
|
|
2476
|
+
emitEnd(true);
|
|
2477
|
+
const reason = outcome.reason ? delimitUntrusted("reviewer note", outcome.reason) : `The pending tool call "${pendingAction.toolName}" was denied by an approver.`;
|
|
2478
|
+
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, formatHookFeedback(reason), true));
|
|
2479
|
+
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
2480
|
+
return;
|
|
2481
|
+
}
|
|
2482
|
+
if (prepared.denyNarrowingPolicy) {
|
|
2483
|
+
const narrowed = await prepared.denyNarrowingPolicy.check({ toolName: pendingAction.toolName, args: resolvedArgs, toolCallId: pendingAction.toolCallId }, prepared.abortController.signal);
|
|
2484
|
+
if (narrowed.action === "deny") {
|
|
2485
|
+
emitEnd(true);
|
|
2486
|
+
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, formatHookFeedback(`The approved tool call "${pendingAction.toolName}" is now denied by a session rule and was not executed${narrowed.reason ? `: ${narrowed.reason}` : ""}.`), true));
|
|
2487
|
+
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
2488
|
+
return;
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
const tool = prepared.tools.find((t) => t.name === canonicalToolName(pendingAction.toolName));
|
|
2492
|
+
if (!tool) {
|
|
2493
|
+
const e = new Error(`the approved tool "${pendingAction.toolName}" is no longer available on resume`);
|
|
2494
|
+
e.code = "resume.tool_unavailable";
|
|
2495
|
+
throw e;
|
|
2496
|
+
}
|
|
2497
|
+
const args = resolvedArgs;
|
|
2498
|
+
try {
|
|
2499
|
+
const res = await tool.execute(pendingAction.toolCallId, args, prepared.abortController.signal);
|
|
2500
|
+
emitEnd(false);
|
|
2501
|
+
onResolvedToolSuccess?.(canonicalToolName(pendingAction.toolName), res.details);
|
|
2502
|
+
const eid = await prepared.session.appendMessage({
|
|
2503
|
+
role: "toolResult",
|
|
2504
|
+
toolCallId: pendingAction.toolCallId,
|
|
2505
|
+
toolName: pendingAction.toolName,
|
|
2506
|
+
content: res.content,
|
|
2507
|
+
details: res.details,
|
|
2508
|
+
isError: false,
|
|
2509
|
+
timestamp: Date.now(),
|
|
2510
|
+
});
|
|
2511
|
+
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
2512
|
+
}
|
|
2513
|
+
catch (err) {
|
|
2514
|
+
emitEnd(true);
|
|
2515
|
+
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, `Error: ${err instanceof Error ? err.message : String(err)}`, true));
|
|
2516
|
+
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
seamCCompactionOptions(prepared) {
|
|
2520
|
+
if (!this.deps.summaryProvider)
|
|
2521
|
+
return undefined;
|
|
2522
|
+
return {
|
|
2523
|
+
summaryProvider: this.deps.summaryProvider,
|
|
2524
|
+
onCompaction: this.deps.onCompaction,
|
|
2525
|
+
maxConsecutiveProviderReuse: this.deps.maxConsecutiveProviderReuse,
|
|
2526
|
+
consecutiveProviderReuse: prepared.compactionReuseRef.consecutive,
|
|
2527
|
+
};
|
|
2528
|
+
}
|
|
2529
|
+
async maybeDynamicRecall(prepared) {
|
|
2530
|
+
const dr = prepared.dynamicRecall;
|
|
2531
|
+
if (!dr)
|
|
2532
|
+
return;
|
|
2533
|
+
try {
|
|
2534
|
+
const cfg = this.deps.dynamicRecall;
|
|
2535
|
+
const { messages } = await prepared.session.buildContext();
|
|
2536
|
+
const query = buildRecallQuery(messages, cfg?.recentMessageWindow, cfg?.maxQueryChars);
|
|
2537
|
+
if (query.length === 0)
|
|
2538
|
+
return;
|
|
2539
|
+
const outcome = await runDynamicRecall({
|
|
2540
|
+
store: this.deps.memoryStore,
|
|
2541
|
+
scopes: dr.scopes,
|
|
2542
|
+
selector: dr.selector,
|
|
2543
|
+
query,
|
|
2544
|
+
recentTools: dr.recentTools,
|
|
2545
|
+
maxSelected: this.deps.memoryRecall?.maxSelected,
|
|
2546
|
+
maxLinked: this.deps.memoryRecall?.maxLinked,
|
|
2547
|
+
timeoutMs: this.deps.memoryRecall?.timeoutMs,
|
|
2548
|
+
signal: prepared.abortController.signal,
|
|
2549
|
+
priorSurfacedIds: dr.surfacedIds,
|
|
2550
|
+
priorSurfacedKeys: dr.surfacedKeys,
|
|
2551
|
+
});
|
|
2552
|
+
if (!outcome.block)
|
|
2553
|
+
return;
|
|
2554
|
+
for (const id of outcome.surfacedIds)
|
|
2555
|
+
dr.surfacedIds.add(id);
|
|
2556
|
+
for (const k of outcome.surfacedKeys)
|
|
2557
|
+
dr.surfacedKeys.add(k);
|
|
2558
|
+
await prepared.harness.steer(outcome.block);
|
|
2559
|
+
}
|
|
2560
|
+
catch {
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
compactionHookOptions(spec, sessionId, trigger) {
|
|
2564
|
+
const hooks = spec.hooks ?? this.deps.hooks;
|
|
2565
|
+
const pre = hooks?.preCompact;
|
|
2566
|
+
const post = hooks?.postCompact;
|
|
2567
|
+
return {
|
|
2568
|
+
trigger,
|
|
2569
|
+
...(pre
|
|
2570
|
+
? {
|
|
2571
|
+
preCompact: async (ctx) => {
|
|
2572
|
+
const report = (err) => {
|
|
2573
|
+
try {
|
|
2574
|
+
this.deps.onError?.(err, { phase: "hook", sessionId });
|
|
2575
|
+
}
|
|
2576
|
+
catch {
|
|
2577
|
+
}
|
|
2578
|
+
};
|
|
2579
|
+
try {
|
|
2580
|
+
const r = await pre.call(hooks, ctx);
|
|
2581
|
+
if (r?.block && ctx.trigger === "forced") {
|
|
2582
|
+
report(new Error(`a preCompact callback blocked a "forced" compaction — ignored (PTL/trim-pressure compaction is not optional): ${r.block}`));
|
|
2583
|
+
}
|
|
2584
|
+
return r;
|
|
2585
|
+
}
|
|
2586
|
+
catch (err) {
|
|
2587
|
+
report(err instanceof Error ? err : new Error(String(err)));
|
|
2588
|
+
return undefined;
|
|
2589
|
+
}
|
|
2590
|
+
},
|
|
2591
|
+
}
|
|
2592
|
+
: {}),
|
|
2593
|
+
...(post
|
|
2594
|
+
? {
|
|
2595
|
+
postCompact: async (ctx) => {
|
|
2596
|
+
try {
|
|
2597
|
+
await post.call(hooks, ctx);
|
|
2598
|
+
}
|
|
2599
|
+
catch (err) {
|
|
2600
|
+
try {
|
|
2601
|
+
this.deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "hook", sessionId });
|
|
2602
|
+
}
|
|
2603
|
+
catch {
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
},
|
|
2607
|
+
}
|
|
2608
|
+
: {}),
|
|
2609
|
+
};
|
|
2610
|
+
}
|
|
2611
|
+
recordCompactionReuse(prepared, comp) {
|
|
2612
|
+
if (!this.deps.summaryProvider || !comp.compacted)
|
|
2613
|
+
return;
|
|
2614
|
+
prepared.compactionReuseRef.consecutive = comp.reused ? prepared.compactionReuseRef.consecutive + 1 : 0;
|
|
2615
|
+
}
|
|
2616
|
+
async finish(spec, prepared, opts) {
|
|
2617
|
+
let comp;
|
|
2618
|
+
if (!opts?.skipCompaction) {
|
|
2619
|
+
try {
|
|
2620
|
+
const finishComp = await maybeCompact({
|
|
2621
|
+
session: prepared.session,
|
|
2622
|
+
model: prepared.model,
|
|
2623
|
+
compactionModel: prepared.compModel,
|
|
2624
|
+
brain: opts?.brain ?? this.deps.brain,
|
|
2625
|
+
getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
|
|
2626
|
+
thinking: prepared.thinking,
|
|
2627
|
+
settings: { ...DEFAULT_COMPACTION_SETTINGS, ...spec.compaction },
|
|
2628
|
+
customInstructions: spec.compaction?.instructions ?? DEFAULT_COMPACTION_INSTRUCTIONS,
|
|
2629
|
+
minTokens: opts?.minTokens,
|
|
2630
|
+
overheadTokens: prepared.promptOverheadTokens,
|
|
2631
|
+
onInputTruncated: (info) => emitTrace(spec.tracer ?? this.deps.tracer, () => ({
|
|
2632
|
+
kind: "compaction.input_truncated",
|
|
2633
|
+
version: 1,
|
|
2634
|
+
taskId: spec.taskId ?? prepared.sessionId,
|
|
2635
|
+
label: info.label,
|
|
2636
|
+
droppedChars: info.droppedChars,
|
|
2637
|
+
keptChars: info.keptChars,
|
|
2638
|
+
ts: Date.now(),
|
|
2639
|
+
})),
|
|
2640
|
+
workingFileAttachments: spec.compaction?.attachWorkingFiles !== false && prepared.readTaskFile
|
|
2641
|
+
? {
|
|
2642
|
+
readFile: prepared.readTaskFile,
|
|
2643
|
+
...(prepared.recentlyReadFiles ? { recentlyReadFiles: prepared.recentlyReadFiles } : {}),
|
|
2644
|
+
...(typeof spec.compaction?.attachWorkingFiles === "object" ? spec.compaction.attachWorkingFiles : undefined),
|
|
2645
|
+
}
|
|
2646
|
+
: undefined,
|
|
2647
|
+
...this.seamCCompactionOptions(prepared),
|
|
2648
|
+
...this.compactionHookOptions(spec, prepared.sessionId, "auto"),
|
|
2649
|
+
});
|
|
2650
|
+
this.recordCompactionReuse(prepared, finishComp);
|
|
2651
|
+
comp = finishComp;
|
|
2652
|
+
}
|
|
2653
|
+
catch (err) {
|
|
2654
|
+
this.deps.onError?.(err, { phase: "compaction", sessionId: prepared.sessionId });
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
try {
|
|
2658
|
+
await this.sessions.touch(prepared.sessionId);
|
|
2659
|
+
}
|
|
2660
|
+
catch (err) {
|
|
2661
|
+
this.deps.onError?.(err, { phase: "config", sessionId: prepared.sessionId });
|
|
2662
|
+
}
|
|
2663
|
+
try {
|
|
2664
|
+
await prepared.mcp.dispose();
|
|
2665
|
+
}
|
|
2666
|
+
catch (err) {
|
|
2667
|
+
this.deps.onError?.(err, { phase: "mcp", sessionId: prepared.sessionId });
|
|
2668
|
+
}
|
|
2669
|
+
return comp;
|
|
2670
|
+
}
|
|
2671
|
+
async teardownOwnedEnv(prepared) {
|
|
2672
|
+
if (prepared.ownedEnv &&
|
|
2673
|
+
hasDestroy(prepared.ownedEnv) &&
|
|
2674
|
+
prepared.suspendRef.token === undefined &&
|
|
2675
|
+
prepared.reviewRef.token === undefined) {
|
|
2676
|
+
try {
|
|
2677
|
+
await prepared.ownedEnv.destroy();
|
|
2678
|
+
}
|
|
2679
|
+
catch (err) {
|
|
2680
|
+
this.deps.onError?.(err, { phase: "config", sessionId: prepared.sessionId });
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
export function runTask(spec, deps) {
|
|
2686
|
+
return new Runner(deps).runTask(spec);
|
|
2687
|
+
}
|
|
2688
|
+
const WALLTIME_SUSPEND_GRACE_SEC = 120;
|
|
2689
|
+
function startTimeout(harness, abortController, timeoutSec, softSuspendable = false) {
|
|
2690
|
+
const state = { fired: false, clear: () => { } };
|
|
2691
|
+
if (timeoutSec && timeoutSec > 0) {
|
|
2692
|
+
const hardSec = softSuspendable ? timeoutSec + WALLTIME_SUSPEND_GRACE_SEC : timeoutSec;
|
|
2693
|
+
const timer = setTimeout(() => {
|
|
2694
|
+
state.fired = true;
|
|
2695
|
+
abortController.abort();
|
|
2696
|
+
void harness.abort();
|
|
2697
|
+
}, hardSec * 1000);
|
|
2698
|
+
timer.unref?.();
|
|
2699
|
+
state.clear = () => clearTimeout(timer);
|
|
2700
|
+
}
|
|
2701
|
+
return state;
|
|
2702
|
+
}
|
|
2703
|
+
//# sourceMappingURL=runtask.js.map
|