@kenkaiiii/ggcoder 4.3.207 → 4.3.209
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +10 -7
- package/dist/cli.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/repomap.js +8 -1
- package/dist/core/repomap.js.map +1 -1
- package/dist/core/repomap.test.js +32 -0
- package/dist/core/repomap.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/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 +2 -3
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +128 -208
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/app-state-persistence.test.js +60 -4
- package/dist/ui/app-state-persistence.test.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-overlay.test.js +30 -0
- package/dist/ui/goal-overlay.test.js.map +1 -1
- package/dist/ui/scroll-stabilization.test.js +2 -2
- package/dist/ui/scroll-stabilization.test.js.map +1 -1
- 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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import React, { useState, useRef, useCallback, useEffect, useMemo } from "react";
|
|
3
|
-
import { Box, Text, Static
|
|
3
|
+
import { Box, Text, Static } 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";
|
|
@@ -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,
|
|
62
|
+
import { appendGoalDecision, appendGoalEvidence, formatGoalBlockingPrerequisites, goalHasBlockingPrerequisites, loadGoalRuns, reconcileActiveGoalRuns, summarizeGoalCounts, summarizeGoalCountsFromRuns, updateGoalTask, upsertGoalRun, } from "../core/goal-store.js";
|
|
63
63
|
import { canCompleteGoalRun, decideGoalNextAction, shouldCreateVerifierFixTask, } 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;
|
|
@@ -298,12 +297,11 @@ export function formatGoalTerminalProgress(run) {
|
|
|
298
297
|
return null;
|
|
299
298
|
}
|
|
300
299
|
}
|
|
301
|
-
export function shouldHideHistoryForOverlayView(
|
|
302
|
-
//
|
|
303
|
-
//
|
|
304
|
-
//
|
|
305
|
-
|
|
306
|
-
return isOverlayView && !isAgentRunning;
|
|
300
|
+
export function shouldHideHistoryForOverlayView(_isOverlayView, _isAgentRunning) {
|
|
301
|
+
// Ink Static is append-only. Passing [] for overlay panes rewrites the Static
|
|
302
|
+
// accumulator and can destroy scrollback when the pane closes. Keep history
|
|
303
|
+
// mounted and let overlays render below it.
|
|
304
|
+
return false;
|
|
307
305
|
}
|
|
308
306
|
export function shouldStabilizeOverlayPaneRerender({ overlayPane, isAgentRunning, }) {
|
|
309
307
|
return isAgentRunning && (overlayPane === "goal" || overlayPane === "plan");
|
|
@@ -321,8 +319,8 @@ export function getScrollStabilizationDecision({ isUserScrolled, hasNewOutput, h
|
|
|
321
319
|
export function isTallLiveUserMessage(text, rows) {
|
|
322
320
|
return text.split("\n").length > Math.max(8, Math.floor(rows * 0.6));
|
|
323
321
|
}
|
|
324
|
-
export function getStaticHistoryKey({ resizeKey
|
|
325
|
-
return `${resizeKey}
|
|
322
|
+
export function getStaticHistoryKey({ resizeKey }) {
|
|
323
|
+
return `${resizeKey}`;
|
|
326
324
|
}
|
|
327
325
|
// flushOnTurnText, flushOnTurnEnd are imported from ./live-item-flush.ts
|
|
328
326
|
/** Check whether an item is still active (running spinner, pending result). */
|
|
@@ -485,7 +483,6 @@ function markTaskInProgress(cwd, taskId) {
|
|
|
485
483
|
export function App(props) {
|
|
486
484
|
const theme = useTheme();
|
|
487
485
|
const switchTheme = useSetTheme();
|
|
488
|
-
const { stdout } = useStdout();
|
|
489
486
|
const { columns, resizeKey } = useTerminalSize();
|
|
490
487
|
// Hoisted before terminal title hook so it can reference them
|
|
491
488
|
const [lastUserMessage, setLastUserMessage] = useState("");
|
|
@@ -547,7 +544,6 @@ export function App(props) {
|
|
|
547
544
|
const startPixelFixRef = useRef(() => { });
|
|
548
545
|
const cwdRef = useRef(props.cwd);
|
|
549
546
|
const [displayedCwd, setDisplayedCwd] = useState(props.cwd);
|
|
550
|
-
const [staticKey, setStaticKey] = useState(0);
|
|
551
547
|
const [doneStatus, setDoneStatus] = useState(props.sessionStore?.doneStatus ?? null);
|
|
552
548
|
// Suppress "done" status when a plan overlay is about to open
|
|
553
549
|
const planOverlayPendingRef = useRef(false);
|
|
@@ -906,14 +902,6 @@ export function App(props) {
|
|
|
906
902
|
// premature "done" status that fires when the agent loop finishes
|
|
907
903
|
planOverlayPendingRef.current = true;
|
|
908
904
|
setTimeout(() => {
|
|
909
|
-
// NOTE: this is the one open-overlay path that does NOT remount via
|
|
910
|
-
// resetUI. It runs while the agent is still mid-turn (after the
|
|
911
|
-
// exit_plan tool returned but before onDone fires), and unmounting
|
|
912
|
-
// here would kill the in-flight agent stream. Keep the bare ANSI
|
|
913
|
-
// clear; the drift bug is tolerable across just the agent's
|
|
914
|
-
// wrap-up turn, and onApprove/onReject both remount cleanly via
|
|
915
|
-
// resetUI when the user resolves the plan.
|
|
916
|
-
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
917
905
|
setPlanAutoExpand(true);
|
|
918
906
|
setOverlay("plan");
|
|
919
907
|
// Don't clear planOverlayPendingRef here — keep it true until
|
|
@@ -928,7 +916,7 @@ export function App(props) {
|
|
|
928
916
|
planPath);
|
|
929
917
|
};
|
|
930
918
|
}
|
|
931
|
-
}, [props.onExitPlanRef, replaceSystemPrompt
|
|
919
|
+
}, [props.onExitPlanRef, replaceSystemPrompt]);
|
|
932
920
|
const appendMessagesToSession = useCallback(async (sessionPath, messages, startIndex) => {
|
|
933
921
|
const sm = sessionManagerRef.current;
|
|
934
922
|
if (!sm)
|
|
@@ -2045,14 +2033,10 @@ export function App(props) {
|
|
|
2045
2033
|
process.exit(0);
|
|
2046
2034
|
}
|
|
2047
2035
|
// Handle /clear — tear down the entire Ink instance and rebuild fresh.
|
|
2048
|
-
//
|
|
2049
|
-
//
|
|
2050
|
-
//
|
|
2051
|
-
//
|
|
2052
|
-
// terminal-state assumptions that ANSI clearing breaks. The reliable
|
|
2053
|
-
// fix is unmount + render again. Runtime state (model, provider,
|
|
2054
|
-
// thinking) survives via renderApp's closure-held `runtimeState`,
|
|
2055
|
-
// mirrored from React state via the useEffects above.
|
|
2036
|
+
// Avoid direct ANSI terminal clears here; they can erase scrollback.
|
|
2037
|
+
// Runtime state (model, provider, thinking) survives via renderApp's
|
|
2038
|
+
// closure-held `runtimeState`, mirrored from React state via the
|
|
2039
|
+
// useEffects above.
|
|
2056
2040
|
if (trimmed === "/clear") {
|
|
2057
2041
|
if (props.resetUI) {
|
|
2058
2042
|
void (async () => {
|
|
@@ -2065,8 +2049,7 @@ export function App(props) {
|
|
|
2065
2049
|
return;
|
|
2066
2050
|
}
|
|
2067
2051
|
// Fallback path (resetUI not wired — e.g. tests). Best-effort: clear
|
|
2068
|
-
// React state in place
|
|
2069
|
-
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
2052
|
+
// React state in place without touching terminal scrollback.
|
|
2070
2053
|
pendingFlushRef.current = [];
|
|
2071
2054
|
setHistory([{ kind: "banner", id: "banner" }]);
|
|
2072
2055
|
setLiveItems([]);
|
|
@@ -2082,7 +2065,6 @@ export function App(props) {
|
|
|
2082
2065
|
agentLoop.reset();
|
|
2083
2066
|
setSessionTitle(undefined);
|
|
2084
2067
|
sessionTitleGeneratedRef.current = false;
|
|
2085
|
-
setStaticKey((k) => k + 1);
|
|
2086
2068
|
setLiveItems([{ kind: "info", text: "Session cleared.", id: getId() }]);
|
|
2087
2069
|
return;
|
|
2088
2070
|
}
|
|
@@ -2190,8 +2172,6 @@ export function App(props) {
|
|
|
2190
2172
|
props.resetUI();
|
|
2191
2173
|
}
|
|
2192
2174
|
else {
|
|
2193
|
-
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
2194
|
-
setStaticKey((key) => key + 1);
|
|
2195
2175
|
if (props.sessionStore) {
|
|
2196
2176
|
props.sessionStore.overlay = "goal";
|
|
2197
2177
|
props.sessionStore.planAutoExpand = false;
|
|
@@ -2549,11 +2529,23 @@ export function App(props) {
|
|
|
2549
2529
|
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));
|
|
2550
2530
|
case "goal_progress": {
|
|
2551
2531
|
const isError = item.status === "failed" || item.status === "fail" || item.status === "blocked";
|
|
2552
|
-
const color =
|
|
2553
|
-
? theme.
|
|
2554
|
-
:
|
|
2555
|
-
? theme.
|
|
2556
|
-
:
|
|
2532
|
+
const color = isError
|
|
2533
|
+
? theme.error
|
|
2534
|
+
: item.phase === "worker_finished"
|
|
2535
|
+
? theme.success
|
|
2536
|
+
: item.phase === "verifier_finished"
|
|
2537
|
+
? theme.accent
|
|
2538
|
+
: item.phase === "orchestrator_reviewing" || item.phase === "orchestrator_working"
|
|
2539
|
+
? theme.secondary
|
|
2540
|
+
: item.phase === "continuing"
|
|
2541
|
+
? theme.warning
|
|
2542
|
+
: item.phase === "verifier_started"
|
|
2543
|
+
? theme.accent
|
|
2544
|
+
: item.phase === "worker_started"
|
|
2545
|
+
? theme.primary
|
|
2546
|
+
: item.phase === "terminal"
|
|
2547
|
+
? theme.success
|
|
2548
|
+
: theme.primary;
|
|
2557
2549
|
const glyph = item.phase === "worker_finished" || item.phase === "verifier_finished"
|
|
2558
2550
|
? "✓ "
|
|
2559
2551
|
: item.phase === "terminal"
|
|
@@ -2675,7 +2667,6 @@ export function App(props) {
|
|
|
2675
2667
|
return;
|
|
2676
2668
|
}
|
|
2677
2669
|
// Fallback path (resetUI not wired — tests).
|
|
2678
|
-
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
2679
2670
|
setHistory([{ kind: "banner", id: "banner" }]);
|
|
2680
2671
|
setLiveItems([]);
|
|
2681
2672
|
messagesRef.current = messagesRef.current.slice(0, 1);
|
|
@@ -2709,15 +2700,7 @@ export function App(props) {
|
|
|
2709
2700
|
setRunAllTasks(false);
|
|
2710
2701
|
}
|
|
2711
2702
|
})();
|
|
2712
|
-
}, [
|
|
2713
|
-
props.cwd,
|
|
2714
|
-
props.resetUI,
|
|
2715
|
-
props.sessionStore,
|
|
2716
|
-
stdout,
|
|
2717
|
-
agentLoop,
|
|
2718
|
-
currentProvider,
|
|
2719
|
-
currentModel,
|
|
2720
|
-
]);
|
|
2703
|
+
}, [props.cwd, props.resetUI, props.sessionStore, agentLoop, currentProvider, currentModel]);
|
|
2721
2704
|
const openOverlay = useCallback((kind) => {
|
|
2722
2705
|
if (props.resetUI && props.sessionStore && !agentLoop.isRunning) {
|
|
2723
2706
|
props.sessionStore.overlay = kind;
|
|
@@ -2738,7 +2721,7 @@ export function App(props) {
|
|
|
2738
2721
|
setPlanAutoExpand(false);
|
|
2739
2722
|
setOverlay(kind);
|
|
2740
2723
|
}
|
|
2741
|
-
}, [agentLoop.isRunning, props
|
|
2724
|
+
}, [agentLoop.isRunning, props]);
|
|
2742
2725
|
const closeOverlay = useCallback(() => {
|
|
2743
2726
|
if (props.resetUI && props.sessionStore && !agentLoop.isRunning) {
|
|
2744
2727
|
props.sessionStore.overlay = null;
|
|
@@ -2750,7 +2733,7 @@ export function App(props) {
|
|
|
2750
2733
|
}
|
|
2751
2734
|
setOverlay(null);
|
|
2752
2735
|
}
|
|
2753
|
-
}, [agentLoop.isRunning, overlay, props
|
|
2736
|
+
}, [agentLoop.isRunning, overlay, props]);
|
|
2754
2737
|
const runGoalSyntheticEvent = useCallback((eventText) => {
|
|
2755
2738
|
const eventInfo = parseGoalSyntheticEvent(eventText);
|
|
2756
2739
|
const detail = eventInfo?.kind === "worker"
|
|
@@ -2841,8 +2824,8 @@ export function App(props) {
|
|
|
2841
2824
|
appendGoalProgress({
|
|
2842
2825
|
kind: "goal_progress",
|
|
2843
2826
|
phase: "continuing",
|
|
2844
|
-
title: `
|
|
2845
|
-
detail: "
|
|
2827
|
+
title: `Choosing next Goal step: ${latestRun.title}`,
|
|
2828
|
+
detail: "Latest result is recorded; starting the next worker task or verifier automatically.",
|
|
2846
2829
|
status: latestRun.status,
|
|
2847
2830
|
});
|
|
2848
2831
|
upsertGoalStatusEntry({
|
|
@@ -3138,143 +3121,89 @@ export function App(props) {
|
|
|
3138
3121
|
detail: run.verifier.command,
|
|
3139
3122
|
goalNumber: goalNumberForRun(run.id),
|
|
3140
3123
|
});
|
|
3141
|
-
|
|
3142
|
-
const { mkdir, writeFile } = await import("node:fs/promises");
|
|
3143
|
-
const { join } = await import("node:path");
|
|
3144
|
-
const logDir = join(projectDir(props.cwd), "verifiers");
|
|
3145
|
-
await mkdir(logDir, { recursive: true });
|
|
3146
|
-
const outputPath = join(logDir, `${run.id}-${startedAt}.log`);
|
|
3147
|
-
const child = spawn(run.verifier.command, {
|
|
3124
|
+
void runGoalVerifierCommand({
|
|
3148
3125
|
cwd: props.cwd,
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
output += chunk.toString("utf-8");
|
|
3156
|
-
if (output.length > 20_000)
|
|
3157
|
-
output = output.slice(output.length - 20_000);
|
|
3158
|
-
});
|
|
3159
|
-
child.stderr?.on("data", (chunk) => {
|
|
3160
|
-
output += chunk.toString("utf-8");
|
|
3161
|
-
if (output.length > 20_000)
|
|
3162
|
-
output = output.slice(output.length - 20_000);
|
|
3163
|
-
});
|
|
3164
|
-
let verifierSettled = false;
|
|
3165
|
-
let timedOut = false;
|
|
3166
|
-
const timeout = verifierTimeoutMs > 0
|
|
3167
|
-
? setTimeout(() => {
|
|
3168
|
-
timedOut = true;
|
|
3169
|
-
if (child.pid)
|
|
3170
|
-
child.kill("SIGTERM");
|
|
3171
|
-
const killTimer = setTimeout(() => {
|
|
3172
|
-
if (!verifierSettled && child.pid)
|
|
3173
|
-
child.kill("SIGKILL");
|
|
3174
|
-
}, 5000);
|
|
3175
|
-
killTimer.unref?.();
|
|
3176
|
-
finishVerifier(124, `Verifier timed out after ${verifierTimeoutMs}ms and was terminated.\n${output}`);
|
|
3177
|
-
}, verifierTimeoutMs)
|
|
3178
|
-
: undefined;
|
|
3179
|
-
timeout?.unref?.();
|
|
3180
|
-
const finishVerifier = (code, forcedOutput) => {
|
|
3181
|
-
if (verifierSettled)
|
|
3182
|
-
return;
|
|
3183
|
-
verifierSettled = true;
|
|
3184
|
-
if (timeout)
|
|
3185
|
-
clearTimeout(timeout);
|
|
3126
|
+
runId: run.id,
|
|
3127
|
+
command: run.verifier.command,
|
|
3128
|
+
timeoutMs: verifierTimeoutMs,
|
|
3129
|
+
now: () => startedAt,
|
|
3130
|
+
})
|
|
3131
|
+
.then(async ({ verification, failureClass, durationMs }) => {
|
|
3186
3132
|
activeVerifierRunIdsRef.current.delete(run.id);
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
label: `Verifier ${status}`,
|
|
3229
|
-
content: `${failureClass}: ${summary}`.slice(0, 4000),
|
|
3230
|
-
path: outputPath,
|
|
3231
|
-
});
|
|
3232
|
-
await appendGoalDecision(props.cwd, run.id, {
|
|
3233
|
-
kind: `verifier_${status}`,
|
|
3234
|
-
reason: `${failureClass}: verifier exited with code ${code ?? 1}.`,
|
|
3235
|
-
content: `outputPath=${outputPath}; durationMs=${Date.now() - startedAt}`,
|
|
3236
|
-
});
|
|
3237
|
-
if (status === "fail" && shouldCreateVerifierFixTask(latestRun)) {
|
|
3238
|
-
await updateGoalTask(props.cwd, run.id, `fix-${Date.now()}`, {
|
|
3239
|
-
title: "Fix verifier failure",
|
|
3240
|
-
prompt: `Goal verifier failed after ${Date.now() - startedAt}ms. Original goal: ${run.goal}\n\n` +
|
|
3241
|
-
`Verifier command: ${run.verifier?.command}\n\n` +
|
|
3242
|
-
`Failure output:\n${summary.slice(-6000)}\n\nFix the cause, record evidence with the goals tool, and rerun relevant verification.`,
|
|
3243
|
-
status: "pending",
|
|
3244
|
-
});
|
|
3245
|
-
const runWithPendingFix = (await loadGoalRuns(props.cwd)).find((item) => item.id === run.id) ?? latestRun;
|
|
3246
|
-
await upsertGoalRun(props.cwd, { ...runWithPendingFix, status: "ready" });
|
|
3247
|
-
}
|
|
3248
|
-
setGoalCount((await summarizeGoalCounts(props.cwd)).active);
|
|
3249
|
-
appendGoalProgress({
|
|
3250
|
-
kind: "goal_progress",
|
|
3251
|
-
phase: "verifier_finished",
|
|
3252
|
-
title: `Verifier ${status}: ${run.title}`,
|
|
3253
|
-
detail: summarizeGoalCompletion(summary),
|
|
3254
|
-
status,
|
|
3255
|
-
});
|
|
3256
|
-
upsertGoalStatusEntry({
|
|
3257
|
-
runId: run.id,
|
|
3258
|
-
label: run.title,
|
|
3259
|
-
phase: status === "pass" ? "reviewing" : "failed",
|
|
3260
|
-
startedAt: Date.now(),
|
|
3261
|
-
detail: status === "pass" ? "reviewing verifier evidence" : "verifier failed",
|
|
3262
|
-
goalNumber: goalNumberForRun(run.id),
|
|
3133
|
+
const status = verification.status;
|
|
3134
|
+
const summary = verification.summary;
|
|
3135
|
+
const outputPath = verification.outputPath;
|
|
3136
|
+
const latestRun = (await loadGoalRuns(props.cwd)).find((item) => item.id === run.id) ?? run;
|
|
3137
|
+
const runWithVerifier = {
|
|
3138
|
+
...latestRun,
|
|
3139
|
+
verifier: {
|
|
3140
|
+
...latestRun.verifier,
|
|
3141
|
+
description: latestRun.verifier?.description ?? "Goal verifier",
|
|
3142
|
+
command: run.verifier?.command,
|
|
3143
|
+
lastResult: verification,
|
|
3144
|
+
},
|
|
3145
|
+
};
|
|
3146
|
+
const completionCheck = canCompleteGoalRun(runWithVerifier);
|
|
3147
|
+
const verifiedRun = await upsertGoalRun(props.cwd, {
|
|
3148
|
+
...runWithVerifier,
|
|
3149
|
+
continueRequestedAt: undefined,
|
|
3150
|
+
status: status === "pass" && completionCheck.ok
|
|
3151
|
+
? "passed"
|
|
3152
|
+
: status === "pass"
|
|
3153
|
+
? "ready"
|
|
3154
|
+
: "failed",
|
|
3155
|
+
});
|
|
3156
|
+
await appendGoalEvidence(props.cwd, run.id, {
|
|
3157
|
+
kind: "command",
|
|
3158
|
+
label: `Verifier ${status}`,
|
|
3159
|
+
content: `${failureClass}: ${summary}`.slice(0, 4000),
|
|
3160
|
+
path: outputPath,
|
|
3161
|
+
});
|
|
3162
|
+
await appendGoalDecision(props.cwd, run.id, {
|
|
3163
|
+
kind: `verifier_${status}`,
|
|
3164
|
+
reason: `${failureClass}: verifier exited with code ${verification.exitCode ?? 1}.`,
|
|
3165
|
+
content: `outputPath=${outputPath ?? ""}; durationMs=${durationMs}`,
|
|
3166
|
+
});
|
|
3167
|
+
if (status === "fail" && shouldCreateVerifierFixTask(latestRun)) {
|
|
3168
|
+
await updateGoalTask(props.cwd, run.id, `fix-${Date.now()}`, {
|
|
3169
|
+
title: "Fix verifier failure",
|
|
3170
|
+
prompt: `Goal verifier failed after ${durationMs}ms. Original goal: ${run.goal}\n\n` +
|
|
3171
|
+
`Verifier command: ${run.verifier?.command}\n\n` +
|
|
3172
|
+
`Failure output:\n${summary.slice(-6000)}\n\nFix the cause, record evidence with the goals tool, and rerun relevant verification.`,
|
|
3173
|
+
status: "pending",
|
|
3263
3174
|
});
|
|
3264
|
-
const
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3175
|
+
const runWithPendingFix = (await loadGoalRuns(props.cwd)).find((item) => item.id === run.id) ?? latestRun;
|
|
3176
|
+
await upsertGoalRun(props.cwd, { ...runWithPendingFix, status: "ready" });
|
|
3177
|
+
}
|
|
3178
|
+
setGoalCount((await summarizeGoalCounts(props.cwd)).active);
|
|
3179
|
+
appendGoalProgress({
|
|
3180
|
+
kind: "goal_progress",
|
|
3181
|
+
phase: "verifier_finished",
|
|
3182
|
+
title: `Verifier ${status}: ${run.title}`,
|
|
3183
|
+
detail: summarizeGoalCompletion(summary),
|
|
3184
|
+
status,
|
|
3274
3185
|
});
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3186
|
+
upsertGoalStatusEntry({
|
|
3187
|
+
runId: run.id,
|
|
3188
|
+
label: run.title,
|
|
3189
|
+
phase: status === "pass" ? "reviewing" : "failed",
|
|
3190
|
+
startedAt: Date.now(),
|
|
3191
|
+
detail: status === "pass" ? "reviewing verifier evidence" : "verifier failed",
|
|
3192
|
+
goalNumber: goalNumberForRun(run.id),
|
|
3193
|
+
});
|
|
3194
|
+
const eventText = formatGoalVerifierCompletionEvent(verifiedRun, status === "pass" ? "pass" : "fail", run.verifier?.command ?? "", verification.exitCode ?? 1, summary);
|
|
3195
|
+
runGoalSyntheticEvent(eventText);
|
|
3196
|
+
const continuationRun = (await loadGoalRuns(props.cwd)).find((item) => item.id === run.id);
|
|
3197
|
+
if (continuationRun?.continueRequestedAt && status === "pass") {
|
|
3198
|
+
setTimeout(() => continueGoalRun(run.id), 500);
|
|
3199
|
+
}
|
|
3200
|
+
})
|
|
3201
|
+
.catch((err) => {
|
|
3202
|
+
activeVerifierRunIdsRef.current.delete(run.id);
|
|
3203
|
+
clearGoalStatusEntry(run.id);
|
|
3204
|
+
log("ERROR", "goal", err instanceof Error ? err.message : String(err));
|
|
3205
|
+
setLiveItems((prev) => [...prev, toErrorItem(err, getId(), "Goal verifier")]);
|
|
3206
|
+
});
|
|
3278
3207
|
}, [
|
|
3279
3208
|
props.cwd,
|
|
3280
3209
|
appendGoalProgress,
|
|
@@ -3364,14 +3293,10 @@ export function App(props) {
|
|
|
3364
3293
|
activeLanguages: detectedForPixelFix,
|
|
3365
3294
|
tools: toolsForPixelFix,
|
|
3366
3295
|
});
|
|
3367
|
-
// Now that the cwd swap is committed, reset chat.
|
|
3368
|
-
//
|
|
3369
|
-
// staticKey would print a second banner with the new cwd — leaving
|
|
3370
|
-
// two banners stacked in the scrollback.
|
|
3371
|
-
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
3296
|
+
// Now that the cwd swap is committed, reset chat. Do not clear the
|
|
3297
|
+
// terminal here; terminal clear sequences can erase saved scrollback.
|
|
3372
3298
|
setHistory([{ kind: "banner", id: "banner" }]);
|
|
3373
3299
|
setLiveItems([]);
|
|
3374
|
-
setStaticKey((k) => k + 1);
|
|
3375
3300
|
messagesRef.current = messagesRef.current.slice(0, 1);
|
|
3376
3301
|
agentLoop.reset();
|
|
3377
3302
|
persistedIndexRef.current = messagesRef.current.length;
|
|
@@ -3403,7 +3328,7 @@ export function App(props) {
|
|
|
3403
3328
|
setLiveItems((prev) => [...prev, toErrorItem(err, getId())]);
|
|
3404
3329
|
}
|
|
3405
3330
|
})();
|
|
3406
|
-
}, [props.cwd,
|
|
3331
|
+
}, [props.cwd, agentLoop, currentProvider, currentModel]);
|
|
3407
3332
|
startPixelFixRef.current = startPixelFix;
|
|
3408
3333
|
// Seed from sessionStore so "Fix All" chaining survives a deferred
|
|
3409
3334
|
// resetUI() if it fires between pixel fixes (e.g. user toggled a pane).
|
|
@@ -3431,12 +3356,13 @@ export function App(props) {
|
|
|
3431
3356
|
overlayPane: overlay,
|
|
3432
3357
|
isAgentRunning: agentLoop.isRunning,
|
|
3433
3358
|
});
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3359
|
+
const staticItems = shouldHideStaticItemsForOverlayView({
|
|
3360
|
+
shouldHideHistoryForOverlay,
|
|
3361
|
+
stabilizeOverlayPaneRerender,
|
|
3362
|
+
})
|
|
3363
|
+
? []
|
|
3364
|
+
: history;
|
|
3365
|
+
return (_jsxs(Box, { flexDirection: "column", width: columns, children: [_jsx(Static, { items: staticItems, style: { width: "100%" }, children: (item) => (_jsx(Box, { flexDirection: "column", paddingRight: 1, children: renderItem(item) }, item.id)) }, getStaticHistoryKey({ resizeKey })), isTaskView ? (_jsx(TaskOverlay, { cwd: props.cwd, agentRunning: agentLoop.isRunning, onClose: () => {
|
|
3440
3366
|
if (props.resetUI && props.sessionStore && !agentLoop.isRunning) {
|
|
3441
3367
|
props.sessionStore.overlay = null;
|
|
3442
3368
|
props.resetUI();
|
|
@@ -3586,10 +3512,8 @@ export function App(props) {
|
|
|
3586
3512
|
approvedPlanPathRef.current = planPath;
|
|
3587
3513
|
planStepsRef.current = steps;
|
|
3588
3514
|
setPlanSteps(steps);
|
|
3589
|
-
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
3590
3515
|
setHistory([{ kind: "banner", id: "banner" }]);
|
|
3591
3516
|
setLiveItems([]);
|
|
3592
|
-
setStaticKey((k) => k + 1);
|
|
3593
3517
|
setPlanAutoExpand(false);
|
|
3594
3518
|
setOverlay(null);
|
|
3595
3519
|
messagesRef.current = [{ role: "system", content: newPrompt }];
|
|
@@ -3630,8 +3554,6 @@ export function App(props) {
|
|
|
3630
3554
|
});
|
|
3631
3555
|
return;
|
|
3632
3556
|
}
|
|
3633
|
-
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
3634
|
-
setStaticKey((k) => k + 1);
|
|
3635
3557
|
setPlanAutoExpand(false);
|
|
3636
3558
|
setOverlay(null);
|
|
3637
3559
|
setDoneStatus(null);
|
|
@@ -3646,8 +3568,6 @@ export function App(props) {
|
|
|
3646
3568
|
});
|
|
3647
3569
|
} })) : (_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 &&
|
|
3648
3570
|
!agentLoop.isRunning && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.success, children: ["✻ ", doneStatus.verb, " ", formatDuration(doneStatus.durationMs)] }) }))), agentLoop.queuedCount > 0 && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.accent, children: ["⏳ ", agentLoop.queuedCount, " message", agentLoop.queuedCount > 1 ? "s" : "", " queued"] }) })), _jsx(InputArea, { onSubmit: handleSubmit, onAbort: handleAbort, disabled: agentLoop.isRunning, isActive: !taskBarFocused && !overlay, onDownAtEnd: handleFocusTaskBar, onShiftTab: handleToggleThinking, onToggleTasks: () => {
|
|
3649
|
-
// While the agent is running, skip the screen-clear + staticKey
|
|
3650
|
-
// bump that would otherwise wipe the chat history from scrollback.
|
|
3651
3571
|
// Just flip the overlay state — Ink's log-update handles the
|
|
3652
3572
|
// live-area transition (chat input → TaskOverlay) natively, and
|
|
3653
3573
|
// the chat history above stays in scrollback. When the overlay
|