@kenkaiiii/ggcoder 4.3.208 → 4.3.210
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/compaction/compactor.d.ts +4 -0
- package/dist/core/compaction/compactor.d.ts.map +1 -1
- package/dist/core/compaction/compactor.js +34 -3
- package/dist/core/compaction/compactor.js.map +1 -1
- package/dist/core/compaction/compactor.test.js +20 -1
- package/dist/core/compaction/compactor.test.js.map +1 -1
- package/dist/core/goal-controller.js +9 -9
- package/dist/core/goal-controller.js.map +1 -1
- package/dist/core/goal-controller.test.js +76 -12
- package/dist/core/goal-controller.test.js.map +1 -1
- package/dist/core/goal-lifecycle-smoke.test.js +2 -3
- package/dist/core/goal-lifecycle-smoke.test.js.map +1 -1
- package/dist/core/goal-store.d.ts +1 -1
- package/dist/core/goal-store.d.ts.map +1 -1
- package/dist/core/goal-store.js +38 -2
- package/dist/core/goal-store.js.map +1 -1
- package/dist/core/goal-store.test.js +32 -0
- package/dist/core/goal-store.test.js.map +1 -1
- package/dist/core/process-manager-dev-server-repro.test.js +37 -1
- package/dist/core/process-manager-dev-server-repro.test.js.map +1 -1
- package/dist/core/process-manager.d.ts +9 -0
- package/dist/core/process-manager.d.ts.map +1 -1
- package/dist/core/process-manager.js +20 -6
- 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 +41 -21
- package/dist/core/prompt-commands.js.map +1 -1
- package/dist/core/prompt-commands.test.js +87 -21
- package/dist/core/prompt-commands.test.js.map +1 -1
- package/dist/system-prompt.js +1 -1
- package/dist/system-prompt.js.map +1 -1
- package/dist/system-prompt.test.js +58 -0
- package/dist/system-prompt.test.js.map +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +2 -51
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/goals.d.ts +1 -0
- package/dist/tools/goals.d.ts.map +1 -1
- package/dist/tools/goals.js +10 -2
- package/dist/tools/goals.js.map +1 -1
- package/dist/tools/goals.test.js +85 -0
- package/dist/tools/goals.test.js.map +1 -1
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js +22 -5
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/grep.test.d.ts +2 -0
- package/dist/tools/grep.test.d.ts.map +1 -0
- package/dist/tools/grep.test.js +20 -0
- package/dist/tools/grep.test.js.map +1 -0
- package/dist/tools/safe-env.d.ts +2 -0
- package/dist/tools/safe-env.d.ts.map +1 -0
- package/dist/tools/safe-env.js +52 -0
- package/dist/tools/safe-env.js.map +1 -0
- package/dist/tools/web-fetch.d.ts +5 -0
- package/dist/tools/web-fetch.d.ts.map +1 -1
- package/dist/tools/web-fetch.js +12 -1
- package/dist/tools/web-fetch.js.map +1 -1
- package/dist/tools/web-fetch.test.js +9 -0
- package/dist/tools/web-fetch.test.js.map +1 -1
- package/dist/ui/App.d.ts +3 -0
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +171 -164
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/app-state-persistence.test.js +56 -0
- package/dist/ui/app-state-persistence.test.js.map +1 -1
- package/dist/ui/components/GoalStatusBar.d.ts.map +1 -1
- package/dist/ui/components/GoalStatusBar.js +2 -1
- package/dist/ui/components/GoalStatusBar.js.map +1 -1
- package/dist/ui/goal-events.test.js +19 -0
- package/dist/ui/goal-events.test.js.map +1 -1
- package/dist/ui/goal-lifecycle-orchestration.test.d.ts +2 -0
- package/dist/ui/goal-lifecycle-orchestration.test.d.ts.map +1 -0
- package/dist/ui/goal-lifecycle-orchestration.test.js +445 -0
- package/dist/ui/goal-lifecycle-orchestration.test.js.map +1 -0
- package/dist/ui/goal-overlay.test.js +30 -0
- package/dist/ui/goal-overlay.test.js.map +1 -1
- package/dist/ui/goal-status-bar.test.js +29 -0
- package/dist/ui/goal-status-bar.test.js.map +1 -1
- package/dist/ui/queued-message.test.d.ts +2 -0
- package/dist/ui/queued-message.test.d.ts.map +1 -0
- package/dist/ui/queued-message.test.js +30 -0
- package/dist/ui/queued-message.test.js.map +1 -0
- package/dist/ui/slash-command-images.test.js +10 -0
- package/dist/ui/slash-command-images.test.js.map +1 -1
- package/package.json +6 -5
package/dist/ui/App.js
CHANGED
|
@@ -59,8 +59,9 @@ import { getLatestUserText, injectRepoMapContextMessages, stripRepoMapContextMes
|
|
|
59
59
|
import { extractPlanSteps, findCompletedMarkers, markStepsCompleted, segmentDisplayText, stripDoneMarkers, } from "../utils/plan-steps.js";
|
|
60
60
|
import { getMCPServers } from "../core/mcp/index.js";
|
|
61
61
|
import { trimFlushedItems, flushOnTurnText, flushOnTurnEnd, flushOverflow, } from "./live-item-flush.js";
|
|
62
|
-
import { appendGoalDecision, appendGoalEvidence, formatGoalBlockingPrerequisites, goalHasBlockingPrerequisites, loadGoalRuns, reconcileActiveGoalRuns,
|
|
63
|
-
import { canCompleteGoalRun, decideGoalNextAction
|
|
62
|
+
import { appendGoalDecision, appendGoalEvidence, formatGoalBlockingPrerequisites, goalHasBlockingPrerequisites, loadGoalRuns, reconcileActiveGoalRuns, summarizeGoalCounts, summarizeGoalCountsFromRuns, updateGoalTask, upsertGoalRun, } from "../core/goal-store.js";
|
|
63
|
+
import { canCompleteGoalRun, decideGoalNextAction } from "../core/goal-controller.js";
|
|
64
|
+
import { runGoalVerifierCommand } from "../core/goal-verifier.js";
|
|
64
65
|
import { listGoalWorkers, startGoalWorker, stopGoalWorker, subscribeGoalWorkerCompletions, } from "../core/goal-worker.js";
|
|
65
66
|
import { formatGoalVerifierCompletionEvent, formatGoalWorkerCompletionEvent, isGoalSyntheticEvent, parseGoalSyntheticEvent, } from "./goal-events.js";
|
|
66
67
|
/** Where ggcoder bugs should be reported. Surfaced in the guidance line. */
|
|
@@ -189,9 +190,7 @@ function summarizeGoalCompletion(summary) {
|
|
|
189
190
|
return statusLine ?? changedLine ?? verificationLine ?? lines[0];
|
|
190
191
|
}
|
|
191
192
|
function formatGoalWorkerFinishedTitle(taskTitle, status) {
|
|
192
|
-
return status === "done"
|
|
193
|
-
? `Worker finished: ${taskTitle}. Reporting back.`
|
|
194
|
-
: `Worker failed: ${taskTitle}. Reporting back.`;
|
|
193
|
+
return status === "done" ? `Done: ${taskTitle}` : `Failed: ${taskTitle}`;
|
|
195
194
|
}
|
|
196
195
|
function countGoalTasksByStatus(tasks, status) {
|
|
197
196
|
return tasks.filter((task) => task.status === status).length;
|
|
@@ -251,6 +250,53 @@ export function buildGoalSummaryRows(run) {
|
|
|
251
250
|
}
|
|
252
251
|
return rows.slice(0, 4);
|
|
253
252
|
}
|
|
253
|
+
function goalTerminalProgressId(run) {
|
|
254
|
+
return `goal-terminal-${run.id}`;
|
|
255
|
+
}
|
|
256
|
+
function goalTerminalRunIdFromItem(item) {
|
|
257
|
+
if (item.kind !== "goal_progress" || item.phase !== "terminal")
|
|
258
|
+
return undefined;
|
|
259
|
+
if (!item.id.startsWith("goal-terminal-"))
|
|
260
|
+
return undefined;
|
|
261
|
+
return item.id.slice("goal-terminal-".length);
|
|
262
|
+
}
|
|
263
|
+
function goalProgressMatchesDraft(item, draft) {
|
|
264
|
+
return (item.title === draft.title &&
|
|
265
|
+
item.detail === draft.detail &&
|
|
266
|
+
item.status === draft.status &&
|
|
267
|
+
JSON.stringify(item.summaryRows ?? []) === JSON.stringify(draft.summaryRows ?? []));
|
|
268
|
+
}
|
|
269
|
+
export function completedItemsWithDurableGoalTerminalProgress(items, runs) {
|
|
270
|
+
const runIds = new Set(runs.map((run) => run.id));
|
|
271
|
+
const terminalByRun = new Map(runs
|
|
272
|
+
.map((run) => [run.id, formatGoalTerminalProgress(run)])
|
|
273
|
+
.filter((entry) => entry[1] !== null));
|
|
274
|
+
if (runIds.size === 0)
|
|
275
|
+
return items;
|
|
276
|
+
const upToDateRunIds = new Set();
|
|
277
|
+
let changed = false;
|
|
278
|
+
const reconciled = items.map((item, index) => {
|
|
279
|
+
const runId = goalTerminalRunIdFromItem(item);
|
|
280
|
+
if (!runId || !runIds.has(runId))
|
|
281
|
+
return item;
|
|
282
|
+
const draft = terminalByRun.get(runId);
|
|
283
|
+
if (draft && goalProgressMatchesDraft(item, draft)) {
|
|
284
|
+
upToDateRunIds.add(runId);
|
|
285
|
+
return item;
|
|
286
|
+
}
|
|
287
|
+
changed = true;
|
|
288
|
+
return { kind: "tombstone", id: `tombstone-${item.id}-${index}` };
|
|
289
|
+
});
|
|
290
|
+
const additions = [];
|
|
291
|
+
for (const [runId, progress] of terminalByRun) {
|
|
292
|
+
if (upToDateRunIds.has(runId))
|
|
293
|
+
continue;
|
|
294
|
+
additions.push({ ...progress, id: goalTerminalProgressId({ id: runId }) });
|
|
295
|
+
}
|
|
296
|
+
if (!changed && additions.length === 0)
|
|
297
|
+
return items;
|
|
298
|
+
return [...reconciled, ...additions];
|
|
299
|
+
}
|
|
254
300
|
export function formatGoalTerminalProgress(run) {
|
|
255
301
|
switch (run.status) {
|
|
256
302
|
case "passed":
|
|
@@ -325,10 +371,11 @@ export function getStaticHistoryKey({ resizeKey }) {
|
|
|
325
371
|
}
|
|
326
372
|
// flushOnTurnText, flushOnTurnEnd are imported from ./live-item-flush.ts
|
|
327
373
|
/** Check whether an item is still active (running spinner, pending result). */
|
|
328
|
-
function isActiveItem(item) {
|
|
374
|
+
export function isActiveItem(item) {
|
|
329
375
|
switch (item.kind) {
|
|
330
376
|
case "tool_start":
|
|
331
377
|
case "server_tool_start":
|
|
378
|
+
case "queued":
|
|
332
379
|
case "compacting":
|
|
333
380
|
return true;
|
|
334
381
|
case "tool_group":
|
|
@@ -727,6 +774,7 @@ export function App(props) {
|
|
|
727
774
|
if (cancelled)
|
|
728
775
|
return;
|
|
729
776
|
setGoalCount(counts.active);
|
|
777
|
+
setHistory((prev) => completedItemsWithDurableGoalTerminalProgress(prev, runs));
|
|
730
778
|
setGoalStatusEntries((prev) => {
|
|
731
779
|
const next = reconcileGoalStatusEntriesWithRuns(prev, runs, {
|
|
732
780
|
isWorkerActive: (workerId, run) => listGoalWorkers(props.cwd).some((worker) => worker.id === workerId &&
|
|
@@ -2530,11 +2578,23 @@ export function App(props) {
|
|
|
2530
2578
|
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: item.title }), item.workerId ? _jsxs(Text, { color: theme.textDim, children: [" \u00B7 worker ", item.workerId] }) : null] }) }, item.id));
|
|
2531
2579
|
case "goal_progress": {
|
|
2532
2580
|
const isError = item.status === "failed" || item.status === "fail" || item.status === "blocked";
|
|
2533
|
-
const color =
|
|
2534
|
-
? theme.
|
|
2535
|
-
:
|
|
2536
|
-
? theme.
|
|
2537
|
-
:
|
|
2581
|
+
const color = isError
|
|
2582
|
+
? theme.error
|
|
2583
|
+
: item.phase === "worker_finished"
|
|
2584
|
+
? theme.success
|
|
2585
|
+
: item.phase === "verifier_finished"
|
|
2586
|
+
? theme.accent
|
|
2587
|
+
: item.phase === "orchestrator_reviewing" || item.phase === "orchestrator_working"
|
|
2588
|
+
? theme.secondary
|
|
2589
|
+
: item.phase === "continuing"
|
|
2590
|
+
? theme.warning
|
|
2591
|
+
: item.phase === "verifier_started"
|
|
2592
|
+
? theme.accent
|
|
2593
|
+
: item.phase === "worker_started"
|
|
2594
|
+
? theme.primary
|
|
2595
|
+
: item.phase === "terminal"
|
|
2596
|
+
? theme.success
|
|
2597
|
+
: theme.primary;
|
|
2538
2598
|
const glyph = item.phase === "worker_finished" || item.phase === "verifier_finished"
|
|
2539
2599
|
? "✓ "
|
|
2540
2600
|
: item.phase === "terminal"
|
|
@@ -2604,7 +2664,7 @@ export function App(props) {
|
|
|
2604
2664
|
case "step_done":
|
|
2605
2665
|
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));
|
|
2606
2666
|
case "queued":
|
|
2607
|
-
return (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: theme.
|
|
2667
|
+
return (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: theme.warning, bold: true, children: "• " }), _jsx(Text, { color: theme.textDim, children: "Queued: " }), _jsxs(Text, { color: theme.text, wrap: "wrap", children: [item.text, item.imageCount
|
|
2608
2668
|
? ` (+${item.imageCount} image${item.imageCount > 1 ? "s" : ""})`
|
|
2609
2669
|
: ""] })] }, item.id));
|
|
2610
2670
|
case "compacting":
|
|
@@ -2791,8 +2851,10 @@ export function App(props) {
|
|
|
2791
2851
|
content: `terminal=${status}`,
|
|
2792
2852
|
});
|
|
2793
2853
|
const terminalProgress = formatGoalTerminalProgress(nextRun);
|
|
2794
|
-
if (terminalProgress)
|
|
2795
|
-
|
|
2854
|
+
if (terminalProgress) {
|
|
2855
|
+
const item = { ...terminalProgress, id: goalTerminalProgressId(nextRun) };
|
|
2856
|
+
setLiveItems((prev) => completedItemsWithDurableGoalTerminalProgress([...prev, item], [nextRun]));
|
|
2857
|
+
}
|
|
2796
2858
|
runningGoalIdsRef.current.delete(runId);
|
|
2797
2859
|
clearGoalStatusEntry(runId);
|
|
2798
2860
|
return;
|
|
@@ -2813,8 +2875,8 @@ export function App(props) {
|
|
|
2813
2875
|
appendGoalProgress({
|
|
2814
2876
|
kind: "goal_progress",
|
|
2815
2877
|
phase: "continuing",
|
|
2816
|
-
title: `
|
|
2817
|
-
detail: "
|
|
2878
|
+
title: `Choosing next Goal step: ${latestRun.title}`,
|
|
2879
|
+
detail: "Latest result is recorded; starting the next worker task or verifier automatically.",
|
|
2818
2880
|
status: latestRun.status,
|
|
2819
2881
|
});
|
|
2820
2882
|
upsertGoalStatusEntry({
|
|
@@ -2915,8 +2977,10 @@ export function App(props) {
|
|
|
2915
2977
|
await appendGoalDecision(props.cwd, run.id, decision);
|
|
2916
2978
|
if (decision.kind === "terminal") {
|
|
2917
2979
|
const terminalProgress = formatGoalTerminalProgress(run);
|
|
2918
|
-
if (terminalProgress)
|
|
2919
|
-
|
|
2980
|
+
if (terminalProgress) {
|
|
2981
|
+
const item = { ...terminalProgress, id: goalTerminalProgressId(run) };
|
|
2982
|
+
setLiveItems((prev) => completedItemsWithDurableGoalTerminalProgress([...prev, item], [run]));
|
|
2983
|
+
}
|
|
2920
2984
|
runningGoalIdsRef.current.delete(run.id);
|
|
2921
2985
|
clearGoalStatusEntry(run.id);
|
|
2922
2986
|
return;
|
|
@@ -2988,20 +3052,21 @@ export function App(props) {
|
|
|
2988
3052
|
return;
|
|
2989
3053
|
}
|
|
2990
3054
|
if (decision.kind === "pause") {
|
|
2991
|
-
await updateGoalTask(props.cwd, run.id, decision.task.id, {
|
|
3055
|
+
const runWithBlockedTask = (await updateGoalTask(props.cwd, run.id, decision.task.id, {
|
|
2992
3056
|
status: "blocked",
|
|
2993
3057
|
attempts: decision.attempts,
|
|
2994
3058
|
lastSummary: "Paused after worker attempt limit.",
|
|
2995
|
-
});
|
|
2996
|
-
await
|
|
2997
|
-
...run,
|
|
2998
|
-
status: "paused",
|
|
2999
|
-
blockers: [...run.blockers, decision.reason],
|
|
3000
|
-
});
|
|
3001
|
-
await appendGoalEvidence(props.cwd, run.id, {
|
|
3059
|
+
})) ?? run;
|
|
3060
|
+
const runWithPauseEvidence = (await appendGoalEvidence(props.cwd, run.id, {
|
|
3002
3061
|
kind: "summary",
|
|
3003
3062
|
label: "Goal paused",
|
|
3004
3063
|
content: decision.reason,
|
|
3064
|
+
})) ?? runWithBlockedTask;
|
|
3065
|
+
await upsertGoalRun(props.cwd, {
|
|
3066
|
+
...runWithPauseEvidence,
|
|
3067
|
+
status: "paused",
|
|
3068
|
+
continueRequestedAt: undefined,
|
|
3069
|
+
blockers: Array.from(new Set([...runWithPauseEvidence.blockers, decision.reason])),
|
|
3005
3070
|
});
|
|
3006
3071
|
setGoalCount((await summarizeGoalCounts(props.cwd)).active);
|
|
3007
3072
|
appendGoalProgress({
|
|
@@ -3015,7 +3080,9 @@ export function App(props) {
|
|
|
3015
3080
|
clearGoalStatusEntry(run.id);
|
|
3016
3081
|
return;
|
|
3017
3082
|
}
|
|
3018
|
-
await updateGoalTask(props.cwd, run.id, decision.task.id, {
|
|
3083
|
+
const runWithAttempt = (await updateGoalTask(props.cwd, run.id, decision.task.id, {
|
|
3084
|
+
attempts: decision.attempts,
|
|
3085
|
+
})) ?? run;
|
|
3019
3086
|
const worker = await startGoalWorker({
|
|
3020
3087
|
cwd: props.cwd,
|
|
3021
3088
|
provider: currentProvider,
|
|
@@ -3025,11 +3092,15 @@ export function App(props) {
|
|
|
3025
3092
|
taskTitle: decision.task.title,
|
|
3026
3093
|
prompt: decision.task.prompt,
|
|
3027
3094
|
});
|
|
3095
|
+
const latestRun = (await loadGoalRuns(props.cwd)).find((item) => item.id === run.id) ?? runWithAttempt;
|
|
3028
3096
|
await upsertGoalRun(props.cwd, {
|
|
3029
|
-
...
|
|
3097
|
+
...latestRun,
|
|
3030
3098
|
status: "running",
|
|
3031
3099
|
activeWorkerId: worker.id,
|
|
3032
3100
|
continueRequestedAt: undefined,
|
|
3101
|
+
tasks: latestRun.tasks.map((item) => item.id === decision.task.id
|
|
3102
|
+
? { ...item, status: "running", workerId: worker.id, attempts: decision.attempts }
|
|
3103
|
+
: item),
|
|
3033
3104
|
});
|
|
3034
3105
|
setOverlay(null);
|
|
3035
3106
|
setGoalCount((await summarizeGoalCounts(props.cwd)).active);
|
|
@@ -3110,143 +3181,74 @@ export function App(props) {
|
|
|
3110
3181
|
detail: run.verifier.command,
|
|
3111
3182
|
goalNumber: goalNumberForRun(run.id),
|
|
3112
3183
|
});
|
|
3113
|
-
|
|
3114
|
-
const { mkdir, writeFile } = await import("node:fs/promises");
|
|
3115
|
-
const { join } = await import("node:path");
|
|
3116
|
-
const logDir = join(projectDir(props.cwd), "verifiers");
|
|
3117
|
-
await mkdir(logDir, { recursive: true });
|
|
3118
|
-
const outputPath = join(logDir, `${run.id}-${startedAt}.log`);
|
|
3119
|
-
const child = spawn(run.verifier.command, {
|
|
3184
|
+
void runGoalVerifierCommand({
|
|
3120
3185
|
cwd: props.cwd,
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
output += chunk.toString("utf-8");
|
|
3128
|
-
if (output.length > 20_000)
|
|
3129
|
-
output = output.slice(output.length - 20_000);
|
|
3130
|
-
});
|
|
3131
|
-
child.stderr?.on("data", (chunk) => {
|
|
3132
|
-
output += chunk.toString("utf-8");
|
|
3133
|
-
if (output.length > 20_000)
|
|
3134
|
-
output = output.slice(output.length - 20_000);
|
|
3135
|
-
});
|
|
3136
|
-
let verifierSettled = false;
|
|
3137
|
-
let timedOut = false;
|
|
3138
|
-
const timeout = verifierTimeoutMs > 0
|
|
3139
|
-
? setTimeout(() => {
|
|
3140
|
-
timedOut = true;
|
|
3141
|
-
if (child.pid)
|
|
3142
|
-
child.kill("SIGTERM");
|
|
3143
|
-
const killTimer = setTimeout(() => {
|
|
3144
|
-
if (!verifierSettled && child.pid)
|
|
3145
|
-
child.kill("SIGKILL");
|
|
3146
|
-
}, 5000);
|
|
3147
|
-
killTimer.unref?.();
|
|
3148
|
-
finishVerifier(124, `Verifier timed out after ${verifierTimeoutMs}ms and was terminated.\n${output}`);
|
|
3149
|
-
}, verifierTimeoutMs)
|
|
3150
|
-
: undefined;
|
|
3151
|
-
timeout?.unref?.();
|
|
3152
|
-
const finishVerifier = (code, forcedOutput) => {
|
|
3153
|
-
if (verifierSettled)
|
|
3154
|
-
return;
|
|
3155
|
-
verifierSettled = true;
|
|
3156
|
-
if (timeout)
|
|
3157
|
-
clearTimeout(timeout);
|
|
3186
|
+
runId: run.id,
|
|
3187
|
+
command: run.verifier.command,
|
|
3188
|
+
timeoutMs: verifierTimeoutMs,
|
|
3189
|
+
now: () => startedAt,
|
|
3190
|
+
})
|
|
3191
|
+
.then(async ({ verification, failureClass, durationMs }) => {
|
|
3158
3192
|
activeVerifierRunIdsRef.current.delete(run.id);
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
command: run.verifier?.command,
|
|
3178
|
-
lastResult: {
|
|
3179
|
-
status,
|
|
3180
|
-
summary,
|
|
3181
|
-
command: run.verifier?.command,
|
|
3182
|
-
exitCode: code ?? 1,
|
|
3183
|
-
outputPath,
|
|
3184
|
-
checkedAt: new Date().toISOString(),
|
|
3185
|
-
},
|
|
3186
|
-
},
|
|
3187
|
-
};
|
|
3188
|
-
const completionCheck = canCompleteGoalRun(runWithVerifier);
|
|
3189
|
-
const verifiedRun = await upsertGoalRun(props.cwd, {
|
|
3190
|
-
...runWithVerifier,
|
|
3191
|
-
continueRequestedAt: undefined,
|
|
3192
|
-
status: status === "pass" && completionCheck.ok
|
|
3193
|
-
? "passed"
|
|
3194
|
-
: status === "pass"
|
|
3195
|
-
? "ready"
|
|
3196
|
-
: "failed",
|
|
3197
|
-
});
|
|
3198
|
-
await appendGoalEvidence(props.cwd, run.id, {
|
|
3199
|
-
kind: "command",
|
|
3200
|
-
label: `Verifier ${status}`,
|
|
3201
|
-
content: `${failureClass}: ${summary}`.slice(0, 4000),
|
|
3202
|
-
path: outputPath,
|
|
3203
|
-
});
|
|
3204
|
-
await appendGoalDecision(props.cwd, run.id, {
|
|
3205
|
-
kind: `verifier_${status}`,
|
|
3206
|
-
reason: `${failureClass}: verifier exited with code ${code ?? 1}.`,
|
|
3207
|
-
content: `outputPath=${outputPath}; durationMs=${Date.now() - startedAt}`,
|
|
3208
|
-
});
|
|
3209
|
-
if (status === "fail" && shouldCreateVerifierFixTask(latestRun)) {
|
|
3210
|
-
await updateGoalTask(props.cwd, run.id, `fix-${Date.now()}`, {
|
|
3211
|
-
title: "Fix verifier failure",
|
|
3212
|
-
prompt: `Goal verifier failed after ${Date.now() - startedAt}ms. Original goal: ${run.goal}\n\n` +
|
|
3213
|
-
`Verifier command: ${run.verifier?.command}\n\n` +
|
|
3214
|
-
`Failure output:\n${summary.slice(-6000)}\n\nFix the cause, record evidence with the goals tool, and rerun relevant verification.`,
|
|
3215
|
-
status: "pending",
|
|
3216
|
-
});
|
|
3217
|
-
const runWithPendingFix = (await loadGoalRuns(props.cwd)).find((item) => item.id === run.id) ?? latestRun;
|
|
3218
|
-
await upsertGoalRun(props.cwd, { ...runWithPendingFix, status: "ready" });
|
|
3219
|
-
}
|
|
3220
|
-
setGoalCount((await summarizeGoalCounts(props.cwd)).active);
|
|
3221
|
-
appendGoalProgress({
|
|
3222
|
-
kind: "goal_progress",
|
|
3223
|
-
phase: "verifier_finished",
|
|
3224
|
-
title: `Verifier ${status}: ${run.title}`,
|
|
3225
|
-
detail: summarizeGoalCompletion(summary),
|
|
3226
|
-
status,
|
|
3227
|
-
});
|
|
3228
|
-
upsertGoalStatusEntry({
|
|
3229
|
-
runId: run.id,
|
|
3230
|
-
label: run.title,
|
|
3231
|
-
phase: status === "pass" ? "reviewing" : "failed",
|
|
3232
|
-
startedAt: Date.now(),
|
|
3233
|
-
detail: status === "pass" ? "reviewing verifier evidence" : "verifier failed",
|
|
3234
|
-
goalNumber: goalNumberForRun(run.id),
|
|
3235
|
-
});
|
|
3236
|
-
const eventText = formatGoalVerifierCompletionEvent(verifiedRun, status, run.verifier?.command ?? "", code ?? 1, summary);
|
|
3237
|
-
runGoalSyntheticEvent(eventText);
|
|
3238
|
-
const continuationRun = (await loadGoalRuns(props.cwd)).find((item) => item.id === run.id);
|
|
3239
|
-
if (continuationRun?.continueRequestedAt && status === "pass") {
|
|
3240
|
-
setTimeout(() => continueGoalRun(run.id), 500);
|
|
3241
|
-
}
|
|
3242
|
-
})().catch((err) => {
|
|
3243
|
-
clearGoalStatusEntry(run.id);
|
|
3244
|
-
log("ERROR", "goal", err instanceof Error ? err.message : String(err));
|
|
3245
|
-
setLiveItems((prev) => [...prev, toErrorItem(err, getId(), "Goal verifier")]);
|
|
3193
|
+
const status = verification.status;
|
|
3194
|
+
const summary = verification.summary;
|
|
3195
|
+
const outputPath = verification.outputPath;
|
|
3196
|
+
const latestRun = (await loadGoalRuns(props.cwd)).find((item) => item.id === run.id) ?? run;
|
|
3197
|
+
const runWithVerifier = {
|
|
3198
|
+
...latestRun,
|
|
3199
|
+
verifier: {
|
|
3200
|
+
...latestRun.verifier,
|
|
3201
|
+
description: latestRun.verifier?.description ?? "Goal verifier",
|
|
3202
|
+
command: run.verifier?.command,
|
|
3203
|
+
lastResult: verification,
|
|
3204
|
+
},
|
|
3205
|
+
};
|
|
3206
|
+
const completionCheck = canCompleteGoalRun(runWithVerifier);
|
|
3207
|
+
const verifiedRun = await upsertGoalRun(props.cwd, {
|
|
3208
|
+
...runWithVerifier,
|
|
3209
|
+
continueRequestedAt: status === "pass" && completionCheck.ok ? undefined : latestRun.continueRequestedAt,
|
|
3210
|
+
status: status === "pass" && completionCheck.ok ? "passed" : "ready",
|
|
3246
3211
|
});
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3212
|
+
await appendGoalEvidence(props.cwd, run.id, {
|
|
3213
|
+
kind: "command",
|
|
3214
|
+
label: `Verifier ${status}`,
|
|
3215
|
+
content: `${failureClass}: ${summary}`.slice(0, 4000),
|
|
3216
|
+
path: outputPath,
|
|
3217
|
+
});
|
|
3218
|
+
await appendGoalDecision(props.cwd, run.id, {
|
|
3219
|
+
kind: `verifier_${status}`,
|
|
3220
|
+
reason: `${failureClass}: verifier exited with code ${verification.exitCode ?? 1}.`,
|
|
3221
|
+
content: `outputPath=${outputPath ?? ""}; durationMs=${durationMs}`,
|
|
3222
|
+
});
|
|
3223
|
+
setGoalCount((await summarizeGoalCounts(props.cwd)).active);
|
|
3224
|
+
appendGoalProgress({
|
|
3225
|
+
kind: "goal_progress",
|
|
3226
|
+
phase: "verifier_finished",
|
|
3227
|
+
title: `Verifier ${status}: ${run.title}`,
|
|
3228
|
+
detail: summarizeGoalCompletion(summary),
|
|
3229
|
+
status,
|
|
3230
|
+
});
|
|
3231
|
+
upsertGoalStatusEntry({
|
|
3232
|
+
runId: run.id,
|
|
3233
|
+
label: run.title,
|
|
3234
|
+
phase: status === "pass" ? "reviewing" : "failed",
|
|
3235
|
+
startedAt: Date.now(),
|
|
3236
|
+
detail: status === "pass" ? "reviewing verifier evidence" : "verifier failed",
|
|
3237
|
+
goalNumber: goalNumberForRun(run.id),
|
|
3238
|
+
});
|
|
3239
|
+
const eventText = formatGoalVerifierCompletionEvent(verifiedRun, status === "pass" ? "pass" : "fail", run.verifier?.command ?? "", verification.exitCode ?? 1, summary);
|
|
3240
|
+
runGoalSyntheticEvent(eventText);
|
|
3241
|
+
const continuationRun = (await loadGoalRuns(props.cwd)).find((item) => item.id === run.id);
|
|
3242
|
+
if (continuationRun?.continueRequestedAt || status === "fail") {
|
|
3243
|
+
setTimeout(() => continueGoalRun(run.id), 500);
|
|
3244
|
+
}
|
|
3245
|
+
})
|
|
3246
|
+
.catch((err) => {
|
|
3247
|
+
activeVerifierRunIdsRef.current.delete(run.id);
|
|
3248
|
+
clearGoalStatusEntry(run.id);
|
|
3249
|
+
log("ERROR", "goal", err instanceof Error ? err.message : String(err));
|
|
3250
|
+
setLiveItems((prev) => [...prev, toErrorItem(err, getId(), "Goal verifier")]);
|
|
3251
|
+
});
|
|
3250
3252
|
}, [
|
|
3251
3253
|
props.cwd,
|
|
3252
3254
|
appendGoalProgress,
|
|
@@ -3260,7 +3262,12 @@ export function App(props) {
|
|
|
3260
3262
|
runningGoalIdsRef.current.delete(run.id);
|
|
3261
3263
|
if (run.activeWorkerId)
|
|
3262
3264
|
await stopGoalWorker(run.activeWorkerId);
|
|
3263
|
-
await
|
|
3265
|
+
const latestRun = (await loadGoalRuns(props.cwd)).find((item) => item.id === run.id) ?? run;
|
|
3266
|
+
await upsertGoalRun(props.cwd, {
|
|
3267
|
+
...latestRun,
|
|
3268
|
+
status: "paused",
|
|
3269
|
+
activeWorkerId: undefined,
|
|
3270
|
+
});
|
|
3264
3271
|
setGoalCount((await summarizeGoalCounts(props.cwd)).active);
|
|
3265
3272
|
appendGoalProgress({
|
|
3266
3273
|
kind: "goal_progress",
|
|
@@ -3610,7 +3617,7 @@ export function App(props) {
|
|
|
3610
3617
|
setLiveItems((prev) => [...prev, toErrorItem(err, getId())]);
|
|
3611
3618
|
});
|
|
3612
3619
|
} })) : (_jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: "column", flexGrow: 1, paddingRight: 1, children: [liveItems.map((item) => renderItem(item)), _jsx(StreamingArea, { isRunning: agentLoop.isRunning, streamingText: agentLoop.streamingText, streamingThinking: agentLoop.streamingThinking, thinkingMs: agentLoop.thinkingMs, planMode: planMode })] }), agentLoop.isRunning && agentLoop.activityPhase !== "idle" ? (_jsx(Box, { marginTop: 1, borderStyle: "round", borderColor: agentLoop.activityPhase === "thinking" ? THINKING_BORDER_COLORS[0] : "transparent", paddingLeft: 1, paddingRight: 1, width: columns, children: _jsx(ActivityIndicator, { phase: agentLoop.activityPhase, elapsedMs: agentLoop.elapsedMs, runStartRef: agentLoop.runStartRef, thinkingMs: agentLoop.thinkingMs, isThinking: agentLoop.isThinking, thinkingEnabled: thinkingEnabled, tokenEstimate: agentLoop.streamedTokenEstimate, charCountRef: agentLoop.charCountRef, realTokensAccumRef: agentLoop.realTokensAccumRef, userMessage: lastUserMessage, activeToolNames: agentLoop.activeToolCalls.map((tc) => tc.name), planMode: planMode, retryInfo: agentLoop.retryInfo, planDone: planSteps.filter((s) => s.completed).length, planTotal: planSteps.length, staticDisplay: true }) })) : agentLoop.stallError ? (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: theme.warning, children: "⚠ API provider stream interrupted — retries exhausted." }), _jsx(Text, { color: theme.textDim, children: " Your conversation is preserved. Send a message to continue." })] })) : (doneStatus &&
|
|
3613
|
-
!agentLoop.isRunning && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.success, children: ["✻ ", doneStatus.verb, " ", formatDuration(doneStatus.durationMs)] }) }))), agentLoop.queuedCount > 0 && (
|
|
3620
|
+
!agentLoop.isRunning && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.success, children: ["✻ ", doneStatus.verb, " ", formatDuration(doneStatus.durationMs)] }) }))), agentLoop.queuedCount > 0 && (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: theme.warning, bold: true, children: "• " }), _jsxs(Text, { color: theme.textDim, children: [agentLoop.queuedCount, " message", agentLoop.queuedCount > 1 ? "s" : "", " queued"] })] })), _jsx(InputArea, { onSubmit: handleSubmit, onAbort: handleAbort, disabled: agentLoop.isRunning, isActive: !taskBarFocused && !overlay, onDownAtEnd: handleFocusTaskBar, onShiftTab: handleToggleThinking, onToggleTasks: () => {
|
|
3614
3621
|
// Just flip the overlay state — Ink's log-update handles the
|
|
3615
3622
|
// live-area transition (chat input → TaskOverlay) natively, and
|
|
3616
3623
|
// the chat history above stays in scrollback. When the overlay
|