@kenkaiiii/ggcoder 4.3.216 → 4.3.218
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/command-routing.d.ts +28 -0
- package/dist/cli/command-routing.d.ts.map +1 -0
- package/dist/cli/command-routing.js +51 -0
- package/dist/cli/command-routing.js.map +1 -0
- package/dist/cli/command-routing.test.d.ts +2 -0
- package/dist/cli/command-routing.test.d.ts.map +1 -0
- package/dist/cli/command-routing.test.js +59 -0
- package/dist/cli/command-routing.test.js.map +1 -0
- package/dist/cli.d.ts +3 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +63 -108
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +2 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-compaction.test.d.ts +2 -0
- package/dist/core/agent-session-compaction.test.d.ts.map +1 -0
- package/dist/core/agent-session-compaction.test.js +121 -0
- package/dist/core/agent-session-compaction.test.js.map +1 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +6 -7
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/goal-controller.d.ts.map +1 -1
- package/dist/core/goal-controller.js +46 -1
- package/dist/core/goal-controller.js.map +1 -1
- package/dist/core/goal-controller.test.js +39 -0
- package/dist/core/goal-controller.test.js.map +1 -1
- package/dist/core/goal-store.d.ts +18 -0
- package/dist/core/goal-store.d.ts.map +1 -1
- package/dist/core/goal-store.js +48 -0
- package/dist/core/goal-store.js.map +1 -1
- package/dist/core/goal-store.test.js +10 -0
- package/dist/core/goal-store.test.js.map +1 -1
- package/dist/core/goal-worker.d.ts +9 -1
- package/dist/core/goal-worker.d.ts.map +1 -1
- package/dist/core/goal-worker.js +66 -19
- package/dist/core/goal-worker.js.map +1 -1
- package/dist/core/goal-worker.test.js +120 -1
- package/dist/core/goal-worker.test.js.map +1 -1
- package/dist/core/goal-worktree.d.ts +33 -0
- package/dist/core/goal-worktree.d.ts.map +1 -0
- package/dist/core/goal-worktree.js +67 -0
- package/dist/core/goal-worktree.js.map +1 -0
- package/dist/core/goal-worktree.test.d.ts +2 -0
- package/dist/core/goal-worktree.test.d.ts.map +1 -0
- package/dist/core/goal-worktree.test.js +101 -0
- package/dist/core/goal-worktree.test.js.map +1 -0
- package/dist/core/repomap-budget.d.ts +7 -0
- package/dist/core/repomap-budget.d.ts.map +1 -0
- package/dist/core/repomap-budget.js +10 -0
- package/dist/core/repomap-budget.js.map +1 -0
- package/dist/core/repomap-budget.test.d.ts +2 -0
- package/dist/core/repomap-budget.test.d.ts.map +1 -0
- package/dist/core/repomap-budget.test.js +26 -0
- package/dist/core/repomap-budget.test.js.map +1 -0
- package/dist/system-prompt.d.ts.map +1 -1
- package/dist/system-prompt.js +4 -0
- package/dist/system-prompt.js.map +1 -1
- package/dist/system-prompt.test.js +18 -0
- package/dist/system-prompt.test.js.map +1 -1
- package/dist/tools/goals.d.ts +9 -0
- package/dist/tools/goals.d.ts.map +1 -1
- package/dist/tools/goals.js +79 -27
- package/dist/tools/goals.js.map +1 -1
- package/dist/tools/goals.test.js +91 -22
- package/dist/tools/goals.test.js.map +1 -1
- package/dist/ui/App.d.ts +11 -385
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +109 -559
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/app-items.d.ts +207 -0
- package/dist/ui/app-items.d.ts.map +1 -0
- package/dist/ui/app-items.js +2 -0
- package/dist/ui/app-items.js.map +1 -0
- package/dist/ui/app-state-persistence.test.js +4 -1
- package/dist/ui/app-state-persistence.test.js.map +1 -1
- package/dist/ui/chat-layout-pinning.test.js +11 -1
- package/dist/ui/chat-layout-pinning.test.js.map +1 -1
- package/dist/ui/components/AnimationContext.d.ts.map +1 -1
- package/dist/ui/components/AnimationContext.js +3 -5
- package/dist/ui/components/AnimationContext.js.map +1 -1
- package/dist/ui/components/AssistantMessage.test.js +12 -12
- package/dist/ui/components/AssistantMessage.test.js.map +1 -1
- package/dist/ui/components/CompactionNotice.js +2 -2
- package/dist/ui/components/CompactionNotice.js.map +1 -1
- package/dist/ui/components/Footer.d.ts +1 -0
- package/dist/ui/components/Footer.d.ts.map +1 -1
- package/dist/ui/components/Footer.js +5 -2
- package/dist/ui/components/Footer.js.map +1 -1
- package/dist/ui/components/ServerToolExecution.d.ts.map +1 -1
- package/dist/ui/components/ServerToolExecution.js +5 -23
- package/dist/ui/components/ServerToolExecution.js.map +1 -1
- package/dist/ui/components/SubAgentPanel.d.ts.map +1 -1
- package/dist/ui/components/SubAgentPanel.js +3 -2
- package/dist/ui/components/SubAgentPanel.js.map +1 -1
- package/dist/ui/components/ToolExecution.d.ts.map +1 -1
- package/dist/ui/components/ToolExecution.js +12 -30
- package/dist/ui/components/ToolExecution.js.map +1 -1
- package/dist/ui/components/ToolGroupExecution.d.ts.map +1 -1
- package/dist/ui/components/ToolGroupExecution.js +5 -24
- package/dist/ui/components/ToolGroupExecution.js.map +1 -1
- package/dist/ui/components/ToolUseLoader.d.ts +3 -1
- package/dist/ui/components/ToolUseLoader.d.ts.map +1 -1
- package/dist/ui/components/ToolUseLoader.js +2 -2
- package/dist/ui/components/ToolUseLoader.js.map +1 -1
- package/dist/ui/footer-status-layout.test.js +9 -1
- package/dist/ui/footer-status-layout.test.js.map +1 -1
- package/dist/ui/goal-events.d.ts +4 -0
- package/dist/ui/goal-events.d.ts.map +1 -1
- package/dist/ui/goal-events.js +19 -1
- package/dist/ui/goal-events.js.map +1 -1
- package/dist/ui/goal-events.test.js +15 -1
- package/dist/ui/goal-events.test.js.map +1 -1
- package/dist/ui/goal-lifecycle-orchestration.test.js +20 -1
- package/dist/ui/goal-lifecycle-orchestration.test.js.map +1 -1
- package/dist/ui/goal-progress.d.ts +31 -0
- package/dist/ui/goal-progress.d.ts.map +1 -0
- package/dist/ui/goal-progress.js +152 -0
- package/dist/ui/goal-progress.js.map +1 -0
- package/dist/ui/item-helpers.d.ts +24 -0
- package/dist/ui/item-helpers.d.ts.map +1 -0
- package/dist/ui/item-helpers.js +96 -0
- package/dist/ui/item-helpers.js.map +1 -0
- package/dist/ui/layout-decisions.d.ts +113 -0
- package/dist/ui/layout-decisions.d.ts.map +1 -0
- package/dist/ui/layout-decisions.js +173 -0
- package/dist/ui/layout-decisions.js.map +1 -0
- package/dist/ui/prompt-routing.d.ts +29 -0
- package/dist/ui/prompt-routing.d.ts.map +1 -0
- package/dist/ui/prompt-routing.js +105 -0
- package/dist/ui/prompt-routing.js.map +1 -0
- package/dist/ui/queued-message.test.js +19 -1
- package/dist/ui/queued-message.test.js.map +1 -1
- package/dist/ui/render.d.ts +1 -0
- package/dist/ui/render.d.ts.map +1 -1
- package/dist/ui/render.js +1 -0
- package/dist/ui/render.js.map +1 -1
- package/dist/ui/scroll-stabilization.test.js +1 -1
- package/dist/ui/scroll-stabilization.test.js.map +1 -1
- package/dist/ui/slash-command-images.test.js +1 -1
- package/dist/ui/slash-command-images.test.js.map +1 -1
- package/dist/ui/terminal-history-format.d.ts +41 -0
- package/dist/ui/terminal-history-format.d.ts.map +1 -0
- package/dist/ui/terminal-history-format.js +131 -0
- package/dist/ui/terminal-history-format.js.map +1 -0
- package/dist/ui/terminal-history-spacing.d.ts +3 -0
- package/dist/ui/terminal-history-spacing.d.ts.map +1 -0
- package/dist/ui/terminal-history-spacing.js +28 -0
- package/dist/ui/terminal-history-spacing.js.map +1 -0
- package/dist/ui/terminal-history-status-renderers.d.ts +16 -0
- package/dist/ui/terminal-history-status-renderers.d.ts.map +1 -0
- package/dist/ui/terminal-history-status-renderers.js +83 -0
- package/dist/ui/terminal-history-status-renderers.js.map +1 -0
- package/dist/ui/terminal-history.d.ts.map +1 -1
- package/dist/ui/terminal-history.js +30 -246
- package/dist/ui/terminal-history.js.map +1 -1
- package/dist/ui/terminal-history.test.js +21 -7
- package/dist/ui/terminal-history.test.js.map +1 -1
- package/dist/ui/thinking-level-cycle.test.d.ts +2 -0
- package/dist/ui/thinking-level-cycle.test.d.ts.map +1 -0
- package/dist/ui/thinking-level-cycle.test.js +25 -0
- package/dist/ui/thinking-level-cycle.test.js.map +1 -0
- package/dist/ui/thinking-level.d.ts +5 -0
- package/dist/ui/thinking-level.d.ts.map +1 -0
- package/dist/ui/thinking-level.js +30 -0
- package/dist/ui/thinking-level.js.map +1 -0
- package/dist/ui/tool-group-summary.d.ts.map +1 -1
- package/dist/ui/tool-group-summary.js +117 -8
- package/dist/ui/tool-group-summary.js.map +1 -1
- package/dist/ui/tool-group-summary.test.d.ts +2 -0
- package/dist/ui/tool-group-summary.test.d.ts.map +1 -0
- package/dist/ui/tool-group-summary.test.js +79 -0
- package/dist/ui/tool-group-summary.test.js.map +1 -0
- package/dist/ui/tui-history-parity.test.js +34 -13
- package/dist/ui/tui-history-parity.test.js.map +1 -1
- package/package.json +4 -4
package/dist/ui/App.js
CHANGED
|
@@ -4,7 +4,6 @@ import { Box, Text, useStdout } from "ink";
|
|
|
4
4
|
import { useTerminalSize } from "./hooks/useTerminalSize.js";
|
|
5
5
|
import { useDoublePress } from "./hooks/useDoublePress.js";
|
|
6
6
|
import { useTaskBarStore, useTaskBarPolling, focusTaskBar, exitTaskBar, expandTaskBar, collapseTaskBar, navigateTaskBar, killTask, } from "./stores/taskbar-store.js";
|
|
7
|
-
import { writeFileSync } from "node:fs";
|
|
8
7
|
import { playNotificationSound } from "../utils/sound.js";
|
|
9
8
|
import { formatError, } from "@kenkaiiii/gg-ai";
|
|
10
9
|
import { extractImagePaths } from "../utils/image.js";
|
|
@@ -30,14 +29,14 @@ import { PlanOverlay } from "./components/PlanOverlay.js";
|
|
|
30
29
|
import { ModelSelector } from "./components/ModelSelector.js";
|
|
31
30
|
import { GoalOverlay } from "./components/GoalOverlay.js";
|
|
32
31
|
import { PixelOverlay } from "./components/PixelOverlay.js";
|
|
33
|
-
import { buildGoalFinalSummarySections, buildGoalSummaryRows, goalPassedDetail, } from "./goal-summary.js";
|
|
34
32
|
import { SkillsOverlay } from "./components/SkillsOverlay.js";
|
|
35
33
|
import { ThemeSelector } from "./components/ThemeSelector.js";
|
|
36
34
|
import { BackgroundTasksBar, getFooterStatusLayoutDecision, } from "./components/BackgroundTasksBar.js";
|
|
37
35
|
import { useTheme, useSetTheme } from "./theme/theme.js";
|
|
38
36
|
import { useTerminalTitle } from "./hooks/useTerminalTitle.js";
|
|
39
37
|
import { getGitBranch } from "../utils/git.js";
|
|
40
|
-
import { getModel, getContextWindow
|
|
38
|
+
import { getModel, getContextWindow } from "../core/model-registry.js";
|
|
39
|
+
import { BLACK_CIRCLE } from "./constants/figures.js";
|
|
41
40
|
import { SessionManager } from "../core/session-manager.js";
|
|
42
41
|
import { appendMessagesToSession as appendSessionMessages, createCompactedSessionCheckpoint, } from "../core/session-compaction.js";
|
|
43
42
|
import { log } from "../core/logger.js";
|
|
@@ -52,18 +51,29 @@ import { loadCustomCommands } from "../core/custom-commands.js";
|
|
|
52
51
|
import { buildSystemPrompt } from "../system-prompt.js";
|
|
53
52
|
import { detectLanguages, LANGUAGE_DISPLAY_NAMES, } from "../core/language-detector.js";
|
|
54
53
|
import { detectVerifyCommands } from "../core/verify-commands.js";
|
|
55
|
-
import {
|
|
54
|
+
import { buildRepoMap, createRepoMapCache, } from "../core/repomap.js";
|
|
55
|
+
import { getRepoMapBudgetForContext } from "../core/repomap-budget.js";
|
|
56
56
|
import { getLatestUserText, injectRepoMapContextMessages, stripRepoMapContextMessages, } from "../core/repomap-context.js";
|
|
57
57
|
import { extractPlanSteps, findCompletedMarkers, markStepsCompleted, segmentDisplayText, stripDoneMarkers, } from "../utils/plan-steps.js";
|
|
58
58
|
import { getMCPServers } from "../core/mcp/index.js";
|
|
59
59
|
import { trimFlushedItems, flushOnTurnText, flushOnTurnEnd, flushOverflow, } from "./live-item-flush.js";
|
|
60
60
|
import { splitAssistantStreamingText } from "./utils/assistant-stream-split.js";
|
|
61
61
|
import { appendGoalDecision, appendGoalEvidence, formatGoalBlockingPrerequisites, goalHasBlockingPrerequisites, loadGoalRuns, reconcileActiveGoalRuns, updateGoalTask, upsertGoalRun, } from "../core/goal-store.js";
|
|
62
|
-
import { canCompleteGoalRun, decideGoalNextAction
|
|
62
|
+
import { canCompleteGoalRun, decideGoalNextAction } from "../core/goal-controller.js";
|
|
63
63
|
import { runGoalPrerequisiteChecks } from "../core/goal-prerequisites.js";
|
|
64
64
|
import { runGoalVerifierCommand } from "../core/goal-verifier.js";
|
|
65
65
|
import { listGoalWorkers, startGoalWorker, stopGoalWorker, subscribeGoalWorkerCompletions, } from "../core/goal-worker.js";
|
|
66
66
|
import { formatGoalVerifierCompletionEvent, formatGoalWorkerCompletionEvent, isGoalSyntheticEvent, parseGoalSyntheticEvent, } from "./goal-events.js";
|
|
67
|
+
import { buildUserContentWithAttachments, isGoalPromptCommandName, routePromptCommandInput, runGoalPromptSetupSequence, } from "./prompt-routing.js";
|
|
68
|
+
import { getNextThinkingLevel, isThinkingLevelSupported } from "./thinking-level.js";
|
|
69
|
+
import { appendGoalProgressDraft, completedItemsWithDurableGoalTerminalProgress, formatGoalTerminalProgress, formatGoalWorkerFinishedTitle, getGoalContinuationChoiceKey, goalTerminalProgressId, routeGoalSyntheticEvent, summarizeGoalCompletion, truncateGoalProgressText, } from "./goal-progress.js";
|
|
70
|
+
import { getChatControlsLayoutDecision, getDoneFlushDecision, getGoalActivationPaneTransition, getGoalSetupFinishedPaneTransition, getGoalSetupPaneTransitionAfterRun, isAgentSpacingItem, MIN_LIVE_AREA_ROWS, nextGoalModeAfterAgentDone, shouldResetUIForGoalSetupPaneTransition, shouldTopSpaceAfterPrintedAgentBoundary, shouldTopSpaceAssistantAfterToolBoundary, shouldTopSpaceStreamingAssistant, } from "./layout-decisions.js";
|
|
71
|
+
import { compactHistory, getNextGeneratedItemId, isActiveItem, isSameAssistantText, normalizeAssistantText, partitionCompleted, pinStreamingTextBeforeToolBoundary, } from "./item-helpers.js";
|
|
72
|
+
export { buildGoalSetupPromptFromPlanner, buildUserContentWithAttachments, collectAssistantTextSince, isGoalPromptCommandName, routePromptCommandInput, runGoalPromptSetupSequence, } from "./prompt-routing.js";
|
|
73
|
+
export { getNextThinkingLevel } from "./thinking-level.js";
|
|
74
|
+
export { appendGoalProgressDraft, completedItemsWithDurableGoalTerminalProgress, formatGoalTerminalProgress, getGoalContinuationChoiceKey, routeGoalSyntheticEvent, truncateGoalProgressText, } from "./goal-progress.js";
|
|
75
|
+
export { getChatControlsLayoutDecision, getDoneFlushDecision, getGoalActivationPaneTransition, getGoalSetupFinishedPaneTransition, getGoalSetupPaneTransitionAfterRun, getScrollStabilizationDecision, getStaticHistoryKey, hasParagraphBreakLiveUserMessage, isTallLiveUserMessage, nextGoalModeAfterAgentDone, shouldHideHistoryForOverlayView, shouldHideStaticItemsForOverlayView, shouldResetUIForGoalSetupPaneTransition, shouldStabilizeOverlayPaneRerender, shouldTopSpaceAfterPrintedAgentBoundary, shouldTopSpaceAssistantAfterToolBoundary, shouldTopSpaceStreamingAssistant, } from "./layout-decisions.js";
|
|
76
|
+
export { getNextGeneratedItemId, isActiveItem, partitionCompleted, pinStreamingTextBeforeToolBoundary, } from "./item-helpers.js";
|
|
67
77
|
/** Where ggcoder bugs should be reported. Surfaced in the guidance line. */
|
|
68
78
|
const GGCODER_BUG_REPORT_URL = "github.com/kenkaiiii/gg-framework/issues";
|
|
69
79
|
/**
|
|
@@ -97,161 +107,27 @@ function toErrorItem(err, id, contextPrefix) {
|
|
|
97
107
|
id,
|
|
98
108
|
};
|
|
99
109
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return null;
|
|
112
|
-
return {
|
|
113
|
-
cmdName,
|
|
114
|
-
cmdArgs,
|
|
115
|
-
promptText,
|
|
116
|
-
fullPrompt: cmdArgs ? `${promptText}\n\n## User Instructions\n\n${cmdArgs}` : promptText,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
const GOAL_PLANNER_OUTPUT_MAX_CHARS = 2400;
|
|
120
|
-
function messageTextContent(message) {
|
|
121
|
-
if (typeof message.content === "string")
|
|
122
|
-
return message.content;
|
|
123
|
-
return message.content
|
|
124
|
-
.filter((part) => part.type === "text")
|
|
125
|
-
.map((part) => part.text)
|
|
126
|
-
.join("\n");
|
|
127
|
-
}
|
|
128
|
-
export function collectAssistantTextSince(messages, startIndex, maxChars = GOAL_PLANNER_OUTPUT_MAX_CHARS) {
|
|
129
|
-
const text = messages
|
|
130
|
-
.slice(startIndex)
|
|
131
|
-
.filter((message) => message.role === "assistant")
|
|
132
|
-
.map(messageTextContent)
|
|
133
|
-
.join("\n")
|
|
134
|
-
.trim();
|
|
135
|
-
if (text.length <= maxChars)
|
|
136
|
-
return text;
|
|
137
|
-
return text.slice(0, maxChars).trimEnd() + "\n[planner output truncated]";
|
|
138
|
-
}
|
|
139
|
-
export function buildGoalSetupPromptFromPlanner({ originalGoalPrompt, plannerOutput, }) {
|
|
140
|
-
const compactPlannerOutput = plannerOutput.trim() || "GOAL_PLAN\nresearch=none\nEND_GOAL_PLAN";
|
|
141
|
-
return (`${originalGoalPrompt.trim()}\n\n` +
|
|
142
|
-
`## Goal Planner Output\n\n${compactPlannerOutput}\n\n` +
|
|
143
|
-
`Use the original objective plus this planner output to create durable Goal setup only. ` +
|
|
144
|
-
`Do not redo planner research unless the planner output is unusable.`);
|
|
145
|
-
}
|
|
146
|
-
export function isGoalPromptCommandName(cmdName) {
|
|
147
|
-
return getPromptCommand(cmdName)?.name === "goal";
|
|
148
|
-
}
|
|
149
|
-
export async function runGoalPromptSetupSequence({ userContent, fullPrompt, messagesRef, setGoalModeAndPrompt, runAgent, onStage, }) {
|
|
150
|
-
onStage?.("Planning Goal setup");
|
|
151
|
-
await setGoalModeAndPrompt("planner");
|
|
152
|
-
const plannerStartIndex = messagesRef.current.length;
|
|
153
|
-
await runAgent(userContent);
|
|
154
|
-
const plannerOutput = collectAssistantTextSince(messagesRef.current, plannerStartIndex);
|
|
155
|
-
const setupPrompt = buildGoalSetupPromptFromPlanner({
|
|
156
|
-
originalGoalPrompt: fullPrompt,
|
|
157
|
-
plannerOutput,
|
|
158
|
-
});
|
|
159
|
-
await setGoalModeAndPrompt("setup");
|
|
160
|
-
onStage?.("Creating Goal run");
|
|
161
|
-
await runAgent(setupPrompt);
|
|
162
|
-
}
|
|
110
|
+
/** Tools that get aggregated into a single compact group when possible. */
|
|
111
|
+
const AGGREGATABLE_TOOLS = new Set([
|
|
112
|
+
"read",
|
|
113
|
+
"grep",
|
|
114
|
+
"find",
|
|
115
|
+
"ls",
|
|
116
|
+
"mcp__kencode-search__searchCode",
|
|
117
|
+
"mcp__kencode-search__referenceSources",
|
|
118
|
+
"mcp__kencode-search__discoverRepos",
|
|
119
|
+
]);
|
|
120
|
+
const RUNNING_INDICATOR_ANIMATION_MS = 1_200;
|
|
163
121
|
function buildGoalTaskPromptWithReferences(run, taskPrompt) {
|
|
164
122
|
if (taskPrompt.includes("## Goal References (MANDATORY)"))
|
|
165
123
|
return taskPrompt;
|
|
166
124
|
const references = formatGoalReferencesForPrompt(run.references ?? []);
|
|
167
125
|
return references ? `${references}\n\n${taskPrompt}` : taskPrompt;
|
|
168
126
|
}
|
|
169
|
-
export function buildUserContentWithAttachments(text, inputImages, modelSupportsImages) {
|
|
170
|
-
if (inputImages.length === 0)
|
|
171
|
-
return text;
|
|
172
|
-
const parts = [];
|
|
173
|
-
if (text) {
|
|
174
|
-
parts.push({ type: "text", text });
|
|
175
|
-
}
|
|
176
|
-
for (const img of inputImages) {
|
|
177
|
-
if (img.kind === "text") {
|
|
178
|
-
parts.push({
|
|
179
|
-
type: "text",
|
|
180
|
-
text: `<file name="${img.fileName}">\n${img.data}\n</file>`,
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
else if (modelSupportsImages) {
|
|
184
|
-
parts.push({ type: "image", mediaType: img.mediaType, data: img.data });
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
// GLM models: save image to temp file and instruct model to use vision MCP tool
|
|
188
|
-
const ext = img.mediaType.split("/")[1] ?? "png";
|
|
189
|
-
const tmpPath = `/tmp/ggcoder-img-${Date.now()}.${ext}`;
|
|
190
|
-
try {
|
|
191
|
-
writeFileSync(tmpPath, Buffer.from(img.data, "base64"));
|
|
192
|
-
parts.push({
|
|
193
|
-
type: "text",
|
|
194
|
-
text: `[User attached an image saved at: ${tmpPath} — use the image_analysis tool to view and analyze it]`,
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
catch {
|
|
198
|
-
parts.push({
|
|
199
|
-
type: "text",
|
|
200
|
-
text: `[User attached an image but it could not be saved for analysis]`,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
// If only text parts remain after stripping images, simplify to plain string
|
|
206
|
-
return parts.length === 1 && parts[0].type === "text" ? parts[0].text : parts;
|
|
207
|
-
}
|
|
208
|
-
/** Tools that get aggregated into a single compact group when possible. */
|
|
209
|
-
const AGGREGATABLE_TOOLS = new Set(["read", "grep", "find", "ls"]);
|
|
210
|
-
const RUNNING_INDICATOR_ANIMATION_MS = 1_200;
|
|
211
|
-
/**
|
|
212
|
-
* Cap memory by replacing old finalized rows with tiny tombstones. The full
|
|
213
|
-
* transcript is already printed into terminal scrollback, so the in-memory copy
|
|
214
|
-
* only needs enough recent structure to survive remounts and session mirroring.
|
|
215
|
-
*/
|
|
216
|
-
const MAX_LIVE_HISTORY = 200;
|
|
217
|
-
function compactHistory(items) {
|
|
218
|
-
if (items.length <= MAX_LIVE_HISTORY)
|
|
219
|
-
return items;
|
|
220
|
-
const cutoff = items.length - MAX_LIVE_HISTORY;
|
|
221
|
-
const compacted = new Array(items.length);
|
|
222
|
-
for (let i = 0; i < cutoff; i++) {
|
|
223
|
-
const it = items[i];
|
|
224
|
-
compacted[i] = it.kind === "tombstone" ? it : { kind: "tombstone", id: it.id };
|
|
225
|
-
}
|
|
226
|
-
for (let i = cutoff; i < items.length; i++) {
|
|
227
|
-
compacted[i] = items[i];
|
|
228
|
-
}
|
|
229
|
-
return compacted;
|
|
230
|
-
}
|
|
231
|
-
function summarizeGoalCompletion(summary) {
|
|
232
|
-
const lines = summary
|
|
233
|
-
.split("\n")
|
|
234
|
-
.map((line) => line.trim())
|
|
235
|
-
.filter((line) => line.length > 0 && line !== "[agent_done]");
|
|
236
|
-
const statusLine = lines.find((line) => /^Status:/i.test(line));
|
|
237
|
-
const changedLine = lines.find((line) => /^(Changed|Implemented|Fixed|Added|Key findings|Full verifier)/i.test(line));
|
|
238
|
-
const verificationLine = lines.find((line) => /^(Verification|Verified|Result):/i.test(line));
|
|
239
|
-
return statusLine ?? changedLine ?? verificationLine ?? lines[0];
|
|
240
|
-
}
|
|
241
|
-
const GOAL_PROGRESS_TEXT_LIMIT = 72;
|
|
242
|
-
export function truncateGoalProgressText(text) {
|
|
243
|
-
const normalized = text.replace(/\s+/g, " ").trim();
|
|
244
|
-
if (normalized.length <= GOAL_PROGRESS_TEXT_LIMIT)
|
|
245
|
-
return normalized;
|
|
246
|
-
return `${normalized.slice(0, GOAL_PROGRESS_TEXT_LIMIT - 1).trimEnd()}…`;
|
|
247
|
-
}
|
|
248
|
-
function formatGoalWorkerFinishedTitle(taskTitle, status) {
|
|
249
|
-
const prefix = status === "done" ? "Done" : "Failed";
|
|
250
|
-
return truncateGoalProgressText(`${prefix}: ${taskTitle}`);
|
|
251
|
-
}
|
|
252
127
|
function goalProgressLoaderStatus(item) {
|
|
253
|
-
if (item.status === "failed" || item.status === "fail" || item.status === "blocked")
|
|
128
|
+
if (item.status === "failed" || item.status === "fail" || item.status === "blocked") {
|
|
254
129
|
return "error";
|
|
130
|
+
}
|
|
255
131
|
if (item.phase === "worker_finished" ||
|
|
256
132
|
item.phase === "verifier_finished" ||
|
|
257
133
|
item.phase === "terminal") {
|
|
@@ -274,371 +150,6 @@ function goalProgressColor(item, theme) {
|
|
|
274
150
|
return theme.warning;
|
|
275
151
|
return theme.primary;
|
|
276
152
|
}
|
|
277
|
-
function goalTerminalProgressId(run) {
|
|
278
|
-
return `goal-terminal-${run.id}`;
|
|
279
|
-
}
|
|
280
|
-
function goalTerminalRunIdFromItem(item) {
|
|
281
|
-
if (item.kind !== "goal_progress" || item.phase !== "terminal")
|
|
282
|
-
return undefined;
|
|
283
|
-
if (!item.id.startsWith("goal-terminal-"))
|
|
284
|
-
return undefined;
|
|
285
|
-
return item.id.slice("goal-terminal-".length);
|
|
286
|
-
}
|
|
287
|
-
function goalProgressMatchesDraft(item, draft) {
|
|
288
|
-
return (item.phase === draft.phase &&
|
|
289
|
-
item.title === draft.title &&
|
|
290
|
-
item.detail === draft.detail &&
|
|
291
|
-
item.workerId === draft.workerId &&
|
|
292
|
-
item.status === draft.status &&
|
|
293
|
-
JSON.stringify(item.summaryRows ?? []) === JSON.stringify(draft.summaryRows ?? []) &&
|
|
294
|
-
JSON.stringify(item.summarySections ?? []) === JSON.stringify(draft.summarySections ?? []));
|
|
295
|
-
}
|
|
296
|
-
export function appendGoalProgressDraft(items, draft, makeId) {
|
|
297
|
-
const previous = items.at(-1);
|
|
298
|
-
if (previous?.kind === "goal_progress" && goalProgressMatchesDraft(previous, draft)) {
|
|
299
|
-
return items;
|
|
300
|
-
}
|
|
301
|
-
return [...items, { ...draft, id: makeId() }];
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Reconcile terminal Goal cards that are already visible in this UI session.
|
|
305
|
-
*
|
|
306
|
-
* This intentionally does not synthesize missing cards from durable GoalRun
|
|
307
|
-
* state. Goal terminal cards are event notifications: they should appear when
|
|
308
|
-
* the terminal event happens in the current UI, not whenever a fresh session
|
|
309
|
-
* polls old Goal runs from the Goal pane. Callers that just observed a terminal
|
|
310
|
-
* event append that card first, then use this helper to tombstone stale older
|
|
311
|
-
* cards for the same run.
|
|
312
|
-
*/
|
|
313
|
-
export function getNextGeneratedItemId(items) {
|
|
314
|
-
let max = -1;
|
|
315
|
-
for (const item of items) {
|
|
316
|
-
const raw = item.id.startsWith("ui-") ? item.id.slice(3) : item.id;
|
|
317
|
-
const n = Number(raw);
|
|
318
|
-
if (Number.isInteger(n) && n >= 0 && n > max)
|
|
319
|
-
max = n;
|
|
320
|
-
}
|
|
321
|
-
return max + 1;
|
|
322
|
-
}
|
|
323
|
-
export function completedItemsWithDurableGoalTerminalProgress(items, runs) {
|
|
324
|
-
const runIds = new Set(runs.map((run) => run.id));
|
|
325
|
-
const terminalByRun = new Map(runs
|
|
326
|
-
.map((run) => [run.id, formatGoalTerminalProgress(run)])
|
|
327
|
-
.filter((entry) => entry[1] !== null));
|
|
328
|
-
if (runIds.size === 0)
|
|
329
|
-
return items;
|
|
330
|
-
let changed = false;
|
|
331
|
-
const reconciled = items.map((item, index) => {
|
|
332
|
-
const runId = goalTerminalRunIdFromItem(item);
|
|
333
|
-
if (!runId || !runIds.has(runId))
|
|
334
|
-
return item;
|
|
335
|
-
const draft = terminalByRun.get(runId);
|
|
336
|
-
if (draft && goalProgressMatchesDraft(item, draft))
|
|
337
|
-
return item;
|
|
338
|
-
changed = true;
|
|
339
|
-
return { kind: "tombstone", id: `tombstone-${item.id}-${index}` };
|
|
340
|
-
});
|
|
341
|
-
return changed ? reconciled : items;
|
|
342
|
-
}
|
|
343
|
-
export function formatGoalTerminalProgress(run) {
|
|
344
|
-
switch (run.status) {
|
|
345
|
-
case "passed":
|
|
346
|
-
return {
|
|
347
|
-
kind: "goal_progress",
|
|
348
|
-
phase: "terminal",
|
|
349
|
-
title: `Goal passed: ${run.title}`,
|
|
350
|
-
detail: goalPassedDetail(run),
|
|
351
|
-
summaryRows: buildGoalSummaryRows(run),
|
|
352
|
-
summarySections: buildGoalFinalSummarySections(run),
|
|
353
|
-
status: run.status,
|
|
354
|
-
};
|
|
355
|
-
case "failed":
|
|
356
|
-
return {
|
|
357
|
-
kind: "goal_progress",
|
|
358
|
-
phase: "terminal",
|
|
359
|
-
title: `Goal failed: ${run.title}`,
|
|
360
|
-
detail: "Auto-continuation stopped. Check Goal tasks for the failing step.",
|
|
361
|
-
summaryRows: buildGoalSummaryRows(run),
|
|
362
|
-
status: run.status,
|
|
363
|
-
};
|
|
364
|
-
case "blocked":
|
|
365
|
-
return {
|
|
366
|
-
kind: "goal_progress",
|
|
367
|
-
phase: "terminal",
|
|
368
|
-
title: `Goal blocked: ${run.title}`,
|
|
369
|
-
detail: goalHasBlockingPrerequisites(run)
|
|
370
|
-
? formatGoalBlockingPrerequisites(run)
|
|
371
|
-
: (run.blockers[0] ?? "A prerequisite or missing verifier blocked progress."),
|
|
372
|
-
summaryRows: buildGoalSummaryRows(run),
|
|
373
|
-
status: run.status,
|
|
374
|
-
};
|
|
375
|
-
case "paused":
|
|
376
|
-
return {
|
|
377
|
-
kind: "goal_progress",
|
|
378
|
-
phase: "terminal",
|
|
379
|
-
title: `Goal paused: ${run.title}`,
|
|
380
|
-
detail: run.blockers[0] ?? "Auto-continuation paused.",
|
|
381
|
-
summaryRows: buildGoalSummaryRows(run),
|
|
382
|
-
status: run.status,
|
|
383
|
-
};
|
|
384
|
-
case "draft":
|
|
385
|
-
case "ready":
|
|
386
|
-
case "running":
|
|
387
|
-
case "verifying":
|
|
388
|
-
return null;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
export function shouldHideHistoryForOverlayView(isOverlayView, _isAgentRunning) {
|
|
392
|
-
// Overlay panes are standalone full-screen states. Finalized chat rows are
|
|
393
|
-
// printed outside Ink, so overlays should never replay transcript UI behind them.
|
|
394
|
-
return isOverlayView;
|
|
395
|
-
}
|
|
396
|
-
export function shouldStabilizeOverlayPaneRerender({ overlayPane, isAgentRunning, }) {
|
|
397
|
-
return isAgentRunning && overlayPane === "goal";
|
|
398
|
-
}
|
|
399
|
-
export function shouldHideStaticItemsForOverlayView({ shouldHideHistoryForOverlay, stabilizeOverlayPaneRerender: _stabilizeOverlayPaneRerender, }) {
|
|
400
|
-
return shouldHideHistoryForOverlay;
|
|
401
|
-
}
|
|
402
|
-
export function getDoneFlushDecision({ planOverlayPending, goalMode, goalAutoExpand, }) {
|
|
403
|
-
return {
|
|
404
|
-
showDoneStatus: !(planOverlayPending ||
|
|
405
|
-
goalMode === "planner" ||
|
|
406
|
-
goalMode === "setup" ||
|
|
407
|
-
goalAutoExpand),
|
|
408
|
-
flushLiveItems: true,
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
export function getGoalSetupFinishedPaneTransition() {
|
|
412
|
-
return {
|
|
413
|
-
overlay: "goal",
|
|
414
|
-
goalAutoExpand: true,
|
|
415
|
-
planAutoExpand: false,
|
|
416
|
-
suppressDoneStatus: true,
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
export function getGoalSetupPaneTransitionAfterRun({ isGoalSetupCommand, setupPanePending, }) {
|
|
420
|
-
return isGoalSetupCommand && setupPanePending ? getGoalSetupFinishedPaneTransition() : null;
|
|
421
|
-
}
|
|
422
|
-
export function shouldResetUIForSetupPaneTransition({ hasResetUI, hasSessionStore, }) {
|
|
423
|
-
// Opening a review pane is a full-screen state transition. A bare React state
|
|
424
|
-
// flip hides history in the virtual tree, but it does not reset Ink/log-update's
|
|
425
|
-
// already-written terminal frame, so the pane can render below prior chat.
|
|
426
|
-
return hasResetUI && hasSessionStore;
|
|
427
|
-
}
|
|
428
|
-
export const shouldResetUIForGoalSetupPaneTransition = shouldResetUIForSetupPaneTransition;
|
|
429
|
-
export function getGoalActivationPaneTransition() {
|
|
430
|
-
return { overlay: null, goalAutoExpand: false, planAutoExpand: false, resetReviewScreen: true };
|
|
431
|
-
}
|
|
432
|
-
export function getGoalContinuationChoiceKey({ runId, decision, }) {
|
|
433
|
-
switch (decision.kind) {
|
|
434
|
-
case "create_task":
|
|
435
|
-
return `${runId}:create_task:${decision.title}:${decision.prompt}`;
|
|
436
|
-
case "start_worker":
|
|
437
|
-
case "pause":
|
|
438
|
-
return `${runId}:${decision.kind}:${decision.task.id}:${decision.attempts}`;
|
|
439
|
-
case "run_verifier":
|
|
440
|
-
return `${runId}:run_verifier:${decision.command}`;
|
|
441
|
-
case "blocked":
|
|
442
|
-
case "complete":
|
|
443
|
-
case "terminal":
|
|
444
|
-
case "wait":
|
|
445
|
-
return `${runId}:${decision.kind}:${decision.reason}`;
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
export function getScrollStabilizationDecision({ isUserScrolled, hasNewOutput, hasTallLiveUserMessage = false, hasParagraphBreakLiveUserMessage = false, }) {
|
|
449
|
-
const shouldPreserveStatic = isUserScrolled || hasTallLiveUserMessage || hasParagraphBreakLiveUserMessage;
|
|
450
|
-
const shouldAutoFollow = !(isUserScrolled || hasTallLiveUserMessage);
|
|
451
|
-
return {
|
|
452
|
-
preserveStatic: shouldPreserveStatic && hasNewOutput,
|
|
453
|
-
autoFollow: shouldAutoFollow,
|
|
454
|
-
};
|
|
455
|
-
}
|
|
456
|
-
export function nextGoalModeAfterAgentDone({ currentMode, runningGoalIds, queuedSyntheticEvents, activeContinuationFlights = 0, wasGoalSetupTurn, }) {
|
|
457
|
-
if (wasGoalSetupTurn)
|
|
458
|
-
return "off";
|
|
459
|
-
if (currentMode === "planner" || currentMode === "setup")
|
|
460
|
-
return currentMode;
|
|
461
|
-
if (queuedSyntheticEvents > 0)
|
|
462
|
-
return "coordinator";
|
|
463
|
-
if (activeContinuationFlights > 0)
|
|
464
|
-
return "coordinator";
|
|
465
|
-
if (currentMode === "coordinator" && runningGoalIds > 0)
|
|
466
|
-
return "coordinator";
|
|
467
|
-
return "off";
|
|
468
|
-
}
|
|
469
|
-
export function hasParagraphBreakLiveUserMessage(text) {
|
|
470
|
-
return /\n[ \t]*\n/.test(text);
|
|
471
|
-
}
|
|
472
|
-
export function isTallLiveUserMessage(text, rows) {
|
|
473
|
-
return text.split("\n").length > Math.max(8, Math.floor(rows * 0.6));
|
|
474
|
-
}
|
|
475
|
-
export function getStaticHistoryKey({ resizeKey }) {
|
|
476
|
-
return `${resizeKey}`;
|
|
477
|
-
}
|
|
478
|
-
const MIN_LIVE_AREA_ROWS = 3;
|
|
479
|
-
const INPUT_AREA_ROWS = 3;
|
|
480
|
-
const STATUS_SLOT_ROWS = 2;
|
|
481
|
-
const FOOTER_ONE_LINE_ROWS = 1;
|
|
482
|
-
const FOOTER_TWO_LINE_ROWS = 2;
|
|
483
|
-
const GOAL_STATUS_ROWS = 1;
|
|
484
|
-
const COLLAPSED_FOOTER_STATUS_ROWS = 1;
|
|
485
|
-
const MAX_EXPANDED_BACKGROUND_TASK_ROWS = 7;
|
|
486
|
-
function isAgentSpacingKind(kind) {
|
|
487
|
-
return [
|
|
488
|
-
"assistant",
|
|
489
|
-
"queued",
|
|
490
|
-
"goal_progress",
|
|
491
|
-
"tool_start",
|
|
492
|
-
"tool_done",
|
|
493
|
-
"tool_group",
|
|
494
|
-
"server_tool_start",
|
|
495
|
-
"server_tool_done",
|
|
496
|
-
"subagent_group",
|
|
497
|
-
"info",
|
|
498
|
-
"error",
|
|
499
|
-
"stopped",
|
|
500
|
-
"plan_transition",
|
|
501
|
-
"goal_agent_transition",
|
|
502
|
-
"thinking_transition",
|
|
503
|
-
"model_transition",
|
|
504
|
-
"theme_transition",
|
|
505
|
-
"plan_event",
|
|
506
|
-
"update_notice",
|
|
507
|
-
"compacting",
|
|
508
|
-
"compacted",
|
|
509
|
-
"style_pack",
|
|
510
|
-
"setup_hint",
|
|
511
|
-
].includes(kind);
|
|
512
|
-
}
|
|
513
|
-
function isToolBoundaryKind(kind) {
|
|
514
|
-
return [
|
|
515
|
-
"goal_progress",
|
|
516
|
-
"tool_start",
|
|
517
|
-
"tool_done",
|
|
518
|
-
"tool_group",
|
|
519
|
-
"server_tool_start",
|
|
520
|
-
"server_tool_done",
|
|
521
|
-
"subagent_group",
|
|
522
|
-
].includes(kind);
|
|
523
|
-
}
|
|
524
|
-
function isAgentSpacingItem(item) {
|
|
525
|
-
return isAgentSpacingKind(item.kind);
|
|
526
|
-
}
|
|
527
|
-
export function shouldTopSpaceAfterPrintedAgentBoundary({ currentKind, previousLiveItem, lastPendingHistoryItem, lastHistoryItem, }) {
|
|
528
|
-
const needsExternalSpacing = isAgentSpacingKind(currentKind);
|
|
529
|
-
if (!needsExternalSpacing)
|
|
530
|
-
return false;
|
|
531
|
-
if (previousLiveItem !== undefined)
|
|
532
|
-
return false;
|
|
533
|
-
const previousKind = lastPendingHistoryItem?.kind ?? lastHistoryItem?.kind;
|
|
534
|
-
return previousKind !== undefined && isAgentSpacingKind(previousKind);
|
|
535
|
-
}
|
|
536
|
-
export function shouldTopSpaceAssistantAfterToolBoundary({ text, previousLiveItem, lastPendingHistoryItem, lastHistoryItem, }) {
|
|
537
|
-
if (text.trim().length === 0)
|
|
538
|
-
return false;
|
|
539
|
-
if (shouldTopSpaceAfterPrintedAgentBoundary({
|
|
540
|
-
currentKind: "assistant",
|
|
541
|
-
previousLiveItem,
|
|
542
|
-
lastPendingHistoryItem,
|
|
543
|
-
lastHistoryItem,
|
|
544
|
-
})) {
|
|
545
|
-
return true;
|
|
546
|
-
}
|
|
547
|
-
const previousKind = previousLiveItem?.kind;
|
|
548
|
-
return previousKind !== undefined && isToolBoundaryKind(previousKind);
|
|
549
|
-
}
|
|
550
|
-
export function shouldTopSpaceStreamingAssistant({ visibleStreamingText, lastLiveItem, lastPendingHistoryItem, lastHistoryItem, }) {
|
|
551
|
-
return shouldTopSpaceAssistantAfterToolBoundary({
|
|
552
|
-
text: visibleStreamingText,
|
|
553
|
-
previousLiveItem: lastLiveItem,
|
|
554
|
-
lastPendingHistoryItem,
|
|
555
|
-
lastHistoryItem,
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
|
-
export function getChatControlsLayoutDecision({ rows, agentRunning, activityVisible, doneStatusVisible, stallStatusVisible, exitPending, footerStatusLayout, taskBarExpanded, goalStatusEntryCount, footerFitsOnOneLine, }) {
|
|
559
|
-
const statusRows = activityVisible || stallStatusVisible || doneStatusVisible || agentRunning
|
|
560
|
-
? STATUS_SLOT_ROWS
|
|
561
|
-
: 0;
|
|
562
|
-
const footerRows = exitPending || footerFitsOnOneLine ? FOOTER_ONE_LINE_ROWS : FOOTER_TWO_LINE_ROWS;
|
|
563
|
-
const goalRows = !exitPending && goalStatusEntryCount > 0 ? GOAL_STATUS_ROWS : 0;
|
|
564
|
-
const footerStatusRows = footerStatusLayout.stack
|
|
565
|
-
? Number(footerStatusLayout.hasBackgroundTasks) + Number(footerStatusLayout.hasUpdateNotice)
|
|
566
|
-
: footerStatusLayout.hasBackgroundTasks || footerStatusLayout.hasUpdateNotice
|
|
567
|
-
? COLLAPSED_FOOTER_STATUS_ROWS
|
|
568
|
-
: 0;
|
|
569
|
-
const expandedTaskRows = taskBarExpanded && footerStatusLayout.hasBackgroundTasks
|
|
570
|
-
? MAX_EXPANDED_BACKGROUND_TASK_ROWS - COLLAPSED_FOOTER_STATUS_ROWS
|
|
571
|
-
: 0;
|
|
572
|
-
const controlsRows = statusRows + INPUT_AREA_ROWS + footerRows + goalRows + footerStatusRows + expandedTaskRows;
|
|
573
|
-
const maxControlsRows = Math.max(1, rows - MIN_LIVE_AREA_ROWS);
|
|
574
|
-
const boundedControlsRows = Math.min(controlsRows, maxControlsRows);
|
|
575
|
-
return {
|
|
576
|
-
controlsRows: boundedControlsRows,
|
|
577
|
-
liveAreaRows: Math.max(MIN_LIVE_AREA_ROWS, rows - boundedControlsRows),
|
|
578
|
-
};
|
|
579
|
-
}
|
|
580
|
-
// flushOnTurnText, flushOnTurnEnd are imported from ./live-item-flush.ts
|
|
581
|
-
/** Check whether an item is still active (running spinner, pending result). */
|
|
582
|
-
export function isActiveItem(item) {
|
|
583
|
-
switch (item.kind) {
|
|
584
|
-
case "tool_start":
|
|
585
|
-
case "server_tool_start":
|
|
586
|
-
case "queued":
|
|
587
|
-
case "compacting":
|
|
588
|
-
return true;
|
|
589
|
-
case "tool_group":
|
|
590
|
-
return item.tools.some((t) => t.status === "running");
|
|
591
|
-
case "subagent_group":
|
|
592
|
-
return item.agents.some((a) => a.status === "running");
|
|
593
|
-
default:
|
|
594
|
-
return false;
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
/**
|
|
598
|
-
* Partition live items into completed (flushable to finalized history) and still-active.
|
|
599
|
-
* Completed items precede active ones — we flush the longest contiguous prefix
|
|
600
|
-
* of completed items to keep ordering stable.
|
|
601
|
-
*/
|
|
602
|
-
export function partitionCompleted(items) {
|
|
603
|
-
// Find the first active item — everything before it is safe to flush as a
|
|
604
|
-
// single chronological prefix. Splitting assistant text out of that prefix
|
|
605
|
-
// lets later tool rows print to scrollback above the message that introduced
|
|
606
|
-
// them, so keep the prefix intact.
|
|
607
|
-
const firstActiveIdx = items.findIndex(isActiveItem);
|
|
608
|
-
if (firstActiveIdx === -1) {
|
|
609
|
-
return { flushed: items, remaining: [] };
|
|
610
|
-
}
|
|
611
|
-
if (firstActiveIdx === 0) {
|
|
612
|
-
return { flushed: [], remaining: items };
|
|
613
|
-
}
|
|
614
|
-
return {
|
|
615
|
-
flushed: items.slice(0, firstActiveIdx),
|
|
616
|
-
remaining: items.slice(firstActiveIdx),
|
|
617
|
-
};
|
|
618
|
-
}
|
|
619
|
-
function normalizeAssistantText(text) {
|
|
620
|
-
return stripDoneMarkers(text).trim();
|
|
621
|
-
}
|
|
622
|
-
function isSameAssistantText(item, text) {
|
|
623
|
-
return item.kind === "assistant" && normalizeAssistantText(item.text) === text;
|
|
624
|
-
}
|
|
625
|
-
export function pinStreamingTextBeforeToolBoundary({ items, visibleStreamingText, thinking, thinkingMs, makeId, }) {
|
|
626
|
-
const text = normalizeAssistantText(visibleStreamingText);
|
|
627
|
-
if (text.length === 0)
|
|
628
|
-
return items;
|
|
629
|
-
if (items.some((item) => item.kind === "assistant"))
|
|
630
|
-
return items;
|
|
631
|
-
return [
|
|
632
|
-
...items,
|
|
633
|
-
{
|
|
634
|
-
kind: "assistant",
|
|
635
|
-
text,
|
|
636
|
-
thinking: thinking.length > 0 ? thinking : undefined,
|
|
637
|
-
thinkingMs: thinking.length > 0 ? thinkingMs : undefined,
|
|
638
|
-
id: makeId(),
|
|
639
|
-
},
|
|
640
|
-
];
|
|
641
|
-
}
|
|
642
153
|
// ── Duration summary ─────────────────────────────────────
|
|
643
154
|
function formatDuration(ms) {
|
|
644
155
|
const totalSec = Math.round(ms / 1000);
|
|
@@ -777,7 +288,7 @@ export function App(props) {
|
|
|
777
288
|
const [currentProvider, setCurrentProvider] = useState(props.provider);
|
|
778
289
|
const [currentTools, setCurrentTools] = useState(props.tools);
|
|
779
290
|
const currentToolsRef = useRef(props.tools);
|
|
780
|
-
const [
|
|
291
|
+
const [thinkingLevel, setThinkingLevel] = useState(props.thinking);
|
|
781
292
|
const [renderMarkdown, setRenderMarkdown] = useState(true);
|
|
782
293
|
const messagesRef = useRef(props.sessionStore?.messages ?? props.messages);
|
|
783
294
|
const repoMapInjectionEnabledRef = useRef(true);
|
|
@@ -923,11 +434,16 @@ export function App(props) {
|
|
|
923
434
|
useEffect(() => {
|
|
924
435
|
onRuntimeStateChange?.({ provider: currentProvider });
|
|
925
436
|
}, [currentProvider, onRuntimeStateChange]);
|
|
437
|
+
useEffect(() => {
|
|
438
|
+
if (thinkingLevel && !isThinkingLevelSupported(currentProvider, currentModel, thinkingLevel)) {
|
|
439
|
+
setThinkingLevel(getNextThinkingLevel(currentProvider, currentModel, undefined));
|
|
440
|
+
}
|
|
441
|
+
}, [currentProvider, currentModel, thinkingLevel]);
|
|
926
442
|
useEffect(() => {
|
|
927
443
|
onRuntimeStateChange?.({
|
|
928
|
-
thinking:
|
|
444
|
+
thinking: thinkingLevel,
|
|
929
445
|
});
|
|
930
|
-
}, [
|
|
446
|
+
}, [thinkingLevel, onRuntimeStateChange]);
|
|
931
447
|
useEffect(() => {
|
|
932
448
|
printHistoryItems(history);
|
|
933
449
|
}, [history, printHistoryItems]);
|
|
@@ -1081,6 +597,29 @@ export function App(props) {
|
|
|
1081
597
|
}
|
|
1082
598
|
return newPrompt;
|
|
1083
599
|
}, [rebuildSystemPrompt]);
|
|
600
|
+
useEffect(() => {
|
|
601
|
+
if (!props.connectInitialMcpTools)
|
|
602
|
+
return;
|
|
603
|
+
let cancelled = false;
|
|
604
|
+
void props
|
|
605
|
+
.connectInitialMcpTools()
|
|
606
|
+
.then((mcpTools) => {
|
|
607
|
+
if (cancelled || mcpTools.length === 0)
|
|
608
|
+
return;
|
|
609
|
+
setCurrentTools((prev) => {
|
|
610
|
+
const next = [...prev.filter((tool) => !tool.name.startsWith("mcp__")), ...mcpTools];
|
|
611
|
+
currentToolsRef.current = next;
|
|
612
|
+
void replaceSystemPrompt({ tools: next });
|
|
613
|
+
return next;
|
|
614
|
+
});
|
|
615
|
+
})
|
|
616
|
+
.catch((err) => {
|
|
617
|
+
log("WARN", "mcp", `MCP initialization failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
618
|
+
});
|
|
619
|
+
return () => {
|
|
620
|
+
cancelled = true;
|
|
621
|
+
};
|
|
622
|
+
}, [props.connectInitialMcpTools, replaceSystemPrompt]);
|
|
1084
623
|
const setGoalModeAndPrompt = useCallback(async (nextMode, options) => {
|
|
1085
624
|
goalModeStateRef.current = nextMode;
|
|
1086
625
|
if (props.goalModeRef)
|
|
@@ -1345,13 +884,10 @@ export function App(props) {
|
|
|
1345
884
|
(props.repoMapReadFilesRef?.current.size ?? 0));
|
|
1346
885
|
}, [props.repoMapChangedFilesRef, props.repoMapReadFilesRef]);
|
|
1347
886
|
const getRepoMapBudget = useCallback(() => {
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
if (readCount > 0)
|
|
1353
|
-
return FOCUSED_REPO_MAP_MAX_CHARS;
|
|
1354
|
-
return FOCUSED_REPO_MAP_MAX_CHARS + 1000;
|
|
887
|
+
return getRepoMapBudgetForContext({
|
|
888
|
+
messages: messagesRef.current,
|
|
889
|
+
readFileCount: props.repoMapReadFilesRef?.current.size ?? 0,
|
|
890
|
+
});
|
|
1355
891
|
}, [props.repoMapReadFilesRef]);
|
|
1356
892
|
const refreshRepoMap = useCallback(async (latestUserPrompt) => {
|
|
1357
893
|
const rendered = await buildRepoMap({
|
|
@@ -1471,7 +1007,7 @@ export function App(props) {
|
|
|
1471
1007
|
tools: currentTools,
|
|
1472
1008
|
webSearch: props.webSearch,
|
|
1473
1009
|
maxTokens: props.maxTokens,
|
|
1474
|
-
thinking:
|
|
1010
|
+
thinking: thinkingLevel,
|
|
1475
1011
|
apiKey: activeApiKey,
|
|
1476
1012
|
baseUrl: activeBaseUrl,
|
|
1477
1013
|
accountId: activeAccountId,
|
|
@@ -2636,20 +2172,20 @@ export function App(props) {
|
|
|
2636
2172
|
}
|
|
2637
2173
|
}, [agentLoop, handleDoubleExit]);
|
|
2638
2174
|
const handleToggleThinking = useCallback(() => {
|
|
2639
|
-
|
|
2640
|
-
const next =
|
|
2641
|
-
log("INFO", "thinking", `Thinking ${next
|
|
2642
|
-
setLiveItems((items) => [
|
|
2643
|
-
...items,
|
|
2644
|
-
{ kind: "thinking_transition", active: next, id: getId() },
|
|
2645
|
-
]);
|
|
2175
|
+
setThinkingLevel((prev) => {
|
|
2176
|
+
const next = getNextThinkingLevel(currentProvider, currentModel, prev);
|
|
2177
|
+
log("INFO", "thinking", next ? `Thinking ${next}` : "Thinking disabled");
|
|
2646
2178
|
if (props.settingsFile) {
|
|
2647
2179
|
const sm = new SettingsManager(props.settingsFile);
|
|
2648
|
-
sm.load().then(() =>
|
|
2180
|
+
void sm.load().then(async () => {
|
|
2181
|
+
await sm.set("thinkingEnabled", !!next);
|
|
2182
|
+
if (next)
|
|
2183
|
+
await sm.set("thinkingLevel", next);
|
|
2184
|
+
});
|
|
2649
2185
|
}
|
|
2650
2186
|
return next;
|
|
2651
2187
|
});
|
|
2652
|
-
}, [props.settingsFile]);
|
|
2188
|
+
}, [currentProvider, currentModel, props.settingsFile]);
|
|
2653
2189
|
const handleModelSelect = useCallback((value) => {
|
|
2654
2190
|
setOverlay(null);
|
|
2655
2191
|
const colonIdx = value.indexOf(":");
|
|
@@ -2838,7 +2374,7 @@ export function App(props) {
|
|
|
2838
2374
|
case "user":
|
|
2839
2375
|
return (_jsx(UserMessage, { text: item.text, imageCount: item.imageCount, pasteInfo: item.pasteInfo }, item.id));
|
|
2840
2376
|
case "goal":
|
|
2841
|
-
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: "Goal: " }), _jsx(Text, { color: theme.success, children: truncateGoalProgressText(item.title) }), item.workerId ? _jsxs(Text, { color: theme.textDim, children: [" \u00B7 worker ", item.workerId] }) : null] }) }, item.id));
|
|
2377
|
+
return (_jsx(Box, { paddingLeft: 1, marginTop: 1, children: _jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: theme.success, bold: true, children: "▶ " }), _jsx(Text, { color: theme.textDim, children: "Goal: " }), _jsx(Text, { color: theme.success, children: truncateGoalProgressText(item.title) }), item.workerId ? _jsxs(Text, { color: theme.textDim, children: [" \u00B7 worker ", item.workerId] }) : null] }) }, item.id));
|
|
2842
2378
|
case "goal_progress": {
|
|
2843
2379
|
const color = goalProgressColor(item, theme);
|
|
2844
2380
|
const loaderStatus = goalProgressLoaderStatus(item);
|
|
@@ -2846,15 +2382,15 @@ export function App(props) {
|
|
|
2846
2382
|
(item.summaryRows !== undefined && item.summaryRows.length > 0) ||
|
|
2847
2383
|
(item.summarySections !== undefined && item.summarySections.length > 0);
|
|
2848
2384
|
const headerContentWidth = Math.max(10, columns - 3);
|
|
2849
|
-
return withPrintedBoundarySpacing(_jsxs(Box, { flexDirection: "column", paddingLeft: 1, marginTop: 1, flexShrink: 1, children: [_jsxs(Box, { flexDirection: "row", children: [_jsx(ToolUseLoader, { status: loaderStatus, staticDisplay: true }), _jsx(Box, { flexGrow: 1, width: headerContentWidth, children: _jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: color, bold: true, children: truncateGoalProgressText(item.title) }), item.workerId ? (_jsxs(Text, { color: theme.textDim, children: [" \u00B7 worker ", item.workerId] })) : null] }) })] }), hasBody ? (_jsx(MessageResponse, { children: _jsxs(Box, { flexDirection: "column", flexShrink: 1, children: [item.detail ? (_jsx(Text, { color: theme.textDim, wrap: "wrap", children: truncateGoalProgressText(item.detail) })) : null, item.summaryRows?.map((row) => (_jsxs(Text, { wrap: "truncate", children: [_jsx(Text, { color: theme.textDim, children: row.label.padEnd(12) }), _jsx(Text, { color: theme.text, children: truncateGoalProgressText(row.value) }), row.detail ? (_jsxs(Text, { color: theme.textDim, children: [" \u00B7 ", truncateGoalProgressText(row.detail)] })) : null] }, row.label))), item.summarySections?.map((section) => (_jsxs(Box, { flexDirection: "column", marginTop: 1, flexShrink: 1, children: [_jsx(Text, { color: theme.textDim, bold: true, children: section.title }), section.lines.map((line, sectionLineIndex) => (_jsx(Text, { color: theme.text, wrap: "wrap", children: `• ${truncateGoalProgressText(line)}` }, `${section.title}-${sectionLineIndex}`)))] }, section.title)))] }) })) : null] }, item.id));
|
|
2385
|
+
return withPrintedBoundarySpacing(_jsxs(Box, { flexDirection: "column", paddingLeft: 1, marginTop: 1, flexShrink: 1, children: [_jsxs(Box, { flexDirection: "row", children: [_jsx(ToolUseLoader, { status: loaderStatus, staticDisplay: true, color: color }), _jsx(Box, { flexGrow: 1, width: headerContentWidth, children: _jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: color, bold: true, children: truncateGoalProgressText(item.title) }), item.workerId ? (_jsxs(Text, { color: theme.textDim, children: [" \u00B7 worker ", item.workerId] })) : null] }) })] }), hasBody ? (_jsx(MessageResponse, { children: _jsxs(Box, { flexDirection: "column", flexShrink: 1, children: [item.detail ? (_jsx(Text, { color: theme.textDim, wrap: "wrap", children: truncateGoalProgressText(item.detail) })) : null, item.summaryRows?.map((row) => (_jsxs(Text, { wrap: "truncate", children: [_jsx(Text, { color: theme.textDim, children: row.label.padEnd(12) }), _jsx(Text, { color: theme.text, children: truncateGoalProgressText(row.value) }), row.detail ? (_jsxs(Text, { color: theme.textDim, children: [" \u00B7 ", truncateGoalProgressText(row.detail)] })) : null] }, row.label))), item.summarySections?.map((section) => (_jsxs(Box, { flexDirection: "column", marginTop: 1, flexShrink: 1, children: [_jsx(Text, { color: theme.textDim, bold: true, children: section.title }), section.lines.map((line, sectionLineIndex) => (_jsx(Text, { color: theme.text, wrap: "wrap", children: `• ${truncateGoalProgressText(line)}` }, `${section.title}-${sectionLineIndex}`)))] }, section.title)))] }) })) : null] }, item.id));
|
|
2850
2386
|
}
|
|
2851
2387
|
case "style_pack": {
|
|
2852
2388
|
const names = item.added.map((id) => LANGUAGE_DISPLAY_NAMES[id]);
|
|
2853
2389
|
const headerLabel = item.added.length > 1 ? "STYLE PACKS ACTIVE" : "STYLE PACK ACTIVE";
|
|
2854
|
-
return (
|
|
2390
|
+
return (_jsx(Box, { paddingLeft: 1, marginTop: 1, flexShrink: 1, children: _jsxs(Box, { flexShrink: 1, flexDirection: "column", borderStyle: "round", borderColor: theme.language, paddingX: 1, children: [_jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: theme.language, bold: true, children: "◆ " }), _jsx(Text, { color: theme.language, bold: true, children: headerLabel })] }), _jsx(Text, { color: theme.text, bold: true, wrap: "wrap", children: names.join(", ") }), item.showSetupHint && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: theme.textMuted, children: "Tip: run " }), _jsx(Text, { color: theme.language, bold: true, children: "/setup" }), _jsx(Text, { color: theme.textMuted, children: " to audit this project against the active pack(s)" })] }) }))] }) }, item.id));
|
|
2855
2391
|
}
|
|
2856
2392
|
case "setup_hint":
|
|
2857
|
-
return (
|
|
2393
|
+
return (_jsx(Box, { paddingLeft: 1, marginTop: 1, flexShrink: 1, children: _jsxs(Box, { flexShrink: 1, flexDirection: "column", borderStyle: "round", borderColor: theme.language, paddingX: 1, children: [_jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: theme.language, bold: true, children: "◆ " }), _jsx(Text, { color: theme.language, bold: true, children: "NO STYLE PACKS DETECTED" })] }), _jsx(Text, { color: theme.textMuted, wrap: "wrap", children: "This directory has no recognized language manifest at its root." }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: theme.textMuted, children: "Tip: run " }), _jsx(Text, { color: theme.language, bold: true, children: "/setup" }), _jsx(Text, { color: theme.textMuted, children: " to audit project hygiene or bootstrap a new project from scratch" })] }) })] }) }, item.id));
|
|
2858
2394
|
case "assistant":
|
|
2859
2395
|
return (_jsx(AssistantMessage, { text: item.text, thinking: item.thinking, thinkingMs: item.thinkingMs, renderMarkdown: renderMarkdown, availableTerminalHeight: measuredLiveAreaRows, marginTop: assistantMarginTop }, item.id));
|
|
2860
2396
|
case "tool_start":
|
|
@@ -2874,15 +2410,11 @@ export function App(props) {
|
|
|
2874
2410
|
case "info":
|
|
2875
2411
|
return renderStatusMessage(item.id, "○ ", item.text, theme.commandColor, { muted: true });
|
|
2876
2412
|
case "update_notice":
|
|
2877
|
-
return (_jsx(Box, { marginTop: 1, flexShrink: 1, borderStyle: "round", borderColor: theme.commandColor, paddingX: 1, children: _jsxs(Text, { color: theme.commandColor, bold: true, wrap: "wrap", children: ["✨ ", item.text] }) }, item.id));
|
|
2413
|
+
return (_jsx(Box, { paddingLeft: 1, marginTop: 1, flexShrink: 1, children: _jsx(Box, { flexShrink: 1, borderStyle: "round", borderColor: theme.commandColor, paddingX: 1, children: _jsxs(Text, { color: theme.commandColor, bold: true, wrap: "wrap", children: ["✨ ", item.text] }) }) }, item.id));
|
|
2878
2414
|
case "plan_transition":
|
|
2879
|
-
return renderStatusMessage(item.id,
|
|
2415
|
+
return renderStatusMessage(item.id, `${BLACK_CIRCLE} `, normalizeStatusText(item.text), theme.commandColor, { bold: true });
|
|
2880
2416
|
case "goal_agent_transition":
|
|
2881
|
-
return renderStatusMessage(item.id,
|
|
2882
|
-
case "thinking_transition": {
|
|
2883
|
-
const glyphColor = item.active ? theme.commandColor : theme.textDim;
|
|
2884
|
-
return renderStatusMessage(item.id, "✻ ", item.active ? "Thinking ON" : "Thinking OFF", glyphColor, { bold: true, muted: !item.active });
|
|
2885
|
-
}
|
|
2417
|
+
return renderStatusMessage(item.id, `${BLACK_CIRCLE} `, normalizeStatusText(item.text), theme.commandColor, { bold: true });
|
|
2886
2418
|
case "model_transition":
|
|
2887
2419
|
return renderStatusMessage(item.id, "▸ ", _jsxs(_Fragment, { children: [_jsx(Text, { color: theme.textDim, children: "Switched to " }), _jsx(Text, { color: theme.commandColor, bold: true, children: item.modelName })] }), theme.commandColor, { bold: true });
|
|
2888
2420
|
case "theme_transition":
|
|
@@ -2903,7 +2435,7 @@ export function App(props) {
|
|
|
2903
2435
|
// gradient. Glyph `⊘` reads as "stop" without being alarming.
|
|
2904
2436
|
return renderStatusMessage(item.id, "⊘ ", normalizeStatusText(item.text), theme.commandColor, { bold: true });
|
|
2905
2437
|
case "step_done":
|
|
2906
|
-
return (_jsx(Box, { marginTop: 1, flexShrink: 1, children: _jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: theme.success, bold: true, children: "✓ " }), _jsx(Text, { color: theme.success, bold: true, children: `Step ${item.stepNum} done` }), item.description ? (_jsx(Text, { color: theme.textDim, children: ` — ${item.description}` })) : null] }) }, item.id));
|
|
2438
|
+
return (_jsx(Box, { paddingLeft: 1, marginTop: 1, flexShrink: 1, children: _jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: theme.success, bold: true, children: "✓ " }), _jsx(Text, { color: theme.success, bold: true, children: `Step ${item.stepNum} done` }), item.description ? (_jsx(Text, { color: theme.textDim, children: ` — ${item.description}` })) : null] }) }, item.id));
|
|
2907
2439
|
case "queued": {
|
|
2908
2440
|
const suffix = item.imageCount
|
|
2909
2441
|
? ` (+${item.imageCount} image${item.imageCount > 1 ? "s" : ""})`
|
|
@@ -2915,7 +2447,7 @@ export function App(props) {
|
|
|
2915
2447
|
case "compacted":
|
|
2916
2448
|
return (_jsx(CompactionDone, { originalCount: item.originalCount, newCount: item.newCount, tokensBefore: item.tokensBefore, tokensAfter: item.tokensAfter }, item.id));
|
|
2917
2449
|
case "duration":
|
|
2918
|
-
return (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.textDim, children: ["✻ ", item.verb, " ", formatDuration(item.durationMs)] }) }, item.id));
|
|
2450
|
+
return (_jsx(Box, { paddingLeft: 1, marginTop: 1, children: _jsxs(Text, { color: theme.textDim, children: ["✻ ", item.verb, " ", formatDuration(item.durationMs)] }) }, item.id));
|
|
2919
2451
|
case "subagent_group":
|
|
2920
2452
|
return withPrintedBoundarySpacing(_jsx(SubAgentPanel, { agents: item.agents, aborted: item.aborted }, item.id));
|
|
2921
2453
|
}
|
|
@@ -2964,9 +2496,13 @@ export function App(props) {
|
|
|
2964
2496
|
const detail = eventInfo?.kind === "worker"
|
|
2965
2497
|
? `Inspecting worker result${eventInfo.task ? ` for ${eventInfo.task}` : ""}.`
|
|
2966
2498
|
: `Inspecting verifier result${eventInfo?.status ? ` (${eventInfo.status})` : ""}.`;
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2499
|
+
const route = routeGoalSyntheticEvent({
|
|
2500
|
+
agentRunning: agentRunningRef.current,
|
|
2501
|
+
queuedSyntheticEvents: queuedGoalSyntheticEventsRef.current,
|
|
2502
|
+
});
|
|
2503
|
+
if (route.action === "queue") {
|
|
2504
|
+
queuedGoalSyntheticEventsRef.current = route.nextQueuedSyntheticEvents;
|
|
2505
|
+
void setGoalModeAndPrompt(route.nextGoalMode);
|
|
2970
2506
|
appendGoalProgress({
|
|
2971
2507
|
kind: "goal_progress",
|
|
2972
2508
|
phase: "orchestrator_reviewing",
|
|
@@ -3315,6 +2851,7 @@ export function App(props) {
|
|
|
3315
2851
|
cwd: props.cwd,
|
|
3316
2852
|
provider: currentProvider,
|
|
3317
2853
|
model: currentModel,
|
|
2854
|
+
thinkingLevel,
|
|
3318
2855
|
goalRunId: checkedRun.id,
|
|
3319
2856
|
goalTaskId: decision.task.id,
|
|
3320
2857
|
taskTitle: decision.task.title,
|
|
@@ -3358,6 +2895,7 @@ export function App(props) {
|
|
|
3358
2895
|
props.cwd,
|
|
3359
2896
|
currentProvider,
|
|
3360
2897
|
currentModel,
|
|
2898
|
+
thinkingLevel,
|
|
3361
2899
|
appendGoalProgress,
|
|
3362
2900
|
clearGoalModeIfIdle,
|
|
3363
2901
|
clearGoalStatusEntry,
|
|
@@ -3667,7 +3205,7 @@ export function App(props) {
|
|
|
3667
3205
|
contextWindowOptions,
|
|
3668
3206
|
cwd: displayedCwd,
|
|
3669
3207
|
gitBranch,
|
|
3670
|
-
thinkingLevel
|
|
3208
|
+
thinkingLevel,
|
|
3671
3209
|
goalMode,
|
|
3672
3210
|
});
|
|
3673
3211
|
const chatControlsLayout = getChatControlsLayoutDecision({
|
|
@@ -3722,12 +3260,24 @@ export function App(props) {
|
|
|
3722
3260
|
const shouldReserveStreamingSpacing = agentLoop.isRunning &&
|
|
3723
3261
|
!hasLiveAssistantItem &&
|
|
3724
3262
|
(visibleStreamingText.trim().length > 0 || liveItems.some(isAgentSpacingItem));
|
|
3263
|
+
const lastLiveItem = liveItems.at(-1);
|
|
3264
|
+
const lastPendingHistoryItem = pendingHistoryFlushRef.current.at(-1);
|
|
3265
|
+
const lastHistoryItem = history.at(-1);
|
|
3725
3266
|
const shouldTopSpaceStreamingText = shouldTopSpaceStreamingAssistant({
|
|
3726
3267
|
visibleStreamingText,
|
|
3727
|
-
lastLiveItem
|
|
3728
|
-
lastPendingHistoryItem
|
|
3729
|
-
lastHistoryItem
|
|
3268
|
+
lastLiveItem,
|
|
3269
|
+
lastPendingHistoryItem,
|
|
3270
|
+
lastHistoryItem,
|
|
3730
3271
|
});
|
|
3272
|
+
const visibleQueuedCount = liveItems.filter((item) => item.kind === "queued").length;
|
|
3273
|
+
const hiddenQueuedCount = Math.max(0, agentLoop.queuedCount - visibleQueuedCount);
|
|
3274
|
+
const shouldTopSpaceQueueIndicator = hiddenQueuedCount > 0 &&
|
|
3275
|
+
shouldTopSpaceAfterPrintedAgentBoundary({
|
|
3276
|
+
currentKind: "queued",
|
|
3277
|
+
previousLiveItem: lastLiveItem,
|
|
3278
|
+
lastPendingHistoryItem,
|
|
3279
|
+
lastHistoryItem,
|
|
3280
|
+
});
|
|
3731
3281
|
return (_jsx(Box, { flexDirection: "column", width: columns, flexShrink: 0, flexGrow: 0, children: isGoalView ? (_jsx(GoalOverlay, { cwd: props.cwd, agentRunning: agentLoop.isRunning, autoExpandNewest: goalAutoExpand, onClose: () => {
|
|
3732
3282
|
goalAutoExpandRef.current = false;
|
|
3733
3283
|
setGoalAutoExpand(false);
|
|
@@ -3946,7 +3496,7 @@ export function App(props) {
|
|
|
3946
3496
|
log("ERROR", "error", errMsg);
|
|
3947
3497
|
setLiveItems((prev) => [...prev, toErrorItem(err, getId())]);
|
|
3948
3498
|
});
|
|
3949
|
-
} })) : (_jsxs(Box, { flexDirection: "column", width: columns, flexShrink: 0, flexGrow: 0, children: [_jsxs(Box, { flexDirection: "column", flexGrow: 0, flexShrink: 1, overflowY: "hidden", children: [liveItems.map((item, index, items) => renderItem(item, index, items)), _jsx(StreamingArea, { isRunning: agentLoop.isRunning, streamingText: visibleStreamingText, streamingThinking: agentLoop.streamingThinking, thinkingMs: agentLoop.thinkingMs, reserveSpacing: shouldReserveStreamingSpacing, renderMarkdown: renderMarkdown, availableTerminalHeight: measuredLiveAreaRows, assistantMarginTop: shouldTopSpaceStreamingText ? 1 : 0, continuation: streamedAssistantFlushRef.current.flushedChars > 0 })] }), _jsxs(Box, { ref: mainControlsRef, flexDirection: "column", flexShrink: 0, flexGrow: 0, children: [
|
|
3499
|
+
} })) : (_jsxs(Box, { flexDirection: "column", width: columns, flexShrink: 0, flexGrow: 0, children: [_jsxs(Box, { flexDirection: "column", flexGrow: 0, flexShrink: 1, overflowY: "hidden", children: [liveItems.map((item, index, items) => renderItem(item, index, items)), _jsx(StreamingArea, { isRunning: agentLoop.isRunning, streamingText: visibleStreamingText, streamingThinking: agentLoop.streamingThinking, thinkingMs: agentLoop.thinkingMs, reserveSpacing: shouldReserveStreamingSpacing, renderMarkdown: renderMarkdown, availableTerminalHeight: measuredLiveAreaRows, assistantMarginTop: shouldTopSpaceStreamingText ? 1 : 0, continuation: streamedAssistantFlushRef.current.flushedChars > 0 })] }), _jsxs(Box, { ref: mainControlsRef, flexDirection: "column", flexShrink: 0, flexGrow: 0, children: [hiddenQueuedCount > 0 && (_jsxs(Box, { flexDirection: "row", paddingLeft: 1, marginTop: shouldTopSpaceQueueIndicator ? 2 : 1, flexShrink: 0, children: [_jsx(Box, { width: 2, flexShrink: 0, children: _jsx(Text, { color: theme.warning, bold: true, children: "• " }) }), _jsxs(Text, { color: theme.textDim, children: [hiddenQueuedCount, " message", hiddenQueuedCount > 1 ? "s" : "", " queued"] })] })), _jsxs(Box, { flexDirection: "column", width: columns, children: [_jsx(Box, { borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: theme.textDim, width: columns, height: 0 }), _jsx(Box, { paddingLeft: 1, paddingRight: 1, width: columns, children: statusSlotVisible ? (activityVisible ? (_jsx(ActivityIndicator, { phase: agentLoop.activityPhase, elapsedMs: agentLoop.elapsedMs, runStartRef: agentLoop.runStartRef, thinkingMs: agentLoop.thinkingMs, isThinking: agentLoop.isThinking, thinkingEnabled: !!thinkingLevel, tokenEstimate: agentLoop.streamedTokenEstimate, charCountRef: agentLoop.charCountRef, realTokensAccumRef: agentLoop.realTokensAccumRef, userMessage: lastUserMessage, activeToolNames: agentLoop.activeToolCalls.map((tc) => tc.name), retryInfo: agentLoop.retryInfo, planDone: planSteps.filter((s) => s.completed).length, planTotal: planSteps.length, staticDisplay: true })) : stallStatusVisible ? (_jsx(Text, { color: theme.warning, wrap: "truncate", children: "⚠ API provider stream interrupted — retries exhausted. Your conversation is preserved." })) : doneStatus ? (_jsxs(Text, { color: theme.success, children: ["✻ ", doneStatus.verb, " ", formatDuration(doneStatus.durationMs)] })) : (_jsxs(Text, { children: [_jsx(Text, { color: theme.commandColor, children: "⠿ " }), _jsx(Text, { color: theme.textDim, children: "Ready to go.." }), !renderMarkdown && (_jsx(Text, { color: theme.warning, children: " · raw markdown mode" }))] }))) : (_jsxs(Text, { children: [_jsx(Text, { color: theme.commandColor, children: "⠿ " }), _jsx(Text, { color: theme.textDim, children: "Ready to go.." })] })) })] }), _jsx(InputArea, { onSubmit: handleSubmit, onAbort: handleAbort, disabled: agentLoop.isRunning, isActive: !taskBarFocused && !overlay, onDownAtEnd: handleFocusTaskBar, onShiftTab: handleToggleThinking, onToggleGoal: () => {
|
|
3950
3500
|
openOverlay("goal");
|
|
3951
3501
|
}, onToggleSkills: () => {
|
|
3952
3502
|
openOverlay("skills");
|
|
@@ -3954,7 +3504,7 @@ export function App(props) {
|
|
|
3954
3504
|
openOverlay("pixel");
|
|
3955
3505
|
}, onToggleMarkdown: () => {
|
|
3956
3506
|
setRenderMarkdown((prev) => !prev);
|
|
3957
|
-
}, 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 })) : (_jsxs(_Fragment, { children: [_jsx(Footer, { model: currentModel, tokensIn: agentLoop.contextUsed, contextWindowOptions: contextWindowOptions, cwd: displayedCwd, gitBranch: gitBranch, thinkingLevel:
|
|
3507
|
+
}, 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 })) : (_jsxs(_Fragment, { children: [_jsx(Footer, { model: currentModel, tokensIn: agentLoop.contextUsed, contextWindowOptions: contextWindowOptions, cwd: displayedCwd, gitBranch: gitBranch, thinkingLevel: thinkingLevel, goalMode: goalMode, exitPending: exitPending, renderMarkdown: renderMarkdown }), !exitPending && _jsx(GoalStatusBar, { entries: goalStatusEntries })] })), (footerStatusLayout.hasBackgroundTasks || footerStatusLayout.hasUpdateNotice) && (_jsxs(Box, { flexDirection: footerStatusLayout.stack ? "column" : "row", width: columns, children: [footerStatusLayout.hasBackgroundTasks && (_jsx(BackgroundTasksBar, { tasks: bgTasks, focused: taskBarFocused, expanded: taskBarExpanded, selectedIndex: selectedTaskIndex, onExpand: handleTaskBarExpand, onCollapse: handleTaskBarCollapse, onKill: handleTaskKill, onExit: handleTaskBarExit, onNavigate: handleTaskNavigate, compact: footerStatusLayout.compactBackgroundTasks })), footerStatusLayout.hasUpdateNotice && (_jsx(Box, { paddingLeft: footerStatusLayout.stack || !footerStatusLayout.hasBackgroundTasks ? 1 : 2, paddingRight: 1, children: _jsx(Text, { color: theme.success, bold: true, wrap: "truncate", children: "\u2728 Update ready \u00B7 restart to apply" }) }))] }))] })] })) }));
|
|
3958
3508
|
}
|
|
3959
3509
|
function formatRepoMapCommandOutput(enabled, markdown, refreshed) {
|
|
3960
3510
|
const status = enabled ? "on" : "off";
|