@calliopelabs/cli 2.5.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 -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.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/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.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.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.js +1 -1
- package/dist/scope.js.map +1 -1
- package/dist/scuttlebot/config.js.map +1 -1
- package/dist/scuttlebot/irc-client.js.map +1 -1
- 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 +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 +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 +128 -179
- package/dist/tools.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 +2 -1
- 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 +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 +45 -1076
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/messages.d.ts +4 -7
- 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 +37 -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 +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.map +1 -1
- package/dist/ui/status-bar.js +2 -3
- 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 +10 -3
- 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/index.js
CHANGED
|
@@ -1,1080 +1,65 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* UI Module - Entry Point
|
|
4
4
|
*
|
|
5
|
-
* TerminalChat (
|
|
6
|
-
*
|
|
5
|
+
* TerminalChat (composition), App wrapper, printBanner, startInkCLI. All state
|
|
6
|
+
* and orchestration live in useChatController; the JSX here just wires the four
|
|
7
|
+
* memoized regions together inside the HUD frame.
|
|
7
8
|
*/
|
|
8
|
-
import
|
|
9
|
-
import { render, Box
|
|
10
|
-
import * as fs from 'fs';
|
|
9
|
+
import { useEffect } from 'react';
|
|
10
|
+
import { render, Box } from 'ink';
|
|
11
11
|
import * as config from '../config.js';
|
|
12
|
-
import { selectProvider } from '../providers/index.js';
|
|
13
|
-
import {
|
|
12
|
+
import { selectProvider, ProviderUnavailableError } from '../providers/index.js';
|
|
13
|
+
import { DEFAULT_MODELS } from '../types.js';
|
|
14
14
|
import { getVersion } from '../version-check.js';
|
|
15
|
-
import {
|
|
16
|
-
import { detectComplexity } from '../risk.js';
|
|
17
|
-
import * as storage from '../storage.js';
|
|
18
|
-
import { parseFileReferences, processFilesForMessage, formatFileInfo } from '../files.js';
|
|
19
|
-
import * as memory from '../memory.js';
|
|
20
|
-
import * as hooks from '../hooks.js';
|
|
21
|
-
import { getCurrentSkin, getCurrentPalette, paletteColorize, applySkin, applyPalette } from '../hud/api.js';
|
|
15
|
+
import { getCurrentSkin, paletteColorize } from '../hud/api.js';
|
|
22
16
|
import { renderColoredBanner, renderSplashAnimation, renderTransition, colorFg } from '../terminal-image.js';
|
|
17
|
+
import { fleetPostOffline } from '../fleet.js';
|
|
23
18
|
import { HUDFrame } from './frame.js';
|
|
24
|
-
import { getCurrentCompanion, applyCompanion, setEmojiConfig } from '../companions.js';
|
|
25
|
-
import { CircuitBreaker } from '../circuit-breaker.js';
|
|
26
|
-
import { IterationLedger } from '../iteration-ledger.js';
|
|
27
|
-
import { getDefaultSmartRoutingConfig } from '../smart-router.js';
|
|
28
|
-
import * as recording from '../terminal-recording.js';
|
|
29
|
-
import * as sessionTimeout from '../session-timeout.js';
|
|
30
|
-
import * as idleEviction from '../idle-eviction.js';
|
|
31
|
-
import { isTmux, getTmuxInfo } from '../tmux.js';
|
|
32
|
-
import { scuttlebotClient } from '../scuttlebot/index.js';
|
|
33
19
|
import { ErrorBoundary } from './error-boundary.js';
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
//
|
|
46
|
-
setEmojiConfig(config);
|
|
47
|
-
// Module-level state for agterm mode
|
|
48
|
-
let moduleAgtermEnabled = false;
|
|
49
|
-
let pendingRestartArgs = null;
|
|
50
|
-
function requestSelfRestart(args = process.argv.slice(1)) {
|
|
51
|
-
pendingRestartArgs = [...args];
|
|
52
|
-
}
|
|
53
|
-
async function spawnPendingRestart() {
|
|
54
|
-
if (!pendingRestartArgs) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
const restartArgs = pendingRestartArgs;
|
|
58
|
-
pendingRestartArgs = null;
|
|
59
|
-
const { spawn } = await import('child_process');
|
|
60
|
-
const child = spawn(process.argv[0], restartArgs, {
|
|
61
|
-
stdio: 'inherit',
|
|
62
|
-
detached: true,
|
|
63
|
-
});
|
|
64
|
-
child.unref();
|
|
65
|
-
}
|
|
66
|
-
// Debug logging for flow control issues
|
|
67
|
-
let debugEnabled = process.env.CALLIOPE_DEBUG === '1';
|
|
68
|
-
const debugLog = (label, ...args) => {
|
|
69
|
-
if (debugEnabled) {
|
|
70
|
-
const timestamp = new Date().toISOString().split('T')[1].slice(0, 12);
|
|
71
|
-
console.error(`[${timestamp}] ${label}:`, ...args);
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
// ============================================================================
|
|
75
|
-
// Main Chat Component
|
|
76
|
-
// ============================================================================
|
|
77
|
-
function TerminalChat() {
|
|
78
|
-
const { exit } = useApp();
|
|
79
|
-
const { stdout } = useStdout();
|
|
80
|
-
// Reactive terminal width - re-renders on resize via SIGWINCH
|
|
81
|
-
const [width, setWidth] = useState(() => stdout?.columns || 80);
|
|
82
|
-
useEffect(() => {
|
|
83
|
-
const onResize = () => {
|
|
84
|
-
const cols = stdout?.columns || process.stdout.columns || 80;
|
|
85
|
-
setWidth(cols);
|
|
86
|
-
};
|
|
87
|
-
process.stdout.on('resize', onResize);
|
|
88
|
-
return () => { process.stdout.off('resize', onResize); };
|
|
89
|
-
}, [stdout]);
|
|
90
|
-
// Core state
|
|
91
|
-
// Input value is held primarily in a ref so that keystrokes don't force
|
|
92
|
-
// a full parent re-render (which cascades through modals, layouts, status bar
|
|
93
|
-
// and causes visible paint lag on every character). We only bump `inputVersion`
|
|
94
|
-
// when we need the parent to re-render with a programmatic value change
|
|
95
|
-
// (history nav, clear on submit) — keystrokes never trigger a parent render.
|
|
96
|
-
const inputRef = useRef('');
|
|
97
|
-
const [inputVersion, setInputVersion] = useState(0);
|
|
98
|
-
const setInputValue = useCallback((value) => {
|
|
99
|
-
inputRef.current = value;
|
|
100
|
-
setInputVersion(v => v + 1);
|
|
101
|
-
}, []);
|
|
102
|
-
const input = inputRef.current;
|
|
103
|
-
const [suggestions, setSuggestions] = useState([]);
|
|
104
|
-
const [messages, setMessages] = useState([]);
|
|
105
|
-
const [isProcessing, setIsProcessing] = useState(false);
|
|
106
|
-
const [thinkingState, setThinkingState] = useState(null);
|
|
107
|
-
const [streamingResponse, setStreamingResponse] = useState('');
|
|
108
|
-
const [activityState, setActivityState] = useState(null);
|
|
109
|
-
// State transition tracking
|
|
110
|
-
const prevProcessingState = useRef('idle');
|
|
111
|
-
const [transition, setTransition] = useState(null);
|
|
112
|
-
useEffect(() => {
|
|
113
|
-
const current = isProcessing && thinkingState && !streamingResponse ? 'thinking' :
|
|
114
|
-
isProcessing && streamingResponse ? 'streaming' :
|
|
115
|
-
!isProcessing && prevProcessingState.current !== 'idle' ? 'done' : 'idle';
|
|
116
|
-
if (current !== prevProcessingState.current) {
|
|
117
|
-
const from = prevProcessingState.current;
|
|
118
|
-
// Only show transitions for meaningful state changes
|
|
119
|
-
if (from !== 'idle' || current !== 'idle') {
|
|
120
|
-
setTransition({ from, to: current });
|
|
121
|
-
}
|
|
122
|
-
prevProcessingState.current = current;
|
|
123
|
-
}
|
|
124
|
-
}, [isProcessing, thinkingState, streamingResponse]);
|
|
125
|
-
// Input history for up/down arrow navigation
|
|
126
|
-
const [inputHistory, setInputHistory] = useState([]);
|
|
127
|
-
const [historyIndex, setHistoryIndex] = useState(-1);
|
|
128
|
-
const [savedInput, setSavedInput] = useState(''); // Save current input when navigating
|
|
129
|
-
// Smart suggestions context
|
|
130
|
-
const [hasGitRepo] = useState(() => {
|
|
131
|
-
try {
|
|
132
|
-
return fs.existsSync('.git') || fs.existsSync('../.git');
|
|
133
|
-
}
|
|
134
|
-
catch {
|
|
135
|
-
return false;
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
const recentCommands = React.useMemo(() => inputHistory.filter(cmd => cmd.startsWith('/')).slice(-10), [inputHistory]);
|
|
139
|
-
// Handle changes coming from the ChatInput. During typing, we update the
|
|
140
|
-
// ref only — ChatInput paints itself synchronously from its own ref, so we
|
|
141
|
-
// don't need to re-render the parent. We still want to clear stale slash
|
|
142
|
-
// suggestions and reset history navigation, but those setters bail out
|
|
143
|
-
// via functional updates when the value is already current.
|
|
144
|
-
const handleInputChange = useCallback((newValue) => {
|
|
145
|
-
inputRef.current = newValue;
|
|
146
|
-
if (!newValue || !newValue.startsWith('/')) {
|
|
147
|
-
setSuggestions(prev => prev.length > 0 ? [] : prev);
|
|
148
|
-
}
|
|
149
|
-
setHistoryIndex(prev => prev === -1 ? prev : -1);
|
|
150
|
-
}, []);
|
|
151
|
-
// Navigate input history
|
|
152
|
-
const navigateHistory = useCallback((direction) => {
|
|
153
|
-
if (inputHistory.length === 0)
|
|
154
|
-
return;
|
|
155
|
-
if (direction === 'up') {
|
|
156
|
-
if (historyIndex === -1) {
|
|
157
|
-
// Save current input before navigating
|
|
158
|
-
setSavedInput(inputRef.current);
|
|
159
|
-
setHistoryIndex(inputHistory.length - 1);
|
|
160
|
-
setInputValue(inputHistory[inputHistory.length - 1]);
|
|
161
|
-
}
|
|
162
|
-
else if (historyIndex > 0) {
|
|
163
|
-
setHistoryIndex(historyIndex - 1);
|
|
164
|
-
setInputValue(inputHistory[historyIndex - 1]);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
if (historyIndex === -1)
|
|
169
|
-
return;
|
|
170
|
-
if (historyIndex < inputHistory.length - 1) {
|
|
171
|
-
setHistoryIndex(historyIndex + 1);
|
|
172
|
-
setInputValue(inputHistory[historyIndex + 1]);
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
// Return to saved input
|
|
176
|
-
setHistoryIndex(-1);
|
|
177
|
-
setInputValue(savedInput);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}, [inputHistory, historyIndex, savedInput, setInputValue]);
|
|
181
|
-
// Add to history when submitting
|
|
182
|
-
const addToHistory = useCallback((value) => {
|
|
183
|
-
if (value.trim() && (inputHistory.length === 0 || inputHistory[inputHistory.length - 1] !== value)) {
|
|
184
|
-
setInputHistory(prev => [...prev.slice(-100), value]); // Keep last 100 entries
|
|
185
|
-
}
|
|
186
|
-
setHistoryIndex(-1);
|
|
187
|
-
setSavedInput('');
|
|
188
|
-
}, [inputHistory]);
|
|
189
|
-
// Config state
|
|
190
|
-
// Use lazy initializers to avoid calling config.get() on every render
|
|
191
|
-
const [provider, setProvider] = useState(() => process.env.CALLIOPE_PROVIDER || config.get('defaultProvider'));
|
|
192
|
-
const [model, setModel] = useState(() => process.env.CALLIOPE_MODEL || config.get('defaultModel'));
|
|
193
|
-
const [persona, setPersona] = useState(() => config.get('persona'));
|
|
194
|
-
const [mode, setMode] = useState('hybrid'); // Default to hybrid mode
|
|
195
|
-
const [confirmMode, setConfirmMode] = useState(true); // Require confirmation for risky ops
|
|
196
|
-
const [layout, setLayout] = useState(() => config.get('layout') || 'response-bottom');
|
|
197
|
-
const [density, setDensity] = useState(() => config.get('density') || 'normal');
|
|
198
|
-
const [collapseSettings, setCollapseSettings] = useState(() => ({
|
|
199
|
-
collapseTools: config.get('collapseTools') ?? false,
|
|
200
|
-
collapseThinking: config.get('collapseThinking') ?? false,
|
|
201
|
-
toolDisplayLimit: config.get('toolDisplayLimit') ?? 0,
|
|
202
|
-
}));
|
|
203
|
-
// Modal state
|
|
204
|
-
const [modalMode, setModalMode] = useState('none');
|
|
205
|
-
const [providerEntries, setProviderEntries] = useState([]);
|
|
206
|
-
const [pendingSetupProvider, setPendingSetupProvider] = useState(null);
|
|
207
|
-
const [pendingComplexPrompt, setPendingComplexPrompt] = useState(null);
|
|
208
|
-
const [previousSession, setPreviousSession] = useState(null);
|
|
209
|
-
const [pendingToolCall, setPendingToolCall] = useState(null);
|
|
210
|
-
const [availableModels, setAvailableModels] = useState([]);
|
|
211
|
-
const [availableSessions, setAvailableSessions] = useState([]);
|
|
212
|
-
const [latestVersion, setLatestVersion] = useState(null);
|
|
213
|
-
// Stats
|
|
214
|
-
const [stats, setStats] = useState({
|
|
215
|
-
inputTokens: 0,
|
|
216
|
-
outputTokens: 0,
|
|
217
|
-
cost: 0,
|
|
218
|
-
messageCount: 0,
|
|
219
|
-
});
|
|
220
|
-
const [contextTokens, setContextTokens] = useState(0);
|
|
221
|
-
// Message queue for human-in-the-loop feedback during processing
|
|
222
|
-
const [queuedMessages, setQueuedMessages] = useState([]);
|
|
223
|
-
const queuedMessagesRef = useRef([]); // Ref to avoid stale closure in runAgent
|
|
224
|
-
const [queueInput, setQueueInput] = useState('');
|
|
225
|
-
const [editingQueueIndex, setEditingQueueIndex] = useState(null);
|
|
226
|
-
// Keep ref in sync with state
|
|
227
|
-
useEffect(() => {
|
|
228
|
-
queuedMessagesRef.current = queuedMessages;
|
|
229
|
-
}, [queuedMessages]);
|
|
230
|
-
// Refs for scuttlebot polling — avoids stale closures across re-renders
|
|
231
|
-
const isProcessingRef = useRef(false);
|
|
232
|
-
const handleSubmitRef = useRef(async () => { });
|
|
233
|
-
const openProviderPickerRef = useRef(null);
|
|
234
|
-
// Keep isProcessingRef in sync
|
|
235
|
-
useEffect(() => {
|
|
236
|
-
isProcessingRef.current = isProcessing;
|
|
237
|
-
}, [isProcessing]);
|
|
238
|
-
// Undo/Redo history - stores snapshots of conversation state
|
|
239
|
-
const undoStack = useRef([]);
|
|
240
|
-
const redoStack = useRef([]);
|
|
241
|
-
const MAX_UNDO_HISTORY = 10;
|
|
242
|
-
// Conversation bookmarks
|
|
243
|
-
const [bookmarks, setBookmarks] = useState([]);
|
|
244
|
-
// Prompt templates
|
|
245
|
-
const [templates, setTemplates] = useState([]);
|
|
246
|
-
// Save state before changes (call before modifying messages)
|
|
247
|
-
const saveUndoState = useCallback(() => {
|
|
248
|
-
undoStack.current.push({
|
|
249
|
-
messages: [...messages],
|
|
250
|
-
llmMessages: [...llmMessages.current],
|
|
251
|
-
timestamp: new Date(),
|
|
252
|
-
});
|
|
253
|
-
// Limit stack size
|
|
254
|
-
if (undoStack.current.length > MAX_UNDO_HISTORY) {
|
|
255
|
-
undoStack.current.shift();
|
|
256
|
-
}
|
|
257
|
-
// Clear redo stack on new action
|
|
258
|
-
redoStack.current = [];
|
|
259
|
-
}, [messages]);
|
|
260
|
-
// LLM conversation history
|
|
261
|
-
const llmMessages = useRef([
|
|
262
|
-
{ role: 'system', content: getSystemPrompt(persona) }
|
|
263
|
-
]);
|
|
264
|
-
// Keep system prompt in sync when persona changes (fixes #46 - persona lost on model fallback)
|
|
265
|
-
useEffect(() => {
|
|
266
|
-
const firstMsg = llmMessages.current[0];
|
|
267
|
-
if (firstMsg && firstMsg.role === 'system') {
|
|
268
|
-
// Preserve any appended memory context
|
|
269
|
-
const currentContent = typeof firstMsg.content === 'string' ? firstMsg.content : '';
|
|
270
|
-
const memoryIdx = currentContent.indexOf('\n\n--- Project Context ---\n');
|
|
271
|
-
const memoryPart = memoryIdx >= 0 ? currentContent.slice(memoryIdx) : '';
|
|
272
|
-
llmMessages.current[0] = {
|
|
273
|
-
role: 'system',
|
|
274
|
-
content: getSystemPrompt(persona) + memoryPart,
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
}, [persona]);
|
|
278
|
-
// Estimate context tokens (conservative: ~2.5 chars per token + 1.35x overhead)
|
|
279
|
-
const estimateContextTokens = useCallback(() => {
|
|
280
|
-
let chars = 0;
|
|
281
|
-
let msgCount = 0;
|
|
282
|
-
for (const msg of llmMessages.current) {
|
|
283
|
-
msgCount++;
|
|
284
|
-
if (typeof msg.content === 'string') {
|
|
285
|
-
chars += msg.content.length;
|
|
286
|
-
}
|
|
287
|
-
else if (Array.isArray(msg.content)) {
|
|
288
|
-
for (const block of msg.content) {
|
|
289
|
-
if (block.type === 'text') {
|
|
290
|
-
chars += block.text.length;
|
|
291
|
-
}
|
|
292
|
-
else if (block.type === 'image') {
|
|
293
|
-
chars += 1000; // Images count as ~250 tokens
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
// Include tool call arguments in estimation
|
|
298
|
-
if (msg.toolCalls) {
|
|
299
|
-
for (const tool of msg.toolCalls) {
|
|
300
|
-
chars += JSON.stringify(tool.arguments || {}).length;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
// Conservative: 2.5 chars/token, 1.35x overhead for formatting/metadata, +50 per message
|
|
305
|
-
return Math.round((chars / 2.5) * 1.35 + msgCount * 50);
|
|
306
|
-
}, []);
|
|
307
|
-
// Session state
|
|
308
|
-
const sessionRef = useRef(null);
|
|
309
|
-
const [autoRoute, setAutoRoute] = useState(false); // Auto model routing
|
|
310
|
-
const [smartRouteActive, setSmartRouteActive] = useState(() => config.get('smartRoutingEnabled') ?? false);
|
|
311
|
-
const [breakerHealth, setBreakerHealth] = useState('ok');
|
|
312
|
-
const ledgerRef = useRef(new IterationLedger());
|
|
313
|
-
const circuitBreakerRef = useRef(config.get('circuitBreakersEnabled') !== false ? (() => {
|
|
314
|
-
const iterTimeSec = config.get('maxIterationTime');
|
|
315
|
-
const cb = new CircuitBreaker();
|
|
316
|
-
if (typeof iterTimeSec === 'number' && iterTimeSec > 0) {
|
|
317
|
-
cb.adjust('wall-clock', { maxIterationDurationMs: iterTimeSec * 1000 });
|
|
318
|
-
}
|
|
319
|
-
// Local/free providers: disable cost breaker, relax token limits
|
|
320
|
-
const prov = config.get('defaultProvider');
|
|
321
|
-
if (prov === 'ollama' || prov === 'litellm') {
|
|
322
|
-
cb.adjust('cost-runaway', { maxSessionCost: 999999, maxCostPerMinute: 999999 });
|
|
323
|
-
cb.adjust('token-burn', { maxTokensPerIteration: 500_000, maxTotalTokens: 20_000_000 });
|
|
324
|
-
}
|
|
325
|
-
return cb;
|
|
326
|
-
})() : null);
|
|
327
|
-
const smartRoutingConfigRef = useRef({
|
|
328
|
-
...getDefaultSmartRoutingConfig(),
|
|
329
|
-
enabled: config.get('smartRoutingEnabled') ?? false,
|
|
330
|
-
costSensitivity: config.get('smartRoutingCostSensitivity') ?? 0.3,
|
|
331
|
-
});
|
|
332
|
-
const [memoryLoaded, setMemoryLoaded] = useState(false);
|
|
333
|
-
// Agent loop state
|
|
334
|
-
const [loopActive, setLoopActive] = useState(false);
|
|
335
|
-
const [loopPrompt, setLoopPrompt] = useState('');
|
|
336
|
-
const [loopMaxIterations, setLoopMaxIterations] = useState(() => resolveIterationLimit(config.get('maxIterations')));
|
|
337
|
-
const [loopCompletionPromise, setLoopCompletionPromise] = useState();
|
|
338
|
-
const [loopIteration, setLoopIteration] = useState(0);
|
|
339
|
-
const loopCancelledRef = useRef(false);
|
|
340
|
-
// Initialize session and load memory on mount
|
|
341
|
-
useEffect(() => {
|
|
342
|
-
const cwd = process.cwd();
|
|
343
|
-
// Always start fresh session - skip resume dialog
|
|
344
|
-
// Note: Previous session data is still available via storage APIs if needed
|
|
345
|
-
const session = storage.getOrCreateSession(cwd);
|
|
346
|
-
sessionRef.current = session;
|
|
347
|
-
ledgerRef.current.setRetentionLimit(config.get('sessionLogLimit') ?? 0);
|
|
348
|
-
ledgerRef.current.setOnChange(() => {
|
|
349
|
-
const activeSessionId = sessionRef.current?.id;
|
|
350
|
-
if (activeSessionId) {
|
|
351
|
-
storage.saveIterationLedger(ledgerRef.current, activeSessionId);
|
|
352
|
-
}
|
|
353
|
-
});
|
|
354
|
-
ledgerRef.current.loadSnapshot(storage.loadIterationLedger(session.id));
|
|
355
|
-
storage.saveIterationLedger(ledgerRef.current, session.id);
|
|
356
|
-
// Load memory context into system prompt
|
|
357
|
-
if (!memoryLoaded) {
|
|
358
|
-
const cwdMem = process.cwd();
|
|
359
|
-
const memoryContext = memory.buildMemoryContext(cwdMem);
|
|
360
|
-
if (memoryContext.trim()) {
|
|
361
|
-
// Append memory context to system prompt
|
|
362
|
-
const currentSystem = llmMessages.current[0];
|
|
363
|
-
if (currentSystem && currentSystem.role === 'system') {
|
|
364
|
-
const systemContent = typeof currentSystem.content === 'string'
|
|
365
|
-
? currentSystem.content
|
|
366
|
-
: '';
|
|
367
|
-
llmMessages.current[0] = {
|
|
368
|
-
role: 'system',
|
|
369
|
-
content: systemContent + '\n\n--- Project Context ---\n' + memoryContext,
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
setMemoryLoaded(true);
|
|
374
|
-
// Execute session start hooks
|
|
375
|
-
hooks.executeHooks('session-start', {}).catch((err) => {
|
|
376
|
-
debugLog('hooks', 'session-start hook failed:', err instanceof Error ? err.message : err);
|
|
377
|
-
});
|
|
378
|
-
// Start session recording (audit log) — respects config
|
|
379
|
-
recording.setRecordingEnabled(config.get('recordSessions') !== false);
|
|
380
|
-
recording.setRetentionDays(config.get('recordingRetentionDays') ?? 0);
|
|
381
|
-
recording.startRecording({
|
|
382
|
-
provider: selectProvider(provider),
|
|
383
|
-
model: model || DEFAULT_MODELS[selectProvider(provider)],
|
|
384
|
-
cwd: cwdMem,
|
|
385
|
-
});
|
|
386
|
-
// Initialize scuttlebot integration
|
|
387
|
-
scuttlebotClient.initialize(session.id, cwdMem).then((enabled) => {
|
|
388
|
-
if (enabled) {
|
|
389
|
-
const status = scuttlebotClient.getStatus();
|
|
390
|
-
debugLog('scuttlebot', `enabled, nick=${status.nick}, irc=${status.config?.ircAddr}`);
|
|
391
|
-
// Show nick in system messages so operators know how to address calliope
|
|
392
|
-
addMessage('system', `IRC connected — address me as: ${status.nick}`);
|
|
393
|
-
scuttlebotClient.postOnline().catch(() => { });
|
|
394
|
-
scuttlebotClient.postMessage(`connected — address me as: ${status.nick}`).catch(() => { });
|
|
395
|
-
// Route incoming IRC instructions into the agent loop
|
|
396
|
-
scuttlebotClient.startPolling((instruction) => {
|
|
397
|
-
if (isProcessingRef.current) {
|
|
398
|
-
setQueuedMessages(prev => [...prev, instruction]);
|
|
399
|
-
}
|
|
400
|
-
else {
|
|
401
|
-
void handleSubmitRef.current(instruction);
|
|
402
|
-
}
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
}).catch((err) => {
|
|
406
|
-
debugLog('scuttlebot', 'initialization failed:', err instanceof Error ? err.message : err);
|
|
407
|
-
});
|
|
408
|
-
// Configure session timeout (opt-in via config)
|
|
409
|
-
const timeoutMs = config.get('sessionTimeoutMs');
|
|
410
|
-
if (timeoutMs) {
|
|
411
|
-
sessionTimeout.configureTimeout({
|
|
412
|
-
enabled: true,
|
|
413
|
-
idleTimeoutMs: typeof timeoutMs === 'number' ? timeoutMs : 2 * 60 * 60 * 1000,
|
|
414
|
-
});
|
|
415
|
-
sessionTimeout.onTimeout((type) => {
|
|
416
|
-
if (type === 'warning') {
|
|
417
|
-
addMessage('system', `\u23f1\ufe0f Session will timeout in ${sessionTimeout.formatTimeRemaining()}`);
|
|
418
|
-
}
|
|
419
|
-
else {
|
|
420
|
-
addMessage('system', '\ud83d\udeaa Session timeout. Saving and exiting...');
|
|
421
|
-
storage.saveMessageHistory(llmMessages.current);
|
|
422
|
-
}
|
|
423
|
-
});
|
|
424
|
-
}
|
|
425
|
-
// Start idle eviction monitor
|
|
426
|
-
idleEviction.configureEviction({ enabled: true });
|
|
427
|
-
idleEviction.onEviction((action) => {
|
|
428
|
-
if (action === 'auto-save') {
|
|
429
|
-
storage.saveMessageHistory(llmMessages.current);
|
|
430
|
-
}
|
|
431
|
-
});
|
|
432
|
-
// Log tmux context if applicable
|
|
433
|
-
if (isTmux()) {
|
|
434
|
-
const info = getTmuxInfo();
|
|
435
|
-
if (info) {
|
|
436
|
-
debugLog('tmux', `session=${info.session}, windows=${info.windows}, panes=${info.panes}`);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
// Load templates from storage
|
|
440
|
-
const savedTemplates = storage.getTemplates();
|
|
441
|
-
if (savedTemplates.length > 0) {
|
|
442
|
-
setTemplates(savedTemplates.map(t => ({
|
|
443
|
-
name: t.name,
|
|
444
|
-
prompt: t.prompt,
|
|
445
|
-
createdAt: new Date(t.createdAt),
|
|
446
|
-
})));
|
|
447
|
-
}
|
|
448
|
-
// Pre-warm model cache in background for faster model switching
|
|
449
|
-
preWarmModelCache().catch((err) => {
|
|
450
|
-
debugLog('cache', 'model cache pre-warm failed:', err instanceof Error ? err.message : err);
|
|
451
|
-
});
|
|
452
|
-
}
|
|
453
|
-
}, [memoryLoaded]);
|
|
454
|
-
// Derived values
|
|
455
|
-
const actualProvider = selectProvider(provider);
|
|
456
|
-
const actualModel = model || DEFAULT_MODELS[actualProvider];
|
|
457
|
-
const isModalActive = modalMode !== 'none';
|
|
458
|
-
// Add message helper
|
|
459
|
-
const addMessage = useCallback((type, content, isError) => {
|
|
460
|
-
setMessages(prev => [...prev, {
|
|
461
|
-
id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
462
|
-
type,
|
|
463
|
-
content,
|
|
464
|
-
// isError is plumbed through to the renderer (messages.tsx) so the tool
|
|
465
|
-
// status icon is driven by the authoritative executeTool flag rather than
|
|
466
|
-
// string-matching the output. Omitted (undefined) for non-tool messages.
|
|
467
|
-
...(isError !== undefined ? { isError } : {}),
|
|
468
|
-
}]);
|
|
469
|
-
// Persist user and assistant messages to storage for session history
|
|
470
|
-
if (type === 'user' || type === 'assistant') {
|
|
471
|
-
storage.addChatMessage({ role: type, content });
|
|
472
|
-
}
|
|
473
|
-
}, []);
|
|
474
|
-
// Handler to edit or delete a queued message
|
|
475
|
-
const handleEditQueuedMessage = useCallback((index, newMsg) => {
|
|
476
|
-
if (newMsg === '') {
|
|
477
|
-
// Delete the message
|
|
478
|
-
setQueuedMessages(prev => prev.filter((_, i) => i !== index));
|
|
479
|
-
addMessage('system', `🗑️ Deleted queued message #${index + 1}`);
|
|
480
|
-
}
|
|
481
|
-
else {
|
|
482
|
-
// Update the message
|
|
483
|
-
setQueuedMessages(prev => prev.map((msg, i) => i === index ? newMsg : msg));
|
|
484
|
-
addMessage('system', `✏️ Updated queued message #${index + 1}`);
|
|
485
|
-
}
|
|
486
|
-
}, [addMessage]);
|
|
487
|
-
// Validate and repair message history
|
|
488
|
-
const validateAndRepairMessages = useCallback(() => {
|
|
489
|
-
return validateAndRepairMessagesImpl({
|
|
490
|
-
llmMessages,
|
|
491
|
-
addMessage,
|
|
492
|
-
debugLog,
|
|
493
|
-
});
|
|
494
|
-
}, [addMessage]);
|
|
495
|
-
// Build agent context
|
|
496
|
-
const buildAgentContext = useCallback(() => ({
|
|
497
|
-
provider,
|
|
498
|
-
model,
|
|
499
|
-
mode,
|
|
500
|
-
confirmMode,
|
|
501
|
-
autoRoute,
|
|
502
|
-
actualProvider,
|
|
503
|
-
actualModel,
|
|
504
|
-
stats,
|
|
505
|
-
agtermEnabled: moduleAgtermEnabled,
|
|
506
|
-
ledger: ledgerRef.current,
|
|
507
|
-
circuitBreaker: circuitBreakerRef.current || undefined,
|
|
508
|
-
smartRouteActive,
|
|
509
|
-
smartRoutingConfig: smartRoutingConfigRef.current,
|
|
510
|
-
setBreakerHealth,
|
|
511
|
-
setStats,
|
|
512
|
-
setStreamingResponse,
|
|
513
|
-
setThinkingState,
|
|
514
|
-
setActivityState,
|
|
515
|
-
setContextTokens,
|
|
516
|
-
setIsProcessing,
|
|
517
|
-
setQueuedMessages,
|
|
518
|
-
setEditingQueueIndex,
|
|
519
|
-
setLoopIteration,
|
|
520
|
-
setLoopActive,
|
|
521
|
-
llmMessages,
|
|
522
|
-
queuedMessagesRef,
|
|
523
|
-
loopCancelledRef,
|
|
524
|
-
sessionRef,
|
|
525
|
-
addMessage,
|
|
526
|
-
estimateContextTokens,
|
|
527
|
-
validateAndRepairMessages,
|
|
528
|
-
debugLog,
|
|
529
|
-
}), [provider, model, mode, confirmMode, autoRoute, smartRouteActive, actualProvider, actualModel, stats, addMessage, estimateContextTokens, validateAndRepairMessages]);
|
|
530
|
-
// Run agent with user prompt
|
|
531
|
-
const runAgent = useCallback(async (content) => {
|
|
532
|
-
const ctx = buildAgentContext();
|
|
533
|
-
await runAgentImpl(ctx, content);
|
|
534
|
-
}, [buildAgentContext]);
|
|
535
|
-
// Agent loop - runs prompt repeatedly until completion promise or max iterations
|
|
536
|
-
const runLoop = useCallback(async (prompt, maxIter, completionPromise) => {
|
|
537
|
-
const ctx = buildAgentContext();
|
|
538
|
-
await runLoopImpl(ctx, prompt, maxIter, completionPromise);
|
|
539
|
-
}, [buildAgentContext]);
|
|
540
|
-
// Build command context
|
|
541
|
-
const buildCommandContext = useCallback(() => ({
|
|
542
|
-
actualProvider,
|
|
543
|
-
actualModel,
|
|
544
|
-
provider,
|
|
545
|
-
model,
|
|
546
|
-
persona,
|
|
547
|
-
mode,
|
|
548
|
-
confirmMode,
|
|
549
|
-
autoRoute,
|
|
550
|
-
layout,
|
|
551
|
-
density,
|
|
552
|
-
collapseSettings,
|
|
553
|
-
messages,
|
|
554
|
-
stats,
|
|
555
|
-
loopActive,
|
|
556
|
-
isProcessing,
|
|
557
|
-
thinkingState,
|
|
558
|
-
streamingResponse,
|
|
559
|
-
queuedMessages,
|
|
560
|
-
bookmarks,
|
|
561
|
-
templates,
|
|
562
|
-
agtermEnabled: moduleAgtermEnabled,
|
|
563
|
-
debugEnabled,
|
|
564
|
-
modalMode,
|
|
565
|
-
circuitBreaker: circuitBreakerRef.current || undefined,
|
|
566
|
-
smartRouteActive,
|
|
567
|
-
smartRoutingConfig: smartRoutingConfigRef.current,
|
|
568
|
-
ledger: ledgerRef.current,
|
|
569
|
-
setProvider,
|
|
570
|
-
setModel,
|
|
571
|
-
setPersona,
|
|
572
|
-
setMode,
|
|
573
|
-
setConfirmMode,
|
|
574
|
-
setAutoRoute,
|
|
575
|
-
setLayout: setLayout,
|
|
576
|
-
setDensity: setDensity,
|
|
577
|
-
setCollapseSettings,
|
|
578
|
-
setMessages,
|
|
579
|
-
setStats,
|
|
580
|
-
setModalMode: setModalMode,
|
|
581
|
-
setPendingComplexPrompt,
|
|
582
|
-
setAvailableModels,
|
|
583
|
-
setAvailableSessions,
|
|
584
|
-
setLatestVersion,
|
|
585
|
-
setLoopActive,
|
|
586
|
-
setLoopPrompt,
|
|
587
|
-
setLoopMaxIterations,
|
|
588
|
-
setLoopCompletionPromise,
|
|
589
|
-
setLoopIteration,
|
|
590
|
-
setIsProcessing,
|
|
591
|
-
setThinkingState,
|
|
592
|
-
setStreamingResponse,
|
|
593
|
-
setQueuedMessages,
|
|
594
|
-
setInput: setInputValue,
|
|
595
|
-
setBookmarks,
|
|
596
|
-
setTemplates,
|
|
597
|
-
setContextTokens,
|
|
598
|
-
setDebugEnabled: (v) => { debugEnabled = v; },
|
|
599
|
-
setSmartRouteActive,
|
|
600
|
-
setBreakerHealth,
|
|
601
|
-
llmMessages,
|
|
602
|
-
undoStack,
|
|
603
|
-
redoStack,
|
|
604
|
-
loopCancelledRef,
|
|
605
|
-
sessionRef,
|
|
606
|
-
addMessage,
|
|
607
|
-
estimateContextTokens,
|
|
608
|
-
saveUndoState,
|
|
609
|
-
runAgent,
|
|
610
|
-
runLoop,
|
|
611
|
-
exit,
|
|
612
|
-
startScuttlebotPolling: () => {
|
|
613
|
-
scuttlebotClient.startPolling((instruction) => {
|
|
614
|
-
if (isProcessingRef.current) {
|
|
615
|
-
setQueuedMessages(prev => [...prev, instruction]);
|
|
616
|
-
}
|
|
617
|
-
else {
|
|
618
|
-
void handleSubmitRef.current(instruction);
|
|
619
|
-
}
|
|
620
|
-
});
|
|
621
|
-
},
|
|
622
|
-
openProviderPicker: () => openProviderPickerRef.current?.(),
|
|
623
|
-
}), [actualProvider, actualModel, provider, model, persona, mode, confirmMode, autoRoute, smartRouteActive,
|
|
624
|
-
layout, density, collapseSettings, messages, stats, loopActive, isProcessing,
|
|
625
|
-
thinkingState, streamingResponse, queuedMessages, bookmarks, templates, modalMode,
|
|
626
|
-
addMessage, estimateContextTokens, saveUndoState, runAgent, runLoop, exit]);
|
|
627
|
-
// Handle slash commands
|
|
628
|
-
const handleCommandWrapped = useCallback(async (cmd) => {
|
|
629
|
-
const ctx = buildCommandContext();
|
|
630
|
-
await handleCommand(cmd, ctx);
|
|
631
|
-
}, [buildCommandContext]);
|
|
632
|
-
// Handle input submission
|
|
633
|
-
const handleSubmit = useCallback(async (value) => {
|
|
634
|
-
const trimmed = value.trim();
|
|
635
|
-
if (!trimmed || isProcessing)
|
|
636
|
-
return;
|
|
637
|
-
// Record activity for timeout/eviction and audit log
|
|
638
|
-
sessionTimeout.recordActivity();
|
|
639
|
-
idleEviction.recordActivity();
|
|
640
|
-
recording.recordEvent('input', trimmed);
|
|
641
|
-
// Add to history for up/down arrow navigation
|
|
642
|
-
addToHistory(trimmed);
|
|
643
|
-
setInputValue('');
|
|
644
|
-
if (trimmed.startsWith('/')) {
|
|
645
|
-
await handleCommandWrapped(trimmed);
|
|
646
|
-
return;
|
|
647
|
-
}
|
|
648
|
-
// ! prefix executes shell commands directly
|
|
649
|
-
if (trimmed.startsWith('!')) {
|
|
650
|
-
const shellCmd = trimmed.slice(1).trim();
|
|
651
|
-
if (shellCmd) {
|
|
652
|
-
addMessage('system', `$ ${shellCmd}`);
|
|
653
|
-
try {
|
|
654
|
-
const { execSync } = await import('child_process');
|
|
655
|
-
const activeCwd = sessionRef.current?.projectPath ?? process.cwd();
|
|
656
|
-
const output = execSync(shellCmd, {
|
|
657
|
-
cwd: activeCwd,
|
|
658
|
-
encoding: 'utf-8',
|
|
659
|
-
timeout: 30000,
|
|
660
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
661
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
662
|
-
}).trim();
|
|
663
|
-
addMessage('system', output || '(no output)');
|
|
664
|
-
}
|
|
665
|
-
catch (err) {
|
|
666
|
-
const execErr = err;
|
|
667
|
-
addMessage('error', execErr.stderr?.trim() || execErr.message || String(err));
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
return;
|
|
671
|
-
}
|
|
672
|
-
// In hybrid mode, check for complex operations
|
|
673
|
-
if (mode === 'hybrid') {
|
|
674
|
-
const complexity = detectComplexity(trimmed);
|
|
675
|
-
if (complexity.isComplex) {
|
|
676
|
-
setPendingComplexPrompt({ prompt: trimmed, complexity });
|
|
677
|
-
setModalMode('complexity-warning');
|
|
678
|
-
return;
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
// Save state for undo before modifying conversation
|
|
682
|
-
saveUndoState();
|
|
683
|
-
// Parse file references from input
|
|
684
|
-
const activeCwd = sessionRef.current?.projectPath ?? process.cwd();
|
|
685
|
-
const { text: cleanText, files } = parseFileReferences(trimmed, activeCwd);
|
|
686
|
-
// Show user message (with file info if any)
|
|
687
|
-
if (files.length > 0) {
|
|
688
|
-
const fileInfo = formatFileInfo(files);
|
|
689
|
-
addMessage('user', `${cleanText}\n📎 ${fileInfo}`);
|
|
690
|
-
}
|
|
691
|
-
else {
|
|
692
|
-
addMessage('user', trimmed);
|
|
693
|
-
}
|
|
694
|
-
// Mirror user input to IRC so observers see what prompted each agent run
|
|
695
|
-
if (scuttlebotClient.isEnabled()) {
|
|
696
|
-
scuttlebotClient.postMessage(cleanText || trimmed).catch(() => { });
|
|
697
|
-
}
|
|
698
|
-
setIsProcessing(true);
|
|
699
|
-
try {
|
|
700
|
-
// Build message content (with file/image support)
|
|
701
|
-
let messageContent;
|
|
702
|
-
if (files.length > 0) {
|
|
703
|
-
const visionSupported = supportsVision(provider, model);
|
|
704
|
-
const { content, warnings } = processFilesForMessage(cleanText || trimmed, files, visionSupported);
|
|
705
|
-
// Show any warnings about files
|
|
706
|
-
for (const warning of warnings) {
|
|
707
|
-
addMessage('system', warning);
|
|
708
|
-
}
|
|
709
|
-
messageContent = content;
|
|
710
|
-
}
|
|
711
|
-
else {
|
|
712
|
-
messageContent = trimmed;
|
|
713
|
-
}
|
|
714
|
-
await runAgent(messageContent);
|
|
715
|
-
}
|
|
716
|
-
finally {
|
|
717
|
-
setIsProcessing(false);
|
|
718
|
-
setThinkingState(null);
|
|
719
|
-
setStreamingResponse('');
|
|
720
|
-
}
|
|
721
|
-
}, [isProcessing, handleCommandWrapped, runAgent, addMessage, provider, model, saveUndoState, addToHistory, mode, setInputValue]);
|
|
722
|
-
// Keep handleSubmitRef current so scuttlebot polling never captures a stale closure
|
|
20
|
+
import { spawnPendingRestart } from './self-restart.js';
|
|
21
|
+
import { useChatController } from './state/use-chat-controller.js';
|
|
22
|
+
import { TranscriptRegion } from './regions/transcript-region.js';
|
|
23
|
+
import { StatusRegion } from './regions/status-region.js';
|
|
24
|
+
import { InputRegion } from './regions/input-region.js';
|
|
25
|
+
import { ModalHost } from './regions/modal-host.js';
|
|
26
|
+
import { probeMount } from './regions/render-probe.js';
|
|
27
|
+
export function TerminalChat({ controllerRef }) {
|
|
28
|
+
const c = useChatController();
|
|
29
|
+
// Mount counter for the reset-without-remount assertion (no-op in production).
|
|
30
|
+
useEffect(() => { probeMount('terminal-chat'); }, []);
|
|
31
|
+
// Expose resetSession for tests/automation; production renders without a ref.
|
|
723
32
|
useEffect(() => {
|
|
724
|
-
|
|
725
|
-
}, [handleSubmit]);
|
|
726
|
-
// Modal handlers
|
|
727
|
-
const handleModelSelect = useCallback((selectedModel) => {
|
|
728
|
-
setModel(selectedModel);
|
|
729
|
-
addMessage('system', `Model: ${selectedModel}`);
|
|
730
|
-
setModalMode('none');
|
|
731
|
-
setAvailableModels([]);
|
|
732
|
-
}, [addMessage]);
|
|
733
|
-
const handleModalCancel = useCallback(() => {
|
|
734
|
-
setModalMode('none');
|
|
735
|
-
setAvailableModels([]);
|
|
736
|
-
setLatestVersion(null);
|
|
737
|
-
}, []);
|
|
738
|
-
const handleUpgradeConfirm = useCallback(async () => {
|
|
739
|
-
setModalMode('none');
|
|
740
|
-
addMessage('system', 'Upgrading...');
|
|
741
|
-
try {
|
|
742
|
-
const { performUpgrade } = await import('../version-check.js');
|
|
743
|
-
const success = await performUpgrade();
|
|
744
|
-
if (success) {
|
|
745
|
-
addMessage('system', 'Upgrade complete! Restarting...');
|
|
746
|
-
requestSelfRestart(process.argv.slice(1));
|
|
747
|
-
exit();
|
|
748
|
-
return;
|
|
749
|
-
}
|
|
750
|
-
else {
|
|
751
|
-
addMessage('error', 'Upgrade failed. Try: npm install -g @calliopelabs/cli@latest');
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
catch (e) {
|
|
755
|
-
addMessage('error', `Upgrade failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
756
|
-
}
|
|
757
|
-
setLatestVersion(null);
|
|
758
|
-
}, [addMessage]);
|
|
759
|
-
// Cycle through modes
|
|
760
|
-
const cycleMode = useCallback(() => {
|
|
761
|
-
setMode(current => {
|
|
762
|
-
const modes = ['plan', 'hybrid', 'work'];
|
|
763
|
-
const idx = modes.indexOf(current);
|
|
764
|
-
const next = modes[(idx + 1) % modes.length];
|
|
765
|
-
return next;
|
|
766
|
-
});
|
|
767
|
-
}, []);
|
|
768
|
-
// Handle Escape key - cancel operation if processing, otherwise show hint
|
|
769
|
-
const handleEscape = useCallback(() => {
|
|
770
|
-
if (isProcessing) {
|
|
771
|
-
// Cancel current operation
|
|
772
|
-
setIsProcessing(false);
|
|
773
|
-
setThinkingState(null);
|
|
774
|
-
setStreamingResponse('');
|
|
775
|
-
setLoopActive(false);
|
|
776
|
-
setEditingQueueIndex(null);
|
|
777
|
-
addMessage('system', '⏹ Operation cancelled. Press Ctrl+C again to quit.');
|
|
778
|
-
}
|
|
779
|
-
else if (modalMode !== 'none') {
|
|
780
|
-
// Close any open modal
|
|
781
|
-
setModalMode('none');
|
|
782
|
-
setPendingComplexPrompt(null);
|
|
783
|
-
}
|
|
784
|
-
else {
|
|
785
|
-
// Not processing - show hint. Second Ctrl+C within 2s will actually exit.
|
|
786
|
-
addMessage('system', '💡 Press Ctrl+C again to quit, or /exit.');
|
|
787
|
-
}
|
|
788
|
-
}, [isProcessing, modalMode, addMessage]);
|
|
789
|
-
const handleExit = useCallback(() => {
|
|
790
|
-
exit();
|
|
791
|
-
}, [exit]);
|
|
792
|
-
// Build the list of providers with their configuration status for the picker.
|
|
793
|
-
// Ordering: Ollama first (recommended onboarding), then others by rough popularity.
|
|
794
|
-
const buildProviderEntries = useCallback(() => {
|
|
795
|
-
const hasBedrock = !!(config.getApiKey('bedrock') || config.getBaseUrl('bedrock')
|
|
796
|
-
|| process.env.AWS_ACCESS_KEY_ID || process.env.AWS_PROFILE);
|
|
797
|
-
const entries = [
|
|
798
|
-
{ id: 'ollama', label: 'Ollama', configured: !!config.getBaseUrl('ollama'), configHint: 'OLLAMA_BASE_URL', recommended: true, note: 'local, free' },
|
|
799
|
-
{ id: 'anthropic', label: 'Anthropic', configured: !!config.getApiKey('anthropic'), configHint: 'ANTHROPIC_API_KEY' },
|
|
800
|
-
{ id: 'openai', label: 'OpenAI', configured: !!config.getApiKey('openai'), configHint: 'OPENAI_API_KEY' },
|
|
801
|
-
{ id: 'google', label: 'Google', configured: !!config.getApiKey('google'), configHint: 'GOOGLE_API_KEY' },
|
|
802
|
-
{ id: 'mistral', label: 'Mistral', configured: !!config.getApiKey('mistral'), configHint: 'MISTRAL_API_KEY' },
|
|
803
|
-
{ id: 'openrouter', label: 'OpenRouter', configured: !!config.getApiKey('openrouter'), configHint: 'OPENROUTER_API_KEY' },
|
|
804
|
-
{ id: 'together', label: 'Together', configured: !!config.getApiKey('together'), configHint: 'TOGETHER_API_KEY' },
|
|
805
|
-
{ id: 'groq', label: 'Groq', configured: !!config.getApiKey('groq'), configHint: 'GROQ_API_KEY' },
|
|
806
|
-
{ id: 'fireworks', label: 'Fireworks', configured: !!config.getApiKey('fireworks'), configHint: 'FIREWORKS_API_KEY' },
|
|
807
|
-
{ id: 'ai21', label: 'AI21', configured: !!config.getApiKey('ai21'), configHint: 'AI21_API_KEY' },
|
|
808
|
-
{ id: 'huggingface', label: 'HuggingFace', configured: !!config.getApiKey('huggingface'), configHint: 'HUGGINGFACE_API_KEY' },
|
|
809
|
-
{ id: 'bedrock', label: 'AWS Bedrock', configured: hasBedrock, configHint: 'AWS_PROFILE or AWS_ACCESS_KEY_ID', note: 'AWS credentials' },
|
|
810
|
-
{ id: 'litellm', label: 'LiteLLM', configured: !!config.getBaseUrl('litellm'), configHint: 'LITELLM_BASE_URL' },
|
|
811
|
-
];
|
|
812
|
-
return entries;
|
|
813
|
-
}, []);
|
|
814
|
-
const openProviderPicker = useCallback(() => {
|
|
815
|
-
setProviderEntries(buildProviderEntries());
|
|
816
|
-
setModalMode('provider');
|
|
817
|
-
}, [buildProviderEntries]);
|
|
818
|
-
const handleProviderSelect = useCallback((entry) => {
|
|
819
|
-
if (entry.configured) {
|
|
820
|
-
setProvider(entry.id);
|
|
821
|
-
addMessage('system', `Provider: ${entry.label}${entry.id === 'ollama' ? ' (local)' : ''}`);
|
|
822
|
-
setModalMode('none');
|
|
823
|
-
setProviderEntries([]);
|
|
824
|
-
return;
|
|
825
|
-
}
|
|
826
|
-
// Not configured — open inline setup.
|
|
827
|
-
setPendingSetupProvider(entry);
|
|
828
|
-
setModalMode('api-key-setup');
|
|
829
|
-
}, [addMessage, setProvider]);
|
|
830
|
-
const handleApiKeySubmit = useCallback((value) => {
|
|
831
|
-
const entry = pendingSetupProvider;
|
|
832
|
-
if (!entry) {
|
|
833
|
-
setModalMode('none');
|
|
33
|
+
if (!controllerRef)
|
|
834
34
|
return;
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
config.set('ollamaBaseUrl', value);
|
|
840
|
-
}
|
|
841
|
-
else if (entry.id === 'litellm') {
|
|
842
|
-
config.set('litellmBaseUrl', value);
|
|
843
|
-
}
|
|
844
|
-
else if (entry.id === 'bedrock') {
|
|
845
|
-
// Simplest path: user enters AWS_PROFILE name. Write to env for this session;
|
|
846
|
-
// persistence is user's responsibility (profile lives in ~/.aws).
|
|
847
|
-
process.env.AWS_PROFILE = value;
|
|
848
|
-
// Clear any stale env-var credentials (e.g. from a prior `aws sso login`
|
|
849
|
-
// export that's since expired) so the profile actually wins.
|
|
850
|
-
delete process.env.AWS_ACCESS_KEY_ID;
|
|
851
|
-
delete process.env.AWS_SECRET_ACCESS_KEY;
|
|
852
|
-
delete process.env.AWS_SESSION_TOKEN;
|
|
853
|
-
addMessage('system', `AWS_PROFILE=${value} set for this session. Add to shell rc to persist.`);
|
|
854
|
-
}
|
|
855
|
-
else {
|
|
856
|
-
const keyMap = {
|
|
857
|
-
anthropic: 'anthropicApiKey',
|
|
858
|
-
openai: 'openaiApiKey',
|
|
859
|
-
google: 'googleApiKey',
|
|
860
|
-
mistral: 'mistralApiKey',
|
|
861
|
-
openrouter: 'openrouterApiKey',
|
|
862
|
-
together: 'togetherApiKey',
|
|
863
|
-
groq: 'groqApiKey',
|
|
864
|
-
fireworks: 'fireworksApiKey',
|
|
865
|
-
ai21: 'ai21ApiKey',
|
|
866
|
-
huggingface: 'huggingfaceApiKey',
|
|
867
|
-
};
|
|
868
|
-
const configKey = keyMap[entry.id];
|
|
869
|
-
if (!configKey)
|
|
870
|
-
throw new Error(`No config mapping for ${entry.id}`);
|
|
871
|
-
// Cast through any — the mapping above guarantees a valid ApiKey field.
|
|
872
|
-
config.set(configKey, value);
|
|
873
|
-
}
|
|
874
|
-
setProvider(entry.id);
|
|
875
|
-
addMessage('system', `✓ Configured ${entry.label}. Provider switched.`);
|
|
876
|
-
}
|
|
877
|
-
catch (e) {
|
|
878
|
-
addMessage('error', `Failed to configure ${entry.label}: ${e instanceof Error ? e.message : String(e)}`);
|
|
879
|
-
}
|
|
880
|
-
setPendingSetupProvider(null);
|
|
881
|
-
setModalMode('none');
|
|
882
|
-
}, [pendingSetupProvider, addMessage, setProvider]);
|
|
883
|
-
const handleApiKeyCancel = useCallback(() => {
|
|
884
|
-
setPendingSetupProvider(null);
|
|
885
|
-
setModalMode('none');
|
|
886
|
-
}, []);
|
|
887
|
-
// Forward-declared ref so buildCommandContext (defined earlier) can open the
|
|
888
|
-
// provider picker without a TDZ on the openProviderPicker callback.
|
|
889
|
-
useEffect(() => {
|
|
890
|
-
openProviderPickerRef.current = openProviderPicker;
|
|
891
|
-
}, [openProviderPicker]);
|
|
892
|
-
// Handle direct send (Shift+Enter) - interrupts current operation and sends immediately
|
|
893
|
-
const handleDirectSend = useCallback((msg) => {
|
|
894
|
-
// Stop current processing
|
|
895
|
-
setIsProcessing(false);
|
|
896
|
-
setThinkingState(null);
|
|
897
|
-
setStreamingResponse('');
|
|
898
|
-
setEditingQueueIndex(null);
|
|
899
|
-
// Show what happened
|
|
900
|
-
addMessage('system', '⚡ Direct send - interrupting current operation');
|
|
901
|
-
addMessage('user', msg);
|
|
902
|
-
// Start new agent run with this message
|
|
903
|
-
setIsProcessing(true);
|
|
904
|
-
runAgent(msg).finally(() => {
|
|
905
|
-
setIsProcessing(false);
|
|
906
|
-
setThinkingState(null);
|
|
907
|
-
setStreamingResponse('');
|
|
908
|
-
setEditingQueueIndex(null);
|
|
909
|
-
});
|
|
910
|
-
}, [addMessage, runAgent]);
|
|
911
|
-
// Streaming response component (reused across layouts)
|
|
912
|
-
const StreamingResponseBox = streamingResponse ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "cyan", children: ["\u2727 ", getCurrentCompanion().name, ":"] }), streamingResponse.split('\n').map((line, i) => (_jsxs(Text, { children: [_jsx(Text, { color: "blue", children: "\u2502" }), " ", line] }, i))), _jsx(Text, { color: "cyan", children: "\u258C" })] })) : null;
|
|
913
|
-
// Thinking/Processing indicator component with state transitions
|
|
914
|
-
const ProcessingBox = (_jsxs(_Fragment, { children: [transition && (_jsx(StateTransition, { from: transition.from, to: transition.to, onComplete: () => setTransition(null) })), isProcessing && thinkingState && !streamingResponse && _jsx(ThinkingDisplay, { state: thinkingState }), isProcessing && !thinkingState && !streamingResponse && _jsx(ProcessingIndicator, { label: "Waiting for response" }), isProcessing && streamingResponse && _jsx(StreamingIndicator, { activity: activityState ?? undefined })] }));
|
|
915
|
-
// Render based on layout
|
|
916
|
-
return (_jsx(HUDFrame, { width: width, children: _jsxs(Box, { flexDirection: "column", width: width, children: [layout === 'split' && (_jsxs(Box, { flexDirection: "row", width: width, children: [_jsxs(Box, { flexDirection: "column", width: "50%", children: [_jsx(Text, { color: "yellow", dimColor: true, children: "\u2500 Tools \u2500" }), _jsx(MessageHistory, { messages: messages, collapseSettings: collapseSettings }), ProcessingBox] }), _jsxs(Box, { flexDirection: "column", width: "50%", borderStyle: "single", borderLeft: true, borderColor: "gray", children: [_jsx(Text, { color: "cyan", dimColor: true, children: "\u2500 Response \u2500" }), StreamingResponseBox] })] })), layout === 'classic' && (_jsxs(_Fragment, { children: [_jsx(MessageHistory, { messages: messages, collapseSettings: collapseSettings }), ProcessingBox, StreamingResponseBox] })), layout === 'response-top' && (_jsxs(_Fragment, { children: [StreamingResponseBox, _jsx(MessageHistory, { messages: messages, collapseSettings: collapseSettings }), ProcessingBox] })), layout === 'response-bottom' && (_jsxs(_Fragment, { children: [_jsx(MessageHistory, { messages: messages, collapseSettings: collapseSettings }), ProcessingBox, StreamingResponseBox] })), layout === 'zen' && (_jsxs(_Fragment, { children: [_jsx(MessageHistory, { messages: messages.filter(m => m.type === 'user' || m.type === 'assistant'), collapseSettings: { collapseTools: true, collapseThinking: true, toolDisplayLimit: 0 } }), ProcessingBox, StreamingResponseBox] })), layout === 'focus' && (_jsxs(_Fragment, { children: [StreamingResponseBox, ProcessingBox, _jsx(MessageHistory, { messages: messages, collapseSettings: { collapseTools: true, collapseThinking: true, toolDisplayLimit: 3 } })] })), layout === 'dashboard' && (_jsxs(_Fragment, { children: [_jsxs(Text, { dimColor: true, children: [' ', stats.inputTokens ? `tokens: ${stats.inputTokens}/${stats.outputTokens}` : '', stats.cost ? ` | cost: $${stats.cost.toFixed(4)}` : '', model ? ` | ${model}` : '', ` | ${getCurrentCompanion().name}`] }), StreamingResponseBox, ProcessingBox, _jsx(MessageHistory, { messages: messages, collapseSettings: collapseSettings })] })), layout === 'minimal' && (_jsxs(_Fragment, { children: [_jsx(MessageHistory, { messages: messages, collapseSettings: collapseSettings }), ProcessingBox, StreamingResponseBox] })), debugEnabled && (_jsx(Box, { marginY: 0, children: _jsxs(Text, { dimColor: true, children: ["[dbg] proc=", isProcessing ? 'Y' : 'N', " think=", thinkingState ? 'Y' : 'N', " stream=", streamingResponse.length, " mode=", mode, " queue=", queuedMessages.length, " activity=", activityState?.action || 'none'] }) })), modalMode === 'model' && availableModels.length > 0 && (_jsx(ModelSelector, { models: availableModels, onSelect: handleModelSelect, onCancel: handleModalCancel })), modalMode === 'sessions' && (_jsx(SessionSelector, { sessions: availableSessions, onSelect: (session) => {
|
|
917
|
-
// Load history from selected session
|
|
918
|
-
addMessage('system', `Loading session: ${session.projectName}...`);
|
|
919
|
-
addMessage('system', `Session path: ${session.projectPath}\nTo load this session, run calliope from that directory.`);
|
|
920
|
-
setModalMode('none');
|
|
921
|
-
}, onDelete: (session) => {
|
|
922
|
-
if (storage.deleteSession(session.id)) {
|
|
923
|
-
addMessage('system', `🗑️ Deleted session: ${session.projectName}`);
|
|
924
|
-
// Refresh the list
|
|
925
|
-
setAvailableSessions(prev => prev.filter(s => s.id !== session.id));
|
|
926
|
-
}
|
|
927
|
-
else {
|
|
928
|
-
addMessage('error', `Failed to delete session: ${session.projectName}`);
|
|
929
|
-
}
|
|
930
|
-
}, onCancel: handleModalCancel })), modalMode === 'upgrade' && latestVersion && (_jsx(UpgradePrompt, { currentVersion: getVersion(), latestVersion: latestVersion, onConfirm: handleUpgradeConfirm, onCancel: handleModalCancel })), modalMode === 'session-resume' && previousSession && (_jsx(SessionResumePrompt, { session: previousSession, onResume: () => {
|
|
931
|
-
const savedMessages = storage.loadMessageHistory();
|
|
932
|
-
if (savedMessages && savedMessages.length > 0) {
|
|
933
|
-
llmMessages.current.length = 0;
|
|
934
|
-
for (const msg of savedMessages) {
|
|
935
|
-
llmMessages.current.push(msg);
|
|
936
|
-
}
|
|
937
|
-
addMessage('system', `✓ Resumed session with ${savedMessages.length} saved messages loaded`);
|
|
938
|
-
const activeSessionId = sessionRef.current?.id;
|
|
939
|
-
if (activeSessionId) {
|
|
940
|
-
ledgerRef.current.loadSnapshot(storage.loadIterationLedger(activeSessionId));
|
|
941
|
-
storage.saveIterationLedger(ledgerRef.current, activeSessionId);
|
|
942
|
-
}
|
|
943
|
-
setContextTokens(estimateContextTokens());
|
|
944
|
-
}
|
|
945
|
-
else {
|
|
946
|
-
const history = storage.getChatHistory(20);
|
|
947
|
-
if (history.length > 0) {
|
|
948
|
-
llmMessages.current.length = 0;
|
|
949
|
-
const activeCwd = sessionRef.current?.projectPath ?? process.cwd();
|
|
950
|
-
const basePrompt = getSystemPrompt(persona);
|
|
951
|
-
const memoryContext = memory.buildMemoryContext(activeCwd);
|
|
952
|
-
llmMessages.current.push({
|
|
953
|
-
role: 'system',
|
|
954
|
-
content: memoryContext.trim()
|
|
955
|
-
? `${basePrompt}\n\n--- Project Context ---\n${memoryContext}`
|
|
956
|
-
: basePrompt,
|
|
957
|
-
});
|
|
958
|
-
for (const msg of history) {
|
|
959
|
-
if (msg.role === 'user' || msg.role === 'assistant') {
|
|
960
|
-
llmMessages.current.push({
|
|
961
|
-
role: msg.role,
|
|
962
|
-
content: msg.content,
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
addMessage('system', `✓ Resumed session with ${history.length} chat messages loaded`);
|
|
967
|
-
setContextTokens(estimateContextTokens());
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
setModalMode('none');
|
|
971
|
-
setPreviousSession(null);
|
|
972
|
-
}, onNew: () => {
|
|
973
|
-
addMessage('system', '✓ Starting fresh session');
|
|
974
|
-
setModalMode('none');
|
|
975
|
-
setPreviousSession(null);
|
|
976
|
-
} })), modalMode === 'complexity-warning' && pendingComplexPrompt && (_jsx(ComplexityWarning, { reason: pendingComplexPrompt.complexity.reason || 'Complex operation detected', prompt: typeof pendingComplexPrompt.prompt === 'string' ? pendingComplexPrompt.prompt : undefined, onProceed: async () => {
|
|
977
|
-
setModalMode('none');
|
|
978
|
-
const prompt = pendingComplexPrompt.prompt;
|
|
979
|
-
setPendingComplexPrompt(null);
|
|
980
|
-
// Proceed with execution
|
|
981
|
-
saveUndoState();
|
|
982
|
-
addMessage('user', typeof prompt === 'string' ? prompt : JSON.stringify(prompt));
|
|
983
|
-
setIsProcessing(true);
|
|
984
|
-
try {
|
|
985
|
-
await runAgent(prompt);
|
|
986
|
-
}
|
|
987
|
-
finally {
|
|
988
|
-
setIsProcessing(false);
|
|
989
|
-
}
|
|
990
|
-
}, onPlan: () => {
|
|
991
|
-
setModalMode('none');
|
|
992
|
-
const prompt = pendingComplexPrompt.prompt;
|
|
993
|
-
setPendingComplexPrompt(null);
|
|
994
|
-
// Switch to plan mode and proceed
|
|
995
|
-
setMode('plan');
|
|
996
|
-
addMessage('system', '📋 Switched to Plan mode - I\'ll describe what I would do without executing.');
|
|
997
|
-
saveUndoState();
|
|
998
|
-
addMessage('user', typeof prompt === 'string' ? prompt : JSON.stringify(prompt));
|
|
999
|
-
setIsProcessing(true);
|
|
1000
|
-
runAgent(prompt).finally(() => setIsProcessing(false));
|
|
1001
|
-
}, onCancel: () => {
|
|
1002
|
-
setModalMode('none');
|
|
1003
|
-
setPendingComplexPrompt(null);
|
|
1004
|
-
addMessage('system', 'Operation cancelled.');
|
|
1005
|
-
} })), modalMode === 'keys' && (_jsx(KeybindingsModal, { onClose: () => setModalMode('none') })), modalMode === 'provider' && providerEntries.length > 0 && (_jsx(ProviderSelector, { providers: providerEntries, onSelect: handleProviderSelect, onCancel: () => {
|
|
1006
|
-
setModalMode('none');
|
|
1007
|
-
setProviderEntries([]);
|
|
1008
|
-
} })), modalMode === 'api-key-setup' && pendingSetupProvider && (_jsx(ApiKeySetup, { provider: pendingSetupProvider.id, configHint: pendingSetupProvider.configHint, onSubmit: handleApiKeySubmit, onCancel: handleApiKeyCancel, extraInstructions: pendingSetupProvider.id === 'ollama'
|
|
1009
|
-
? 'e.g. http://localhost:11434 (start with: ollama serve)'
|
|
1010
|
-
: pendingSetupProvider.id === 'bedrock'
|
|
1011
|
-
? 'Enter your AWS profile name. Ensure it exists in ~/.aws/credentials or ~/.aws/config.'
|
|
1012
|
-
: pendingSetupProvider.id === 'litellm'
|
|
1013
|
-
? 'e.g. http://localhost:4000'
|
|
1014
|
-
: undefined })), modalMode === 'theme-picker' && (_jsx(ThemePicker, { currentLayout: layout, currentSkin: getCurrentSkin().name, currentPalette: getCurrentPalette().name, currentCompanion: getCurrentCompanion().name, onApply: (selection) => {
|
|
1015
|
-
// Apply all selections
|
|
1016
|
-
setLayout(selection.layout);
|
|
1017
|
-
config.set('layout', selection.layout);
|
|
1018
|
-
applySkin(selection.skin);
|
|
1019
|
-
config.set('activeSkin', selection.skin);
|
|
1020
|
-
applyPalette(selection.palette);
|
|
1021
|
-
config.set('activePalette', selection.palette);
|
|
1022
|
-
applyCompanion(selection.companion);
|
|
1023
|
-
config.set('activeCompanion', selection.companion);
|
|
1024
|
-
const changes = [];
|
|
1025
|
-
if (selection.layout !== layout)
|
|
1026
|
-
changes.push(`layout=${selection.layout}`);
|
|
1027
|
-
if (selection.skin !== getCurrentSkin().name)
|
|
1028
|
-
changes.push(`skin=${selection.skin}`);
|
|
1029
|
-
if (selection.palette !== getCurrentPalette().name)
|
|
1030
|
-
changes.push(`palette=${selection.palette}`);
|
|
1031
|
-
if (selection.companion !== getCurrentCompanion().name)
|
|
1032
|
-
changes.push(`companion=${selection.companion}`);
|
|
1033
|
-
addMessage('system', changes.length > 0
|
|
1034
|
-
? `Theme applied: ${changes.join(', ')}`
|
|
1035
|
-
: 'Theme unchanged.');
|
|
1036
|
-
setModalMode('none');
|
|
1037
|
-
}, onCancel: () => setModalMode('none') })), modalMode === 'pack-picker' && (_jsx(PackPicker, { onApply: async (packName) => {
|
|
1038
|
-
setModalMode('none');
|
|
1039
|
-
// Run transition animation if defined
|
|
1040
|
-
const targetPack = getThemePack(packName);
|
|
1041
|
-
if (targetPack?.skin.splash?.transition) {
|
|
1042
|
-
await renderTransition(targetPack.skin.splash.transition);
|
|
1043
|
-
}
|
|
1044
|
-
const success = applyThemePack(packName, getCompanionMode());
|
|
1045
|
-
if (success) {
|
|
1046
|
-
const pack = getCurrentPack();
|
|
1047
|
-
config.set('activeThemePack', packName);
|
|
1048
|
-
config.set('activeSkin', pack.skin.name);
|
|
1049
|
-
config.set('activePalette', pack.palette.name);
|
|
1050
|
-
const companion = getCompanionMode() === 'professional'
|
|
1051
|
-
? pack.companions.professional
|
|
1052
|
-
: pack.companions.immersive;
|
|
1053
|
-
config.set('activeCompanion', companion.name);
|
|
1054
|
-
addMessage('system', `Theme pack: ${packName}\n` +
|
|
1055
|
-
` Skin: ${pack.skin.name}, Palette: ${pack.palette.name}, Companion: ${companion.name}\n` +
|
|
1056
|
-
` "${companion.greeting}"`);
|
|
1057
|
-
}
|
|
1058
|
-
}, onCancel: () => setModalMode('none') })), _jsx(ChatInput, { value: input, valueVersion: inputVersion, onChange: handleInputChange, onSubmit: handleSubmit, onEscape: handleEscape, onExit: handleExit, onCycleMode: cycleMode, disabled: isModalActive, isProcessing: isProcessing, queuedCount: queuedMessages.length, queuedMessages: queuedMessages, editingQueueIndex: editingQueueIndex, onQueueMessage: (msg) => {
|
|
1059
|
-
setQueuedMessages(prev => [...prev, msg]);
|
|
1060
|
-
addMessage('system', `📨 Queued: "${msg.substring(0, 50)}${msg.length > 50 ? '...' : ''}"`);
|
|
1061
|
-
}, onEditQueuedMessage: handleEditQueuedMessage, onSetEditingQueueIndex: setEditingQueueIndex, onDirectSend: handleDirectSend, cwd: process.cwd(), suggestions: suggestions, onSuggestionsChange: setSuggestions, onNavigateHistory: navigateHistory,
|
|
1062
|
-
// Smart suggestions context
|
|
1063
|
-
currentMode: mode, contextPercentage: Math.round((contextTokens / getModelContextLimit(actualProvider, actualModel)) * 100), recentCommands: recentCommands, hasGitRepo: hasGitRepo }), _jsx(StatusBar, { provider: actualProvider, model: actualModel, mode: mode, stats: stats, contextTokens: contextTokens, breakerHealth: config.get('circuitBreakersEnabled') !== false ? breakerHealth : undefined, smartRouteActive: smartRouteActive, width: width })] }) }));
|
|
35
|
+
controllerRef.current = { resetSession: c.resetSession };
|
|
36
|
+
return () => { controllerRef.current = null; };
|
|
37
|
+
}, [controllerRef, c.resetSession]);
|
|
38
|
+
return (_jsx(HUDFrame, { width: c.width, children: _jsxs(Box, { flexDirection: "column", width: c.width, children: [_jsx(TranscriptRegion, { ...c.transcript }), _jsx(ModalHost, { ...c.modal }), _jsx(InputRegion, { ...c.input }), _jsx(StatusRegion, { ...c.status })] }) }));
|
|
1064
39
|
}
|
|
1065
40
|
// ============================================================================
|
|
1066
41
|
// App Wrapper & Entry Point
|
|
1067
42
|
// ============================================================================
|
|
1068
43
|
function App() {
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
}, []);
|
|
1073
|
-
return (_jsx(ErrorBoundary, { onReset: handleReset, children: _jsx(TerminalChat, {}, resetKey) }));
|
|
44
|
+
// The ErrorBoundary swaps in a fallback on a render crash; retrying clears its
|
|
45
|
+
// error state, which remounts TerminalChat fresh — no remount key needed.
|
|
46
|
+
return (_jsx(ErrorBoundary, { children: _jsx(TerminalChat, {}) }));
|
|
1074
47
|
}
|
|
1075
48
|
// Print banner before Ink takes over (stays fixed at top)
|
|
1076
49
|
export async function printBanner() {
|
|
1077
|
-
const
|
|
50
|
+
const requested = config.get('defaultProvider');
|
|
51
|
+
// Never claim a provider that won't serve (#217). If the selected provider is
|
|
52
|
+
// unconfigured, show the real selection annotated as such rather than
|
|
53
|
+
// crashing the banner or pretending a working provider.
|
|
54
|
+
let provider;
|
|
55
|
+
let providerNote = '';
|
|
56
|
+
try {
|
|
57
|
+
provider = selectProvider(requested);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
provider = err instanceof ProviderUnavailableError ? err.provider : requested;
|
|
61
|
+
providerNote = ' (not configured — run calliope --setup)';
|
|
62
|
+
}
|
|
1078
63
|
const model = config.get('defaultModel') || DEFAULT_MODELS[provider];
|
|
1079
64
|
const skin = getCurrentSkin();
|
|
1080
65
|
const dim = '\x1b[2m';
|
|
@@ -1122,17 +107,11 @@ export async function printBanner() {
|
|
|
1122
107
|
console.log();
|
|
1123
108
|
}
|
|
1124
109
|
}
|
|
1125
|
-
|
|
1126
|
-
console.log(`${dim} v${getVersion()} | ${provider}:${model}${reset}`);
|
|
110
|
+
console.log(`${dim} v${getVersion()} | ${provider}:${model}${providerNote}${reset}`);
|
|
1127
111
|
console.log(`${dim} /help for commands | ESC to exit${reset}`);
|
|
1128
|
-
if (companion.greeting) {
|
|
1129
|
-
console.log(`${dim} ${companion.greeting}${reset}`);
|
|
1130
|
-
}
|
|
1131
112
|
console.log();
|
|
1132
113
|
}
|
|
1133
114
|
export async function startInkCLI(options = {}) {
|
|
1134
|
-
// Set module-level agterm state
|
|
1135
|
-
moduleAgtermEnabled = options.agtermEnabled ?? false;
|
|
1136
115
|
// Print banner BEFORE Ink starts - it stays fixed at the top
|
|
1137
116
|
await printBanner();
|
|
1138
117
|
const { waitUntilExit } = render(_jsx(App, {}), {
|
|
@@ -1140,17 +119,7 @@ export async function startInkCLI(options = {}) {
|
|
|
1140
119
|
});
|
|
1141
120
|
await waitUntilExit();
|
|
1142
121
|
// Session cleanup
|
|
1143
|
-
|
|
1144
|
-
await scuttlebotClient.postOffline().catch(() => {
|
|
1145
|
-
// Silent fail
|
|
1146
|
-
});
|
|
1147
|
-
await scuttlebotClient.disconnect().catch(() => {
|
|
1148
|
-
// Silent fail
|
|
1149
|
-
});
|
|
1150
|
-
}
|
|
1151
|
-
recording.stopRecording();
|
|
1152
|
-
sessionTimeout.clearTimers();
|
|
1153
|
-
idleEviction.stopMonitor();
|
|
122
|
+
await fleetPostOffline();
|
|
1154
123
|
await spawnPendingRestart();
|
|
1155
124
|
}
|
|
1156
125
|
//# sourceMappingURL=index.js.map
|