@kenkaiiii/ggcoder 4.3.206 → 4.3.207
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/core/goal-controller.js +13 -13
- package/dist/core/goal-controller.js.map +1 -1
- package/dist/core/goal-controller.test.js +54 -12
- package/dist/core/goal-controller.test.js.map +1 -1
- package/dist/core/goal-worker-dev-server-lifecycle.test.d.ts +2 -0
- package/dist/core/goal-worker-dev-server-lifecycle.test.d.ts.map +1 -0
- package/dist/core/goal-worker-dev-server-lifecycle.test.js +68 -0
- package/dist/core/goal-worker-dev-server-lifecycle.test.js.map +1 -0
- package/dist/core/goal-worker.d.ts +7 -3
- package/dist/core/goal-worker.d.ts.map +1 -1
- package/dist/core/goal-worker.js +15 -5
- package/dist/core/goal-worker.js.map +1 -1
- package/dist/core/goal-worker.test.js +19 -1
- package/dist/core/goal-worker.test.js.map +1 -1
- package/dist/core/model-registry.test.js +51 -1
- package/dist/core/model-registry.test.js.map +1 -1
- package/dist/core/process-manager-dev-server-repro.test.d.ts +2 -0
- package/dist/core/process-manager-dev-server-repro.test.d.ts.map +1 -0
- package/dist/core/process-manager-dev-server-repro.test.js +100 -0
- package/dist/core/process-manager-dev-server-repro.test.js.map +1 -0
- package/dist/core/process-manager.js +2 -2
- package/dist/core/process-manager.js.map +1 -1
- package/dist/core/prompt-commands.d.ts.map +1 -1
- package/dist/core/prompt-commands.js +2 -1
- package/dist/core/prompt-commands.js.map +1 -1
- package/dist/core/prompt-commands.test.js +2 -0
- package/dist/core/prompt-commands.test.js.map +1 -1
- package/dist/system-prompt.d.ts.map +1 -1
- package/dist/system-prompt.js +1 -0
- package/dist/system-prompt.js.map +1 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +22 -12
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/edit.test.js +29 -6
- package/dist/tools/edit.test.js.map +1 -1
- package/dist/ui/App.d.ts +39 -3
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +197 -91
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/app-state-persistence.test.js +76 -2
- package/dist/ui/app-state-persistence.test.js.map +1 -1
- package/dist/ui/components/GoalOverlay.d.ts +54 -1
- package/dist/ui/components/GoalOverlay.d.ts.map +1 -1
- package/dist/ui/components/GoalOverlay.js +392 -53
- package/dist/ui/components/GoalOverlay.js.map +1 -1
- package/dist/ui/components/InputArea.d.ts.map +1 -1
- package/dist/ui/components/InputArea.js +38 -1
- package/dist/ui/components/InputArea.js.map +1 -1
- package/dist/ui/components/InputArea.test.d.ts +2 -0
- package/dist/ui/components/InputArea.test.d.ts.map +1 -0
- package/dist/ui/components/InputArea.test.js +79 -0
- package/dist/ui/components/InputArea.test.js.map +1 -0
- package/dist/ui/components/ToolExecution.d.ts.map +1 -1
- package/dist/ui/components/ToolExecution.js +2 -2
- package/dist/ui/components/ToolExecution.js.map +1 -1
- package/dist/ui/goal-events.d.ts +88 -1
- package/dist/ui/goal-events.d.ts.map +1 -1
- package/dist/ui/goal-events.js +249 -28
- package/dist/ui/goal-events.js.map +1 -1
- package/dist/ui/goal-events.test.js +89 -4
- package/dist/ui/goal-events.test.js.map +1 -1
- package/dist/ui/goal-overlay.test.js +155 -1
- package/dist/ui/goal-overlay.test.js.map +1 -1
- package/dist/ui/render.d.ts +3 -1
- package/dist/ui/render.d.ts.map +1 -1
- package/dist/ui/render.js +2 -0
- package/dist/ui/render.js.map +1 -1
- package/dist/ui/scroll-stabilization.test.js +49 -2
- package/dist/ui/scroll-stabilization.test.js.map +1 -1
- package/dist/ui/slash-command-images.test.d.ts +2 -0
- package/dist/ui/slash-command-images.test.d.ts.map +1 -0
- package/dist/ui/slash-command-images.test.js +47 -0
- package/dist/ui/slash-command-images.test.js.map +1 -0
- package/package.json +5 -5
package/dist/ui/App.js
CHANGED
|
@@ -96,6 +96,64 @@ function toErrorItem(err, id, contextPrefix) {
|
|
|
96
96
|
id,
|
|
97
97
|
};
|
|
98
98
|
}
|
|
99
|
+
export function routePromptCommandInput(input, promptCommands = PROMPT_COMMANDS, customCommands = []) {
|
|
100
|
+
const trimmed = input.trim();
|
|
101
|
+
if (!trimmed.startsWith("/"))
|
|
102
|
+
return null;
|
|
103
|
+
const parts = trimmed.slice(1).split(" ");
|
|
104
|
+
const cmdName = parts[0];
|
|
105
|
+
const cmdArgs = parts.slice(1).join(" ").trim();
|
|
106
|
+
const builtinCmd = promptCommands.find((c) => c.name === cmdName || c.aliases.includes(cmdName));
|
|
107
|
+
const customCmd = !builtinCmd ? customCommands.find((c) => c.name === cmdName) : undefined;
|
|
108
|
+
const promptText = builtinCmd?.prompt ?? customCmd?.prompt;
|
|
109
|
+
if (!promptText)
|
|
110
|
+
return null;
|
|
111
|
+
return {
|
|
112
|
+
cmdName,
|
|
113
|
+
cmdArgs,
|
|
114
|
+
promptText,
|
|
115
|
+
fullPrompt: cmdArgs ? `${promptText}\n\n## User Instructions\n\n${cmdArgs}` : promptText,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
export function buildUserContentWithAttachments(text, inputImages, modelSupportsImages) {
|
|
119
|
+
if (inputImages.length === 0)
|
|
120
|
+
return text;
|
|
121
|
+
const parts = [];
|
|
122
|
+
if (text) {
|
|
123
|
+
parts.push({ type: "text", text });
|
|
124
|
+
}
|
|
125
|
+
for (const img of inputImages) {
|
|
126
|
+
if (img.kind === "text") {
|
|
127
|
+
parts.push({
|
|
128
|
+
type: "text",
|
|
129
|
+
text: `<file name="${img.fileName}">\n${img.data}\n</file>`,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
else if (modelSupportsImages) {
|
|
133
|
+
parts.push({ type: "image", mediaType: img.mediaType, data: img.data });
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
// GLM models: save image to temp file and instruct model to use vision MCP tool
|
|
137
|
+
const ext = img.mediaType.split("/")[1] ?? "png";
|
|
138
|
+
const tmpPath = `/tmp/ggcoder-img-${Date.now()}.${ext}`;
|
|
139
|
+
try {
|
|
140
|
+
writeFileSync(tmpPath, Buffer.from(img.data, "base64"));
|
|
141
|
+
parts.push({
|
|
142
|
+
type: "text",
|
|
143
|
+
text: `[User attached an image saved at: ${tmpPath} — use the image_analysis tool to view and analyze it]`,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
parts.push({
|
|
148
|
+
type: "text",
|
|
149
|
+
text: `[User attached an image but it could not be saved for analysis]`,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// If only text parts remain after stripping images, simplify to plain string
|
|
155
|
+
return parts.length === 1 && parts[0].type === "text" ? parts[0].text : parts;
|
|
156
|
+
}
|
|
99
157
|
/** Tools that get aggregated into a single compact group when concurrent. */
|
|
100
158
|
const AGGREGATABLE_TOOLS = new Set(["read", "grep", "find", "ls"]);
|
|
101
159
|
const RUNNING_INDICATOR_ANIMATION_MS = 1_200;
|
|
@@ -135,6 +193,64 @@ function formatGoalWorkerFinishedTitle(taskTitle, status) {
|
|
|
135
193
|
? `Worker finished: ${taskTitle}. Reporting back.`
|
|
136
194
|
: `Worker failed: ${taskTitle}. Reporting back.`;
|
|
137
195
|
}
|
|
196
|
+
function countGoalTasksByStatus(tasks, status) {
|
|
197
|
+
return tasks.filter((task) => task.status === status).length;
|
|
198
|
+
}
|
|
199
|
+
function firstText(values) {
|
|
200
|
+
return values.find((value) => value !== undefined && value.trim().length > 0)?.trim();
|
|
201
|
+
}
|
|
202
|
+
function truncateGoalSummary(value, maxLength = 90) {
|
|
203
|
+
const normalized = value.replace(/\s+/g, " ").trim();
|
|
204
|
+
if (normalized.length <= maxLength)
|
|
205
|
+
return normalized;
|
|
206
|
+
return `${normalized.slice(0, maxLength - 1)}…`;
|
|
207
|
+
}
|
|
208
|
+
export function buildGoalSummaryRows(run) {
|
|
209
|
+
const rows = [];
|
|
210
|
+
const doneTasks = countGoalTasksByStatus(run.tasks, "done");
|
|
211
|
+
const failedTasks = countGoalTasksByStatus(run.tasks, "failed");
|
|
212
|
+
const blockedTasks = countGoalTasksByStatus(run.tasks, "blocked");
|
|
213
|
+
const taskSuffix = [
|
|
214
|
+
failedTasks > 0 ? `${failedTasks} failed` : undefined,
|
|
215
|
+
blockedTasks > 0 ? `${blockedTasks} blocked` : undefined,
|
|
216
|
+
].filter((item) => item !== undefined);
|
|
217
|
+
rows.push({
|
|
218
|
+
label: "Tasks",
|
|
219
|
+
value: run.tasks.length > 0 ? `${doneTasks}/${run.tasks.length} done` : "none",
|
|
220
|
+
...(taskSuffix.length > 0 ? { detail: taskSuffix.join(", ") } : {}),
|
|
221
|
+
});
|
|
222
|
+
const verifierResult = run.verifier?.lastResult;
|
|
223
|
+
const verifierDetail = firstText([verifierResult?.outputPath, run.verifier?.command]);
|
|
224
|
+
rows.push({
|
|
225
|
+
label: "Verifier",
|
|
226
|
+
value: verifierResult?.status ?? (run.verifier?.command ? "ready" : "missing"),
|
|
227
|
+
...(verifierDetail ? { detail: truncateGoalSummary(verifierDetail) } : {}),
|
|
228
|
+
});
|
|
229
|
+
const latestEvidence = run.evidence.at(-1);
|
|
230
|
+
rows.push({
|
|
231
|
+
label: "Evidence",
|
|
232
|
+
value: `${run.evidence.length} recorded`,
|
|
233
|
+
...(latestEvidence
|
|
234
|
+
? { detail: truncateGoalSummary(latestEvidence.path ?? latestEvidence.label) }
|
|
235
|
+
: {}),
|
|
236
|
+
});
|
|
237
|
+
if (run.status === "blocked" || run.status === "paused" || run.blockers.length > 0) {
|
|
238
|
+
rows.push({
|
|
239
|
+
label: run.status === "paused" ? "Paused on" : "Blocked on",
|
|
240
|
+
value: truncateGoalSummary(goalHasBlockingPrerequisites(run)
|
|
241
|
+
? formatGoalBlockingPrerequisites(run)
|
|
242
|
+
: (run.blockers[0] ?? "manual review"), 110),
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
else if (run.successCriteria.length > 0) {
|
|
246
|
+
rows.push({
|
|
247
|
+
label: "Criteria",
|
|
248
|
+
value: `${run.successCriteria.length} checked`,
|
|
249
|
+
detail: truncateGoalSummary(run.successCriteria[0] ?? "", 80),
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
return rows.slice(0, 4);
|
|
253
|
+
}
|
|
138
254
|
export function formatGoalTerminalProgress(run) {
|
|
139
255
|
switch (run.status) {
|
|
140
256
|
case "passed":
|
|
@@ -143,6 +259,7 @@ export function formatGoalTerminalProgress(run) {
|
|
|
143
259
|
phase: "terminal",
|
|
144
260
|
title: `Goal passed: ${run.title}`,
|
|
145
261
|
detail: "Verifier evidence is recorded; auto-continuation stopped.",
|
|
262
|
+
summaryRows: buildGoalSummaryRows(run),
|
|
146
263
|
status: run.status,
|
|
147
264
|
};
|
|
148
265
|
case "failed":
|
|
@@ -151,6 +268,7 @@ export function formatGoalTerminalProgress(run) {
|
|
|
151
268
|
phase: "terminal",
|
|
152
269
|
title: `Goal failed: ${run.title}`,
|
|
153
270
|
detail: "Auto-continuation stopped. Check Goal tasks for the failing step.",
|
|
271
|
+
summaryRows: buildGoalSummaryRows(run),
|
|
154
272
|
status: run.status,
|
|
155
273
|
};
|
|
156
274
|
case "blocked":
|
|
@@ -161,6 +279,7 @@ export function formatGoalTerminalProgress(run) {
|
|
|
161
279
|
detail: goalHasBlockingPrerequisites(run)
|
|
162
280
|
? formatGoalBlockingPrerequisites(run)
|
|
163
281
|
: (run.blockers[0] ?? "A prerequisite or missing verifier blocked progress."),
|
|
282
|
+
summaryRows: buildGoalSummaryRows(run),
|
|
164
283
|
status: run.status,
|
|
165
284
|
};
|
|
166
285
|
case "paused":
|
|
@@ -169,6 +288,7 @@ export function formatGoalTerminalProgress(run) {
|
|
|
169
288
|
phase: "terminal",
|
|
170
289
|
title: `Goal paused: ${run.title}`,
|
|
171
290
|
detail: run.blockers[0] ?? "Auto-continuation paused.",
|
|
291
|
+
summaryRows: buildGoalSummaryRows(run),
|
|
172
292
|
status: run.status,
|
|
173
293
|
};
|
|
174
294
|
case "draft":
|
|
@@ -185,12 +305,25 @@ export function shouldHideHistoryForOverlayView(isOverlayView, isAgentRunning) {
|
|
|
185
305
|
// agent is running we keep Static mounted because resetUI would abort the run.
|
|
186
306
|
return isOverlayView && !isAgentRunning;
|
|
187
307
|
}
|
|
188
|
-
export function
|
|
308
|
+
export function shouldStabilizeOverlayPaneRerender({ overlayPane, isAgentRunning, }) {
|
|
309
|
+
return isAgentRunning && (overlayPane === "goal" || overlayPane === "plan");
|
|
310
|
+
}
|
|
311
|
+
export function shouldHideStaticItemsForOverlayView({ shouldHideHistoryForOverlay, stabilizeOverlayPaneRerender, }) {
|
|
312
|
+
return shouldHideHistoryForOverlay && !stabilizeOverlayPaneRerender;
|
|
313
|
+
}
|
|
314
|
+
export function getScrollStabilizationDecision({ isUserScrolled, hasNewOutput, hasTallLiveUserMessage = false, }) {
|
|
315
|
+
const shouldStabilize = isUserScrolled || hasTallLiveUserMessage;
|
|
189
316
|
return {
|
|
190
|
-
preserveStatic:
|
|
191
|
-
autoFollow: !
|
|
317
|
+
preserveStatic: shouldStabilize && hasNewOutput,
|
|
318
|
+
autoFollow: !shouldStabilize,
|
|
192
319
|
};
|
|
193
320
|
}
|
|
321
|
+
export function isTallLiveUserMessage(text, rows) {
|
|
322
|
+
return text.split("\n").length > Math.max(8, Math.floor(rows * 0.6));
|
|
323
|
+
}
|
|
324
|
+
export function getStaticHistoryKey({ resizeKey, staticKey, }) {
|
|
325
|
+
return `${resizeKey}-${staticKey}`;
|
|
326
|
+
}
|
|
194
327
|
// flushOnTurnText, flushOnTurnEnd are imported from ./live-item-flush.ts
|
|
195
328
|
/** Check whether an item is still active (running spinner, pending result). */
|
|
196
329
|
function isActiveItem(item) {
|
|
@@ -415,7 +548,7 @@ export function App(props) {
|
|
|
415
548
|
const cwdRef = useRef(props.cwd);
|
|
416
549
|
const [displayedCwd, setDisplayedCwd] = useState(props.cwd);
|
|
417
550
|
const [staticKey, setStaticKey] = useState(0);
|
|
418
|
-
const [doneStatus, setDoneStatus] = useState(null);
|
|
551
|
+
const [doneStatus, setDoneStatus] = useState(props.sessionStore?.doneStatus ?? null);
|
|
419
552
|
// Suppress "done" status when a plan overlay is about to open
|
|
420
553
|
const planOverlayPendingRef = useRef(false);
|
|
421
554
|
const [gitBranch, setGitBranch] = useState(null);
|
|
@@ -552,6 +685,10 @@ export function App(props) {
|
|
|
552
685
|
if (sessionStore)
|
|
553
686
|
sessionStore.liveItems = liveItems;
|
|
554
687
|
}, [liveItems, sessionStore]);
|
|
688
|
+
useEffect(() => {
|
|
689
|
+
if (sessionStore)
|
|
690
|
+
sessionStore.doneStatus = doneStatus;
|
|
691
|
+
}, [doneStatus, sessionStore]);
|
|
555
692
|
useEffect(() => {
|
|
556
693
|
if (sessionStore)
|
|
557
694
|
sessionStore.planSteps = planSteps;
|
|
@@ -2086,49 +2223,49 @@ export function App(props) {
|
|
|
2086
2223
|
return;
|
|
2087
2224
|
}
|
|
2088
2225
|
// Handle prompt-template commands (built-in + custom from .gg/commands/)
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
const cmdName =
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
log("INFO", "command", `Prompt command: /${cmdName}${cmdArgs ? ` (args: ${cmdArgs})` : ""}`);
|
|
2098
|
-
// Move live items into history before starting
|
|
2099
|
-
setLiveItems((prev) => {
|
|
2100
|
-
if (prev.length > 0) {
|
|
2101
|
-
pendingFlushRef.current = [...pendingFlushRef.current, ...prev];
|
|
2102
|
-
}
|
|
2103
|
-
return [];
|
|
2104
|
-
});
|
|
2105
|
-
// Show the command name as the user message
|
|
2106
|
-
const userItem = { kind: "user", text: trimmed, id: getId() };
|
|
2107
|
-
setLastUserMessage(trimmed);
|
|
2108
|
-
setDoneStatus(null);
|
|
2109
|
-
setLiveItems([userItem]);
|
|
2110
|
-
// Send the full prompt to the agent, with user args appended if provided
|
|
2111
|
-
const fullPrompt = cmdArgs
|
|
2112
|
-
? `${promptText}\n\n## User Instructions\n\n${cmdArgs}`
|
|
2113
|
-
: promptText;
|
|
2114
|
-
try {
|
|
2115
|
-
await agentLoop.run(fullPrompt);
|
|
2116
|
-
}
|
|
2117
|
-
catch (err) {
|
|
2118
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
2119
|
-
log("ERROR", "error", msg);
|
|
2120
|
-
const isAbort = msg.includes("aborted") || msg.includes("abort");
|
|
2121
|
-
setLiveItems((prev) => [
|
|
2122
|
-
...prev,
|
|
2123
|
-
isAbort
|
|
2124
|
-
? { kind: "stopped", text: "Request was stopped.", id: getId() }
|
|
2125
|
-
: toErrorItem(err, getId()),
|
|
2126
|
-
]);
|
|
2226
|
+
const promptCommandRoute = routePromptCommandInput(trimmed, PROMPT_COMMANDS, customCommands);
|
|
2227
|
+
if (promptCommandRoute) {
|
|
2228
|
+
const { cmdName, cmdArgs, fullPrompt } = promptCommandRoute;
|
|
2229
|
+
log("INFO", "command", `Prompt command: /${cmdName}${cmdArgs ? ` (args: ${cmdArgs})` : ""}`);
|
|
2230
|
+
// Move live items into history before starting
|
|
2231
|
+
setLiveItems((prev) => {
|
|
2232
|
+
if (prev.length > 0) {
|
|
2233
|
+
pendingFlushRef.current = [...pendingFlushRef.current, ...prev];
|
|
2127
2234
|
}
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2235
|
+
return [];
|
|
2236
|
+
});
|
|
2237
|
+
const hasImages = inputImages.length > 0;
|
|
2238
|
+
const modelInfo = getModel(currentModel);
|
|
2239
|
+
const modelSupportsImages = modelInfo?.supportsImages ?? true;
|
|
2240
|
+
const userContent = buildUserContentWithAttachments(fullPrompt, inputImages, modelSupportsImages);
|
|
2241
|
+
// Show the typed command as the user message
|
|
2242
|
+
const userItem = {
|
|
2243
|
+
kind: "user",
|
|
2244
|
+
text: trimmed,
|
|
2245
|
+
imageCount: hasImages ? inputImages.length : undefined,
|
|
2246
|
+
id: getId(),
|
|
2247
|
+
};
|
|
2248
|
+
setLastUserMessage(trimmed);
|
|
2249
|
+
setDoneStatus(null);
|
|
2250
|
+
setLiveItems([userItem]);
|
|
2251
|
+
// Send the full prompt to the agent, with user args appended if provided
|
|
2252
|
+
try {
|
|
2253
|
+
await agentLoop.run(userContent);
|
|
2131
2254
|
}
|
|
2255
|
+
catch (err) {
|
|
2256
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2257
|
+
log("ERROR", "error", msg);
|
|
2258
|
+
const isAbort = msg.includes("aborted") || msg.includes("abort");
|
|
2259
|
+
setLiveItems((prev) => [
|
|
2260
|
+
...prev,
|
|
2261
|
+
isAbort
|
|
2262
|
+
? { kind: "stopped", text: "Request was stopped.", id: getId() }
|
|
2263
|
+
: toErrorItem(err, getId()),
|
|
2264
|
+
]);
|
|
2265
|
+
}
|
|
2266
|
+
// Reload custom commands in case a setup command created new ones
|
|
2267
|
+
reloadCustomCommands();
|
|
2268
|
+
return;
|
|
2132
2269
|
}
|
|
2133
2270
|
// Check slash commands
|
|
2134
2271
|
if (props.onSlashCommand && input.startsWith("/")) {
|
|
@@ -2142,47 +2279,7 @@ export function App(props) {
|
|
|
2142
2279
|
const hasImages = inputImages.length > 0;
|
|
2143
2280
|
const modelInfo = getModel(currentModel);
|
|
2144
2281
|
const modelSupportsImages = modelInfo?.supportsImages ?? true;
|
|
2145
|
-
|
|
2146
|
-
if (hasImages) {
|
|
2147
|
-
const parts = [];
|
|
2148
|
-
if (trimmed) {
|
|
2149
|
-
parts.push({ type: "text", text: trimmed });
|
|
2150
|
-
}
|
|
2151
|
-
for (const img of inputImages) {
|
|
2152
|
-
if (img.kind === "text") {
|
|
2153
|
-
parts.push({
|
|
2154
|
-
type: "text",
|
|
2155
|
-
text: `<file name="${img.fileName}">\n${img.data}\n</file>`,
|
|
2156
|
-
});
|
|
2157
|
-
}
|
|
2158
|
-
else if (modelSupportsImages) {
|
|
2159
|
-
parts.push({ type: "image", mediaType: img.mediaType, data: img.data });
|
|
2160
|
-
}
|
|
2161
|
-
else {
|
|
2162
|
-
// GLM models: save image to temp file and instruct model to use vision MCP tool
|
|
2163
|
-
const ext = img.mediaType.split("/")[1] ?? "png";
|
|
2164
|
-
const tmpPath = `/tmp/ggcoder-img-${Date.now()}.${ext}`;
|
|
2165
|
-
try {
|
|
2166
|
-
writeFileSync(tmpPath, Buffer.from(img.data, "base64"));
|
|
2167
|
-
parts.push({
|
|
2168
|
-
type: "text",
|
|
2169
|
-
text: `[User attached an image saved at: ${tmpPath} — use the image_analysis tool to view and analyze it]`,
|
|
2170
|
-
});
|
|
2171
|
-
}
|
|
2172
|
-
catch {
|
|
2173
|
-
parts.push({
|
|
2174
|
-
type: "text",
|
|
2175
|
-
text: `[User attached an image but it could not be saved for analysis]`,
|
|
2176
|
-
});
|
|
2177
|
-
}
|
|
2178
|
-
}
|
|
2179
|
-
}
|
|
2180
|
-
// If only text parts remain after stripping images, simplify to plain string
|
|
2181
|
-
userContent = parts.length === 1 && parts[0].type === "text" ? parts[0].text : parts;
|
|
2182
|
-
}
|
|
2183
|
-
else {
|
|
2184
|
-
userContent = input;
|
|
2185
|
-
}
|
|
2282
|
+
const userContent = buildUserContentWithAttachments(input, inputImages, modelSupportsImages);
|
|
2186
2283
|
// ── Queue message if agent is already running ──
|
|
2187
2284
|
if (agentLoop.isRunning) {
|
|
2188
2285
|
log("INFO", "queue", `Queued message: ${trimmed.length > 80 ? trimmed.slice(0, 80) + "..." : trimmed}`);
|
|
@@ -2464,7 +2561,7 @@ export function App(props) {
|
|
|
2464
2561
|
? "◆ "
|
|
2465
2562
|
: "! "
|
|
2466
2563
|
: "↻ ";
|
|
2467
|
-
return (_jsxs(Box, { marginTop: 1, flexDirection: "column", flexShrink: 1, children: [_jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: color, bold: true, children: glyph }), _jsx(Text, { color: color, bold: true, children: item.title }), item.workerId ? _jsxs(Text, { color: theme.textDim, children: [" \u00B7 worker ", item.workerId] }) : null] }), item.detail ? (_jsx(Text, { color: theme.textDim, wrap: "wrap", children: ` ${item.detail}` })) : null] }, item.id));
|
|
2564
|
+
return (_jsxs(Box, { marginTop: 1, flexDirection: "column", flexShrink: 1, children: [_jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: color, bold: true, children: glyph }), _jsx(Text, { color: color, bold: true, children: item.title }), item.workerId ? _jsxs(Text, { color: theme.textDim, children: [" \u00B7 worker ", item.workerId] }) : null] }), item.detail ? (_jsx(Text, { color: theme.textDim, wrap: "wrap", children: ` ${item.detail}` })) : null, item.summaryRows && item.summaryRows.length > 0 ? (_jsx(Box, { flexDirection: "column", marginTop: 1, marginLeft: 2, flexShrink: 1, children: item.summaryRows.map((row) => (_jsxs(Text, { wrap: "truncate", children: [_jsx(Text, { color: theme.textDim, children: row.label.padEnd(10) }), _jsx(Text, { color: theme.text, children: row.value }), row.detail ? _jsxs(Text, { color: theme.textDim, children: [" \u00B7 ", row.detail] }) : null] }, row.label))) })) : null] }, item.id));
|
|
2468
2565
|
}
|
|
2469
2566
|
case "style_pack": {
|
|
2470
2567
|
const names = item.added.map((id) => LANGUAGE_DISPLAY_NAMES[id]);
|
|
@@ -2633,8 +2730,9 @@ export function App(props) {
|
|
|
2633
2730
|
props.sessionStore.overlay = kind;
|
|
2634
2731
|
if (kind !== "plan")
|
|
2635
2732
|
props.sessionStore.planAutoExpand = false;
|
|
2636
|
-
if (agentLoop.isRunning)
|
|
2733
|
+
if (agentLoop.isRunning && kind !== "goal" && kind !== "plan") {
|
|
2637
2734
|
props.sessionStore.pendingResetUI = true;
|
|
2735
|
+
}
|
|
2638
2736
|
}
|
|
2639
2737
|
if (kind !== "plan")
|
|
2640
2738
|
setPlanAutoExpand(false);
|
|
@@ -2649,8 +2747,6 @@ export function App(props) {
|
|
|
2649
2747
|
else {
|
|
2650
2748
|
if (props.sessionStore) {
|
|
2651
2749
|
props.sessionStore.overlay = null;
|
|
2652
|
-
if (agentLoop.isRunning)
|
|
2653
|
-
props.sessionStore.pendingResetUI = true;
|
|
2654
2750
|
}
|
|
2655
2751
|
setOverlay(null);
|
|
2656
2752
|
}
|
|
@@ -2954,6 +3050,7 @@ export function App(props) {
|
|
|
2954
3050
|
model: currentModel,
|
|
2955
3051
|
goalRunId: run.id,
|
|
2956
3052
|
goalTaskId: decision.task.id,
|
|
3053
|
+
taskTitle: decision.task.title,
|
|
2957
3054
|
prompt: decision.task.prompt,
|
|
2958
3055
|
});
|
|
2959
3056
|
await upsertGoalRun(props.cwd, {
|
|
@@ -3330,7 +3427,16 @@ export function App(props) {
|
|
|
3330
3427
|
const isPixelView = overlay === "pixel";
|
|
3331
3428
|
const isOverlayView = isTaskView || isGoalView || isSkillsView || isPlanView || isEyesView || isPixelView;
|
|
3332
3429
|
const shouldHideHistoryForOverlay = shouldHideHistoryForOverlayView(isOverlayView, agentLoop.isRunning);
|
|
3333
|
-
|
|
3430
|
+
const stabilizeOverlayPaneRerender = shouldStabilizeOverlayPaneRerender({
|
|
3431
|
+
overlayPane: overlay,
|
|
3432
|
+
isAgentRunning: agentLoop.isRunning,
|
|
3433
|
+
});
|
|
3434
|
+
return (_jsxs(Box, { flexDirection: "column", width: columns, children: [_jsx(Static, { items: shouldHideStaticItemsForOverlayView({
|
|
3435
|
+
shouldHideHistoryForOverlay,
|
|
3436
|
+
stabilizeOverlayPaneRerender,
|
|
3437
|
+
})
|
|
3438
|
+
? []
|
|
3439
|
+
: history, style: { width: "100%" }, children: (item) => (_jsx(Box, { flexDirection: "column", paddingRight: 1, children: renderItem(item) }, item.id)) }, getStaticHistoryKey({ resizeKey, staticKey })), isTaskView ? (_jsx(TaskOverlay, { cwd: props.cwd, agentRunning: agentLoop.isRunning, onClose: () => {
|
|
3334
3440
|
if (props.resetUI && props.sessionStore && !agentLoop.isRunning) {
|
|
3335
3441
|
props.sessionStore.overlay = null;
|
|
3336
3442
|
props.resetUI();
|