@kenkaiiii/ggcoder 4.3.217 → 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 +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +46 -103
- package/dist/cli.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-store.d.ts +9 -0
- package/dist/core/goal-store.d.ts.map +1 -1
- package/dist/core/goal-store.js +24 -0
- package/dist/core/goal-store.js.map +1 -1
- package/dist/core/goal-worker.d.ts +7 -0
- package/dist/core/goal-worker.d.ts.map +1 -1
- package/dist/core/goal-worker.js +62 -20
- package/dist/core/goal-worker.js.map +1 -1
- package/dist/core/goal-worker.test.js +92 -2
- 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.js +2 -2
- package/dist/system-prompt.js.map +1 -1
- package/dist/system-prompt.test.js +2 -2
- package/dist/system-prompt.test.js.map +1 -1
- package/dist/tools/goals.d.ts.map +1 -1
- package/dist/tools/goals.js +30 -26
- package/dist/tools/goals.js.map +1 -1
- package/dist/tools/goals.test.js +47 -1
- package/dist/tools/goals.test.js.map +1 -1
- package/dist/ui/App.d.ts +11 -381
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +61 -554
- 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 +2 -1
- package/dist/ui/chat-layout-pinning.test.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/goal-events.test.js +2 -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 +1 -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 +17 -233
- package/dist/ui/terminal-history.js.map +1 -1
- package/dist/ui/terminal-history.test.js +2 -1
- package/dist/ui/terminal-history.test.js.map +1 -1
- package/dist/ui/thinking-level-cycle.test.js +7 -1
- package/dist/ui/thinking-level-cycle.test.js.map +1 -1
- 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/tui-history-parity.test.js +4 -3
- package/dist/ui/tui-history-parity.test.js.map +1 -1
- package/package.json +3 -3
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,114 +107,6 @@ function toErrorItem(err, id, contextPrefix) {
|
|
|
97
107
|
id,
|
|
98
108
|
};
|
|
99
109
|
}
|
|
100
|
-
export function routePromptCommandInput(input, promptCommands = PROMPT_COMMANDS, customCommands = []) {
|
|
101
|
-
const trimmed = input.trim();
|
|
102
|
-
if (!trimmed.startsWith("/"))
|
|
103
|
-
return null;
|
|
104
|
-
const parts = trimmed.slice(1).split(" ");
|
|
105
|
-
const cmdName = parts[0];
|
|
106
|
-
const cmdArgs = parts.slice(1).join(" ").trim();
|
|
107
|
-
const builtinCmd = promptCommands.find((c) => c.name === cmdName || c.aliases.includes(cmdName));
|
|
108
|
-
const customCmd = !builtinCmd ? customCommands.find((c) => c.name === cmdName) : undefined;
|
|
109
|
-
const promptText = builtinCmd?.prompt ?? customCmd?.prompt;
|
|
110
|
-
if (!promptText)
|
|
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
|
-
}
|
|
163
|
-
function buildGoalTaskPromptWithReferences(run, taskPrompt) {
|
|
164
|
-
if (taskPrompt.includes("## Goal References (MANDATORY)"))
|
|
165
|
-
return taskPrompt;
|
|
166
|
-
const references = formatGoalReferencesForPrompt(run.references ?? []);
|
|
167
|
-
return references ? `${references}\n\n${taskPrompt}` : taskPrompt;
|
|
168
|
-
}
|
|
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
110
|
/** Tools that get aggregated into a single compact group when possible. */
|
|
209
111
|
const AGGREGATABLE_TOOLS = new Set([
|
|
210
112
|
"read",
|
|
@@ -215,66 +117,17 @@ const AGGREGATABLE_TOOLS = new Set([
|
|
|
215
117
|
"mcp__kencode-search__referenceSources",
|
|
216
118
|
"mcp__kencode-search__discoverRepos",
|
|
217
119
|
]);
|
|
218
|
-
const OPENAI_GPT_THINKING_LEVELS = ["medium", "high", "xhigh"];
|
|
219
|
-
function isOpenAIGptModel(provider, model) {
|
|
220
|
-
return provider === "openai" && model.startsWith("gpt-");
|
|
221
|
-
}
|
|
222
|
-
export function getNextThinkingLevel(provider, model, current) {
|
|
223
|
-
if (!isOpenAIGptModel(provider, model)) {
|
|
224
|
-
return current ? undefined : getMaxThinkingLevel(model);
|
|
225
|
-
}
|
|
226
|
-
if (!current)
|
|
227
|
-
return "medium";
|
|
228
|
-
const index = OPENAI_GPT_THINKING_LEVELS.indexOf(current);
|
|
229
|
-
if (index === -1)
|
|
230
|
-
return "medium";
|
|
231
|
-
return OPENAI_GPT_THINKING_LEVELS[index + 1];
|
|
232
|
-
}
|
|
233
120
|
const RUNNING_INDICATOR_ANIMATION_MS = 1_200;
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
const MAX_LIVE_HISTORY = 200;
|
|
240
|
-
function compactHistory(items) {
|
|
241
|
-
if (items.length <= MAX_LIVE_HISTORY)
|
|
242
|
-
return items;
|
|
243
|
-
const cutoff = items.length - MAX_LIVE_HISTORY;
|
|
244
|
-
const compacted = new Array(items.length);
|
|
245
|
-
for (let i = 0; i < cutoff; i++) {
|
|
246
|
-
const it = items[i];
|
|
247
|
-
compacted[i] = it.kind === "tombstone" ? it : { kind: "tombstone", id: it.id };
|
|
248
|
-
}
|
|
249
|
-
for (let i = cutoff; i < items.length; i++) {
|
|
250
|
-
compacted[i] = items[i];
|
|
251
|
-
}
|
|
252
|
-
return compacted;
|
|
253
|
-
}
|
|
254
|
-
function summarizeGoalCompletion(summary) {
|
|
255
|
-
const lines = summary
|
|
256
|
-
.split("\n")
|
|
257
|
-
.map((line) => line.trim())
|
|
258
|
-
.filter((line) => line.length > 0 && line !== "[agent_done]");
|
|
259
|
-
const statusLine = lines.find((line) => /^Status:/i.test(line));
|
|
260
|
-
const changedLine = lines.find((line) => /^(Changed|Implemented|Fixed|Added|Key findings|Full verifier)/i.test(line));
|
|
261
|
-
const verificationLine = lines.find((line) => /^(Verification|Verified|Result):/i.test(line));
|
|
262
|
-
return statusLine ?? changedLine ?? verificationLine ?? lines[0];
|
|
263
|
-
}
|
|
264
|
-
const GOAL_PROGRESS_TEXT_LIMIT = 72;
|
|
265
|
-
export function truncateGoalProgressText(text) {
|
|
266
|
-
const normalized = text.replace(/\s+/g, " ").trim();
|
|
267
|
-
if (normalized.length <= GOAL_PROGRESS_TEXT_LIMIT)
|
|
268
|
-
return normalized;
|
|
269
|
-
return `${normalized.slice(0, GOAL_PROGRESS_TEXT_LIMIT - 1).trimEnd()}…`;
|
|
270
|
-
}
|
|
271
|
-
function formatGoalWorkerFinishedTitle(taskTitle, status) {
|
|
272
|
-
const prefix = status === "done" ? "Done" : "Failed";
|
|
273
|
-
return truncateGoalProgressText(`${prefix}: ${taskTitle}`);
|
|
121
|
+
function buildGoalTaskPromptWithReferences(run, taskPrompt) {
|
|
122
|
+
if (taskPrompt.includes("## Goal References (MANDATORY)"))
|
|
123
|
+
return taskPrompt;
|
|
124
|
+
const references = formatGoalReferencesForPrompt(run.references ?? []);
|
|
125
|
+
return references ? `${references}\n\n${taskPrompt}` : taskPrompt;
|
|
274
126
|
}
|
|
275
127
|
function goalProgressLoaderStatus(item) {
|
|
276
|
-
if (item.status === "failed" || item.status === "fail" || item.status === "blocked")
|
|
128
|
+
if (item.status === "failed" || item.status === "fail" || item.status === "blocked") {
|
|
277
129
|
return "error";
|
|
130
|
+
}
|
|
278
131
|
if (item.phase === "worker_finished" ||
|
|
279
132
|
item.phase === "verifier_finished" ||
|
|
280
133
|
item.phase === "terminal") {
|
|
@@ -297,373 +150,6 @@ function goalProgressColor(item, theme) {
|
|
|
297
150
|
return theme.warning;
|
|
298
151
|
return theme.primary;
|
|
299
152
|
}
|
|
300
|
-
function goalTerminalProgressId(run) {
|
|
301
|
-
return `goal-terminal-${run.id}`;
|
|
302
|
-
}
|
|
303
|
-
function goalTerminalRunIdFromItem(item) {
|
|
304
|
-
if (item.kind !== "goal_progress" || item.phase !== "terminal")
|
|
305
|
-
return undefined;
|
|
306
|
-
if (!item.id.startsWith("goal-terminal-"))
|
|
307
|
-
return undefined;
|
|
308
|
-
return item.id.slice("goal-terminal-".length);
|
|
309
|
-
}
|
|
310
|
-
function goalProgressMatchesDraft(item, draft) {
|
|
311
|
-
return (item.phase === draft.phase &&
|
|
312
|
-
item.title === draft.title &&
|
|
313
|
-
item.detail === draft.detail &&
|
|
314
|
-
item.workerId === draft.workerId &&
|
|
315
|
-
item.status === draft.status &&
|
|
316
|
-
JSON.stringify(item.summaryRows ?? []) === JSON.stringify(draft.summaryRows ?? []) &&
|
|
317
|
-
JSON.stringify(item.summarySections ?? []) === JSON.stringify(draft.summarySections ?? []));
|
|
318
|
-
}
|
|
319
|
-
export function appendGoalProgressDraft(items, draft, makeId) {
|
|
320
|
-
const previous = items.at(-1);
|
|
321
|
-
if (previous?.kind === "goal_progress" && goalProgressMatchesDraft(previous, draft)) {
|
|
322
|
-
return items;
|
|
323
|
-
}
|
|
324
|
-
return [...items, { ...draft, id: makeId() }];
|
|
325
|
-
}
|
|
326
|
-
/**
|
|
327
|
-
* Reconcile terminal Goal cards that are already visible in this UI session.
|
|
328
|
-
*
|
|
329
|
-
* This intentionally does not synthesize missing cards from durable GoalRun
|
|
330
|
-
* state. Goal terminal cards are event notifications: they should appear when
|
|
331
|
-
* the terminal event happens in the current UI, not whenever a fresh session
|
|
332
|
-
* polls old Goal runs from the Goal pane. Callers that just observed a terminal
|
|
333
|
-
* event append that card first, then use this helper to tombstone stale older
|
|
334
|
-
* cards for the same run.
|
|
335
|
-
*/
|
|
336
|
-
export function getNextGeneratedItemId(items) {
|
|
337
|
-
let max = -1;
|
|
338
|
-
for (const item of items) {
|
|
339
|
-
const raw = item.id.startsWith("ui-") ? item.id.slice(3) : item.id;
|
|
340
|
-
const n = Number(raw);
|
|
341
|
-
if (Number.isInteger(n) && n >= 0 && n > max)
|
|
342
|
-
max = n;
|
|
343
|
-
}
|
|
344
|
-
return max + 1;
|
|
345
|
-
}
|
|
346
|
-
export function completedItemsWithDurableGoalTerminalProgress(items, runs) {
|
|
347
|
-
const runIds = new Set(runs.map((run) => run.id));
|
|
348
|
-
const terminalByRun = new Map(runs
|
|
349
|
-
.map((run) => [run.id, formatGoalTerminalProgress(run)])
|
|
350
|
-
.filter((entry) => entry[1] !== null));
|
|
351
|
-
if (runIds.size === 0)
|
|
352
|
-
return items;
|
|
353
|
-
let changed = false;
|
|
354
|
-
const reconciled = items.map((item, index) => {
|
|
355
|
-
const runId = goalTerminalRunIdFromItem(item);
|
|
356
|
-
if (!runId || !runIds.has(runId))
|
|
357
|
-
return item;
|
|
358
|
-
const draft = terminalByRun.get(runId);
|
|
359
|
-
if (draft && goalProgressMatchesDraft(item, draft))
|
|
360
|
-
return item;
|
|
361
|
-
changed = true;
|
|
362
|
-
return { kind: "tombstone", id: `tombstone-${item.id}-${index}` };
|
|
363
|
-
});
|
|
364
|
-
return changed ? reconciled : items;
|
|
365
|
-
}
|
|
366
|
-
export function formatGoalTerminalProgress(run) {
|
|
367
|
-
switch (run.status) {
|
|
368
|
-
case "passed":
|
|
369
|
-
return {
|
|
370
|
-
kind: "goal_progress",
|
|
371
|
-
phase: "terminal",
|
|
372
|
-
title: `Goal passed: ${run.title}`,
|
|
373
|
-
detail: goalPassedDetail(run),
|
|
374
|
-
summaryRows: buildGoalSummaryRows(run),
|
|
375
|
-
summarySections: buildGoalFinalSummarySections(run),
|
|
376
|
-
status: run.status,
|
|
377
|
-
};
|
|
378
|
-
case "failed":
|
|
379
|
-
return {
|
|
380
|
-
kind: "goal_progress",
|
|
381
|
-
phase: "terminal",
|
|
382
|
-
title: `Goal failed: ${run.title}`,
|
|
383
|
-
detail: "Auto-continuation stopped. Check Goal tasks for the failing step.",
|
|
384
|
-
summaryRows: buildGoalSummaryRows(run),
|
|
385
|
-
status: run.status,
|
|
386
|
-
};
|
|
387
|
-
case "blocked":
|
|
388
|
-
return {
|
|
389
|
-
kind: "goal_progress",
|
|
390
|
-
phase: "terminal",
|
|
391
|
-
title: `Goal blocked: ${run.title}`,
|
|
392
|
-
detail: goalHasBlockingPrerequisites(run)
|
|
393
|
-
? formatGoalBlockingPrerequisites(run)
|
|
394
|
-
: (run.blockers[0] ?? "A prerequisite or missing verifier blocked progress."),
|
|
395
|
-
summaryRows: buildGoalSummaryRows(run),
|
|
396
|
-
status: run.status,
|
|
397
|
-
};
|
|
398
|
-
case "paused":
|
|
399
|
-
return {
|
|
400
|
-
kind: "goal_progress",
|
|
401
|
-
phase: "terminal",
|
|
402
|
-
title: `Goal paused: ${run.title}`,
|
|
403
|
-
detail: run.blockers[0] ?? "Auto-continuation paused.",
|
|
404
|
-
summaryRows: buildGoalSummaryRows(run),
|
|
405
|
-
status: run.status,
|
|
406
|
-
};
|
|
407
|
-
case "draft":
|
|
408
|
-
case "ready":
|
|
409
|
-
case "running":
|
|
410
|
-
case "verifying":
|
|
411
|
-
return null;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
export function shouldHideHistoryForOverlayView(isOverlayView, _isAgentRunning) {
|
|
415
|
-
// Overlay panes are standalone full-screen states. Finalized chat rows are
|
|
416
|
-
// printed outside Ink, so overlays should never replay transcript UI behind them.
|
|
417
|
-
return isOverlayView;
|
|
418
|
-
}
|
|
419
|
-
export function shouldStabilizeOverlayPaneRerender({ overlayPane, isAgentRunning, }) {
|
|
420
|
-
return isAgentRunning && overlayPane === "goal";
|
|
421
|
-
}
|
|
422
|
-
export function shouldHideStaticItemsForOverlayView({ shouldHideHistoryForOverlay, stabilizeOverlayPaneRerender: _stabilizeOverlayPaneRerender, }) {
|
|
423
|
-
return shouldHideHistoryForOverlay;
|
|
424
|
-
}
|
|
425
|
-
export function getDoneFlushDecision({ planOverlayPending, goalMode, goalAutoExpand, }) {
|
|
426
|
-
return {
|
|
427
|
-
showDoneStatus: !(planOverlayPending ||
|
|
428
|
-
goalMode === "planner" ||
|
|
429
|
-
goalMode === "setup" ||
|
|
430
|
-
goalAutoExpand),
|
|
431
|
-
flushLiveItems: true,
|
|
432
|
-
};
|
|
433
|
-
}
|
|
434
|
-
export function getGoalSetupFinishedPaneTransition() {
|
|
435
|
-
return {
|
|
436
|
-
overlay: "goal",
|
|
437
|
-
goalAutoExpand: true,
|
|
438
|
-
planAutoExpand: false,
|
|
439
|
-
suppressDoneStatus: true,
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
export function getGoalSetupPaneTransitionAfterRun({ isGoalSetupCommand, setupPanePending, }) {
|
|
443
|
-
return isGoalSetupCommand && setupPanePending ? getGoalSetupFinishedPaneTransition() : null;
|
|
444
|
-
}
|
|
445
|
-
export function shouldResetUIForSetupPaneTransition({ hasResetUI, hasSessionStore, }) {
|
|
446
|
-
// Opening a review pane is a full-screen state transition. A bare React state
|
|
447
|
-
// flip hides history in the virtual tree, but it does not reset Ink/log-update's
|
|
448
|
-
// already-written terminal frame, so the pane can render below prior chat.
|
|
449
|
-
return hasResetUI && hasSessionStore;
|
|
450
|
-
}
|
|
451
|
-
export const shouldResetUIForGoalSetupPaneTransition = shouldResetUIForSetupPaneTransition;
|
|
452
|
-
export function getGoalActivationPaneTransition() {
|
|
453
|
-
return { overlay: null, goalAutoExpand: false, planAutoExpand: false, resetReviewScreen: true };
|
|
454
|
-
}
|
|
455
|
-
export function getGoalContinuationChoiceKey({ runId, decision, }) {
|
|
456
|
-
switch (decision.kind) {
|
|
457
|
-
case "create_task":
|
|
458
|
-
return `${runId}:create_task:${decision.title}:${decision.prompt}`;
|
|
459
|
-
case "start_worker":
|
|
460
|
-
case "pause":
|
|
461
|
-
return `${runId}:${decision.kind}:${decision.task.id}:${decision.attempts}`;
|
|
462
|
-
case "run_verifier":
|
|
463
|
-
return `${runId}:run_verifier:${decision.command}`;
|
|
464
|
-
case "blocked":
|
|
465
|
-
case "complete":
|
|
466
|
-
case "terminal":
|
|
467
|
-
case "wait":
|
|
468
|
-
return `${runId}:${decision.kind}:${decision.reason}`;
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
export function getScrollStabilizationDecision({ isUserScrolled, hasNewOutput, hasTallLiveUserMessage = false, hasParagraphBreakLiveUserMessage = false, }) {
|
|
472
|
-
const shouldPreserveStatic = isUserScrolled || hasTallLiveUserMessage || hasParagraphBreakLiveUserMessage;
|
|
473
|
-
const shouldAutoFollow = !(isUserScrolled || hasTallLiveUserMessage);
|
|
474
|
-
return {
|
|
475
|
-
preserveStatic: shouldPreserveStatic && hasNewOutput,
|
|
476
|
-
autoFollow: shouldAutoFollow,
|
|
477
|
-
};
|
|
478
|
-
}
|
|
479
|
-
export function nextGoalModeAfterAgentDone({ currentMode, runningGoalIds, queuedSyntheticEvents, activeContinuationFlights = 0, wasGoalSetupTurn, }) {
|
|
480
|
-
if (wasGoalSetupTurn)
|
|
481
|
-
return "off";
|
|
482
|
-
if (currentMode === "planner" || currentMode === "setup")
|
|
483
|
-
return currentMode;
|
|
484
|
-
if (queuedSyntheticEvents > 0)
|
|
485
|
-
return "coordinator";
|
|
486
|
-
if (activeContinuationFlights > 0)
|
|
487
|
-
return "coordinator";
|
|
488
|
-
if (currentMode === "coordinator" && runningGoalIds > 0)
|
|
489
|
-
return "coordinator";
|
|
490
|
-
return "off";
|
|
491
|
-
}
|
|
492
|
-
export function hasParagraphBreakLiveUserMessage(text) {
|
|
493
|
-
return /\n[ \t]*\n/.test(text);
|
|
494
|
-
}
|
|
495
|
-
export function isTallLiveUserMessage(text, rows) {
|
|
496
|
-
return text.split("\n").length > Math.max(8, Math.floor(rows * 0.6));
|
|
497
|
-
}
|
|
498
|
-
export function getStaticHistoryKey({ resizeKey }) {
|
|
499
|
-
return `${resizeKey}`;
|
|
500
|
-
}
|
|
501
|
-
const MIN_LIVE_AREA_ROWS = 3;
|
|
502
|
-
const INPUT_AREA_ROWS = 3;
|
|
503
|
-
const STATUS_SLOT_ROWS = 2;
|
|
504
|
-
const FOOTER_ONE_LINE_ROWS = 1;
|
|
505
|
-
const FOOTER_TWO_LINE_ROWS = 2;
|
|
506
|
-
const GOAL_STATUS_ROWS = 1;
|
|
507
|
-
const COLLAPSED_FOOTER_STATUS_ROWS = 1;
|
|
508
|
-
const MAX_EXPANDED_BACKGROUND_TASK_ROWS = 7;
|
|
509
|
-
function isAgentSpacingKind(kind) {
|
|
510
|
-
return [
|
|
511
|
-
"assistant",
|
|
512
|
-
"queued",
|
|
513
|
-
"goal_progress",
|
|
514
|
-
"tool_start",
|
|
515
|
-
"tool_done",
|
|
516
|
-
"tool_group",
|
|
517
|
-
"server_tool_start",
|
|
518
|
-
"server_tool_done",
|
|
519
|
-
"subagent_group",
|
|
520
|
-
"info",
|
|
521
|
-
"error",
|
|
522
|
-
"stopped",
|
|
523
|
-
"plan_transition",
|
|
524
|
-
"goal_agent_transition",
|
|
525
|
-
"model_transition",
|
|
526
|
-
"theme_transition",
|
|
527
|
-
"plan_event",
|
|
528
|
-
"update_notice",
|
|
529
|
-
"compacting",
|
|
530
|
-
"compacted",
|
|
531
|
-
"style_pack",
|
|
532
|
-
"setup_hint",
|
|
533
|
-
].includes(kind);
|
|
534
|
-
}
|
|
535
|
-
function isToolBoundaryKind(kind) {
|
|
536
|
-
return [
|
|
537
|
-
"goal_progress",
|
|
538
|
-
"tool_start",
|
|
539
|
-
"tool_done",
|
|
540
|
-
"tool_group",
|
|
541
|
-
"server_tool_start",
|
|
542
|
-
"server_tool_done",
|
|
543
|
-
"subagent_group",
|
|
544
|
-
].includes(kind);
|
|
545
|
-
}
|
|
546
|
-
function isAgentSpacingItem(item) {
|
|
547
|
-
return isAgentSpacingKind(item.kind);
|
|
548
|
-
}
|
|
549
|
-
export function shouldTopSpaceAfterPrintedAgentBoundary({ currentKind, previousLiveItem, lastPendingHistoryItem, lastHistoryItem, }) {
|
|
550
|
-
const needsExternalSpacing = isAgentSpacingKind(currentKind);
|
|
551
|
-
if (!needsExternalSpacing)
|
|
552
|
-
return false;
|
|
553
|
-
if (previousLiveItem !== undefined)
|
|
554
|
-
return false;
|
|
555
|
-
const previousKind = lastPendingHistoryItem?.kind ?? lastHistoryItem?.kind;
|
|
556
|
-
return previousKind !== undefined && isAgentSpacingKind(previousKind);
|
|
557
|
-
}
|
|
558
|
-
export function shouldTopSpaceAssistantAfterToolBoundary({ text, previousLiveItem, lastPendingHistoryItem, lastHistoryItem, }) {
|
|
559
|
-
if (text.trim().length === 0)
|
|
560
|
-
return false;
|
|
561
|
-
if (shouldTopSpaceAfterPrintedAgentBoundary({
|
|
562
|
-
currentKind: "assistant",
|
|
563
|
-
previousLiveItem,
|
|
564
|
-
lastPendingHistoryItem,
|
|
565
|
-
lastHistoryItem,
|
|
566
|
-
})) {
|
|
567
|
-
return true;
|
|
568
|
-
}
|
|
569
|
-
const previousKind = previousLiveItem?.kind;
|
|
570
|
-
return previousKind !== undefined && isToolBoundaryKind(previousKind);
|
|
571
|
-
}
|
|
572
|
-
export function shouldTopSpaceStreamingAssistant({ visibleStreamingText, lastLiveItem, lastPendingHistoryItem, lastHistoryItem, }) {
|
|
573
|
-
return shouldTopSpaceAssistantAfterToolBoundary({
|
|
574
|
-
text: visibleStreamingText,
|
|
575
|
-
previousLiveItem: lastLiveItem,
|
|
576
|
-
lastPendingHistoryItem,
|
|
577
|
-
lastHistoryItem,
|
|
578
|
-
});
|
|
579
|
-
}
|
|
580
|
-
export function getChatControlsLayoutDecision({ rows, agentRunning, activityVisible, doneStatusVisible, stallStatusVisible, exitPending, footerStatusLayout, taskBarExpanded, goalStatusEntryCount, footerFitsOnOneLine, }) {
|
|
581
|
-
const statusRows = activityVisible || stallStatusVisible || doneStatusVisible || agentRunning
|
|
582
|
-
? STATUS_SLOT_ROWS
|
|
583
|
-
: 0;
|
|
584
|
-
const footerRows = exitPending || footerFitsOnOneLine ? FOOTER_ONE_LINE_ROWS : FOOTER_TWO_LINE_ROWS;
|
|
585
|
-
const goalRows = !exitPending && goalStatusEntryCount > 0 ? GOAL_STATUS_ROWS : 0;
|
|
586
|
-
const footerStatusRows = footerStatusLayout.stack
|
|
587
|
-
? Number(footerStatusLayout.hasBackgroundTasks) + Number(footerStatusLayout.hasUpdateNotice)
|
|
588
|
-
: footerStatusLayout.hasBackgroundTasks || footerStatusLayout.hasUpdateNotice
|
|
589
|
-
? COLLAPSED_FOOTER_STATUS_ROWS
|
|
590
|
-
: 0;
|
|
591
|
-
const expandedTaskRows = taskBarExpanded && footerStatusLayout.hasBackgroundTasks
|
|
592
|
-
? MAX_EXPANDED_BACKGROUND_TASK_ROWS - COLLAPSED_FOOTER_STATUS_ROWS
|
|
593
|
-
: 0;
|
|
594
|
-
const controlsRows = statusRows + INPUT_AREA_ROWS + footerRows + goalRows + footerStatusRows + expandedTaskRows;
|
|
595
|
-
const maxControlsRows = Math.max(1, rows - MIN_LIVE_AREA_ROWS);
|
|
596
|
-
const boundedControlsRows = Math.min(controlsRows, maxControlsRows);
|
|
597
|
-
return {
|
|
598
|
-
controlsRows: boundedControlsRows,
|
|
599
|
-
liveAreaRows: Math.max(MIN_LIVE_AREA_ROWS, rows - boundedControlsRows),
|
|
600
|
-
};
|
|
601
|
-
}
|
|
602
|
-
// flushOnTurnText, flushOnTurnEnd are imported from ./live-item-flush.ts
|
|
603
|
-
/** Check whether an item is still active (running spinner, pending result). */
|
|
604
|
-
export function isActiveItem(item) {
|
|
605
|
-
switch (item.kind) {
|
|
606
|
-
case "tool_start":
|
|
607
|
-
case "server_tool_start":
|
|
608
|
-
case "queued":
|
|
609
|
-
case "compacting":
|
|
610
|
-
return true;
|
|
611
|
-
case "tool_group":
|
|
612
|
-
return item.tools.some((t) => t.status === "running");
|
|
613
|
-
case "subagent_group":
|
|
614
|
-
return item.agents.some((a) => a.status === "running");
|
|
615
|
-
default:
|
|
616
|
-
return false;
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
/**
|
|
620
|
-
* Partition live items into completed (flushable to finalized history) and still-active.
|
|
621
|
-
* Completed items precede active ones — we flush the longest contiguous prefix
|
|
622
|
-
* of completed items to keep ordering stable.
|
|
623
|
-
*/
|
|
624
|
-
export function partitionCompleted(items) {
|
|
625
|
-
// Find the first active item — everything before it is safe to flush as a
|
|
626
|
-
// single chronological prefix. Splitting assistant text out of that prefix
|
|
627
|
-
// lets later tool rows print to scrollback above the message that introduced
|
|
628
|
-
// them, so keep the prefix intact.
|
|
629
|
-
const firstActiveIdx = items.findIndex(isActiveItem);
|
|
630
|
-
if (firstActiveIdx === -1) {
|
|
631
|
-
return { flushed: items, remaining: [] };
|
|
632
|
-
}
|
|
633
|
-
if (firstActiveIdx === 0) {
|
|
634
|
-
return { flushed: [], remaining: items };
|
|
635
|
-
}
|
|
636
|
-
return {
|
|
637
|
-
flushed: items.slice(0, firstActiveIdx),
|
|
638
|
-
remaining: items.slice(firstActiveIdx),
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
function normalizeAssistantText(text) {
|
|
642
|
-
return stripDoneMarkers(text).trim();
|
|
643
|
-
}
|
|
644
|
-
function isReasoningMarkerText(text) {
|
|
645
|
-
return /^(?:currentItem\?\.type\s*=+\s*)?["']?reasoning["']?$/u.test(text.trim());
|
|
646
|
-
}
|
|
647
|
-
function isSameAssistantText(item, text) {
|
|
648
|
-
return item.kind === "assistant" && normalizeAssistantText(item.text) === text;
|
|
649
|
-
}
|
|
650
|
-
export function pinStreamingTextBeforeToolBoundary({ items, visibleStreamingText, thinking, thinkingMs, makeId, }) {
|
|
651
|
-
const text = normalizeAssistantText(visibleStreamingText);
|
|
652
|
-
if (text.length === 0 || isReasoningMarkerText(text))
|
|
653
|
-
return items;
|
|
654
|
-
if (items.some((item) => item.kind === "assistant"))
|
|
655
|
-
return items;
|
|
656
|
-
return [
|
|
657
|
-
...items,
|
|
658
|
-
{
|
|
659
|
-
kind: "assistant",
|
|
660
|
-
text,
|
|
661
|
-
thinking: thinking.length > 0 ? thinking : undefined,
|
|
662
|
-
thinkingMs: thinking.length > 0 ? thinkingMs : undefined,
|
|
663
|
-
id: makeId(),
|
|
664
|
-
},
|
|
665
|
-
];
|
|
666
|
-
}
|
|
667
153
|
// ── Duration summary ─────────────────────────────────────
|
|
668
154
|
function formatDuration(ms) {
|
|
669
155
|
const totalSec = Math.round(ms / 1000);
|
|
@@ -949,11 +435,8 @@ export function App(props) {
|
|
|
949
435
|
onRuntimeStateChange?.({ provider: currentProvider });
|
|
950
436
|
}, [currentProvider, onRuntimeStateChange]);
|
|
951
437
|
useEffect(() => {
|
|
952
|
-
if (thinkingLevel && !
|
|
953
|
-
|
|
954
|
-
if (thinkingLevel !== maxLevel) {
|
|
955
|
-
setThinkingLevel(maxLevel);
|
|
956
|
-
}
|
|
438
|
+
if (thinkingLevel && !isThinkingLevelSupported(currentProvider, currentModel, thinkingLevel)) {
|
|
439
|
+
setThinkingLevel(getNextThinkingLevel(currentProvider, currentModel, undefined));
|
|
957
440
|
}
|
|
958
441
|
}, [currentProvider, currentModel, thinkingLevel]);
|
|
959
442
|
useEffect(() => {
|
|
@@ -1114,6 +597,29 @@ export function App(props) {
|
|
|
1114
597
|
}
|
|
1115
598
|
return newPrompt;
|
|
1116
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]);
|
|
1117
623
|
const setGoalModeAndPrompt = useCallback(async (nextMode, options) => {
|
|
1118
624
|
goalModeStateRef.current = nextMode;
|
|
1119
625
|
if (props.goalModeRef)
|
|
@@ -1378,13 +884,10 @@ export function App(props) {
|
|
|
1378
884
|
(props.repoMapReadFilesRef?.current.size ?? 0));
|
|
1379
885
|
}, [props.repoMapChangedFilesRef, props.repoMapReadFilesRef]);
|
|
1380
886
|
const getRepoMapBudget = useCallback(() => {
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
if (readCount > 0)
|
|
1386
|
-
return FOCUSED_REPO_MAP_MAX_CHARS;
|
|
1387
|
-
return FOCUSED_REPO_MAP_MAX_CHARS + 1000;
|
|
887
|
+
return getRepoMapBudgetForContext({
|
|
888
|
+
messages: messagesRef.current,
|
|
889
|
+
readFileCount: props.repoMapReadFilesRef?.current.size ?? 0,
|
|
890
|
+
});
|
|
1388
891
|
}, [props.repoMapReadFilesRef]);
|
|
1389
892
|
const refreshRepoMap = useCallback(async (latestUserPrompt) => {
|
|
1390
893
|
const rendered = await buildRepoMap({
|
|
@@ -2879,7 +2382,7 @@ export function App(props) {
|
|
|
2879
2382
|
(item.summaryRows !== undefined && item.summaryRows.length > 0) ||
|
|
2880
2383
|
(item.summarySections !== undefined && item.summarySections.length > 0);
|
|
2881
2384
|
const headerContentWidth = Math.max(10, columns - 3);
|
|
2882
|
-
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));
|
|
2883
2386
|
}
|
|
2884
2387
|
case "style_pack": {
|
|
2885
2388
|
const names = item.added.map((id) => LANGUAGE_DISPLAY_NAMES[id]);
|
|
@@ -2909,9 +2412,9 @@ export function App(props) {
|
|
|
2909
2412
|
case "update_notice":
|
|
2910
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));
|
|
2911
2414
|
case "plan_transition":
|
|
2912
|
-
return renderStatusMessage(item.id,
|
|
2415
|
+
return renderStatusMessage(item.id, `${BLACK_CIRCLE} `, normalizeStatusText(item.text), theme.commandColor, { bold: true });
|
|
2913
2416
|
case "goal_agent_transition":
|
|
2914
|
-
return renderStatusMessage(item.id,
|
|
2417
|
+
return renderStatusMessage(item.id, `${BLACK_CIRCLE} `, normalizeStatusText(item.text), theme.commandColor, { bold: true });
|
|
2915
2418
|
case "model_transition":
|
|
2916
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 });
|
|
2917
2420
|
case "theme_transition":
|
|
@@ -2993,9 +2496,13 @@ export function App(props) {
|
|
|
2993
2496
|
const detail = eventInfo?.kind === "worker"
|
|
2994
2497
|
? `Inspecting worker result${eventInfo.task ? ` for ${eventInfo.task}` : ""}.`
|
|
2995
2498
|
: `Inspecting verifier result${eventInfo?.status ? ` (${eventInfo.status})` : ""}.`;
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
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);
|
|
2999
2506
|
appendGoalProgress({
|
|
3000
2507
|
kind: "goal_progress",
|
|
3001
2508
|
phase: "orchestrator_reviewing",
|