@prestyj/cli 4.2.77 → 4.3.15
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/cli.js +55 -71
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +33 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.js +3 -3
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +4 -1
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/compaction/compactor.d.ts +14 -1
- package/dist/core/compaction/compactor.d.ts.map +1 -1
- package/dist/core/compaction/compactor.js +35 -7
- package/dist/core/compaction/compactor.js.map +1 -1
- package/dist/core/compaction/token-estimator.d.ts.map +1 -1
- package/dist/core/compaction/token-estimator.js +4 -0
- package/dist/core/compaction/token-estimator.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/mcp/client.d.ts.map +1 -1
- package/dist/core/mcp/client.js +15 -2
- package/dist/core/mcp/client.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +59 -3
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/oauth/types.d.ts +1 -0
- package/dist/core/oauth/types.d.ts.map +1 -1
- package/dist/core/settings-manager.d.ts +4 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +11 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/interactive.js +1 -1
- package/dist/interactive.js.map +1 -1
- package/dist/system-prompt.d.ts.map +1 -1
- package/dist/system-prompt.js +8 -58
- package/dist/system-prompt.js.map +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +8 -1
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +6 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/overflow.js +1 -1
- package/dist/tools/overflow.js.map +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +14 -1
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/web-search.d.ts +9 -0
- package/dist/tools/web-search.d.ts.map +1 -0
- package/dist/tools/web-search.js +241 -0
- package/dist/tools/web-search.js.map +1 -0
- package/dist/tools/write.js +2 -2
- package/dist/tools/write.js.map +1 -1
- package/dist/tools/write.test.js +7 -9
- package/dist/tools/write.test.js.map +1 -1
- package/dist/ui/App.d.ts +4 -0
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +223 -114
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/activity-phrases.d.ts.map +1 -1
- package/dist/ui/activity-phrases.js +74 -129
- package/dist/ui/activity-phrases.js.map +1 -1
- package/dist/ui/components/ActivityIndicator.d.ts +3 -1
- package/dist/ui/components/ActivityIndicator.d.ts.map +1 -1
- package/dist/ui/components/ActivityIndicator.js +5 -1
- package/dist/ui/components/ActivityIndicator.js.map +1 -1
- package/dist/ui/components/AssistantMessage.d.ts +1 -0
- package/dist/ui/components/AssistantMessage.d.ts.map +1 -1
- package/dist/ui/components/AssistantMessage.js +2 -2
- package/dist/ui/components/AssistantMessage.js.map +1 -1
- package/dist/ui/components/DiffFrame.d.ts +10 -0
- package/dist/ui/components/DiffFrame.d.ts.map +1 -0
- package/dist/ui/components/DiffFrame.js +13 -0
- package/dist/ui/components/DiffFrame.js.map +1 -0
- package/dist/ui/components/Footer.d.ts +2 -3
- package/dist/ui/components/Footer.d.ts.map +1 -1
- package/dist/ui/components/Footer.js +5 -4
- package/dist/ui/components/Footer.js.map +1 -1
- package/dist/ui/components/InputArea.d.ts.map +1 -1
- package/dist/ui/components/InputArea.js +297 -8
- package/dist/ui/components/InputArea.js.map +1 -1
- package/dist/ui/components/Markdown.d.ts +1 -1
- package/dist/ui/components/Markdown.d.ts.map +1 -1
- package/dist/ui/components/Markdown.js +25 -213
- package/dist/ui/components/Markdown.js.map +1 -1
- package/dist/ui/components/ModelSelector.d.ts.map +1 -1
- package/dist/ui/components/ModelSelector.js +9 -2
- package/dist/ui/components/ModelSelector.js.map +1 -1
- package/dist/ui/components/Ratchet.d.ts.map +1 -1
- package/dist/ui/components/Ratchet.js +10 -3
- package/dist/ui/components/Ratchet.js.map +1 -1
- package/dist/ui/components/StreamingArea.d.ts.map +1 -1
- package/dist/ui/components/StreamingArea.js +2 -2
- package/dist/ui/components/StreamingArea.js.map +1 -1
- package/dist/ui/components/SubAgentPanel.d.ts.map +1 -1
- package/dist/ui/components/SubAgentPanel.js +5 -1
- package/dist/ui/components/SubAgentPanel.js.map +1 -1
- package/dist/ui/components/ThemeSelector.d.ts +9 -0
- package/dist/ui/components/ThemeSelector.d.ts.map +1 -0
- package/dist/ui/components/ThemeSelector.js +25 -0
- package/dist/ui/components/ThemeSelector.js.map +1 -0
- package/dist/ui/components/ToolExecution.d.ts +2 -0
- package/dist/ui/components/ToolExecution.d.ts.map +1 -1
- package/dist/ui/components/ToolExecution.js +95 -30
- package/dist/ui/components/ToolExecution.js.map +1 -1
- package/dist/ui/components/ToolGroupExecution.d.ts +3 -2
- package/dist/ui/components/ToolGroupExecution.d.ts.map +1 -1
- package/dist/ui/components/ToolGroupExecution.js +68 -51
- package/dist/ui/components/ToolGroupExecution.js.map +1 -1
- package/dist/ui/hooks/useAgentLoop.d.ts +10 -3
- package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
- package/dist/ui/hooks/useAgentLoop.js +112 -16
- package/dist/ui/hooks/useAgentLoop.js.map +1 -1
- package/dist/ui/hooks/useDoublePress.d.ts +8 -0
- package/dist/ui/hooks/useDoublePress.d.ts.map +1 -0
- package/dist/ui/hooks/useDoublePress.js +44 -0
- package/dist/ui/hooks/useDoublePress.js.map +1 -0
- package/dist/ui/hooks/useTerminalProgress.d.ts +1 -1
- package/dist/ui/hooks/useTerminalProgress.d.ts.map +1 -1
- package/dist/ui/hooks/useTerminalProgress.js +18 -15
- package/dist/ui/hooks/useTerminalProgress.js.map +1 -1
- package/dist/ui/hooks/useTerminalTitle.d.ts +3 -6
- package/dist/ui/hooks/useTerminalTitle.d.ts.map +1 -1
- package/dist/ui/hooks/useTerminalTitle.js +10 -19
- package/dist/ui/hooks/useTerminalTitle.js.map +1 -1
- package/dist/ui/live-item-flush.d.ts +4 -0
- package/dist/ui/live-item-flush.d.ts.map +1 -1
- package/dist/ui/live-item-flush.js +36 -0
- package/dist/ui/live-item-flush.js.map +1 -1
- package/dist/ui/login.d.ts.map +1 -1
- package/dist/ui/login.js +2 -0
- package/dist/ui/login.js.map +1 -1
- package/dist/ui/render.d.ts +3 -1
- package/dist/ui/render.d.ts.map +1 -1
- package/dist/ui/render.js +9 -3
- package/dist/ui/render.js.map +1 -1
- package/dist/ui/theme/dark-ansi.json +48 -0
- package/dist/ui/theme/dark-daltonized.json +48 -0
- package/dist/ui/theme/dark.json +22 -1
- package/dist/ui/theme/detect-theme.d.ts +12 -8
- package/dist/ui/theme/detect-theme.d.ts.map +1 -1
- package/dist/ui/theme/detect-theme.js +58 -39
- package/dist/ui/theme/detect-theme.js.map +1 -1
- package/dist/ui/theme/light-ansi.json +48 -0
- package/dist/ui/theme/light-daltonized.json +48 -0
- package/dist/ui/theme/light.json +44 -23
- package/dist/ui/theme/theme.d.ts +27 -1
- package/dist/ui/theme/theme.d.ts.map +1 -1
- package/dist/ui/theme/theme.js +24 -1
- package/dist/ui/theme/theme.js.map +1 -1
- package/dist/ui/utils/highlight.d.ts +1 -1
- package/dist/ui/utils/highlight.d.ts.map +1 -1
- package/dist/ui/utils/highlight.js +24 -4
- package/dist/ui/utils/highlight.js.map +1 -1
- package/dist/ui/utils/hyperlink.d.ts +11 -0
- package/dist/ui/utils/hyperlink.d.ts.map +1 -0
- package/dist/ui/utils/hyperlink.js +27 -0
- package/dist/ui/utils/hyperlink.js.map +1 -0
- package/dist/ui/utils/markdown-cache.d.ts +5 -6
- package/dist/ui/utils/markdown-cache.d.ts.map +1 -1
- package/dist/ui/utils/markdown-cache.js +5 -5
- package/dist/ui/utils/markdown-cache.js.map +1 -1
- package/dist/ui/utils/signal.d.ts +21 -0
- package/dist/ui/utils/signal.d.ts.map +1 -0
- package/dist/ui/utils/signal.js +30 -0
- package/dist/ui/utils/signal.js.map +1 -0
- package/dist/ui/utils/supports-hyperlinks.d.ts +6 -0
- package/dist/ui/utils/supports-hyperlinks.d.ts.map +1 -0
- package/dist/ui/utils/supports-hyperlinks.js +52 -0
- package/dist/ui/utils/supports-hyperlinks.js.map +1 -0
- package/dist/ui/utils/token-to-ansi.d.ts +9 -0
- package/dist/ui/utils/token-to-ansi.d.ts.map +1 -0
- package/dist/ui/utils/token-to-ansi.js +305 -0
- package/dist/ui/utils/token-to-ansi.js.map +1 -0
- package/dist/utils/session-title.d.ts +19 -0
- package/dist/utils/session-title.d.ts.map +1 -0
- package/dist/utils/session-title.js +82 -0
- package/dist/utils/session-title.js.map +1 -0
- package/package.json +3 -3
package/dist/ui/App.js
CHANGED
|
@@ -2,6 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import React, { useState, useRef, useCallback, useEffect, useMemo } from "react";
|
|
3
3
|
import { Box, Text, Static, useStdout } from "ink";
|
|
4
4
|
import { useTerminalSize } from "./hooks/useTerminalSize.js";
|
|
5
|
+
import { useDoublePress } from "./hooks/useDoublePress.js";
|
|
6
|
+
import { useTaskBarStore, useTaskBarPolling, focusTaskBar, exitTaskBar, expandTaskBar, collapseTaskBar, navigateTaskBar, killTask, } from "./stores/taskbar-store.js";
|
|
5
7
|
import crypto, { createHash } from "node:crypto";
|
|
6
8
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
7
9
|
import { homedir } from "node:os";
|
|
@@ -25,8 +27,9 @@ import { PlanOverlay } from "./components/PlanOverlay.js";
|
|
|
25
27
|
import { ModelSelector } from "./components/ModelSelector.js";
|
|
26
28
|
import { TaskOverlay } from "./components/TaskOverlay.js";
|
|
27
29
|
import { SkillsOverlay } from "./components/SkillsOverlay.js";
|
|
30
|
+
import { ThemeSelector } from "./components/ThemeSelector.js";
|
|
28
31
|
import { BackgroundTasksBar } from "./components/BackgroundTasksBar.js";
|
|
29
|
-
import { useTheme } from "./theme/theme.js";
|
|
32
|
+
import { useTheme, useSetTheme } from "./theme/theme.js";
|
|
30
33
|
import { useAnimationTick, useAnimationActive, deriveFrame, } from "./components/AnimationContext.js";
|
|
31
34
|
import { useTerminalTitle } from "./hooks/useTerminalTitle.js";
|
|
32
35
|
import { useTerminalProgress } from "./hooks/useTerminalProgress.js";
|
|
@@ -34,6 +37,7 @@ import { getGitBranch } from "../utils/git.js";
|
|
|
34
37
|
import { getModel, getContextWindow } from "../core/model-registry.js";
|
|
35
38
|
import { SessionManager } from "../core/session-manager.js";
|
|
36
39
|
import { log } from "../core/logger.js";
|
|
40
|
+
import { generateSessionTitle } from "../utils/session-title.js";
|
|
37
41
|
import { SettingsManager } from "../core/settings-manager.js";
|
|
38
42
|
import { shouldCompact, compact } from "../core/compaction/compactor.js";
|
|
39
43
|
import { estimateConversationTokens } from "../core/compaction/token-estimator.js";
|
|
@@ -42,7 +46,7 @@ import { loadCustomCommands } from "../core/custom-commands.js";
|
|
|
42
46
|
import { buildSystemPrompt } from "../system-prompt.js";
|
|
43
47
|
import { extractPlanSteps, findCompletedMarkers, markStepsCompleted, stripDoneMarkers, } from "../utils/plan-steps.js";
|
|
44
48
|
import { getMCPServers } from "../core/mcp/index.js";
|
|
45
|
-
import { trimFlushedItems, flushOnTurnText, flushOnTurnEnd } from "./live-item-flush.js";
|
|
49
|
+
import { trimFlushedItems, flushOnTurnText, flushOnTurnEnd, flushOverflow, } from "./live-item-flush.js";
|
|
46
50
|
import { Buddy } from "./buddy/Buddy.js";
|
|
47
51
|
// ── Provider Error Hints ──────────────────────────────────
|
|
48
52
|
/** Detect provider-side errors and return a user-facing hint. */
|
|
@@ -264,22 +268,23 @@ function markTaskInProgress(cwd, taskId) {
|
|
|
264
268
|
// ── App Component ──────────────────────────────────────────
|
|
265
269
|
export function App(props) {
|
|
266
270
|
const theme = useTheme();
|
|
271
|
+
const switchTheme = useSetTheme();
|
|
267
272
|
const { stdout } = useStdout();
|
|
268
273
|
const { columns, resizeKey } = useTerminalSize();
|
|
269
274
|
// Hoisted before terminal title hook so it can reference them
|
|
270
275
|
const [lastUserMessage, setLastUserMessage] = useState("");
|
|
276
|
+
const [exitPending, setExitPending] = useState(false);
|
|
271
277
|
const [planMode, setPlanMode] = useState(false);
|
|
278
|
+
const planModeLocalRef = useRef(false);
|
|
279
|
+
planModeLocalRef.current = planMode;
|
|
272
280
|
// Terminal title — updated later after agentLoop is created
|
|
273
281
|
// (hoisted here so the hook is always called in the same order)
|
|
274
|
-
const [titlePhase, setTitlePhase] = useState("idle");
|
|
275
282
|
const [titleRunning, setTitleRunning] = useState(false);
|
|
276
|
-
const [
|
|
283
|
+
const [sessionTitle, setSessionTitle] = useState(undefined);
|
|
284
|
+
const sessionTitleGeneratedRef = useRef(false);
|
|
277
285
|
useTerminalTitle({
|
|
278
|
-
phase: titlePhase,
|
|
279
286
|
isRunning: titleRunning,
|
|
280
|
-
|
|
281
|
-
activeToolNames: titleToolNames,
|
|
282
|
-
planMode,
|
|
287
|
+
sessionTitle,
|
|
283
288
|
});
|
|
284
289
|
// Items scrolled into Static (history). For restored sessions, skip the
|
|
285
290
|
// banner and add restored items via useEffect so Ink's <Static> treats them
|
|
@@ -328,10 +333,19 @@ export function App(props) {
|
|
|
328
333
|
// Two-phase flush: items waiting to be moved to Static history after the
|
|
329
334
|
// live area has been cleared and Ink has committed the smaller output.
|
|
330
335
|
const pendingFlushRef = useRef([]);
|
|
336
|
+
const [flushGeneration, setFlushGeneration] = useState(0);
|
|
337
|
+
/** Queue items for two-phase flush and signal the drain effect. */
|
|
338
|
+
const queueFlush = useCallback((items) => {
|
|
339
|
+
if (items.length === 0)
|
|
340
|
+
return;
|
|
341
|
+
pendingFlushRef.current = [...pendingFlushRef.current, ...items];
|
|
342
|
+
setFlushGeneration((g) => g + 1);
|
|
343
|
+
}, []);
|
|
331
344
|
// Derive credentials for the current provider
|
|
332
345
|
const currentCreds = props.credentialsByProvider?.[currentProvider];
|
|
333
346
|
const activeApiKey = currentCreds?.accessToken ?? props.apiKey;
|
|
334
347
|
const activeAccountId = currentCreds?.accountId ?? props.accountId;
|
|
348
|
+
const activeBaseUrl = currentCreds?.baseUrl ?? props.baseUrl;
|
|
335
349
|
// Load git branch
|
|
336
350
|
useEffect(() => {
|
|
337
351
|
getGitBranch(props.cwd).then(setGitBranch);
|
|
@@ -392,7 +406,11 @@ export function App(props) {
|
|
|
392
406
|
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
393
407
|
setPlanAutoExpand(true);
|
|
394
408
|
setOverlay("plan");
|
|
395
|
-
planOverlayPendingRef
|
|
409
|
+
// Don't clear planOverlayPendingRef here — keep it true until
|
|
410
|
+
// the user actually approves/rejects the plan. Clearing it on a
|
|
411
|
+
// timer causes a race where agent_done fires after the 300ms
|
|
412
|
+
// timeout but before the user interacts, triggering a premature
|
|
413
|
+
// completion sound.
|
|
396
414
|
}, 300);
|
|
397
415
|
return ("Plan submitted. Exiting plan mode.\n" +
|
|
398
416
|
"The plan pane is opening for user review.\n" +
|
|
@@ -523,63 +541,23 @@ export function App(props) {
|
|
|
523
541
|
}
|
|
524
542
|
return messages;
|
|
525
543
|
}, [currentModel, compactConversation]);
|
|
526
|
-
// ── Background task bar state
|
|
527
|
-
const
|
|
528
|
-
|
|
529
|
-
const
|
|
530
|
-
const
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
if (!props.processManager)
|
|
534
|
-
return;
|
|
535
|
-
const pm = props.processManager;
|
|
536
|
-
const poll = () => {
|
|
537
|
-
const running = pm.list().filter((p) => p.exitCode === null);
|
|
538
|
-
setBgTasks(running);
|
|
539
|
-
};
|
|
540
|
-
poll();
|
|
541
|
-
const interval = setInterval(poll, 2000);
|
|
542
|
-
return () => clearInterval(interval);
|
|
543
|
-
}, [props.processManager]);
|
|
544
|
-
// Auto-exit task panel when all tasks gone
|
|
545
|
-
useEffect(() => {
|
|
546
|
-
if (bgTasks.length === 0) {
|
|
547
|
-
setTaskBarFocused(false);
|
|
548
|
-
setTaskBarExpanded(false);
|
|
549
|
-
}
|
|
550
|
-
// Clamp selected index
|
|
551
|
-
const maxIdx = Math.min(bgTasks.length, 5) - 1;
|
|
552
|
-
if (selectedTaskIndex > maxIdx && maxIdx >= 0) {
|
|
553
|
-
setSelectedTaskIndex(maxIdx);
|
|
554
|
-
}
|
|
555
|
-
}, [bgTasks.length, selectedTaskIndex]);
|
|
556
|
-
const handleFocusTaskBar = useCallback(() => {
|
|
557
|
-
if (bgTasks.length > 0) {
|
|
558
|
-
setTaskBarFocused(true);
|
|
559
|
-
}
|
|
560
|
-
}, [bgTasks.length]);
|
|
561
|
-
const handleTaskBarExit = useCallback(() => {
|
|
562
|
-
setTaskBarFocused(false);
|
|
563
|
-
setTaskBarExpanded(false);
|
|
564
|
-
}, []);
|
|
565
|
-
const handleTaskBarExpand = useCallback(() => {
|
|
566
|
-
setTaskBarExpanded(true);
|
|
567
|
-
setSelectedTaskIndex(0);
|
|
568
|
-
}, []);
|
|
569
|
-
const handleTaskBarCollapse = useCallback(() => {
|
|
570
|
-
setTaskBarExpanded(false);
|
|
571
|
-
}, []);
|
|
544
|
+
// ── Background task bar state (external store) ──────────
|
|
545
|
+
const { bgTasks, focused: taskBarFocused, expanded: taskBarExpanded, selectedIndex: selectedTaskIndex, } = useTaskBarStore();
|
|
546
|
+
useTaskBarPolling(props.processManager);
|
|
547
|
+
const handleFocusTaskBar = useCallback(() => focusTaskBar(), []);
|
|
548
|
+
const handleTaskBarExit = useCallback(() => exitTaskBar(), []);
|
|
549
|
+
const handleTaskBarExpand = useCallback(() => expandTaskBar(), []);
|
|
550
|
+
const handleTaskBarCollapse = useCallback(() => collapseTaskBar(), []);
|
|
572
551
|
const handleTaskKill = useCallback((id) => {
|
|
573
|
-
props.processManager
|
|
552
|
+
if (props.processManager)
|
|
553
|
+
killTask(props.processManager, id);
|
|
574
554
|
}, [props.processManager]);
|
|
575
|
-
const handleTaskNavigate = useCallback((index) =>
|
|
576
|
-
setSelectedTaskIndex(index);
|
|
577
|
-
}, []);
|
|
555
|
+
const handleTaskNavigate = useCallback((index) => navigateTaskBar(index), []);
|
|
578
556
|
// Resolve fresh OAuth credentials before each agent loop run.
|
|
579
557
|
// Falls back to the static props when authStorage is not available.
|
|
580
|
-
const resolveCredentials = useCallback(async () => {
|
|
558
|
+
const resolveCredentials = useCallback(async (opts) => {
|
|
581
559
|
if (props.authStorage) {
|
|
582
|
-
const creds = await props.authStorage.resolveCredentials(currentProvider);
|
|
560
|
+
const creds = await props.authStorage.resolveCredentials(currentProvider, opts);
|
|
583
561
|
return { apiKey: creds.accessToken, accountId: creds.accountId };
|
|
584
562
|
}
|
|
585
563
|
return { apiKey: activeApiKey, accountId: activeAccountId };
|
|
@@ -592,7 +570,7 @@ export function App(props) {
|
|
|
592
570
|
maxTokens: props.maxTokens,
|
|
593
571
|
thinking: thinkingEnabled ? (props.thinking ?? "medium") : undefined,
|
|
594
572
|
apiKey: activeApiKey,
|
|
595
|
-
baseUrl:
|
|
573
|
+
baseUrl: activeBaseUrl,
|
|
596
574
|
accountId: activeAccountId,
|
|
597
575
|
resolveCredentials,
|
|
598
576
|
transformContext,
|
|
@@ -613,7 +591,57 @@ export function App(props) {
|
|
|
613
591
|
}
|
|
614
592
|
})();
|
|
615
593
|
}
|
|
616
|
-
|
|
594
|
+
// Generate session title after the first turn (background, best-effort)
|
|
595
|
+
if (!sessionTitleGeneratedRef.current) {
|
|
596
|
+
sessionTitleGeneratedRef.current = true;
|
|
597
|
+
const msgs = messagesRef.current;
|
|
598
|
+
// Find the first user message and first assistant text
|
|
599
|
+
const userMsg = msgs.find((m) => m.role === "user");
|
|
600
|
+
const assistantMsg = msgs.find((m) => m.role === "assistant");
|
|
601
|
+
const userText = typeof userMsg?.content === "string"
|
|
602
|
+
? userMsg.content
|
|
603
|
+
: Array.isArray(userMsg?.content)
|
|
604
|
+
? userMsg.content
|
|
605
|
+
.filter((c) => c.type === "text")
|
|
606
|
+
.map((c) => c.text)
|
|
607
|
+
.join(" ")
|
|
608
|
+
: "";
|
|
609
|
+
const assistantText = typeof assistantMsg?.content === "string"
|
|
610
|
+
? assistantMsg.content
|
|
611
|
+
: Array.isArray(assistantMsg?.content)
|
|
612
|
+
? assistantMsg.content
|
|
613
|
+
.filter((c) => c.type === "text")
|
|
614
|
+
.map((c) => c.text)
|
|
615
|
+
.join(" ")
|
|
616
|
+
: "";
|
|
617
|
+
if (userText) {
|
|
618
|
+
generateSessionTitle({
|
|
619
|
+
provider: currentProvider,
|
|
620
|
+
userMessage: userText,
|
|
621
|
+
assistantPreview: assistantText.slice(0, 200),
|
|
622
|
+
apiKey: activeApiKey,
|
|
623
|
+
baseUrl: activeBaseUrl,
|
|
624
|
+
accountId: activeAccountId,
|
|
625
|
+
resolveCredentials,
|
|
626
|
+
}).then((title) => {
|
|
627
|
+
setSessionTitle(title);
|
|
628
|
+
log("INFO", "title", `Session title generated: ${title}`);
|
|
629
|
+
}, () => {
|
|
630
|
+
// Best-effort — silently ignore failures
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}, [
|
|
635
|
+
persistNewMessages,
|
|
636
|
+
planMode,
|
|
637
|
+
props.cwd,
|
|
638
|
+
props.skills,
|
|
639
|
+
currentProvider,
|
|
640
|
+
activeApiKey,
|
|
641
|
+
activeAccountId,
|
|
642
|
+
activeBaseUrl,
|
|
643
|
+
resolveCredentials,
|
|
644
|
+
]),
|
|
617
645
|
onTurnText: useCallback((text, thinking, thinkingMs) => {
|
|
618
646
|
// Track [DONE:n] markers for plan step progress
|
|
619
647
|
if (planStepsRef.current.length > 0) {
|
|
@@ -629,13 +657,28 @@ export function App(props) {
|
|
|
629
657
|
// Flush all completed items from the previous turn to Static history.
|
|
630
658
|
// This keeps liveItems bounded per-turn, preventing Ink's live area from
|
|
631
659
|
// growing unbounded, which makes Ink's live-area re-renders expensive.
|
|
660
|
+
//
|
|
661
|
+
// Items are queued in pendingFlushRef (not sent to setHistory directly)
|
|
662
|
+
// so the Static write happens in a SEPARATE render cycle from the
|
|
663
|
+
// live-area change — avoiding both Ink cursor-math clipping and the
|
|
664
|
+
// brief duplicate that occurred when setHistory was nested inside the
|
|
665
|
+
// setLiveItems updater.
|
|
632
666
|
setLiveItems((prev) => {
|
|
633
667
|
const flushed = flushOnTurnText(prev);
|
|
634
668
|
if (flushed.length > 0) {
|
|
635
|
-
|
|
669
|
+
queueFlush(flushed);
|
|
636
670
|
}
|
|
637
671
|
const displayText = planStepsRef.current.length > 0 ? stripDoneMarkers(text) : text;
|
|
638
|
-
return [
|
|
672
|
+
return [
|
|
673
|
+
{
|
|
674
|
+
kind: "assistant",
|
|
675
|
+
text: displayText,
|
|
676
|
+
thinking,
|
|
677
|
+
thinkingMs,
|
|
678
|
+
planMode: planModeLocalRef.current,
|
|
679
|
+
id: getId(),
|
|
680
|
+
},
|
|
681
|
+
];
|
|
639
682
|
});
|
|
640
683
|
}, []),
|
|
641
684
|
onToolStart: useCallback((toolCallId, name, args) => {
|
|
@@ -646,7 +689,7 @@ export function App(props) {
|
|
|
646
689
|
setLiveItems((prev) => {
|
|
647
690
|
const { flushed, remaining } = partitionCompleted(prev);
|
|
648
691
|
if (flushed.length > 0) {
|
|
649
|
-
|
|
692
|
+
queueFlush(flushed);
|
|
650
693
|
}
|
|
651
694
|
return remaining;
|
|
652
695
|
});
|
|
@@ -707,6 +750,24 @@ export function App(props) {
|
|
|
707
750
|
}
|
|
708
751
|
}, []),
|
|
709
752
|
onToolUpdate: useCallback((toolCallId, update) => {
|
|
753
|
+
const u = update;
|
|
754
|
+
// Bash progress streaming — append output to tool_start item
|
|
755
|
+
if (u.type === "bash_progress") {
|
|
756
|
+
setLiveItems((prev) => {
|
|
757
|
+
const idx = prev.findIndex((item) => item.kind === "tool_start" && item.toolCallId === toolCallId);
|
|
758
|
+
if (idx === -1)
|
|
759
|
+
return prev;
|
|
760
|
+
const item = prev[idx];
|
|
761
|
+
const next = [...prev];
|
|
762
|
+
next[idx] = {
|
|
763
|
+
...item,
|
|
764
|
+
progressOutput: (item.progressOutput ?? "") + String(u.output ?? ""),
|
|
765
|
+
};
|
|
766
|
+
return next;
|
|
767
|
+
});
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
// Subagent updates
|
|
710
771
|
setLiveItems((prev) => {
|
|
711
772
|
const groupIdx = prev.findIndex((item) => item.kind === "subagent_group");
|
|
712
773
|
if (groupIdx === -1)
|
|
@@ -759,7 +820,7 @@ export function App(props) {
|
|
|
759
820
|
// Flush completed items to Static to keep the live area small
|
|
760
821
|
const { flushed, remaining } = partitionCompleted(next);
|
|
761
822
|
if (flushed.length > 0) {
|
|
762
|
-
|
|
823
|
+
queueFlush(flushed);
|
|
763
824
|
}
|
|
764
825
|
return remaining;
|
|
765
826
|
});
|
|
@@ -818,7 +879,14 @@ export function App(props) {
|
|
|
818
879
|
// Flush completed items to Static to keep the live area small
|
|
819
880
|
const { flushed, remaining } = partitionCompleted(updated);
|
|
820
881
|
if (flushed.length > 0) {
|
|
821
|
-
|
|
882
|
+
queueFlush(flushed);
|
|
883
|
+
return remaining;
|
|
884
|
+
}
|
|
885
|
+
// Overflow flush: if live area is still large, flush aggressively
|
|
886
|
+
const overflow = flushOverflow(updated);
|
|
887
|
+
if (overflow.flushed.length > 0) {
|
|
888
|
+
queueFlush(overflow.flushed);
|
|
889
|
+
return overflow.remaining;
|
|
822
890
|
}
|
|
823
891
|
return remaining;
|
|
824
892
|
});
|
|
@@ -831,7 +899,7 @@ export function App(props) {
|
|
|
831
899
|
setLiveItems((prev) => {
|
|
832
900
|
const { flushed, remaining } = partitionCompleted(prev);
|
|
833
901
|
if (flushed.length > 0) {
|
|
834
|
-
|
|
902
|
+
queueFlush(flushed);
|
|
835
903
|
}
|
|
836
904
|
return [
|
|
837
905
|
...remaining,
|
|
@@ -882,7 +950,7 @@ export function App(props) {
|
|
|
882
950
|
// Flush completed items to Static
|
|
883
951
|
const { flushed, remaining } = partitionCompleted(updated);
|
|
884
952
|
if (flushed.length > 0) {
|
|
885
|
-
|
|
953
|
+
queueFlush(flushed);
|
|
886
954
|
}
|
|
887
955
|
return remaining;
|
|
888
956
|
});
|
|
@@ -895,15 +963,18 @@ export function App(props) {
|
|
|
895
963
|
...(usage.cacheRead != null && { cacheRead: String(usage.cacheRead) }),
|
|
896
964
|
...(usage.cacheWrite != null && { cacheWrite: String(usage.cacheWrite) }),
|
|
897
965
|
});
|
|
898
|
-
// Track actual token count for compaction decisions
|
|
966
|
+
// Track actual token count for compaction decisions.
|
|
967
|
+
// Anthropic has separate input/output limits — only count input.
|
|
968
|
+
// All other providers share the context window — count both.
|
|
969
|
+
const inputContext = usage.inputTokens + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0);
|
|
899
970
|
lastActualTokensRef.current =
|
|
900
|
-
|
|
971
|
+
currentProvider === "anthropic" ? inputContext : inputContext + usage.outputTokens;
|
|
901
972
|
// For tool-only turns (no text), flush completed items to Static so
|
|
902
973
|
// liveItems doesn't grow unbounded across consecutive tool-only turns.
|
|
903
974
|
setLiveItems((prev) => {
|
|
904
975
|
const { flushed, remaining } = flushOnTurnEnd(prev, stopReason);
|
|
905
976
|
if (flushed.length > 0) {
|
|
906
|
-
|
|
977
|
+
queueFlush(flushed);
|
|
907
978
|
}
|
|
908
979
|
return remaining;
|
|
909
980
|
});
|
|
@@ -926,9 +997,8 @@ export function App(props) {
|
|
|
926
997
|
// separate render cycle so the Static write never coincides with
|
|
927
998
|
// a live-area height change in the same frame.
|
|
928
999
|
setLiveItems((prev) => {
|
|
929
|
-
if (prev.length > 0)
|
|
930
|
-
|
|
931
|
-
}
|
|
1000
|
+
if (prev.length > 0)
|
|
1001
|
+
queueFlush(prev);
|
|
932
1002
|
return [];
|
|
933
1003
|
});
|
|
934
1004
|
// Run-all: auto-start next pending task after a short delay
|
|
@@ -1007,9 +1077,8 @@ export function App(props) {
|
|
|
1007
1077
|
? undefined
|
|
1008
1078
|
: content.filter((c) => c.type === "image").length || undefined;
|
|
1009
1079
|
setLiveItems((prev) => {
|
|
1010
|
-
if (prev.length > 0)
|
|
1011
|
-
|
|
1012
|
-
}
|
|
1080
|
+
if (prev.length > 0)
|
|
1081
|
+
queueFlush(prev);
|
|
1013
1082
|
return [];
|
|
1014
1083
|
});
|
|
1015
1084
|
const userItem = {
|
|
@@ -1036,14 +1105,11 @@ export function App(props) {
|
|
|
1036
1105
|
pendingFlushRef.current = [];
|
|
1037
1106
|
setHistory((h) => compactHistory([...h, ...trimFlushedItems(items)]));
|
|
1038
1107
|
}
|
|
1039
|
-
});
|
|
1108
|
+
}, [flushGeneration]);
|
|
1040
1109
|
// Sync terminal title with agent loop state
|
|
1041
|
-
const activeToolNamesKey = agentLoop.activeToolCalls.map((tc) => tc.name).join(",");
|
|
1042
1110
|
useEffect(() => {
|
|
1043
|
-
setTitlePhase(agentLoop.activityPhase);
|
|
1044
1111
|
setTitleRunning(agentLoop.isRunning);
|
|
1045
|
-
|
|
1046
|
-
}, [agentLoop.activityPhase, agentLoop.isRunning, activeToolNamesKey]);
|
|
1112
|
+
}, [agentLoop.isRunning]);
|
|
1047
1113
|
// Terminal progress bar (OSC 9;4) — pulsing bar in supported terminals
|
|
1048
1114
|
useTerminalProgress(agentLoop.isRunning, agentLoop.activeToolCalls.length > 0);
|
|
1049
1115
|
// Animated thinking border — derived from global animation tick
|
|
@@ -1096,9 +1162,16 @@ export function App(props) {
|
|
|
1096
1162
|
messagesRef.current = [{ role: "system", content: newPrompt }];
|
|
1097
1163
|
})();
|
|
1098
1164
|
agentLoop.reset();
|
|
1165
|
+
setSessionTitle(undefined);
|
|
1166
|
+
sessionTitleGeneratedRef.current = false;
|
|
1099
1167
|
setLiveItems([{ kind: "info", text: "Session cleared.", id: getId() }]);
|
|
1100
1168
|
return;
|
|
1101
1169
|
}
|
|
1170
|
+
// Handle /theme — open theme selector overlay
|
|
1171
|
+
if (trimmed === "/theme" || trimmed === "/t") {
|
|
1172
|
+
setOverlay("theme");
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1102
1175
|
// Handle /plan — toggle plan mode
|
|
1103
1176
|
if (trimmed === "/plan" || trimmed === "/plan on") {
|
|
1104
1177
|
setPlanMode(true);
|
|
@@ -1175,7 +1248,7 @@ export function App(props) {
|
|
|
1175
1248
|
// Move live items into history before starting
|
|
1176
1249
|
setLiveItems((prev) => {
|
|
1177
1250
|
if (prev.length > 0) {
|
|
1178
|
-
|
|
1251
|
+
pendingFlushRef.current = [...pendingFlushRef.current, ...prev];
|
|
1179
1252
|
}
|
|
1180
1253
|
return [];
|
|
1181
1254
|
});
|
|
@@ -1281,7 +1354,7 @@ export function App(props) {
|
|
|
1281
1354
|
// Move any remaining live items into history (Static) before starting new turn
|
|
1282
1355
|
setLiveItems((prev) => {
|
|
1283
1356
|
if (prev.length > 0) {
|
|
1284
|
-
|
|
1357
|
+
pendingFlushRef.current = [...pendingFlushRef.current, ...prev];
|
|
1285
1358
|
}
|
|
1286
1359
|
return [];
|
|
1287
1360
|
});
|
|
@@ -1323,15 +1396,16 @@ export function App(props) {
|
|
|
1323
1396
|
]);
|
|
1324
1397
|
}
|
|
1325
1398
|
}, [agentLoop, props.onSlashCommand, compactConversation]);
|
|
1399
|
+
const handleDoubleExit = useDoublePress(setExitPending, () => process.exit(0));
|
|
1326
1400
|
const handleAbort = useCallback(() => {
|
|
1327
1401
|
if (agentLoop.isRunning) {
|
|
1328
1402
|
agentLoop.clearQueue();
|
|
1329
1403
|
agentLoop.abort();
|
|
1330
1404
|
}
|
|
1331
1405
|
else {
|
|
1332
|
-
|
|
1406
|
+
handleDoubleExit();
|
|
1333
1407
|
}
|
|
1334
|
-
}, [agentLoop]);
|
|
1408
|
+
}, [agentLoop, handleDoubleExit]);
|
|
1335
1409
|
const handleToggleThinking = useCallback(() => {
|
|
1336
1410
|
setThinkingEnabled((prev) => {
|
|
1337
1411
|
const next = !prev;
|
|
@@ -1408,12 +1482,28 @@ export function App(props) {
|
|
|
1408
1482
|
});
|
|
1409
1483
|
}
|
|
1410
1484
|
}, [props.settingsFile, props.mcpManager, props.credentialsByProvider, props.authStorage]);
|
|
1485
|
+
const handleThemeSelect = useCallback((name) => {
|
|
1486
|
+
setOverlay(null);
|
|
1487
|
+
if (switchTheme) {
|
|
1488
|
+
switchTheme(name);
|
|
1489
|
+
}
|
|
1490
|
+
// Persist to settings
|
|
1491
|
+
if (props.settingsFile) {
|
|
1492
|
+
const sm = new SettingsManager(props.settingsFile);
|
|
1493
|
+
sm.load().then(() => sm.set("theme", name));
|
|
1494
|
+
}
|
|
1495
|
+
setLiveItems((prev) => [
|
|
1496
|
+
...prev,
|
|
1497
|
+
{ kind: "info", text: `Theme switched to: ${name}`, id: getId() },
|
|
1498
|
+
]);
|
|
1499
|
+
}, [switchTheme, props.settingsFile]);
|
|
1411
1500
|
// All available slash commands for the command palette
|
|
1412
1501
|
const allCommands = useMemo(() => [
|
|
1413
1502
|
{ name: "model", aliases: ["m"], description: "Switch model" },
|
|
1414
1503
|
{ name: "compact", aliases: ["c"], description: "Compact conversation" },
|
|
1415
1504
|
{ name: "clear", aliases: [], description: "Clear session and terminal" },
|
|
1416
1505
|
{ name: "quit", aliases: ["q", "exit"], description: "Exit the agent" },
|
|
1506
|
+
{ name: "theme", aliases: ["t"], description: "Switch theme" },
|
|
1417
1507
|
{ name: "plan", aliases: [], description: "Toggle plan mode (on/off)" },
|
|
1418
1508
|
{ name: "plans", aliases: [], description: "Open plans pane" },
|
|
1419
1509
|
...PROMPT_COMMANDS.map((cmd) => ({
|
|
@@ -1432,15 +1522,15 @@ export function App(props) {
|
|
|
1432
1522
|
case "tombstone":
|
|
1433
1523
|
return null;
|
|
1434
1524
|
case "banner":
|
|
1435
|
-
return (_jsx(Banner, { version: props.version, model:
|
|
1525
|
+
return (_jsx(Banner, { version: props.version, model: currentModel, provider: currentProvider, cwd: props.cwd, taskCount: taskCount }, item.id));
|
|
1436
1526
|
case "user":
|
|
1437
1527
|
return (_jsx(UserMessage, { text: item.text, imageCount: item.imageCount, pasteInfo: item.pasteInfo }, item.id));
|
|
1438
1528
|
case "task":
|
|
1439
1529
|
return (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: theme.success, bold: true, children: "▶ " }), _jsx(Text, { color: theme.textDim, children: "Task: " }), _jsx(Text, { color: theme.success, children: item.title })] }) }, item.id));
|
|
1440
1530
|
case "assistant":
|
|
1441
|
-
return (_jsx(AssistantMessage, { text: item.text, thinking: item.thinking, thinkingMs: item.thinkingMs, showThinking: props.showThinking }, item.id));
|
|
1531
|
+
return (_jsx(AssistantMessage, { text: item.text, thinking: item.thinking, thinkingMs: item.thinkingMs, showThinking: props.showThinking, planMode: item.planMode }, item.id));
|
|
1442
1532
|
case "tool_start":
|
|
1443
|
-
return _jsx(ToolExecution, { status: "running", name: item.name, args: item.args }, item.id);
|
|
1533
|
+
return (_jsx(ToolExecution, { status: "running", name: item.name, args: item.args, progressOutput: item.progressOutput }, item.id));
|
|
1444
1534
|
case "tool_done":
|
|
1445
1535
|
return (_jsx(ToolExecution, { status: "done", name: item.name, args: item.args, result: item.result, isError: item.isError, details: item.details }, item.id));
|
|
1446
1536
|
case "tool_group":
|
|
@@ -1456,7 +1546,7 @@ export function App(props) {
|
|
|
1456
1546
|
case "info":
|
|
1457
1547
|
return (_jsx(Box, { marginTop: 1, flexShrink: 1, children: _jsx(Text, { color: theme.textDim, wrap: "wrap", children: item.text }) }, item.id));
|
|
1458
1548
|
case "plan_transition":
|
|
1459
|
-
return (_jsx(Box, { marginTop: 1, flexShrink: 1, children: _jsxs(Text, { color: theme.planPrimary, bold: true, wrap: "wrap", children: [item.active ? "
|
|
1549
|
+
return (_jsx(Box, { marginTop: 1, flexShrink: 1, children: _jsxs(Text, { color: theme.planPrimary, bold: true, wrap: "wrap", children: [item.active ? "● " : "● ", item.text] }) }, item.id));
|
|
1460
1550
|
case "queued":
|
|
1461
1551
|
return (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: theme.accent, bold: true, children: "⏳ Queued: " }), _jsxs(Text, { color: theme.text, wrap: "wrap", children: [item.text, item.imageCount
|
|
1462
1552
|
? ` (+${item.imageCount} image${item.imageCount > 1 ? "s" : ""})`
|
|
@@ -1547,11 +1637,17 @@ export function App(props) {
|
|
|
1547
1637
|
setStaticKey((k) => k + 1);
|
|
1548
1638
|
setOverlay(null);
|
|
1549
1639
|
} })) : isPlanView ? (_jsx(PlanOverlay, { cwd: props.cwd, autoExpandNewest: planAutoExpand, onClose: () => {
|
|
1640
|
+
planOverlayPendingRef.current = false;
|
|
1550
1641
|
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
1551
1642
|
setStaticKey((k) => k + 1);
|
|
1552
1643
|
setPlanAutoExpand(false);
|
|
1553
1644
|
setOverlay(null);
|
|
1554
1645
|
}, onApprove: (planPath) => {
|
|
1646
|
+
log("INFO", "plan", "Plan approved — transitioning to implementation", {
|
|
1647
|
+
planPath,
|
|
1648
|
+
});
|
|
1649
|
+
// Plan overlay dismissed — allow future onDone to fire normally
|
|
1650
|
+
planOverlayPendingRef.current = false;
|
|
1555
1651
|
// Store approved plan path — will be injected into the new system prompt
|
|
1556
1652
|
approvedPlanPathRef.current = planPath;
|
|
1557
1653
|
// Extract plan steps for progress tracking
|
|
@@ -1569,28 +1665,36 @@ export function App(props) {
|
|
|
1569
1665
|
setOverlay(null);
|
|
1570
1666
|
// Rebuild system prompt with the approved plan, then reset the session
|
|
1571
1667
|
void (async () => {
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1668
|
+
try {
|
|
1669
|
+
const newPrompt = await buildSystemPrompt(props.cwd, props.skills, false, planPath);
|
|
1670
|
+
messagesRef.current = [{ role: "system", content: newPrompt }];
|
|
1671
|
+
agentLoop.reset();
|
|
1672
|
+
persistedIndexRef.current = messagesRef.current.length;
|
|
1673
|
+
// Create a new session file
|
|
1674
|
+
const sm = sessionManagerRef.current;
|
|
1675
|
+
if (sm) {
|
|
1676
|
+
const s = await sm.create(props.cwd, currentProvider, currentModel);
|
|
1677
|
+
sessionPathRef.current = s.path;
|
|
1678
|
+
}
|
|
1679
|
+
// Start implementation with a clean context
|
|
1680
|
+
setLiveItems([
|
|
1681
|
+
{
|
|
1682
|
+
kind: "info",
|
|
1683
|
+
text: "Plan approved — starting fresh session for implementation",
|
|
1684
|
+
id: getId(),
|
|
1685
|
+
},
|
|
1686
|
+
]);
|
|
1687
|
+
setDoneStatus(null);
|
|
1688
|
+
await agentLoop.run("The plan has been approved. Implement it now, following each step in order.");
|
|
1689
|
+
}
|
|
1690
|
+
catch (err) {
|
|
1691
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1692
|
+
log("ERROR", "error", errMsg);
|
|
1693
|
+
setLiveItems((prev) => [...prev, { kind: "error", message: errMsg, id: getId() }]);
|
|
1581
1694
|
}
|
|
1582
|
-
// Start implementation with a clean context
|
|
1583
|
-
setLiveItems([
|
|
1584
|
-
{
|
|
1585
|
-
kind: "info",
|
|
1586
|
-
text: "Plan approved — starting fresh session for implementation",
|
|
1587
|
-
id: getId(),
|
|
1588
|
-
},
|
|
1589
|
-
]);
|
|
1590
|
-
setDoneStatus(null);
|
|
1591
|
-
await agentLoop.run("The plan has been approved. Implement it now, following each step in order.");
|
|
1592
1695
|
})();
|
|
1593
1696
|
}, onReject: (planPath, feedback) => {
|
|
1697
|
+
planOverlayPendingRef.current = false;
|
|
1594
1698
|
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
1595
1699
|
setStaticKey((k) => k + 1);
|
|
1596
1700
|
setPlanAutoExpand(false);
|
|
@@ -1603,10 +1707,15 @@ export function App(props) {
|
|
|
1603
1707
|
...prev,
|
|
1604
1708
|
{ kind: "info", text: `Plan rejected — "${feedback}"`, id: getId() },
|
|
1605
1709
|
]);
|
|
1606
|
-
void agentLoop.run(msg)
|
|
1710
|
+
void agentLoop.run(msg).catch((err) => {
|
|
1711
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1712
|
+
log("ERROR", "error", errMsg);
|
|
1713
|
+
setLiveItems((prev) => [...prev, { kind: "error", message: errMsg, id: getId() }]);
|
|
1714
|
+
});
|
|
1607
1715
|
} })) : (_jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: "column", flexGrow: 1, paddingRight: 1, children: [liveItems.map((item) => renderItem(item)), _jsx(StreamingArea, { isRunning: agentLoop.isRunning, streamingText: agentLoop.streamingText, streamingThinking: agentLoop.streamingThinking, showThinking: props.showThinking, thinkingMs: agentLoop.thinkingMs, planMode: planMode })] }), agentLoop.isRunning && agentLoop.activityPhase !== "idle" ? (_jsx(Box, { marginTop: 1, borderStyle: "round", borderColor: agentLoop.activityPhase === "thinking"
|
|
1608
1716
|
? THINKING_BORDER_COLORS[thinkingBorderFrame]
|
|
1609
|
-
: "transparent", paddingLeft: 1, paddingRight: 1, width: columns, children: _jsx(ActivityIndicator, { phase: agentLoop.activityPhase, elapsedMs: agentLoop.elapsedMs, thinkingMs: agentLoop.thinkingMs, isThinking: agentLoop.isThinking, tokenEstimate: agentLoop.streamedTokenEstimate, charCountRef: agentLoop.charCountRef, realTokensAccumRef: agentLoop.realTokensAccumRef, userMessage: lastUserMessage, activeToolNames: agentLoop.activeToolCalls.map((tc) => tc.name), planMode: planMode, retryInfo: agentLoop.retryInfo, planDone: planSteps.filter((s) => s.completed).length, planTotal: planSteps.length }) })) :
|
|
1717
|
+
: "transparent", paddingLeft: 1, paddingRight: 1, width: columns, children: _jsx(ActivityIndicator, { phase: agentLoop.activityPhase, elapsedMs: agentLoop.elapsedMs, runStartRef: agentLoop.runStartRef, thinkingMs: agentLoop.thinkingMs, isThinking: agentLoop.isThinking, tokenEstimate: agentLoop.streamedTokenEstimate, charCountRef: agentLoop.charCountRef, realTokensAccumRef: agentLoop.realTokensAccumRef, userMessage: lastUserMessage, activeToolNames: agentLoop.activeToolCalls.map((tc) => tc.name), planMode: planMode, retryInfo: agentLoop.retryInfo, planDone: planSteps.filter((s) => s.completed).length, planTotal: planSteps.length }) })) : agentLoop.stallError ? (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: theme.warning, children: "⚠ API provider stream interrupted — retries exhausted." }), _jsx(Text, { color: theme.textDim, children: " Your conversation is preserved. Send a message to continue." })] })) : (doneStatus &&
|
|
1718
|
+
!agentLoop.isRunning && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.success, children: ["✻ ", doneStatus.verb, " ", formatDuration(doneStatus.durationMs)] }) }))), agentLoop.queuedCount > 0 && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.accent, children: ["⏳ ", agentLoop.queuedCount, " message", agentLoop.queuedCount > 1 ? "s" : "", " queued"] }) })), _jsx(InputArea, { onSubmit: handleSubmit, onAbort: handleAbort, disabled: agentLoop.isRunning, isActive: !taskBarFocused && !overlay, onDownAtEnd: handleFocusTaskBar, onShiftTab: handleToggleThinking, onToggleTasks: () => {
|
|
1610
1719
|
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
1611
1720
|
setOverlay("tasks");
|
|
1612
1721
|
}, onToggleSkills: () => {
|
|
@@ -1625,6 +1734,6 @@ export function App(props) {
|
|
|
1625
1734
|
id: getId(),
|
|
1626
1735
|
},
|
|
1627
1736
|
]);
|
|
1628
|
-
}, cwd: props.cwd, commands: allCommands }), overlay === "model" ? (_jsx(ModelSelector, { onSelect: handleModelSelect, onCancel: () => setOverlay(null), loggedInProviders: props.loggedInProviders ?? [currentProvider], currentModel: currentModel, currentProvider: currentProvider })) : (_jsx(
|
|
1737
|
+
}, cwd: props.cwd, commands: allCommands }), overlay === "model" ? (_jsx(ModelSelector, { onSelect: handleModelSelect, onCancel: () => setOverlay(null), loggedInProviders: props.loggedInProviders ?? [currentProvider], currentModel: currentModel, currentProvider: currentProvider })) : overlay === "theme" ? (_jsx(ThemeSelector, { onSelect: handleThemeSelect, onCancel: () => setOverlay(null), currentTheme: theme.name })) : (_jsx(Footer, { model: currentModel, tokensIn: agentLoop.contextUsed, cwd: props.cwd, gitBranch: gitBranch, thinkingEnabled: thinkingEnabled, planMode: planMode, exitPending: exitPending })), buddyEnabled && _jsx(Buddy, { phase: agentLoop.activityPhase }), bgTasks.length > 0 && (_jsx(BackgroundTasksBar, { tasks: bgTasks, focused: taskBarFocused, expanded: taskBarExpanded, selectedIndex: selectedTaskIndex, onExpand: handleTaskBarExpand, onCollapse: handleTaskBarCollapse, onKill: handleTaskKill, onExit: handleTaskBarExit, onNavigate: handleTaskNavigate }))] }))] }));
|
|
1629
1738
|
}
|
|
1630
1739
|
//# sourceMappingURL=App.js.map
|