@calliopelabs/cli 0.8.20 → 2.0.2
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/dist/agents/agent-config-loader.d.ts +60 -0
- package/dist/agents/agent-config-loader.d.ts.map +1 -0
- package/dist/agents/agent-config-loader.js +402 -0
- package/dist/agents/agent-config-loader.js.map +1 -0
- package/dist/agents/agent-config-presets.d.ts +10 -0
- package/dist/agents/agent-config-presets.d.ts.map +1 -0
- package/dist/agents/agent-config-presets.js +940 -0
- package/dist/agents/agent-config-presets.js.map +1 -0
- package/dist/agents/agent-config-types.d.ts +145 -0
- package/dist/agents/agent-config-types.d.ts.map +1 -0
- package/dist/agents/agent-config-types.js +12 -0
- package/dist/agents/agent-config-types.js.map +1 -0
- package/dist/{agterm → agents}/agent-detection.d.ts +1 -1
- package/dist/{agterm → agents}/agent-detection.d.ts.map +1 -1
- package/dist/{agterm → agents}/agent-detection.js +21 -5
- package/dist/agents/agent-detection.js.map +1 -0
- package/dist/agents/aggregator.d.ts +19 -0
- package/dist/agents/aggregator.d.ts.map +1 -0
- package/dist/agents/aggregator.js +141 -0
- package/dist/agents/aggregator.js.map +1 -0
- package/dist/{agterm → agents}/cli-backend.d.ts +1 -1
- package/dist/{agterm → agents}/cli-backend.d.ts.map +1 -1
- package/dist/{agterm → agents}/cli-backend.js +90 -12
- package/dist/agents/cli-backend.js.map +1 -0
- package/dist/agents/council-types.d.ts +113 -0
- package/dist/agents/council-types.d.ts.map +1 -0
- package/dist/agents/council-types.js +81 -0
- package/dist/agents/council-types.js.map +1 -0
- package/dist/agents/council.d.ts +107 -0
- package/dist/agents/council.d.ts.map +1 -0
- package/dist/agents/council.js +586 -0
- package/dist/agents/council.js.map +1 -0
- package/dist/agents/decomposer.d.ts +33 -0
- package/dist/agents/decomposer.d.ts.map +1 -0
- package/dist/agents/decomposer.js +138 -0
- package/dist/agents/decomposer.js.map +1 -0
- package/dist/agents/dynamic-tools.d.ts +52 -0
- package/dist/agents/dynamic-tools.d.ts.map +1 -0
- package/dist/agents/dynamic-tools.js +395 -0
- package/dist/agents/dynamic-tools.js.map +1 -0
- package/dist/agents/index.d.ts +29 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +29 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/installer.d.ts +39 -0
- package/dist/agents/installer.d.ts.map +1 -0
- package/dist/agents/installer.js +205 -0
- package/dist/agents/installer.js.map +1 -0
- package/dist/{agterm → agents}/orchestrator.d.ts +7 -2
- package/dist/agents/orchestrator.d.ts.map +1 -0
- package/dist/{agterm → agents}/orchestrator.js +22 -2
- package/dist/agents/orchestrator.js.map +1 -0
- package/dist/agents/sdk-backend.d.ts +63 -0
- package/dist/agents/sdk-backend.d.ts.map +1 -0
- package/dist/agents/sdk-backend.js +489 -0
- package/dist/agents/sdk-backend.js.map +1 -0
- package/dist/agents/swarm-types.d.ts +83 -0
- package/dist/agents/swarm-types.d.ts.map +1 -0
- package/dist/agents/swarm-types.js +20 -0
- package/dist/agents/swarm-types.js.map +1 -0
- package/dist/agents/swarm.d.ts +74 -0
- package/dist/agents/swarm.d.ts.map +1 -0
- package/dist/agents/swarm.js +307 -0
- package/dist/agents/swarm.js.map +1 -0
- package/dist/{agterm → agents}/tools.d.ts +7 -5
- package/dist/agents/tools.d.ts.map +1 -0
- package/dist/agents/tools.js +776 -0
- package/dist/agents/tools.js.map +1 -0
- package/dist/{agterm → agents}/types.d.ts +14 -2
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/{agterm → agents}/types.js +2 -2
- package/dist/agents/types.js.map +1 -0
- package/dist/api-server.d.ts +26 -0
- package/dist/api-server.d.ts.map +1 -0
- package/dist/api-server.js +230 -0
- package/dist/api-server.js.map +1 -0
- package/dist/auto-checkpoint.d.ts +35 -0
- package/dist/auto-checkpoint.d.ts.map +1 -0
- package/dist/auto-checkpoint.js +143 -0
- package/dist/auto-checkpoint.js.map +1 -0
- package/dist/auto-compressor.d.ts +44 -0
- package/dist/auto-compressor.d.ts.map +1 -0
- package/dist/auto-compressor.js +145 -0
- package/dist/auto-compressor.js.map +1 -0
- package/dist/background-jobs.d.ts +45 -0
- package/dist/background-jobs.d.ts.map +1 -0
- package/dist/background-jobs.js +122 -0
- package/dist/background-jobs.js.map +1 -0
- package/dist/bin.d.ts +6 -2
- package/dist/bin.d.ts.map +1 -1
- package/dist/bin.js +127 -24
- package/dist/bin.js.map +1 -1
- package/dist/checkpoint.d.ts +49 -0
- package/dist/checkpoint.d.ts.map +1 -0
- package/dist/checkpoint.js +219 -0
- package/dist/checkpoint.js.map +1 -0
- package/dist/circuit-breaker/breaker.d.ts +80 -0
- package/dist/circuit-breaker/breaker.d.ts.map +1 -0
- package/dist/circuit-breaker/breaker.js +408 -0
- package/dist/circuit-breaker/breaker.js.map +1 -0
- package/dist/circuit-breaker/defaults.d.ts +8 -0
- package/dist/circuit-breaker/defaults.d.ts.map +1 -0
- package/dist/circuit-breaker/defaults.js +35 -0
- package/dist/circuit-breaker/defaults.js.map +1 -0
- package/dist/circuit-breaker/index.d.ts +9 -0
- package/dist/circuit-breaker/index.d.ts.map +1 -0
- package/dist/circuit-breaker/index.js +8 -0
- package/dist/circuit-breaker/index.js.map +1 -0
- package/dist/circuit-breaker/types.d.ts +77 -0
- package/dist/circuit-breaker/types.d.ts.map +1 -0
- package/dist/circuit-breaker/types.js +8 -0
- package/dist/circuit-breaker/types.js.map +1 -0
- package/dist/circuit-breaker.d.ts +8 -0
- package/dist/circuit-breaker.d.ts.map +1 -0
- package/dist/circuit-breaker.js +7 -0
- package/dist/circuit-breaker.js.map +1 -0
- package/dist/cli/agent.d.ts +9 -0
- package/dist/cli/agent.d.ts.map +1 -0
- package/dist/cli/agent.js +262 -0
- package/dist/cli/agent.js.map +1 -0
- package/dist/cli/commands.d.ts +12 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/{cli.js → cli/commands.js} +285 -422
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +222 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/types.d.ts +30 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +20 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/companions.d.ts +54 -0
- package/dist/companions.d.ts.map +1 -0
- package/dist/companions.js +440 -0
- package/dist/companions.js.map +1 -0
- package/dist/config.d.ts +23 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +95 -22
- package/dist/config.js.map +1 -1
- package/dist/diff.d.ts +27 -0
- package/dist/diff.d.ts.map +1 -1
- package/dist/diff.js +415 -10
- package/dist/diff.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +20 -11
- package/dist/errors.js.map +1 -1
- package/dist/git-status.d.ts +23 -0
- package/dist/git-status.d.ts.map +1 -0
- package/dist/git-status.js +92 -0
- package/dist/git-status.js.map +1 -0
- package/dist/headless.d.ts +25 -0
- package/dist/headless.d.ts.map +1 -0
- package/dist/headless.js +182 -0
- package/dist/headless.js.map +1 -0
- package/dist/hud/api.d.ts +35 -0
- package/dist/hud/api.d.ts.map +1 -0
- package/dist/hud/api.js +448 -0
- package/dist/hud/api.js.map +1 -0
- package/dist/hud/palettes.d.ts +9 -0
- package/dist/hud/palettes.d.ts.map +1 -0
- package/dist/hud/palettes.js +280 -0
- package/dist/hud/palettes.js.map +1 -0
- package/dist/hud/skins.d.ts +12 -0
- package/dist/hud/skins.d.ts.map +1 -0
- package/dist/hud/skins.js +365 -0
- package/dist/hud/skins.js.map +1 -0
- package/dist/hud/theme-packs/api.d.ts +51 -0
- package/dist/hud/theme-packs/api.d.ts.map +1 -0
- package/dist/hud/theme-packs/api.js +145 -0
- package/dist/hud/theme-packs/api.js.map +1 -0
- package/dist/hud/theme-packs/index.d.ts +18 -0
- package/dist/hud/theme-packs/index.d.ts.map +1 -0
- package/dist/hud/theme-packs/index.js +38 -0
- package/dist/hud/theme-packs/index.js.map +1 -0
- package/dist/hud/theme-packs/types.d.ts +29 -0
- package/dist/hud/theme-packs/types.d.ts.map +1 -0
- package/dist/hud/theme-packs/types.js +9 -0
- package/dist/hud/theme-packs/types.js.map +1 -0
- package/dist/hud/types.d.ts +182 -0
- package/dist/hud/types.d.ts.map +1 -0
- package/dist/hud/types.js +7 -0
- package/dist/hud/types.js.map +1 -0
- package/dist/idle-eviction.d.ts +34 -0
- package/dist/idle-eviction.d.ts.map +1 -0
- package/dist/idle-eviction.js +78 -0
- package/dist/idle-eviction.js.map +1 -0
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/dist/iteration-ledger.d.ts +105 -0
- package/dist/iteration-ledger.d.ts.map +1 -0
- package/dist/iteration-ledger.js +237 -0
- package/dist/iteration-ledger.js.map +1 -0
- package/dist/markdown.d.ts.map +1 -1
- package/dist/markdown.js +1 -27
- package/dist/markdown.js.map +1 -1
- package/dist/mcp.d.ts +35 -0
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +291 -7
- package/dist/mcp.js.map +1 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +12 -2
- package/dist/memory.js.map +1 -1
- package/dist/model-detection.d.ts +5 -0
- package/dist/model-detection.d.ts.map +1 -1
- package/dist/model-detection.js +278 -10
- package/dist/model-detection.js.map +1 -1
- package/dist/model-router.d.ts.map +1 -1
- package/dist/model-router.js +33 -11
- package/dist/model-router.js.map +1 -1
- package/dist/plugins.d.ts +8 -0
- package/dist/plugins.d.ts.map +1 -1
- package/dist/plugins.js +97 -6
- package/dist/plugins.js.map +1 -1
- package/dist/providers/anthropic.d.ts +10 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +221 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/bedrock.d.ts +17 -0
- package/dist/providers/bedrock.d.ts.map +1 -0
- package/dist/providers/bedrock.js +574 -0
- package/dist/providers/bedrock.js.map +1 -0
- package/dist/providers/compat.d.ts +13 -0
- package/dist/providers/compat.d.ts.map +1 -0
- package/dist/providers/compat.js +202 -0
- package/dist/providers/compat.js.map +1 -0
- package/dist/providers/google.d.ts +10 -0
- package/dist/providers/google.d.ts.map +1 -0
- package/dist/providers/google.js +203 -0
- package/dist/providers/google.js.map +1 -0
- package/dist/providers/index.d.ts +23 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +145 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/ollama.d.ts +17 -0
- package/dist/providers/ollama.d.ts.map +1 -0
- package/dist/providers/ollama.js +289 -0
- package/dist/providers/ollama.js.map +1 -0
- package/dist/providers/openai.d.ts +121 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +485 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/types.d.ts +63 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +164 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/sandbox-native.d.ts +59 -0
- package/dist/sandbox-native.d.ts.map +1 -0
- package/dist/sandbox-native.js +292 -0
- package/dist/sandbox-native.js.map +1 -0
- package/dist/sandbox.d.ts +2 -2
- package/dist/sandbox.d.ts.map +1 -1
- package/dist/sandbox.js +59 -13
- package/dist/sandbox.js.map +1 -1
- package/dist/scope.d.ts +3 -1
- package/dist/scope.d.ts.map +1 -1
- package/dist/scope.js +13 -1
- package/dist/scope.js.map +1 -1
- package/dist/session-timeout.d.ts +31 -0
- package/dist/session-timeout.d.ts.map +1 -0
- package/dist/session-timeout.js +100 -0
- package/dist/session-timeout.js.map +1 -0
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +29 -17
- package/dist/setup.js.map +1 -1
- package/dist/smart-router.d.ts +73 -0
- package/dist/smart-router.d.ts.map +1 -0
- package/dist/smart-router.js +332 -0
- package/dist/smart-router.js.map +1 -0
- package/dist/storage.d.ts +19 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +164 -1
- package/dist/storage.js.map +1 -1
- package/dist/streaming.d.ts +4 -0
- package/dist/streaming.d.ts.map +1 -1
- package/dist/streaming.js +12 -0
- package/dist/streaming.js.map +1 -1
- package/dist/styles.d.ts +32 -0
- package/dist/styles.d.ts.map +1 -1
- package/dist/styles.js +91 -0
- package/dist/styles.js.map +1 -1
- package/dist/summarization.d.ts +1 -1
- package/dist/summarization.js +4 -4
- package/dist/summarization.js.map +1 -1
- package/dist/terminal-image.d.ts +115 -0
- package/dist/terminal-image.d.ts.map +1 -0
- package/dist/terminal-image.js +766 -0
- package/dist/terminal-image.js.map +1 -0
- package/dist/terminal-recording.d.ts +55 -0
- package/dist/terminal-recording.d.ts.map +1 -0
- package/dist/terminal-recording.js +182 -0
- package/dist/terminal-recording.js.map +1 -0
- package/dist/themes.d.ts +19 -35
- package/dist/themes.d.ts.map +1 -1
- package/dist/themes.js +101 -210
- package/dist/themes.js.map +1 -1
- package/dist/tmux.d.ts +35 -0
- package/dist/tmux.d.ts.map +1 -0
- package/dist/tmux.js +106 -0
- package/dist/tmux.js.map +1 -0
- package/dist/tools.d.ts +3 -3
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +587 -45
- package/dist/tools.js.map +1 -1
- package/dist/trust.d.ts +53 -0
- package/dist/trust.d.ts.map +1 -0
- package/dist/trust.js +154 -0
- package/dist/trust.js.map +1 -0
- package/dist/types.d.ts +7 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +70 -32
- package/dist/types.js.map +1 -1
- package/dist/ui/agent.d.ts +61 -0
- package/dist/ui/agent.d.ts.map +1 -0
- package/dist/ui/agent.js +768 -0
- package/dist/ui/agent.js.map +1 -0
- package/dist/ui/chat-input.d.ts +32 -0
- package/dist/ui/chat-input.d.ts.map +1 -0
- package/dist/ui/chat-input.js +355 -0
- package/dist/ui/chat-input.js.map +1 -0
- package/dist/ui/commands.d.ts +92 -0
- package/dist/ui/commands.d.ts.map +1 -0
- package/dist/ui/commands.js +3006 -0
- package/dist/ui/commands.js.map +1 -0
- package/dist/ui/completions.d.ts +22 -0
- package/dist/ui/completions.d.ts.map +1 -0
- package/dist/ui/completions.js +215 -0
- package/dist/ui/completions.js.map +1 -0
- package/dist/ui/components.d.ts +38 -0
- package/dist/ui/components.d.ts.map +1 -0
- package/dist/ui/components.js +422 -0
- package/dist/ui/components.js.map +1 -0
- package/dist/ui/context.d.ts +12 -0
- package/dist/ui/context.d.ts.map +1 -0
- package/dist/ui/context.js +102 -0
- package/dist/ui/context.js.map +1 -0
- package/dist/ui/error-boundary.d.ts +33 -0
- package/dist/ui/error-boundary.d.ts.map +1 -0
- package/dist/ui/error-boundary.js +94 -0
- package/dist/ui/error-boundary.js.map +1 -0
- package/dist/ui/frame.d.ts +13 -0
- package/dist/ui/frame.d.ts.map +1 -0
- package/dist/ui/frame.js +89 -0
- package/dist/ui/frame.js.map +1 -0
- package/dist/ui/index.d.ts +12 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +928 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/messages.d.ts +19 -0
- package/dist/ui/messages.d.ts.map +1 -0
- package/dist/ui/messages.js +181 -0
- package/dist/ui/messages.js.map +1 -0
- package/dist/ui/modals.d.ts +52 -0
- package/dist/ui/modals.d.ts.map +1 -0
- package/dist/ui/modals.js +204 -0
- package/dist/ui/modals.js.map +1 -0
- package/dist/ui/pack-picker.d.ts +12 -0
- package/dist/ui/pack-picker.d.ts.map +1 -0
- package/dist/ui/pack-picker.js +101 -0
- package/dist/ui/pack-picker.js.map +1 -0
- package/dist/ui/status-bar.d.ts +20 -0
- package/dist/ui/status-bar.d.ts.map +1 -0
- package/dist/ui/status-bar.js +41 -0
- package/dist/ui/status-bar.js.map +1 -0
- package/dist/ui/theme-picker.d.ts +24 -0
- package/dist/ui/theme-picker.d.ts.map +1 -0
- package/dist/ui/theme-picker.js +190 -0
- package/dist/ui/theme-picker.js.map +1 -0
- package/dist/ui/types.d.ts +62 -0
- package/dist/ui/types.d.ts.map +1 -0
- package/dist/ui/types.js +7 -0
- package/dist/ui/types.js.map +1 -0
- package/dist/version-check.d.ts.map +1 -1
- package/dist/version-check.js +1 -9
- package/dist/version-check.js.map +1 -1
- package/package.json +8 -3
- package/dist/agterm/agent-detection.js.map +0 -1
- package/dist/agterm/cli-backend.js.map +0 -1
- package/dist/agterm/index.d.ts +0 -12
- package/dist/agterm/index.d.ts.map +0 -1
- package/dist/agterm/index.js +0 -15
- package/dist/agterm/index.js.map +0 -1
- package/dist/agterm/orchestrator.d.ts.map +0 -1
- package/dist/agterm/orchestrator.js.map +0 -1
- package/dist/agterm/tools.d.ts.map +0 -1
- package/dist/agterm/tools.js +0 -278
- package/dist/agterm/tools.js.map +0 -1
- package/dist/agterm/types.d.ts.map +0 -1
- package/dist/agterm/types.js.map +0 -1
- package/dist/cli.d.ts +0 -14
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/providers.d.ts +0 -51
- package/dist/providers.d.ts.map +0 -1
- package/dist/providers.js +0 -1146
- package/dist/providers.js.map +0 -1
- package/dist/ui-cli.d.ts +0 -17
- package/dist/ui-cli.d.ts.map +0 -1
- package/dist/ui-cli.js +0 -3730
- package/dist/ui-cli.js.map +0 -1
package/dist/ui/index.js
ADDED
|
@@ -0,0 +1,928 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* UI Module - Entry Point
|
|
4
|
+
*
|
|
5
|
+
* TerminalChat (main component), App wrapper, printBanner, startInkCLI.
|
|
6
|
+
* Imports extracted modules and wires state through context bags.
|
|
7
|
+
*/
|
|
8
|
+
import React, { useState, useCallback, useRef, useEffect } from 'react';
|
|
9
|
+
import { render, Box, Text, useApp, useStdout } from 'ink';
|
|
10
|
+
import * as fs from 'fs';
|
|
11
|
+
import * as config from '../config.js';
|
|
12
|
+
import { selectProvider } from '../providers/index.js';
|
|
13
|
+
import { getSystemPrompt, DEFAULT_MODELS, supportsVision } from '../types.js';
|
|
14
|
+
import { getVersion } from '../version-check.js';
|
|
15
|
+
import { getModelContextLimit, preWarmModelCache } from '../model-detection.js';
|
|
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';
|
|
22
|
+
import { renderColoredBanner, renderSplashAnimation, renderTransition, colorFg } from '../terminal-image.js';
|
|
23
|
+
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 { ErrorBoundary } from './error-boundary.js';
|
|
33
|
+
import { ThinkingDisplay, ProcessingIndicator, StreamingIndicator, StateTransition } from './components.js';
|
|
34
|
+
import { MessageHistory } from './messages.js';
|
|
35
|
+
import { ModelSelector, SessionSelector, UpgradePrompt, ComplexityWarning, SessionResumePrompt, KeybindingsModal, } from './modals.js';
|
|
36
|
+
import { ThemePicker } from './theme-picker.js';
|
|
37
|
+
import { PackPicker } from './pack-picker.js';
|
|
38
|
+
import { applyThemePack, getCurrentPack, getCompanionMode, getThemePack } from '../hud/theme-packs/api.js';
|
|
39
|
+
import { ChatInput } from './chat-input.js';
|
|
40
|
+
import { StatusBar } from './status-bar.js';
|
|
41
|
+
import { handleCommand } from './commands.js';
|
|
42
|
+
import { runAgentImpl, runLoopImpl, validateAndRepairMessagesImpl } from './agent.js';
|
|
43
|
+
// Wire emoji config at module load (breaks circular dep: companions → config)
|
|
44
|
+
setEmojiConfig(config);
|
|
45
|
+
// Module-level state for agterm mode
|
|
46
|
+
let moduleAgtermEnabled = false;
|
|
47
|
+
// Debug logging for flow control issues
|
|
48
|
+
let debugEnabled = process.env.CALLIOPE_DEBUG === '1';
|
|
49
|
+
const debugLog = (label, ...args) => {
|
|
50
|
+
if (debugEnabled) {
|
|
51
|
+
const timestamp = new Date().toISOString().split('T')[1].slice(0, 12);
|
|
52
|
+
console.error(`[${timestamp}] ${label}:`, ...args);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// Main Chat Component
|
|
57
|
+
// ============================================================================
|
|
58
|
+
function TerminalChat() {
|
|
59
|
+
const { exit } = useApp();
|
|
60
|
+
const { stdout } = useStdout();
|
|
61
|
+
// Reactive terminal width - re-renders on resize via SIGWINCH
|
|
62
|
+
const [width, setWidth] = useState(() => stdout?.columns || 80);
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
const onResize = () => {
|
|
65
|
+
const cols = stdout?.columns || process.stdout.columns || 80;
|
|
66
|
+
setWidth(cols);
|
|
67
|
+
};
|
|
68
|
+
process.stdout.on('resize', onResize);
|
|
69
|
+
return () => { process.stdout.off('resize', onResize); };
|
|
70
|
+
}, [stdout]);
|
|
71
|
+
// Core state
|
|
72
|
+
const [input, setInput] = useState('');
|
|
73
|
+
const [suggestions, setSuggestions] = useState([]);
|
|
74
|
+
const [messages, setMessages] = useState([]);
|
|
75
|
+
const [isProcessing, setIsProcessing] = useState(false);
|
|
76
|
+
const [thinkingState, setThinkingState] = useState(null);
|
|
77
|
+
const [streamingResponse, setStreamingResponse] = useState('');
|
|
78
|
+
const [activityState, setActivityState] = useState(null);
|
|
79
|
+
// State transition tracking
|
|
80
|
+
const prevProcessingState = useRef('idle');
|
|
81
|
+
const [transition, setTransition] = useState(null);
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
const current = isProcessing && thinkingState && !streamingResponse ? 'thinking' :
|
|
84
|
+
isProcessing && streamingResponse ? 'streaming' :
|
|
85
|
+
!isProcessing && prevProcessingState.current !== 'idle' ? 'done' : 'idle';
|
|
86
|
+
if (current !== prevProcessingState.current) {
|
|
87
|
+
const from = prevProcessingState.current;
|
|
88
|
+
// Only show transitions for meaningful state changes
|
|
89
|
+
if (from !== 'idle' || current !== 'idle') {
|
|
90
|
+
setTransition({ from, to: current });
|
|
91
|
+
}
|
|
92
|
+
prevProcessingState.current = current;
|
|
93
|
+
}
|
|
94
|
+
}, [isProcessing, thinkingState, streamingResponse]);
|
|
95
|
+
// Input history for up/down arrow navigation
|
|
96
|
+
const [inputHistory, setInputHistory] = useState([]);
|
|
97
|
+
const [historyIndex, setHistoryIndex] = useState(-1);
|
|
98
|
+
const [savedInput, setSavedInput] = useState(''); // Save current input when navigating
|
|
99
|
+
// Smart suggestions context
|
|
100
|
+
const [hasGitRepo] = useState(() => {
|
|
101
|
+
try {
|
|
102
|
+
return fs.existsSync('.git') || fs.existsSync('../.git');
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
const recentCommands = React.useMemo(() => inputHistory.filter(cmd => cmd.startsWith('/')).slice(-10), [inputHistory]);
|
|
109
|
+
// Clear suggestions when input changes significantly
|
|
110
|
+
const handleInputChange = useCallback((newValue) => {
|
|
111
|
+
setInput(newValue);
|
|
112
|
+
// Clear suggestions if user clears input or submits
|
|
113
|
+
if (!newValue || !newValue.startsWith('/')) {
|
|
114
|
+
setSuggestions([]);
|
|
115
|
+
}
|
|
116
|
+
// Reset history navigation when user types
|
|
117
|
+
setHistoryIndex(-1);
|
|
118
|
+
}, []);
|
|
119
|
+
// Navigate input history
|
|
120
|
+
const navigateHistory = useCallback((direction) => {
|
|
121
|
+
if (inputHistory.length === 0)
|
|
122
|
+
return;
|
|
123
|
+
if (direction === 'up') {
|
|
124
|
+
if (historyIndex === -1) {
|
|
125
|
+
// Save current input before navigating
|
|
126
|
+
setSavedInput(input);
|
|
127
|
+
setHistoryIndex(inputHistory.length - 1);
|
|
128
|
+
setInput(inputHistory[inputHistory.length - 1]);
|
|
129
|
+
}
|
|
130
|
+
else if (historyIndex > 0) {
|
|
131
|
+
setHistoryIndex(historyIndex - 1);
|
|
132
|
+
setInput(inputHistory[historyIndex - 1]);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
if (historyIndex === -1)
|
|
137
|
+
return;
|
|
138
|
+
if (historyIndex < inputHistory.length - 1) {
|
|
139
|
+
setHistoryIndex(historyIndex + 1);
|
|
140
|
+
setInput(inputHistory[historyIndex + 1]);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
// Return to saved input
|
|
144
|
+
setHistoryIndex(-1);
|
|
145
|
+
setInput(savedInput);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}, [inputHistory, historyIndex, input, savedInput]);
|
|
149
|
+
// Add to history when submitting
|
|
150
|
+
const addToHistory = useCallback((value) => {
|
|
151
|
+
if (value.trim() && (inputHistory.length === 0 || inputHistory[inputHistory.length - 1] !== value)) {
|
|
152
|
+
setInputHistory(prev => [...prev.slice(-100), value]); // Keep last 100 entries
|
|
153
|
+
}
|
|
154
|
+
setHistoryIndex(-1);
|
|
155
|
+
setSavedInput('');
|
|
156
|
+
}, [inputHistory]);
|
|
157
|
+
// Config state
|
|
158
|
+
// Use lazy initializers to avoid calling config.get() on every render
|
|
159
|
+
const [provider, setProvider] = useState(() => process.env.CALLIOPE_PROVIDER || config.get('defaultProvider'));
|
|
160
|
+
const [model, setModel] = useState(() => process.env.CALLIOPE_MODEL || config.get('defaultModel'));
|
|
161
|
+
const [persona, setPersona] = useState(() => config.get('persona'));
|
|
162
|
+
const [mode, setMode] = useState('hybrid'); // Default to hybrid mode
|
|
163
|
+
const [confirmMode, setConfirmMode] = useState(true); // Require confirmation for risky ops
|
|
164
|
+
const [layout, setLayout] = useState(() => config.get('layout') || 'response-bottom');
|
|
165
|
+
const [density, setDensity] = useState(() => config.get('density') || 'normal');
|
|
166
|
+
const [collapseSettings, setCollapseSettings] = useState(() => ({
|
|
167
|
+
collapseTools: config.get('collapseTools') ?? false,
|
|
168
|
+
collapseThinking: config.get('collapseThinking') ?? false,
|
|
169
|
+
toolDisplayLimit: config.get('toolDisplayLimit') ?? 0,
|
|
170
|
+
}));
|
|
171
|
+
// Modal state
|
|
172
|
+
const [modalMode, setModalMode] = useState('none');
|
|
173
|
+
const [pendingComplexPrompt, setPendingComplexPrompt] = useState(null);
|
|
174
|
+
const [previousSession, setPreviousSession] = useState(null);
|
|
175
|
+
const [pendingToolCall, setPendingToolCall] = useState(null);
|
|
176
|
+
const [availableModels, setAvailableModels] = useState([]);
|
|
177
|
+
const [availableSessions, setAvailableSessions] = useState([]);
|
|
178
|
+
const [latestVersion, setLatestVersion] = useState(null);
|
|
179
|
+
// Stats
|
|
180
|
+
const [stats, setStats] = useState({
|
|
181
|
+
inputTokens: 0,
|
|
182
|
+
outputTokens: 0,
|
|
183
|
+
cost: 0,
|
|
184
|
+
messageCount: 0,
|
|
185
|
+
});
|
|
186
|
+
const [contextTokens, setContextTokens] = useState(0);
|
|
187
|
+
// Message queue for human-in-the-loop feedback during processing
|
|
188
|
+
const [queuedMessages, setQueuedMessages] = useState([]);
|
|
189
|
+
const queuedMessagesRef = useRef([]); // Ref to avoid stale closure in runAgent
|
|
190
|
+
const [queueInput, setQueueInput] = useState('');
|
|
191
|
+
const [editingQueueIndex, setEditingQueueIndex] = useState(null);
|
|
192
|
+
// Keep ref in sync with state
|
|
193
|
+
useEffect(() => {
|
|
194
|
+
queuedMessagesRef.current = queuedMessages;
|
|
195
|
+
}, [queuedMessages]);
|
|
196
|
+
// Undo/Redo history - stores snapshots of conversation state
|
|
197
|
+
const undoStack = useRef([]);
|
|
198
|
+
const redoStack = useRef([]);
|
|
199
|
+
const MAX_UNDO_HISTORY = 10;
|
|
200
|
+
// Conversation bookmarks
|
|
201
|
+
const [bookmarks, setBookmarks] = useState([]);
|
|
202
|
+
// Prompt templates
|
|
203
|
+
const [templates, setTemplates] = useState([]);
|
|
204
|
+
// Save state before changes (call before modifying messages)
|
|
205
|
+
const saveUndoState = useCallback(() => {
|
|
206
|
+
undoStack.current.push({
|
|
207
|
+
messages: [...messages],
|
|
208
|
+
llmMessages: [...llmMessages.current],
|
|
209
|
+
timestamp: new Date(),
|
|
210
|
+
});
|
|
211
|
+
// Limit stack size
|
|
212
|
+
if (undoStack.current.length > MAX_UNDO_HISTORY) {
|
|
213
|
+
undoStack.current.shift();
|
|
214
|
+
}
|
|
215
|
+
// Clear redo stack on new action
|
|
216
|
+
redoStack.current = [];
|
|
217
|
+
}, [messages]);
|
|
218
|
+
// LLM conversation history
|
|
219
|
+
const llmMessages = useRef([
|
|
220
|
+
{ role: 'system', content: getSystemPrompt(persona) }
|
|
221
|
+
]);
|
|
222
|
+
// Keep system prompt in sync when persona changes (fixes #46 - persona lost on model fallback)
|
|
223
|
+
useEffect(() => {
|
|
224
|
+
const firstMsg = llmMessages.current[0];
|
|
225
|
+
if (firstMsg && firstMsg.role === 'system') {
|
|
226
|
+
// Preserve any appended memory context
|
|
227
|
+
const currentContent = typeof firstMsg.content === 'string' ? firstMsg.content : '';
|
|
228
|
+
const memoryIdx = currentContent.indexOf('\n\n--- Project Context ---\n');
|
|
229
|
+
const memoryPart = memoryIdx >= 0 ? currentContent.slice(memoryIdx) : '';
|
|
230
|
+
llmMessages.current[0] = {
|
|
231
|
+
role: 'system',
|
|
232
|
+
content: getSystemPrompt(persona) + memoryPart,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}, [persona]);
|
|
236
|
+
// Estimate context tokens (conservative: ~2.5 chars per token + 1.35x overhead)
|
|
237
|
+
const estimateContextTokens = useCallback(() => {
|
|
238
|
+
let chars = 0;
|
|
239
|
+
let msgCount = 0;
|
|
240
|
+
for (const msg of llmMessages.current) {
|
|
241
|
+
msgCount++;
|
|
242
|
+
if (typeof msg.content === 'string') {
|
|
243
|
+
chars += msg.content.length;
|
|
244
|
+
}
|
|
245
|
+
else if (Array.isArray(msg.content)) {
|
|
246
|
+
for (const block of msg.content) {
|
|
247
|
+
if (block.type === 'text') {
|
|
248
|
+
chars += block.text.length;
|
|
249
|
+
}
|
|
250
|
+
else if (block.type === 'image') {
|
|
251
|
+
chars += 1000; // Images count as ~250 tokens
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// Include tool call arguments in estimation
|
|
256
|
+
if (msg.toolCalls) {
|
|
257
|
+
for (const tool of msg.toolCalls) {
|
|
258
|
+
chars += JSON.stringify(tool.arguments || {}).length;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// Conservative: 2.5 chars/token, 1.35x overhead for formatting/metadata, +50 per message
|
|
263
|
+
return Math.round((chars / 2.5) * 1.35 + msgCount * 50);
|
|
264
|
+
}, []);
|
|
265
|
+
// Session state
|
|
266
|
+
const sessionRef = useRef(null);
|
|
267
|
+
const [autoRoute, setAutoRoute] = useState(false); // Auto model routing
|
|
268
|
+
const [smartRouteActive, setSmartRouteActive] = useState(() => config.get('smartRoutingEnabled') ?? false);
|
|
269
|
+
const [breakerHealth, setBreakerHealth] = useState('ok');
|
|
270
|
+
const ledgerRef = useRef(new IterationLedger());
|
|
271
|
+
const circuitBreakerRef = useRef(config.get('circuitBreakersEnabled') !== false ? (() => {
|
|
272
|
+
const iterTimeSec = config.get('maxIterationTime');
|
|
273
|
+
const cb = new CircuitBreaker();
|
|
274
|
+
if (typeof iterTimeSec === 'number' && iterTimeSec > 0) {
|
|
275
|
+
cb.adjust('wall-clock', { maxIterationDurationMs: iterTimeSec * 1000 });
|
|
276
|
+
}
|
|
277
|
+
// Local/free providers: disable cost breaker, relax token limits
|
|
278
|
+
const prov = config.get('defaultProvider');
|
|
279
|
+
if (prov === 'ollama' || prov === 'litellm') {
|
|
280
|
+
cb.adjust('cost-runaway', { maxSessionCost: 999999, maxCostPerMinute: 999999 });
|
|
281
|
+
cb.adjust('token-burn', { maxTokensPerIteration: 500_000, maxTotalTokens: 20_000_000 });
|
|
282
|
+
}
|
|
283
|
+
return cb;
|
|
284
|
+
})() : null);
|
|
285
|
+
const smartRoutingConfigRef = useRef({
|
|
286
|
+
...getDefaultSmartRoutingConfig(),
|
|
287
|
+
enabled: config.get('smartRoutingEnabled') ?? false,
|
|
288
|
+
costSensitivity: config.get('smartRoutingCostSensitivity') ?? 0.3,
|
|
289
|
+
});
|
|
290
|
+
const [memoryLoaded, setMemoryLoaded] = useState(false);
|
|
291
|
+
// Agent loop state
|
|
292
|
+
const [loopActive, setLoopActive] = useState(false);
|
|
293
|
+
const [loopPrompt, setLoopPrompt] = useState('');
|
|
294
|
+
const [loopMaxIterations, setLoopMaxIterations] = useState(100);
|
|
295
|
+
const [loopCompletionPromise, setLoopCompletionPromise] = useState();
|
|
296
|
+
const [loopIteration, setLoopIteration] = useState(0);
|
|
297
|
+
const loopCancelledRef = useRef(false);
|
|
298
|
+
// Initialize session and load memory on mount
|
|
299
|
+
useEffect(() => {
|
|
300
|
+
const cwd = process.cwd();
|
|
301
|
+
// Check for existing session with messages
|
|
302
|
+
const existingSessions = storage.listSessions(5);
|
|
303
|
+
const recentSession = existingSessions.find(s => s.projectPath === cwd &&
|
|
304
|
+
s.messageCount > 0 &&
|
|
305
|
+
Date.now() - new Date(s.lastAccessedAt).getTime() < 24 * 60 * 60 * 1000 // Within 24 hours
|
|
306
|
+
);
|
|
307
|
+
if (recentSession && !sessionRef.current) {
|
|
308
|
+
// Offer to resume
|
|
309
|
+
setPreviousSession({
|
|
310
|
+
projectName: recentSession.projectName,
|
|
311
|
+
lastAccessedAt: recentSession.lastAccessedAt,
|
|
312
|
+
messageCount: recentSession.messageCount,
|
|
313
|
+
});
|
|
314
|
+
setModalMode('session-resume');
|
|
315
|
+
}
|
|
316
|
+
const session = storage.getOrCreateSession(cwd);
|
|
317
|
+
sessionRef.current = session;
|
|
318
|
+
// Load memory context into system prompt
|
|
319
|
+
if (!memoryLoaded) {
|
|
320
|
+
const cwdMem = process.cwd();
|
|
321
|
+
const memoryContext = memory.buildMemoryContext(cwdMem);
|
|
322
|
+
if (memoryContext.trim()) {
|
|
323
|
+
// Append memory context to system prompt
|
|
324
|
+
const currentSystem = llmMessages.current[0];
|
|
325
|
+
if (currentSystem && currentSystem.role === 'system') {
|
|
326
|
+
const systemContent = typeof currentSystem.content === 'string'
|
|
327
|
+
? currentSystem.content
|
|
328
|
+
: '';
|
|
329
|
+
llmMessages.current[0] = {
|
|
330
|
+
role: 'system',
|
|
331
|
+
content: systemContent + '\n\n--- Project Context ---\n' + memoryContext,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
setMemoryLoaded(true);
|
|
336
|
+
// Execute session start hooks
|
|
337
|
+
hooks.executeHooks('session-start', {}).catch((err) => {
|
|
338
|
+
debugLog('hooks', 'session-start hook failed:', err instanceof Error ? err.message : err);
|
|
339
|
+
});
|
|
340
|
+
// Start session recording (audit log) — respects config
|
|
341
|
+
recording.setRecordingEnabled(config.get('recordSessions') !== false);
|
|
342
|
+
recording.setRetentionDays(config.get('recordingRetentionDays') ?? 0);
|
|
343
|
+
recording.startRecording({
|
|
344
|
+
provider: selectProvider(provider),
|
|
345
|
+
model: model || DEFAULT_MODELS[selectProvider(provider)],
|
|
346
|
+
cwd: cwdMem,
|
|
347
|
+
});
|
|
348
|
+
// Configure session timeout (opt-in via config)
|
|
349
|
+
const timeoutMs = config.get('sessionTimeoutMs');
|
|
350
|
+
if (timeoutMs) {
|
|
351
|
+
sessionTimeout.configureTimeout({
|
|
352
|
+
enabled: true,
|
|
353
|
+
idleTimeoutMs: typeof timeoutMs === 'number' ? timeoutMs : 2 * 60 * 60 * 1000,
|
|
354
|
+
});
|
|
355
|
+
sessionTimeout.onTimeout((type) => {
|
|
356
|
+
if (type === 'warning') {
|
|
357
|
+
addMessage('system', `\u23f1\ufe0f Session will timeout in ${sessionTimeout.formatTimeRemaining()}`);
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
addMessage('system', '\ud83d\udeaa Session timeout. Saving and exiting...');
|
|
361
|
+
storage.saveMessageHistory(llmMessages.current);
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
// Start idle eviction monitor
|
|
366
|
+
idleEviction.configureEviction({ enabled: true });
|
|
367
|
+
idleEviction.onEviction((action) => {
|
|
368
|
+
if (action === 'auto-save') {
|
|
369
|
+
storage.saveMessageHistory(llmMessages.current);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
// Log tmux context if applicable
|
|
373
|
+
if (isTmux()) {
|
|
374
|
+
const info = getTmuxInfo();
|
|
375
|
+
if (info) {
|
|
376
|
+
debugLog('tmux', `session=${info.session}, windows=${info.windows}, panes=${info.panes}`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
// Load templates from storage
|
|
380
|
+
const savedTemplates = storage.getTemplates();
|
|
381
|
+
if (savedTemplates.length > 0) {
|
|
382
|
+
setTemplates(savedTemplates.map(t => ({
|
|
383
|
+
name: t.name,
|
|
384
|
+
prompt: t.prompt,
|
|
385
|
+
createdAt: new Date(t.createdAt),
|
|
386
|
+
})));
|
|
387
|
+
}
|
|
388
|
+
// Pre-warm model cache in background for faster model switching
|
|
389
|
+
preWarmModelCache().catch((err) => {
|
|
390
|
+
debugLog('cache', 'model cache pre-warm failed:', err instanceof Error ? err.message : err);
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
}, [memoryLoaded]);
|
|
394
|
+
// Derived values
|
|
395
|
+
const actualProvider = selectProvider(provider);
|
|
396
|
+
const actualModel = model || DEFAULT_MODELS[actualProvider];
|
|
397
|
+
const isModalActive = modalMode !== 'none';
|
|
398
|
+
// Add message helper
|
|
399
|
+
const addMessage = useCallback((type, content) => {
|
|
400
|
+
setMessages(prev => [...prev, {
|
|
401
|
+
id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
402
|
+
type,
|
|
403
|
+
content
|
|
404
|
+
}]);
|
|
405
|
+
// Persist user and assistant messages to storage for session history
|
|
406
|
+
if (type === 'user' || type === 'assistant') {
|
|
407
|
+
storage.addChatMessage({ role: type, content });
|
|
408
|
+
}
|
|
409
|
+
}, []);
|
|
410
|
+
// Handler to edit or delete a queued message
|
|
411
|
+
const handleEditQueuedMessage = useCallback((index, newMsg) => {
|
|
412
|
+
if (newMsg === '') {
|
|
413
|
+
// Delete the message
|
|
414
|
+
setQueuedMessages(prev => prev.filter((_, i) => i !== index));
|
|
415
|
+
addMessage('system', `🗑️ Deleted queued message #${index + 1}`);
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
// Update the message
|
|
419
|
+
setQueuedMessages(prev => prev.map((msg, i) => i === index ? newMsg : msg));
|
|
420
|
+
addMessage('system', `✏️ Updated queued message #${index + 1}`);
|
|
421
|
+
}
|
|
422
|
+
}, [addMessage]);
|
|
423
|
+
// Validate and repair message history
|
|
424
|
+
const validateAndRepairMessages = useCallback(() => {
|
|
425
|
+
return validateAndRepairMessagesImpl({
|
|
426
|
+
llmMessages,
|
|
427
|
+
addMessage,
|
|
428
|
+
debugLog,
|
|
429
|
+
});
|
|
430
|
+
}, [addMessage]);
|
|
431
|
+
// Build agent context
|
|
432
|
+
const buildAgentContext = useCallback(() => ({
|
|
433
|
+
provider,
|
|
434
|
+
model,
|
|
435
|
+
mode,
|
|
436
|
+
confirmMode,
|
|
437
|
+
autoRoute,
|
|
438
|
+
actualProvider,
|
|
439
|
+
actualModel,
|
|
440
|
+
stats,
|
|
441
|
+
agtermEnabled: moduleAgtermEnabled,
|
|
442
|
+
ledger: ledgerRef.current,
|
|
443
|
+
circuitBreaker: circuitBreakerRef.current || undefined,
|
|
444
|
+
smartRouteActive,
|
|
445
|
+
smartRoutingConfig: smartRoutingConfigRef.current,
|
|
446
|
+
setBreakerHealth,
|
|
447
|
+
setStats,
|
|
448
|
+
setStreamingResponse,
|
|
449
|
+
setThinkingState,
|
|
450
|
+
setActivityState,
|
|
451
|
+
setContextTokens,
|
|
452
|
+
setIsProcessing,
|
|
453
|
+
setQueuedMessages,
|
|
454
|
+
setEditingQueueIndex,
|
|
455
|
+
setLoopIteration,
|
|
456
|
+
setLoopActive,
|
|
457
|
+
llmMessages,
|
|
458
|
+
queuedMessagesRef,
|
|
459
|
+
loopCancelledRef,
|
|
460
|
+
sessionRef,
|
|
461
|
+
addMessage,
|
|
462
|
+
estimateContextTokens,
|
|
463
|
+
validateAndRepairMessages,
|
|
464
|
+
debugLog,
|
|
465
|
+
}), [provider, model, mode, confirmMode, autoRoute, smartRouteActive, actualProvider, actualModel, stats, addMessage, estimateContextTokens, validateAndRepairMessages]);
|
|
466
|
+
// Run agent with user prompt
|
|
467
|
+
const runAgent = useCallback(async (content) => {
|
|
468
|
+
const ctx = buildAgentContext();
|
|
469
|
+
await runAgentImpl(ctx, content);
|
|
470
|
+
}, [buildAgentContext]);
|
|
471
|
+
// Agent loop - runs prompt repeatedly until completion promise or max iterations
|
|
472
|
+
const runLoop = useCallback(async (prompt, maxIter, completionPromise) => {
|
|
473
|
+
const ctx = buildAgentContext();
|
|
474
|
+
await runLoopImpl(ctx, prompt, maxIter, completionPromise);
|
|
475
|
+
}, [buildAgentContext]);
|
|
476
|
+
// Build command context
|
|
477
|
+
const buildCommandContext = useCallback(() => ({
|
|
478
|
+
actualProvider,
|
|
479
|
+
actualModel,
|
|
480
|
+
provider,
|
|
481
|
+
model,
|
|
482
|
+
persona,
|
|
483
|
+
mode,
|
|
484
|
+
confirmMode,
|
|
485
|
+
autoRoute,
|
|
486
|
+
layout,
|
|
487
|
+
density,
|
|
488
|
+
collapseSettings,
|
|
489
|
+
messages,
|
|
490
|
+
stats,
|
|
491
|
+
loopActive,
|
|
492
|
+
isProcessing,
|
|
493
|
+
thinkingState,
|
|
494
|
+
streamingResponse,
|
|
495
|
+
queuedMessages,
|
|
496
|
+
bookmarks,
|
|
497
|
+
templates,
|
|
498
|
+
agtermEnabled: moduleAgtermEnabled,
|
|
499
|
+
debugEnabled,
|
|
500
|
+
modalMode,
|
|
501
|
+
circuitBreaker: circuitBreakerRef.current || undefined,
|
|
502
|
+
smartRouteActive,
|
|
503
|
+
smartRoutingConfig: smartRoutingConfigRef.current,
|
|
504
|
+
setProvider,
|
|
505
|
+
setModel,
|
|
506
|
+
setPersona,
|
|
507
|
+
setMode,
|
|
508
|
+
setConfirmMode,
|
|
509
|
+
setAutoRoute,
|
|
510
|
+
setLayout: setLayout,
|
|
511
|
+
setDensity: setDensity,
|
|
512
|
+
setCollapseSettings,
|
|
513
|
+
setMessages,
|
|
514
|
+
setStats,
|
|
515
|
+
setModalMode: setModalMode,
|
|
516
|
+
setPendingComplexPrompt,
|
|
517
|
+
setAvailableModels,
|
|
518
|
+
setAvailableSessions,
|
|
519
|
+
setLatestVersion,
|
|
520
|
+
setLoopActive,
|
|
521
|
+
setLoopPrompt,
|
|
522
|
+
setLoopMaxIterations,
|
|
523
|
+
setLoopCompletionPromise,
|
|
524
|
+
setLoopIteration,
|
|
525
|
+
setIsProcessing,
|
|
526
|
+
setThinkingState,
|
|
527
|
+
setStreamingResponse,
|
|
528
|
+
setQueuedMessages,
|
|
529
|
+
setInput,
|
|
530
|
+
setBookmarks,
|
|
531
|
+
setTemplates,
|
|
532
|
+
setContextTokens,
|
|
533
|
+
setDebugEnabled: (v) => { debugEnabled = v; },
|
|
534
|
+
setSmartRouteActive,
|
|
535
|
+
setBreakerHealth,
|
|
536
|
+
llmMessages,
|
|
537
|
+
undoStack,
|
|
538
|
+
redoStack,
|
|
539
|
+
loopCancelledRef,
|
|
540
|
+
sessionRef,
|
|
541
|
+
addMessage,
|
|
542
|
+
estimateContextTokens,
|
|
543
|
+
saveUndoState,
|
|
544
|
+
runAgent,
|
|
545
|
+
runLoop,
|
|
546
|
+
exit,
|
|
547
|
+
}), [actualProvider, actualModel, provider, model, persona, mode, confirmMode, autoRoute, smartRouteActive,
|
|
548
|
+
layout, density, collapseSettings, messages, stats, loopActive, isProcessing,
|
|
549
|
+
thinkingState, streamingResponse, queuedMessages, bookmarks, templates, modalMode,
|
|
550
|
+
addMessage, estimateContextTokens, saveUndoState, runAgent, runLoop, exit]);
|
|
551
|
+
// Handle slash commands
|
|
552
|
+
const handleCommandWrapped = useCallback(async (cmd) => {
|
|
553
|
+
const ctx = buildCommandContext();
|
|
554
|
+
await handleCommand(cmd, ctx);
|
|
555
|
+
}, [buildCommandContext]);
|
|
556
|
+
// Handle input submission
|
|
557
|
+
const handleSubmit = useCallback(async (value) => {
|
|
558
|
+
const trimmed = value.trim();
|
|
559
|
+
if (!trimmed || isProcessing)
|
|
560
|
+
return;
|
|
561
|
+
// Record activity for timeout/eviction and audit log
|
|
562
|
+
sessionTimeout.recordActivity();
|
|
563
|
+
idleEviction.recordActivity();
|
|
564
|
+
recording.recordEvent('input', trimmed);
|
|
565
|
+
// Add to history for up/down arrow navigation
|
|
566
|
+
addToHistory(trimmed);
|
|
567
|
+
setInput('');
|
|
568
|
+
if (trimmed.startsWith('/')) {
|
|
569
|
+
await handleCommandWrapped(trimmed);
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
// ! prefix executes shell commands directly
|
|
573
|
+
if (trimmed.startsWith('!')) {
|
|
574
|
+
const shellCmd = trimmed.slice(1).trim();
|
|
575
|
+
if (shellCmd) {
|
|
576
|
+
addMessage('system', `$ ${shellCmd}`);
|
|
577
|
+
try {
|
|
578
|
+
const { execSync } = await import('child_process');
|
|
579
|
+
const output = execSync(shellCmd, {
|
|
580
|
+
cwd: process.cwd(),
|
|
581
|
+
encoding: 'utf-8',
|
|
582
|
+
timeout: 30000,
|
|
583
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
584
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
585
|
+
}).trim();
|
|
586
|
+
addMessage('system', output || '(no output)');
|
|
587
|
+
}
|
|
588
|
+
catch (err) {
|
|
589
|
+
const execErr = err;
|
|
590
|
+
addMessage('error', execErr.stderr?.trim() || execErr.message || String(err));
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
// In hybrid mode, check for complex operations
|
|
596
|
+
if (mode === 'hybrid') {
|
|
597
|
+
const complexity = detectComplexity(trimmed);
|
|
598
|
+
if (complexity.isComplex) {
|
|
599
|
+
setPendingComplexPrompt({ prompt: trimmed, complexity });
|
|
600
|
+
setModalMode('complexity-warning');
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
// Save state for undo before modifying conversation
|
|
605
|
+
saveUndoState();
|
|
606
|
+
// Parse file references from input
|
|
607
|
+
const { text: cleanText, files } = parseFileReferences(trimmed, process.cwd());
|
|
608
|
+
// Show user message (with file info if any)
|
|
609
|
+
if (files.length > 0) {
|
|
610
|
+
const fileInfo = formatFileInfo(files);
|
|
611
|
+
addMessage('user', `${cleanText}\n📎 ${fileInfo}`);
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
addMessage('user', trimmed);
|
|
615
|
+
}
|
|
616
|
+
setIsProcessing(true);
|
|
617
|
+
try {
|
|
618
|
+
// Build message content (with file/image support)
|
|
619
|
+
let messageContent;
|
|
620
|
+
if (files.length > 0) {
|
|
621
|
+
const visionSupported = supportsVision(provider, model);
|
|
622
|
+
const { content, warnings } = processFilesForMessage(cleanText || trimmed, files, visionSupported);
|
|
623
|
+
// Show any warnings about files
|
|
624
|
+
for (const warning of warnings) {
|
|
625
|
+
addMessage('system', warning);
|
|
626
|
+
}
|
|
627
|
+
messageContent = content;
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
messageContent = trimmed;
|
|
631
|
+
}
|
|
632
|
+
await runAgent(messageContent);
|
|
633
|
+
}
|
|
634
|
+
finally {
|
|
635
|
+
setIsProcessing(false);
|
|
636
|
+
setThinkingState(null);
|
|
637
|
+
setStreamingResponse('');
|
|
638
|
+
}
|
|
639
|
+
}, [isProcessing, handleCommandWrapped, runAgent, addMessage, provider, model, saveUndoState, addToHistory, mode]);
|
|
640
|
+
// Modal handlers
|
|
641
|
+
const handleModelSelect = useCallback((selectedModel) => {
|
|
642
|
+
setModel(selectedModel);
|
|
643
|
+
addMessage('system', `Model: ${selectedModel}`);
|
|
644
|
+
setModalMode('none');
|
|
645
|
+
setAvailableModels([]);
|
|
646
|
+
}, [addMessage]);
|
|
647
|
+
const handleModalCancel = useCallback(() => {
|
|
648
|
+
setModalMode('none');
|
|
649
|
+
setAvailableModels([]);
|
|
650
|
+
setLatestVersion(null);
|
|
651
|
+
}, []);
|
|
652
|
+
const handleUpgradeConfirm = useCallback(async () => {
|
|
653
|
+
setModalMode('none');
|
|
654
|
+
addMessage('system', 'Upgrading...');
|
|
655
|
+
try {
|
|
656
|
+
const { performUpgrade } = await import('../version-check.js');
|
|
657
|
+
const success = await performUpgrade();
|
|
658
|
+
if (success) {
|
|
659
|
+
addMessage('system', 'Upgrade complete! Restarting...');
|
|
660
|
+
const { spawn } = await import('child_process');
|
|
661
|
+
const child = spawn(process.argv[0], process.argv.slice(1), {
|
|
662
|
+
stdio: 'inherit',
|
|
663
|
+
detached: true,
|
|
664
|
+
});
|
|
665
|
+
child.unref();
|
|
666
|
+
process.exit(0);
|
|
667
|
+
}
|
|
668
|
+
else {
|
|
669
|
+
addMessage('error', 'Upgrade failed. Try: npm install -g @calliopelabs/cli@latest');
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
catch (e) {
|
|
673
|
+
addMessage('error', `Upgrade failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
674
|
+
}
|
|
675
|
+
setLatestVersion(null);
|
|
676
|
+
}, [addMessage]);
|
|
677
|
+
// Cycle through modes
|
|
678
|
+
const cycleMode = useCallback(() => {
|
|
679
|
+
setMode(current => {
|
|
680
|
+
const modes = ['plan', 'hybrid', 'work'];
|
|
681
|
+
const idx = modes.indexOf(current);
|
|
682
|
+
const next = modes[(idx + 1) % modes.length];
|
|
683
|
+
return next;
|
|
684
|
+
});
|
|
685
|
+
}, []);
|
|
686
|
+
// Handle Escape key - cancel operation if processing, otherwise show hint
|
|
687
|
+
const handleEscape = useCallback(() => {
|
|
688
|
+
if (isProcessing) {
|
|
689
|
+
// Cancel current operation
|
|
690
|
+
setIsProcessing(false);
|
|
691
|
+
setThinkingState(null);
|
|
692
|
+
setStreamingResponse('');
|
|
693
|
+
setLoopActive(false);
|
|
694
|
+
setEditingQueueIndex(null);
|
|
695
|
+
addMessage('system', '⏹ Operation cancelled. Use /exit to quit.');
|
|
696
|
+
}
|
|
697
|
+
else if (modalMode !== 'none') {
|
|
698
|
+
// Close any open modal
|
|
699
|
+
setModalMode('none');
|
|
700
|
+
setPendingComplexPrompt(null);
|
|
701
|
+
}
|
|
702
|
+
else {
|
|
703
|
+
// Not processing - show hint instead of exiting
|
|
704
|
+
addMessage('system', '💡 Use /exit to quit, or Ctrl+C.');
|
|
705
|
+
}
|
|
706
|
+
}, [isProcessing, modalMode, addMessage]);
|
|
707
|
+
// Handle direct send (Shift+Enter) - interrupts current operation and sends immediately
|
|
708
|
+
const handleDirectSend = useCallback((msg) => {
|
|
709
|
+
// Stop current processing
|
|
710
|
+
setIsProcessing(false);
|
|
711
|
+
setThinkingState(null);
|
|
712
|
+
setStreamingResponse('');
|
|
713
|
+
setEditingQueueIndex(null);
|
|
714
|
+
// Show what happened
|
|
715
|
+
addMessage('system', '⚡ Direct send - interrupting current operation');
|
|
716
|
+
addMessage('user', msg);
|
|
717
|
+
// Start new agent run with this message
|
|
718
|
+
setIsProcessing(true);
|
|
719
|
+
runAgent(msg).finally(() => {
|
|
720
|
+
setIsProcessing(false);
|
|
721
|
+
setThinkingState(null);
|
|
722
|
+
setStreamingResponse('');
|
|
723
|
+
setEditingQueueIndex(null);
|
|
724
|
+
});
|
|
725
|
+
}, [addMessage, runAgent]);
|
|
726
|
+
// Streaming response component (reused across layouts)
|
|
727
|
+
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;
|
|
728
|
+
// Thinking/Processing indicator component with state transitions
|
|
729
|
+
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 })] }));
|
|
730
|
+
// Render based on layout
|
|
731
|
+
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) => {
|
|
732
|
+
// Load history from selected session
|
|
733
|
+
addMessage('system', `Loading session: ${session.projectName}...`);
|
|
734
|
+
addMessage('system', `Session path: ${session.projectPath}\nTo load this session, run calliope from that directory.`);
|
|
735
|
+
setModalMode('none');
|
|
736
|
+
}, onDelete: (session) => {
|
|
737
|
+
if (storage.deleteSession(session.id)) {
|
|
738
|
+
addMessage('system', `🗑️ Deleted session: ${session.projectName}`);
|
|
739
|
+
// Refresh the list
|
|
740
|
+
setAvailableSessions(prev => prev.filter(s => s.id !== session.id));
|
|
741
|
+
}
|
|
742
|
+
else {
|
|
743
|
+
addMessage('error', `Failed to delete session: ${session.projectName}`);
|
|
744
|
+
}
|
|
745
|
+
}, onCancel: handleModalCancel })), modalMode === 'upgrade' && latestVersion && (_jsx(UpgradePrompt, { currentVersion: getVersion(), latestVersion: latestVersion, onConfirm: handleUpgradeConfirm, onCancel: handleModalCancel })), modalMode === 'session-resume' && previousSession && (_jsx(SessionResumePrompt, { session: previousSession, onResume: () => {
|
|
746
|
+
// Load chat history into context
|
|
747
|
+
const history = storage.getChatHistory(20);
|
|
748
|
+
if (history.length > 0) {
|
|
749
|
+
for (const msg of history) {
|
|
750
|
+
if (msg.role === 'user' || msg.role === 'assistant') {
|
|
751
|
+
llmMessages.current.push({
|
|
752
|
+
role: msg.role,
|
|
753
|
+
content: msg.content,
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
addMessage('system', `✓ Resumed session with ${history.length} messages loaded`);
|
|
758
|
+
setContextTokens(estimateContextTokens());
|
|
759
|
+
}
|
|
760
|
+
setModalMode('none');
|
|
761
|
+
setPreviousSession(null);
|
|
762
|
+
}, onNew: () => {
|
|
763
|
+
addMessage('system', '✓ Starting fresh session');
|
|
764
|
+
setModalMode('none');
|
|
765
|
+
setPreviousSession(null);
|
|
766
|
+
} })), modalMode === 'complexity-warning' && pendingComplexPrompt && (_jsx(ComplexityWarning, { reason: pendingComplexPrompt.complexity.reason || 'Complex operation detected', prompt: typeof pendingComplexPrompt.prompt === 'string' ? pendingComplexPrompt.prompt : undefined, onProceed: async () => {
|
|
767
|
+
setModalMode('none');
|
|
768
|
+
const prompt = pendingComplexPrompt.prompt;
|
|
769
|
+
setPendingComplexPrompt(null);
|
|
770
|
+
// Proceed with execution
|
|
771
|
+
saveUndoState();
|
|
772
|
+
addMessage('user', typeof prompt === 'string' ? prompt : JSON.stringify(prompt));
|
|
773
|
+
setIsProcessing(true);
|
|
774
|
+
try {
|
|
775
|
+
await runAgent(prompt);
|
|
776
|
+
}
|
|
777
|
+
finally {
|
|
778
|
+
setIsProcessing(false);
|
|
779
|
+
}
|
|
780
|
+
}, onPlan: () => {
|
|
781
|
+
setModalMode('none');
|
|
782
|
+
const prompt = pendingComplexPrompt.prompt;
|
|
783
|
+
setPendingComplexPrompt(null);
|
|
784
|
+
// Switch to plan mode and proceed
|
|
785
|
+
setMode('plan');
|
|
786
|
+
addMessage('system', '📋 Switched to Plan mode - I\'ll describe what I would do without executing.');
|
|
787
|
+
saveUndoState();
|
|
788
|
+
addMessage('user', typeof prompt === 'string' ? prompt : JSON.stringify(prompt));
|
|
789
|
+
setIsProcessing(true);
|
|
790
|
+
runAgent(prompt).finally(() => setIsProcessing(false));
|
|
791
|
+
}, onCancel: () => {
|
|
792
|
+
setModalMode('none');
|
|
793
|
+
setPendingComplexPrompt(null);
|
|
794
|
+
addMessage('system', 'Operation cancelled.');
|
|
795
|
+
} })), modalMode === 'keys' && (_jsx(KeybindingsModal, { onClose: () => setModalMode('none') })), modalMode === 'theme-picker' && (_jsx(ThemePicker, { currentLayout: layout, currentSkin: getCurrentSkin().name, currentPalette: getCurrentPalette().name, currentCompanion: getCurrentCompanion().name, onApply: (selection) => {
|
|
796
|
+
// Apply all selections
|
|
797
|
+
setLayout(selection.layout);
|
|
798
|
+
config.set('layout', selection.layout);
|
|
799
|
+
applySkin(selection.skin);
|
|
800
|
+
config.set('activeSkin', selection.skin);
|
|
801
|
+
applyPalette(selection.palette);
|
|
802
|
+
config.set('activePalette', selection.palette);
|
|
803
|
+
applyCompanion(selection.companion);
|
|
804
|
+
config.set('activeCompanion', selection.companion);
|
|
805
|
+
const changes = [];
|
|
806
|
+
if (selection.layout !== layout)
|
|
807
|
+
changes.push(`layout=${selection.layout}`);
|
|
808
|
+
if (selection.skin !== getCurrentSkin().name)
|
|
809
|
+
changes.push(`skin=${selection.skin}`);
|
|
810
|
+
if (selection.palette !== getCurrentPalette().name)
|
|
811
|
+
changes.push(`palette=${selection.palette}`);
|
|
812
|
+
if (selection.companion !== getCurrentCompanion().name)
|
|
813
|
+
changes.push(`companion=${selection.companion}`);
|
|
814
|
+
addMessage('system', changes.length > 0
|
|
815
|
+
? `Theme applied: ${changes.join(', ')}`
|
|
816
|
+
: 'Theme unchanged.');
|
|
817
|
+
setModalMode('none');
|
|
818
|
+
}, onCancel: () => setModalMode('none') })), modalMode === 'pack-picker' && (_jsx(PackPicker, { onApply: async (packName) => {
|
|
819
|
+
setModalMode('none');
|
|
820
|
+
// Run transition animation if defined
|
|
821
|
+
const targetPack = getThemePack(packName);
|
|
822
|
+
if (targetPack?.skin.splash?.transition) {
|
|
823
|
+
await renderTransition(targetPack.skin.splash.transition);
|
|
824
|
+
}
|
|
825
|
+
const success = applyThemePack(packName, getCompanionMode());
|
|
826
|
+
if (success) {
|
|
827
|
+
const pack = getCurrentPack();
|
|
828
|
+
config.set('activeThemePack', packName);
|
|
829
|
+
config.set('activeSkin', pack.skin.name);
|
|
830
|
+
config.set('activePalette', pack.palette.name);
|
|
831
|
+
const companion = getCompanionMode() === 'professional'
|
|
832
|
+
? pack.companions.professional
|
|
833
|
+
: pack.companions.immersive;
|
|
834
|
+
config.set('activeCompanion', companion.name);
|
|
835
|
+
addMessage('system', `Theme pack: ${packName}\n` +
|
|
836
|
+
` Skin: ${pack.skin.name}, Palette: ${pack.palette.name}, Companion: ${companion.name}\n` +
|
|
837
|
+
` "${companion.greeting}"`);
|
|
838
|
+
}
|
|
839
|
+
}, onCancel: () => setModalMode('none') })), _jsx(ChatInput, { value: input, onChange: handleInputChange, onSubmit: handleSubmit, onEscape: handleEscape, onCycleMode: cycleMode, disabled: isModalActive, isProcessing: isProcessing, queuedCount: queuedMessages.length, queuedMessages: queuedMessages, editingQueueIndex: editingQueueIndex, onQueueMessage: (msg) => {
|
|
840
|
+
setQueuedMessages(prev => [...prev, msg]);
|
|
841
|
+
addMessage('system', `📨 Queued: "${msg.substring(0, 50)}${msg.length > 50 ? '...' : ''}"`);
|
|
842
|
+
}, onEditQueuedMessage: handleEditQueuedMessage, onSetEditingQueueIndex: setEditingQueueIndex, onDirectSend: handleDirectSend, cwd: process.cwd(), suggestions: suggestions, onSuggestionsChange: setSuggestions, onNavigateHistory: navigateHistory,
|
|
843
|
+
// Smart suggestions context
|
|
844
|
+
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 })] }) }));
|
|
845
|
+
}
|
|
846
|
+
// ============================================================================
|
|
847
|
+
// App Wrapper & Entry Point
|
|
848
|
+
// ============================================================================
|
|
849
|
+
function App() {
|
|
850
|
+
const [resetKey, setResetKey] = React.useState(0);
|
|
851
|
+
const handleReset = React.useCallback(() => {
|
|
852
|
+
setResetKey(k => k + 1);
|
|
853
|
+
}, []);
|
|
854
|
+
return (_jsx(ErrorBoundary, { onReset: handleReset, children: _jsx(TerminalChat, {}, resetKey) }));
|
|
855
|
+
}
|
|
856
|
+
// Print banner before Ink takes over (stays fixed at top)
|
|
857
|
+
export async function printBanner() {
|
|
858
|
+
const provider = selectProvider(config.get('defaultProvider'));
|
|
859
|
+
const model = config.get('defaultModel') || DEFAULT_MODELS[provider];
|
|
860
|
+
const skin = getCurrentSkin();
|
|
861
|
+
const dim = '\x1b[2m';
|
|
862
|
+
const reset = '\x1b[0m';
|
|
863
|
+
// Run theme transition on startup if configured
|
|
864
|
+
if (skin.splash?.transition && skin.splash.transition.effect !== 'none') {
|
|
865
|
+
await renderTransition(skin.splash.transition);
|
|
866
|
+
}
|
|
867
|
+
if (skin.banner.style === 'none') {
|
|
868
|
+
// No banner
|
|
869
|
+
}
|
|
870
|
+
else if (skin.splash?.coloredArt && skin.splash.coloredArt.length > 0) {
|
|
871
|
+
// Rich colored banner from splash config
|
|
872
|
+
console.log();
|
|
873
|
+
if (skin.splash.entryAnimation && skin.splash.entryAnimation !== 'none') {
|
|
874
|
+
// Animated splash
|
|
875
|
+
const coloredLines = skin.splash.coloredArt.map(l => colorFg(l.text, l.color));
|
|
876
|
+
await renderSplashAnimation(coloredLines, skin.splash.entryAnimation, skin.splash.animationSpeed ?? 50);
|
|
877
|
+
}
|
|
878
|
+
else {
|
|
879
|
+
// Static colored banner
|
|
880
|
+
const banner = renderColoredBanner(skin.splash.coloredArt, skin.banner.tagline);
|
|
881
|
+
console.log(banner);
|
|
882
|
+
}
|
|
883
|
+
console.log();
|
|
884
|
+
if (skin.banner.tagline && skin.splash.entryAnimation) {
|
|
885
|
+
console.log(`${dim} ${skin.banner.tagline}${reset}`);
|
|
886
|
+
console.log();
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
else {
|
|
890
|
+
// Standard banner (existing behavior)
|
|
891
|
+
console.log();
|
|
892
|
+
for (const line of skin.banner.art) {
|
|
893
|
+
if (line.includes('\x1b[')) {
|
|
894
|
+
console.log(line);
|
|
895
|
+
}
|
|
896
|
+
else {
|
|
897
|
+
console.log(paletteColorize(line, 'primary'));
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
console.log();
|
|
901
|
+
if (skin.banner.tagline) {
|
|
902
|
+
console.log(`${dim} ${skin.banner.tagline}${reset}`);
|
|
903
|
+
console.log();
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
const companion = getCurrentCompanion();
|
|
907
|
+
console.log(`${dim} v${getVersion()} | ${provider}:${model}${reset}`);
|
|
908
|
+
console.log(`${dim} /help for commands | ESC to exit${reset}`);
|
|
909
|
+
if (companion.greeting) {
|
|
910
|
+
console.log(`${dim} ${companion.greeting}${reset}`);
|
|
911
|
+
}
|
|
912
|
+
console.log();
|
|
913
|
+
}
|
|
914
|
+
export async function startInkCLI(options = {}) {
|
|
915
|
+
// Set module-level agterm state
|
|
916
|
+
moduleAgtermEnabled = options.agtermEnabled ?? false;
|
|
917
|
+
// Print banner BEFORE Ink starts - it stays fixed at the top
|
|
918
|
+
await printBanner();
|
|
919
|
+
const { waitUntilExit } = render(_jsx(App, {}), {
|
|
920
|
+
patchConsole: true, // Prevent console.log during session from mixing with Ink
|
|
921
|
+
});
|
|
922
|
+
await waitUntilExit();
|
|
923
|
+
// Session cleanup
|
|
924
|
+
recording.stopRecording();
|
|
925
|
+
sessionTimeout.clearTimers();
|
|
926
|
+
idleEviction.stopMonitor();
|
|
927
|
+
}
|
|
928
|
+
//# sourceMappingURL=index.js.map
|