@calliopelabs/cli 2.3.0 → 2.5.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 +17 -0
- package/dist/agents/agent-config-loader.js +1 -1
- package/dist/agents/agent-config-presets.js +13 -13
- package/dist/agents/agent-config-presets.js.map +1 -1
- package/dist/agents/agent-config-types.d.ts +1 -1
- package/dist/agents/agent-config-types.d.ts.map +1 -1
- package/dist/agents/dynamic-tools.d.ts.map +1 -1
- package/dist/agents/dynamic-tools.js +39 -10
- package/dist/agents/dynamic-tools.js.map +1 -1
- package/dist/agents/sdk-backend.js +1 -1
- package/dist/agents/sdk-backend.js.map +1 -1
- package/dist/api-server.d.ts +9 -0
- package/dist/api-server.d.ts.map +1 -1
- package/dist/api-server.js +74 -3
- package/dist/api-server.js.map +1 -1
- package/dist/auto-checkpoint.d.ts.map +1 -1
- package/dist/auto-checkpoint.js +50 -17
- package/dist/auto-checkpoint.js.map +1 -1
- package/dist/auto-compressor.d.ts.map +1 -1
- package/dist/auto-compressor.js +9 -5
- package/dist/auto-compressor.js.map +1 -1
- package/dist/bin.d.ts +8 -0
- package/dist/bin.d.ts.map +1 -1
- package/dist/bin.js +59 -4
- package/dist/bin.js.map +1 -1
- package/dist/branching.d.ts.map +1 -1
- package/dist/branching.js +14 -1
- package/dist/branching.js.map +1 -1
- package/dist/checkpoint.d.ts.map +1 -1
- package/dist/checkpoint.js +13 -1
- package/dist/checkpoint.js.map +1 -1
- package/dist/cli/agent.d.ts.map +1 -1
- package/dist/cli/agent.js +19 -3
- package/dist/cli/agent.js.map +1 -1
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +99 -0
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +32 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/types.js +1 -1
- package/dist/cli/types.js.map +1 -1
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/diff.d.ts.map +1 -1
- package/dist/diff.js +42 -4
- package/dist/diff.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +30 -3
- package/dist/errors.js.map +1 -1
- package/dist/headless.d.ts.map +1 -1
- package/dist/headless.js +56 -2
- package/dist/headless.js.map +1 -1
- package/dist/hooks.d.ts +8 -2
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +97 -11
- package/dist/hooks.js.map +1 -1
- package/dist/idle-eviction.d.ts.map +1 -1
- package/dist/idle-eviction.js +8 -1
- package/dist/idle-eviction.js.map +1 -1
- package/dist/markdown.d.ts.map +1 -1
- package/dist/markdown.js +32 -10
- package/dist/markdown.js.map +1 -1
- package/dist/mcp.d.ts +35 -5
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +186 -12
- package/dist/mcp.js.map +1 -1
- package/dist/model-detection.d.ts +14 -1
- package/dist/model-detection.d.ts.map +1 -1
- package/dist/model-detection.js +307 -114
- package/dist/model-detection.js.map +1 -1
- package/dist/model-router.js +7 -7
- package/dist/model-router.js.map +1 -1
- package/dist/parallel-tools.d.ts +9 -1
- package/dist/parallel-tools.d.ts.map +1 -1
- package/dist/parallel-tools.js +6 -5
- package/dist/parallel-tools.js.map +1 -1
- package/dist/plugins.d.ts +37 -0
- package/dist/plugins.d.ts.map +1 -1
- package/dist/plugins.js +87 -0
- package/dist/plugins.js.map +1 -1
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +36 -2
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/bedrock.d.ts.map +1 -1
- package/dist/providers/bedrock.js +81 -17
- package/dist/providers/bedrock.js.map +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +2 -0
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/types.js +19 -10
- package/dist/providers/types.js.map +1 -1
- package/dist/risk.d.ts.map +1 -1
- package/dist/risk.js +15 -5
- package/dist/risk.js.map +1 -1
- package/dist/sandbox-native.d.ts +1 -0
- package/dist/sandbox-native.d.ts.map +1 -1
- package/dist/sandbox-native.js +37 -5
- package/dist/sandbox-native.js.map +1 -1
- package/dist/scope.d.ts +10 -0
- package/dist/scope.d.ts.map +1 -1
- package/dist/scope.js +75 -15
- package/dist/scope.js.map +1 -1
- package/dist/scuttlebot/client.d.ts +83 -0
- package/dist/scuttlebot/client.d.ts.map +1 -0
- package/dist/scuttlebot/client.js +350 -0
- package/dist/scuttlebot/client.js.map +1 -0
- package/dist/scuttlebot/config.d.ts +28 -0
- package/dist/scuttlebot/config.d.ts.map +1 -0
- package/dist/scuttlebot/config.js +91 -0
- package/dist/scuttlebot/config.js.map +1 -0
- package/dist/scuttlebot/http-client.d.ts +63 -0
- package/dist/scuttlebot/http-client.d.ts.map +1 -0
- package/dist/scuttlebot/http-client.js +124 -0
- package/dist/scuttlebot/http-client.js.map +1 -0
- package/dist/scuttlebot/index.d.ts +13 -0
- package/dist/scuttlebot/index.d.ts.map +1 -0
- package/dist/scuttlebot/index.js +10 -0
- package/dist/scuttlebot/index.js.map +1 -0
- package/dist/scuttlebot/irc-client.d.ts +124 -0
- package/dist/scuttlebot/irc-client.d.ts.map +1 -0
- package/dist/scuttlebot/irc-client.js +599 -0
- package/dist/scuttlebot/irc-client.js.map +1 -0
- package/dist/skills.d.ts +19 -0
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +98 -10
- package/dist/skills.js.map +1 -1
- package/dist/smart-router.js +4 -4
- package/dist/smart-router.js.map +1 -1
- package/dist/storage.d.ts +0 -4
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +81 -5
- package/dist/storage.js.map +1 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +232 -38
- package/dist/tools.js.map +1 -1
- package/dist/trust.d.ts +16 -3
- package/dist/trust.d.ts.map +1 -1
- package/dist/trust.js +23 -4
- package/dist/trust.js.map +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +13 -4
- package/dist/types.js.map +1 -1
- package/dist/ui/agent.d.ts +1 -1
- package/dist/ui/agent.d.ts.map +1 -1
- package/dist/ui/agent.js +35 -44
- package/dist/ui/agent.js.map +1 -1
- package/dist/ui/chat-input.d.ts +3 -1
- package/dist/ui/chat-input.d.ts.map +1 -1
- package/dist/ui/chat-input.js +82 -17
- package/dist/ui/chat-input.js.map +1 -1
- package/dist/ui/commands.d.ts +2 -0
- package/dist/ui/commands.d.ts.map +1 -1
- package/dist/ui/commands.js +318 -10
- package/dist/ui/commands.js.map +1 -1
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +236 -46
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/input-utils.d.ts +20 -0
- package/dist/ui/input-utils.d.ts.map +1 -0
- package/dist/ui/input-utils.js +35 -0
- package/dist/ui/input-utils.js.map +1 -0
- package/dist/ui/messages.d.ts +6 -2
- package/dist/ui/messages.d.ts.map +1 -1
- package/dist/ui/messages.js +42 -11
- package/dist/ui/messages.js.map +1 -1
- package/dist/ui/modals.d.ts +21 -1
- package/dist/ui/modals.d.ts.map +1 -1
- package/dist/ui/modals.js +67 -5
- package/dist/ui/modals.js.map +1 -1
- package/dist/ui/status-bar.d.ts +4 -1
- package/dist/ui/status-bar.d.ts.map +1 -1
- package/dist/ui/status-bar.js +12 -1
- package/dist/ui/status-bar.js.map +1 -1
- package/dist/ui/types.d.ts +3 -0
- package/dist/ui/types.d.ts.map +1 -1
- package/package.json +4 -7
- package/dist/completion.d.ts +0 -75
- package/dist/completion.d.ts.map +0 -1
- package/dist/completion.js +0 -234
- package/dist/completion.js.map +0 -1
- package/dist/keyboard.d.ts +0 -57
- package/dist/keyboard.d.ts.map +0 -1
- package/dist/keyboard.js +0 -265
- package/dist/keyboard.js.map +0 -1
package/dist/ui/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AA45CH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CA+DjD;AAED,wBAAsB,WAAW,CAAC,OAAO,GAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBrH"}
|
package/dist/ui/index.js
CHANGED
|
@@ -29,10 +29,11 @@ import * as recording from '../terminal-recording.js';
|
|
|
29
29
|
import * as sessionTimeout from '../session-timeout.js';
|
|
30
30
|
import * as idleEviction from '../idle-eviction.js';
|
|
31
31
|
import { isTmux, getTmuxInfo } from '../tmux.js';
|
|
32
|
+
import { scuttlebotClient } from '../scuttlebot/index.js';
|
|
32
33
|
import { ErrorBoundary } from './error-boundary.js';
|
|
33
34
|
import { ThinkingDisplay, ProcessingIndicator, StreamingIndicator, StateTransition } from './components.js';
|
|
34
35
|
import { MessageHistory } from './messages.js';
|
|
35
|
-
import { ModelSelector, SessionSelector, UpgradePrompt, ComplexityWarning, SessionResumePrompt, KeybindingsModal, } from './modals.js';
|
|
36
|
+
import { ModelSelector, SessionSelector, UpgradePrompt, ComplexityWarning, SessionResumePrompt, KeybindingsModal, ProviderSelector, ApiKeySetup, } from './modals.js';
|
|
36
37
|
import { ThemePicker } from './theme-picker.js';
|
|
37
38
|
import { PackPicker } from './pack-picker.js';
|
|
38
39
|
import { applyThemePack, getCurrentPack, getCompanionMode, getThemePack } from '../hud/theme-packs/api.js';
|
|
@@ -45,6 +46,23 @@ import { resolveIterationLimit } from '../iteration-limit.js';
|
|
|
45
46
|
setEmojiConfig(config);
|
|
46
47
|
// Module-level state for agterm mode
|
|
47
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
|
+
}
|
|
48
66
|
// Debug logging for flow control issues
|
|
49
67
|
let debugEnabled = process.env.CALLIOPE_DEBUG === '1';
|
|
50
68
|
const debugLog = (label, ...args) => {
|
|
@@ -70,7 +88,18 @@ function TerminalChat() {
|
|
|
70
88
|
return () => { process.stdout.off('resize', onResize); };
|
|
71
89
|
}, [stdout]);
|
|
72
90
|
// Core state
|
|
73
|
-
|
|
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;
|
|
74
103
|
const [suggestions, setSuggestions] = useState([]);
|
|
75
104
|
const [messages, setMessages] = useState([]);
|
|
76
105
|
const [isProcessing, setIsProcessing] = useState(false);
|
|
@@ -107,15 +136,17 @@ function TerminalChat() {
|
|
|
107
136
|
}
|
|
108
137
|
});
|
|
109
138
|
const recentCommands = React.useMemo(() => inputHistory.filter(cmd => cmd.startsWith('/')).slice(-10), [inputHistory]);
|
|
110
|
-
//
|
|
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.
|
|
111
144
|
const handleInputChange = useCallback((newValue) => {
|
|
112
|
-
|
|
113
|
-
// Clear suggestions if user clears input or submits
|
|
145
|
+
inputRef.current = newValue;
|
|
114
146
|
if (!newValue || !newValue.startsWith('/')) {
|
|
115
|
-
setSuggestions([]);
|
|
147
|
+
setSuggestions(prev => prev.length > 0 ? [] : prev);
|
|
116
148
|
}
|
|
117
|
-
|
|
118
|
-
setHistoryIndex(-1);
|
|
149
|
+
setHistoryIndex(prev => prev === -1 ? prev : -1);
|
|
119
150
|
}, []);
|
|
120
151
|
// Navigate input history
|
|
121
152
|
const navigateHistory = useCallback((direction) => {
|
|
@@ -124,13 +155,13 @@ function TerminalChat() {
|
|
|
124
155
|
if (direction === 'up') {
|
|
125
156
|
if (historyIndex === -1) {
|
|
126
157
|
// Save current input before navigating
|
|
127
|
-
setSavedInput(
|
|
158
|
+
setSavedInput(inputRef.current);
|
|
128
159
|
setHistoryIndex(inputHistory.length - 1);
|
|
129
|
-
|
|
160
|
+
setInputValue(inputHistory[inputHistory.length - 1]);
|
|
130
161
|
}
|
|
131
162
|
else if (historyIndex > 0) {
|
|
132
163
|
setHistoryIndex(historyIndex - 1);
|
|
133
|
-
|
|
164
|
+
setInputValue(inputHistory[historyIndex - 1]);
|
|
134
165
|
}
|
|
135
166
|
}
|
|
136
167
|
else {
|
|
@@ -138,15 +169,15 @@ function TerminalChat() {
|
|
|
138
169
|
return;
|
|
139
170
|
if (historyIndex < inputHistory.length - 1) {
|
|
140
171
|
setHistoryIndex(historyIndex + 1);
|
|
141
|
-
|
|
172
|
+
setInputValue(inputHistory[historyIndex + 1]);
|
|
142
173
|
}
|
|
143
174
|
else {
|
|
144
175
|
// Return to saved input
|
|
145
176
|
setHistoryIndex(-1);
|
|
146
|
-
|
|
177
|
+
setInputValue(savedInput);
|
|
147
178
|
}
|
|
148
179
|
}
|
|
149
|
-
}, [inputHistory, historyIndex,
|
|
180
|
+
}, [inputHistory, historyIndex, savedInput, setInputValue]);
|
|
150
181
|
// Add to history when submitting
|
|
151
182
|
const addToHistory = useCallback((value) => {
|
|
152
183
|
if (value.trim() && (inputHistory.length === 0 || inputHistory[inputHistory.length - 1] !== value)) {
|
|
@@ -171,6 +202,8 @@ function TerminalChat() {
|
|
|
171
202
|
}));
|
|
172
203
|
// Modal state
|
|
173
204
|
const [modalMode, setModalMode] = useState('none');
|
|
205
|
+
const [providerEntries, setProviderEntries] = useState([]);
|
|
206
|
+
const [pendingSetupProvider, setPendingSetupProvider] = useState(null);
|
|
174
207
|
const [pendingComplexPrompt, setPendingComplexPrompt] = useState(null);
|
|
175
208
|
const [previousSession, setPreviousSession] = useState(null);
|
|
176
209
|
const [pendingToolCall, setPendingToolCall] = useState(null);
|
|
@@ -194,6 +227,14 @@ function TerminalChat() {
|
|
|
194
227
|
useEffect(() => {
|
|
195
228
|
queuedMessagesRef.current = queuedMessages;
|
|
196
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]);
|
|
197
238
|
// Undo/Redo history - stores snapshots of conversation state
|
|
198
239
|
const undoStack = useRef([]);
|
|
199
240
|
const redoStack = useRef([]);
|
|
@@ -299,21 +340,8 @@ function TerminalChat() {
|
|
|
299
340
|
// Initialize session and load memory on mount
|
|
300
341
|
useEffect(() => {
|
|
301
342
|
const cwd = process.cwd();
|
|
302
|
-
//
|
|
303
|
-
|
|
304
|
-
const recentSession = existingSessions.find(s => s.projectPath === cwd &&
|
|
305
|
-
s.messageCount > 0 &&
|
|
306
|
-
Date.now() - new Date(s.lastAccessedAt).getTime() < 24 * 60 * 60 * 1000 // Within 24 hours
|
|
307
|
-
);
|
|
308
|
-
if (recentSession && !sessionRef.current) {
|
|
309
|
-
// Offer to resume
|
|
310
|
-
setPreviousSession({
|
|
311
|
-
projectName: recentSession.projectName,
|
|
312
|
-
lastAccessedAt: recentSession.lastAccessedAt,
|
|
313
|
-
messageCount: recentSession.messageCount,
|
|
314
|
-
});
|
|
315
|
-
setModalMode('session-resume');
|
|
316
|
-
}
|
|
343
|
+
// Always start fresh session - skip resume dialog
|
|
344
|
+
// Note: Previous session data is still available via storage APIs if needed
|
|
317
345
|
const session = storage.getOrCreateSession(cwd);
|
|
318
346
|
sessionRef.current = session;
|
|
319
347
|
ledgerRef.current.setRetentionLimit(config.get('sessionLogLimit') ?? 0);
|
|
@@ -355,6 +383,28 @@ function TerminalChat() {
|
|
|
355
383
|
model: model || DEFAULT_MODELS[selectProvider(provider)],
|
|
356
384
|
cwd: cwdMem,
|
|
357
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
|
+
});
|
|
358
408
|
// Configure session timeout (opt-in via config)
|
|
359
409
|
const timeoutMs = config.get('sessionTimeoutMs');
|
|
360
410
|
if (timeoutMs) {
|
|
@@ -406,11 +456,15 @@ function TerminalChat() {
|
|
|
406
456
|
const actualModel = model || DEFAULT_MODELS[actualProvider];
|
|
407
457
|
const isModalActive = modalMode !== 'none';
|
|
408
458
|
// Add message helper
|
|
409
|
-
const addMessage = useCallback((type, content) => {
|
|
459
|
+
const addMessage = useCallback((type, content, isError) => {
|
|
410
460
|
setMessages(prev => [...prev, {
|
|
411
461
|
id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
412
462
|
type,
|
|
413
|
-
content
|
|
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 } : {}),
|
|
414
468
|
}]);
|
|
415
469
|
// Persist user and assistant messages to storage for session history
|
|
416
470
|
if (type === 'user' || type === 'assistant') {
|
|
@@ -537,7 +591,7 @@ function TerminalChat() {
|
|
|
537
591
|
setThinkingState,
|
|
538
592
|
setStreamingResponse,
|
|
539
593
|
setQueuedMessages,
|
|
540
|
-
setInput,
|
|
594
|
+
setInput: setInputValue,
|
|
541
595
|
setBookmarks,
|
|
542
596
|
setTemplates,
|
|
543
597
|
setContextTokens,
|
|
@@ -555,6 +609,17 @@ function TerminalChat() {
|
|
|
555
609
|
runAgent,
|
|
556
610
|
runLoop,
|
|
557
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?.(),
|
|
558
623
|
}), [actualProvider, actualModel, provider, model, persona, mode, confirmMode, autoRoute, smartRouteActive,
|
|
559
624
|
layout, density, collapseSettings, messages, stats, loopActive, isProcessing,
|
|
560
625
|
thinkingState, streamingResponse, queuedMessages, bookmarks, templates, modalMode,
|
|
@@ -575,7 +640,7 @@ function TerminalChat() {
|
|
|
575
640
|
recording.recordEvent('input', trimmed);
|
|
576
641
|
// Add to history for up/down arrow navigation
|
|
577
642
|
addToHistory(trimmed);
|
|
578
|
-
|
|
643
|
+
setInputValue('');
|
|
579
644
|
if (trimmed.startsWith('/')) {
|
|
580
645
|
await handleCommandWrapped(trimmed);
|
|
581
646
|
return;
|
|
@@ -626,6 +691,10 @@ function TerminalChat() {
|
|
|
626
691
|
else {
|
|
627
692
|
addMessage('user', trimmed);
|
|
628
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
|
+
}
|
|
629
698
|
setIsProcessing(true);
|
|
630
699
|
try {
|
|
631
700
|
// Build message content (with file/image support)
|
|
@@ -649,7 +718,11 @@ function TerminalChat() {
|
|
|
649
718
|
setThinkingState(null);
|
|
650
719
|
setStreamingResponse('');
|
|
651
720
|
}
|
|
652
|
-
}, [isProcessing, handleCommandWrapped, runAgent, addMessage, provider, model, saveUndoState, addToHistory, mode]);
|
|
721
|
+
}, [isProcessing, handleCommandWrapped, runAgent, addMessage, provider, model, saveUndoState, addToHistory, mode, setInputValue]);
|
|
722
|
+
// Keep handleSubmitRef current so scuttlebot polling never captures a stale closure
|
|
723
|
+
useEffect(() => {
|
|
724
|
+
handleSubmitRef.current = handleSubmit;
|
|
725
|
+
}, [handleSubmit]);
|
|
653
726
|
// Modal handlers
|
|
654
727
|
const handleModelSelect = useCallback((selectedModel) => {
|
|
655
728
|
setModel(selectedModel);
|
|
@@ -670,13 +743,9 @@ function TerminalChat() {
|
|
|
670
743
|
const success = await performUpgrade();
|
|
671
744
|
if (success) {
|
|
672
745
|
addMessage('system', 'Upgrade complete! Restarting...');
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
detached: true,
|
|
677
|
-
});
|
|
678
|
-
child.unref();
|
|
679
|
-
process.exit(0);
|
|
746
|
+
requestSelfRestart(process.argv.slice(1));
|
|
747
|
+
exit();
|
|
748
|
+
return;
|
|
680
749
|
}
|
|
681
750
|
else {
|
|
682
751
|
addMessage('error', 'Upgrade failed. Try: npm install -g @calliopelabs/cli@latest');
|
|
@@ -705,7 +774,7 @@ function TerminalChat() {
|
|
|
705
774
|
setStreamingResponse('');
|
|
706
775
|
setLoopActive(false);
|
|
707
776
|
setEditingQueueIndex(null);
|
|
708
|
-
addMessage('system', '⏹ Operation cancelled.
|
|
777
|
+
addMessage('system', '⏹ Operation cancelled. Press Ctrl+C again to quit.');
|
|
709
778
|
}
|
|
710
779
|
else if (modalMode !== 'none') {
|
|
711
780
|
// Close any open modal
|
|
@@ -713,10 +782,113 @@ function TerminalChat() {
|
|
|
713
782
|
setPendingComplexPrompt(null);
|
|
714
783
|
}
|
|
715
784
|
else {
|
|
716
|
-
// Not processing - show hint
|
|
717
|
-
addMessage('system', '💡
|
|
785
|
+
// Not processing - show hint. Second Ctrl+C within 2s will actually exit.
|
|
786
|
+
addMessage('system', '💡 Press Ctrl+C again to quit, or /exit.');
|
|
718
787
|
}
|
|
719
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');
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
try {
|
|
837
|
+
// Map provider → config key. Bedrock/Ollama/LiteLLM are special (base URL or AWS).
|
|
838
|
+
if (entry.id === 'ollama') {
|
|
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]);
|
|
720
892
|
// Handle direct send (Shift+Enter) - interrupts current operation and sends immediately
|
|
721
893
|
const handleDirectSend = useCallback((msg) => {
|
|
722
894
|
// Stop current processing
|
|
@@ -830,7 +1002,16 @@ function TerminalChat() {
|
|
|
830
1002
|
setModalMode('none');
|
|
831
1003
|
setPendingComplexPrompt(null);
|
|
832
1004
|
addMessage('system', 'Operation cancelled.');
|
|
833
|
-
} })), modalMode === 'keys' && (_jsx(KeybindingsModal, { onClose: () => setModalMode('none') })), modalMode === '
|
|
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) => {
|
|
834
1015
|
// Apply all selections
|
|
835
1016
|
setLayout(selection.layout);
|
|
836
1017
|
config.set('layout', selection.layout);
|
|
@@ -874,7 +1055,7 @@ function TerminalChat() {
|
|
|
874
1055
|
` Skin: ${pack.skin.name}, Palette: ${pack.palette.name}, Companion: ${companion.name}\n` +
|
|
875
1056
|
` "${companion.greeting}"`);
|
|
876
1057
|
}
|
|
877
|
-
}, 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) => {
|
|
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) => {
|
|
878
1059
|
setQueuedMessages(prev => [...prev, msg]);
|
|
879
1060
|
addMessage('system', `📨 Queued: "${msg.substring(0, 50)}${msg.length > 50 ? '...' : ''}"`);
|
|
880
1061
|
}, onEditQueuedMessage: handleEditQueuedMessage, onSetEditingQueueIndex: setEditingQueueIndex, onDirectSend: handleDirectSend, cwd: process.cwd(), suggestions: suggestions, onSuggestionsChange: setSuggestions, onNavigateHistory: navigateHistory,
|
|
@@ -959,8 +1140,17 @@ export async function startInkCLI(options = {}) {
|
|
|
959
1140
|
});
|
|
960
1141
|
await waitUntilExit();
|
|
961
1142
|
// Session cleanup
|
|
1143
|
+
if (scuttlebotClient.isEnabled()) {
|
|
1144
|
+
await scuttlebotClient.postOffline().catch(() => {
|
|
1145
|
+
// Silent fail
|
|
1146
|
+
});
|
|
1147
|
+
await scuttlebotClient.disconnect().catch(() => {
|
|
1148
|
+
// Silent fail
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
962
1151
|
recording.stopRecording();
|
|
963
1152
|
sessionTimeout.clearTimers();
|
|
964
1153
|
idleEviction.stopMonitor();
|
|
1154
|
+
await spawnPendingRestart();
|
|
965
1155
|
}
|
|
966
1156
|
//# sourceMappingURL=index.js.map
|