@caupulican/pi-adaptative 0.81.38 → 0.81.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +131 -0
- package/README.md +2 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/README.md +18 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts +133 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts.map +1 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js +198 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js.map +1 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.ts +311 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts +31 -2
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts.map +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.js +823 -79
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.js.map +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.ts +1043 -86
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/__init__.py +43 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/fs.py +270 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/search.py +252 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/strings.py +399 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/text.py +575 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/context.py +52 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/errors.py +49 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/exec.py +734 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/expand.py +238 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/main.py +132 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/nodes.py +116 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/parser.py +287 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/proc.py +137 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/state.py +100 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/tokens.py +579 -0
- package/dist/bundled-resources/skills/tool-call-repair/SKILL.md +19 -9
- package/dist/bundled-resources/skills/tool-call-repair/references/failure-grammar.md +22 -5
- package/dist/bundled-resources/skills/tool-call-repair/references/repair-catalogue.md +7 -7
- package/dist/bundled-resources/skills/tool-call-repair/references/text-protocol-grammar.md +28 -7
- package/dist/cli/args.d.ts +5 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +24 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-directory-layout.d.ts +15 -0
- package/dist/core/agent-directory-layout.d.ts.map +1 -0
- package/dist/core/agent-directory-layout.js +72 -0
- package/dist/core/agent-directory-layout.js.map +1 -0
- package/dist/core/agent-paths.d.ts +66 -0
- package/dist/core/agent-paths.d.ts.map +1 -0
- package/dist/core/agent-paths.js +170 -0
- package/dist/core/agent-paths.js.map +1 -0
- package/dist/core/agent-session-runtime.d.ts +52 -8
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +397 -79
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +1 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +4 -3
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +255 -103
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +754 -1173
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/autonomy/approval-gate.d.ts +3 -2
- package/dist/core/autonomy/approval-gate.d.ts.map +1 -1
- package/dist/core/autonomy/approval-gate.js +6 -28
- package/dist/core/autonomy/approval-gate.js.map +1 -1
- package/dist/core/autonomy/bounded-completion.d.ts +1 -1
- package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
- package/dist/core/autonomy/bounded-completion.js +1 -1
- package/dist/core/autonomy/bounded-completion.js.map +1 -1
- package/dist/core/autonomy/contracts.d.ts +26 -9
- package/dist/core/autonomy/contracts.d.ts.map +1 -1
- package/dist/core/autonomy/contracts.js.map +1 -1
- package/dist/core/autonomy/foreground-envelope.d.ts +8 -2
- package/dist/core/autonomy/foreground-envelope.d.ts.map +1 -1
- package/dist/core/autonomy/foreground-envelope.js +8 -25
- package/dist/core/autonomy/foreground-envelope.js.map +1 -1
- package/dist/core/autonomy/gates.d.ts.map +1 -1
- package/dist/core/autonomy/gates.js +8 -10
- package/dist/core/autonomy/gates.js.map +1 -1
- package/dist/core/autonomy/lane-private-paths.d.ts +2 -0
- package/dist/core/autonomy/lane-private-paths.d.ts.map +1 -0
- package/dist/core/autonomy/lane-private-paths.js +16 -0
- package/dist/core/autonomy/lane-private-paths.js.map +1 -0
- package/dist/core/autonomy/lane-tool-surface.d.ts +10 -0
- package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
- package/dist/core/autonomy/lane-tool-surface.js +46 -8
- package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
- package/dist/core/autonomy/lane-tracker.d.ts +24 -1
- package/dist/core/autonomy/lane-tracker.d.ts.map +1 -1
- package/dist/core/autonomy/lane-tracker.js +43 -1
- package/dist/core/autonomy/lane-tracker.js.map +1 -1
- package/dist/core/autonomy/risk-assessment.d.ts +1 -2
- package/dist/core/autonomy/risk-assessment.d.ts.map +1 -1
- package/dist/core/autonomy/risk-assessment.js +1 -123
- package/dist/core/autonomy/risk-assessment.js.map +1 -1
- package/dist/core/autonomy/session-lane-record.d.ts +4 -4
- package/dist/core/autonomy/session-lane-record.d.ts.map +1 -1
- package/dist/core/autonomy/session-lane-record.js +16 -29
- package/dist/core/autonomy/session-lane-record.js.map +1 -1
- package/dist/core/autonomy/telemetry-events.d.ts +1 -1
- package/dist/core/autonomy/telemetry-events.d.ts.map +1 -1
- package/dist/core/autonomy/telemetry-events.js +1 -1
- package/dist/core/autonomy/telemetry-events.js.map +1 -1
- package/dist/core/autonomy-telemetry.d.ts +7 -7
- package/dist/core/autonomy-telemetry.d.ts.map +1 -1
- package/dist/core/autonomy-telemetry.js +9 -9
- package/dist/core/autonomy-telemetry.js.map +1 -1
- package/dist/core/background-lane-controller.d.ts +90 -156
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +192 -968
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/bash-execution-controller.d.ts +4 -0
- package/dist/core/bash-execution-controller.d.ts.map +1 -1
- package/dist/core/bash-execution-controller.js +7 -1
- package/dist/core/bash-execution-controller.js.map +1 -1
- package/dist/core/capability-contract.d.ts +4 -0
- package/dist/core/capability-contract.d.ts.map +1 -0
- package/dist/core/capability-contract.js +28 -0
- package/dist/core/capability-contract.js.map +1 -0
- package/dist/core/compaction-controller.d.ts +90 -0
- package/dist/core/compaction-controller.d.ts.map +1 -0
- package/dist/core/compaction-controller.js +452 -0
- package/dist/core/compaction-controller.js.map +1 -0
- package/dist/core/compaction-support.d.ts +24 -13
- package/dist/core/compaction-support.d.ts.map +1 -1
- package/dist/core/compaction-support.js +43 -7
- package/dist/core/compaction-support.js.map +1 -1
- package/dist/core/context/context-artifacts.d.ts +6 -2
- package/dist/core/context/context-artifacts.d.ts.map +1 -1
- package/dist/core/context/context-artifacts.js +34 -2
- package/dist/core/context/context-artifacts.js.map +1 -1
- package/dist/core/context/context-audit.d.ts +33 -1
- package/dist/core/context/context-audit.d.ts.map +1 -1
- package/dist/core/context/context-audit.js +31 -5
- package/dist/core/context/context-audit.js.map +1 -1
- package/dist/core/context/context-store-retention.d.ts +8 -2
- package/dist/core/context/context-store-retention.d.ts.map +1 -1
- package/dist/core/context/context-store-retention.js +158 -35
- package/dist/core/context/context-store-retention.js.map +1 -1
- package/dist/core/context/okf-memory.d.ts.map +1 -1
- package/dist/core/context/okf-memory.js +9 -11
- package/dist/core/context/okf-memory.js.map +1 -1
- package/dist/core/context/sqlite-runtime-index.d.ts.map +1 -1
- package/dist/core/context/sqlite-runtime-index.js +6 -8
- package/dist/core/context/sqlite-runtime-index.js.map +1 -1
- package/dist/core/context-gc.d.ts +3 -0
- package/dist/core/context-gc.d.ts.map +1 -1
- package/dist/core/context-gc.js +26 -6
- package/dist/core/context-gc.js.map +1 -1
- package/dist/core/context-pipeline.d.ts +26 -4
- package/dist/core/context-pipeline.d.ts.map +1 -1
- package/dist/core/context-pipeline.js +146 -18
- package/dist/core/context-pipeline.js.map +1 -1
- package/dist/core/cost-guard.d.ts +19 -3
- package/dist/core/cost-guard.d.ts.map +1 -1
- package/dist/core/cost-guard.js +18 -3
- package/dist/core/cost-guard.js.map +1 -1
- package/dist/core/default-tool-surface.d.ts.map +1 -1
- package/dist/core/default-tool-surface.js +1 -0
- package/dist/core/default-tool-surface.js.map +1 -1
- package/dist/core/delegation/managed-lane-controller.d.ts +34 -0
- package/dist/core/delegation/managed-lane-controller.d.ts.map +1 -0
- package/dist/core/delegation/managed-lane-controller.js +217 -0
- package/dist/core/delegation/managed-lane-controller.js.map +1 -0
- package/dist/core/delegation/session-worker-claim.d.ts +44 -0
- package/dist/core/delegation/session-worker-claim.d.ts.map +1 -0
- package/dist/core/delegation/session-worker-claim.js +98 -0
- package/dist/core/delegation/session-worker-claim.js.map +1 -0
- package/dist/core/delegation/worker-actions.d.ts +7 -5
- package/dist/core/delegation/worker-actions.d.ts.map +1 -1
- package/dist/core/delegation/worker-actions.js +20 -4
- package/dist/core/delegation/worker-actions.js.map +1 -1
- package/dist/core/delegation/worker-claim.d.ts +56 -0
- package/dist/core/delegation/worker-claim.d.ts.map +1 -0
- package/dist/core/delegation/worker-claim.js +239 -0
- package/dist/core/delegation/worker-claim.js.map +1 -0
- package/dist/core/delegation/worker-delegation-controller.d.ts +110 -0
- package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -0
- package/dist/core/delegation/worker-delegation-controller.js +861 -0
- package/dist/core/delegation/worker-delegation-controller.js.map +1 -0
- package/dist/core/delegation/worker-delegation-request.d.ts +11 -0
- package/dist/core/delegation/worker-delegation-request.d.ts.map +1 -0
- package/dist/core/delegation/worker-delegation-request.js +2 -0
- package/dist/core/delegation/worker-delegation-request.js.map +1 -0
- package/dist/core/delegation/worker-dispatch-scheduler.d.ts +40 -0
- package/dist/core/delegation/worker-dispatch-scheduler.d.ts.map +1 -0
- package/dist/core/delegation/worker-dispatch-scheduler.js +93 -0
- package/dist/core/delegation/worker-dispatch-scheduler.js.map +1 -0
- package/dist/core/delegation/worker-execution-policy.d.ts +62 -0
- package/dist/core/delegation/worker-execution-policy.d.ts.map +1 -0
- package/dist/core/delegation/worker-execution-policy.js +160 -0
- package/dist/core/delegation/worker-execution-policy.js.map +1 -0
- package/dist/core/delegation/worker-lane-projection.d.ts +14 -0
- package/dist/core/delegation/worker-lane-projection.d.ts.map +1 -0
- package/dist/core/delegation/worker-lane-projection.js +100 -0
- package/dist/core/delegation/worker-lane-projection.js.map +1 -0
- package/dist/core/delegation/worker-lifecycle.d.ts +97 -0
- package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -0
- package/dist/core/delegation/worker-lifecycle.js +339 -0
- package/dist/core/delegation/worker-lifecycle.js.map +1 -0
- package/dist/core/delegation/worker-notification-coordinator.d.ts +36 -0
- package/dist/core/delegation/worker-notification-coordinator.d.ts.map +1 -0
- package/dist/core/delegation/worker-notification-coordinator.js +90 -0
- package/dist/core/delegation/worker-notification-coordinator.js.map +1 -0
- package/dist/core/delegation/worker-profile-resolver.d.ts +55 -0
- package/dist/core/delegation/worker-profile-resolver.d.ts.map +1 -0
- package/dist/core/delegation/worker-profile-resolver.js +113 -0
- package/dist/core/delegation/worker-profile-resolver.js.map +1 -0
- package/dist/core/delegation/worker-runner.d.ts +25 -14
- package/dist/core/delegation/worker-runner.d.ts.map +1 -1
- package/dist/core/delegation/worker-runner.js +122 -53
- package/dist/core/delegation/worker-runner.js.map +1 -1
- package/dist/core/delegation/worker-task-label.d.ts +3 -0
- package/dist/core/delegation/worker-task-label.d.ts.map +1 -0
- package/dist/core/delegation/worker-task-label.js +11 -0
- package/dist/core/delegation/worker-task-label.js.map +1 -0
- package/dist/core/doctor.d.ts +8 -0
- package/dist/core/doctor.d.ts.map +1 -1
- package/dist/core/doctor.js +32 -1
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +5 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +68 -13
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +7 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/storage.d.ts +8 -0
- package/dist/core/extensions/storage.d.ts.map +1 -0
- package/dist/core/extensions/storage.js +28 -0
- package/dist/core/extensions/storage.js.map +1 -0
- package/dist/core/extensions/types.d.ts +101 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/failure-corpus.d.ts +1 -15
- package/dist/core/failure-corpus.d.ts.map +1 -1
- package/dist/core/failure-corpus.js +9 -37
- package/dist/core/failure-corpus.js.map +1 -1
- package/dist/core/foreground-recovery-controller.d.ts +52 -0
- package/dist/core/foreground-recovery-controller.d.ts.map +1 -0
- package/dist/core/foreground-recovery-controller.js +111 -0
- package/dist/core/foreground-recovery-controller.js.map +1 -0
- package/dist/core/gateways/channel-provider.d.ts +3 -0
- package/dist/core/gateways/channel-provider.d.ts.map +1 -1
- package/dist/core/gateways/channel-provider.js +31 -6
- package/dist/core/gateways/channel-provider.js.map +1 -1
- package/dist/core/goal-loop-controller.d.ts +17 -1
- package/dist/core/goal-loop-controller.d.ts.map +1 -1
- package/dist/core/goal-loop-controller.js +79 -11
- package/dist/core/goal-loop-controller.js.map +1 -1
- package/dist/core/goals/goal-acceptance.d.ts +11 -0
- package/dist/core/goals/goal-acceptance.d.ts.map +1 -0
- package/dist/core/goals/goal-acceptance.js +25 -0
- package/dist/core/goals/goal-acceptance.js.map +1 -0
- package/dist/core/goals/goal-auto-continue-controller.d.ts +27 -0
- package/dist/core/goals/goal-auto-continue-controller.d.ts.map +1 -0
- package/dist/core/goals/goal-auto-continue-controller.js +93 -0
- package/dist/core/goals/goal-auto-continue-controller.js.map +1 -0
- package/dist/core/goals/goal-continuation-controller.d.ts +48 -2
- package/dist/core/goals/goal-continuation-controller.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-controller.js +87 -0
- package/dist/core/goals/goal-continuation-controller.js.map +1 -1
- package/dist/core/goals/goal-continuation-defaults.d.ts +39 -0
- package/dist/core/goals/goal-continuation-defaults.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-defaults.js +42 -0
- package/dist/core/goals/goal-continuation-defaults.js.map +1 -1
- package/dist/core/goals/goal-continuation-prompt.d.ts +5 -1
- package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-prompt.js +56 -15
- package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
- package/dist/core/goals/goal-lifecycle.d.ts +20 -0
- package/dist/core/goals/goal-lifecycle.d.ts.map +1 -0
- package/dist/core/goals/goal-lifecycle.js +17 -0
- package/dist/core/goals/goal-lifecycle.js.map +1 -0
- package/dist/core/goals/goal-runtime-snapshot.d.ts +105 -4
- package/dist/core/goals/goal-runtime-snapshot.d.ts.map +1 -1
- package/dist/core/goals/goal-runtime-snapshot.js +91 -6
- package/dist/core/goals/goal-runtime-snapshot.js.map +1 -1
- package/dist/core/goals/goal-state.d.ts +90 -1
- package/dist/core/goals/goal-state.d.ts.map +1 -1
- package/dist/core/goals/goal-state.js +78 -15
- package/dist/core/goals/goal-state.js.map +1 -1
- package/dist/core/goals/goal-tool-core.d.ts +58 -2
- package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
- package/dist/core/goals/goal-tool-core.js +82 -6
- package/dist/core/goals/goal-tool-core.js.map +1 -1
- package/dist/core/goals/session-goal-state.d.ts +13 -6
- package/dist/core/goals/session-goal-state.d.ts.map +1 -1
- package/dist/core/goals/session-goal-state.js +20 -28
- package/dist/core/goals/session-goal-state.js.map +1 -1
- package/dist/core/human-input.d.ts +100 -0
- package/dist/core/human-input.d.ts.map +1 -0
- package/dist/core/human-input.js +362 -0
- package/dist/core/human-input.js.map +1 -0
- package/dist/core/index.d.ts +9 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +9 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/{telemetry.d.ts → install-telemetry.d.ts} +1 -1
- package/dist/core/install-telemetry.d.ts.map +1 -0
- package/dist/core/{telemetry.js → install-telemetry.js} +1 -1
- package/dist/core/install-telemetry.js.map +1 -0
- package/dist/core/keybindings.d.ts +27 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +34 -8
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/learning/learning-audit.d.ts +2 -4
- package/dist/core/learning/learning-audit.d.ts.map +1 -1
- package/dist/core/learning/learning-audit.js +9 -29
- package/dist/core/learning/learning-audit.js.map +1 -1
- package/dist/core/learning/learning-gate.d.ts.map +1 -1
- package/dist/core/learning/learning-gate.js +1 -6
- package/dist/core/learning/learning-gate.js.map +1 -1
- package/dist/core/learning/observation-store.d.ts +12 -3
- package/dist/core/learning/observation-store.d.ts.map +1 -1
- package/dist/core/learning/observation-store.js +30 -15
- package/dist/core/learning/observation-store.js.map +1 -1
- package/dist/core/learning/session-learning-decision.d.ts +2 -4
- package/dist/core/learning/session-learning-decision.d.ts.map +1 -1
- package/dist/core/learning/session-learning-decision.js +9 -29
- package/dist/core/learning/session-learning-decision.js.map +1 -1
- package/dist/core/learning/skill-curator.d.ts +5 -1
- package/dist/core/learning/skill-curator.d.ts.map +1 -1
- package/dist/core/learning/skill-curator.js +21 -19
- package/dist/core/learning/skill-curator.js.map +1 -1
- package/dist/core/local-runtime-controller.d.ts +65 -3
- package/dist/core/local-runtime-controller.d.ts.map +1 -1
- package/dist/core/local-runtime-controller.js +186 -26
- package/dist/core/local-runtime-controller.js.map +1 -1
- package/dist/core/memory/providers/file-store.d.ts +1 -1
- package/dist/core/memory/providers/file-store.d.ts.map +1 -1
- package/dist/core/memory/providers/file-store.js +6 -6
- package/dist/core/memory/providers/file-store.js.map +1 -1
- package/dist/core/memory-controller.d.ts.map +1 -1
- package/dist/core/memory-controller.js +4 -4
- package/dist/core/memory-controller.js.map +1 -1
- package/dist/core/model-capability.d.ts +34 -0
- package/dist/core/model-capability.d.ts.map +1 -1
- package/dist/core/model-capability.js +43 -1
- package/dist/core/model-capability.js.map +1 -1
- package/dist/core/model-resolver.d.ts +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +6 -9
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-router/intent-classifier.d.ts +1 -4
- package/dist/core/model-router/intent-classifier.d.ts.map +1 -1
- package/dist/core/model-router/intent-classifier.js +1 -156
- package/dist/core/model-router/intent-classifier.js.map +1 -1
- package/dist/core/model-router/tool-escalation.d.ts +15 -0
- package/dist/core/model-router/tool-escalation.d.ts.map +1 -1
- package/dist/core/model-router/tool-escalation.js +23 -1
- package/dist/core/model-router/tool-escalation.js.map +1 -1
- package/dist/core/model-router-controller.d.ts +34 -7
- package/dist/core/model-router-controller.d.ts.map +1 -1
- package/dist/core/model-router-controller.js +95 -16
- package/dist/core/model-router-controller.js.map +1 -1
- package/dist/core/model-selection-controller.d.ts +4 -4
- package/dist/core/model-selection-controller.d.ts.map +1 -1
- package/dist/core/model-selection-controller.js.map +1 -1
- package/dist/core/model-tool-protocol.d.ts +23 -0
- package/dist/core/model-tool-protocol.d.ts.map +1 -0
- package/dist/core/model-tool-protocol.js +43 -0
- package/dist/core/model-tool-protocol.js.map +1 -0
- package/dist/core/models/adaptation-store.d.ts +10 -5
- package/dist/core/models/adaptation-store.d.ts.map +1 -1
- package/dist/core/models/adaptation-store.js +129 -81
- package/dist/core/models/adaptation-store.js.map +1 -1
- package/dist/core/models/default-model-suggestions.d.ts.map +1 -1
- package/dist/core/models/default-model-suggestions.js +17 -0
- package/dist/core/models/default-model-suggestions.js.map +1 -1
- package/dist/core/models/fitness-store.d.ts +4 -11
- package/dist/core/models/fitness-store.d.ts.map +1 -1
- package/dist/core/models/fitness-store.js +87 -51
- package/dist/core/models/fitness-store.js.map +1 -1
- package/dist/core/models/host-state-store.d.ts +34 -0
- package/dist/core/models/host-state-store.d.ts.map +1 -0
- package/dist/core/models/host-state-store.js +86 -0
- package/dist/core/models/host-state-store.js.map +1 -0
- package/dist/core/models/llamacpp-runtime.d.ts +180 -0
- package/dist/core/models/llamacpp-runtime.d.ts.map +1 -0
- package/dist/core/models/llamacpp-runtime.js +475 -0
- package/dist/core/models/llamacpp-runtime.js.map +1 -0
- package/dist/core/models/local-registration.d.ts +40 -0
- package/dist/core/models/local-registration.d.ts.map +1 -1
- package/dist/core/models/local-registration.js +94 -5
- package/dist/core/models/local-registration.js.map +1 -1
- package/dist/core/models/local-runtime.d.ts.map +1 -1
- package/dist/core/models/local-runtime.js +6 -5
- package/dist/core/models/local-runtime.js.map +1 -1
- package/dist/core/models/model-ref.d.ts +7 -0
- package/dist/core/models/model-ref.d.ts.map +1 -1
- package/dist/core/models/model-ref.js +26 -0
- package/dist/core/models/model-ref.js.map +1 -1
- package/dist/core/models/needle-runtime.d.ts +257 -0
- package/dist/core/models/needle-runtime.d.ts.map +1 -0
- package/dist/core/models/needle-runtime.js +519 -0
- package/dist/core/models/needle-runtime.js.map +1 -0
- package/dist/core/models/prism-llamacpp-lifecycle.d.ts +89 -0
- package/dist/core/models/prism-llamacpp-lifecycle.d.ts.map +1 -0
- package/dist/core/models/prism-llamacpp-lifecycle.js +121 -0
- package/dist/core/models/prism-llamacpp-lifecycle.js.map +1 -0
- package/dist/core/orchestration/agent-resume.d.ts +24 -0
- package/dist/core/orchestration/agent-resume.d.ts.map +1 -0
- package/dist/core/orchestration/agent-resume.js +90 -0
- package/dist/core/orchestration/agent-resume.js.map +1 -0
- package/dist/core/orchestration/capability-gateway.d.ts +61 -0
- package/dist/core/orchestration/capability-gateway.d.ts.map +1 -0
- package/dist/core/orchestration/capability-gateway.js +152 -0
- package/dist/core/orchestration/capability-gateway.js.map +1 -0
- package/dist/core/orchestration/contracts.d.ts +343 -0
- package/dist/core/orchestration/contracts.d.ts.map +1 -0
- package/dist/core/orchestration/contracts.js +135 -0
- package/dist/core/orchestration/contracts.js.map +1 -0
- package/dist/core/orchestration/delegation-ledger.d.ts +56 -0
- package/dist/core/orchestration/delegation-ledger.d.ts.map +1 -0
- package/dist/core/orchestration/delegation-ledger.js +197 -0
- package/dist/core/orchestration/delegation-ledger.js.map +1 -0
- package/dist/core/orchestration/event-store.d.ts +83 -0
- package/dist/core/orchestration/event-store.d.ts.map +1 -0
- package/dist/core/orchestration/event-store.js +560 -0
- package/dist/core/orchestration/event-store.js.map +1 -0
- package/dist/core/orchestration/lane-tool-manifests.d.ts +9 -0
- package/dist/core/orchestration/lane-tool-manifests.d.ts.map +1 -0
- package/dist/core/orchestration/lane-tool-manifests.js +37 -0
- package/dist/core/orchestration/lane-tool-manifests.js.map +1 -0
- package/dist/core/orchestration/model-binding.d.ts +15 -0
- package/dist/core/orchestration/model-binding.d.ts.map +1 -0
- package/dist/core/orchestration/model-binding.js +29 -0
- package/dist/core/orchestration/model-binding.js.map +1 -0
- package/dist/core/orchestration/policy-compiler.d.ts +49 -0
- package/dist/core/orchestration/policy-compiler.d.ts.map +1 -0
- package/dist/core/orchestration/policy-compiler.js +191 -0
- package/dist/core/orchestration/policy-compiler.js.map +1 -0
- package/dist/core/orchestration/policy-gate.d.ts +18 -0
- package/dist/core/orchestration/policy-gate.d.ts.map +1 -0
- package/dist/core/orchestration/policy-gate.js +24 -0
- package/dist/core/orchestration/policy-gate.js.map +1 -0
- package/dist/core/orchestration/profile-registry.d.ts +51 -0
- package/dist/core/orchestration/profile-registry.d.ts.map +1 -0
- package/dist/core/orchestration/profile-registry.js +393 -0
- package/dist/core/orchestration/profile-registry.js.map +1 -0
- package/dist/core/orchestration/profile-store.d.ts +31 -0
- package/dist/core/orchestration/profile-store.d.ts.map +1 -0
- package/dist/core/orchestration/profile-store.js +136 -0
- package/dist/core/orchestration/profile-store.js.map +1 -0
- package/dist/core/orchestration/risk-budget.d.ts +9 -0
- package/dist/core/orchestration/risk-budget.d.ts.map +1 -0
- package/dist/core/orchestration/risk-budget.js +48 -0
- package/dist/core/orchestration/risk-budget.js.map +1 -0
- package/dist/core/orchestration/task-runtime.d.ts +168 -0
- package/dist/core/orchestration/task-runtime.d.ts.map +1 -0
- package/dist/core/orchestration/task-runtime.js +1600 -0
- package/dist/core/orchestration/task-runtime.js.map +1 -0
- package/dist/core/orchestration/work-state-projection.d.ts +32 -0
- package/dist/core/orchestration/work-state-projection.d.ts.map +1 -0
- package/dist/core/orchestration/work-state-projection.js +116 -0
- package/dist/core/orchestration/work-state-projection.js.map +1 -0
- package/dist/core/orchestration/worker-execution-contract.d.ts +21 -0
- package/dist/core/orchestration/worker-execution-contract.d.ts.map +1 -0
- package/dist/core/orchestration/worker-execution-contract.js +179 -0
- package/dist/core/orchestration/worker-execution-contract.js.map +1 -0
- package/dist/core/orchestration/worker-result-adapter.d.ts +21 -0
- package/dist/core/orchestration/worker-result-adapter.d.ts.map +1 -0
- package/dist/core/orchestration/worker-result-adapter.js +109 -0
- package/dist/core/orchestration/worker-result-adapter.js.map +1 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +11 -10
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/process-identity.d.ts +11 -0
- package/dist/core/process-identity.d.ts.map +1 -0
- package/dist/core/process-identity.js +26 -0
- package/dist/core/process-identity.js.map +1 -0
- package/dist/core/process-matrix/codes.d.ts +83 -0
- package/dist/core/process-matrix/codes.d.ts.map +1 -0
- package/dist/core/process-matrix/codes.js +15 -0
- package/dist/core/process-matrix/codes.js.map +1 -0
- package/dist/core/process-matrix/resume-launcher.d.ts +26 -0
- package/dist/core/process-matrix/resume-launcher.d.ts.map +1 -0
- package/dist/core/process-matrix/resume-launcher.js +62 -0
- package/dist/core/process-matrix/resume-launcher.js.map +1 -0
- package/dist/core/process-matrix/runtime.d.ts +78 -0
- package/dist/core/process-matrix/runtime.d.ts.map +1 -0
- package/dist/core/process-matrix/runtime.js +633 -0
- package/dist/core/process-matrix/runtime.js.map +1 -0
- package/dist/core/process-matrix/store.d.ts +25 -0
- package/dist/core/process-matrix/store.d.ts.map +1 -0
- package/dist/core/process-matrix/store.js +189 -0
- package/dist/core/process-matrix/store.js.map +1 -0
- package/dist/core/process-matrix/supervisor.d.ts +75 -0
- package/dist/core/process-matrix/supervisor.d.ts.map +1 -0
- package/dist/core/process-matrix/supervisor.js +161 -0
- package/dist/core/process-matrix/supervisor.js.map +1 -0
- package/dist/core/profile-registry.d.ts +7 -0
- package/dist/core/profile-registry.d.ts.map +1 -1
- package/dist/core/profile-registry.js +25 -7
- package/dist/core/profile-registry.js.map +1 -1
- package/dist/core/python-runtime.d.ts.map +1 -1
- package/dist/core/python-runtime.js +3 -3
- package/dist/core/python-runtime.js.map +1 -1
- package/dist/core/reflection-controller.d.ts +33 -9
- package/dist/core/reflection-controller.d.ts.map +1 -1
- package/dist/core/reflection-controller.js +217 -127
- package/dist/core/reflection-controller.js.map +1 -1
- package/dist/core/reload-blockers.d.ts +36 -0
- package/dist/core/reload-blockers.d.ts.map +1 -1
- package/dist/core/reload-blockers.js +50 -8
- package/dist/core/reload-blockers.js.map +1 -1
- package/dist/core/research/evidence-bundle.d.ts.map +1 -1
- package/dist/core/research/evidence-bundle.js +1 -6
- package/dist/core/research/evidence-bundle.js.map +1 -1
- package/dist/core/research/lane-model-resolver.d.ts +33 -0
- package/dist/core/research/lane-model-resolver.d.ts.map +1 -0
- package/dist/core/research/lane-model-resolver.js +65 -0
- package/dist/core/research/lane-model-resolver.js.map +1 -0
- package/dist/core/research/model-fitness-controller.d.ts +38 -0
- package/dist/core/research/model-fitness-controller.d.ts.map +1 -0
- package/dist/core/research/model-fitness-controller.js +106 -0
- package/dist/core/research/model-fitness-controller.js.map +1 -0
- package/dist/core/research/model-fitness.d.ts +1 -1
- package/dist/core/research/model-fitness.d.ts.map +1 -1
- package/dist/core/research/model-fitness.js +4 -4
- package/dist/core/research/model-fitness.js.map +1 -1
- package/dist/core/research/research-gate.d.ts.map +1 -1
- package/dist/core/research/research-gate.js +7 -7
- package/dist/core/research/research-gate.js.map +1 -1
- package/dist/core/research/research-lane-controller.d.ts +62 -0
- package/dist/core/research/research-lane-controller.d.ts.map +1 -0
- package/dist/core/research/research-lane-controller.js +292 -0
- package/dist/core/research/research-lane-controller.js.map +1 -0
- package/dist/core/research/session-evidence-bundle.d.ts +2 -4
- package/dist/core/research/session-evidence-bundle.d.ts.map +1 -1
- package/dist/core/research/session-evidence-bundle.js +10 -46
- package/dist/core/research/session-evidence-bundle.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +15 -9
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/risk-classifier.d.ts +6 -0
- package/dist/core/risk-classifier.d.ts.map +1 -0
- package/dist/core/risk-classifier.js +225 -0
- package/dist/core/risk-classifier.js.map +1 -0
- package/dist/core/runtime-builder.d.ts +76 -17
- package/dist/core/runtime-builder.d.ts.map +1 -1
- package/dist/core/runtime-builder.js +367 -124
- package/dist/core/runtime-builder.js.map +1 -1
- package/dist/core/scout-controller.d.ts +6 -0
- package/dist/core/scout-controller.d.ts.map +1 -1
- package/dist/core/scout-controller.js +66 -52
- package/dist/core/scout-controller.js.map +1 -1
- package/dist/core/sdk.d.ts +6 -3
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +64 -27
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-analytics.d.ts.map +1 -1
- package/dist/core/session-analytics.js +28 -1
- package/dist/core/session-analytics.js.map +1 -1
- package/dist/core/session-image-store.d.ts +33 -0
- package/dist/core/session-image-store.d.ts.map +1 -0
- package/dist/core/session-image-store.js +283 -0
- package/dist/core/session-image-store.js.map +1 -0
- package/dist/core/session-role.d.ts +33 -0
- package/dist/core/session-role.d.ts.map +1 -0
- package/dist/core/session-role.js +58 -0
- package/dist/core/session-role.js.map +1 -0
- package/dist/core/session-snapshot.d.ts +34 -0
- package/dist/core/session-snapshot.d.ts.map +1 -0
- package/dist/core/session-snapshot.js +68 -0
- package/dist/core/session-snapshot.js.map +1 -0
- package/dist/core/session-supervision-runtime.d.ts +31 -0
- package/dist/core/session-supervision-runtime.d.ts.map +1 -0
- package/dist/core/session-supervision-runtime.js +97 -0
- package/dist/core/session-supervision-runtime.js.map +1 -0
- package/dist/core/settings-manager.d.ts +51 -7
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +168 -82
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt-builder.d.ts +12 -0
- package/dist/core/system-prompt-builder.d.ts.map +1 -1
- package/dist/core/system-prompt-builder.js +6 -0
- package/dist/core/system-prompt-builder.js.map +1 -1
- package/dist/core/system-prompt.d.ts +14 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +20 -3
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tasks/session-task-state.d.ts +13 -6
- package/dist/core/tasks/session-task-state.d.ts.map +1 -1
- package/dist/core/tasks/session-task-state.js +20 -22
- package/dist/core/tasks/session-task-state.js.map +1 -1
- package/dist/core/tasks/task-contract-monitor.d.ts +45 -0
- package/dist/core/tasks/task-contract-monitor.d.ts.map +1 -0
- package/dist/core/tasks/task-contract-monitor.js +56 -0
- package/dist/core/tasks/task-contract-monitor.js.map +1 -0
- package/dist/core/tasks/task-projection.d.ts +20 -0
- package/dist/core/tasks/task-projection.d.ts.map +1 -0
- package/dist/core/tasks/task-projection.js +47 -0
- package/dist/core/tasks/task-projection.js.map +1 -0
- package/dist/core/tasks/task-state.d.ts +12 -0
- package/dist/core/tasks/task-state.d.ts.map +1 -1
- package/dist/core/tasks/task-state.js +56 -12
- package/dist/core/tasks/task-state.js.map +1 -1
- package/dist/core/tool-capability-policy.d.ts +13 -0
- package/dist/core/tool-capability-policy.d.ts.map +1 -0
- package/dist/core/tool-capability-policy.js +85 -0
- package/dist/core/tool-capability-policy.js.map +1 -0
- package/dist/core/tool-gate-controller.d.ts.map +1 -1
- package/dist/core/tool-gate-controller.js +5 -0
- package/dist/core/tool-gate-controller.js.map +1 -1
- package/dist/core/tool-protocol-controller.d.ts +91 -0
- package/dist/core/tool-protocol-controller.d.ts.map +1 -0
- package/dist/core/tool-protocol-controller.js +512 -0
- package/dist/core/tool-protocol-controller.js.map +1 -0
- package/dist/core/tool-recovery-log-records.d.ts +6 -0
- package/dist/core/tool-recovery-log-records.d.ts.map +1 -1
- package/dist/core/tool-recovery-log-records.js +13 -31
- package/dist/core/tool-recovery-log-records.js.map +1 -1
- package/dist/core/tool-selection/promotion.d.ts +54 -0
- package/dist/core/tool-selection/promotion.d.ts.map +1 -0
- package/dist/core/tool-selection/promotion.js +81 -0
- package/dist/core/tool-selection/promotion.js.map +1 -0
- package/dist/core/tool-selection/tool-performance-store.d.ts +39 -6
- package/dist/core/tool-selection/tool-performance-store.d.ts.map +1 -1
- package/dist/core/tool-selection/tool-performance-store.js +153 -110
- package/dist/core/tool-selection/tool-performance-store.js.map +1 -1
- package/dist/core/tool-selection/tool-selection-controller.d.ts +45 -0
- package/dist/core/tool-selection/tool-selection-controller.d.ts.map +1 -1
- package/dist/core/tool-selection/tool-selection-controller.js +96 -0
- package/dist/core/tool-selection/tool-selection-controller.js.map +1 -1
- package/dist/core/tools/ask-question.d.ts +124 -0
- package/dist/core/tools/ask-question.d.ts.map +1 -0
- package/dist/core/tools/ask-question.js +693 -0
- package/dist/core/tools/ask-question.js.map +1 -0
- package/dist/core/tools/bash.d.ts +18 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +95 -16
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/delegate-status.d.ts +44 -2
- package/dist/core/tools/delegate-status.d.ts.map +1 -1
- package/dist/core/tools/delegate-status.js +214 -19
- package/dist/core/tools/delegate-status.js.map +1 -1
- package/dist/core/tools/delegate.d.ts +15 -13
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +124 -22
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/core/tools/extensionify.d.ts.map +1 -1
- package/dist/core/tools/extensionify.js +1 -0
- package/dist/core/tools/extensionify.js.map +1 -1
- package/dist/core/tools/goal.d.ts +94 -3
- package/dist/core/tools/goal.d.ts.map +1 -1
- package/dist/core/tools/goal.js +165 -15
- package/dist/core/tools/goal.js.map +1 -1
- package/dist/core/tools/model-fitness.d.ts +7 -0
- package/dist/core/tools/model-fitness.d.ts.map +1 -1
- package/dist/core/tools/model-fitness.js +4 -4
- package/dist/core/tools/model-fitness.js.map +1 -1
- package/dist/core/tools/orchestration-panel.d.ts +43 -0
- package/dist/core/tools/orchestration-panel.d.ts.map +1 -0
- package/dist/core/tools/orchestration-panel.js +168 -0
- package/dist/core/tools/orchestration-panel.js.map +1 -0
- package/dist/core/tools/run-process.d.ts +26 -0
- package/dist/core/tools/run-process.d.ts.map +1 -0
- package/dist/core/tools/run-process.js +138 -0
- package/dist/core/tools/run-process.js.map +1 -0
- package/dist/core/tools/shell-contract-router.d.ts +6 -1
- package/dist/core/tools/shell-contract-router.d.ts.map +1 -1
- package/dist/core/tools/shell-contract-router.js +23 -2
- package/dist/core/tools/shell-contract-router.js.map +1 -1
- package/dist/core/tools/shell-session.d.ts +89 -0
- package/dist/core/tools/shell-session.d.ts.map +1 -0
- package/dist/core/tools/shell-session.js +432 -0
- package/dist/core/tools/shell-session.js.map +1 -0
- package/dist/core/tools/task-steps.d.ts +6 -0
- package/dist/core/tools/task-steps.d.ts.map +1 -1
- package/dist/core/tools/task-steps.js +165 -8
- package/dist/core/tools/task-steps.js.map +1 -1
- package/dist/core/tools/tmux-dispatch.d.ts +84 -0
- package/dist/core/tools/tmux-dispatch.d.ts.map +1 -0
- package/dist/core/tools/tmux-dispatch.js +91 -0
- package/dist/core/tools/tmux-dispatch.js.map +1 -0
- package/dist/core/tools/windows-shell-engine.d.ts +42 -0
- package/dist/core/tools/windows-shell-engine.d.ts.map +1 -0
- package/dist/core/tools/windows-shell-engine.js +151 -0
- package/dist/core/tools/windows-shell-engine.js.map +1 -0
- package/dist/core/tools/windows-shell-state.d.ts +40 -0
- package/dist/core/tools/windows-shell-state.d.ts.map +1 -0
- package/dist/core/tools/windows-shell-state.js +59 -0
- package/dist/core/tools/windows-shell-state.js.map +1 -0
- package/dist/core/tools/worktree-sync.d.ts +24 -0
- package/dist/core/tools/worktree-sync.d.ts.map +1 -0
- package/dist/core/tools/worktree-sync.js +338 -0
- package/dist/core/tools/worktree-sync.js.map +1 -0
- package/dist/core/trust-manager.d.ts +4 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +20 -2
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/core/util/atomic-file.d.ts +55 -0
- package/dist/core/util/atomic-file.d.ts.map +1 -0
- package/dist/core/util/atomic-file.js +255 -0
- package/dist/core/util/atomic-file.js.map +1 -0
- package/dist/core/util/bounded-jsonl.d.ts +10 -0
- package/dist/core/util/bounded-jsonl.d.ts.map +1 -0
- package/dist/core/util/bounded-jsonl.js +75 -0
- package/dist/core/util/bounded-jsonl.js.map +1 -0
- package/dist/core/util/minimatch-cache.d.ts +33 -0
- package/dist/core/util/minimatch-cache.d.ts.map +1 -0
- package/dist/core/util/minimatch-cache.js +0 -0
- package/dist/core/util/minimatch-cache.js.map +1 -0
- package/dist/core/util/value-guards.d.ts +5 -0
- package/dist/core/util/value-guards.d.ts.map +1 -0
- package/dist/core/util/value-guards.js +12 -0
- package/dist/core/util/value-guards.js.map +1 -0
- package/dist/core/worktree-sync/codes.d.ts +227 -0
- package/dist/core/worktree-sync/codes.d.ts.map +1 -0
- package/dist/core/worktree-sync/codes.js +14 -0
- package/dist/core/worktree-sync/codes.js.map +1 -0
- package/dist/core/worktree-sync/git-engine.d.ts +156 -0
- package/dist/core/worktree-sync/git-engine.d.ts.map +1 -0
- package/dist/core/worktree-sync/git-engine.js +1191 -0
- package/dist/core/worktree-sync/git-engine.js.map +1 -0
- package/dist/core/worktree-sync/lane-gate.d.ts +75 -0
- package/dist/core/worktree-sync/lane-gate.d.ts.map +1 -0
- package/dist/core/worktree-sync/lane-gate.js +360 -0
- package/dist/core/worktree-sync/lane-gate.js.map +1 -0
- package/dist/core/worktree-sync/runtime.d.ts +47 -0
- package/dist/core/worktree-sync/runtime.d.ts.map +1 -0
- package/dist/core/worktree-sync/runtime.js +96 -0
- package/dist/core/worktree-sync/runtime.js.map +1 -0
- package/dist/core/worktree-sync/store.d.ts +69 -0
- package/dist/core/worktree-sync/store.d.ts.map +1 -0
- package/dist/core/worktree-sync/store.js +252 -0
- package/dist/core/worktree-sync/store.js.map +1 -0
- package/dist/core/worktree-sync/watcher.d.ts +29 -0
- package/dist/core/worktree-sync/watcher.d.ts.map +1 -0
- package/dist/core/worktree-sync/watcher.js +93 -0
- package/dist/core/worktree-sync/watcher.js.map +1 -0
- package/dist/index.d.ts +13 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +179 -1
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts +7 -0
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +197 -1
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/auto-learn-controller.d.ts +16 -1
- package/dist/modes/interactive/auto-learn-controller.d.ts.map +1 -1
- package/dist/modes/interactive/auto-learn-controller.js +50 -8
- package/dist/modes/interactive/auto-learn-controller.js.map +1 -1
- package/dist/modes/interactive/clipboard-input.d.ts +7 -1
- package/dist/modes/interactive/clipboard-input.d.ts.map +1 -1
- package/dist/modes/interactive/clipboard-input.js +44 -6
- package/dist/modes/interactive/clipboard-input.js.map +1 -1
- package/dist/modes/interactive/components/profile-resource-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/profile-resource-editor.js +23 -1
- package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +2 -2
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/config-backup.d.ts.map +1 -1
- package/dist/modes/interactive/config-backup.js +5 -4
- package/dist/modes/interactive/config-backup.js.map +1 -1
- package/dist/modes/interactive/extension-ui-host.d.ts +14 -0
- package/dist/modes/interactive/extension-ui-host.d.ts.map +1 -1
- package/dist/modes/interactive/extension-ui-host.js +76 -15
- package/dist/modes/interactive/extension-ui-host.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +67 -1
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/key-handlers.d.ts.map +1 -1
- package/dist/modes/interactive/key-handlers.js +1 -1
- package/dist/modes/interactive/key-handlers.js.map +1 -1
- package/dist/modes/interactive/local-model-commands.d.ts +43 -0
- package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
- package/dist/modes/interactive/local-model-commands.js +290 -3
- package/dist/modes/interactive/local-model-commands.js.map +1 -1
- package/dist/modes/interactive/profile-menu-controller.d.ts.map +1 -1
- package/dist/modes/interactive/profile-menu-controller.js +7 -7
- package/dist/modes/interactive/profile-menu-controller.js.map +1 -1
- package/dist/modes/interactive/report-commands.d.ts.map +1 -1
- package/dist/modes/interactive/report-commands.js +21 -0
- package/dist/modes/interactive/report-commands.js.map +1 -1
- package/dist/modes/interactive/session-flow-commands.d.ts +17 -2
- package/dist/modes/interactive/session-flow-commands.d.ts.map +1 -1
- package/dist/modes/interactive/session-flow-commands.js +74 -21
- package/dist/modes/interactive/session-flow-commands.js.map +1 -1
- package/dist/modes/interactive/session-io-commands.d.ts.map +1 -1
- package/dist/modes/interactive/session-io-commands.js +16 -6
- package/dist/modes/interactive/session-io-commands.js.map +1 -1
- package/dist/modes/interactive/session-replacement-errors.d.ts +7 -0
- package/dist/modes/interactive/session-replacement-errors.d.ts.map +1 -0
- package/dist/modes/interactive/session-replacement-errors.js +15 -0
- package/dist/modes/interactive/session-replacement-errors.js.map +1 -0
- package/dist/modes/interactive/startup-checks.d.ts.map +1 -1
- package/dist/modes/interactive/startup-checks.js +1 -1
- package/dist/modes/interactive/startup-checks.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +82 -23
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +15 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js +138 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +2 -13
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/clipboard-native.d.ts +1 -0
- package/dist/utils/clipboard-native.d.ts.map +1 -1
- package/dist/utils/clipboard-native.js.map +1 -1
- package/dist/utils/clipboard.d.ts +1 -0
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +52 -1
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/fs-watch.d.ts +11 -0
- package/dist/utils/fs-watch.d.ts.map +1 -1
- package/dist/utils/fs-watch.js +20 -2
- package/dist/utils/fs-watch.js.map +1 -1
- package/dist/utils/highlight-js-languages.d.ts +4 -0
- package/dist/utils/highlight-js-languages.d.ts.map +1 -0
- package/dist/utils/highlight-js-languages.js +573 -0
- package/dist/utils/highlight-js-languages.js.map +1 -0
- package/dist/utils/platform.d.ts +3 -0
- package/dist/utils/platform.d.ts.map +1 -0
- package/dist/utils/platform.js +8 -0
- package/dist/utils/platform.js.map +1 -0
- package/dist/utils/shell.d.ts +7 -1
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +21 -6
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +53 -5
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +112 -1
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/work-directory.d.ts +11 -0
- package/dist/utils/work-directory.d.ts.map +1 -1
- package/dist/utils/work-directory.js +30 -9
- package/dist/utils/work-directory.js.map +1 -1
- package/docs/UI_POLISH_ORCHESTRATION.md +44 -0
- package/docs/UI_QUALITY_ORCHESTRATION.md +30 -0
- package/docs/docs.json +4 -0
- package/docs/extensions.md +62 -2
- package/docs/harness-architecture.md +156 -0
- package/docs/keybindings.md +11 -1
- package/docs/process-matrix.md +153 -0
- package/docs/rpc.md +45 -2
- package/docs/sessions.md +15 -0
- package/docs/settings.md +128 -11
- package/docs/task-steps.md +8 -0
- package/docs/telemetry.md +19 -0
- package/docs/tmux-agent-manager.md +92 -2
- package/docs/ui-reference/README.md +9 -0
- package/docs/ui-reference/orchestration-after.txt +7 -0
- package/docs/ui-reference/orchestration-before.txt +7 -0
- package/docs/ui-reference/orchestration-reference.txt +6 -0
- package/docs/usage.md +14 -2
- package/docs/windows.md +52 -3
- package/docs/work-directory.md +42 -2
- package/docs/worktree-sync.md +250 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/question.ts +34 -245
- package/examples/extensions/questionnaire.ts +41 -407
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +10 -4
- package/dist/core/delegation/session-worker-result.d.ts +0 -16
- package/dist/core/delegation/session-worker-result.d.ts.map +0 -1
- package/dist/core/delegation/session-worker-result.js +0 -53
- package/dist/core/delegation/session-worker-result.js.map +0 -1
- package/dist/core/delegation/worker-result.d.ts +0 -15
- package/dist/core/delegation/worker-result.d.ts.map +0 -1
- package/dist/core/delegation/worker-result.js +0 -161
- package/dist/core/delegation/worker-result.js.map +0 -1
- package/dist/core/telemetry.d.ts.map +0 -1
- package/dist/core/telemetry.js.map +0 -1
- package/docs/integration-sweep-builder-blueprint-2026-07-09.md +0 -365
- package/docs/integration-sweep-resume-2026-07-09.md +0 -407
|
@@ -1,40 +1,45 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { cleanupSessionResources,
|
|
6
|
-
import { Type } from "typebox";
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { basename, dirname } from "node:path";
|
|
4
|
+
import { compactToolResultDetailsForRetention, createCustomMessage, DEFAULT_STREAM_IDLE, withStreamIdleWatchdog, } from "@caupulican/pi-agent-core";
|
|
5
|
+
import { cleanupSessionResources, getSupportedThinkingLevels, modelsAreEqual, streamSimple } from "@caupulican/pi-ai";
|
|
7
6
|
import { getAgentDir } from "../config.js";
|
|
8
7
|
import { stripFrontmatter } from "../utils/frontmatter.js";
|
|
9
|
-
import {
|
|
8
|
+
import { resourceDir, stateFile } from "./agent-paths.js";
|
|
10
9
|
import { formatNoApiKeyFoundMessage, formatNoModelSelectedMessage } from "./auth-guidance.js";
|
|
11
10
|
import { buildForegroundEnvelope, formatForegroundEnvelopeObservation } from "./autonomy/foreground-envelope.js";
|
|
12
11
|
import { evaluateToolGate } from "./autonomy/gates.js";
|
|
13
12
|
import { AutonomyTelemetry } from "./autonomy-telemetry.js";
|
|
14
13
|
import { BackgroundLaneController } from "./background-lane-controller.js";
|
|
15
14
|
import { BashExecutionController } from "./bash-execution-controller.js";
|
|
16
|
-
import {
|
|
15
|
+
import { CompactionController } from "./compaction-controller.js";
|
|
17
16
|
import { CompactionSupport } from "./compaction-support.js";
|
|
18
17
|
import { buildContextCompositionReport, formatContextCompositionDashboard, } from "./context/context-composition.js";
|
|
19
18
|
import { ContextPipeline } from "./context-pipeline.js";
|
|
20
19
|
import { downgradeReasoning, estimateTurnCostUsd, evaluateCostGuard } from "./cost-guard.js";
|
|
21
|
-
import {
|
|
20
|
+
import { appendWorkerClaimSnapshot, getWorkerClaimSnapshots } from "./delegation/session-worker-claim.js";
|
|
22
21
|
import { FailureCorpusRecorder } from "./failure-corpus.js";
|
|
22
|
+
import { ForegroundRecoveryController } from "./foreground-recovery-controller.js";
|
|
23
23
|
import { GatewayRegistry } from "./gateways/channel-provider.js";
|
|
24
24
|
import { GoalLoopController } from "./goal-loop-controller.js";
|
|
25
25
|
import { buildGoalRuntimeSnapshot, } from "./goals/goal-runtime-snapshot.js";
|
|
26
|
+
import { applyGoalEvent } from "./goals/goal-state.js";
|
|
26
27
|
import { appendGoalStateSnapshot, getLatestGoalStateSnapshot } from "./goals/session-goal-state.js";
|
|
27
28
|
import { constrainStreamIdleToHttpTimeout } from "./http-dispatcher.js";
|
|
29
|
+
import { beginHumanInputRequest, createHumanInputRequest, formatHumanInputAnswerText, getLatestHumanInputSnapshots, getResumableHumanInputSnapshot, getWorkerHumanInputsRequiringDelivery, HUMAN_INPUT_WORKER_RESPONSE_CUSTOM_TYPE, resolveHumanInput, } from "./human-input.js";
|
|
28
30
|
import { appendLearningDecisionSnapshot, getLearningDecisionSnapshots } from "./learning/session-learning-decision.js";
|
|
29
31
|
import { isPromotedFrontmatter, SkillCurator } from "./learning/skill-curator.js";
|
|
30
32
|
import { LocalRuntimeController } from "./local-runtime-controller.js";
|
|
31
33
|
import { MemoryController } from "./memory-controller.js";
|
|
32
|
-
import { deriveModelCapabilityProfile, filterToolNamesForCapability, } from "./model-capability.js";
|
|
34
|
+
import { deriveModelCapabilityProfile, evaluateLaneWorkerRefusal, filterToolNamesForCapability, } from "./model-capability.js";
|
|
35
|
+
import { isLocalOrManagedRouterModel } from "./model-router/tool-escalation.js";
|
|
33
36
|
import { formatModelRouterModel, ModelRouterController } from "./model-router-controller.js";
|
|
34
37
|
import { ModelSelectionController } from "./model-selection-controller.js";
|
|
35
38
|
import { ModelAdaptationStore } from "./models/adaptation-store.js";
|
|
36
39
|
import { HF_TRANSFORMERS_PROVIDER, OLLAMA_PROVIDER } from "./models/local-registration.js";
|
|
37
40
|
import { DEFAULT_ADAPTIVE_STREAM_IDLE_CEILING_MS, estimateContextPromptTokens, resolveAdaptiveStreamIdleOptions, withModelPerfProfile, } from "./models/perf-profile.js";
|
|
41
|
+
import { resolveConfiguredOrchestrationModel } from "./orchestration/model-binding.js";
|
|
42
|
+
import { validateOrchestrationProfile } from "./orchestration/profile-registry.js";
|
|
38
43
|
import { ProfileFilterController } from "./profile-filter-controller.js";
|
|
39
44
|
import { expandPromptTemplate } from "./prompt-templates.js";
|
|
40
45
|
import { ReflectionController } from "./reflection-controller.js";
|
|
@@ -43,17 +48,19 @@ import { collectWorkspaceSources } from "./research/workspace-collector.js";
|
|
|
43
48
|
import { stripResourceProfileBlocks } from "./resource-profile-blocks.js";
|
|
44
49
|
import { RuntimeBuilder } from "./runtime-builder.js";
|
|
45
50
|
import { SessionAnalytics } from "./session-analytics.js";
|
|
51
|
+
import { SessionImageStore } from "./session-image-store.js";
|
|
52
|
+
import { getActiveSessionBranchEntries } from "./session-snapshot.js";
|
|
46
53
|
import { SessionTreeNavigator } from "./session-tree-navigator.js";
|
|
47
54
|
import { SystemPromptBuilder } from "./system-prompt-builder.js";
|
|
48
55
|
import { appendTaskStepsStateSnapshot, getLatestTaskStepsStateSnapshot } from "./tasks/session-task-state.js";
|
|
49
56
|
import { formatTaskStepsContext } from "./tasks/task-state.js";
|
|
50
57
|
import { ToolGateController } from "./tool-gate-controller.js";
|
|
58
|
+
import { ToolProtocolController } from "./tool-protocol-controller.js";
|
|
51
59
|
import { TOOL_RECOVERY_EVENT_LOG_FILE } from "./tool-recovery-log-records.js";
|
|
52
60
|
import { ToolRecoveryLogger } from "./tool-recovery-logger.js";
|
|
53
|
-
import { formatToolRepairHealthReport } from "./tool-repair-health.js";
|
|
54
|
-
import { resolveCurrentToolRepairSettings } from "./tool-repair-settings.js";
|
|
55
61
|
import { ToolPerformanceStore } from "./tool-selection/tool-performance-store.js";
|
|
56
|
-
import { ToolSelectionController } from "./tool-selection/tool-selection-controller.js";
|
|
62
|
+
import { formatToolSelectionReport, ToolSelectionController } from "./tool-selection/tool-selection-controller.js";
|
|
63
|
+
import { disposePersistentShellSession } from "./tools/shell-session.js";
|
|
57
64
|
// ============================================================================
|
|
58
65
|
// Stream-idle watchdog wiring
|
|
59
66
|
// ============================================================================
|
|
@@ -67,26 +74,6 @@ import { ToolSelectionController } from "./tool-selection/tool-selection-control
|
|
|
67
74
|
* key stable regardless of how many times this module is evaluated.
|
|
68
75
|
*/
|
|
69
76
|
const RAW_STREAM_MARKER = Symbol.for("pi.rawStreamSimple");
|
|
70
|
-
const MODEL_ADAPTATION_REPAIR_THRESHOLD = 3;
|
|
71
|
-
const TEXT_TOOL_PROTOCOL_VERSION = 1;
|
|
72
|
-
const TEXT_TOOL_PROTOCOL_TRIALS_PER_VARIANT = 2;
|
|
73
|
-
const TEXT_TOOL_PROTOCOL_PARSE_FAILURE_THRESHOLD = 3;
|
|
74
|
-
const TEXT_TOOL_PROTOCOL_VARIANTS = [
|
|
75
|
-
"tool-tag",
|
|
76
|
-
"tool-call",
|
|
77
|
-
"fenced-json",
|
|
78
|
-
"function-xml",
|
|
79
|
-
];
|
|
80
|
-
const TEXT_TOOL_PROTOCOL_ECHO_TOOL = {
|
|
81
|
-
name: "echo",
|
|
82
|
-
description: "Echo calibration data",
|
|
83
|
-
parameters: Type.Object({ data: Type.String() }),
|
|
84
|
-
};
|
|
85
|
-
const NATIVE_TOOL_PROBE_READ_TOOL = {
|
|
86
|
-
name: "read",
|
|
87
|
-
description: "Read file contents",
|
|
88
|
-
parameters: Type.Object({ path: Type.String() }),
|
|
89
|
-
};
|
|
90
77
|
/** Test-only override of the stream-idle bounds. Read per-request by the wiring's resolver. */
|
|
91
78
|
let streamIdleOptionsOverride;
|
|
92
79
|
/**
|
|
@@ -123,6 +110,43 @@ export function parseSkillBlock(text) {
|
|
|
123
110
|
}
|
|
124
111
|
/** customType for spawned-usage roll-up entries (Cost Aggregation, Model A). */
|
|
125
112
|
export const SPAWNED_USAGE_CUSTOM_TYPE = "spawned_usage";
|
|
113
|
+
/**
|
|
114
|
+
* customType for a persisted runaway-loop-backstop entry: the agent loop stopped a turn stuck
|
|
115
|
+
* repeating one identical tool-call signature. This is the session-log/telemetry sink for
|
|
116
|
+
* {@link Agent.onRunawayStop} — see `_installAgentToolHooks`.
|
|
117
|
+
*/
|
|
118
|
+
export const RUNAWAY_STOP_CUSTOM_TYPE = "runaway_stop";
|
|
119
|
+
/**
|
|
120
|
+
* customType for a persisted tool-validation-escalation entry: the agent loop bounced the same
|
|
121
|
+
* tool-call validation failure enough times to escalate. This is the session-log/telemetry sink for
|
|
122
|
+
* {@link Agent.onToolValidationEscalation} — see `_installAgentToolHooks`.
|
|
123
|
+
*/
|
|
124
|
+
export const TOOL_VALIDATION_ESCALATION_CUSTOM_TYPE = "tool_validation_escalation";
|
|
125
|
+
/** Fallback {@link IsolatedCompletionOptions.laneKind} for callers that do not tag their lane. */
|
|
126
|
+
export const DEFAULT_ISOLATED_LANE_KIND = "isolated";
|
|
127
|
+
/**
|
|
128
|
+
* Derive a STABLE synthetic cache-affinity key for an isolated completion lane. Isolated calls
|
|
129
|
+
* deliberately never carry the real session id (see reflection-controller.ts's isolation invariants —
|
|
130
|
+
* an isolated call must not entangle with the main session), which today also means every isolated
|
|
131
|
+
* call looks like a brand-new, uncorrelated session to providers with session-affinity headers /
|
|
132
|
+
* `prompt_cache_key` (anthropic.ts's `x-session-affinity`, openai-responses.ts /
|
|
133
|
+
* openai-completions.ts's `prompt_cache_key`), defeating their cache routing.
|
|
134
|
+
*
|
|
135
|
+
* This key is deterministic per `(laneKind, model, systemPrompt)` — the SAME lane calling the SAME
|
|
136
|
+
* model with the SAME (static) system prompt always gets the SAME key, so repeat calls route to the
|
|
137
|
+
* same cache-warm backend — while remaining fully synthetic: it is a salted hash, namespaced with a
|
|
138
|
+
* `lane:` prefix, and never derived from or equal to the real session id.
|
|
139
|
+
*/
|
|
140
|
+
export function computeLaneAffinityKey(laneKind, model, systemPrompt) {
|
|
141
|
+
const modelKey = model ? `${model.provider}/${model.id}` : "unknown-model";
|
|
142
|
+
// NUL-separated fields: laneKind/modelKey are drawn from small caller-controlled vocabularies
|
|
143
|
+
// that never contain a raw NUL, so this cannot field-collide the way a plain colon/space join could.
|
|
144
|
+
const digest = createHash("sha256")
|
|
145
|
+
.update(["pi-lane-affinity-v1", laneKind, modelKey, systemPrompt].join("\u0000"))
|
|
146
|
+
.digest("hex")
|
|
147
|
+
.slice(0, 32);
|
|
148
|
+
return `lane:${laneKind}:${digest}`;
|
|
149
|
+
}
|
|
126
150
|
// ============================================================================
|
|
127
151
|
// Constants
|
|
128
152
|
// ============================================================================
|
|
@@ -144,17 +168,16 @@ export class AgentSession {
|
|
|
144
168
|
_queuedExtensionCommands = [];
|
|
145
169
|
_pendingNextTurnMessages = [];
|
|
146
170
|
_streamingPromptSubmissionTail = Promise.resolve();
|
|
171
|
+
/** Serializes owner-input overlays so concurrent worker terminals cannot replace each other. */
|
|
172
|
+
_humanInputTail = Promise.resolve();
|
|
173
|
+
_activeHumanInputRequestIds = new Set();
|
|
147
174
|
/**
|
|
148
175
|
* The last tool set requested via setActiveToolsByName BEFORE model-capability filtering, so
|
|
149
176
|
* switching from a small-window model back to a large one restores the full requested set.
|
|
150
177
|
*/
|
|
151
178
|
_requestedActiveToolNames;
|
|
152
|
-
_compactionAbortController = undefined;
|
|
153
|
-
_autoCompactionAbortController = undefined;
|
|
154
|
-
_overflowRecoveryAttempted = false;
|
|
155
179
|
_unboundToolGrantWarnings = [];
|
|
156
180
|
_branchSummaryAbortController = undefined;
|
|
157
|
-
_retryController;
|
|
158
181
|
_modelSelection;
|
|
159
182
|
_bash;
|
|
160
183
|
_profileFilter;
|
|
@@ -166,23 +189,21 @@ export class AgentSession {
|
|
|
166
189
|
_resourceLoader;
|
|
167
190
|
_customTools;
|
|
168
191
|
_cwd;
|
|
192
|
+
/** Per-agent persistent shell session identity: stable across runtime reloads, disposed with the session. */
|
|
193
|
+
_shellSessionKey = `agent:${randomUUID()}`;
|
|
169
194
|
_agentDir;
|
|
170
195
|
_collectWorkspaceSources;
|
|
171
196
|
_localRuntimeController;
|
|
172
|
-
|
|
197
|
+
_toolProtocol;
|
|
173
198
|
_prefixWarmer;
|
|
174
199
|
_completedPrefixWarms = new Set();
|
|
175
|
-
_repairModeSessionCounts = new Map();
|
|
176
|
-
_textProtocolParseFailures = new Map();
|
|
177
|
-
_textProtocolParseObservedThisTurn = false;
|
|
178
|
-
_textProtocolValidationOutcomeThisTurn;
|
|
179
200
|
/** Assembles the session's base system prompt from live session state (see
|
|
180
201
|
* system-prompt-builder.ts); owns the paired _baseSystemPromptOptions. */
|
|
181
202
|
_systemPromptBuilder;
|
|
182
|
-
/**
|
|
203
|
+
/** Autonomy telemetry sink + status/diagnostic snapshots (see autonomy-telemetry.ts); owns
|
|
183
204
|
* the latest gate outcome and the bounded gate-outcome history. */
|
|
184
205
|
_autonomyTelemetry;
|
|
185
|
-
/** Goal auto-continue + research lane +
|
|
206
|
+
/** Goal auto-continue + research lane + profile-bound worker delegation + model-fitness probe (see
|
|
186
207
|
* background-lane-controller.ts); owns the lane timers/guards, the last research-lane skip
|
|
187
208
|
* reason, the live LaneTracker, and the in-flight research/worker abort controllers. */
|
|
188
209
|
_backgroundLanes;
|
|
@@ -191,6 +212,7 @@ export class AgentSession {
|
|
|
191
212
|
* effectiveness tracker, and the extension-contributed pending providers. */
|
|
192
213
|
_memory;
|
|
193
214
|
_compactionSupport;
|
|
215
|
+
_compaction;
|
|
194
216
|
/** Per-turn context-shaping subsystem (see context-pipeline.ts); owns the latest
|
|
195
217
|
* audit/policy/correlation/enforcement/gc reports, the brain-curation sidecar + its skip reasons,
|
|
196
218
|
* and the tool-output artifact store. Invoked stage-by-stage from the context transform. */
|
|
@@ -208,17 +230,25 @@ export class AgentSession {
|
|
|
208
230
|
_analytics;
|
|
209
231
|
_treeNavigator;
|
|
210
232
|
_lastCostGuardDecision;
|
|
233
|
+
/**
|
|
234
|
+
* `getSpawnedUsage().cost` snapshotted at the start of the CURRENT foreground prompt cycle (see
|
|
235
|
+
* `_promptUnserialized`), so the cost guard can attribute only background/spawned spend since THIS
|
|
236
|
+
* turn began, not the session's entire lifetime spend. Reset on every new user prompt; every
|
|
237
|
+
* round-trip within the same turn (tool-call iterations) shares this one baseline.
|
|
238
|
+
*/
|
|
239
|
+
_costGuardTurnBaselineUsd = 0;
|
|
211
240
|
/** Per-turn model-router subsystem (see model-router-controller.ts); owns the transient route/intent,
|
|
212
241
|
* the cheap-turn session buffer, the escalation/retry flags, and the sticky last-decision/skip-reason
|
|
213
242
|
* used by the status report. Its parallel routed drive path delegates every turn back to
|
|
214
243
|
* {@link _runAgentPrompt} so the drive loop stays host-side. */
|
|
215
244
|
_modelRouter;
|
|
216
|
-
|
|
245
|
+
_foregroundRecovery;
|
|
217
246
|
_failureCorpus;
|
|
218
247
|
_toolRecoveryLogger;
|
|
219
248
|
_toolRecoveryEventLogPath;
|
|
220
249
|
_skillCuratorInstance;
|
|
221
250
|
_disposed = false;
|
|
251
|
+
_disposeCompletion;
|
|
222
252
|
_reflectionAbort = new AbortController();
|
|
223
253
|
/** Native reflection engine + learning-apply/rollback path (see reflection-controller.ts); owns no
|
|
224
254
|
* session state, applies durable writes through the bundled memory tool and the session log. */
|
|
@@ -248,7 +278,7 @@ export class AgentSession {
|
|
|
248
278
|
this.agent = config.agent;
|
|
249
279
|
// Bound every provider stream this session starts against a silently dead connection: a
|
|
250
280
|
// stall aborts the inner request and surfaces as a retryable "stream stalled" error, which
|
|
251
|
-
//
|
|
281
|
+
// ForegroundRecoveryController routes into the existing auto-retry path. Wrapped exactly once, here.
|
|
252
282
|
// The wrapper reports the stall immediately and aborts the inner request; releasing the
|
|
253
283
|
// inner pump relies on the provider ending its stream after abort (real providers do — see
|
|
254
284
|
// withStreamIdleWatchdog's contract), so no extra drain is added at this wiring site.
|
|
@@ -292,31 +322,46 @@ export class AgentSession {
|
|
|
292
322
|
}), baseStreamFn === streamSimple);
|
|
293
323
|
this.sessionManager = config.sessionManager;
|
|
294
324
|
this.settingsManager = config.settingsManager;
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
325
|
+
if (config.orchestrationProfile) {
|
|
326
|
+
validateOrchestrationProfile(config.orchestrationProfile);
|
|
327
|
+
const resolved = resolveConfiguredOrchestrationModel(config.orchestrationProfile, config.modelRegistry);
|
|
328
|
+
if (!resolved) {
|
|
329
|
+
throw new TypeError(`Orchestration profile '${config.orchestrationProfile.profileId}' has no configured, authenticated model that supports its exact thinking level.`);
|
|
330
|
+
}
|
|
331
|
+
if (!this.agent.state.model || !modelsAreEqual(this.agent.state.model, resolved.model)) {
|
|
332
|
+
this.sessionManager.appendModelChange(resolved.model.provider, resolved.model.id);
|
|
333
|
+
}
|
|
334
|
+
if (this.agent.state.thinkingLevel !== resolved.binding.thinkingLevel) {
|
|
335
|
+
this.sessionManager.appendThinkingLevelChange(resolved.binding.thinkingLevel);
|
|
336
|
+
}
|
|
337
|
+
this.agent.state.model = resolved.model;
|
|
338
|
+
this.agent.state.thinkingLevel = resolved.binding.thinkingLevel;
|
|
339
|
+
this.settingsManager.setRuntimeResourceProfiles([...config.orchestrationProfile.resourceProfileNames]);
|
|
340
|
+
}
|
|
341
|
+
this._scopedModels = config.orchestrationProfile
|
|
342
|
+
? [{ model: this.agent.state.model, thinkingLevel: this.agent.state.thinkingLevel }]
|
|
343
|
+
: (config.scopedModels ?? []);
|
|
313
344
|
this._resourceLoader = config.resourceLoader;
|
|
314
345
|
this._customTools = config.customTools ?? [];
|
|
315
346
|
this._cwd = config.cwd;
|
|
316
347
|
this._agentDir = agentDir;
|
|
317
|
-
this.
|
|
318
|
-
|
|
319
|
-
|
|
348
|
+
this._toolProtocol = new ToolProtocolController({
|
|
349
|
+
agent: this.agent,
|
|
350
|
+
agentDir: this._agentDir,
|
|
351
|
+
settingsManager: this.settingsManager,
|
|
352
|
+
getModelRegistry: () => this._modelRegistry,
|
|
353
|
+
adaptationStore: modelAdaptationStore,
|
|
354
|
+
isRawStreamSimple: (fn) => this._isRawStreamSimple(fn),
|
|
355
|
+
getRequiredRequestAuth: (model) => this._getRequiredRequestAuth(model),
|
|
356
|
+
addSpawnedUsage: (usage, opts) => this.addSpawnedUsage(usage, opts),
|
|
357
|
+
emitWarning: (message) => this._emit({ type: "warning", message }),
|
|
358
|
+
sendCorrectiveSteer: (message) => this.sendCustomMessage({ customType: "text-protocol-corrective-steer", content: message, display: false }, { deliverAs: "nextTurn" }),
|
|
359
|
+
findLastAssistantMessage: () => this._findLastAssistantMessage(),
|
|
360
|
+
isDisposed: () => this._disposed,
|
|
361
|
+
probeForAuto: (model) => this._probeToolCallingForModel(model),
|
|
362
|
+
});
|
|
363
|
+
this.agent.onTextToolProtocolParse = (event) => this._toolProtocol.handleTextProtocolParse(event);
|
|
364
|
+
this._toolProtocol.applyRepairLayerSettings();
|
|
320
365
|
this._collectWorkspaceSources = config.collectWorkspaceSources ?? collectWorkspaceSources;
|
|
321
366
|
this._localRuntimeController = new LocalRuntimeController({
|
|
322
367
|
agentDir: this._agentDir,
|
|
@@ -335,10 +380,13 @@ export class AgentSession {
|
|
|
335
380
|
hasTool: (name) => this._runtimeBuilder.hasTool(name),
|
|
336
381
|
getToolPromptSnippet: (name) => this._runtimeBuilder.getToolPromptSnippet(name),
|
|
337
382
|
getToolPromptGuidelines: (name) => this._runtimeBuilder.getToolPromptGuidelines(name),
|
|
338
|
-
getModelAdaptationRules: () => this.
|
|
383
|
+
getModelAdaptationRules: () => this._toolProtocol.getAdaptationRulesForPrompt(),
|
|
339
384
|
getActiveExtensions: () => this._extensionRunner.activeExtensions,
|
|
340
385
|
getContextWindow: () => this.model?.contextWindow,
|
|
341
386
|
getThinkingLevel: () => this.thinkingLevel,
|
|
387
|
+
// The evidence-gated tool-selection hint block — self-gated by kill switch/evidence
|
|
388
|
+
// thresholds inside getActiveHints() itself, so this is a plain always-on pass-through.
|
|
389
|
+
getToolSelectionHints: () => this._toolSelection.getActiveHints(),
|
|
342
390
|
});
|
|
343
391
|
this._autonomyTelemetry = new AutonomyTelemetry({
|
|
344
392
|
getSessionManager: () => this.sessionManager,
|
|
@@ -347,9 +395,9 @@ export class AgentSession {
|
|
|
347
395
|
getCostSummary: () => this.getCostSummary(),
|
|
348
396
|
getGoalStateSnapshot: () => this.getGoalStateSnapshot(),
|
|
349
397
|
getActiveLaneCount: () => this._backgroundLanes.getActiveLaneCount(),
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
398
|
+
getSessionEvidenceBundleHistory: () => getEvidenceBundleSnapshots(this.sessionManager.getEntries()),
|
|
399
|
+
getSessionWorkerClaimHistory: () => getWorkerClaimSnapshots(this.sessionManager.getEntries()),
|
|
400
|
+
getSessionLearningDecisionHistory: () => getLearningDecisionSnapshots(this.sessionManager.getEntries()),
|
|
353
401
|
getLearningAuditRecords: () => this.getLearningAuditRecords(),
|
|
354
402
|
});
|
|
355
403
|
this._modelRegistry = config.modelRegistry;
|
|
@@ -361,10 +409,12 @@ export class AgentSession {
|
|
|
361
409
|
getAgentDir: () => this._agentDir,
|
|
362
410
|
getSessionManager: () => this.sessionManager,
|
|
363
411
|
getSettingsManager: () => this.settingsManager,
|
|
412
|
+
getActiveOrchestrationProfile: () => config.orchestrationProfile,
|
|
364
413
|
getModelRegistry: () => this._modelRegistry,
|
|
365
|
-
isModelExhausted: (model) => this.
|
|
414
|
+
isModelExhausted: (model) => this._foregroundRecovery.isModelExhausted(`${model.provider}/${model.id}`),
|
|
366
415
|
getModel: () => this.model ?? undefined,
|
|
367
416
|
isDelegateToolActive: () => this.getActiveToolNames().includes("delegate"),
|
|
417
|
+
isGoalToolActive: () => this.getActiveToolNames().includes("goal"),
|
|
368
418
|
getCapabilityEnvelope: () => this.capabilityEnvelope,
|
|
369
419
|
getModelCapabilityProfile: () => this.getModelCapabilityProfile(),
|
|
370
420
|
emit: (event) => this._emit(event),
|
|
@@ -374,11 +424,15 @@ export class AgentSession {
|
|
|
374
424
|
getGoalRuntimeSnapshot: (settings) => this.getGoalRuntimeSnapshot(settings),
|
|
375
425
|
getEvidenceBundleSnapshot: () => this.getEvidenceBundleSnapshot(),
|
|
376
426
|
saveEvidenceBundleSnapshot: (bundle) => this.saveEvidenceBundleSnapshot(bundle),
|
|
377
|
-
|
|
427
|
+
saveWorkerClaimSnapshot: (claim, request) => this.saveWorkerClaimSnapshot(claim, request),
|
|
428
|
+
queueWorkerHumanInput: (request) => this._queueWorkerHumanInput(request),
|
|
378
429
|
readMemoryForLane: (query) => this._memory.readMemoryForLane(query),
|
|
379
430
|
addSpawnedUsage: (usage, opts) => this.addSpawnedUsage(usage, opts),
|
|
380
431
|
runIsolatedCompletion: (opts) => this.runIsolatedCompletion(opts),
|
|
381
|
-
continueGoalLoop
|
|
432
|
+
// RAW loop, deliberately bypassing the public `continueGoalLoop` — that method now delegates
|
|
433
|
+
// to this controller's own `continueGoalLoopExclusive` guard, so routing through it here would
|
|
434
|
+
// recurse into the guard from inside itself instead of driving the actual continuation pass.
|
|
435
|
+
continueGoalLoop: (options) => this._goalContinuation.continueGoalLoop(options),
|
|
382
436
|
collectWorkspaceSources: (args) => this._collectWorkspaceSources(args),
|
|
383
437
|
});
|
|
384
438
|
this._memory = new MemoryController({
|
|
@@ -398,12 +452,16 @@ export class AgentSession {
|
|
|
398
452
|
getModelRegistry: () => this._modelRegistry,
|
|
399
453
|
isRawStream: () => this._isRawStreamSimple(this.agent.streamFn),
|
|
400
454
|
getRequiredRequestAuth: (model) => this._getRequiredRequestAuth(model),
|
|
401
|
-
isModelExhausted: (ref) => this.
|
|
455
|
+
isModelExhausted: (ref) => this._foregroundRecovery.isModelExhausted(ref),
|
|
402
456
|
getStoredFitnessReport: (ref) => this.getStoredFitnessReports().find((entry) => entry.model === ref)?.report,
|
|
403
457
|
// Live context is an over-estimate of the span to summarize (includes the kept tail) —
|
|
404
458
|
// conservative in the safe direction for the summarizer capacity check.
|
|
405
459
|
estimateSummarizationInputTokens: () => this._pipeline.estimateCurrentContextTokens(this.agent.state.messages),
|
|
406
460
|
emitWarning: (message) => this._emit({ type: "warning", message }),
|
|
461
|
+
// Route a managed-local summarizer through the same readiness/residency gate every
|
|
462
|
+
// other isolated consumer uses, so compact() never calls a local model that was never
|
|
463
|
+
// confirmed up, installed, or resident (no-op for cloud models).
|
|
464
|
+
ensureModelReady: (model) => this._localRuntimeController.ensureIsolatedModelReady(model),
|
|
407
465
|
});
|
|
408
466
|
this._pipeline = new ContextPipeline({
|
|
409
467
|
getTurnIndex: () => this._turnIndex,
|
|
@@ -419,25 +477,53 @@ export class AgentSession {
|
|
|
419
477
|
addSpawnedUsage: (usage, opts) => this.addSpawnedUsage(usage, opts),
|
|
420
478
|
runIsolatedCompletion: (opts) => this.runIsolatedCompletion(opts),
|
|
421
479
|
});
|
|
422
|
-
const failureCorpusPath =
|
|
423
|
-
this._toolRecoveryEventLogPath =
|
|
424
|
-
const toolRepairSettings = this.
|
|
480
|
+
const failureCorpusPath = stateFile(this._agentDir, "failure-corpus.jsonl");
|
|
481
|
+
this._toolRecoveryEventLogPath = stateFile(this._agentDir, TOOL_RECOVERY_EVENT_LOG_FILE);
|
|
482
|
+
const toolRepairSettings = this._toolProtocol.getRepairSettings();
|
|
425
483
|
this._failureCorpus = new FailureCorpusRecorder({
|
|
426
484
|
filePath: failureCorpusPath,
|
|
427
485
|
});
|
|
486
|
+
this._compaction = new CompactionController({
|
|
487
|
+
agent: this.agent,
|
|
488
|
+
sessionManager: this.sessionManager,
|
|
489
|
+
settingsManager: this.settingsManager,
|
|
490
|
+
getModel: () => this.model,
|
|
491
|
+
getAdaptedSettings: () => this._getAdaptedCompactionSettings(),
|
|
492
|
+
getRequestAuth: (model) => this._getCompactionRequestAuth(model),
|
|
493
|
+
resolveModelAndAuth: (compactionModel, sessionModel) => this._resolveCompactionModelAndAuth(compactionModel, sessionModel),
|
|
494
|
+
resolveModel: (sessionModel) => this._resolveCompactionModel(sessionModel),
|
|
495
|
+
getSelectionReason: () => this._getLastCompactionSelectionReason(),
|
|
496
|
+
resolveThinkingLevel: (compactionModel, sessionModel) => this._resolveCompactionThinkingLevel(compactionModel, sessionModel),
|
|
497
|
+
describeSummarizer: () => this._describeCompactionSummarizer(),
|
|
498
|
+
getExtensionRunner: () => this._extensionRunner,
|
|
499
|
+
isRawStream: () => this._isRawStreamSimple(this.agent.streamFn),
|
|
500
|
+
disconnectAgent: () => this._disconnectFromAgent(),
|
|
501
|
+
reconnectAgent: () => this._reconnectToAgent(),
|
|
502
|
+
abortForeground: () => this.abort(),
|
|
503
|
+
emit: (event) => this._emit(event),
|
|
504
|
+
estimateCurrentContextTokens: (messages) => this._pipeline.estimateCurrentContextTokens(messages),
|
|
505
|
+
buildPreDigest: () => this._buildCompactionPreDigest(),
|
|
506
|
+
refreshAfterCompaction: () => this._refreshAfterCompaction(),
|
|
507
|
+
getFailureCorpus: () => this._failureCorpus,
|
|
508
|
+
measureLiveContextTokens: () => this._measureLiveContextTokensForCompaction(),
|
|
509
|
+
runAutoCompaction: (reason, willRetry) => this._runAutoCompaction(reason, willRetry),
|
|
510
|
+
compactWithRetry: (run, signal, provider) => this._compactWithRetry(run, signal, provider),
|
|
511
|
+
});
|
|
428
512
|
this._toolRecoveryLogger = new ToolRecoveryLogger({
|
|
429
513
|
enabled: toolRepairSettings.logging,
|
|
430
514
|
sessionId: this.sessionManager.getSessionId(),
|
|
431
515
|
eventLogPath: this._toolRecoveryEventLogPath,
|
|
432
516
|
failureCorpusPath,
|
|
433
517
|
});
|
|
434
|
-
this.
|
|
518
|
+
this._foregroundRecovery = new ForegroundRecoveryController({
|
|
435
519
|
agent: this.agent,
|
|
436
520
|
modelRegistry: this._modelRegistry,
|
|
437
|
-
|
|
438
|
-
|
|
521
|
+
settingsManager: this.settingsManager,
|
|
522
|
+
failureCorpus: this._failureCorpus,
|
|
523
|
+
getContextWindow: () => this.model?.contextWindow ?? 0,
|
|
439
524
|
emit: (event) => this._emit(event),
|
|
440
|
-
|
|
525
|
+
checkCompaction: (message) => this._checkCompaction(message),
|
|
526
|
+
onSuccessfulAssistant: () => this._compaction.resetOverflowRecovery(),
|
|
441
527
|
});
|
|
442
528
|
this._modelRouter = new ModelRouterController({
|
|
443
529
|
getAgent: () => this.agent,
|
|
@@ -445,8 +531,11 @@ export class AgentSession {
|
|
|
445
531
|
getSettingsManager: () => this.settingsManager,
|
|
446
532
|
getSessionManager: () => this.sessionManager,
|
|
447
533
|
getModelRegistry: () => this._modelRegistry,
|
|
448
|
-
isModelExhausted: (model) => this.
|
|
449
|
-
getFailoverStatus: () => ({
|
|
534
|
+
isModelExhausted: (model) => this._foregroundRecovery.isModelExhausted(`${model.provider}/${model.id}`),
|
|
535
|
+
getFailoverStatus: () => ({
|
|
536
|
+
...this._foregroundRecovery.getFailoverStatus(),
|
|
537
|
+
failureStats: this._failureCorpus.stats(),
|
|
538
|
+
}),
|
|
450
539
|
getAgentDir: () => this._agentDir,
|
|
451
540
|
getReflectionSignal: () => this._reflectionAbort.signal,
|
|
452
541
|
getBaseSystemPrompt: () => this._baseSystemPrompt,
|
|
@@ -459,6 +548,7 @@ export class AgentSession {
|
|
|
459
548
|
emitAutonomyTelemetry: (event) => this._emitAutonomyTelemetry(event),
|
|
460
549
|
resolveLaneModel: (pattern) => this._backgroundLanes.resolveLaneModel(pattern),
|
|
461
550
|
resolveCurationModelIfFit: () => this._resolveCurationModelIfFit(),
|
|
551
|
+
getToolProbeVerdict: (model) => this._toolProtocol.getToolProbeVerdict(model),
|
|
462
552
|
});
|
|
463
553
|
this._reflection = new ReflectionController({
|
|
464
554
|
getModel: () => this.model,
|
|
@@ -472,7 +562,7 @@ export class AgentSession {
|
|
|
472
562
|
isChildSession: () => this._isChildSession,
|
|
473
563
|
isDisposed: () => this._disposed,
|
|
474
564
|
getReflectionSignal: () => this._reflectionAbort.signal,
|
|
475
|
-
resolveTextToolCallProtocol: (model) => this.
|
|
565
|
+
resolveTextToolCallProtocol: (model) => this._resolveModelToolProtocol(model).protocol,
|
|
476
566
|
archivePromotedSkill: (name) => this.archivePromotedSkill(name),
|
|
477
567
|
emitAutonomyTelemetry: (event) => this._emitAutonomyTelemetry(event),
|
|
478
568
|
ensureModelReady: (model) => this._localRuntimeController.ensureIsolatedModelReady(model),
|
|
@@ -482,11 +572,22 @@ export class AgentSession {
|
|
|
482
572
|
this._goalContinuation = new GoalLoopController({
|
|
483
573
|
getGoalRuntimeSnapshot: (settings) => this.getGoalRuntimeSnapshot(settings),
|
|
484
574
|
prompt: (text, options) => this.prompt(text, options),
|
|
575
|
+
recordGoalContinuationPass: (pass) => this.recordGoalContinuationPass(pass),
|
|
485
576
|
});
|
|
486
577
|
this._extensionRunnerRef = config.extensionRunnerRef;
|
|
487
|
-
this._initialActiveToolNames = config.
|
|
488
|
-
|
|
489
|
-
|
|
578
|
+
this._initialActiveToolNames = config.orchestrationProfile
|
|
579
|
+
? [...config.orchestrationProfile.toolNames]
|
|
580
|
+
: config.initialActiveToolNames;
|
|
581
|
+
this._allowedToolNames = config.orchestrationProfile
|
|
582
|
+
? new Set(config.orchestrationProfile.toolNames)
|
|
583
|
+
: config.allowedToolNames
|
|
584
|
+
? new Set(config.allowedToolNames)
|
|
585
|
+
: undefined;
|
|
586
|
+
this._excludedToolNames = config.orchestrationProfile
|
|
587
|
+
? undefined
|
|
588
|
+
: config.excludedToolNames
|
|
589
|
+
? new Set(config.excludedToolNames)
|
|
590
|
+
: undefined;
|
|
490
591
|
this._toolProfileFilter = config.toolProfileFilter
|
|
491
592
|
? { allow: config.toolProfileFilter.allow ?? [], block: config.toolProfileFilter.block ?? [] }
|
|
492
593
|
: undefined;
|
|
@@ -498,11 +599,12 @@ export class AgentSession {
|
|
|
498
599
|
this._runtimeBuilder = new RuntimeBuilder({
|
|
499
600
|
getAgent: () => this.agent,
|
|
500
601
|
getCwd: () => this._cwd,
|
|
602
|
+
getShellSessionKey: () => this._shellSessionKey,
|
|
501
603
|
getAgentDir: () => this._agentDir,
|
|
502
604
|
getSessionManager: () => this.sessionManager,
|
|
503
605
|
getSettingsManager: () => this.settingsManager,
|
|
504
606
|
getModelRegistry: () => this._modelRegistry,
|
|
505
|
-
isModelExhausted: (model) => this.
|
|
607
|
+
isModelExhausted: (model) => this._foregroundRecovery.isModelExhausted(`${model.provider}/${model.id}`),
|
|
506
608
|
getResourceLoader: () => this._resourceLoader,
|
|
507
609
|
getExtensionRunner: () => this._extensionRunner,
|
|
508
610
|
setExtensionRunner: (runner) => {
|
|
@@ -526,6 +628,7 @@ export class AgentSession {
|
|
|
526
628
|
this._toolProfileFilter = filter;
|
|
527
629
|
},
|
|
528
630
|
getAllowedToolNames: () => this._allowedToolNames,
|
|
631
|
+
getOrchestrationProfile: () => config.orchestrationProfile,
|
|
529
632
|
getExcludedToolNames: () => this._excludedToolNames,
|
|
530
633
|
deriveToolProfileFilter: () => this._profileFilter.deriveToolProfileFilter(),
|
|
531
634
|
isToolOrCommandAllowedByProfile: (name) => this._profileFilter.isToolOrCommandAllowedByProfile(name),
|
|
@@ -546,6 +649,7 @@ export class AgentSession {
|
|
|
546
649
|
reapplyActiveProfileModelSettings: () => this._profileFilter.reapplyActiveProfileModelSettings(),
|
|
547
650
|
notifyExtensionsChanged: () => this._notifyExtensionsChanged(),
|
|
548
651
|
getToolArtifactStore: () => this._getToolArtifactStore(),
|
|
652
|
+
getSessionImageStore: () => this._getSessionImageStore(),
|
|
549
653
|
getMemoryManager: () => this._memory.getMemoryManager(),
|
|
550
654
|
getMemoryAuditDiagnostics: () => this._memory.getMemoryAuditDiagnostics(),
|
|
551
655
|
clearPendingMemoryProviders: () => this._memory.clearPendingProviders(),
|
|
@@ -558,13 +662,16 @@ export class AgentSession {
|
|
|
558
662
|
saveTaskStepsStateSnapshot: (state) => this.saveTaskStepsStateSnapshot(state),
|
|
559
663
|
getContextGcReport: (messages) => this.getContextGcReport(messages),
|
|
560
664
|
startWorkerDelegation: (request) => this._backgroundLanes.startWorkerDelegation(request),
|
|
665
|
+
getOrchestrationProfileCatalog: () => this._backgroundLanes.getOrchestrationProfileCatalog(),
|
|
561
666
|
getWorkerLaneRecords: () => this._backgroundLanes.getLaneRecords(),
|
|
562
|
-
|
|
667
|
+
getWorkerClaimSnapshots: () => this.getWorkerClaimSnapshots(),
|
|
668
|
+
resolveManagedLaneId: (id) => this._backgroundLanes.resolveManagedLaneId(id),
|
|
563
669
|
runWorkerDelegationOnce: (request) => this.runWorkerDelegationOnce(request),
|
|
564
670
|
runModelFitness: (args) => this.runModelFitness(args),
|
|
565
671
|
resolveCurationModelIfFit: () => this._resolveCurationModelIfFit(),
|
|
566
672
|
runIsolatedCompletion: (opts) => this.runIsolatedCompletion(opts),
|
|
567
673
|
addSpawnedUsage: (usage, opts) => this.addSpawnedUsage(usage, opts),
|
|
674
|
+
getLaneWorkerRefusal: () => this.getLaneWorkerRefusal(),
|
|
568
675
|
createAgentContextSnapshot: () => this._createAgentContextSnapshot(),
|
|
569
676
|
getContextUsage: () => this.getContextUsage(),
|
|
570
677
|
isStreaming: () => this.isStreaming,
|
|
@@ -573,6 +680,10 @@ export class AgentSession {
|
|
|
573
680
|
getExtensionCommandContextActions: () => this._extensionCommandContextActions,
|
|
574
681
|
getExtensionShutdownHandler: () => this._extensionShutdownHandler,
|
|
575
682
|
getExtensionErrorListener: () => this._extensionErrorListener,
|
|
683
|
+
// Stop any pi-spawned local runtime the just-committed reload no longer routes to.
|
|
684
|
+
reconcileLocalRuntimes: () => {
|
|
685
|
+
this._localRuntimeController.reconcile(this._collectEligibleLocalModelsForReconcile());
|
|
686
|
+
},
|
|
576
687
|
});
|
|
577
688
|
this._analytics = new SessionAnalytics({
|
|
578
689
|
getState: () => this.state,
|
|
@@ -585,7 +696,7 @@ export class AgentSession {
|
|
|
585
696
|
});
|
|
586
697
|
const previousToolArgumentValidation = this.agent.onToolArgumentValidation;
|
|
587
698
|
this.agent.onToolArgumentValidation = (event) => {
|
|
588
|
-
const taggedEvent = this.
|
|
699
|
+
const taggedEvent = this._toolProtocol.tagAdaptationRuleTeaching(event);
|
|
589
700
|
if (taggedEvent.outcome === "repaired" || taggedEvent.outcome === "bounced") {
|
|
590
701
|
this._toolSelection.recordValidation(taggedEvent.tool, taggedEvent.outcome);
|
|
591
702
|
}
|
|
@@ -594,8 +705,8 @@ export class AgentSession {
|
|
|
594
705
|
if (!logRecord)
|
|
595
706
|
return;
|
|
596
707
|
this._analytics.recordToolArgumentValidation(logRecord);
|
|
597
|
-
this.
|
|
598
|
-
this.
|
|
708
|
+
this._toolProtocol.handleValidationOutcome(taggedEvent);
|
|
709
|
+
this._toolProtocol.handleAdaptationTelemetry(taggedEvent);
|
|
599
710
|
};
|
|
600
711
|
this._treeNavigator = new SessionTreeNavigator({
|
|
601
712
|
getSessionManager: () => this.sessionManager,
|
|
@@ -633,6 +744,7 @@ export class AgentSession {
|
|
|
633
744
|
getSessionManager: () => this.sessionManager,
|
|
634
745
|
getSettingsManager: () => this.settingsManager,
|
|
635
746
|
isStreaming: () => this.isStreaming,
|
|
747
|
+
getShellSessionKey: () => this._shellSessionKey,
|
|
636
748
|
});
|
|
637
749
|
this._profileFilter = new ProfileFilterController({
|
|
638
750
|
getSettingsManager: () => this.settingsManager,
|
|
@@ -844,18 +956,9 @@ export class AgentSession {
|
|
|
844
956
|
const authoritativeMessages = this.agent.state.messages.length > 0 ? this.agent.state.messages : transformed;
|
|
845
957
|
let currentMessages = authoritativeMessages;
|
|
846
958
|
try {
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
const contextTokens = this._estimateCurrentContextTokens(authoritativeMessages);
|
|
851
|
-
if (shouldCompact(contextTokens, contextWindow, settings, this.model?.autoCompactionTriggerTokens)) {
|
|
852
|
-
const latestBefore = getLatestCompactionEntry(this.sessionManager.getBranch())?.id;
|
|
853
|
-
await this._runAutoCompaction("threshold", false);
|
|
854
|
-
const latestAfter = getLatestCompactionEntry(this.sessionManager.getBranch())?.id;
|
|
855
|
-
if (latestAfter && latestAfter !== latestBefore) {
|
|
856
|
-
currentMessages = this.agent.state.messages.slice();
|
|
857
|
-
}
|
|
858
|
-
}
|
|
959
|
+
if (!this.isCompacting &&
|
|
960
|
+
(await this._compaction.maybeCompactBeforeContextTransform(authoritativeMessages, (messages) => this._applyContextGc(messages, false).messages))) {
|
|
961
|
+
currentMessages = this.agent.state.messages.slice();
|
|
859
962
|
}
|
|
860
963
|
}
|
|
861
964
|
catch {
|
|
@@ -888,6 +991,16 @@ export class AgentSession {
|
|
|
888
991
|
* model, full system prompt, converted messages, and tool schemas are known. The guard is a
|
|
889
992
|
* projection threshold rather than a hard output cap: warning mode never reduces capability, while
|
|
890
993
|
* opt-in downgrade changes only this request's reasoning effort. Best-effort: never throws.
|
|
994
|
+
*
|
|
995
|
+
* The ceiling is turn-cumulative: the next foreground call's projection is folded together with
|
|
996
|
+
* background/research/worker/reflection spend recorded SINCE THIS TURN BEGAN — {@link getSpawnedUsage}'s
|
|
997
|
+
* already-recorded rollup (the same read-side-deduped total the footer's SUBAGENTS line uses) minus the
|
|
998
|
+
* baseline snapshotted before a new turn enters routing in `_promptUnserialized`
|
|
999
|
+
* ({@link _costGuardTurnBaselineUsd}) — so a
|
|
1000
|
+
* turn that is cheap in the foreground but has spent heavily via background lanes THIS turn still trips
|
|
1001
|
+
* the warning, while a prior turn's background spend does not keep every later turn's guard stuck
|
|
1002
|
+
* "over". A background lane that finishes mid-turn is attributed to whichever turn it completes in.
|
|
1003
|
+
* Per-lane dollar caps (research/worker `maxUsd`) are separate and untouched by this guard.
|
|
891
1004
|
*/
|
|
892
1005
|
_resolveCostGuardRequestReasoning(model, context, reasoning, requestMaxTokens) {
|
|
893
1006
|
try {
|
|
@@ -907,7 +1020,10 @@ export class AgentSession {
|
|
|
907
1020
|
cost: model.cost,
|
|
908
1021
|
longContextPricing: model.longContextPricing,
|
|
909
1022
|
});
|
|
910
|
-
|
|
1023
|
+
// Only spend recorded SINCE this turn's baseline counts -- never negative (a dedup/rollup
|
|
1024
|
+
// correction could otherwise move the total backward transiently).
|
|
1025
|
+
const cumulativeBackgroundUsd = Math.max(0, this.getSpawnedUsage().cost - this._costGuardTurnBaselineUsd);
|
|
1026
|
+
const decision = evaluateCostGuard(estUsd, { maxTurnUsd: guard.maxTurnUsd, action: guard.action }, cumulativeBackgroundUsd);
|
|
911
1027
|
this._lastCostGuardDecision = decision;
|
|
912
1028
|
if (!decision.over || guard.action !== "downgrade" || reasoning === undefined)
|
|
913
1029
|
return reasoning;
|
|
@@ -925,7 +1041,7 @@ export class AgentSession {
|
|
|
925
1041
|
}
|
|
926
1042
|
get _skillCurator() {
|
|
927
1043
|
if (!this._skillCuratorInstance) {
|
|
928
|
-
this._skillCuratorInstance = new SkillCurator(
|
|
1044
|
+
this._skillCuratorInstance = new SkillCurator(resourceDir("skills", this._agentDir));
|
|
929
1045
|
}
|
|
930
1046
|
return this._skillCuratorInstance;
|
|
931
1047
|
}
|
|
@@ -981,437 +1097,32 @@ export class AgentSession {
|
|
|
981
1097
|
tools: this.agent.state.tools.slice(),
|
|
982
1098
|
};
|
|
983
1099
|
}
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
_toolRepairSettings() {
|
|
988
|
-
return resolveCurrentToolRepairSettings(this.settingsManager.settings);
|
|
1100
|
+
/** Compatibility seam retained for focused auto-probe regressions. */
|
|
1101
|
+
_probeToolCallingForModel(model) {
|
|
1102
|
+
return this._toolProtocol.probeToolCallingForModel(model);
|
|
989
1103
|
}
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
this.
|
|
993
|
-
}
|
|
994
|
-
_getModelAdaptationRulesForPrompt() {
|
|
995
|
-
if (!this._toolRepairSettings().teach)
|
|
996
|
-
return [];
|
|
997
|
-
const modelKey = this._modelAdaptationKeyFor(this.agent.state.model);
|
|
998
|
-
return modelKey ? this._modelAdaptationStore.get(modelKey).rules : [];
|
|
999
|
-
}
|
|
1000
|
-
_textProtocolFlag(model) {
|
|
1001
|
-
// Phase 7 gating hierarchy: PI_TEXT_TOOL_CALL_PROTOCOL_DISABLED is resolved
|
|
1002
|
-
// in _toolRepairSettings() as the env kill switch, then settings.toolRepair.textProtocol
|
|
1003
|
-
// force-enables/disables globally, then Model.textToolCallProtocol opts in per model,
|
|
1004
|
-
// then a persisted /toolprobe text-protocol verdict opts in that exact model. The
|
|
1005
|
-
// calibration store is consulted after this flag; native provider tool calls still
|
|
1006
|
-
// win when emitted, and this only enables the text-protocol fallback lane.
|
|
1007
|
-
const override = this._toolRepairSettings().textProtocol;
|
|
1008
|
-
if (override !== undefined)
|
|
1009
|
-
return override;
|
|
1010
|
-
if (model?.textToolCallProtocol === true)
|
|
1011
|
-
return true;
|
|
1012
|
-
const modelKey = this._modelAdaptationKeyFor(model);
|
|
1013
|
-
return !!modelKey && this._modelAdaptationStore.get(modelKey).toolProbe?.status === "text-protocol";
|
|
1014
|
-
}
|
|
1015
|
-
async _streamForToolProbe(model, context, options) {
|
|
1016
|
-
let requestOptions = options;
|
|
1017
|
-
if (this._isRawStreamSimple(this.agent.streamFn)) {
|
|
1018
|
-
const auth = await this._getRequiredRequestAuth(model);
|
|
1019
|
-
requestOptions = {
|
|
1020
|
-
...options,
|
|
1021
|
-
apiKey: auth.apiKey,
|
|
1022
|
-
headers: auth.headers || options.headers ? { ...auth.headers, ...options.headers } : undefined,
|
|
1023
|
-
};
|
|
1024
|
-
}
|
|
1025
|
-
return this.agent.streamFn(model, context, requestOptions);
|
|
1026
|
-
}
|
|
1027
|
-
_textProtocolCalibrationContext(variant, token) {
|
|
1028
|
-
const primer = generateTextToolProtocolPrimer([TEXT_TOOL_PROTOCOL_ECHO_TOOL], { variant });
|
|
1029
|
-
const instruction = `Text tool protocol calibration trial. Using the protocol above, call echo with data exactly "${token}". Output only the tool-call envelope.`;
|
|
1030
|
-
return {
|
|
1031
|
-
systemPrompt: `${primer}\n\n${instruction}`,
|
|
1032
|
-
messages: [{ role: "user", content: [{ type: "text", text: instruction }], timestamp: Date.now() }],
|
|
1033
|
-
};
|
|
1034
|
-
}
|
|
1035
|
-
_messageHasToolCallWithStringArgument(message, toolName, argName, argValue) {
|
|
1036
|
-
return message.content.some((block) => {
|
|
1037
|
-
if (block.type !== "toolCall" || block.name !== toolName)
|
|
1038
|
-
return false;
|
|
1039
|
-
const args = block.arguments;
|
|
1040
|
-
return (typeof args === "object" &&
|
|
1041
|
-
args !== null &&
|
|
1042
|
-
!Array.isArray(args) &&
|
|
1043
|
-
args[argName] === argValue);
|
|
1044
|
-
});
|
|
1045
|
-
}
|
|
1046
|
-
_nativeToolProbeSystemPrompt(instruction) {
|
|
1047
|
-
const base = (this.agent.state.systemPrompt ?? "").trim();
|
|
1048
|
-
return base ? `${base}\n\n${instruction}` : instruction;
|
|
1049
|
-
}
|
|
1050
|
-
async _runNativeReadTaskProbeTrial(model, path) {
|
|
1051
|
-
const instruction = `Native tool-call capability probe: task-scale read. Use provider-native tool calling, not prose. ` +
|
|
1052
|
-
`Call read exactly once with path exactly "${path}".`;
|
|
1053
|
-
const stream = await this._streamForToolProbe(model, {
|
|
1054
|
-
systemPrompt: this._nativeToolProbeSystemPrompt(instruction),
|
|
1055
|
-
messages: [{ role: "user", content: [{ type: "text", text: instruction }], timestamp: Date.now() }],
|
|
1056
|
-
tools: [NATIVE_TOOL_PROBE_READ_TOOL],
|
|
1057
|
-
}, { textToolCallProtocol: false, maxRetries: 0, temperature: 0, maxTokens: 768 });
|
|
1058
|
-
return this._messageHasToolCallWithStringArgument(await stream.result(), "read", "path", path);
|
|
1059
|
-
}
|
|
1060
|
-
async _runNativeEchoToolProbeTrial(model, token) {
|
|
1061
|
-
const instruction = `Native tool-call capability probe: echo-only. Use provider-native tool calling, not prose. ` +
|
|
1062
|
-
`Call echo with data exactly "${token}".`;
|
|
1063
|
-
const stream = await this._streamForToolProbe(model, {
|
|
1064
|
-
systemPrompt: this._nativeToolProbeSystemPrompt(instruction),
|
|
1065
|
-
messages: [{ role: "user", content: [{ type: "text", text: instruction }], timestamp: Date.now() }],
|
|
1066
|
-
tools: [TEXT_TOOL_PROTOCOL_ECHO_TOOL],
|
|
1067
|
-
}, { textToolCallProtocol: false, maxRetries: 0, temperature: 0, maxTokens: 256 });
|
|
1068
|
-
return this._messageHasToolCallWithStringArgument(await stream.result(), "echo", "data", token);
|
|
1069
|
-
}
|
|
1070
|
-
async _gradeNativeToolCallingForModel(model, token) {
|
|
1071
|
-
const path = join(getProcessWorkRun(this._agentDir, "probes", "native-tools").path, `pi-native-probe-${process.pid}-${Date.now()}.txt`);
|
|
1072
|
-
writeFileSync(path, token, "utf-8");
|
|
1073
|
-
try {
|
|
1074
|
-
const taskPassed = await this._runNativeReadTaskProbeTrial(model, path);
|
|
1075
|
-
if (taskPassed)
|
|
1076
|
-
return "task";
|
|
1077
|
-
const echoPassed = await this._runNativeEchoToolProbeTrial(model, token);
|
|
1078
|
-
if (echoPassed)
|
|
1079
|
-
return "echo-only";
|
|
1080
|
-
return "absent";
|
|
1081
|
-
}
|
|
1082
|
-
finally {
|
|
1083
|
-
rmSync(path, { force: true });
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
async _runTextProtocolTrial(model, variant, token) {
|
|
1087
|
-
const stream = await this._streamForToolProbe(model, this._textProtocolCalibrationContext(variant, token), {
|
|
1088
|
-
textToolCallProtocol: false,
|
|
1089
|
-
maxRetries: 0,
|
|
1090
|
-
temperature: 0,
|
|
1091
|
-
maxTokens: 256,
|
|
1092
|
-
});
|
|
1093
|
-
const message = await stream.result();
|
|
1094
|
-
const text = message.content
|
|
1095
|
-
.filter((block) => block.type === "text")
|
|
1096
|
-
.map((block) => block.text)
|
|
1097
|
-
.join("\n")
|
|
1098
|
-
.trim();
|
|
1099
|
-
if (!text)
|
|
1100
|
-
return false;
|
|
1101
|
-
const parsed = parseTextToolCalls(text, [TEXT_TOOL_PROTOCOL_ECHO_TOOL]);
|
|
1102
|
-
return parsed.calls.some((call) => call.name === "echo" && call.arguments.data === token);
|
|
1103
|
-
}
|
|
1104
|
-
async _calibrateTextToolProtocolForModel(model, modelKey, options) {
|
|
1105
|
-
const variantsTried = [];
|
|
1106
|
-
for (const variant of TEXT_TOOL_PROTOCOL_VARIANTS) {
|
|
1107
|
-
variantsTried.push(variant);
|
|
1108
|
-
let passed = true;
|
|
1109
|
-
for (let trial = 0; trial < TEXT_TOOL_PROTOCOL_TRIALS_PER_VARIANT; trial++) {
|
|
1110
|
-
const ok = await this._runTextProtocolTrial(model, variant, `pi-calibration-${trial + 1}`);
|
|
1111
|
-
if (!ok) {
|
|
1112
|
-
passed = false;
|
|
1113
|
-
break;
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
if (passed) {
|
|
1117
|
-
const calibratedAt = new Date().toISOString();
|
|
1118
|
-
if (modelKey) {
|
|
1119
|
-
this._modelAdaptationStore.setProtocol(modelKey, { version: TEXT_TOOL_PROTOCOL_VERSION, status: "calibrated", variant, calibratedAt }, calibratedAt);
|
|
1120
|
-
}
|
|
1121
|
-
return { status: "calibrated", variant, calibratedAt };
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
const attemptedAt = new Date().toISOString();
|
|
1125
|
-
if (modelKey && options.persistFailure) {
|
|
1126
|
-
this._modelAdaptationStore.setProtocol(modelKey, { version: TEXT_TOOL_PROTOCOL_VERSION, status: "failed", attemptedAt, variantsTried }, attemptedAt);
|
|
1127
|
-
}
|
|
1128
|
-
return { status: "failed", attemptedAt, variantsTried };
|
|
1129
|
-
}
|
|
1130
|
-
async _ensureTextToolProtocolForActiveModel() {
|
|
1131
|
-
const model = this.agent.state.model;
|
|
1132
|
-
if (!this._textProtocolFlag(model)) {
|
|
1133
|
-
this.agent.textToolCallProtocol = undefined;
|
|
1134
|
-
return;
|
|
1135
|
-
}
|
|
1136
|
-
const modelKey = this._modelAdaptationKeyFor(model);
|
|
1137
|
-
if (!modelKey) {
|
|
1138
|
-
this.agent.textToolCallProtocol = true;
|
|
1139
|
-
return;
|
|
1140
|
-
}
|
|
1141
|
-
const profile = this._modelAdaptationStore.get(modelKey);
|
|
1142
|
-
if (profile.protocol?.version === TEXT_TOOL_PROTOCOL_VERSION) {
|
|
1143
|
-
if (profile.protocol.status === "failed") {
|
|
1144
|
-
this.agent.textToolCallProtocol = undefined;
|
|
1145
|
-
throw new Error(`Previous text tool protocol calibration failed for ${modelKey} at ${profile.protocol.attemptedAt}. ` +
|
|
1146
|
-
`Variants tried: ${profile.protocol.variantsTried.join(", ")}. ` +
|
|
1147
|
-
`Run /toolhealth for details or /toolprotocol-reset ${modelKey} to retry calibration.`);
|
|
1148
|
-
}
|
|
1149
|
-
this.agent.textToolCallProtocol = { variant: profile.protocol.variant };
|
|
1150
|
-
return;
|
|
1151
|
-
}
|
|
1152
|
-
const result = await this._calibrateTextToolProtocolForModel(model, modelKey, { persistFailure: true });
|
|
1153
|
-
if (result.status === "calibrated") {
|
|
1154
|
-
this.agent.textToolCallProtocol = { variant: result.variant };
|
|
1155
|
-
return;
|
|
1156
|
-
}
|
|
1157
|
-
this.agent.textToolCallProtocol = undefined;
|
|
1158
|
-
throw new Error(`Model ${modelKey} cannot follow the text tool protocol after calibration. ` +
|
|
1159
|
-
`Run /toolhealth for details or /toolprotocol-reset ${modelKey} to retry calibration.`);
|
|
1160
|
-
}
|
|
1161
|
-
_modelRef(model) {
|
|
1162
|
-
return `${model.provider}/${model.id}`;
|
|
1163
|
-
}
|
|
1164
|
-
_formatToolProbeReport(results) {
|
|
1165
|
-
const lines = [
|
|
1166
|
-
"Tool probe results:",
|
|
1167
|
-
"Model | Verdict | Variant | Native grade | Diagnostic",
|
|
1168
|
-
"--- | --- | --- | --- | ---",
|
|
1169
|
-
];
|
|
1170
|
-
for (const result of results) {
|
|
1171
|
-
lines.push([
|
|
1172
|
-
result.model,
|
|
1173
|
-
result.verdict,
|
|
1174
|
-
result.variant ?? "-",
|
|
1175
|
-
result.nativeGrade ?? "-",
|
|
1176
|
-
result.diagnostic ? result.diagnostic.replace(/\s+/g, " ").slice(0, 160) : "-",
|
|
1177
|
-
].join(" | "));
|
|
1178
|
-
}
|
|
1179
|
-
return lines.join("\n");
|
|
1180
|
-
}
|
|
1181
|
-
_storeToolProbe(modelKey, probe) {
|
|
1182
|
-
this._modelAdaptationStore.setToolProbe(modelKey, probe, probe.probedAt);
|
|
1183
|
-
}
|
|
1184
|
-
async _probeToolCallingForModel(model) {
|
|
1185
|
-
const modelKey = this._modelRef(model);
|
|
1186
|
-
const probedAt = new Date().toISOString();
|
|
1187
|
-
let nativeGrade = "absent";
|
|
1188
|
-
let diagnostic;
|
|
1189
|
-
try {
|
|
1190
|
-
nativeGrade = await this._gradeNativeToolCallingForModel(model, "pi-native-probe");
|
|
1191
|
-
if (nativeGrade === "task") {
|
|
1192
|
-
this._storeToolProbe(modelKey, {
|
|
1193
|
-
version: TEXT_TOOL_PROTOCOL_VERSION,
|
|
1194
|
-
status: "native",
|
|
1195
|
-
probedAt,
|
|
1196
|
-
nativeGrade,
|
|
1197
|
-
});
|
|
1198
|
-
return { model: modelKey, verdict: "native", nativeGrade };
|
|
1199
|
-
}
|
|
1200
|
-
diagnostic =
|
|
1201
|
-
nativeGrade === "echo-only"
|
|
1202
|
-
? "Native echo probe passed but task-scale read probe failed."
|
|
1203
|
-
: "Native task-scale read and echo probes did not produce provider-native tool calls.";
|
|
1204
|
-
}
|
|
1205
|
-
catch (error) {
|
|
1206
|
-
diagnostic = error instanceof Error ? error.message : String(error);
|
|
1207
|
-
}
|
|
1208
|
-
try {
|
|
1209
|
-
const calibrated = await this._calibrateTextToolProtocolForModel(model, modelKey, { persistFailure: false });
|
|
1210
|
-
if (calibrated.status === "calibrated") {
|
|
1211
|
-
this._storeToolProbe(modelKey, {
|
|
1212
|
-
version: TEXT_TOOL_PROTOCOL_VERSION,
|
|
1213
|
-
status: "text-protocol",
|
|
1214
|
-
probedAt: calibrated.calibratedAt,
|
|
1215
|
-
variant: calibrated.variant,
|
|
1216
|
-
nativeGrade,
|
|
1217
|
-
diagnostic,
|
|
1218
|
-
});
|
|
1219
|
-
return { model: modelKey, verdict: "text-protocol", variant: calibrated.variant, nativeGrade, diagnostic };
|
|
1220
|
-
}
|
|
1221
|
-
diagnostic = `${diagnostic ? `${diagnostic} ` : ""}Text protocol variants failed: ${calibrated.variantsTried.join(", ")}`;
|
|
1222
|
-
}
|
|
1223
|
-
catch (error) {
|
|
1224
|
-
diagnostic = error instanceof Error ? error.message : String(error);
|
|
1225
|
-
}
|
|
1226
|
-
this._storeToolProbe(modelKey, {
|
|
1227
|
-
version: TEXT_TOOL_PROTOCOL_VERSION,
|
|
1228
|
-
status: "none",
|
|
1229
|
-
probedAt,
|
|
1230
|
-
nativeGrade,
|
|
1231
|
-
diagnostic,
|
|
1232
|
-
});
|
|
1233
|
-
return { model: modelKey, verdict: "none", nativeGrade, diagnostic };
|
|
1234
|
-
}
|
|
1235
|
-
async _resolveToolProbeModels(target) {
|
|
1236
|
-
const trimmed = target?.trim();
|
|
1237
|
-
if (!trimmed)
|
|
1238
|
-
return this._modelRegistry.getAvailable();
|
|
1239
|
-
const [provider, ...modelParts] = trimmed.split("/");
|
|
1240
|
-
const modelId = modelParts.join("/");
|
|
1241
|
-
if (!provider || !modelId)
|
|
1242
|
-
throw new Error("Usage: /toolprobe [provider/model]");
|
|
1243
|
-
const exact = this._modelRegistry.find(provider, modelId);
|
|
1244
|
-
if (exact)
|
|
1245
|
-
return [exact];
|
|
1246
|
-
const current = this.agent.state.model;
|
|
1247
|
-
if (current?.provider === provider && current.id === modelId)
|
|
1248
|
-
return [current];
|
|
1249
|
-
throw new Error(`Model not found: ${trimmed}`);
|
|
1104
|
+
/** Compatibility seam retained for focused protocol-selection doctrine regressions. */
|
|
1105
|
+
_resolveModelToolProtocol(model) {
|
|
1106
|
+
return this._toolProtocol.resolveModelToolProtocol(model);
|
|
1250
1107
|
}
|
|
1251
1108
|
async probeToolCalling(target) {
|
|
1252
|
-
|
|
1253
|
-
if (models.length === 0)
|
|
1254
|
-
throw new Error("No available models to probe.");
|
|
1255
|
-
const results = [];
|
|
1256
|
-
for (const model of models) {
|
|
1257
|
-
results.push(await this._probeToolCallingForModel(model));
|
|
1258
|
-
}
|
|
1259
|
-
return { results, table: this._formatToolProbeReport(results) };
|
|
1260
|
-
}
|
|
1261
|
-
_handleTextToolProtocolParse(event) {
|
|
1262
|
-
this._textProtocolParseObservedThisTurn = true;
|
|
1263
|
-
const modelKey = `${event.provider}/${event.model}`;
|
|
1264
|
-
if (event.status === "parsed")
|
|
1265
|
-
return;
|
|
1266
|
-
const signature = `${event.variant}:${event.reason ?? "failed"}`;
|
|
1267
|
-
const previous = this._textProtocolParseFailures.get(modelKey);
|
|
1268
|
-
const repeats = previous?.signature === signature ? previous.repeats + 1 : 1;
|
|
1269
|
-
this._textProtocolParseFailures.set(modelKey, { signature, repeats });
|
|
1270
|
-
if (repeats < TEXT_TOOL_PROTOCOL_PARSE_FAILURE_THRESHOLD)
|
|
1271
|
-
return;
|
|
1272
|
-
const profile = this._modelAdaptationStore.get(modelKey);
|
|
1273
|
-
if (profile.protocol?.version === TEXT_TOOL_PROTOCOL_VERSION && profile.protocol.status !== "failed") {
|
|
1274
|
-
this._modelAdaptationStore.removeProtocol(modelKey);
|
|
1275
|
-
this.agent.textToolCallProtocol = undefined;
|
|
1276
|
-
}
|
|
1277
|
-
this._textProtocolParseFailures.delete(modelKey);
|
|
1278
|
-
}
|
|
1279
|
-
_handleTextToolProtocolValidationOutcome(event) {
|
|
1280
|
-
if (event.source !== "text-protocol")
|
|
1281
|
-
return;
|
|
1282
|
-
const protocol = this.agent.textToolCallProtocol;
|
|
1283
|
-
const variant = protocol === true ? "tool-tag" : protocol ? protocol.variant : undefined;
|
|
1284
|
-
if (!variant)
|
|
1285
|
-
return;
|
|
1286
|
-
const status = event.outcome === "bounced" ? "failed" : "parsed";
|
|
1287
|
-
if (this._textProtocolValidationOutcomeThisTurn?.status === "parsed" && status === "failed")
|
|
1288
|
-
return;
|
|
1289
|
-
this._textProtocolValidationOutcomeThisTurn = {
|
|
1290
|
-
provider: event.provider ?? this.agent.state.model.provider,
|
|
1291
|
-
model: event.model ?? this.agent.state.model.id,
|
|
1292
|
-
variant,
|
|
1293
|
-
status,
|
|
1294
|
-
callCount: 1,
|
|
1295
|
-
textLength: 0,
|
|
1296
|
-
...(status === "failed" && {
|
|
1297
|
-
reason: event.errorKeywords?.includes("unknown_tool") ? "unknown-tool" : "validation-failed",
|
|
1298
|
-
}),
|
|
1299
|
-
};
|
|
1300
|
-
}
|
|
1301
|
-
_recordTextToolProtocolParseOutcomeFromLastAssistant() {
|
|
1302
|
-
const validationOutcome = this._textProtocolValidationOutcomeThisTurn;
|
|
1303
|
-
this._textProtocolValidationOutcomeThisTurn = undefined;
|
|
1304
|
-
if (validationOutcome?.status === "parsed") {
|
|
1305
|
-
this._textProtocolParseObservedThisTurn = true;
|
|
1306
|
-
this._textProtocolParseFailures.delete(`${validationOutcome.provider}/${validationOutcome.model}`);
|
|
1307
|
-
return;
|
|
1308
|
-
}
|
|
1309
|
-
if (validationOutcome) {
|
|
1310
|
-
this._handleTextToolProtocolParse(validationOutcome);
|
|
1311
|
-
return;
|
|
1312
|
-
}
|
|
1313
|
-
if (this._textProtocolParseObservedThisTurn)
|
|
1314
|
-
return;
|
|
1315
|
-
const protocol = this.agent.textToolCallProtocol;
|
|
1316
|
-
if (protocol === false || protocol === true || !protocol?.variant)
|
|
1317
|
-
return;
|
|
1318
|
-
const response = this._findLastAssistantMessage();
|
|
1319
|
-
if (!response)
|
|
1320
|
-
return;
|
|
1321
|
-
const responseText = response.content
|
|
1322
|
-
.filter((content) => content.type === "text")
|
|
1323
|
-
.map((content) => content.text)
|
|
1324
|
-
.join("\n");
|
|
1325
|
-
if (!responseText)
|
|
1326
|
-
return;
|
|
1327
|
-
const parsed = parseTextToolCalls(responseText, this.agent.state.tools);
|
|
1328
|
-
const attempted = parsed.attempted || this._looksLikeTextToolProtocolAttempt(responseText);
|
|
1329
|
-
if (!attempted)
|
|
1330
|
-
return;
|
|
1331
|
-
this._handleTextToolProtocolParse({
|
|
1332
|
-
provider: this.agent.state.model.provider,
|
|
1333
|
-
model: this.agent.state.model.id,
|
|
1334
|
-
variant: protocol.variant,
|
|
1335
|
-
status: parsed.calls.length > 0 ? "parsed" : "failed",
|
|
1336
|
-
reason: parsed.failure,
|
|
1337
|
-
callCount: parsed.calls.length,
|
|
1338
|
-
textLength: responseText.length,
|
|
1339
|
-
});
|
|
1340
|
-
}
|
|
1341
|
-
_looksLikeTextToolProtocolAttempt(text) {
|
|
1342
|
-
return /<pi:call\b|<tool_call\b|```(?:tool|tool_call)[\s\S]*"name"\s*:/i.test(text);
|
|
1343
|
-
}
|
|
1344
|
-
_tagModelAdaptationRuleTeaching(event) {
|
|
1345
|
-
if (!this._toolRepairSettings().teach || event.taught !== "none")
|
|
1346
|
-
return event;
|
|
1347
|
-
const modelKey = event.provider && event.model
|
|
1348
|
-
? `${event.provider}/${event.model}`
|
|
1349
|
-
: this._modelAdaptationKeyFor(this.agent.state.model);
|
|
1350
|
-
if (!modelKey)
|
|
1351
|
-
return event;
|
|
1352
|
-
try {
|
|
1353
|
-
const rules = this._modelAdaptationStore.get(modelKey).rules;
|
|
1354
|
-
const modes = new Set([...event.failureModes, ...event.repairsApplied]);
|
|
1355
|
-
if (rules.some((rule) => modes.has(rule.mode))) {
|
|
1356
|
-
return { ...event, taught: "rule" };
|
|
1357
|
-
}
|
|
1358
|
-
}
|
|
1359
|
-
catch {
|
|
1360
|
-
// Adaptation telemetry tagging is best-effort; leave the original event unchanged.
|
|
1361
|
-
}
|
|
1362
|
-
return event;
|
|
1363
|
-
}
|
|
1364
|
-
_repairSessionCount(modelKey, mode) {
|
|
1365
|
-
const key = `${modelKey}\0${mode}`;
|
|
1366
|
-
const count = (this._repairModeSessionCounts.get(key) ?? 0) + 1;
|
|
1367
|
-
this._repairModeSessionCounts.set(key, count);
|
|
1368
|
-
return count;
|
|
1369
|
-
}
|
|
1370
|
-
_handleModelAdaptationTelemetry(event) {
|
|
1371
|
-
if (!this._toolRepairSettings().teach || event.outcome !== "repaired" || event.repairsApplied.length === 0)
|
|
1372
|
-
return;
|
|
1373
|
-
const modelKey = event.provider && event.model
|
|
1374
|
-
? `${event.provider}/${event.model}`
|
|
1375
|
-
: this._modelAdaptationKeyFor(this.agent.state.model);
|
|
1376
|
-
if (!modelKey)
|
|
1377
|
-
return;
|
|
1378
|
-
try {
|
|
1379
|
-
for (const mode of [...new Set(event.repairsApplied)]) {
|
|
1380
|
-
const profile = this._modelAdaptationStore.get(modelKey);
|
|
1381
|
-
const stats = profile.teachStats[mode] ?? { taught: 0, recurrenceBefore: 0, recurrenceAfter: 0 };
|
|
1382
|
-
if (profile.rules.some((rule) => rule.mode === mode)) {
|
|
1383
|
-
this._modelAdaptationStore.markRuleFired(modelKey, mode);
|
|
1384
|
-
this._modelAdaptationStore.setTeachStats(modelKey, mode, {
|
|
1385
|
-
...stats,
|
|
1386
|
-
recurrenceAfter: stats.recurrenceAfter + 1,
|
|
1387
|
-
});
|
|
1388
|
-
continue;
|
|
1389
|
-
}
|
|
1390
|
-
const recurrenceBefore = stats.recurrenceBefore + 1;
|
|
1391
|
-
this._modelAdaptationStore.setTeachStats(modelKey, mode, { ...stats, recurrenceBefore });
|
|
1392
|
-
const sessionCount = this._repairSessionCount(modelKey, mode);
|
|
1393
|
-
if (sessionCount >= MODEL_ADAPTATION_REPAIR_THRESHOLD ||
|
|
1394
|
-
recurrenceBefore >= MODEL_ADAPTATION_REPAIR_THRESHOLD) {
|
|
1395
|
-
this._modelAdaptationStore.addRule(modelKey, {
|
|
1396
|
-
mode,
|
|
1397
|
-
text: formatToolRepairStandingRule(mode),
|
|
1398
|
-
});
|
|
1399
|
-
this._modelAdaptationStore.setTeachStats(modelKey, mode, {
|
|
1400
|
-
...stats,
|
|
1401
|
-
taught: stats.taught + 1,
|
|
1402
|
-
recurrenceBefore,
|
|
1403
|
-
});
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
catch {
|
|
1408
|
-
// Model adaptation is best-effort; failed telemetry persistence must not affect the turn.
|
|
1409
|
-
}
|
|
1109
|
+
return this._toolProtocol.probeToolCalling(target);
|
|
1410
1110
|
}
|
|
1411
1111
|
/** Tool-build call-site delegation to {@link ContextPipeline.getToolArtifactStore}. */
|
|
1412
1112
|
_getToolArtifactStore() {
|
|
1413
1113
|
return this._pipeline.getToolArtifactStore();
|
|
1414
1114
|
}
|
|
1115
|
+
_getSessionImageStore() {
|
|
1116
|
+
const directory = this.settingsManager.getClipboardImageDirectory();
|
|
1117
|
+
if (!this.sessionManager.isPersisted() && !directory)
|
|
1118
|
+
return undefined;
|
|
1119
|
+
return new SessionImageStore({
|
|
1120
|
+
agentDir: this._agentDir,
|
|
1121
|
+
cwd: this._cwd,
|
|
1122
|
+
sessionId: this.sessionId,
|
|
1123
|
+
directory,
|
|
1124
|
+
});
|
|
1125
|
+
}
|
|
1415
1126
|
/**
|
|
1416
1127
|
* Context-transform hot-path delegation to {@link ContextPipeline.runContextAudit}. Kept as a
|
|
1417
1128
|
* one-line method (not inlined) so the transform stays the single owner of the pass ordering.
|
|
@@ -1543,10 +1254,10 @@ export class AgentSession {
|
|
|
1543
1254
|
...this._profileFilter.profileDeniedResourceObservations(),
|
|
1544
1255
|
...this._profileFilter.getInertExtensionWarnings(),
|
|
1545
1256
|
...this._unboundToolGrantWarnings,
|
|
1546
|
-
//
|
|
1257
|
+
// Auto-built per-turn foreground envelope (observe-only; not enforced). Falls back to a
|
|
1547
1258
|
// live preview when no turn has run yet so /context always shows the current scope.
|
|
1548
1259
|
formatForegroundEnvelopeObservation(this._currentForegroundEnvelope ?? this._buildForegroundEnvelopeFromState()),
|
|
1549
|
-
//
|
|
1260
|
+
// A user disable always beats a profile grant — surface the conflict.
|
|
1550
1261
|
...["tools", "skills", "prompts", "extensions"].flatMap((kind) => this.settingsManager
|
|
1551
1262
|
.getProfileGrantsOverriddenByUserDisable(kind)
|
|
1552
1263
|
.map((entry) => `profile grants ${kind} "${entry}" but your disable list overrides it (user disable wins; re-enable to use)`)),
|
|
@@ -1558,18 +1269,19 @@ export class AgentSession {
|
|
|
1558
1269
|
return formatContextCompositionDashboard(this.getContextCompositionReport());
|
|
1559
1270
|
}
|
|
1560
1271
|
formatToolRepairHealthReport() {
|
|
1561
|
-
return
|
|
1272
|
+
return [
|
|
1273
|
+
this._toolProtocol.formatRepairHealth(this._toolRecoveryLogger.getStats()),
|
|
1274
|
+
formatToolSelectionReport(this._toolSelection.getReport()),
|
|
1275
|
+
].join("\n\n");
|
|
1562
1276
|
}
|
|
1563
1277
|
async flushToolRecoveryLogsForTests(timeoutMs = 1000) {
|
|
1564
1278
|
await this._toolRecoveryLogger.flush(timeoutMs);
|
|
1565
1279
|
}
|
|
1566
1280
|
removeToolRepairRule(model, mode) {
|
|
1567
|
-
return this.
|
|
1281
|
+
return this._toolProtocol.removeRepairRule(model, mode);
|
|
1568
1282
|
}
|
|
1569
1283
|
resetToolProtocolCalibration(model) {
|
|
1570
|
-
|
|
1571
|
-
this._textProtocolParseFailures.delete(model);
|
|
1572
|
-
return removed;
|
|
1284
|
+
return this._toolProtocol.resetProtocolCalibration(model);
|
|
1573
1285
|
}
|
|
1574
1286
|
/** Curation status for diagnostics/dashboard: settings, live telemetry, last refusal reason. */
|
|
1575
1287
|
/** Curation status for diagnostics/dashboard (delegates to {@link ContextPipeline.getContextCurationStatus}). */
|
|
@@ -1614,17 +1326,86 @@ export class AgentSession {
|
|
|
1614
1326
|
getContextGcReport(messages) {
|
|
1615
1327
|
return this._pipeline.getContextGcReport(messages);
|
|
1616
1328
|
}
|
|
1617
|
-
/**
|
|
1618
|
-
* Context-transform hot-path delegation to {@link ContextPipeline.estimateCurrentContextTokens}. Kept
|
|
1619
|
-
* as a one-line method (not inlined) so the transform stays the single owner of the pass ordering;
|
|
1620
|
-
* also feeds the per-turn cost guard.
|
|
1621
|
-
*/
|
|
1622
|
-
_estimateCurrentContextTokens(messages) {
|
|
1623
|
-
return this._pipeline.estimateCurrentContextTokens(messages);
|
|
1624
|
-
}
|
|
1625
1329
|
_installAgentToolHooks() {
|
|
1626
1330
|
this.agent.beforeToolCall = this._toolGate.beforeToolCall;
|
|
1627
1331
|
this.agent.afterToolCall = this._toolGate.afterToolCall;
|
|
1332
|
+
this.agent.onRunawayStop = (info) => this._handleRunawayStop(info);
|
|
1333
|
+
this.agent.onToolValidationEscalation = (event) => this._handleToolValidationEscalation(event);
|
|
1334
|
+
}
|
|
1335
|
+
/**
|
|
1336
|
+
* The runaway-loop backstop ({@link Agent.maxStallTurns}) stopped a turn stuck repeating one
|
|
1337
|
+
* identical tool-call signature. Previously silent — this is the first host handler. Records a
|
|
1338
|
+
* session-log/telemetry entry (see {@link RUNAWAY_STOP_CUSTOM_TYPE}) and surfaces a user-visible
|
|
1339
|
+
* warning through the same event the context-window/compaction backstops use.
|
|
1340
|
+
*/
|
|
1341
|
+
_handleRunawayStop(info) {
|
|
1342
|
+
const record = {
|
|
1343
|
+
signature: info.signature,
|
|
1344
|
+
repeats: info.repeats,
|
|
1345
|
+
model: this.model?.id,
|
|
1346
|
+
provider: this.model?.provider,
|
|
1347
|
+
at: new Date().toISOString(),
|
|
1348
|
+
};
|
|
1349
|
+
this.sessionManager.appendCustomEntry(RUNAWAY_STOP_CUSTOM_TYPE, record);
|
|
1350
|
+
this._emit({
|
|
1351
|
+
type: "warning",
|
|
1352
|
+
message: `Stopped: the model repeated the same tool call ${info.repeats} times in a row without making progress. Review the last tool result and steer or retry with a different approach.`,
|
|
1353
|
+
});
|
|
1354
|
+
}
|
|
1355
|
+
/**
|
|
1356
|
+
* Evidence-gated native→phone auto-probe for a LOCAL/MANAGED model (never cloud — see
|
|
1357
|
+
* {@link isLocalOrManagedRouterModel}) that just crossed the tool-argument-validation escalation
|
|
1358
|
+
* threshold — repeated identical validation failures with no successful native call in between,
|
|
1359
|
+
* which is exactly the graded evidence {@link Agent.onToolValidationEscalation} already requires
|
|
1360
|
+
* before firing. Runs the SAME probe `/toolprobe` uses ({@link _probeToolCallingForModel}: native
|
|
1361
|
+
* trials first, so a model that can actually tool-call natively still resolves to verdict
|
|
1362
|
+
* "native" and is never phoned) entirely OFF the hot path — fired here but never awaited by the
|
|
1363
|
+
* caller, so a slow or failing probe can never block or throw the user's in-flight turn.
|
|
1364
|
+
* Anti-loop: skipped when this session already auto-probed this model, or a fresh persisted
|
|
1365
|
+
* verdict already exists (enforced by {@link ToolProtocolController}) — otherwise a model that keeps
|
|
1366
|
+
* failing validation every turn would re-fire the (multi-completion) probe every single turn.
|
|
1367
|
+
*/
|
|
1368
|
+
_maybeAutoProbeOnValidationEscalation(model) {
|
|
1369
|
+
this._toolProtocol.maybeAutoProbe(model);
|
|
1370
|
+
}
|
|
1371
|
+
/**
|
|
1372
|
+
* A repeated identical tool-argument-validation failure crossed the escalation threshold
|
|
1373
|
+
* ({@link Agent.toolValidationEscalationThreshold}) — the graded evidence the capability-gate
|
|
1374
|
+
* spine acts on. Always records a session-log/telemetry entry (see {@link
|
|
1375
|
+
* TOOL_VALIDATION_ESCALATION_CUSTOM_TYPE}), then branches on the failing model's class:
|
|
1376
|
+
* - LOCAL/MANAGED ({@link isLocalOrManagedRouterModel}, never cloud): the failure is evidence the
|
|
1377
|
+
* model may lack native tool-calling, so it fires the evidence-gated native→phone auto-probe
|
|
1378
|
+
* off the hot path ({@link _maybeAutoProbeOnValidationEscalation}). Escalating a local model's
|
|
1379
|
+
* ROUTER TIER on a tool-call failure would not fix a capability problem, so this branch never
|
|
1380
|
+
* touches the model router.
|
|
1381
|
+
* - CLOUD (known tool-capable): the failure is evidence the routed tier is too weak for this
|
|
1382
|
+
* request, so it escalates via {@link ModelRouterController.requestValidationFailureEscalation}
|
|
1383
|
+
* — de-conflated from the beforeToolCall mutation gate ({@link
|
|
1384
|
+
* ModelRouterController.maybeEscalateToolCall}/`shouldEscalateModelRouterTool`): repeated
|
|
1385
|
+
* validation failure is grounds to escalate REGARDLESS of the failing tool's mutation status,
|
|
1386
|
+
* so a read-only tool's repeated failure now escalates too (previously a no-op, since the old
|
|
1387
|
+
* code reused the mutation gate verbatim for this unrelated signal). Cloud models are never
|
|
1388
|
+
* probe-gated or phoned by this handler.
|
|
1389
|
+
* If the registry can no longer resolve `event.model`/`event.provider` (e.g. the model was
|
|
1390
|
+
* unregistered mid-session), falls back to the cloud/tier-escalation path — the previously
|
|
1391
|
+
* existing behavior — rather than silently dropping the signal.
|
|
1392
|
+
*/
|
|
1393
|
+
_handleToolValidationEscalation(event) {
|
|
1394
|
+
const record = {
|
|
1395
|
+
tool: event.tool,
|
|
1396
|
+
signature: event.signature,
|
|
1397
|
+
repeats: event.repeats,
|
|
1398
|
+
model: event.model,
|
|
1399
|
+
provider: event.provider,
|
|
1400
|
+
at: new Date().toISOString(),
|
|
1401
|
+
};
|
|
1402
|
+
this.sessionManager.appendCustomEntry(TOOL_VALIDATION_ESCALATION_CUSTOM_TYPE, record);
|
|
1403
|
+
const model = this._modelRegistry.find(event.provider, event.model);
|
|
1404
|
+
if (model && isLocalOrManagedRouterModel(model)) {
|
|
1405
|
+
this._maybeAutoProbeOnValidationEscalation(model);
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
this._modelRouter.requestValidationFailureEscalation();
|
|
1628
1409
|
}
|
|
1629
1410
|
// =========================================================================
|
|
1630
1411
|
// Event Subscription
|
|
@@ -1636,11 +1417,23 @@ export class AgentSession {
|
|
|
1636
1417
|
}
|
|
1637
1418
|
}
|
|
1638
1419
|
async _notifyWorkerTerminalHandoff(records) {
|
|
1639
|
-
if (
|
|
1420
|
+
if (records.length === 0)
|
|
1640
1421
|
return;
|
|
1422
|
+
if (this._disposed)
|
|
1423
|
+
throw new Error("Session disposed before worker terminal handoff was persisted");
|
|
1424
|
+
// sendCustomMessage only queues in process memory while the foreground is streaming. Wait for
|
|
1425
|
+
// the owning run to settle so a successful return proves the handoff reached session storage
|
|
1426
|
+
// before the durable notification outbox marks it delivered.
|
|
1427
|
+
await this.agent.waitForIdle();
|
|
1428
|
+
if (this._disposed)
|
|
1429
|
+
throw new Error("Session disposed before worker terminal handoff was persisted");
|
|
1641
1430
|
const included = records.slice(0, 8);
|
|
1642
1431
|
const sanitize = (value) => value.replace(/[\r\n]+/g, " ").slice(0, 120);
|
|
1643
1432
|
const omitted = records.length - included.length;
|
|
1433
|
+
const workerRequestsAwaitingDelivery = new Set(getWorkerHumanInputsRequiringDelivery(this.sessionManager).flatMap((snapshot) => snapshot.request.workerRequestId ? [snapshot.request.workerRequestId] : []));
|
|
1434
|
+
const ownerQuestionWillWakeParent = this._extensionUIContext !== undefined &&
|
|
1435
|
+
this._extensionMode !== "print" &&
|
|
1436
|
+
records.every((record) => workerRequestsAwaitingDelivery.has(record.laneId));
|
|
1644
1437
|
const content = [
|
|
1645
1438
|
"Background worker terminal handoff:",
|
|
1646
1439
|
...included.map((record) => {
|
|
@@ -1655,7 +1448,7 @@ export class AgentSession {
|
|
|
1655
1448
|
content,
|
|
1656
1449
|
display: true,
|
|
1657
1450
|
details: { records: included },
|
|
1658
|
-
}, { triggerTurn:
|
|
1451
|
+
}, { triggerTurn: !ownerQuestionWillWakeParent, deliverAs: "followUp" });
|
|
1659
1452
|
}
|
|
1660
1453
|
_emitQueueUpdate() {
|
|
1661
1454
|
this._emit({
|
|
@@ -1665,8 +1458,6 @@ export class AgentSession {
|
|
|
1665
1458
|
commands: [...this._queuedExtensionCommands],
|
|
1666
1459
|
});
|
|
1667
1460
|
}
|
|
1668
|
-
// Track last assistant message for auto-compaction check
|
|
1669
|
-
_lastAssistantMessage = undefined;
|
|
1670
1461
|
/**
|
|
1671
1462
|
* User messages already painted to the UI by an early, synthetic `message_start` fired from
|
|
1672
1463
|
* `_promptUnserialized` — before the model-router judge's bounded LLM call — so the prompt
|
|
@@ -1682,7 +1473,7 @@ export class AgentSession {
|
|
|
1682
1473
|
// When a user message starts, check if it's from either queue and remove it BEFORE emitting
|
|
1683
1474
|
// This ensures the UI sees the updated queue state
|
|
1684
1475
|
if (event.type === "message_start" && event.message.role === "user") {
|
|
1685
|
-
this.
|
|
1476
|
+
this._compaction.resetOverflowRecovery();
|
|
1686
1477
|
const messageText = this._getUserMessageText(event.message);
|
|
1687
1478
|
if (messageText) {
|
|
1688
1479
|
// Check steering queue first
|
|
@@ -1741,41 +1532,21 @@ export class AgentSession {
|
|
|
1741
1532
|
messagePersisted = true;
|
|
1742
1533
|
}
|
|
1743
1534
|
// Other message types (bashExecution, compactionSummary, branchSummary) are persisted elsewhere
|
|
1744
|
-
// Track
|
|
1535
|
+
// Track the response for ordered retry/failover/compaction handling after agent_end.
|
|
1745
1536
|
if (event.message.role === "assistant") {
|
|
1746
|
-
this._lastAssistantMessage = event.message;
|
|
1747
1537
|
const assistantMsg = event.message;
|
|
1748
1538
|
if (messagePersisted) {
|
|
1749
1539
|
this._pipeline.observeProviderUsage(this.agent.state.messages, assistantMsg);
|
|
1750
1540
|
}
|
|
1751
|
-
|
|
1752
|
-
this._overflowRecoveryAttempted = false;
|
|
1753
|
-
}
|
|
1754
|
-
// Reset retry counter immediately on successful assistant response
|
|
1755
|
-
// This prevents accumulation across multiple LLM calls within a turn
|
|
1756
|
-
if (assistantMsg.stopReason !== "error" && this._retryController.attempt > 0) {
|
|
1757
|
-
this._emit({
|
|
1758
|
-
type: "auto_retry_end",
|
|
1759
|
-
success: true,
|
|
1760
|
-
attempt: this._retryController.attempt,
|
|
1761
|
-
});
|
|
1762
|
-
this._retryController.reset();
|
|
1763
|
-
}
|
|
1541
|
+
this._foregroundRecovery.observeAssistant(assistantMsg);
|
|
1764
1542
|
}
|
|
1765
1543
|
}
|
|
1544
|
+
if (event.type === "agent_end" && !this._willRetryAfterAgentEnd(event)) {
|
|
1545
|
+
this._schedulePendingWorkerHumanInputs();
|
|
1546
|
+
}
|
|
1766
1547
|
};
|
|
1767
1548
|
_willRetryAfterAgentEnd(event) {
|
|
1768
|
-
|
|
1769
|
-
if (!settings.enabled || this._retryController.attempt >= settings.maxRetries) {
|
|
1770
|
-
return false;
|
|
1771
|
-
}
|
|
1772
|
-
for (let i = event.messages.length - 1; i >= 0; i--) {
|
|
1773
|
-
const message = event.messages[i];
|
|
1774
|
-
if (message.role === "assistant") {
|
|
1775
|
-
return this._isRetryableError(message);
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
return false;
|
|
1549
|
+
return this._foregroundRecovery.willRetryAfterAgentEnd(event);
|
|
1779
1550
|
}
|
|
1780
1551
|
/** Extract text content from a message */
|
|
1781
1552
|
_getUserMessageText(message) {
|
|
@@ -1963,44 +1734,50 @@ export class AgentSession {
|
|
|
1963
1734
|
* Call this when completely done with the session.
|
|
1964
1735
|
*/
|
|
1965
1736
|
dispose() {
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1737
|
+
if (this._disposed)
|
|
1738
|
+
return;
|
|
1739
|
+
this._disposed = true;
|
|
1740
|
+
const shutdowns = [];
|
|
1741
|
+
const safely = (action) => {
|
|
1742
|
+
try {
|
|
1743
|
+
action();
|
|
1744
|
+
}
|
|
1745
|
+
catch {
|
|
1746
|
+
// Every independent cleanup remains best-effort; one failure cannot skip the rest.
|
|
1747
|
+
}
|
|
1748
|
+
};
|
|
1749
|
+
const track = (action) => {
|
|
1750
|
+
try {
|
|
1751
|
+
shutdowns.push(action());
|
|
1752
|
+
}
|
|
1753
|
+
catch {
|
|
1754
|
+
// A synchronously throwing async-resource adapter cannot skip the remaining shutdowns.
|
|
1755
|
+
}
|
|
1756
|
+
};
|
|
1757
|
+
safely(() => this._backgroundLanes.clearGoalAutoContinueTimer());
|
|
1758
|
+
safely(() => this._backgroundLanes.clearResearchLaneTimer());
|
|
1759
|
+
safely(() => this.abortRetry());
|
|
1760
|
+
safely(() => this.abortCompaction());
|
|
1761
|
+
safely(() => this.abortBranchSummary());
|
|
1762
|
+
safely(() => this.abortBash());
|
|
1763
|
+
safely(() => disposePersistentShellSession(this._shellSessionKey));
|
|
1764
|
+
safely(() => this._cancelPrefixWarm());
|
|
1765
|
+
safely(() => this.agent.abort());
|
|
1766
|
+
track(() => this._gatewayRegistry.stop());
|
|
1767
|
+
safely(() => this._reflectionAbort.abort());
|
|
1768
|
+
safely(() => this._backgroundLanes.abortInFlightLanes());
|
|
1769
|
+
safely(() => {
|
|
1987
1770
|
this.agent.afterToolCall = undefined;
|
|
1988
1771
|
this.agent.transformContext = undefined;
|
|
1989
|
-
}
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
}
|
|
1993
|
-
this._extensionRunner.invalidate("This extension ctx is stale after session replacement or reload. Do not use a captured pi or command ctx after ctx.newSession(), ctx.fork(), ctx.switchSession(), or ctx.reload(). For newSession, fork, and switchSession, move post-replacement work into withSession and use the ctx passed to withSession. For reload, do not use the old ctx after await ctx.reload().");
|
|
1994
|
-
this._disconnectFromAgent();
|
|
1772
|
+
});
|
|
1773
|
+
safely(() => this._extensionRunner.invalidate("This extension ctx is stale after session replacement or reload. Do not use a captured pi or command ctx after ctx.newSession(), ctx.fork(), ctx.switchSession(), or ctx.reload(). For newSession, fork, and switchSession, move post-replacement work into withSession and use the ctx passed to withSession. For reload, do not use the old ctx after await ctx.reload()."));
|
|
1774
|
+
safely(() => this._disconnectFromAgent());
|
|
1995
1775
|
this._eventListeners = [];
|
|
1996
1776
|
// Best-effort memory cleanup (release locks/handles). Write-side onSessionEnd is wired on a
|
|
1997
|
-
// true session-end hook
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
.catch(() => { });
|
|
2002
|
-
void this._toolRecoveryLogger.shutdown().catch(() => { });
|
|
2003
|
-
cleanupSessionResources(this.sessionId);
|
|
1777
|
+
// true session-end hook; file-store shutdown is a no-op.
|
|
1778
|
+
track(() => this._memory.getMemoryManager().shutdownAll());
|
|
1779
|
+
track(() => this._toolRecoveryLogger.shutdown());
|
|
1780
|
+
safely(() => cleanupSessionResources(this.sessionId));
|
|
2004
1781
|
// Best-effort final sweep for any grep/find artifact already released (reference
|
|
2005
1782
|
// count zero) but not yet reclaimed -- e.g. a release whose cleanup() call failed
|
|
2006
1783
|
// transiently. This is conservative: it never releases a still-referenced
|
|
@@ -2008,12 +1785,13 @@ export class AgentSession {
|
|
|
2008
1785
|
// short to cross preserveRecentMessages) correctly leaves that artifact in place,
|
|
2009
1786
|
// resolvable if the same session is resumed later. It does not sweep OTHER
|
|
2010
1787
|
// sessions' artifact directories.
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
1788
|
+
safely(() => this._pipeline.cleanupToolArtifactStoreOnDispose());
|
|
1789
|
+
this._disposeCompletion = Promise.allSettled(shutdowns).then(() => undefined);
|
|
1790
|
+
}
|
|
1791
|
+
/** Dispose synchronously-visible state, then await all session-owned asynchronous shutdowns. */
|
|
1792
|
+
async disposeAndWait() {
|
|
1793
|
+
this.dispose();
|
|
1794
|
+
await this._disposeCompletion;
|
|
2017
1795
|
}
|
|
2018
1796
|
// =========================================================================
|
|
2019
1797
|
// Read-only State Access
|
|
@@ -2040,7 +1818,7 @@ export class AgentSession {
|
|
|
2040
1818
|
}
|
|
2041
1819
|
/** Current retry attempt (0 if not retrying) */
|
|
2042
1820
|
get retryAttempt() {
|
|
2043
|
-
return this.
|
|
1821
|
+
return this._foregroundRecovery.attempt;
|
|
2044
1822
|
}
|
|
2045
1823
|
/**
|
|
2046
1824
|
* Get the names of currently active tools.
|
|
@@ -2049,7 +1827,7 @@ export class AgentSession {
|
|
|
2049
1827
|
getActiveToolNames() {
|
|
2050
1828
|
return this.agent.state.tools.map((t) => t.name);
|
|
2051
1829
|
}
|
|
2052
|
-
/**
|
|
1830
|
+
/** Build a foreground {@link CapabilityEnvelope} from the live session state (active tools, cwd, cost ceiling). */
|
|
2053
1831
|
_buildForegroundEnvelopeFromState() {
|
|
2054
1832
|
return buildForegroundEnvelope({
|
|
2055
1833
|
turnIndex: this._turnIndex,
|
|
@@ -2059,7 +1837,7 @@ export class AgentSession {
|
|
|
2059
1837
|
});
|
|
2060
1838
|
}
|
|
2061
1839
|
/**
|
|
2062
|
-
*
|
|
1840
|
+
* (Re)build the foreground envelope for the current turn. Visibility only -- the foreground
|
|
2063
1841
|
* envelope is NOT enforced this round. Best-effort: never throws into the turn.
|
|
2064
1842
|
*/
|
|
2065
1843
|
_refreshForegroundEnvelope() {
|
|
@@ -2070,7 +1848,7 @@ export class AgentSession {
|
|
|
2070
1848
|
// Visibility only: a failure to build the envelope must never disturb the turn.
|
|
2071
1849
|
}
|
|
2072
1850
|
}
|
|
2073
|
-
/**
|
|
1851
|
+
/** The auto-constructed foreground envelope for the current/most-recent turn (visibility only). */
|
|
2074
1852
|
getForegroundEnvelope() {
|
|
2075
1853
|
return this._currentForegroundEnvelope;
|
|
2076
1854
|
}
|
|
@@ -2090,7 +1868,7 @@ export class AgentSession {
|
|
|
2090
1868
|
* Changes take effect on the next agent turn.
|
|
2091
1869
|
*
|
|
2092
1870
|
* artifact_retrieve is auto-activated as a companion whenever an artifact-producing tool
|
|
2093
|
-
* (grep, find, or
|
|
1871
|
+
* (grep, find, run_toolkit_script, or ask_question) ends up in the resulting active set and artifact_retrieve
|
|
2094
1872
|
* is registered (i.e. not excluded/
|
|
2095
1873
|
* blocked/outside an allowlist -- the registry itself is built with that same filter,
|
|
2096
1874
|
* so registry presence already tracks "allowed"). This is enforced here, not just in
|
|
@@ -2124,7 +1902,8 @@ export class AgentSession {
|
|
|
2124
1902
|
}
|
|
2125
1903
|
if (validToolNames.includes("grep") ||
|
|
2126
1904
|
validToolNames.includes("find") ||
|
|
2127
|
-
validToolNames.includes("run_toolkit_script")
|
|
1905
|
+
validToolNames.includes("run_toolkit_script") ||
|
|
1906
|
+
validToolNames.includes("ask_question")) {
|
|
2128
1907
|
addIfRegistered("artifact_retrieve");
|
|
2129
1908
|
}
|
|
2130
1909
|
if (validToolNames.includes("delegate")) {
|
|
@@ -2138,9 +1917,7 @@ export class AgentSession {
|
|
|
2138
1917
|
}
|
|
2139
1918
|
/** Whether compaction or branch summarization is currently running */
|
|
2140
1919
|
get isCompacting() {
|
|
2141
|
-
return (this.
|
|
2142
|
-
this._compactionAbortController !== undefined ||
|
|
2143
|
-
this._branchSummaryAbortController !== undefined);
|
|
1920
|
+
return this._compaction.isRunning() || this._branchSummaryAbortController !== undefined;
|
|
2144
1921
|
}
|
|
2145
1922
|
/** All messages including custom types like BashExecutionMessage */
|
|
2146
1923
|
get messages() {
|
|
@@ -2199,7 +1976,7 @@ export class AgentSession {
|
|
|
2199
1976
|
}
|
|
2200
1977
|
/**
|
|
2201
1978
|
* Build a system prompt for a specific tool surface WITHOUT touching the session's base prompt
|
|
2202
|
-
* state (
|
|
1979
|
+
* state (used by the router's model swap; see {@link SystemPromptBuilder.buildSystemPromptForToolNames}).
|
|
2203
1980
|
*/
|
|
2204
1981
|
_buildSystemPromptForToolNames(toolNames) {
|
|
2205
1982
|
return this._systemPromptBuilder.buildSystemPromptForToolNames(toolNames);
|
|
@@ -2211,7 +1988,7 @@ export class AgentSession {
|
|
|
2211
1988
|
try {
|
|
2212
1989
|
const maxGoalLoopRounds = this.settingsManager.getAutonomySettings().maxStallTurns;
|
|
2213
1990
|
this.agent.maxStallTurns = maxGoalLoopRounds;
|
|
2214
|
-
await this.
|
|
1991
|
+
await this._toolProtocol.ensureActiveModelProtocol();
|
|
2215
1992
|
let goalLoopRounds = 1;
|
|
2216
1993
|
await this.agent.prompt(messages);
|
|
2217
1994
|
while ((maxGoalLoopRounds === 0 || goalLoopRounds < maxGoalLoopRounds) && (await this._handlePostAgentRun())) {
|
|
@@ -2224,6 +2001,187 @@ export class AgentSession {
|
|
|
2224
2001
|
await this._drainQueuedExtensionCommands();
|
|
2225
2002
|
}
|
|
2226
2003
|
}
|
|
2004
|
+
_queueWorkerHumanInput(request) {
|
|
2005
|
+
const existing = getLatestHumanInputSnapshots(this.sessionManager).find((snapshot) => snapshot.request.workerRequestId === request.workerRequestId);
|
|
2006
|
+
if (existing)
|
|
2007
|
+
return;
|
|
2008
|
+
const blockerText = request.blockers
|
|
2009
|
+
.slice(0, 4)
|
|
2010
|
+
.map((blocker) => blocker.slice(0, 500))
|
|
2011
|
+
.join("; ");
|
|
2012
|
+
const question = createHumanInputRequest({
|
|
2013
|
+
source: "worker",
|
|
2014
|
+
workerRequestId: request.workerRequestId,
|
|
2015
|
+
questions: [
|
|
2016
|
+
{
|
|
2017
|
+
id: "worker_review",
|
|
2018
|
+
header: "Worker review",
|
|
2019
|
+
question: `${request.message.slice(0, 1_500)}${blockerText ? ` Blockers: ${blockerText}` : ""}`,
|
|
2020
|
+
options: [
|
|
2021
|
+
{
|
|
2022
|
+
label: "Review now",
|
|
2023
|
+
description: "Have the parent inspect the worker claim and evidence before reconciling it.",
|
|
2024
|
+
},
|
|
2025
|
+
{
|
|
2026
|
+
label: "Keep blocked",
|
|
2027
|
+
description: "Leave the worker result blocked and continue without accepting its output.",
|
|
2028
|
+
},
|
|
2029
|
+
],
|
|
2030
|
+
},
|
|
2031
|
+
],
|
|
2032
|
+
acceptsImages: false,
|
|
2033
|
+
});
|
|
2034
|
+
beginHumanInputRequest(this.sessionManager, question);
|
|
2035
|
+
if (!this.isStreaming)
|
|
2036
|
+
this._scheduleWorkerHumanInput(question);
|
|
2037
|
+
}
|
|
2038
|
+
_schedulePendingWorkerHumanInputs() {
|
|
2039
|
+
for (const snapshot of getWorkerHumanInputsRequiringDelivery(this.sessionManager)) {
|
|
2040
|
+
this._scheduleWorkerHumanInput(snapshot);
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
_scheduleWorkerHumanInput(input) {
|
|
2044
|
+
const snapshot = "status" in input ? input : { request: input, status: "pending", answers: [], updatedAt: input.createdAt };
|
|
2045
|
+
const requestId = snapshot.request.requestId;
|
|
2046
|
+
if (this._activeHumanInputRequestIds.has(requestId))
|
|
2047
|
+
return;
|
|
2048
|
+
this._activeHumanInputRequestIds.add(requestId);
|
|
2049
|
+
this._humanInputTail = this._humanInputTail
|
|
2050
|
+
.then(async () => {
|
|
2051
|
+
await this._resolveWorkerHumanInput(snapshot);
|
|
2052
|
+
})
|
|
2053
|
+
.catch((error) => {
|
|
2054
|
+
this._emit({
|
|
2055
|
+
type: "warning",
|
|
2056
|
+
message: `Worker owner-input request failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
2057
|
+
});
|
|
2058
|
+
})
|
|
2059
|
+
.finally(() => {
|
|
2060
|
+
this._activeHumanInputRequestIds.delete(requestId);
|
|
2061
|
+
});
|
|
2062
|
+
}
|
|
2063
|
+
async _resolveWorkerHumanInput(initial) {
|
|
2064
|
+
const ui = this._extensionUIContext;
|
|
2065
|
+
if (!ui || this._extensionMode === "print")
|
|
2066
|
+
return false;
|
|
2067
|
+
// This path may be scheduled by agent_end. Waiting on the owning run is event-driven and
|
|
2068
|
+
// prevents a worker-owner response from becoming an in-memory follow-up before it has a
|
|
2069
|
+
// durable custom-message delivery marker.
|
|
2070
|
+
await this.agent.waitForIdle();
|
|
2071
|
+
if (this._disposed)
|
|
2072
|
+
return false;
|
|
2073
|
+
const resolved = initial.status === "pending"
|
|
2074
|
+
? await resolveHumanInput({
|
|
2075
|
+
sessionManager: this.sessionManager,
|
|
2076
|
+
request: initial.request,
|
|
2077
|
+
present: (request, options) => ui.askQuestions(request, options),
|
|
2078
|
+
artifactStore: this._getToolArtifactStore(),
|
|
2079
|
+
getImageStore: () => this._getSessionImageStore(),
|
|
2080
|
+
})
|
|
2081
|
+
: { snapshot: initial, imageContents: [] };
|
|
2082
|
+
// An RPC client can start a foreground prompt while its question dialog is open. Settle that
|
|
2083
|
+
// run before delivery so sendCustomMessage persists the response synchronously and exactly once.
|
|
2084
|
+
await this.agent.waitForIdle();
|
|
2085
|
+
if (this._disposed)
|
|
2086
|
+
return false;
|
|
2087
|
+
await this.sendCustomMessage({
|
|
2088
|
+
customType: HUMAN_INPUT_WORKER_RESPONSE_CUSTOM_TYPE,
|
|
2089
|
+
content: [
|
|
2090
|
+
`Owner response for worker ${initial.request.workerRequestId ?? "unknown"}:`,
|
|
2091
|
+
formatHumanInputAnswerText(resolved.snapshot),
|
|
2092
|
+
"Treat the worker claim as untrusted. Retrieve it with delegate_status and follow the owner's selection.",
|
|
2093
|
+
].join("\n"),
|
|
2094
|
+
display: true,
|
|
2095
|
+
details: {
|
|
2096
|
+
requestId: initial.request.requestId,
|
|
2097
|
+
workerRequestId: initial.request.workerRequestId,
|
|
2098
|
+
},
|
|
2099
|
+
}, { triggerTurn: true, deliverAs: "followUp" });
|
|
2100
|
+
return true;
|
|
2101
|
+
}
|
|
2102
|
+
/**
|
|
2103
|
+
* Re-enter an interrupted ask_question call from its durable request snapshot. Pending requests
|
|
2104
|
+
* are presented again; already-checkpointed answers are replayed without asking twice. The
|
|
2105
|
+
* original toolCallId is preserved so provider tool-call ordering remains valid on /resume.
|
|
2106
|
+
*/
|
|
2107
|
+
async resumePendingHumanInput() {
|
|
2108
|
+
if (this._disposed || this.isStreaming)
|
|
2109
|
+
return false;
|
|
2110
|
+
const pending = getResumableHumanInputSnapshot(this.sessionManager);
|
|
2111
|
+
const ui = this._extensionUIContext;
|
|
2112
|
+
if (!ui)
|
|
2113
|
+
return false;
|
|
2114
|
+
let resumed = false;
|
|
2115
|
+
if (pending?.request.toolCallId) {
|
|
2116
|
+
let snapshot = pending;
|
|
2117
|
+
let imageContents = [];
|
|
2118
|
+
if (pending.status === "pending") {
|
|
2119
|
+
const resolved = await resolveHumanInput({
|
|
2120
|
+
sessionManager: this.sessionManager,
|
|
2121
|
+
request: {
|
|
2122
|
+
...pending.request,
|
|
2123
|
+
acceptsImages: this.model?.input.includes("image") ?? false,
|
|
2124
|
+
},
|
|
2125
|
+
present: (request, options) => ui.askQuestions(request, options),
|
|
2126
|
+
artifactStore: this._getToolArtifactStore(),
|
|
2127
|
+
getImageStore: () => this._getSessionImageStore(),
|
|
2128
|
+
});
|
|
2129
|
+
snapshot = resolved.snapshot;
|
|
2130
|
+
imageContents = resolved.imageContents;
|
|
2131
|
+
}
|
|
2132
|
+
else if (pending.status === "answered") {
|
|
2133
|
+
const imageStore = this._getSessionImageStore();
|
|
2134
|
+
if (imageStore) {
|
|
2135
|
+
const referencedText = snapshot.answers
|
|
2136
|
+
.flatMap((answer) => [answer.custom ?? "", ...(answer.images?.map((image) => image.label) ?? [])])
|
|
2137
|
+
.join(" ");
|
|
2138
|
+
imageContents = imageStore.resolveReferences(referencedText);
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
const answerImageCount = snapshot.answers.reduce((total, answer) => total + (answer.images?.length ?? 0), 0);
|
|
2142
|
+
const modelAcceptsImages = this.model?.input.includes("image") ?? false;
|
|
2143
|
+
const missingImageNotice = answerImageCount > imageContents.length
|
|
2144
|
+
? `\n\n[${answerImageCount - imageContents.length} attached image(s) could not be restored from durable storage.]`
|
|
2145
|
+
: "";
|
|
2146
|
+
const unsupportedImageNotice = imageContents.length > 0 && !modelAcceptsImages
|
|
2147
|
+
? "\n\n[Attached images were retained but not sent because the selected model does not accept image input.]"
|
|
2148
|
+
: "";
|
|
2149
|
+
const toolResult = {
|
|
2150
|
+
role: "toolResult",
|
|
2151
|
+
toolCallId: pending.request.toolCallId,
|
|
2152
|
+
toolName: pending.request.toolName ?? "ask_question",
|
|
2153
|
+
content: [
|
|
2154
|
+
{
|
|
2155
|
+
type: "text",
|
|
2156
|
+
text: `${formatHumanInputAnswerText(snapshot)}${missingImageNotice}${unsupportedImageNotice}`,
|
|
2157
|
+
},
|
|
2158
|
+
...(modelAcceptsImages ? imageContents : []),
|
|
2159
|
+
],
|
|
2160
|
+
details: {
|
|
2161
|
+
questions: snapshot.request.questions,
|
|
2162
|
+
answers: snapshot.answers,
|
|
2163
|
+
cancelled: snapshot.status === "cancelled",
|
|
2164
|
+
...(snapshot.reason ? { reason: snapshot.reason } : {}),
|
|
2165
|
+
},
|
|
2166
|
+
isError: false,
|
|
2167
|
+
timestamp: Date.now(),
|
|
2168
|
+
};
|
|
2169
|
+
await this._runAgentPrompt(toolResult);
|
|
2170
|
+
resumed = true;
|
|
2171
|
+
}
|
|
2172
|
+
for (const workerInput of getWorkerHumanInputsRequiringDelivery(this.sessionManager)) {
|
|
2173
|
+
if (this._activeHumanInputRequestIds.has(workerInput.request.requestId))
|
|
2174
|
+
continue;
|
|
2175
|
+
this._activeHumanInputRequestIds.add(workerInput.request.requestId);
|
|
2176
|
+
try {
|
|
2177
|
+
resumed = (await this._resolveWorkerHumanInput(workerInput)) || resumed;
|
|
2178
|
+
}
|
|
2179
|
+
finally {
|
|
2180
|
+
this._activeHumanInputRequestIds.delete(workerInput.request.requestId);
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
return resumed;
|
|
2184
|
+
}
|
|
2227
2185
|
/**
|
|
2228
2186
|
* Shared {@link OllamaRuntime} for a given server, lazily created and cached by baseUrl so every
|
|
2229
2187
|
* caller — the router's readiness gate and any host UI's own model-lifecycle commands (e.g.
|
|
@@ -2236,6 +2194,12 @@ export class AgentSession {
|
|
|
2236
2194
|
getTransformersRuntime(modelId, baseUrl) {
|
|
2237
2195
|
return this._localRuntimeController.getTransformersRuntime(modelId, baseUrl);
|
|
2238
2196
|
}
|
|
2197
|
+
/** Shared {@link PrismLlamaCppRuntime} for pi's own managed prism install — see
|
|
2198
|
+
* {@link LocalRuntimeController.getPrismLlamaCppRuntime}. Delegates so `/models` and the
|
|
2199
|
+
* readiness gate share the SAME cached instance, same contract as getLocalRuntime above. */
|
|
2200
|
+
getPrismLlamaCppRuntime() {
|
|
2201
|
+
return this._localRuntimeController.getPrismLlamaCppRuntime();
|
|
2202
|
+
}
|
|
2239
2203
|
/** models.json registers a local model's baseUrl as `<server>/v1` (OpenAI-compat); the runtime's
|
|
2240
2204
|
* own health/boot endpoints are on the Ollama-native server root. Delegates to
|
|
2241
2205
|
* {@link LocalRuntimeController}; kept here for `_warnIfManualModelChoiceIsRisky`'s own use. */
|
|
@@ -2250,44 +2214,32 @@ export class AgentSession {
|
|
|
2250
2214
|
async _ensureRouteModelReady(resolved) {
|
|
2251
2215
|
return this._localRuntimeController.ensureRouteModelReady(resolved);
|
|
2252
2216
|
}
|
|
2217
|
+
/**
|
|
2218
|
+
* Every local model the CURRENT (post-reload) configuration could still route a turn to —
|
|
2219
|
+
* the foreground model plus any router tier (cheap/medium/expensive) that still resolves to a
|
|
2220
|
+
* real, authed, non-exhausted model. Fed to {@link LocalRuntimeController.reconcile} via the
|
|
2221
|
+
* `reconcileLocalRuntimes` hook above, ONLY after a reload generation has fully committed, so a
|
|
2222
|
+
* local model dropped from the live configuration has its pi-spawned runtime stopped instead of
|
|
2223
|
+
* leaking a child process, while one still referenced here is left untouched. Read-only — never
|
|
2224
|
+
* used for routing itself.
|
|
2225
|
+
*/
|
|
2226
|
+
_collectEligibleLocalModelsForReconcile() {
|
|
2227
|
+
const models = [];
|
|
2228
|
+
const foregroundModel = this.agent.state.model;
|
|
2229
|
+
if (foregroundModel)
|
|
2230
|
+
models.push(foregroundModel);
|
|
2231
|
+
for (const tier of ["cheap", "medium", "expensive"]) {
|
|
2232
|
+
const resolved = this._modelRouter.resolveConfiguredTierModel(tier);
|
|
2233
|
+
if (resolved)
|
|
2234
|
+
models.push(resolved);
|
|
2235
|
+
}
|
|
2236
|
+
return models;
|
|
2237
|
+
}
|
|
2253
2238
|
getModelRouterStatus(formatLabel) {
|
|
2254
2239
|
return this._modelRouter.getStatus(formatLabel);
|
|
2255
2240
|
}
|
|
2256
2241
|
async _handlePostAgentRun() {
|
|
2257
|
-
|
|
2258
|
-
this._lastAssistantMessage = undefined;
|
|
2259
|
-
if (!msg) {
|
|
2260
|
-
return false;
|
|
2261
|
-
}
|
|
2262
|
-
const classified = this._classifyAssistantError(msg);
|
|
2263
|
-
if (classified) {
|
|
2264
|
-
this._failureCorpus.record({
|
|
2265
|
-
provider: msg.provider,
|
|
2266
|
-
modelId: msg.model,
|
|
2267
|
-
message: msg.errorMessage ?? "",
|
|
2268
|
-
classified,
|
|
2269
|
-
});
|
|
2270
|
-
}
|
|
2271
|
-
if (classified?.retryable && (await this._prepareRetry(msg))) {
|
|
2272
|
-
return true;
|
|
2273
|
-
}
|
|
2274
|
-
if (await this._billingFailover.handleAssistantError(msg, classified))
|
|
2275
|
-
return false;
|
|
2276
|
-
if (msg.stopReason === "error" && this._retryController.attempt > 0) {
|
|
2277
|
-
this._emit({
|
|
2278
|
-
type: "auto_retry_end",
|
|
2279
|
-
success: false,
|
|
2280
|
-
attempt: this._retryController.attempt,
|
|
2281
|
-
finalError: msg.errorMessage,
|
|
2282
|
-
});
|
|
2283
|
-
this._retryController.reset();
|
|
2284
|
-
}
|
|
2285
|
-
if (await this._checkCompaction(msg)) {
|
|
2286
|
-
return true;
|
|
2287
|
-
}
|
|
2288
|
-
// The agent loop drains both queues before emitting agent_end. Any messages
|
|
2289
|
-
// here were queued by agent_end extension handlers and need a continuation.
|
|
2290
|
-
return this.agent.hasQueuedMessages();
|
|
2242
|
+
return this._foregroundRecovery.handlePostAgentRun();
|
|
2291
2243
|
}
|
|
2292
2244
|
/**
|
|
2293
2245
|
* Send a prompt to the agent.
|
|
@@ -2310,7 +2262,7 @@ export class AgentSession {
|
|
|
2310
2262
|
return this._promptUnserialized(text, options);
|
|
2311
2263
|
}
|
|
2312
2264
|
async _promptUnserialized(text, options) {
|
|
2313
|
-
this.
|
|
2265
|
+
this._toolProtocol.applyRepairLayerSettings();
|
|
2314
2266
|
this._cancelPrefixWarm();
|
|
2315
2267
|
const expandPromptTemplates = options?.expandPromptTemplates ?? true;
|
|
2316
2268
|
const processSlashCommands = options?.processSlashCommands ?? expandPromptTemplates;
|
|
@@ -2322,7 +2274,9 @@ export class AgentSession {
|
|
|
2322
2274
|
// selected/authenticated — can un-register it from _earlyDisplayedUserMessages instead of
|
|
2323
2275
|
// leaking the reference forever.
|
|
2324
2276
|
let userMessage;
|
|
2325
|
-
|
|
2277
|
+
let routingStarted = false;
|
|
2278
|
+
let pendingNextTurnCount = 0;
|
|
2279
|
+
// Effectiveness feedback: remember the recall page + the query so we can score, after the
|
|
2326
2280
|
// response, whether the agent actually used the recalled context.
|
|
2327
2281
|
let injectedRecall = "";
|
|
2328
2282
|
let recallQuery = "";
|
|
@@ -2382,6 +2336,9 @@ export class AgentSession {
|
|
|
2382
2336
|
preflightResult?.(true);
|
|
2383
2337
|
return;
|
|
2384
2338
|
}
|
|
2339
|
+
// This submission is starting a new foreground turn. Queued steer/follow-up messages above
|
|
2340
|
+
// remain part of the already-active turn and must not erase its spawned-cost baseline.
|
|
2341
|
+
this._costGuardTurnBaselineUsd = this.getSpawnedUsage().cost;
|
|
2385
2342
|
// Flush any pending bash messages before the new prompt
|
|
2386
2343
|
this._flushPendingBashMessages();
|
|
2387
2344
|
// Build the user message now — before the router judge — and paint it to the UI
|
|
@@ -2405,6 +2362,7 @@ export class AgentSession {
|
|
|
2405
2362
|
// prompt with nothing happening for however long the judge takes. routing_end is emitted
|
|
2406
2363
|
// exactly once below: either in the catch block (this phase failed) or right after the try
|
|
2407
2364
|
// block (this phase succeeded, whether or not it produced a turn to run).
|
|
2365
|
+
routingStarted = true;
|
|
2408
2366
|
this._emit({ type: "routing_start" });
|
|
2409
2367
|
const resolvedRouteInfo = await this._modelRouter.resolveTurnRouteJudged(expandedText, {
|
|
2410
2368
|
// Internally generated turns (goal continuation, lane follow-ups) never consult the judge:
|
|
@@ -2421,6 +2379,9 @@ export class AgentSession {
|
|
|
2421
2379
|
if (!requestModel) {
|
|
2422
2380
|
throw new Error(formatNoModelSelectedMessage());
|
|
2423
2381
|
}
|
|
2382
|
+
if (currentImages && currentImages.length > 0 && !requestModel.input.includes("image")) {
|
|
2383
|
+
throw new Error(`Model "${requestModel.provider}/${requestModel.id}" does not accept image input. Select an image-capable model or route the inspection to a vision worker.`);
|
|
2384
|
+
}
|
|
2424
2385
|
// A manual/default local model has no RouteDecision, so the router readiness gate above is
|
|
2425
2386
|
// intentionally a no-op. It still needs the same managed-runtime boot/residency guarantee.
|
|
2426
2387
|
if (!resolvedRouteInfo) {
|
|
@@ -2445,7 +2406,7 @@ export class AgentSession {
|
|
|
2445
2406
|
}
|
|
2446
2407
|
// Build messages array (recall page, then custom message if any, then user message)
|
|
2447
2408
|
messages = [];
|
|
2448
|
-
//
|
|
2409
|
+
// Cross-session similarity recall. For a substantive turn, ask the memory providers to
|
|
2449
2410
|
// prefetch a relevant <memory_context> page from past sessions and prepend it as data ahead of
|
|
2450
2411
|
// the user message. Best-effort and gated: trivial turns are skipped, and providers return ""
|
|
2451
2412
|
// (no page) when nothing is relevant — so it stays net-negative and the GC packs stale pages.
|
|
@@ -2457,8 +2418,8 @@ export class AgentSession {
|
|
|
2457
2418
|
recallQuery = expandedText;
|
|
2458
2419
|
// Inject as a GC-managed custom context message (role "custom", customType
|
|
2459
2420
|
// "memory_context"), NOT a persisted user message: the semantic-memory context-GC packs
|
|
2460
|
-
// stale recall pages so they don't accumulate forever
|
|
2461
|
-
// only re-reads user/assistant text so recalled snippets can't recirculate
|
|
2421
|
+
// stale recall pages so they don't accumulate forever, and the transcript index
|
|
2422
|
+
// only re-reads user/assistant text so recalled snippets can't recirculate.
|
|
2462
2423
|
messages.push(createCustomMessage("memory_context", recall, false, undefined, new Date().toISOString()));
|
|
2463
2424
|
}
|
|
2464
2425
|
}
|
|
@@ -2470,10 +2431,10 @@ export class AgentSession {
|
|
|
2470
2431
|
// immediately — see the early message_start emit above).
|
|
2471
2432
|
messages.push(userMessage);
|
|
2472
2433
|
// Inject any pending "nextTurn" messages as context alongside the user message
|
|
2473
|
-
|
|
2434
|
+
pendingNextTurnCount = this._pendingNextTurnMessages.length;
|
|
2435
|
+
for (const msg of this._pendingNextTurnMessages.slice(0, pendingNextTurnCount)) {
|
|
2474
2436
|
messages.push(msg);
|
|
2475
2437
|
}
|
|
2476
|
-
this._pendingNextTurnMessages = [];
|
|
2477
2438
|
const taskStepsState = this.getTaskStepsStateSnapshot();
|
|
2478
2439
|
const taskStepsContext = taskStepsState ? formatTaskStepsContext(taskStepsState) : undefined;
|
|
2479
2440
|
if (taskStepsState && taskStepsContext) {
|
|
@@ -2502,6 +2463,9 @@ export class AgentSession {
|
|
|
2502
2463
|
// Ensure we're using the base prompt (in case previous turn had modifications)
|
|
2503
2464
|
this.agent.state.systemPrompt = this._baseSystemPrompt;
|
|
2504
2465
|
}
|
|
2466
|
+
// Commit consumption only after all fallible preflight hooks have succeeded. Messages added
|
|
2467
|
+
// while the hook was running remain queued for the following turn.
|
|
2468
|
+
this._pendingNextTurnMessages.splice(0, pendingNextTurnCount);
|
|
2505
2469
|
}
|
|
2506
2470
|
catch (error) {
|
|
2507
2471
|
// The turn never reached _runAgentPrompt, so the authoritative message_start that would
|
|
@@ -2512,23 +2476,32 @@ export class AgentSession {
|
|
|
2512
2476
|
}
|
|
2513
2477
|
// The routing/prep phase (routing_start above) failed before ever reaching the turn — end
|
|
2514
2478
|
// it here, or the UI's "working" indicator for it spins forever with nothing behind it.
|
|
2515
|
-
|
|
2479
|
+
if (routingStarted)
|
|
2480
|
+
this._emit({ type: "routing_end" });
|
|
2516
2481
|
preflightResult?.(false);
|
|
2517
2482
|
throw error;
|
|
2518
2483
|
}
|
|
2519
2484
|
// The routing/prep phase is over — either we're about to hand off into the turn (which emits
|
|
2520
2485
|
// its own agent_start/streaming events right after), or messages is unexpectedly unset and we
|
|
2521
2486
|
// bail below. Either way nothing is left "routing" past this point.
|
|
2522
|
-
|
|
2487
|
+
if (routingStarted)
|
|
2488
|
+
this._emit({ type: "routing_end" });
|
|
2523
2489
|
if (!messages) {
|
|
2524
2490
|
return;
|
|
2525
2491
|
}
|
|
2526
2492
|
preflightResult?.(true);
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2493
|
+
try {
|
|
2494
|
+
this._toolProtocol.resetTurnState();
|
|
2495
|
+
await this._modelRouter.runRoutedTurn(messages, routedTurnModel, routedTurnRouteDecision);
|
|
2496
|
+
this._toolProtocol.recordParseOutcomeFromLastAssistant();
|
|
2497
|
+
}
|
|
2498
|
+
finally {
|
|
2499
|
+
// Normally consumed by the authoritative message_start. If execution failed before that
|
|
2500
|
+
// event, do not retain the early-painted message identity indefinitely.
|
|
2501
|
+
if (userMessage)
|
|
2502
|
+
this._earlyDisplayedUserMessages.delete(userMessage);
|
|
2503
|
+
}
|
|
2504
|
+
// Score whether the agent actually used the recalled context, so the recall gate can adapt.
|
|
2532
2505
|
if (injectedRecall) {
|
|
2533
2506
|
const response = this._findLastAssistantMessage();
|
|
2534
2507
|
const responseText = response
|
|
@@ -2895,162 +2868,13 @@ export class AgentSession {
|
|
|
2895
2868
|
* @param customInstructions Optional instructions for the compaction summary
|
|
2896
2869
|
*/
|
|
2897
2870
|
async compact(customInstructions) {
|
|
2898
|
-
this.
|
|
2899
|
-
await this.abort();
|
|
2900
|
-
this._compactionAbortController = new AbortController();
|
|
2901
|
-
this._emit({ type: "compaction_start", reason: "manual" });
|
|
2902
|
-
try {
|
|
2903
|
-
if (!this.model) {
|
|
2904
|
-
throw new Error(formatNoModelSelectedMessage());
|
|
2905
|
-
}
|
|
2906
|
-
const sessionModel = this.model;
|
|
2907
|
-
const selectedCompactionModel = this._resolveCompactionModel(sessionModel);
|
|
2908
|
-
if (this._isRawStreamSimple(this.agent.streamFn)) {
|
|
2909
|
-
await this._getCompactionRequestAuth(selectedCompactionModel);
|
|
2910
|
-
}
|
|
2911
|
-
const selectionReason = this._getLastCompactionSelectionReason() ?? "unknown";
|
|
2912
|
-
const settings = this._getAdaptedCompactionSettings();
|
|
2913
|
-
const initialBranch = this.sessionManager.getBranch();
|
|
2914
|
-
const initialPreparation = prepareCompaction(initialBranch, settings);
|
|
2915
|
-
if (!initialPreparation) {
|
|
2916
|
-
const lastEntry = initialBranch[initialBranch.length - 1];
|
|
2917
|
-
if (lastEntry?.type === "compaction") {
|
|
2918
|
-
throw new Error("Already compacted");
|
|
2919
|
-
}
|
|
2920
|
-
throw new Error("Nothing to compact (session too small)");
|
|
2921
|
-
}
|
|
2922
|
-
let extensionCompaction;
|
|
2923
|
-
let fromExtension = false;
|
|
2924
|
-
if (this._extensionRunner.hasHandlers("session_before_compact")) {
|
|
2925
|
-
const result = (await this._extensionRunner.emit({
|
|
2926
|
-
type: "session_before_compact",
|
|
2927
|
-
preparation: initialPreparation,
|
|
2928
|
-
branchEntries: initialBranch,
|
|
2929
|
-
customInstructions,
|
|
2930
|
-
signal: this._compactionAbortController.signal,
|
|
2931
|
-
}));
|
|
2932
|
-
if (result?.cancel)
|
|
2933
|
-
throw new Error("Compaction cancelled");
|
|
2934
|
-
if (result?.compaction) {
|
|
2935
|
-
extensionCompaction = result.compaction;
|
|
2936
|
-
fromExtension = true;
|
|
2937
|
-
}
|
|
2938
|
-
}
|
|
2939
|
-
if (extensionCompaction) {
|
|
2940
|
-
this.sessionManager.appendCompaction(extensionCompaction.summary, extensionCompaction.firstKeptEntryId, extensionCompaction.tokensBefore, extensionCompaction.details, true);
|
|
2941
|
-
this._refreshAfterCompaction();
|
|
2942
|
-
const savedCompactionEntry = this.sessionManager
|
|
2943
|
-
.getEntries()
|
|
2944
|
-
.find((entry) => entry.type === "compaction" && entry.summary === extensionCompaction.summary);
|
|
2945
|
-
if (this._extensionRunner && savedCompactionEntry) {
|
|
2946
|
-
await this._extensionRunner.emit({
|
|
2947
|
-
type: "session_compact",
|
|
2948
|
-
compactionEntry: savedCompactionEntry,
|
|
2949
|
-
fromExtension: true,
|
|
2950
|
-
});
|
|
2951
|
-
}
|
|
2952
|
-
this._emit({
|
|
2953
|
-
type: "compaction_end",
|
|
2954
|
-
reason: "manual",
|
|
2955
|
-
result: extensionCompaction,
|
|
2956
|
-
aborted: false,
|
|
2957
|
-
willRetry: false,
|
|
2958
|
-
});
|
|
2959
|
-
return extensionCompaction;
|
|
2960
|
-
}
|
|
2961
|
-
let appliedResult;
|
|
2962
|
-
const signal = this._compactionAbortController.signal;
|
|
2963
|
-
const outcome = await runCompactionLoop({
|
|
2964
|
-
measureLiveTokens: () => Math.max(this._estimateCurrentContextTokens(this.agent.state.messages), 1),
|
|
2965
|
-
shouldCompact: () => true,
|
|
2966
|
-
getPostApplyMargin: () => 0,
|
|
2967
|
-
getBranch: () => this.sessionManager.getBranch(),
|
|
2968
|
-
getBaseKeepRecentTokens: () => settings.keepRecentTokens,
|
|
2969
|
-
resolveModelAndAuth: async (modelTier) => {
|
|
2970
|
-
const model = modelTier === "cheap" ? selectedCompactionModel : sessionModel;
|
|
2971
|
-
const { apiKey, headers } = await this._resolveCompactionModelAndAuth(model, sessionModel);
|
|
2972
|
-
return { model, apiKey, headers };
|
|
2973
|
-
},
|
|
2974
|
-
summarizeAndVerify: async (params, model, apiKey, headers, branch) => {
|
|
2975
|
-
const preparation = prepareCompaction(branch, {
|
|
2976
|
-
...settings,
|
|
2977
|
-
keepRecentTokens: params.keepRecentTokens,
|
|
2978
|
-
}, { allowTrailingCompactionAsPrevious: true });
|
|
2979
|
-
if (!preparation)
|
|
2980
|
-
throw new Error("Nothing to compact (session too small)");
|
|
2981
|
-
if (extensionCompaction)
|
|
2982
|
-
return { result: extensionCompaction };
|
|
2983
|
-
const compactionThinkingLevel = this._resolveCompactionThinkingLevel(model, sessionModel);
|
|
2984
|
-
const result = await this._compactWithRetry(() => compact(preparation, model, apiKey, headers, customInstructions, signal, compactionThinkingLevel, this.agent.streamFn, this._buildCompactionPreDigest(), { chunked: params.chunked }), signal, model.provider);
|
|
2985
|
-
return { result };
|
|
2986
|
-
},
|
|
2987
|
-
buildDeterministicCheckpoint: () => ({ result: createDeterministicCompaction(initialPreparation) }),
|
|
2988
|
-
apply: async (result) => {
|
|
2989
|
-
if (signal.aborted)
|
|
2990
|
-
throw new Error("Compaction cancelled");
|
|
2991
|
-
this.sessionManager.appendCompaction(result.summary, result.firstKeptEntryId, result.tokensBefore, result.details, fromExtension);
|
|
2992
|
-
this._refreshAfterCompaction();
|
|
2993
|
-
const savedCompactionEntry = this.sessionManager
|
|
2994
|
-
.getEntries()
|
|
2995
|
-
.find((entry) => entry.type === "compaction" && entry.summary === result.summary);
|
|
2996
|
-
if (this._extensionRunner && savedCompactionEntry) {
|
|
2997
|
-
await this._extensionRunner.emit({
|
|
2998
|
-
type: "session_compact",
|
|
2999
|
-
compactionEntry: savedCompactionEntry,
|
|
3000
|
-
fromExtension,
|
|
3001
|
-
});
|
|
3002
|
-
}
|
|
3003
|
-
appliedResult = result;
|
|
3004
|
-
},
|
|
3005
|
-
verifyPostApplyEffect: () => false,
|
|
3006
|
-
onTransition: ({ cycle, cause, detail }) => {
|
|
3007
|
-
this._emit({
|
|
3008
|
-
type: "warning",
|
|
3009
|
-
message: `manual compaction cycle ${cycle}: ${cause}${detail ? ` (${detail})` : ""} — retrying from step 0 (${this._describeCompactionSummarizer()})`,
|
|
3010
|
-
});
|
|
3011
|
-
},
|
|
3012
|
-
signal,
|
|
3013
|
-
});
|
|
3014
|
-
if (outcome.kind === "failed") {
|
|
3015
|
-
if (outcome.reason === "aborted")
|
|
3016
|
-
throw new Error("Compaction cancelled");
|
|
3017
|
-
throw new Error(`manual compaction failed after retry ladder using ${selectedCompactionModel.provider}/${selectedCompactionModel.id} (${selectionReason}); first failure: ${outcome.reason}`);
|
|
3018
|
-
}
|
|
3019
|
-
if (outcome.kind === "skip" || !appliedResult)
|
|
3020
|
-
throw new Error(outcome.kind === "skip" ? outcome.reason : "Compaction failed");
|
|
3021
|
-
this._emit({
|
|
3022
|
-
type: "compaction_end",
|
|
3023
|
-
reason: "manual",
|
|
3024
|
-
result: appliedResult,
|
|
3025
|
-
aborted: false,
|
|
3026
|
-
willRetry: false,
|
|
3027
|
-
});
|
|
3028
|
-
return appliedResult;
|
|
3029
|
-
}
|
|
3030
|
-
catch (error) {
|
|
3031
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
3032
|
-
const aborted = message === "Compaction cancelled" || (error instanceof Error && error.name === "AbortError");
|
|
3033
|
-
this._emit({
|
|
3034
|
-
type: "compaction_end",
|
|
3035
|
-
reason: "manual",
|
|
3036
|
-
result: undefined,
|
|
3037
|
-
aborted,
|
|
3038
|
-
willRetry: false,
|
|
3039
|
-
errorMessage: aborted ? undefined : `Compaction failed: ${message}`,
|
|
3040
|
-
});
|
|
3041
|
-
throw error;
|
|
3042
|
-
}
|
|
3043
|
-
finally {
|
|
3044
|
-
this._compactionAbortController = undefined;
|
|
3045
|
-
this._reconnectToAgent();
|
|
3046
|
-
}
|
|
2871
|
+
return this._compaction.compact(customInstructions);
|
|
3047
2872
|
}
|
|
3048
2873
|
/**
|
|
3049
2874
|
* Cancel in-progress compaction (manual or auto).
|
|
3050
2875
|
*/
|
|
3051
2876
|
abortCompaction() {
|
|
3052
|
-
this.
|
|
3053
|
-
this._autoCompactionAbortController?.abort();
|
|
2877
|
+
this._compaction.abort();
|
|
3054
2878
|
}
|
|
3055
2879
|
/**
|
|
3056
2880
|
* Cancel in-progress branch summarization.
|
|
@@ -3073,280 +2897,19 @@ export class AgentSession {
|
|
|
3073
2897
|
return this._compactionSupport.getAdaptedSettings();
|
|
3074
2898
|
}
|
|
3075
2899
|
_checkContextWindowUsageWarning() {
|
|
3076
|
-
|
|
3077
|
-
return;
|
|
3078
|
-
const contextWindow = this.model.contextWindow ?? 0;
|
|
3079
|
-
if (contextWindow <= 0)
|
|
3080
|
-
return;
|
|
3081
|
-
const systemPromptTokens = Math.ceil((this.agent.state.systemPrompt ?? "").length / 4);
|
|
3082
|
-
let toolsChars = 0;
|
|
3083
|
-
for (const tool of this.agent.state.tools || []) {
|
|
3084
|
-
toolsChars += tool.name.length;
|
|
3085
|
-
toolsChars += tool.description?.length ?? 0;
|
|
3086
|
-
if (tool.parameters) {
|
|
3087
|
-
toolsChars += JSON.stringify(tool.parameters).length;
|
|
3088
|
-
}
|
|
3089
|
-
}
|
|
3090
|
-
const toolsTokens = Math.ceil(toolsChars / 4);
|
|
3091
|
-
const baseTokens = systemPromptTokens + toolsTokens;
|
|
3092
|
-
if (baseTokens >= contextWindow) {
|
|
3093
|
-
this._emit({
|
|
3094
|
-
type: "warning",
|
|
3095
|
-
message: `Base configuration (system prompt and active tools) consumes ${baseTokens} tokens, which exceeds the model's context window of ${contextWindow} tokens. The model cannot process any prompts in this state.`,
|
|
3096
|
-
});
|
|
3097
|
-
}
|
|
3098
|
-
else if (baseTokens >= contextWindow * 0.7) {
|
|
3099
|
-
this._emit({
|
|
3100
|
-
type: "warning",
|
|
3101
|
-
message: `Base configuration (system prompt and active tools) consumes ${baseTokens} tokens (${Math.round((baseTokens / contextWindow) * 100)}% of the ${contextWindow} context window). This leaves very little room for conversation history and may cause immediate compaction or context overflow.`,
|
|
3102
|
-
});
|
|
3103
|
-
}
|
|
2900
|
+
this._compaction.checkContextWindowUsageWarning();
|
|
3104
2901
|
}
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
if (!settings.enabled)
|
|
3108
|
-
return false;
|
|
3109
|
-
// Skip if message was aborted (user cancelled) - unless skipAbortedCheck is false
|
|
3110
|
-
if (skipAbortedCheck && assistantMessage.stopReason === "aborted")
|
|
3111
|
-
return false;
|
|
3112
|
-
const contextWindow = this.model?.contextWindow ?? 0;
|
|
3113
|
-
// Skip overflow check if the message came from a different model.
|
|
3114
|
-
// This handles the case where user switched from a smaller-context model (e.g. opus)
|
|
3115
|
-
// to a larger-context model (e.g. codex) - the overflow error from the old model
|
|
3116
|
-
// shouldn't trigger compaction for the new model.
|
|
3117
|
-
const sameModel = this.model && assistantMessage.provider === this.model.provider && assistantMessage.model === this.model.id;
|
|
3118
|
-
// Skip compaction checks if this assistant message is older than the latest
|
|
3119
|
-
// compaction boundary. This prevents a stale pre-compaction usage/error
|
|
3120
|
-
// from retriggering compaction on the first prompt after compaction.
|
|
3121
|
-
const compactionEntry = getLatestCompactionEntry(this.sessionManager.getBranch());
|
|
3122
|
-
const assistantIsFromBeforeCompaction = compactionEntry !== null && assistantMessage.timestamp <= new Date(compactionEntry.timestamp).getTime();
|
|
3123
|
-
if (assistantIsFromBeforeCompaction) {
|
|
3124
|
-
return false;
|
|
3125
|
-
}
|
|
3126
|
-
// Case 1: Overflow - LLM returned context overflow error
|
|
3127
|
-
if (sameModel && isContextOverflow(assistantMessage, contextWindow)) {
|
|
3128
|
-
if (this._overflowRecoveryAttempted) {
|
|
3129
|
-
this._emit({
|
|
3130
|
-
type: "compaction_end",
|
|
3131
|
-
reason: "overflow",
|
|
3132
|
-
result: undefined,
|
|
3133
|
-
aborted: false,
|
|
3134
|
-
willRetry: false,
|
|
3135
|
-
errorMessage: "Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model.",
|
|
3136
|
-
});
|
|
3137
|
-
return false;
|
|
3138
|
-
}
|
|
3139
|
-
this._overflowRecoveryAttempted = true;
|
|
3140
|
-
// Remove the error message from agent state (it IS saved to session for history,
|
|
3141
|
-
// but we don't want it in context for the retry)
|
|
3142
|
-
const messages = this.agent.state.messages;
|
|
3143
|
-
if (messages.length > 0 && messages[messages.length - 1].role === "assistant") {
|
|
3144
|
-
this.agent.state.messages = messages.slice(0, -1);
|
|
3145
|
-
}
|
|
3146
|
-
return await this._runAutoCompaction("overflow", true);
|
|
3147
|
-
}
|
|
3148
|
-
// Case 2: Threshold - context is getting large
|
|
3149
|
-
// For error messages (no usage data), estimate from last successful response.
|
|
3150
|
-
// This ensures sessions that hit persistent API errors (e.g. 529) can still compact.
|
|
3151
|
-
let contextTokens;
|
|
3152
|
-
if (assistantMessage.stopReason === "error") {
|
|
3153
|
-
const messages = this.agent.state.messages;
|
|
3154
|
-
const estimate = estimateContextTokens(messages);
|
|
3155
|
-
if (estimate.lastUsageIndex === null)
|
|
3156
|
-
return false; // No usage data at all
|
|
3157
|
-
// Verify the usage source is post-compaction. Kept pre-compaction messages
|
|
3158
|
-
// have stale usage reflecting the old (larger) context and would falsely
|
|
3159
|
-
// trigger compaction right after one just finished.
|
|
3160
|
-
const usageMsg = messages[estimate.lastUsageIndex];
|
|
3161
|
-
if (compactionEntry &&
|
|
3162
|
-
usageMsg.role === "assistant" &&
|
|
3163
|
-
usageMsg.timestamp <= new Date(compactionEntry.timestamp).getTime()) {
|
|
3164
|
-
return false;
|
|
3165
|
-
}
|
|
3166
|
-
contextTokens = estimate.tokens;
|
|
3167
|
-
}
|
|
3168
|
-
else {
|
|
3169
|
-
contextTokens = calculateContextTokens(assistantMessage.usage);
|
|
3170
|
-
const estimate = estimateContextTokens(this.agent.state.messages);
|
|
3171
|
-
if (estimate.lastUsageIndex !== null) {
|
|
3172
|
-
const usageMsg = this.agent.state.messages[estimate.lastUsageIndex];
|
|
3173
|
-
const usageIsPostCompaction = !(compactionEntry &&
|
|
3174
|
-
usageMsg.role === "assistant" &&
|
|
3175
|
-
usageMsg.timestamp <= new Date(compactionEntry.timestamp).getTime());
|
|
3176
|
-
if (usageIsPostCompaction) {
|
|
3177
|
-
contextTokens = Math.max(contextTokens, estimate.tokens);
|
|
3178
|
-
}
|
|
3179
|
-
}
|
|
3180
|
-
}
|
|
3181
|
-
if (shouldCompact(contextTokens, contextWindow, settings, this.model?.autoCompactionTriggerTokens)) {
|
|
3182
|
-
return await this._runAutoCompaction("threshold", false);
|
|
3183
|
-
}
|
|
3184
|
-
return false;
|
|
2902
|
+
_checkCompaction(assistantMessage, skipAbortedCheck = true) {
|
|
2903
|
+
return this._compaction.check(assistantMessage, skipAbortedCheck);
|
|
3185
2904
|
}
|
|
3186
2905
|
_measureLiveContextTokensForCompaction() {
|
|
3187
|
-
|
|
3188
|
-
const assistantMessage = findLastAssistantMessage(this.agent.state.messages);
|
|
3189
|
-
if (!assistantMessage || assistantMessage.stopReason === "error" || assistantMessage.stopReason === "aborted") {
|
|
3190
|
-
return estimatedTokens;
|
|
3191
|
-
}
|
|
3192
|
-
const compactionEntry = getLatestCompactionEntry(this.sessionManager.getBranch());
|
|
3193
|
-
if (compactionEntry && assistantMessage.timestamp <= new Date(compactionEntry.timestamp).getTime()) {
|
|
3194
|
-
return estimatedTokens;
|
|
3195
|
-
}
|
|
3196
|
-
return Math.max(calculateContextTokens(assistantMessage.usage), estimatedTokens);
|
|
2906
|
+
return this._compaction.measureLiveContextTokens();
|
|
3197
2907
|
}
|
|
3198
2908
|
/**
|
|
3199
2909
|
* Internal: Run auto-compaction with events.
|
|
3200
2910
|
*/
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
const model = this.model;
|
|
3204
|
-
this._emit({ type: "compaction_start", reason });
|
|
3205
|
-
const hadQueuedMessages = this.agent.hasQueuedMessages();
|
|
3206
|
-
this._autoCompactionAbortController = new AbortController();
|
|
3207
|
-
const signal = this._autoCompactionAbortController.signal;
|
|
3208
|
-
let fromExtension = false;
|
|
3209
|
-
let lastCompaction;
|
|
3210
|
-
let extensionCancelled = false;
|
|
3211
|
-
try {
|
|
3212
|
-
if (!model) {
|
|
3213
|
-
this._emit({
|
|
3214
|
-
type: "compaction_end",
|
|
3215
|
-
reason,
|
|
3216
|
-
result: undefined,
|
|
3217
|
-
aborted: false,
|
|
3218
|
-
willRetry: false,
|
|
3219
|
-
skipReason: "no model selected",
|
|
3220
|
-
});
|
|
3221
|
-
return hadQueuedMessages || this.agent.hasQueuedMessages();
|
|
3222
|
-
}
|
|
3223
|
-
const contextWindow = model.contextWindow;
|
|
3224
|
-
const margin = Math.max(0, Math.floor(0.01 * contextWindow));
|
|
3225
|
-
const outcome = await runCompactionLoop({
|
|
3226
|
-
getBranch: () => this.sessionManager.getBranch(),
|
|
3227
|
-
measureLiveTokens: () => this._measureLiveContextTokensForCompaction(),
|
|
3228
|
-
shouldCompact: reason === "overflow"
|
|
3229
|
-
? () => true
|
|
3230
|
-
: (tokens) => shouldCompact(tokens, contextWindow, settings, model.autoCompactionTriggerTokens),
|
|
3231
|
-
getPostApplyMargin: () => margin,
|
|
3232
|
-
getBaseKeepRecentTokens: () => settings.keepRecentTokens,
|
|
3233
|
-
resolveModelAndAuth: async (modelTier) => this._resolveCompactionModelAndAuth(modelTier === "session" ? model : this._resolveCompactionModel(model), model),
|
|
3234
|
-
summarizeAndVerify: async (params, compactModel, apiKey, headers, branchEntries) => {
|
|
3235
|
-
fromExtension = false;
|
|
3236
|
-
const adaptedSettings = { ...settings, keepRecentTokens: params.keepRecentTokens };
|
|
3237
|
-
const preparation = prepareCompaction(branchEntries, adaptedSettings);
|
|
3238
|
-
if (!preparation) {
|
|
3239
|
-
throw new Error("already compacted");
|
|
3240
|
-
}
|
|
3241
|
-
const compactionThinkingLevel = this._resolveCompactionThinkingLevel(compactModel, model);
|
|
3242
|
-
if (this._extensionRunner.hasHandlers("session_before_compact")) {
|
|
3243
|
-
const extensionResult = (await this._extensionRunner.emit({
|
|
3244
|
-
type: "session_before_compact",
|
|
3245
|
-
preparation,
|
|
3246
|
-
branchEntries,
|
|
3247
|
-
customInstructions: undefined,
|
|
3248
|
-
signal,
|
|
3249
|
-
}));
|
|
3250
|
-
if (extensionResult?.cancel) {
|
|
3251
|
-
extensionCancelled = true;
|
|
3252
|
-
throw new Error("auto-compaction-cancelled");
|
|
3253
|
-
}
|
|
3254
|
-
if (extensionResult?.compaction) {
|
|
3255
|
-
fromExtension = true;
|
|
3256
|
-
return { result: extensionResult.compaction };
|
|
3257
|
-
}
|
|
3258
|
-
}
|
|
3259
|
-
const compactResult = await this._compactWithRetry(() => compact(preparation, compactModel, apiKey, headers, undefined, signal, compactionThinkingLevel, this.agent.streamFn, this._buildCompactionPreDigest(), { chunked: params.chunked }), signal, compactModel.provider);
|
|
3260
|
-
return { result: compactResult };
|
|
3261
|
-
},
|
|
3262
|
-
buildDeterministicCheckpoint: async () => {
|
|
3263
|
-
const branch = this.sessionManager.getBranch();
|
|
3264
|
-
const preparation = prepareCompaction(branch, {
|
|
3265
|
-
...settings,
|
|
3266
|
-
});
|
|
3267
|
-
if (!preparation) {
|
|
3268
|
-
throw new Error("already compacted");
|
|
3269
|
-
}
|
|
3270
|
-
fromExtension = false;
|
|
3271
|
-
return { result: createDeterministicCompaction(preparation) };
|
|
3272
|
-
},
|
|
3273
|
-
apply: async (result) => {
|
|
3274
|
-
lastCompaction = result;
|
|
3275
|
-
this.sessionManager.appendCompaction(result.summary, result.firstKeptEntryId, result.tokensBefore, result.details, fromExtension);
|
|
3276
|
-
this._refreshAfterCompaction();
|
|
3277
|
-
const newEntries = this.sessionManager.getEntries();
|
|
3278
|
-
const savedCompactionEntry = newEntries.find((entry) => entry.type === "compaction" && entry.summary === result.summary);
|
|
3279
|
-
if (this._extensionRunner && savedCompactionEntry) {
|
|
3280
|
-
await this._extensionRunner.emit({
|
|
3281
|
-
type: "session_compact",
|
|
3282
|
-
compactionEntry: savedCompactionEntry,
|
|
3283
|
-
fromExtension,
|
|
3284
|
-
});
|
|
3285
|
-
}
|
|
3286
|
-
},
|
|
3287
|
-
verifyPostApplyEffect: reason === "overflow" ? () => false : undefined,
|
|
3288
|
-
onTransition: ({ cycle, cause, detail }) => {
|
|
3289
|
-
this._emit({
|
|
3290
|
-
type: "warning",
|
|
3291
|
-
message: `auto-compaction cycle ${cycle}: ${cause}${detail ? ` (${detail})` : ""} — retrying from step 0 (${this._describeCompactionSummarizer()})`,
|
|
3292
|
-
});
|
|
3293
|
-
},
|
|
3294
|
-
signal,
|
|
3295
|
-
});
|
|
3296
|
-
if (outcome.kind === "skip") {
|
|
3297
|
-
this._emit({
|
|
3298
|
-
type: "compaction_end",
|
|
3299
|
-
reason,
|
|
3300
|
-
result: undefined,
|
|
3301
|
-
aborted: false,
|
|
3302
|
-
willRetry: false,
|
|
3303
|
-
skipReason: outcome.reason,
|
|
3304
|
-
});
|
|
3305
|
-
return hadQueuedMessages || this.agent.hasQueuedMessages();
|
|
3306
|
-
}
|
|
3307
|
-
if (outcome.kind === "failed") {
|
|
3308
|
-
if (outcome.reason === "aborted") {
|
|
3309
|
-
this._emit({ type: "compaction_end", reason, result: undefined, aborted: true, willRetry: false });
|
|
3310
|
-
return hadQueuedMessages || this.agent.hasQueuedMessages();
|
|
3311
|
-
}
|
|
3312
|
-
throw new Error(outcome.reason);
|
|
3313
|
-
}
|
|
3314
|
-
if (extensionCancelled || signal.aborted) {
|
|
3315
|
-
this._emit({ type: "compaction_end", reason, result: undefined, aborted: true, willRetry: false });
|
|
3316
|
-
return hadQueuedMessages || this.agent.hasQueuedMessages();
|
|
3317
|
-
}
|
|
3318
|
-
const result = outcome.kind === "success" ? outcome.result : lastCompaction;
|
|
3319
|
-
if (!result) {
|
|
3320
|
-
throw new Error("Auto-compaction succeeded without a result");
|
|
3321
|
-
}
|
|
3322
|
-
this._emit({ type: "compaction_end", reason, result, aborted: false, willRetry });
|
|
3323
|
-
if (willRetry) {
|
|
3324
|
-
const messages = this.agent.state.messages;
|
|
3325
|
-
const lastMsg = messages[messages.length - 1];
|
|
3326
|
-
if (lastMsg?.role === "assistant" && lastMsg.stopReason === "error") {
|
|
3327
|
-
this.agent.state.messages = messages.slice(0, -1);
|
|
3328
|
-
}
|
|
3329
|
-
return true;
|
|
3330
|
-
}
|
|
3331
|
-
return hadQueuedMessages || this.agent.hasQueuedMessages();
|
|
3332
|
-
}
|
|
3333
|
-
catch (error) {
|
|
3334
|
-
const errorMessage = error instanceof Error ? error.message : "compaction failed";
|
|
3335
|
-
this._emit({
|
|
3336
|
-
type: "compaction_end",
|
|
3337
|
-
reason,
|
|
3338
|
-
result: undefined,
|
|
3339
|
-
aborted: false,
|
|
3340
|
-
willRetry: false,
|
|
3341
|
-
errorMessage: reason === "overflow"
|
|
3342
|
-
? `Context overflow recovery failed: ${errorMessage}`
|
|
3343
|
-
: `Auto-compaction failed: ${errorMessage}`,
|
|
3344
|
-
});
|
|
3345
|
-
return hadQueuedMessages || this.agent.hasQueuedMessages();
|
|
3346
|
-
}
|
|
3347
|
-
finally {
|
|
3348
|
-
this._autoCompactionAbortController = undefined;
|
|
3349
|
-
}
|
|
2911
|
+
_runAutoCompaction(reason, willRetry) {
|
|
2912
|
+
return this._compaction.runAuto(reason, willRetry);
|
|
3350
2913
|
}
|
|
3351
2914
|
/**
|
|
3352
2915
|
* Run one compaction attempt, retrying retryable provider failures (stream stalls,
|
|
@@ -3356,30 +2919,8 @@ export class AgentSession {
|
|
|
3356
2919
|
* same failure via auto-retry. Caller aborts are never retried; sleepAbortable rejects
|
|
3357
2920
|
* with the abort reason if the signal fires mid-backoff.
|
|
3358
2921
|
*/
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
const maxAttempts = retrySettings.enabled ? Math.max(1, retrySettings.maxRetries + 1) : 1;
|
|
3362
|
-
const policy = {
|
|
3363
|
-
maxAttempts,
|
|
3364
|
-
baseDelayMs: retrySettings.baseDelayMs,
|
|
3365
|
-
maxDelayMs: DEFAULT_RETRY_POLICY.maxDelayMs,
|
|
3366
|
-
jitterRatio: 0,
|
|
3367
|
-
};
|
|
3368
|
-
for (let attempt = 1;; attempt++) {
|
|
3369
|
-
try {
|
|
3370
|
-
return await run();
|
|
3371
|
-
}
|
|
3372
|
-
catch (error) {
|
|
3373
|
-
if (signal.aborted || attempt >= maxAttempts)
|
|
3374
|
-
throw error;
|
|
3375
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
3376
|
-
const classified = classifyFailure({ message, provider });
|
|
3377
|
-
this._failureCorpus.record({ provider, message, classified });
|
|
3378
|
-
if (!classified.retryable)
|
|
3379
|
-
throw error;
|
|
3380
|
-
await sleepAbortable(computeRetryDelayMs(policy, attempt, { retryAfterMs: classified.retryAfterMs }), signal);
|
|
3381
|
-
}
|
|
3382
|
-
}
|
|
2922
|
+
_compactWithRetry(run, signal, provider) {
|
|
2923
|
+
return this._compaction.compactWithRetry(run, signal, provider);
|
|
3383
2924
|
}
|
|
3384
2925
|
/**
|
|
3385
2926
|
* Toggle auto-compaction setting.
|
|
@@ -3548,6 +3089,9 @@ export class AgentSession {
|
|
|
3548
3089
|
reportSpawnedUsage: (usage, opts) => {
|
|
3549
3090
|
this.addSpawnedUsage(usage, opts);
|
|
3550
3091
|
},
|
|
3092
|
+
reportManagedLane: (event) => {
|
|
3093
|
+
this._backgroundLanes.recordManagedLane(event);
|
|
3094
|
+
},
|
|
3551
3095
|
}, {
|
|
3552
3096
|
getModel: () => this.model,
|
|
3553
3097
|
isIdle: () => !this.isStreaming,
|
|
@@ -3608,15 +3152,15 @@ export class AgentSession {
|
|
|
3608
3152
|
registerContextMemoryProvider(provider) {
|
|
3609
3153
|
this._memory.registerContextMemoryProvider(provider);
|
|
3610
3154
|
}
|
|
3611
|
-
/**
|
|
3155
|
+
/** The gateway/scheduler registry. A deployment runner registers providers and drives start/stop. */
|
|
3612
3156
|
get gateways() {
|
|
3613
3157
|
return this._gatewayRegistry;
|
|
3614
3158
|
}
|
|
3615
|
-
/**
|
|
3159
|
+
/** Register a deployment-supplied transport channel (gateway). */
|
|
3616
3160
|
registerChannelProvider(provider) {
|
|
3617
3161
|
this._gatewayRegistry.registerChannel(provider);
|
|
3618
3162
|
}
|
|
3619
|
-
/**
|
|
3163
|
+
/** Register a deployment-supplied job scheduler (cron). */
|
|
3620
3164
|
registerJobScheduler(provider) {
|
|
3621
3165
|
this._gatewayRegistry.registerScheduler(provider);
|
|
3622
3166
|
}
|
|
@@ -3655,40 +3199,15 @@ export class AgentSession {
|
|
|
3655
3199
|
// =========================================================================
|
|
3656
3200
|
// Auto-Retry
|
|
3657
3201
|
// =========================================================================
|
|
3658
|
-
/**
|
|
3659
|
-
* Check if an error is retryable (transient provider/network failures). Billing/quota and auth
|
|
3660
|
-
* are terminal; context overflow is handled by compaction, not retry. The verdict comes from the
|
|
3661
|
-
* reliability kernel's classifier, fed the host-computed context-overflow flag.
|
|
3662
|
-
*/
|
|
3663
|
-
_classifyAssistantError(message) {
|
|
3664
|
-
if (message.stopReason !== "error" || !message.errorMessage)
|
|
3665
|
-
return undefined;
|
|
3666
|
-
const contextWindow = this.model?.contextWindow ?? 0;
|
|
3667
|
-
return classifyFailure({
|
|
3668
|
-
message: message.errorMessage,
|
|
3669
|
-
contextOverflow: isContextOverflow(message, contextWindow),
|
|
3670
|
-
provider: message.provider,
|
|
3671
|
-
});
|
|
3672
|
-
}
|
|
3673
|
-
_isRetryableError(message) {
|
|
3674
|
-
return this._classifyAssistantError(message)?.retryable ?? false;
|
|
3675
|
-
}
|
|
3676
|
-
/**
|
|
3677
|
-
* Prepare a retryable error for continuation with exponential backoff.
|
|
3678
|
-
* @returns true if the caller should continue the agent, false otherwise
|
|
3679
|
-
*/
|
|
3680
|
-
async _prepareRetry(message) {
|
|
3681
|
-
return this._retryController.prepareRetry(message);
|
|
3682
|
-
}
|
|
3683
3202
|
/**
|
|
3684
3203
|
* Cancel in-progress retry.
|
|
3685
3204
|
*/
|
|
3686
3205
|
abortRetry() {
|
|
3687
|
-
this.
|
|
3206
|
+
this._foregroundRecovery.abortRetry();
|
|
3688
3207
|
}
|
|
3689
3208
|
/** Whether auto-retry is currently in progress */
|
|
3690
3209
|
get isRetrying() {
|
|
3691
|
-
return this.
|
|
3210
|
+
return this._foregroundRecovery.isRetrying;
|
|
3692
3211
|
}
|
|
3693
3212
|
/** Whether auto-retry is enabled */
|
|
3694
3213
|
get autoRetryEnabled() {
|
|
@@ -3769,13 +3288,48 @@ export class AgentSession {
|
|
|
3769
3288
|
* @returns the id of the appended custom entry
|
|
3770
3289
|
*/
|
|
3771
3290
|
saveGoalStateSnapshot(state) {
|
|
3772
|
-
|
|
3291
|
+
const entryId = appendGoalStateSnapshot(this.sessionManager, state);
|
|
3292
|
+
try {
|
|
3293
|
+
this._backgroundLanes.synchronizeGoalState(state);
|
|
3294
|
+
}
|
|
3295
|
+
catch (error) {
|
|
3296
|
+
this._emit({
|
|
3297
|
+
type: "warning",
|
|
3298
|
+
message: `Goal state persisted but durable worker reconciliation failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
3299
|
+
});
|
|
3300
|
+
}
|
|
3301
|
+
return entryId;
|
|
3773
3302
|
}
|
|
3774
3303
|
/**
|
|
3775
3304
|
* Retrieve the latest valid goal state snapshot from the session log.
|
|
3776
3305
|
*/
|
|
3777
3306
|
getGoalStateSnapshot() {
|
|
3778
|
-
return getLatestGoalStateSnapshot(this.sessionManager
|
|
3307
|
+
return getLatestGoalStateSnapshot(this.sessionManager);
|
|
3308
|
+
}
|
|
3309
|
+
/**
|
|
3310
|
+
* Persist one submitted continuation pass's turn/wall-clock/spend contribution onto the active
|
|
3311
|
+
* goal's durable cumulative budget (see `GoalState.continuationTurnsUsed` et al.). This is the
|
|
3312
|
+
* "persistence dep" `GoalLoopController` calls once per pass actually submitted — it, not the
|
|
3313
|
+
* loop controller, is where USD gets attributed: it reads the session's OWN cumulative model
|
|
3314
|
+
* spend (`getCostSummary().ownCost` — deliberately excludes worker/subagent spend, which is
|
|
3315
|
+
* tracked and budgeted separately) and threads that single absolute reading into a
|
|
3316
|
+
* `record_continuation_budget` event; the pure reducer in `goal-state.ts` derives the delta.
|
|
3317
|
+
* A no-op when no goal state exists (defensive — in practice this is only ever called right
|
|
3318
|
+
* after a pass the loop already confirmed was submitted against an active goal).
|
|
3319
|
+
*/
|
|
3320
|
+
recordGoalContinuationPass(pass) {
|
|
3321
|
+
const state = this.getGoalStateSnapshot();
|
|
3322
|
+
if (!state)
|
|
3323
|
+
return;
|
|
3324
|
+
const sessionCostUsd = this.getCostSummary().ownCost;
|
|
3325
|
+
const updated = applyGoalEvent(state, {
|
|
3326
|
+
type: "record_continuation_budget",
|
|
3327
|
+
turns: pass.turns,
|
|
3328
|
+
wallClockMs: pass.wallClockMs,
|
|
3329
|
+
sessionCostUsd,
|
|
3330
|
+
now: new Date().toISOString(),
|
|
3331
|
+
});
|
|
3332
|
+
this.saveGoalStateSnapshot(updated);
|
|
3779
3333
|
}
|
|
3780
3334
|
/** Save native task-step state to the active session log. */
|
|
3781
3335
|
saveTaskStepsStateSnapshot(state) {
|
|
@@ -3783,7 +3337,7 @@ export class AgentSession {
|
|
|
3783
3337
|
}
|
|
3784
3338
|
/** Retrieve the latest valid native task-step state from the active session log. */
|
|
3785
3339
|
getTaskStepsStateSnapshot() {
|
|
3786
|
-
return getLatestTaskStepsStateSnapshot(this.sessionManager
|
|
3340
|
+
return getLatestTaskStepsStateSnapshot(this.sessionManager);
|
|
3787
3341
|
}
|
|
3788
3342
|
/**
|
|
3789
3343
|
* Save a snapshot of the evidence bundle to the session log.
|
|
@@ -3794,19 +3348,20 @@ export class AgentSession {
|
|
|
3794
3348
|
return appendEvidenceBundleSnapshot(this.sessionManager, bundle);
|
|
3795
3349
|
}
|
|
3796
3350
|
/**
|
|
3797
|
-
* Retrieve the latest valid evidence bundle snapshot from the
|
|
3351
|
+
* Retrieve the latest valid evidence bundle snapshot from the active branch.
|
|
3798
3352
|
*/
|
|
3799
3353
|
getEvidenceBundleSnapshot() {
|
|
3800
|
-
return getLatestEvidenceBundleSnapshot(this.sessionManager
|
|
3354
|
+
return getLatestEvidenceBundleSnapshot(getActiveSessionBranchEntries(this.sessionManager));
|
|
3801
3355
|
}
|
|
3356
|
+
/** Retrieve all valid evidence bundle snapshots from the active branch. */
|
|
3802
3357
|
getEvidenceBundleSnapshots() {
|
|
3803
|
-
return getEvidenceBundleSnapshots(this.sessionManager
|
|
3358
|
+
return getEvidenceBundleSnapshots(getActiveSessionBranchEntries(this.sessionManager));
|
|
3804
3359
|
}
|
|
3805
3360
|
/** Live lane records tracked by this process (running and terminal). */
|
|
3806
3361
|
getLaneRecords() {
|
|
3807
3362
|
return this._backgroundLanes.getLaneRecords();
|
|
3808
3363
|
}
|
|
3809
|
-
//
|
|
3364
|
+
// Autonomy telemetry + gate-outcome history live in AutonomyTelemetry (see
|
|
3810
3365
|
// autonomy-telemetry.ts). These stubs keep the god file's internal call surface stable while the
|
|
3811
3366
|
// sink logic and the owned gate-outcome fields live there.
|
|
3812
3367
|
_emitAutonomyTelemetry(event) {
|
|
@@ -3815,26 +3370,35 @@ export class AgentSession {
|
|
|
3815
3370
|
_recordGateOutcome(outcome) {
|
|
3816
3371
|
this._autonomyTelemetry.recordGateOutcome(outcome);
|
|
3817
3372
|
}
|
|
3818
|
-
/**
|
|
3373
|
+
/** Copies of the bounded gate-outcome history, oldest first, latest last. */
|
|
3819
3374
|
getGateOutcomeHistory() {
|
|
3820
3375
|
return this._autonomyTelemetry.getGateOutcomeHistory();
|
|
3821
3376
|
}
|
|
3822
|
-
|
|
3823
|
-
return
|
|
3377
|
+
saveWorkerClaimSnapshot(claim, request) {
|
|
3378
|
+
return appendWorkerClaimSnapshot(this.sessionManager, claim, request);
|
|
3824
3379
|
}
|
|
3825
|
-
|
|
3826
|
-
return
|
|
3380
|
+
getWorkerClaimSnapshots() {
|
|
3381
|
+
return getWorkerClaimSnapshots(getActiveSessionBranchEntries(this.sessionManager));
|
|
3827
3382
|
}
|
|
3828
3383
|
saveLearningDecisionSnapshot(decision) {
|
|
3829
3384
|
return appendLearningDecisionSnapshot(this.sessionManager, decision);
|
|
3830
3385
|
}
|
|
3831
3386
|
getLearningDecisionSnapshots() {
|
|
3832
|
-
return getLearningDecisionSnapshots(this.sessionManager
|
|
3387
|
+
return getLearningDecisionSnapshots(getActiveSessionBranchEntries(this.sessionManager));
|
|
3833
3388
|
}
|
|
3389
|
+
/**
|
|
3390
|
+
* The single injection point that makes the goal-continuation snapshot lane-aware:
|
|
3391
|
+
* `laneRecords` feeds BOTH `evaluateGoalContinuation`'s "waiting" branch (a worker dispatched
|
|
3392
|
+
* against an open requirement) and the per-goal worker-spend overlay — read fresh here so BOTH the
|
|
3393
|
+
* goal loop (`GoalLoopController`) and the idle scheduler (`BackgroundLaneController`) see the
|
|
3394
|
+
* same live lane state, since both reach this same method.
|
|
3395
|
+
*/
|
|
3834
3396
|
getGoalRuntimeSnapshot(settings) {
|
|
3835
3397
|
return buildGoalRuntimeSnapshot({
|
|
3836
|
-
|
|
3398
|
+
sessionManager: this.sessionManager,
|
|
3837
3399
|
settings,
|
|
3400
|
+
laneRecords: this._backgroundLanes.getLaneRecords(),
|
|
3401
|
+
taskRuntime: this._backgroundLanes.getTaskRuntimeSnapshot(),
|
|
3838
3402
|
});
|
|
3839
3403
|
}
|
|
3840
3404
|
/**
|
|
@@ -3847,6 +3411,25 @@ export class AgentSession {
|
|
|
3847
3411
|
mode: this.settingsManager.getModelCapabilitySettings().mode,
|
|
3848
3412
|
});
|
|
3849
3413
|
}
|
|
3414
|
+
/**
|
|
3415
|
+
* Whether the CURRENT session model may drive a worktree-sync lane worker (see
|
|
3416
|
+
* `evaluateLaneWorkerRefusal` in model-capability.ts): full capability class, a DECLARED
|
|
3417
|
+
* (registry) context window, an ADVERTISED native tool-call path (`Model.textToolCallProtocol`
|
|
3418
|
+
* unset/false -- `true` means phone-only), and no graded `/toolprobe` demotion to
|
|
3419
|
+
* "text-protocol"/"none" on record. An unprobed model (no verdict on record yet) is eligible on
|
|
3420
|
+
* its advertised support alone. `undefined` means eligible.
|
|
3421
|
+
*/
|
|
3422
|
+
getLaneWorkerRefusal() {
|
|
3423
|
+
const profile = this.getModelCapabilityProfile();
|
|
3424
|
+
const model = this.model;
|
|
3425
|
+
const verdict = model ? this._toolProtocol.getToolProbeVerdict(model) : undefined;
|
|
3426
|
+
return evaluateLaneWorkerRefusal({
|
|
3427
|
+
capabilityClass: profile.class,
|
|
3428
|
+
contextWindow: profile.contextWindow,
|
|
3429
|
+
toolCallingAdvertised: model?.textToolCallProtocol !== true,
|
|
3430
|
+
toolCallingDemoted: verdict === "text-protocol" || verdict === "none",
|
|
3431
|
+
});
|
|
3432
|
+
}
|
|
3850
3433
|
/**
|
|
3851
3434
|
* Run one bounded, read-only research pass and persist its results. Delegates to
|
|
3852
3435
|
* {@link BackgroundLaneController}; see there for the full gating/budget/dedupe contract.
|
|
@@ -3855,7 +3438,7 @@ export class AgentSession {
|
|
|
3855
3438
|
return this._backgroundLanes.runResearchLaneOnce(request);
|
|
3856
3439
|
}
|
|
3857
3440
|
/**
|
|
3858
|
-
* Run one bounded
|
|
3441
|
+
* Run one bounded profile-bound worker delegation. Delegates to {@link BackgroundLaneController};
|
|
3859
3442
|
* consumed by the `delegate` tool.
|
|
3860
3443
|
*/
|
|
3861
3444
|
async runWorkerDelegationOnce(request) {
|
|
@@ -3875,8 +3458,15 @@ export class AgentSession {
|
|
|
3875
3458
|
async continueGoalOnce(options) {
|
|
3876
3459
|
return this._goalContinuation.continueGoalOnce(options);
|
|
3877
3460
|
}
|
|
3461
|
+
/**
|
|
3462
|
+
* Public entry point for BOTH idle autosteer and manual (`/goal start`, `/goal-continue`)
|
|
3463
|
+
* continuation. Delegates to {@link BackgroundLaneController.continueGoalLoopExclusive}, the
|
|
3464
|
+
* single-flight guard that prevents two goal loops from racing to submit prompts through the
|
|
3465
|
+
* same session (which throws "Agent is already processing" from the second submission). Do not
|
|
3466
|
+
* call `this._goalContinuation.continueGoalLoop` directly from here — that bypasses the guard.
|
|
3467
|
+
*/
|
|
3878
3468
|
async continueGoalLoop(options) {
|
|
3879
|
-
return this.
|
|
3469
|
+
return this._backgroundLanes.continueGoalLoopExclusive(options);
|
|
3880
3470
|
}
|
|
3881
3471
|
/**
|
|
3882
3472
|
* Run a one-shot LLM completion fully ISOLATED from the main session — the load-bearing primitive
|
|
@@ -3886,7 +3476,7 @@ export class AgentSession {
|
|
|
3886
3476
|
return this._reflection.runIsolatedCompletion(opts);
|
|
3887
3477
|
}
|
|
3888
3478
|
/**
|
|
3889
|
-
* Native end-of-loop reflection pass
|
|
3479
|
+
* Native end-of-loop reflection pass. Delegates to {@link ReflectionController}; returns null
|
|
3890
3480
|
* when the demand gate skips or in a child session.
|
|
3891
3481
|
*/
|
|
3892
3482
|
async runReflectionPass(input) {
|
|
@@ -3948,13 +3538,4 @@ export class AgentSession {
|
|
|
3948
3538
|
return this._extensionRunner;
|
|
3949
3539
|
}
|
|
3950
3540
|
}
|
|
3951
|
-
function findLastAssistantMessage(messages) {
|
|
3952
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
3953
|
-
const message = messages[i];
|
|
3954
|
-
if (message.role === "assistant") {
|
|
3955
|
-
return message;
|
|
3956
|
-
}
|
|
3957
|
-
}
|
|
3958
|
-
return undefined;
|
|
3959
|
-
}
|
|
3960
3541
|
//# sourceMappingURL=agent-session.js.map
|