@calliopelabs/cli 2.3.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -644
- package/dist/acp.d.ts +78 -0
- package/dist/acp.d.ts.map +1 -0
- package/dist/acp.js +572 -0
- package/dist/acp.js.map +1 -0
- package/dist/auto-compressor.d.ts.map +1 -1
- package/dist/auto-compressor.js +9 -5
- package/dist/auto-compressor.js.map +1 -1
- package/dist/bin.d.ts +9 -5
- package/dist/bin.d.ts.map +1 -1
- package/dist/bin.js +108 -76
- package/dist/bin.js.map +1 -1
- package/dist/budget.d.ts +64 -0
- package/dist/budget.d.ts.map +1 -0
- package/dist/budget.js +157 -0
- package/dist/budget.js.map +1 -0
- package/dist/checkpoint.d.ts +63 -30
- package/dist/checkpoint.d.ts.map +1 -1
- package/dist/checkpoint.js +249 -150
- package/dist/checkpoint.js.map +1 -1
- package/dist/circuit-breaker/breaker.js.map +1 -1
- package/dist/config.d.ts +86 -81
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +237 -305
- package/dist/config.js.map +1 -1
- package/dist/cost.d.ts +114 -0
- package/dist/cost.d.ts.map +1 -0
- package/dist/cost.js +499 -0
- package/dist/cost.js.map +1 -0
- package/dist/diff.d.ts.map +1 -1
- package/dist/diff.js +53 -11
- package/dist/diff.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +33 -6
- package/dist/errors.js.map +1 -1
- package/dist/files.js.map +1 -1
- package/dist/fleet.d.ts +30 -0
- package/dist/fleet.d.ts.map +1 -0
- package/dist/fleet.js +86 -0
- package/dist/fleet.js.map +1 -0
- package/dist/fuzzy-search.d.ts.map +1 -1
- package/dist/fuzzy-search.js +2 -1
- package/dist/fuzzy-search.js.map +1 -1
- package/dist/git-status.js.map +1 -1
- package/dist/headless.d.ts +1 -2
- package/dist/headless.d.ts.map +1 -1
- package/dist/headless.js +222 -26
- package/dist/headless.js.map +1 -1
- package/dist/hooks.d.ts +8 -2
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +97 -11
- package/dist/hooks.js.map +1 -1
- package/dist/hud/api.d.ts +3 -18
- package/dist/hud/api.d.ts.map +1 -1
- package/dist/hud/api.js +9 -352
- package/dist/hud/api.js.map +1 -1
- package/dist/hud/palettes.d.ts.map +1 -1
- package/dist/hud/palettes.js +0 -180
- package/dist/hud/palettes.js.map +1 -1
- package/dist/hud/skins.d.ts.map +1 -1
- package/dist/hud/skins.js +0 -263
- package/dist/hud/skins.js.map +1 -1
- package/dist/hud/types.d.ts +2 -5
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -4
- package/dist/index.js.map +1 -1
- package/dist/iteration-ledger.d.ts.map +1 -1
- package/dist/iteration-ledger.js +3 -2
- package/dist/iteration-ledger.js.map +1 -1
- package/dist/local-model.d.ts +150 -0
- package/dist/local-model.d.ts.map +1 -0
- package/dist/local-model.js +408 -0
- package/dist/local-model.js.map +1 -0
- package/dist/markdown.d.ts.map +1 -1
- package/dist/markdown.js +32 -10
- package/dist/markdown.js.map +1 -1
- package/dist/mcp.d.ts +35 -5
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +190 -13
- package/dist/mcp.js.map +1 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +6 -4
- package/dist/memory.js.map +1 -1
- package/dist/model-detection.d.ts +14 -1
- package/dist/model-detection.d.ts.map +1 -1
- package/dist/model-detection.js +308 -115
- package/dist/model-detection.js.map +1 -1
- package/dist/parallel-tools.d.ts +9 -1
- package/dist/parallel-tools.d.ts.map +1 -1
- package/dist/parallel-tools.js +6 -5
- package/dist/parallel-tools.js.map +1 -1
- package/dist/plugins.d.ts +68 -0
- package/dist/plugins.d.ts.map +1 -1
- package/dist/plugins.js +201 -3
- package/dist/plugins.js.map +1 -1
- package/dist/policy.d.ts +43 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +139 -0
- package/dist/policy.js.map +1 -0
- package/dist/project-config.d.ts +1 -2
- package/dist/project-config.d.ts.map +1 -1
- package/dist/project-config.js +0 -3
- package/dist/project-config.js.map +1 -1
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +36 -2
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/bedrock.d.ts.map +1 -1
- package/dist/providers/bedrock.js +83 -22
- package/dist/providers/bedrock.js.map +1 -1
- package/dist/providers/compat.d.ts +21 -0
- package/dist/providers/compat.d.ts.map +1 -1
- package/dist/providers/compat.js +176 -6
- package/dist/providers/compat.js.map +1 -1
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/index.d.ts +19 -4
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +62 -9
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/ollama.d.ts +2 -2
- package/dist/providers/ollama.d.ts.map +1 -1
- package/dist/providers/ollama.js +26 -6
- package/dist/providers/ollama.js.map +1 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +14 -10
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/types.d.ts +8 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/types.js +19 -10
- package/dist/providers/types.js.map +1 -1
- package/dist/replay.d.ts +37 -0
- package/dist/replay.d.ts.map +1 -0
- package/dist/replay.js +168 -0
- package/dist/replay.js.map +1 -0
- package/dist/risk.d.ts.map +1 -1
- package/dist/risk.js +15 -5
- package/dist/risk.js.map +1 -1
- package/dist/router.d.ts +133 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/{model-router.js → router.js} +336 -10
- package/dist/router.js.map +1 -0
- package/dist/runlog.d.ts +185 -0
- package/dist/runlog.d.ts.map +1 -0
- package/dist/runlog.js +398 -0
- package/dist/runlog.js.map +1 -0
- package/dist/{sandbox.d.ts → sandbox/docker.d.ts} +1 -1
- package/dist/sandbox/docker.d.ts.map +1 -0
- package/dist/{sandbox.js → sandbox/docker.js} +1 -1
- package/dist/sandbox/docker.js.map +1 -0
- package/dist/sandbox/index.d.ts +36 -0
- package/dist/sandbox/index.d.ts.map +1 -0
- package/dist/sandbox/index.js +62 -0
- package/dist/sandbox/index.js.map +1 -0
- package/dist/{sandbox-native.d.ts → sandbox/native.d.ts} +2 -1
- package/dist/sandbox/native.d.ts.map +1 -0
- package/dist/{sandbox-native.js → sandbox/native.js} +38 -6
- package/dist/sandbox/native.js.map +1 -0
- package/dist/scope.d.ts +10 -0
- package/dist/scope.d.ts.map +1 -1
- package/dist/scope.js +76 -16
- package/dist/scope.js.map +1 -1
- package/dist/scuttlebot/client.d.ts +83 -0
- package/dist/scuttlebot/client.d.ts.map +1 -0
- package/dist/scuttlebot/client.js +350 -0
- package/dist/scuttlebot/client.js.map +1 -0
- package/dist/scuttlebot/config.d.ts +28 -0
- package/dist/scuttlebot/config.d.ts.map +1 -0
- package/dist/scuttlebot/config.js +91 -0
- package/dist/scuttlebot/config.js.map +1 -0
- package/dist/scuttlebot/http-client.d.ts +63 -0
- package/dist/scuttlebot/http-client.d.ts.map +1 -0
- package/dist/scuttlebot/http-client.js +124 -0
- package/dist/scuttlebot/http-client.js.map +1 -0
- package/dist/scuttlebot/index.d.ts +13 -0
- package/dist/scuttlebot/index.d.ts.map +1 -0
- package/dist/scuttlebot/index.js +10 -0
- package/dist/scuttlebot/index.js.map +1 -0
- package/dist/scuttlebot/irc-client.d.ts +124 -0
- package/dist/scuttlebot/irc-client.d.ts.map +1 -0
- package/dist/scuttlebot/irc-client.js +599 -0
- package/dist/scuttlebot/irc-client.js.map +1 -0
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +22 -54
- package/dist/setup.js.map +1 -1
- package/dist/skills.d.ts +58 -0
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +186 -16
- package/dist/skills.js.map +1 -1
- package/dist/storage.d.ts +0 -4
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +87 -8
- package/dist/storage.js.map +1 -1
- package/dist/streaming.d.ts +25 -0
- package/dist/streaming.d.ts.map +1 -1
- package/dist/streaming.js +59 -0
- package/dist/streaming.js.map +1 -1
- package/dist/styles.d.ts +6 -0
- package/dist/styles.d.ts.map +1 -1
- package/dist/styles.js +11 -0
- package/dist/styles.js.map +1 -1
- package/dist/summarization.js.map +1 -1
- package/dist/terminal-image.js +1 -1
- package/dist/terminal-image.js.map +1 -1
- package/dist/themes.d.ts +5 -4
- package/dist/themes.d.ts.map +1 -1
- package/dist/themes.js +35 -90
- package/dist/themes.js.map +1 -1
- package/dist/tools.d.ts +27 -3
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +352 -209
- package/dist/tools.js.map +1 -1
- package/dist/trust.d.ts +16 -3
- package/dist/trust.d.ts.map +1 -1
- package/dist/trust.js +23 -4
- package/dist/trust.js.map +1 -1
- package/dist/types.d.ts +4 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +34 -44
- package/dist/types.js.map +1 -1
- package/dist/ui/agent.d.ts +4 -7
- package/dist/ui/agent.d.ts.map +1 -1
- package/dist/ui/agent.js +354 -73
- package/dist/ui/agent.js.map +1 -1
- package/dist/ui/animations.d.ts +37 -0
- package/dist/ui/animations.d.ts.map +1 -0
- package/dist/ui/animations.js +227 -0
- package/dist/ui/animations.js.map +1 -0
- package/dist/ui/chat-input-keys.d.ts +42 -0
- package/dist/ui/chat-input-keys.d.ts.map +1 -0
- package/dist/ui/chat-input-keys.js +360 -0
- package/dist/ui/chat-input-keys.js.map +1 -0
- package/dist/ui/chat-input.d.ts +5 -2
- package/dist/ui/chat-input.d.ts.map +1 -1
- package/dist/ui/chat-input.js +54 -308
- package/dist/ui/chat-input.js.map +1 -1
- package/dist/ui/commands.d.ts +15 -45
- package/dist/ui/commands.d.ts.map +1 -1
- package/dist/ui/commands.js +461 -2636
- package/dist/ui/commands.js.map +1 -1
- package/dist/ui/completions.d.ts +1 -0
- package/dist/ui/completions.d.ts.map +1 -1
- package/dist/ui/completions.js +36 -107
- package/dist/ui/completions.js.map +1 -1
- package/dist/ui/components.d.ts +14 -6
- package/dist/ui/components.d.ts.map +1 -1
- package/dist/ui/components.js +17 -226
- package/dist/ui/components.js.map +1 -1
- package/dist/ui/context.js +6 -6
- package/dist/ui/context.js.map +1 -1
- package/dist/ui/debug-log.d.ts +11 -0
- package/dist/ui/debug-log.d.ts.map +1 -0
- package/dist/ui/debug-log.js +21 -0
- package/dist/ui/debug-log.js.map +1 -0
- package/dist/ui/error-boundary.d.ts.map +1 -1
- package/dist/ui/error-boundary.js.map +1 -1
- package/dist/ui/frame.d.ts.map +1 -1
- package/dist/ui/frame.js +1 -3
- package/dist/ui/frame.js.map +1 -1
- package/dist/ui/index.d.ts +11 -3
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +46 -887
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/input-utils.d.ts +20 -0
- package/dist/ui/input-utils.d.ts.map +1 -0
- package/dist/ui/input-utils.js +35 -0
- package/dist/ui/input-utils.js.map +1 -0
- package/dist/ui/messages.d.ts +8 -7
- package/dist/ui/messages.d.ts.map +1 -1
- package/dist/ui/messages.js +51 -35
- package/dist/ui/messages.js.map +1 -1
- package/dist/ui/modals/index.d.ts +11 -0
- package/dist/ui/modals/index.d.ts.map +1 -0
- package/dist/ui/modals/index.js +10 -0
- package/dist/ui/modals/index.js.map +1 -0
- package/dist/ui/{modals.d.ts → modals/prompts.d.ts} +5 -20
- package/dist/ui/modals/prompts.d.ts.map +1 -0
- package/dist/ui/{modals.js → modals/prompts.js} +14 -94
- package/dist/ui/modals/prompts.js.map +1 -0
- package/dist/ui/modals/selectors.d.ts +33 -0
- package/dist/ui/modals/selectors.d.ts.map +1 -0
- package/dist/ui/modals/selectors.js +111 -0
- package/dist/ui/modals/selectors.js.map +1 -0
- package/dist/ui/modals/setup.d.ts +17 -0
- package/dist/ui/modals/setup.d.ts.map +1 -0
- package/dist/ui/modals/setup.js +51 -0
- package/dist/ui/modals/setup.js.map +1 -0
- package/dist/ui/regions/input-region.d.ts +40 -0
- package/dist/ui/regions/input-region.d.ts.map +1 -0
- package/dist/ui/regions/input-region.js +116 -0
- package/dist/ui/regions/input-region.js.map +1 -0
- package/dist/ui/regions/modal-host.d.ts +41 -0
- package/dist/ui/regions/modal-host.d.ts.map +1 -0
- package/dist/ui/regions/modal-host.js +51 -0
- package/dist/ui/regions/modal-host.js.map +1 -0
- package/dist/ui/regions/render-probe.d.ts +22 -0
- package/dist/ui/regions/render-probe.d.ts.map +1 -0
- package/dist/ui/regions/render-probe.js +45 -0
- package/dist/ui/regions/render-probe.js.map +1 -0
- package/dist/ui/regions/static-scrollback.d.ts +39 -0
- package/dist/ui/regions/static-scrollback.d.ts.map +1 -0
- package/dist/ui/regions/static-scrollback.js +68 -0
- package/dist/ui/regions/static-scrollback.js.map +1 -0
- package/dist/ui/regions/status-region.d.ts +24 -0
- package/dist/ui/regions/status-region.d.ts.map +1 -0
- package/dist/ui/regions/status-region.js +17 -0
- package/dist/ui/regions/status-region.js.map +1 -0
- package/dist/ui/regions/transcript-region.d.ts +41 -0
- package/dist/ui/regions/transcript-region.d.ts.map +1 -0
- package/dist/ui/regions/transcript-region.js +52 -0
- package/dist/ui/regions/transcript-region.js.map +1 -0
- package/dist/ui/self-restart.d.ts +9 -0
- package/dist/ui/self-restart.d.ts.map +1 -0
- package/dist/ui/self-restart.js +24 -0
- package/dist/ui/self-restart.js.map +1 -0
- package/dist/ui/state/use-chat-controller.d.ts +23 -0
- package/dist/ui/state/use-chat-controller.d.ts.map +1 -0
- package/dist/ui/state/use-chat-controller.js +643 -0
- package/dist/ui/state/use-chat-controller.js.map +1 -0
- package/dist/ui/state/use-loop-state.d.ts +23 -0
- package/dist/ui/state/use-loop-state.d.ts.map +1 -0
- package/dist/ui/state/use-loop-state.js +39 -0
- package/dist/ui/state/use-loop-state.js.map +1 -0
- package/dist/ui/state/use-modal-state.d.ts +52 -0
- package/dist/ui/state/use-modal-state.d.ts.map +1 -0
- package/dist/ui/state/use-modal-state.js +44 -0
- package/dist/ui/state/use-modal-state.js.map +1 -0
- package/dist/ui/state/use-model-state.d.ts +29 -0
- package/dist/ui/state/use-model-state.d.ts.map +1 -0
- package/dist/ui/state/use-model-state.js +47 -0
- package/dist/ui/state/use-model-state.js.map +1 -0
- package/dist/ui/state/use-processing-state.d.ts +24 -0
- package/dist/ui/state/use-processing-state.d.ts.map +1 -0
- package/dist/ui/state/use-processing-state.js +46 -0
- package/dist/ui/state/use-processing-state.js.map +1 -0
- package/dist/ui/state/use-queue-state.d.ts +19 -0
- package/dist/ui/state/use-queue-state.d.ts.map +1 -0
- package/dist/ui/state/use-queue-state.js +30 -0
- package/dist/ui/state/use-queue-state.js.map +1 -0
- package/dist/ui/state/use-session-init.d.ts +22 -0
- package/dist/ui/state/use-session-init.d.ts.map +1 -0
- package/dist/ui/state/use-session-init.js +77 -0
- package/dist/ui/state/use-session-init.js.map +1 -0
- package/dist/ui/state/use-session-stats.d.ts +16 -0
- package/dist/ui/state/use-session-stats.d.ts.map +1 -0
- package/dist/ui/state/use-session-stats.js +18 -0
- package/dist/ui/state/use-session-stats.js.map +1 -0
- package/dist/ui/state/use-terminal-width.d.ts +8 -0
- package/dist/ui/state/use-terminal-width.d.ts.map +1 -0
- package/dist/ui/state/use-terminal-width.js +22 -0
- package/dist/ui/state/use-terminal-width.js.map +1 -0
- package/dist/ui/state/use-transcript-state.d.ts +21 -0
- package/dist/ui/state/use-transcript-state.d.ts.map +1 -0
- package/dist/ui/state/use-transcript-state.js +49 -0
- package/dist/ui/state/use-transcript-state.js.map +1 -0
- package/dist/ui/status-bar.d.ts +4 -1
- package/dist/ui/status-bar.d.ts.map +1 -1
- package/dist/ui/status-bar.js +13 -3
- package/dist/ui/status-bar.js.map +1 -1
- package/dist/ui/types.d.ts +3 -0
- package/dist/ui/types.d.ts.map +1 -1
- package/dist/version-check.d.ts.map +1 -1
- package/dist/version-check.js +10 -0
- package/dist/version-check.js.map +1 -1
- package/package.json +12 -8
- package/dist/agents/agent-config-loader.d.ts +0 -60
- package/dist/agents/agent-config-loader.d.ts.map +0 -1
- package/dist/agents/agent-config-loader.js +0 -402
- package/dist/agents/agent-config-loader.js.map +0 -1
- package/dist/agents/agent-config-presets.d.ts +0 -10
- package/dist/agents/agent-config-presets.d.ts.map +0 -1
- package/dist/agents/agent-config-presets.js +0 -940
- package/dist/agents/agent-config-presets.js.map +0 -1
- package/dist/agents/agent-config-types.d.ts +0 -145
- package/dist/agents/agent-config-types.d.ts.map +0 -1
- package/dist/agents/agent-config-types.js +0 -12
- package/dist/agents/agent-config-types.js.map +0 -1
- package/dist/agents/agent-detection.d.ts +0 -35
- package/dist/agents/agent-detection.d.ts.map +0 -1
- package/dist/agents/agent-detection.js +0 -110
- package/dist/agents/agent-detection.js.map +0 -1
- package/dist/agents/aggregator.d.ts +0 -19
- package/dist/agents/aggregator.d.ts.map +0 -1
- package/dist/agents/aggregator.js +0 -141
- package/dist/agents/aggregator.js.map +0 -1
- package/dist/agents/cli-backend.d.ts +0 -32
- package/dist/agents/cli-backend.d.ts.map +0 -1
- package/dist/agents/cli-backend.js +0 -271
- package/dist/agents/cli-backend.js.map +0 -1
- package/dist/agents/council-types.d.ts +0 -115
- package/dist/agents/council-types.d.ts.map +0 -1
- package/dist/agents/council-types.js +0 -81
- package/dist/agents/council-types.js.map +0 -1
- package/dist/agents/council.d.ts +0 -112
- package/dist/agents/council.d.ts.map +0 -1
- package/dist/agents/council.js +0 -722
- package/dist/agents/council.js.map +0 -1
- package/dist/agents/decomposer.d.ts +0 -33
- package/dist/agents/decomposer.d.ts.map +0 -1
- package/dist/agents/decomposer.js +0 -138
- package/dist/agents/decomposer.js.map +0 -1
- package/dist/agents/dynamic-tools.d.ts +0 -52
- package/dist/agents/dynamic-tools.d.ts.map +0 -1
- package/dist/agents/dynamic-tools.js +0 -395
- package/dist/agents/dynamic-tools.js.map +0 -1
- package/dist/agents/index.d.ts +0 -29
- package/dist/agents/index.d.ts.map +0 -1
- package/dist/agents/index.js +0 -29
- package/dist/agents/index.js.map +0 -1
- package/dist/agents/installer.d.ts +0 -39
- package/dist/agents/installer.d.ts.map +0 -1
- package/dist/agents/installer.js +0 -205
- package/dist/agents/installer.js.map +0 -1
- package/dist/agents/orchestrator.d.ts +0 -106
- package/dist/agents/orchestrator.d.ts.map +0 -1
- package/dist/agents/orchestrator.js +0 -363
- package/dist/agents/orchestrator.js.map +0 -1
- package/dist/agents/sdk-backend.d.ts +0 -63
- package/dist/agents/sdk-backend.d.ts.map +0 -1
- package/dist/agents/sdk-backend.js +0 -489
- package/dist/agents/sdk-backend.js.map +0 -1
- package/dist/agents/swarm-types.d.ts +0 -84
- package/dist/agents/swarm-types.d.ts.map +0 -1
- package/dist/agents/swarm-types.js +0 -20
- package/dist/agents/swarm-types.js.map +0 -1
- package/dist/agents/swarm.d.ts +0 -79
- package/dist/agents/swarm.d.ts.map +0 -1
- package/dist/agents/swarm.js +0 -375
- package/dist/agents/swarm.js.map +0 -1
- package/dist/agents/tools.d.ts +0 -26
- package/dist/agents/tools.d.ts.map +0 -1
- package/dist/agents/tools.js +0 -776
- package/dist/agents/tools.js.map +0 -1
- package/dist/agents/types.d.ts +0 -108
- package/dist/agents/types.d.ts.map +0 -1
- package/dist/agents/types.js +0 -43
- package/dist/agents/types.js.map +0 -1
- package/dist/api-server.d.ts +0 -36
- package/dist/api-server.d.ts.map +0 -1
- package/dist/api-server.js +0 -255
- package/dist/api-server.js.map +0 -1
- package/dist/auto-checkpoint.d.ts +0 -35
- package/dist/auto-checkpoint.d.ts.map +0 -1
- package/dist/auto-checkpoint.js +0 -143
- package/dist/auto-checkpoint.js.map +0 -1
- package/dist/background-jobs.d.ts +0 -45
- package/dist/background-jobs.d.ts.map +0 -1
- package/dist/background-jobs.js +0 -127
- package/dist/background-jobs.js.map +0 -1
- package/dist/branching.d.ts +0 -56
- package/dist/branching.d.ts.map +0 -1
- package/dist/branching.js +0 -211
- package/dist/branching.js.map +0 -1
- package/dist/cli/agent.d.ts +0 -9
- package/dist/cli/agent.d.ts.map +0 -1
- package/dist/cli/agent.js +0 -314
- package/dist/cli/agent.js.map +0 -1
- package/dist/cli/commands.d.ts +0 -12
- package/dist/cli/commands.d.ts.map +0 -1
- package/dist/cli/commands.js +0 -943
- package/dist/cli/commands.js.map +0 -1
- package/dist/cli/index.d.ts +0 -8
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js +0 -237
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/types.d.ts +0 -33
- package/dist/cli/types.d.ts.map +0 -1
- package/dist/cli/types.js +0 -21
- package/dist/cli/types.js.map +0 -1
- package/dist/companions.d.ts +0 -54
- package/dist/companions.d.ts.map +0 -1
- package/dist/companions.js +0 -440
- package/dist/companions.js.map +0 -1
- package/dist/completion.d.ts +0 -75
- package/dist/completion.d.ts.map +0 -1
- package/dist/completion.js +0 -234
- package/dist/completion.js.map +0 -1
- package/dist/file-watcher.d.ts +0 -91
- package/dist/file-watcher.d.ts.map +0 -1
- package/dist/file-watcher.js +0 -269
- package/dist/file-watcher.js.map +0 -1
- package/dist/hud/theme-packs/api.d.ts +0 -51
- package/dist/hud/theme-packs/api.d.ts.map +0 -1
- package/dist/hud/theme-packs/api.js +0 -145
- package/dist/hud/theme-packs/api.js.map +0 -1
- package/dist/hud/theme-packs/index.d.ts +0 -18
- package/dist/hud/theme-packs/index.d.ts.map +0 -1
- package/dist/hud/theme-packs/index.js +0 -38
- package/dist/hud/theme-packs/index.js.map +0 -1
- package/dist/hud/theme-packs/types.d.ts +0 -29
- package/dist/hud/theme-packs/types.d.ts.map +0 -1
- package/dist/hud/theme-packs/types.js +0 -9
- package/dist/hud/theme-packs/types.js.map +0 -1
- package/dist/idle-eviction.d.ts +0 -34
- package/dist/idle-eviction.d.ts.map +0 -1
- package/dist/idle-eviction.js +0 -78
- package/dist/idle-eviction.js.map +0 -1
- package/dist/keyboard.d.ts +0 -57
- package/dist/keyboard.d.ts.map +0 -1
- package/dist/keyboard.js +0 -265
- package/dist/keyboard.js.map +0 -1
- package/dist/model-router.d.ts +0 -67
- package/dist/model-router.d.ts.map +0 -1
- package/dist/model-router.js.map +0 -1
- package/dist/providers/openai-compat-shims.d.ts +0 -31
- package/dist/providers/openai-compat-shims.d.ts.map +0 -1
- package/dist/providers/openai-compat-shims.js +0 -179
- package/dist/providers/openai-compat-shims.js.map +0 -1
- package/dist/sandbox-native.d.ts.map +0 -1
- package/dist/sandbox-native.js.map +0 -1
- package/dist/sandbox.d.ts.map +0 -1
- package/dist/sandbox.js.map +0 -1
- package/dist/session-timeout.d.ts +0 -31
- package/dist/session-timeout.d.ts.map +0 -1
- package/dist/session-timeout.js +0 -100
- package/dist/session-timeout.js.map +0 -1
- package/dist/smart-router.d.ts +0 -73
- package/dist/smart-router.d.ts.map +0 -1
- package/dist/smart-router.js +0 -332
- package/dist/smart-router.js.map +0 -1
- package/dist/terminal-recording.d.ts +0 -55
- package/dist/terminal-recording.d.ts.map +0 -1
- package/dist/terminal-recording.js +0 -182
- package/dist/terminal-recording.js.map +0 -1
- package/dist/tmux.d.ts +0 -35
- package/dist/tmux.d.ts.map +0 -1
- package/dist/tmux.js +0 -106
- package/dist/tmux.js.map +0 -1
- package/dist/ui/modals.d.ts.map +0 -1
- package/dist/ui/modals.js.map +0 -1
- package/dist/ui/pack-picker.d.ts +0 -12
- package/dist/ui/pack-picker.d.ts.map +0 -1
- package/dist/ui/pack-picker.js +0 -101
- package/dist/ui/pack-picker.js.map +0 -1
- package/dist/ui/theme-picker.d.ts +0 -24
- package/dist/ui/theme-picker.d.ts.map +0 -1
- package/dist/ui/theme-picker.js +0 -190
- package/dist/ui/theme-picker.js.map +0 -1
package/dist/ui/agent.js
CHANGED
|
@@ -15,16 +15,21 @@ import { formatError, classifyError } from '../errors.js';
|
|
|
15
15
|
import { getAvailableProviders } from '../providers/index.js';
|
|
16
16
|
import * as storage from '../storage.js';
|
|
17
17
|
import * as hooks from '../hooks.js';
|
|
18
|
-
import * as
|
|
18
|
+
import * as router from '../router.js';
|
|
19
|
+
import { fleetActive, fleetMirrorAssistant } from '../fleet.js';
|
|
19
20
|
import * as summarization from '../summarization.js';
|
|
21
|
+
import { autoCompress } from '../auto-compressor.js';
|
|
22
|
+
import { createStreamFlusher } from '../streaming.js';
|
|
20
23
|
import { executeParallel, getParallelizationStats } from '../parallel-tools.js';
|
|
21
|
-
import { setMood } from '../companions.js';
|
|
22
24
|
import { checkAndWarnContextLimit } from './context.js';
|
|
23
|
-
import { smartRoute } from '../
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
25
|
+
import { smartRoute } from '../router.js';
|
|
26
|
+
import { isLocalBackend, detectMalformedToolCall, buildRepairMessage, buildToolCallEnvelopeSchema, extractRepairedToolCall, getLocalModelProfile, } from '../local-model.js';
|
|
27
|
+
import { shouldCheckpoint, createCheckpoint } from '../checkpoint.js';
|
|
26
28
|
import { startPreventSleep, stopPreventSleep } from '../prevent-sleep.js';
|
|
27
29
|
import { resolveIterationLimit, formatIterationProgress, isFiniteIterationLimit, } from '../iteration-limit.js';
|
|
30
|
+
import { RunLog } from '../runlog.js';
|
|
31
|
+
import { getBudgetCaps, evaluateBudget, hasBudgetCaps, recordProjectSpend, loadProjectSpend, formatBudgetHalt, } from '../budget.js';
|
|
32
|
+
import { evaluatePolicy, isPolicyEnabled } from '../policy.js';
|
|
28
33
|
// ============================================================================
|
|
29
34
|
// Tool Result Truncation
|
|
30
35
|
// ============================================================================
|
|
@@ -99,15 +104,116 @@ export function validateAndRepairMessagesImpl(ctx) {
|
|
|
99
104
|
return repaired;
|
|
100
105
|
}
|
|
101
106
|
// ============================================================================
|
|
107
|
+
// Local-model tool-call repair (features 2 & 3)
|
|
108
|
+
// ============================================================================
|
|
109
|
+
/**
|
|
110
|
+
* When a LOCAL backend returns a malformed tool call, spend ONE corrective
|
|
111
|
+
* round-trip trying to fix it before the error surfaces naturally through
|
|
112
|
+
* executeTool. At most one call is repaired per response (cost control on
|
|
113
|
+
* self-hosted hardware); each call id gets at most one repair (`repairedCallIds`).
|
|
114
|
+
*
|
|
115
|
+
* On Ollama we grammar-constrain the repair reply with the tool-call envelope
|
|
116
|
+
* schema (the `format` param) — but only here, never on normal prose turns.
|
|
117
|
+
*
|
|
118
|
+
* Cloud backends and well-formed responses pass straight through unchanged.
|
|
119
|
+
*/
|
|
120
|
+
async function maybeRepairLocalToolCalls(ctx, baseMessages, response, effectiveModel, repairedCallIds) {
|
|
121
|
+
if (!response.toolCalls?.length)
|
|
122
|
+
return response;
|
|
123
|
+
const provider = ctx.actualProvider;
|
|
124
|
+
if (!isLocalBackend(provider))
|
|
125
|
+
return response;
|
|
126
|
+
const tools = getTools();
|
|
127
|
+
let target;
|
|
128
|
+
for (const call of response.toolCalls) {
|
|
129
|
+
if (repairedCallIds.has(call.id))
|
|
130
|
+
continue;
|
|
131
|
+
const fault = detectMalformedToolCall(call, tools);
|
|
132
|
+
if (fault) {
|
|
133
|
+
target = { call, reason: fault.reason };
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (!target)
|
|
138
|
+
return response;
|
|
139
|
+
const { call, reason } = target;
|
|
140
|
+
repairedCallIds.add(call.id); // one repair per tool call, whatever the outcome
|
|
141
|
+
ctx.debugLog('repair', 'malformed local tool call', call.name, reason);
|
|
142
|
+
ctx.addMessage('system', `🔧 Malformed tool call from local model (${reason}); attempting one repair…`);
|
|
143
|
+
// Grammar-constrain the reply when the backend supports JSON-schema format.
|
|
144
|
+
let format;
|
|
145
|
+
try {
|
|
146
|
+
const profile = await getLocalModelProfile(provider, effectiveModel || ctx.actualModel);
|
|
147
|
+
if (profile.supportsJsonSchemaFormat) {
|
|
148
|
+
format = buildToolCallEnvelopeSchema(tools.map(t => t.name));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// No format constraint — the corrective message alone still helps.
|
|
153
|
+
}
|
|
154
|
+
const repairMessages = [
|
|
155
|
+
...baseMessages,
|
|
156
|
+
{ role: 'assistant', content: typeof response.content === 'string' ? response.content : '', toolCalls: response.toolCalls },
|
|
157
|
+
{ role: 'user', content: buildRepairMessage(call, { reason }) },
|
|
158
|
+
];
|
|
159
|
+
let repaired;
|
|
160
|
+
try {
|
|
161
|
+
repaired = await chat(ctx.provider, repairMessages, tools, effectiveModel, undefined, undefined, format ? { format } : undefined);
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
ctx.ledger?.recordAction('repair', { tool: call.name }, 'error', reason);
|
|
165
|
+
ctx.debugLog('repair', 'repair round-trip threw', err instanceof Error ? err.message : String(err));
|
|
166
|
+
return response; // let the original error surface through executeTool
|
|
167
|
+
}
|
|
168
|
+
const corrected = extractRepairedToolCall(typeof repaired.content === 'string' ? repaired.content : '', repaired.toolCalls, call.id);
|
|
169
|
+
if (!corrected) {
|
|
170
|
+
ctx.ledger?.recordAction('repair', { tool: call.name }, 'error', reason);
|
|
171
|
+
ctx.addMessage('system', '🔧 Repair produced no usable tool call; surfacing the original error.');
|
|
172
|
+
return response;
|
|
173
|
+
}
|
|
174
|
+
const stillBad = detectMalformedToolCall(corrected, tools);
|
|
175
|
+
ctx.ledger?.recordAction('repair', { tool: call.name, to: corrected.name }, stillBad ? 'error' : 'ok', stillBad?.reason);
|
|
176
|
+
ctx.addMessage('system', stillBad
|
|
177
|
+
? '🔧 Repair did not resolve the malformed call; surfacing the error.'
|
|
178
|
+
: `🔧 Repaired tool call → ${corrected.name}`);
|
|
179
|
+
return { ...response, toolCalls: response.toolCalls.map(tc => (tc.id === call.id ? corrected : tc)) };
|
|
180
|
+
}
|
|
181
|
+
// ============================================================================
|
|
182
|
+
// Provider warnings
|
|
183
|
+
// ============================================================================
|
|
184
|
+
// Provider warnings already surfaced this process, keyed by sessionId::warning,
|
|
185
|
+
// so a repeated model substitution across turns/iterations isn't shown twice
|
|
186
|
+
// (#217). Non-fatal notices must be surfaced, never swallowed.
|
|
187
|
+
const surfacedWarnings = new Set();
|
|
188
|
+
function surfaceResponseWarnings(ctx, response) {
|
|
189
|
+
if (!response.warnings?.length)
|
|
190
|
+
return;
|
|
191
|
+
const sessionId = ctx.sessionRef.current?.id ?? 'session_adhoc';
|
|
192
|
+
for (const warning of response.warnings) {
|
|
193
|
+
const key = `${sessionId}::${warning}`;
|
|
194
|
+
if (surfacedWarnings.has(key))
|
|
195
|
+
continue;
|
|
196
|
+
surfacedWarnings.add(key);
|
|
197
|
+
ctx.addMessage('system', warning);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// ============================================================================
|
|
102
201
|
// Run Agent
|
|
103
202
|
// ============================================================================
|
|
104
203
|
/**
|
|
105
204
|
* Run agent with user prompt. Core execution loop that handles LLM calls,
|
|
106
205
|
* tool execution (parallel + sequential), auto-compaction, and queued messages.
|
|
107
206
|
*/
|
|
207
|
+
// #231: remember the previous turn's mode so the first work-mode turn after
|
|
208
|
+
// a plan-mode turn can carry an explicit execution directive. Module scope is
|
|
209
|
+
// intentional — mode itself outlives session resets.
|
|
210
|
+
let previousTurnMode = null;
|
|
211
|
+
/** Test seam: clear the plan-to-work transition tracking. */
|
|
212
|
+
export function _resetModeTracking() {
|
|
213
|
+
previousTurnMode = null;
|
|
214
|
+
}
|
|
108
215
|
export async function runAgentImpl(ctx, content) {
|
|
109
216
|
ctx.debugLog('runAgent', 'ENTER', typeof content === 'string' ? content.substring(0, 50) : '[complex]');
|
|
110
|
-
setMood('thinking');
|
|
111
217
|
// Validate message history before adding new content
|
|
112
218
|
ctx.validateAndRepairMessages();
|
|
113
219
|
ctx.llmMessages.current.push({ role: 'user', content });
|
|
@@ -128,7 +234,7 @@ export async function runAgentImpl(ctx, content) {
|
|
|
128
234
|
}
|
|
129
235
|
}
|
|
130
236
|
else if (ctx.autoRoute && typeof content === 'string') {
|
|
131
|
-
const routeDecision =
|
|
237
|
+
const routeDecision = router.routeRequest(content, ctx.provider, {
|
|
132
238
|
messageCount: ctx.stats.messageCount,
|
|
133
239
|
hasCode: content.includes('```') || /\.(ts|js|py|go|rs|java)/.test(content),
|
|
134
240
|
});
|
|
@@ -150,6 +256,57 @@ export async function runAgentImpl(ctx, content) {
|
|
|
150
256
|
: undefined;
|
|
151
257
|
let runStatus;
|
|
152
258
|
let runErrorSummary;
|
|
259
|
+
// Local-backend behaviours (features 2 & 4): whether to append anchor hashes
|
|
260
|
+
// to read_file output, and which tool-call ids have already spent their one
|
|
261
|
+
// repair round-trip.
|
|
262
|
+
const localBackend = isLocalBackend(ctx.actualProvider);
|
|
263
|
+
const executeOptions = { appendAnchorHash: localBackend };
|
|
264
|
+
const repairedCallIds = new Set();
|
|
265
|
+
// Governance (#189): audit run log, budget caps, policy hook. The run log is a
|
|
266
|
+
// per-session append-only JSONL trace; each agent turn is one run within it.
|
|
267
|
+
const sessionId = ctx.sessionRef.current?.id ?? 'session_adhoc';
|
|
268
|
+
const projectDir = ctx.sessionRef.current?.projectPath ?? process.cwd();
|
|
269
|
+
const runlog = RunLog.open(sessionId);
|
|
270
|
+
const budgetCaps = getBudgetCaps();
|
|
271
|
+
// Only maintain the cross-run project ledger when a project cap is active.
|
|
272
|
+
const trackProjectBudget = typeof budgetCaps.maxCostPerProject === 'number';
|
|
273
|
+
const runStartedAt = Date.now();
|
|
274
|
+
let runInputTokens = 0;
|
|
275
|
+
let runOutputTokens = 0;
|
|
276
|
+
let runCostUsd = 0;
|
|
277
|
+
let runToolCalls = 0;
|
|
278
|
+
let runEnded = false;
|
|
279
|
+
let runExitReason;
|
|
280
|
+
let budgetVerdict;
|
|
281
|
+
runlog.runStart({
|
|
282
|
+
session: sessionId,
|
|
283
|
+
cwd: projectDir,
|
|
284
|
+
provider: effectiveProvider,
|
|
285
|
+
model: effectiveModel || ctx.actualModel,
|
|
286
|
+
config: config.getConfig(),
|
|
287
|
+
});
|
|
288
|
+
runlog.userPrompt(summarizeMessageContent(content));
|
|
289
|
+
// #231: a terse reply right after leaving plan mode is an approval, not a
|
|
290
|
+
// conversation — bind it to execution explicitly or weak models answer
|
|
291
|
+
// "done" without doing anything.
|
|
292
|
+
const justLeftPlanMode = previousTurnMode === 'plan' && ctx.mode !== 'plan';
|
|
293
|
+
previousTurnMode = ctx.mode;
|
|
294
|
+
const finalizeRun = (exitReason) => {
|
|
295
|
+
if (runEnded)
|
|
296
|
+
return;
|
|
297
|
+
runEnded = true;
|
|
298
|
+
runlog.runEnd({
|
|
299
|
+
totals: {
|
|
300
|
+
inputTokens: runInputTokens,
|
|
301
|
+
outputTokens: runOutputTokens,
|
|
302
|
+
cost: runCostUsd,
|
|
303
|
+
toolCalls: runToolCalls,
|
|
304
|
+
durationMs: Date.now() - runStartedAt,
|
|
305
|
+
},
|
|
306
|
+
exitReason,
|
|
307
|
+
});
|
|
308
|
+
void runlog.flush();
|
|
309
|
+
};
|
|
153
310
|
// Check context limit and warn if approaching capacity
|
|
154
311
|
// Uses model's actual context length from API when available
|
|
155
312
|
let currentContextTokens = ctx.estimateContextTokens();
|
|
@@ -157,36 +314,18 @@ export async function runAgentImpl(ctx, content) {
|
|
|
157
314
|
let contextPercentage = (currentContextTokens / modelLimit) * 100;
|
|
158
315
|
// Adaptive preserveRecent: small models keep fewer messages to leave room for output
|
|
159
316
|
const preserveRecent = modelLimit < 8000 ? 2 : modelLimit < 16000 ? 4 : modelLimit < 32000 ? 6 : modelLimit < 64000 ? 10 : 15;
|
|
160
|
-
// Auto-compact
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
ctx.llmMessages.current = result.messages;
|
|
169
|
-
currentContextTokens = ctx.estimateContextTokens();
|
|
170
|
-
contextPercentage = (currentContextTokens / modelLimit) * 100;
|
|
171
|
-
ctx.setContextTokens(currentContextTokens);
|
|
172
|
-
ctx.addMessage('system', `✓ Compacted ${result.summarizedCount} messages. Now at ${Math.round(contextPercentage)}% (${Math.round(currentContextTokens / 1000)}K/${Math.round(modelLimit / 1000)}K)`);
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
// If compaction didn't help enough, force-trim old messages
|
|
176
|
-
if (contextPercentage > 98) {
|
|
177
|
-
const systemMsgs = ctx.llmMessages.current.filter(m => m.role === 'system');
|
|
178
|
-
const recentMsgs = ctx.llmMessages.current.filter(m => m.role !== 'system').slice(-5);
|
|
179
|
-
ctx.llmMessages.current = [...systemMsgs, ...recentMsgs];
|
|
180
|
-
currentContextTokens = ctx.estimateContextTokens();
|
|
181
|
-
contextPercentage = (currentContextTokens / modelLimit) * 100;
|
|
182
|
-
ctx.setContextTokens(currentContextTokens);
|
|
183
|
-
ctx.addMessage('system', `⚠️ Force-trimmed to last 5 messages (${Math.round(contextPercentage)}%). Use /clear for a full reset.`);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
317
|
+
// Auto-compact using the new auto-compressor
|
|
318
|
+
const autoCompressResult = await autoCompress(ctx.llmMessages.current, modelLimit, ctx.provider, effectiveModel);
|
|
319
|
+
if (autoCompressResult.compressed) {
|
|
320
|
+
ctx.llmMessages.current = autoCompressResult.messages;
|
|
321
|
+
currentContextTokens = ctx.estimateContextTokens();
|
|
322
|
+
contextPercentage = (currentContextTokens / modelLimit) * 100;
|
|
323
|
+
ctx.setContextTokens(currentContextTokens);
|
|
324
|
+
ctx.addMessage('system', `🔄 Auto-compressed ${autoCompressResult.summarizedCount} messages using ${autoCompressResult.method} (${Math.round(autoCompressResult.originalTokens / 1000)}K → ${Math.round(autoCompressResult.compressedTokens / 1000)}K tokens)`);
|
|
186
325
|
}
|
|
187
326
|
else if (contextPercentage > 65) {
|
|
188
327
|
ctx.addMessage('system', `⚠️ Context at ${Math.round(contextPercentage)}% capacity (${Math.round(currentContextTokens / 1000)}K/${Math.round(modelLimit / 1000)}K tokens)
|
|
189
|
-
Consider: /
|
|
328
|
+
Consider: /compact | /clear | shorter messages`);
|
|
190
329
|
}
|
|
191
330
|
// Inject failed approaches into context so the agent avoids repeating mistakes
|
|
192
331
|
if (ctx.ledger) {
|
|
@@ -203,20 +342,21 @@ export async function runAgentImpl(ctx, content) {
|
|
|
203
342
|
// Safety check at start of each iteration - context may have grown from tool results
|
|
204
343
|
if (i > 0) {
|
|
205
344
|
const iterContextTokens = ctx.estimateContextTokens();
|
|
206
|
-
const
|
|
207
|
-
if (
|
|
208
|
-
ctx.
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
preserveRecent,
|
|
212
|
-
});
|
|
213
|
-
if (result.summarizedCount > 0) {
|
|
214
|
-
ctx.llmMessages.current = result.messages;
|
|
215
|
-
ctx.setContextTokens(ctx.estimateContextTokens());
|
|
216
|
-
ctx.addMessage('system', `✓ Compacted ${result.summarizedCount} messages during iteration ${i + 1}`);
|
|
217
|
-
}
|
|
345
|
+
const iterAutoCompressResult = await autoCompress(ctx.llmMessages.current, modelLimit, ctx.provider, effectiveModel);
|
|
346
|
+
if (iterAutoCompressResult.compressed) {
|
|
347
|
+
ctx.llmMessages.current = iterAutoCompressResult.messages;
|
|
348
|
+
ctx.setContextTokens(ctx.estimateContextTokens());
|
|
349
|
+
ctx.addMessage('system', `🔄 Auto-compressed ${iterAutoCompressResult.summarizedCount} messages during iteration ${i + 1} (${iterAutoCompressResult.method})`);
|
|
218
350
|
}
|
|
219
351
|
}
|
|
352
|
+
// Frame-bounded streaming: coalesce provider tokens so setStreamingResponse
|
|
353
|
+
// fires at most ~30fps instead of once per token (which re-rendered the
|
|
354
|
+
// transcript region on every token). The flusher is per-iteration; its
|
|
355
|
+
// pending timer is drained by flush() on success and cleared by destroy()
|
|
356
|
+
// on the error/cancel path below.
|
|
357
|
+
const streamFlusher = createStreamFlusher((delta) => {
|
|
358
|
+
ctx.setStreamingResponse(prev => prev + delta);
|
|
359
|
+
});
|
|
220
360
|
try {
|
|
221
361
|
// Update thinking state for LLM call
|
|
222
362
|
ctx.setThinkingState({
|
|
@@ -230,10 +370,15 @@ export async function runAgentImpl(ctx, content) {
|
|
|
230
370
|
target: `iteration ${i + 1}`,
|
|
231
371
|
startTime: Date.now(),
|
|
232
372
|
});
|
|
233
|
-
// Streaming callback for final response
|
|
373
|
+
// Streaming callback for final response. Clear the thinking indicator on
|
|
374
|
+
// the first token, then hand the token to the frame-bounded flusher.
|
|
375
|
+
let streamStarted = false;
|
|
234
376
|
const onToken = (token) => {
|
|
235
|
-
|
|
236
|
-
|
|
377
|
+
if (!streamStarted) {
|
|
378
|
+
ctx.setThinkingState(null); // Clear thinking when streaming starts
|
|
379
|
+
streamStarted = true;
|
|
380
|
+
}
|
|
381
|
+
streamFlusher.push(token);
|
|
237
382
|
};
|
|
238
383
|
// Retry callback for error recovery
|
|
239
384
|
const onRetry = (attempt, error, delayMs) => {
|
|
@@ -251,8 +396,36 @@ export async function runAgentImpl(ctx, content) {
|
|
|
251
396
|
ctx.debugLog('chat', 'CLEANED orphaned tool results', `removed=${ctx.llmMessages.current.length - validatedMessages.length}`);
|
|
252
397
|
ctx.llmMessages.current = validatedMessages;
|
|
253
398
|
}
|
|
399
|
+
// Plan mode: tell the model it is planning and that plans need receipts.
|
|
400
|
+
// Injected per-request (not persisted) so mode switches take effect
|
|
401
|
+
// immediately and history stays clean.
|
|
402
|
+
if (ctx.mode === 'plan') {
|
|
403
|
+
validatedMessages = [
|
|
404
|
+
...validatedMessages,
|
|
405
|
+
{
|
|
406
|
+
role: 'system',
|
|
407
|
+
content: 'You are in PLAN mode: no mutating tools will execute. '
|
|
408
|
+
+ 'Read-only tools (read_file, list_files, think, create_plan, ask_question) ARE available — use them. '
|
|
409
|
+
+ 'Before proposing any plan, read the files you intend to change and cite file:line for every claim. '
|
|
410
|
+
+ 'State explicitly what you verified versus what you assume. '
|
|
411
|
+
+ 'A plan produced without reading anything will be marked unverified.',
|
|
412
|
+
},
|
|
413
|
+
];
|
|
414
|
+
}
|
|
415
|
+
if (justLeftPlanMode && i === 0) {
|
|
416
|
+
validatedMessages = [
|
|
417
|
+
...validatedMessages,
|
|
418
|
+
{
|
|
419
|
+
role: 'system',
|
|
420
|
+
content: 'The user has just switched from plan mode to work mode and replied. '
|
|
421
|
+
+ 'Treat their message as approval to execute the plan discussed above. '
|
|
422
|
+
+ 'Carry it out now using tools, step by step. '
|
|
423
|
+
+ 'Never state that work is done unless you performed it with tool calls in this turn.',
|
|
424
|
+
},
|
|
425
|
+
];
|
|
426
|
+
}
|
|
254
427
|
// Pre-request summarization check - summarize BEFORE sending if context is too large
|
|
255
|
-
const tools = getTools(
|
|
428
|
+
const tools = getTools();
|
|
256
429
|
const contextCheck = estimateContextUsage(ctx.provider, effectiveModel || DEFAULT_MODELS[ctx.provider], validatedMessages, tools);
|
|
257
430
|
ctx.debugLog('chat', 'CONTEXT CHECK', `estimated=${contextCheck.estimated}, limit=${contextCheck.limit}, percent=${contextCheck.percent}%`);
|
|
258
431
|
if (contextCheck.needsSummarization) {
|
|
@@ -264,8 +437,16 @@ export async function runAgentImpl(ctx, content) {
|
|
|
264
437
|
ctx.debugLog('chat', 'PRE-SUMMARIZED', `removed=${result.summarizedCount} messages, reduced from ${result.originalTokens} to ${result.reducedTokens}`);
|
|
265
438
|
}
|
|
266
439
|
}
|
|
267
|
-
|
|
440
|
+
let response = await chat(ctx.provider, validatedMessages, tools, effectiveModel, onToken, onRetry);
|
|
441
|
+
streamFlusher.flush(); // drain any batched tail before we swap to history
|
|
268
442
|
ctx.debugLog('chat', 'GOT response', `toolCalls=${response.toolCalls?.length ?? 0}`);
|
|
443
|
+
// Local backends: give a malformed tool call ONE corrective round-trip
|
|
444
|
+
// before it surfaces as an execution error (features 2 & 3). No-op for
|
|
445
|
+
// cloud providers and well-formed responses.
|
|
446
|
+
response = await maybeRepairLocalToolCalls(ctx, validatedMessages, response, effectiveModel, repairedCallIds);
|
|
447
|
+
// Surface provider warnings (e.g. Ollama model substitution) once per
|
|
448
|
+
// unique message per session — never swallow them (#217).
|
|
449
|
+
surfaceResponseWarnings(ctx, response);
|
|
269
450
|
// Update token stats and cost
|
|
270
451
|
if (response.usage) {
|
|
271
452
|
const usageCost = calculateCost(ctx.model || DEFAULT_MODELS[ctx.provider], response.usage.inputTokens, response.usage.outputTokens);
|
|
@@ -290,6 +471,33 @@ export async function runAgentImpl(ctx, content) {
|
|
|
290
471
|
}
|
|
291
472
|
}
|
|
292
473
|
}
|
|
474
|
+
// Governance: accrue run spend, audit the assistant turn, evaluate budget.
|
|
475
|
+
{
|
|
476
|
+
const turnCost = response.usage
|
|
477
|
+
? calculateCost(ctx.model || DEFAULT_MODELS[ctx.provider], response.usage.inputTokens, response.usage.outputTokens)
|
|
478
|
+
: 0;
|
|
479
|
+
if (response.usage) {
|
|
480
|
+
runInputTokens += response.usage.inputTokens;
|
|
481
|
+
runOutputTokens += response.usage.outputTokens;
|
|
482
|
+
runCostUsd += turnCost;
|
|
483
|
+
if (trackProjectBudget)
|
|
484
|
+
recordProjectSpend(projectDir, turnCost);
|
|
485
|
+
}
|
|
486
|
+
runlog.assistantMessage({
|
|
487
|
+
content: response.content,
|
|
488
|
+
tokens: { input: response.usage?.inputTokens ?? 0, output: response.usage?.outputTokens ?? 0 },
|
|
489
|
+
cost: turnCost,
|
|
490
|
+
});
|
|
491
|
+
if (hasBudgetCaps(budgetCaps)) {
|
|
492
|
+
const verdict = evaluateBudget(budgetCaps, {
|
|
493
|
+
runCostUsd,
|
|
494
|
+
runTokens: runInputTokens + runOutputTokens,
|
|
495
|
+
projectCostUsd: loadProjectSpend(projectDir).spentUsd,
|
|
496
|
+
});
|
|
497
|
+
if (verdict.exceeded)
|
|
498
|
+
budgetVerdict = verdict;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
293
501
|
// Circuit breaker check after each iteration
|
|
294
502
|
if (ctx.circuitBreaker) {
|
|
295
503
|
const iterData = {
|
|
@@ -318,10 +526,16 @@ export async function runAgentImpl(ctx, content) {
|
|
|
318
526
|
content: response.content,
|
|
319
527
|
toolCalls: response.toolCalls,
|
|
320
528
|
});
|
|
529
|
+
// Mirror any text the assistant produced alongside the tool calls
|
|
530
|
+
if (fleetActive() && response.content) {
|
|
531
|
+
fleetMirrorAssistant(response.content);
|
|
532
|
+
}
|
|
321
533
|
const preChecks = [];
|
|
322
534
|
const executableTools = [];
|
|
323
535
|
for (const toolCall of response.toolCalls) {
|
|
324
536
|
const args = toolCall.arguments;
|
|
537
|
+
runlog.toolCall({ id: toolCall.id, name: toolCall.name, args });
|
|
538
|
+
runToolCalls++;
|
|
325
539
|
const toolPreview = String(args.command || args.path || '...');
|
|
326
540
|
const risk = assessToolRisk(toolCall);
|
|
327
541
|
const riskConfig = RISK_CONFIG[risk.level];
|
|
@@ -363,15 +577,38 @@ export async function runAgentImpl(ctx, content) {
|
|
|
363
577
|
ctx.addMessage('tool', `🛑 Blocked by hook: ${preHookResult.reason}`);
|
|
364
578
|
}
|
|
365
579
|
else {
|
|
366
|
-
//
|
|
367
|
-
|
|
368
|
-
|
|
580
|
+
// Pre-tool policy gate (fail closed). Distinct from the hook above:
|
|
581
|
+
// the policy engine receives the tool-call JSON on stdin and denies
|
|
582
|
+
// via a non-zero exit code (see policy.ts / docs/governance.md).
|
|
583
|
+
const policyResult = isPolicyEnabled() ? await evaluatePolicy(toolCall) : undefined;
|
|
584
|
+
if (policyResult) {
|
|
585
|
+
runlog.policyEvent({
|
|
586
|
+
tool: toolCall.name,
|
|
587
|
+
decision: policyResult.decision,
|
|
588
|
+
source: policyResult.source,
|
|
589
|
+
reason: policyResult.reason,
|
|
590
|
+
durationMs: policyResult.durationMs,
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
if (policyResult && policyResult.decision === 'deny') {
|
|
594
|
+
preCheck.blocked = true;
|
|
595
|
+
preCheck.blockReason = 'denied by policy';
|
|
596
|
+
preCheck.blockContent = `[Denied by policy: ${policyResult.reason || 'no reason given'}]`;
|
|
597
|
+
ctx.addMessage('tool', `⚡ ${toolCall.name}: ${toolPreview}${riskDisplay}`);
|
|
598
|
+
ctx.addMessage('tool', `⛨ Denied by policy: ${policyResult.reason || 'no reason given'}`);
|
|
599
|
+
}
|
|
600
|
+
else {
|
|
601
|
+
// Tool can be executed
|
|
602
|
+
executableTools.push(toolCall);
|
|
603
|
+
ctx.addMessage('tool', `⚡ ${toolCall.name}: ${toolPreview}${riskDisplay}`);
|
|
604
|
+
}
|
|
369
605
|
}
|
|
370
606
|
}
|
|
371
607
|
preChecks.push(preCheck);
|
|
372
608
|
// Record blocked tools in ledger and add to LLM messages
|
|
373
609
|
if (preCheck.blocked) {
|
|
374
610
|
ctx.ledger?.recordAction(toolCall.name, args, 'blocked', preCheck.blockReason);
|
|
611
|
+
runlog.toolResult({ id: toolCall.id, result: preCheck.blockContent, isError: true, durationMs: 0 });
|
|
375
612
|
ctx.llmMessages.current.push({
|
|
376
613
|
role: 'tool',
|
|
377
614
|
content: preCheck.blockContent,
|
|
@@ -401,8 +638,8 @@ export async function runAgentImpl(ctx, content) {
|
|
|
401
638
|
// Execute in parallel using dependency-aware staging
|
|
402
639
|
ctx.debugLog('tools', 'PARALLEL exec start', `count=${executableTools.length}`);
|
|
403
640
|
const results = await executeParallel(executableTools, async (call) => {
|
|
404
|
-
const result = await executeTool(call, ctx.sessionRef.current?.projectPath ?? process.cwd());
|
|
405
|
-
return result.result;
|
|
641
|
+
const result = await executeTool(call, ctx.sessionRef.current?.projectPath ?? process.cwd(), 60000, undefined, executeOptions);
|
|
642
|
+
return { result: result.result, isError: result.isError };
|
|
406
643
|
}, (completed, total, current) => {
|
|
407
644
|
const args = current.arguments;
|
|
408
645
|
const target = args.path || args.command?.substring(0, 30) || current.name;
|
|
@@ -423,10 +660,14 @@ export async function runAgentImpl(ctx, content) {
|
|
|
423
660
|
for (const result of results) {
|
|
424
661
|
const toolCall = result.toolCall;
|
|
425
662
|
const args = toolCall.arguments;
|
|
426
|
-
|
|
427
|
-
|
|
663
|
+
// A tool-level failure (result.isError) or a thrown exception (result.error)
|
|
664
|
+
// both count as a failure — mirror the sequential branch.
|
|
665
|
+
const failed = result.isError || !!result.error;
|
|
666
|
+
const failureText = result.error || result.result;
|
|
428
667
|
// Record in iteration ledger
|
|
429
|
-
ctx.ledger?.recordAction(toolCall.name, args,
|
|
668
|
+
ctx.ledger?.recordAction(toolCall.name, args, failed ? 'error' : 'ok', failed ? failureText : undefined);
|
|
669
|
+
// Audit the tool result (parallel exec has no per-call duration).
|
|
670
|
+
runlog.toolResult({ id: toolCall.id, result: failed ? failureText : result.result, isError: failed, durationMs: 0 });
|
|
430
671
|
// Execute post-tool hooks
|
|
431
672
|
hooks.executeHooks('post-tool', {
|
|
432
673
|
tool: toolCall.name,
|
|
@@ -441,11 +682,11 @@ export async function runAgentImpl(ctx, content) {
|
|
|
441
682
|
ctx.addMessage('tool', thought);
|
|
442
683
|
}
|
|
443
684
|
else if (result.error) {
|
|
444
|
-
ctx.addMessage('tool', `Error: ${result.error}
|
|
685
|
+
ctx.addMessage('tool', `Error: ${result.error}`, true);
|
|
445
686
|
}
|
|
446
687
|
else {
|
|
447
688
|
const preview = result.result.split('\n').slice(0, 3).join('\n');
|
|
448
|
-
ctx.addMessage('tool', preview + (result.result.split('\n').length > 3 ? '\n...' : ''));
|
|
689
|
+
ctx.addMessage('tool', preview + (result.result.split('\n').length > 3 ? '\n...' : ''), failed);
|
|
449
690
|
}
|
|
450
691
|
ctx.llmMessages.current.push({
|
|
451
692
|
role: 'tool',
|
|
@@ -498,12 +739,11 @@ export async function runAgentImpl(ctx, content) {
|
|
|
498
739
|
});
|
|
499
740
|
}
|
|
500
741
|
ctx.debugLog('tools', 'EXEC', toolCall.name, toolPreview.substring(0, 30));
|
|
501
|
-
recordEvent('tool_call', toolCall.name, { name: toolCall.name, arguments: args });
|
|
502
742
|
// Auto-checkpoint before destructive operations
|
|
503
743
|
if (shouldCheckpoint(toolCall.name, args)) {
|
|
504
744
|
const hash = createCheckpoint(toolCall.name, args);
|
|
505
745
|
if (hash) {
|
|
506
|
-
ctx.debugLog('checkpoint', `
|
|
746
|
+
ctx.debugLog('checkpoint', `checkpoint ${hash} before ${toolCall.name}`);
|
|
507
747
|
}
|
|
508
748
|
}
|
|
509
749
|
// Stream shell output in real-time (#15)
|
|
@@ -515,11 +755,13 @@ export async function runAgentImpl(ctx, content) {
|
|
|
515
755
|
detail: chunk.trimEnd().split('\n').pop()?.substring(0, 60),
|
|
516
756
|
});
|
|
517
757
|
} : undefined;
|
|
518
|
-
const
|
|
758
|
+
const seqStart = Date.now();
|
|
759
|
+
const result = await executeTool(toolCall, ctx.sessionRef.current?.projectPath ?? process.cwd(), 60000, shellStreamCallback, executeOptions);
|
|
519
760
|
ctx.debugLog('tools', 'DONE', toolCall.name);
|
|
520
|
-
recordEvent('tool_result', result.result.slice(0, 1000), { name: toolCall.name, isError: result.isError });
|
|
521
761
|
// Record in iteration ledger
|
|
522
762
|
ctx.ledger?.recordAction(toolCall.name, args, result.isError ? 'error' : 'ok', result.isError ? result.result : undefined);
|
|
763
|
+
// Audit the tool result.
|
|
764
|
+
runlog.toolResult({ id: toolCall.id, result: result.result, isError: result.isError || false, durationMs: Date.now() - seqStart });
|
|
523
765
|
// Execute post-tool hooks
|
|
524
766
|
hooks.executeHooks('post-tool', {
|
|
525
767
|
tool: toolCall.name,
|
|
@@ -562,12 +804,12 @@ export async function runAgentImpl(ctx, content) {
|
|
|
562
804
|
if (planReasoning)
|
|
563
805
|
planMsg += `\n ${planReasoning}\n`;
|
|
564
806
|
planMsg += '\n' + planSteps.map((s, idx) => ` ${idx + 1}. [ ] ${s}`).join('\n');
|
|
565
|
-
planMsg += '\n\n
|
|
807
|
+
planMsg += '\n\n Switch to work mode (Shift+Tab) and reply to execute, or give feedback to revise.';
|
|
566
808
|
ctx.addMessage('assistant', planMsg);
|
|
567
809
|
// Tell the LLM to wait for user approval
|
|
568
810
|
ctx.llmMessages.current.push({
|
|
569
811
|
role: 'tool',
|
|
570
|
-
content: '[Plan displayed to user. Waiting for approval. The user will
|
|
812
|
+
content: '[Plan displayed to user. Waiting for approval. The user will reply to approve and execute the plan (they may switch to work mode first), or provide feedback to revise it. Do NOT proceed with execution until the user approves.]',
|
|
571
813
|
toolCallId: toolCall.id,
|
|
572
814
|
});
|
|
573
815
|
// Break out of tool loop - wait for user approval
|
|
@@ -576,7 +818,7 @@ export async function runAgentImpl(ctx, content) {
|
|
|
576
818
|
else {
|
|
577
819
|
const display = result.displayResult || result.result;
|
|
578
820
|
const preview = display.split('\n').slice(0, 5).join('\n');
|
|
579
|
-
ctx.addMessage('tool', preview + (display.split('\n').length > 5 ? '\n...' : ''));
|
|
821
|
+
ctx.addMessage('tool', preview + (display.split('\n').length > 5 ? '\n...' : ''), result.isError);
|
|
580
822
|
}
|
|
581
823
|
if (toolCall.name !== 'ask_question') {
|
|
582
824
|
ctx.llmMessages.current.push({
|
|
@@ -589,6 +831,24 @@ export async function runAgentImpl(ctx, content) {
|
|
|
589
831
|
}
|
|
590
832
|
}
|
|
591
833
|
ctx.ledger?.endIteration();
|
|
834
|
+
// Budget halt: the current tool result is finished; stop before the next
|
|
835
|
+
// provider call. Record a budget_event and summarize spend vs cap.
|
|
836
|
+
if (budgetVerdict?.exceeded) {
|
|
837
|
+
const summary = formatBudgetHalt(budgetVerdict);
|
|
838
|
+
runlog.budgetEvent({
|
|
839
|
+
scope: budgetVerdict.scope ?? 'run',
|
|
840
|
+
kind: budgetVerdict.kind ?? 'cost',
|
|
841
|
+
spent: budgetVerdict.spent ?? 0,
|
|
842
|
+
cap: budgetVerdict.cap ?? 0,
|
|
843
|
+
message: budgetVerdict.message ?? summary,
|
|
844
|
+
});
|
|
845
|
+
ctx.addMessage('system', `‖ ${summary}`);
|
|
846
|
+
runStatus = 'stopped';
|
|
847
|
+
runErrorSummary = summary;
|
|
848
|
+
runExitReason = 'budget';
|
|
849
|
+
completedNaturally = true;
|
|
850
|
+
break;
|
|
851
|
+
}
|
|
592
852
|
if (completedNaturally)
|
|
593
853
|
break; // ask_question pauses for user input (#42)
|
|
594
854
|
continue;
|
|
@@ -597,11 +857,13 @@ export async function runAgentImpl(ctx, content) {
|
|
|
597
857
|
ctx.setThinkingState(null);
|
|
598
858
|
ctx.llmMessages.current.push({ role: 'assistant', content: response.content });
|
|
599
859
|
ctx.addMessage('assistant', response.content);
|
|
600
|
-
|
|
860
|
+
// Mirror assistant message to the fleet channel
|
|
861
|
+
if (fleetActive()) {
|
|
862
|
+
fleetMirrorAssistant(response.content);
|
|
863
|
+
}
|
|
601
864
|
ctx.setStreamingResponse('');
|
|
602
865
|
ctx.setContextTokens(ctx.estimateContextTokens());
|
|
603
866
|
checkAndWarnContextLimit(ctx.actualProvider, ctx.actualModel, ctx.estimateContextTokens(), ctx.addMessage);
|
|
604
|
-
setMood('success');
|
|
605
867
|
// Auto-continue if response was truncated due to length
|
|
606
868
|
if (response.finishReason === 'length') {
|
|
607
869
|
ctx.addMessage('system', '(auto-continuing...)');
|
|
@@ -610,6 +872,20 @@ export async function runAgentImpl(ctx, content) {
|
|
|
610
872
|
}
|
|
611
873
|
completedNaturally = true;
|
|
612
874
|
runStatus = 'completed';
|
|
875
|
+
// The answer is already shown; if this final turn crossed a budget cap,
|
|
876
|
+
// still record it so the audit trail reflects why spend stopped here.
|
|
877
|
+
if (budgetVerdict?.exceeded) {
|
|
878
|
+
const summary = formatBudgetHalt(budgetVerdict);
|
|
879
|
+
runlog.budgetEvent({
|
|
880
|
+
scope: budgetVerdict.scope ?? 'run',
|
|
881
|
+
kind: budgetVerdict.kind ?? 'cost',
|
|
882
|
+
spent: budgetVerdict.spent ?? 0,
|
|
883
|
+
cap: budgetVerdict.cap ?? 0,
|
|
884
|
+
message: budgetVerdict.message ?? summary,
|
|
885
|
+
});
|
|
886
|
+
ctx.addMessage('system', `‖ ${summary}`);
|
|
887
|
+
runExitReason = 'budget';
|
|
888
|
+
}
|
|
613
889
|
// End iteration ledger entry
|
|
614
890
|
ctx.ledger?.endIteration('success');
|
|
615
891
|
// Auto-save full message history for session persistence
|
|
@@ -617,13 +893,13 @@ export async function runAgentImpl(ctx, content) {
|
|
|
617
893
|
break;
|
|
618
894
|
}
|
|
619
895
|
catch (error) {
|
|
896
|
+
streamFlusher.destroy(); // stop any pending flush timer on error/cancel
|
|
620
897
|
ctx.setThinkingState(null);
|
|
621
898
|
ctx.setActivityState(null);
|
|
622
899
|
ctx.setStreamingResponse('');
|
|
623
900
|
// End iteration ledger entry with error
|
|
624
901
|
ctx.ledger?.endIteration('error');
|
|
625
902
|
// Format error with provider context for better suggestions
|
|
626
|
-
setMood('error');
|
|
627
903
|
const errorMsg = formatError(error, { provider: ctx.actualProvider });
|
|
628
904
|
ctx.addMessage('error', errorMsg);
|
|
629
905
|
// Classify error to provide additional recovery suggestions
|
|
@@ -652,7 +928,7 @@ export async function runAgentImpl(ctx, content) {
|
|
|
652
928
|
// Suggest alternatives based on error type
|
|
653
929
|
if (classified.category === 'rate_limit' || classified.category === 'server') {
|
|
654
930
|
if (otherProviders.length > 0) {
|
|
655
|
-
ctx.addMessage('system', `\u{1f4a1} Try switching providers: /provider ${otherProviders[0]} or /
|
|
931
|
+
ctx.addMessage('system', `\u{1f4a1} Try switching providers: /provider ${otherProviders[0]} or /model to see alternatives`);
|
|
656
932
|
}
|
|
657
933
|
}
|
|
658
934
|
else if (classified.category === 'timeout' || classified.category === 'network') {
|
|
@@ -680,6 +956,7 @@ export async function runAgentImpl(ctx, content) {
|
|
|
680
956
|
if (runId && runStatus) {
|
|
681
957
|
ctx.ledger?.finishRun(runId, runStatus, { errorSummary: runErrorSummary });
|
|
682
958
|
}
|
|
959
|
+
finalizeRun(runExitReason ?? runStatus ?? 'error');
|
|
683
960
|
return; // Exit early on error - don't process queued messages
|
|
684
961
|
}
|
|
685
962
|
}
|
|
@@ -694,10 +971,15 @@ export async function runAgentImpl(ctx, content) {
|
|
|
694
971
|
if (runId) {
|
|
695
972
|
ctx.ledger?.finishRun(runId, runStatus || 'completed', { errorSummary: runErrorSummary });
|
|
696
973
|
}
|
|
974
|
+
finalizeRun(runExitReason ?? runStatus ?? 'completed');
|
|
697
975
|
// Update context tokens after agent run
|
|
698
976
|
ctx.setContextTokens(ctx.estimateContextTokens());
|
|
699
977
|
// Process any queued messages (human-in-the-loop feedback)
|
|
700
978
|
// CRITICAL: Use ref to get current value, not stale closure
|
|
979
|
+
// #224: a plan produced with zero tool calls carries no evidence — say so.
|
|
980
|
+
if (ctx.mode === 'plan' && runToolCalls === 0) {
|
|
981
|
+
ctx.addMessage('system', '⚠ Unverified plan — the agent read nothing to produce this. Ask it to verify (it can read files in plan mode), or treat claims as assumptions.');
|
|
982
|
+
}
|
|
701
983
|
const currentQueued = ctx.queuedMessagesRef.current;
|
|
702
984
|
ctx.debugLog('runAgent', 'EXIT loop', `queued=${currentQueued.length}`);
|
|
703
985
|
if (currentQueued.length > 0) {
|
|
@@ -735,7 +1017,6 @@ export async function runAgentImpl(ctx, content) {
|
|
|
735
1017
|
*/
|
|
736
1018
|
export async function runLoopImpl(ctx, prompt, maxIter, completionPromise) {
|
|
737
1019
|
ctx.setIsProcessing(true);
|
|
738
|
-
setMood('focused');
|
|
739
1020
|
// Prevent system sleep during long agent loops (macOS)
|
|
740
1021
|
startPreventSleep();
|
|
741
1022
|
let completedIterations = 0;
|