@calliopelabs/cli 2.5.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -661
- 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/bin.d.ts +3 -7
- package/dist/bin.d.ts.map +1 -1
- package/dist/bin.js +56 -79
- 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 +247 -160
- package/dist/checkpoint.js.map +1 -1
- package/dist/circuit-breaker/breaker.d.ts.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 +236 -304
- 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 +11 -7
- package/dist/diff.js.map +1 -1
- package/dist/env-expansion.js.map +1 -1
- package/dist/errors.js +3 -3
- 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 +166 -24
- package/dist/headless.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/mcp.d.ts.map +1 -1
- package/dist/mcp.js +5 -2
- 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.js +2 -2
- package/dist/model-detection.js.map +1 -1
- package/dist/parallel-tools.js.map +1 -1
- package/dist/plugins.d.ts +32 -1
- package/dist/plugins.d.ts.map +1 -1
- package/dist/plugins.js +116 -5
- 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/bedrock.d.ts.map +1 -1
- package/dist/providers/bedrock.js +2 -5
- 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 +61 -10
- 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 +10 -10
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +18 -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.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/router.d.ts +133 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/{model-router.js → router.js} +329 -3
- 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} +1 -1
- package/dist/sandbox/native.d.ts.map +1 -0
- package/dist/{sandbox-native.js → sandbox/native.js} +1 -1
- package/dist/sandbox/native.js.map +1 -0
- package/dist/scope.d.ts.map +1 -1
- package/dist/scope.js +1 -1
- package/dist/scope.js.map +1 -1
- package/dist/scuttlebot/client.d.ts.map +1 -1
- package/dist/scuttlebot/client.js.map +1 -1
- package/dist/scuttlebot/config.js.map +1 -1
- package/dist/scuttlebot/http-client.d.ts.map +1 -1
- package/dist/scuttlebot/irc-client.d.ts +5 -5
- package/dist/scuttlebot/irc-client.d.ts.map +1 -1
- package/dist/scuttlebot/irc-client.js.map +1 -1
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +25 -54
- package/dist/setup.js.map +1 -1
- package/dist/skills.d.ts +39 -0
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +90 -8
- package/dist/skills.js.map +1 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +6 -3
- 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 +62 -56
- package/dist/styles.d.ts.map +1 -1
- package/dist/styles.js +11 -0
- package/dist/styles.js.map +1 -1
- package/dist/summarization.d.ts.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 +128 -179
- package/dist/tools.js.map +1 -1
- 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 +21 -40
- package/dist/types.js.map +1 -1
- package/dist/ui/agent.d.ts +3 -6
- package/dist/ui/agent.d.ts.map +1 -1
- package/dist/ui/agent.js +328 -38
- 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 +4 -2
- package/dist/ui/chat-input.d.ts.map +1 -1
- package/dist/ui/chat-input.js +31 -350
- package/dist/ui/chat-input.js.map +1 -1
- package/dist/ui/commands.d.ts +14 -46
- package/dist/ui/commands.d.ts.map +1 -1
- package/dist/ui/commands.js +416 -2899
- 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 +20 -12
- 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 +1 -1
- 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 +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 +45 -1076
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/messages.d.ts +5 -8
- package/dist/ui/messages.d.ts.map +1 -1
- package/dist/ui/messages.js +15 -30
- 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/prompts.d.ts +38 -0
- package/dist/ui/modals/prompts.d.ts.map +1 -0
- package/dist/ui/modals/prompts.js +124 -0
- package/dist/ui/modals/prompts.js.map +1 -0
- package/dist/ui/modals/selectors.d.ts +34 -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 +18 -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 +1 -1
- package/dist/ui/status-bar.d.ts.map +1 -1
- package/dist/ui/status-bar.js +3 -4
- package/dist/ui/status-bar.js.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 +17 -10
- 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 -424
- 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 -45
- package/dist/api-server.d.ts.map +0 -1
- package/dist/api-server.js +0 -326
- 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 -176
- 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 -224
- 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 -330
- 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 -1042
- 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 -268
- 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/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 -85
- package/dist/idle-eviction.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 +0 -72
- package/dist/ui/modals.d.ts.map +0 -1
- package/dist/ui/modals.js +0 -266
- 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,18 +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
|
|
19
|
-
import {
|
|
18
|
+
import * as router from '../router.js';
|
|
19
|
+
import { fleetActive, fleetMirrorAssistant } from '../fleet.js';
|
|
20
20
|
import * as summarization from '../summarization.js';
|
|
21
21
|
import { autoCompress } from '../auto-compressor.js';
|
|
22
|
+
import { createStreamFlusher } from '../streaming.js';
|
|
22
23
|
import { executeParallel, getParallelizationStats } from '../parallel-tools.js';
|
|
23
|
-
import { setMood } from '../companions.js';
|
|
24
24
|
import { checkAndWarnContextLimit } from './context.js';
|
|
25
|
-
import { smartRoute } from '../
|
|
26
|
-
import {
|
|
27
|
-
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';
|
|
28
28
|
import { startPreventSleep, stopPreventSleep } from '../prevent-sleep.js';
|
|
29
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';
|
|
30
33
|
// ============================================================================
|
|
31
34
|
// Tool Result Truncation
|
|
32
35
|
// ============================================================================
|
|
@@ -101,15 +104,116 @@ export function validateAndRepairMessagesImpl(ctx) {
|
|
|
101
104
|
return repaired;
|
|
102
105
|
}
|
|
103
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
|
+
// ============================================================================
|
|
104
201
|
// Run Agent
|
|
105
202
|
// ============================================================================
|
|
106
203
|
/**
|
|
107
204
|
* Run agent with user prompt. Core execution loop that handles LLM calls,
|
|
108
205
|
* tool execution (parallel + sequential), auto-compaction, and queued messages.
|
|
109
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
|
+
}
|
|
110
215
|
export async function runAgentImpl(ctx, content) {
|
|
111
216
|
ctx.debugLog('runAgent', 'ENTER', typeof content === 'string' ? content.substring(0, 50) : '[complex]');
|
|
112
|
-
setMood('thinking');
|
|
113
217
|
// Validate message history before adding new content
|
|
114
218
|
ctx.validateAndRepairMessages();
|
|
115
219
|
ctx.llmMessages.current.push({ role: 'user', content });
|
|
@@ -130,7 +234,7 @@ export async function runAgentImpl(ctx, content) {
|
|
|
130
234
|
}
|
|
131
235
|
}
|
|
132
236
|
else if (ctx.autoRoute && typeof content === 'string') {
|
|
133
|
-
const routeDecision =
|
|
237
|
+
const routeDecision = router.routeRequest(content, ctx.provider, {
|
|
134
238
|
messageCount: ctx.stats.messageCount,
|
|
135
239
|
hasCode: content.includes('```') || /\.(ts|js|py|go|rs|java)/.test(content),
|
|
136
240
|
});
|
|
@@ -152,6 +256,57 @@ export async function runAgentImpl(ctx, content) {
|
|
|
152
256
|
: undefined;
|
|
153
257
|
let runStatus;
|
|
154
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
|
+
};
|
|
155
310
|
// Check context limit and warn if approaching capacity
|
|
156
311
|
// Uses model's actual context length from API when available
|
|
157
312
|
let currentContextTokens = ctx.estimateContextTokens();
|
|
@@ -170,7 +325,7 @@ export async function runAgentImpl(ctx, content) {
|
|
|
170
325
|
}
|
|
171
326
|
else if (contextPercentage > 65) {
|
|
172
327
|
ctx.addMessage('system', `⚠️ Context at ${Math.round(contextPercentage)}% capacity (${Math.round(currentContextTokens / 1000)}K/${Math.round(modelLimit / 1000)}K tokens)
|
|
173
|
-
Consider: /
|
|
328
|
+
Consider: /compact | /clear | shorter messages`);
|
|
174
329
|
}
|
|
175
330
|
// Inject failed approaches into context so the agent avoids repeating mistakes
|
|
176
331
|
if (ctx.ledger) {
|
|
@@ -194,6 +349,14 @@ export async function runAgentImpl(ctx, content) {
|
|
|
194
349
|
ctx.addMessage('system', `🔄 Auto-compressed ${iterAutoCompressResult.summarizedCount} messages during iteration ${i + 1} (${iterAutoCompressResult.method})`);
|
|
195
350
|
}
|
|
196
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
|
+
});
|
|
197
360
|
try {
|
|
198
361
|
// Update thinking state for LLM call
|
|
199
362
|
ctx.setThinkingState({
|
|
@@ -207,10 +370,15 @@ export async function runAgentImpl(ctx, content) {
|
|
|
207
370
|
target: `iteration ${i + 1}`,
|
|
208
371
|
startTime: Date.now(),
|
|
209
372
|
});
|
|
210
|
-
// 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;
|
|
211
376
|
const onToken = (token) => {
|
|
212
|
-
|
|
213
|
-
|
|
377
|
+
if (!streamStarted) {
|
|
378
|
+
ctx.setThinkingState(null); // Clear thinking when streaming starts
|
|
379
|
+
streamStarted = true;
|
|
380
|
+
}
|
|
381
|
+
streamFlusher.push(token);
|
|
214
382
|
};
|
|
215
383
|
// Retry callback for error recovery
|
|
216
384
|
const onRetry = (attempt, error, delayMs) => {
|
|
@@ -228,8 +396,36 @@ export async function runAgentImpl(ctx, content) {
|
|
|
228
396
|
ctx.debugLog('chat', 'CLEANED orphaned tool results', `removed=${ctx.llmMessages.current.length - validatedMessages.length}`);
|
|
229
397
|
ctx.llmMessages.current = validatedMessages;
|
|
230
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
|
+
}
|
|
231
427
|
// Pre-request summarization check - summarize BEFORE sending if context is too large
|
|
232
|
-
const tools = getTools(
|
|
428
|
+
const tools = getTools();
|
|
233
429
|
const contextCheck = estimateContextUsage(ctx.provider, effectiveModel || DEFAULT_MODELS[ctx.provider], validatedMessages, tools);
|
|
234
430
|
ctx.debugLog('chat', 'CONTEXT CHECK', `estimated=${contextCheck.estimated}, limit=${contextCheck.limit}, percent=${contextCheck.percent}%`);
|
|
235
431
|
if (contextCheck.needsSummarization) {
|
|
@@ -241,8 +437,16 @@ export async function runAgentImpl(ctx, content) {
|
|
|
241
437
|
ctx.debugLog('chat', 'PRE-SUMMARIZED', `removed=${result.summarizedCount} messages, reduced from ${result.originalTokens} to ${result.reducedTokens}`);
|
|
242
438
|
}
|
|
243
439
|
}
|
|
244
|
-
|
|
440
|
+
let response = await chat(ctx.provider, validatedMessages, tools, effectiveModel, onToken, onRetry);
|
|
441
|
+
streamFlusher.flush(); // drain any batched tail before we swap to history
|
|
245
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);
|
|
246
450
|
// Update token stats and cost
|
|
247
451
|
if (response.usage) {
|
|
248
452
|
const usageCost = calculateCost(ctx.model || DEFAULT_MODELS[ctx.provider], response.usage.inputTokens, response.usage.outputTokens);
|
|
@@ -267,6 +471,33 @@ export async function runAgentImpl(ctx, content) {
|
|
|
267
471
|
}
|
|
268
472
|
}
|
|
269
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
|
+
}
|
|
270
501
|
// Circuit breaker check after each iteration
|
|
271
502
|
if (ctx.circuitBreaker) {
|
|
272
503
|
const iterData = {
|
|
@@ -296,13 +527,15 @@ export async function runAgentImpl(ctx, content) {
|
|
|
296
527
|
toolCalls: response.toolCalls,
|
|
297
528
|
});
|
|
298
529
|
// Mirror any text the assistant produced alongside the tool calls
|
|
299
|
-
if (
|
|
300
|
-
|
|
530
|
+
if (fleetActive() && response.content) {
|
|
531
|
+
fleetMirrorAssistant(response.content);
|
|
301
532
|
}
|
|
302
533
|
const preChecks = [];
|
|
303
534
|
const executableTools = [];
|
|
304
535
|
for (const toolCall of response.toolCalls) {
|
|
305
536
|
const args = toolCall.arguments;
|
|
537
|
+
runlog.toolCall({ id: toolCall.id, name: toolCall.name, args });
|
|
538
|
+
runToolCalls++;
|
|
306
539
|
const toolPreview = String(args.command || args.path || '...');
|
|
307
540
|
const risk = assessToolRisk(toolCall);
|
|
308
541
|
const riskConfig = RISK_CONFIG[risk.level];
|
|
@@ -344,15 +577,38 @@ export async function runAgentImpl(ctx, content) {
|
|
|
344
577
|
ctx.addMessage('tool', `🛑 Blocked by hook: ${preHookResult.reason}`);
|
|
345
578
|
}
|
|
346
579
|
else {
|
|
347
|
-
//
|
|
348
|
-
|
|
349
|
-
|
|
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
|
+
}
|
|
350
605
|
}
|
|
351
606
|
}
|
|
352
607
|
preChecks.push(preCheck);
|
|
353
608
|
// Record blocked tools in ledger and add to LLM messages
|
|
354
609
|
if (preCheck.blocked) {
|
|
355
610
|
ctx.ledger?.recordAction(toolCall.name, args, 'blocked', preCheck.blockReason);
|
|
611
|
+
runlog.toolResult({ id: toolCall.id, result: preCheck.blockContent, isError: true, durationMs: 0 });
|
|
356
612
|
ctx.llmMessages.current.push({
|
|
357
613
|
role: 'tool',
|
|
358
614
|
content: preCheck.blockContent,
|
|
@@ -382,7 +638,7 @@ export async function runAgentImpl(ctx, content) {
|
|
|
382
638
|
// Execute in parallel using dependency-aware staging
|
|
383
639
|
ctx.debugLog('tools', 'PARALLEL exec start', `count=${executableTools.length}`);
|
|
384
640
|
const results = await executeParallel(executableTools, async (call) => {
|
|
385
|
-
const result = await executeTool(call, ctx.sessionRef.current?.projectPath ?? process.cwd());
|
|
641
|
+
const result = await executeTool(call, ctx.sessionRef.current?.projectPath ?? process.cwd(), 60000, undefined, executeOptions);
|
|
386
642
|
return { result: result.result, isError: result.isError };
|
|
387
643
|
}, (completed, total, current) => {
|
|
388
644
|
const args = current.arguments;
|
|
@@ -408,10 +664,10 @@ export async function runAgentImpl(ctx, content) {
|
|
|
408
664
|
// both count as a failure — mirror the sequential branch.
|
|
409
665
|
const failed = result.isError || !!result.error;
|
|
410
666
|
const failureText = result.error || result.result;
|
|
411
|
-
recordEvent('tool_call', toolCall.name, { name: toolCall.name, arguments: args });
|
|
412
|
-
recordEvent('tool_result', (result.result || result.error || '').slice(0, 1000), { name: toolCall.name, isError: failed });
|
|
413
667
|
// Record in iteration ledger
|
|
414
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 });
|
|
415
671
|
// Execute post-tool hooks
|
|
416
672
|
hooks.executeHooks('post-tool', {
|
|
417
673
|
tool: toolCall.name,
|
|
@@ -483,12 +739,11 @@ export async function runAgentImpl(ctx, content) {
|
|
|
483
739
|
});
|
|
484
740
|
}
|
|
485
741
|
ctx.debugLog('tools', 'EXEC', toolCall.name, toolPreview.substring(0, 30));
|
|
486
|
-
recordEvent('tool_call', toolCall.name, { name: toolCall.name, arguments: args });
|
|
487
742
|
// Auto-checkpoint before destructive operations
|
|
488
743
|
if (shouldCheckpoint(toolCall.name, args)) {
|
|
489
744
|
const hash = createCheckpoint(toolCall.name, args);
|
|
490
745
|
if (hash) {
|
|
491
|
-
ctx.debugLog('checkpoint', `
|
|
746
|
+
ctx.debugLog('checkpoint', `checkpoint ${hash} before ${toolCall.name}`);
|
|
492
747
|
}
|
|
493
748
|
}
|
|
494
749
|
// Stream shell output in real-time (#15)
|
|
@@ -500,11 +755,13 @@ export async function runAgentImpl(ctx, content) {
|
|
|
500
755
|
detail: chunk.trimEnd().split('\n').pop()?.substring(0, 60),
|
|
501
756
|
});
|
|
502
757
|
} : undefined;
|
|
503
|
-
const
|
|
758
|
+
const seqStart = Date.now();
|
|
759
|
+
const result = await executeTool(toolCall, ctx.sessionRef.current?.projectPath ?? process.cwd(), 60000, shellStreamCallback, executeOptions);
|
|
504
760
|
ctx.debugLog('tools', 'DONE', toolCall.name);
|
|
505
|
-
recordEvent('tool_result', result.result.slice(0, 1000), { name: toolCall.name, isError: result.isError });
|
|
506
761
|
// Record in iteration ledger
|
|
507
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 });
|
|
508
765
|
// Execute post-tool hooks
|
|
509
766
|
hooks.executeHooks('post-tool', {
|
|
510
767
|
tool: toolCall.name,
|
|
@@ -547,12 +804,12 @@ export async function runAgentImpl(ctx, content) {
|
|
|
547
804
|
if (planReasoning)
|
|
548
805
|
planMsg += `\n ${planReasoning}\n`;
|
|
549
806
|
planMsg += '\n' + planSteps.map((s, idx) => ` ${idx + 1}. [ ] ${s}`).join('\n');
|
|
550
|
-
planMsg += '\n\n
|
|
807
|
+
planMsg += '\n\n Switch to work mode (Shift+Tab) and reply to execute, or give feedback to revise.';
|
|
551
808
|
ctx.addMessage('assistant', planMsg);
|
|
552
809
|
// Tell the LLM to wait for user approval
|
|
553
810
|
ctx.llmMessages.current.push({
|
|
554
811
|
role: 'tool',
|
|
555
|
-
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.]',
|
|
556
813
|
toolCallId: toolCall.id,
|
|
557
814
|
});
|
|
558
815
|
// Break out of tool loop - wait for user approval
|
|
@@ -574,6 +831,24 @@ export async function runAgentImpl(ctx, content) {
|
|
|
574
831
|
}
|
|
575
832
|
}
|
|
576
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
|
+
}
|
|
577
852
|
if (completedNaturally)
|
|
578
853
|
break; // ask_question pauses for user input (#42)
|
|
579
854
|
continue;
|
|
@@ -582,17 +857,13 @@ export async function runAgentImpl(ctx, content) {
|
|
|
582
857
|
ctx.setThinkingState(null);
|
|
583
858
|
ctx.llmMessages.current.push({ role: 'assistant', content: response.content });
|
|
584
859
|
ctx.addMessage('assistant', response.content);
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
scuttlebotClient.mirrorAssistant(response.content).catch(() => {
|
|
589
|
-
// Silent fail
|
|
590
|
-
});
|
|
860
|
+
// Mirror assistant message to the fleet channel
|
|
861
|
+
if (fleetActive()) {
|
|
862
|
+
fleetMirrorAssistant(response.content);
|
|
591
863
|
}
|
|
592
864
|
ctx.setStreamingResponse('');
|
|
593
865
|
ctx.setContextTokens(ctx.estimateContextTokens());
|
|
594
866
|
checkAndWarnContextLimit(ctx.actualProvider, ctx.actualModel, ctx.estimateContextTokens(), ctx.addMessage);
|
|
595
|
-
setMood('success');
|
|
596
867
|
// Auto-continue if response was truncated due to length
|
|
597
868
|
if (response.finishReason === 'length') {
|
|
598
869
|
ctx.addMessage('system', '(auto-continuing...)');
|
|
@@ -601,6 +872,20 @@ export async function runAgentImpl(ctx, content) {
|
|
|
601
872
|
}
|
|
602
873
|
completedNaturally = true;
|
|
603
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
|
+
}
|
|
604
889
|
// End iteration ledger entry
|
|
605
890
|
ctx.ledger?.endIteration('success');
|
|
606
891
|
// Auto-save full message history for session persistence
|
|
@@ -608,13 +893,13 @@ export async function runAgentImpl(ctx, content) {
|
|
|
608
893
|
break;
|
|
609
894
|
}
|
|
610
895
|
catch (error) {
|
|
896
|
+
streamFlusher.destroy(); // stop any pending flush timer on error/cancel
|
|
611
897
|
ctx.setThinkingState(null);
|
|
612
898
|
ctx.setActivityState(null);
|
|
613
899
|
ctx.setStreamingResponse('');
|
|
614
900
|
// End iteration ledger entry with error
|
|
615
901
|
ctx.ledger?.endIteration('error');
|
|
616
902
|
// Format error with provider context for better suggestions
|
|
617
|
-
setMood('error');
|
|
618
903
|
const errorMsg = formatError(error, { provider: ctx.actualProvider });
|
|
619
904
|
ctx.addMessage('error', errorMsg);
|
|
620
905
|
// Classify error to provide additional recovery suggestions
|
|
@@ -643,7 +928,7 @@ export async function runAgentImpl(ctx, content) {
|
|
|
643
928
|
// Suggest alternatives based on error type
|
|
644
929
|
if (classified.category === 'rate_limit' || classified.category === 'server') {
|
|
645
930
|
if (otherProviders.length > 0) {
|
|
646
|
-
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`);
|
|
647
932
|
}
|
|
648
933
|
}
|
|
649
934
|
else if (classified.category === 'timeout' || classified.category === 'network') {
|
|
@@ -671,6 +956,7 @@ export async function runAgentImpl(ctx, content) {
|
|
|
671
956
|
if (runId && runStatus) {
|
|
672
957
|
ctx.ledger?.finishRun(runId, runStatus, { errorSummary: runErrorSummary });
|
|
673
958
|
}
|
|
959
|
+
finalizeRun(runExitReason ?? runStatus ?? 'error');
|
|
674
960
|
return; // Exit early on error - don't process queued messages
|
|
675
961
|
}
|
|
676
962
|
}
|
|
@@ -685,10 +971,15 @@ export async function runAgentImpl(ctx, content) {
|
|
|
685
971
|
if (runId) {
|
|
686
972
|
ctx.ledger?.finishRun(runId, runStatus || 'completed', { errorSummary: runErrorSummary });
|
|
687
973
|
}
|
|
974
|
+
finalizeRun(runExitReason ?? runStatus ?? 'completed');
|
|
688
975
|
// Update context tokens after agent run
|
|
689
976
|
ctx.setContextTokens(ctx.estimateContextTokens());
|
|
690
977
|
// Process any queued messages (human-in-the-loop feedback)
|
|
691
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
|
+
}
|
|
692
983
|
const currentQueued = ctx.queuedMessagesRef.current;
|
|
693
984
|
ctx.debugLog('runAgent', 'EXIT loop', `queued=${currentQueued.length}`);
|
|
694
985
|
if (currentQueued.length > 0) {
|
|
@@ -726,7 +1017,6 @@ export async function runAgentImpl(ctx, content) {
|
|
|
726
1017
|
*/
|
|
727
1018
|
export async function runLoopImpl(ctx, prompt, maxIter, completionPromise) {
|
|
728
1019
|
ctx.setIsProcessing(true);
|
|
729
|
-
setMood('focused');
|
|
730
1020
|
// Prevent system sleep during long agent loops (macOS)
|
|
731
1021
|
startPreventSleep();
|
|
732
1022
|
let completedIterations = 0;
|