@kenkaiiii/ggcoder 4.3.218 → 4.3.219

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.
Files changed (58) hide show
  1. package/dist/cli.js +2 -1
  2. package/dist/cli.js.map +1 -1
  3. package/dist/core/goal-store.d.ts +2 -0
  4. package/dist/core/goal-store.d.ts.map +1 -1
  5. package/dist/core/goal-store.js +21 -0
  6. package/dist/core/goal-store.js.map +1 -1
  7. package/dist/core/tasks-store.d.ts +24 -0
  8. package/dist/core/tasks-store.d.ts.map +1 -0
  9. package/dist/core/tasks-store.js +81 -0
  10. package/dist/core/tasks-store.js.map +1 -0
  11. package/dist/tools/index.d.ts +1 -0
  12. package/dist/tools/index.d.ts.map +1 -1
  13. package/dist/tools/index.js +3 -0
  14. package/dist/tools/index.js.map +1 -1
  15. package/dist/tools/tasks.d.ts +16 -0
  16. package/dist/tools/tasks.d.ts.map +1 -0
  17. package/dist/tools/tasks.js +93 -0
  18. package/dist/tools/tasks.js.map +1 -0
  19. package/dist/ui/App.d.ts +1 -0
  20. package/dist/ui/App.d.ts.map +1 -1
  21. package/dist/ui/App.js +153 -127
  22. package/dist/ui/App.js.map +1 -1
  23. package/dist/ui/app-items.d.ts +6 -1
  24. package/dist/ui/app-items.d.ts.map +1 -1
  25. package/dist/ui/app-state-persistence.test.js +19 -1
  26. package/dist/ui/app-state-persistence.test.js.map +1 -1
  27. package/dist/ui/chat-layout-pinning.test.js +10 -0
  28. package/dist/ui/chat-layout-pinning.test.js.map +1 -1
  29. package/dist/ui/components/Banner.js +2 -2
  30. package/dist/ui/components/Banner.js.map +1 -1
  31. package/dist/ui/components/GoalPickerMenu.d.ts +9 -0
  32. package/dist/ui/components/GoalPickerMenu.d.ts.map +1 -0
  33. package/dist/ui/components/GoalPickerMenu.js +37 -0
  34. package/dist/ui/components/GoalPickerMenu.js.map +1 -0
  35. package/dist/ui/components/InputArea.d.ts +16 -1
  36. package/dist/ui/components/InputArea.d.ts.map +1 -1
  37. package/dist/ui/components/InputArea.js +91 -3
  38. package/dist/ui/components/InputArea.js.map +1 -1
  39. package/dist/ui/components/TaskPickerMenu.d.ts +9 -0
  40. package/dist/ui/components/TaskPickerMenu.d.ts.map +1 -0
  41. package/dist/ui/components/TaskPickerMenu.js +33 -0
  42. package/dist/ui/components/TaskPickerMenu.js.map +1 -0
  43. package/dist/ui/item-helpers.d.ts +2 -0
  44. package/dist/ui/item-helpers.d.ts.map +1 -1
  45. package/dist/ui/item-helpers.js +16 -0
  46. package/dist/ui/item-helpers.js.map +1 -1
  47. package/dist/ui/layout-decisions.d.ts.map +1 -1
  48. package/dist/ui/layout-decisions.js +1 -0
  49. package/dist/ui/layout-decisions.js.map +1 -1
  50. package/dist/ui/terminal-history-spacing.d.ts.map +1 -1
  51. package/dist/ui/terminal-history-spacing.js +1 -0
  52. package/dist/ui/terminal-history-spacing.js.map +1 -1
  53. package/dist/ui/terminal-history.d.ts.map +1 -1
  54. package/dist/ui/terminal-history.js +6 -3
  55. package/dist/ui/terminal-history.js.map +1 -1
  56. package/dist/ui/tui-history-parity.test.js +3 -0
  57. package/dist/ui/tui-history-parity.test.js.map +1 -1
  58. package/package.json +3 -3
package/dist/ui/App.js CHANGED
@@ -27,7 +27,6 @@ import { GoalStatusBar, reconcileGoalStatusEntriesWithRuns, removeGoalStatusEntr
27
27
  import { Banner } from "./components/Banner.js";
28
28
  import { PlanOverlay } from "./components/PlanOverlay.js";
29
29
  import { ModelSelector } from "./components/ModelSelector.js";
30
- import { GoalOverlay } from "./components/GoalOverlay.js";
31
30
  import { PixelOverlay } from "./components/PixelOverlay.js";
32
31
  import { SkillsOverlay } from "./components/SkillsOverlay.js";
33
32
  import { ThemeSelector } from "./components/ThemeSelector.js";
@@ -58,7 +57,8 @@ import { extractPlanSteps, findCompletedMarkers, markStepsCompleted, segmentDisp
58
57
  import { getMCPServers } from "../core/mcp/index.js";
59
58
  import { trimFlushedItems, flushOnTurnText, flushOnTurnEnd, flushOverflow, } from "./live-item-flush.js";
60
59
  import { splitAssistantStreamingText } from "./utils/assistant-stream-split.js";
61
- import { appendGoalDecision, appendGoalEvidence, formatGoalBlockingPrerequisites, goalHasBlockingPrerequisites, loadGoalRuns, reconcileActiveGoalRuns, updateGoalTask, upsertGoalRun, } from "../core/goal-store.js";
60
+ import { appendGoalDecision, appendGoalEvidence, formatGoalBlockingPrerequisites, goalHasBlockingPrerequisites, loadGoalRuns, loadGoalRunsSync, reconcileActiveGoalRuns, saveGoalRunsSync, updateGoalTask, upsertGoalRun, } from "../core/goal-store.js";
61
+ import { getNextPendingTask, loadTasksSync, markTaskInProgress, saveTasksSync, } from "../core/tasks-store.js";
62
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";
@@ -67,8 +67,8 @@ import { formatGoalVerifierCompletionEvent, formatGoalWorkerCompletionEvent, isG
67
67
  import { buildUserContentWithAttachments, isGoalPromptCommandName, routePromptCommandInput, runGoalPromptSetupSequence, } from "./prompt-routing.js";
68
68
  import { getNextThinkingLevel, isThinkingLevelSupported } from "./thinking-level.js";
69
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";
70
+ import { getChatControlsLayoutDecision, getDoneFlushDecision, getGoalSetupPaneTransitionAfterRun, isAgentSpacingItem, MIN_LIVE_AREA_ROWS, nextGoalModeAfterAgentDone, shouldTopSpaceAfterPrintedAgentBoundary, shouldTopSpaceAssistantAfterToolBoundary, shouldTopSpaceStreamingAssistant, } from "./layout-decisions.js";
71
+ import { compactHistory, getNextGeneratedItemId, isActiveItem, isSameAssistantText, normalizeAssistantText, partitionCompleted, pinStreamingTextBeforeToolBoundary, removeItemsWithIds, uniqueItemsById, } from "./item-helpers.js";
72
72
  export { buildGoalSetupPromptFromPlanner, buildUserContentWithAttachments, collectAssistantTextSince, isGoalPromptCommandName, routePromptCommandInput, runGoalPromptSetupSequence, } from "./prompt-routing.js";
73
73
  export { getNextThinkingLevel } from "./thinking-level.js";
74
74
  export { appendGoalProgressDraft, completedItemsWithDurableGoalTerminalProgress, formatGoalTerminalProgress, getGoalContinuationChoiceKey, routeGoalSyntheticEvent, truncateGoalProgressText, } from "./goal-progress.js";
@@ -261,7 +261,11 @@ export function App(props) {
261
261
  // Items from the current/last turn — rendered in the live area so they stay visible.
262
262
  // Seed from sessionStore so Goal progress/completion rows and other live output
263
263
  // survive pane/overlay/resize remounts before they are finalized.
264
- const [liveItems, setLiveItems] = useState(() => props.sessionStore?.liveItems ?? []);
264
+ const [liveItems, setLiveItems] = useState(() => {
265
+ const restoredLiveItems = uniqueItemsById(props.sessionStore?.liveItems ?? []);
266
+ const restoredHistoryIds = new Set(history.map((item) => item.id));
267
+ return removeItemsWithIds(restoredLiveItems, restoredHistoryIds);
268
+ });
265
269
  // overlay seeded from sessionStore (lives across remount). Falls back to
266
270
  // props.initialOverlay (CLI launched with one), then null.
267
271
  const [overlay, setOverlay] = useState(props.sessionStore?.overlay ?? props.initialOverlay ?? null);
@@ -274,6 +278,13 @@ export function App(props) {
274
278
  const goalContinuationFlightsRef = useRef(new Set());
275
279
  const goalContinuationRecentChoicesRef = useRef(new Map());
276
280
  const startGoalRunRef = useRef(() => { });
281
+ const [runAllTasks, setRunAllTasks] = useState(props.sessionStore?.runAllTasks ?? false);
282
+ const [taskPickerOpen, setTaskPickerOpen] = useState(false);
283
+ const [taskPickerTasks, setTaskPickerTasks] = useState(() => loadTasksSync(props.cwd));
284
+ const [goalPickerOpen, setGoalPickerOpen] = useState(false);
285
+ const [goalPickerGoals, setGoalPickerGoals] = useState(() => loadGoalRunsSync(props.cwd));
286
+ const runAllTasksRef = useRef(props.sessionStore?.runAllTasks ?? false);
287
+ const startTaskRef = useRef(() => { });
277
288
  const runAllPixelRef = useRef(props.sessionStore?.runAllPixel ?? false);
278
289
  const currentPixelFixRef = useRef(null);
279
290
  const startPixelFixRef = useRef(() => { });
@@ -412,7 +423,7 @@ export function App(props) {
412
423
  pendingHistoryFlushRef.current = [...pendingHistoryFlushRef.current, ...flushed];
413
424
  if (sessionStore) {
414
425
  const queuedIds = new Set(items.map((item) => item.id));
415
- sessionStore.liveItems = (sessionStore.liveItems ?? []).filter((item) => !queuedIds.has(item.id));
426
+ sessionStore.liveItems = removeItemsWithIds(uniqueItemsById(sessionStore.liveItems ?? []), queuedIds);
416
427
  }
417
428
  setHistoryFlushGeneration((generation) => generation + 1);
418
429
  }, [sessionStore]);
@@ -477,8 +488,10 @@ export function App(props) {
477
488
  sessionStore.history = history;
478
489
  }, [history, sessionStore]);
479
490
  useEffect(() => {
480
- if (sessionStore)
481
- sessionStore.liveItems = liveItems;
491
+ if (!sessionStore)
492
+ return;
493
+ const historyIds = new Set(historyRef.current.map((item) => item.id));
494
+ sessionStore.liveItems = removeItemsWithIds(uniqueItemsById(liveItems), historyIds);
482
495
  }, [liveItems, sessionStore]);
483
496
  useEffect(() => {
484
497
  if (sessionStore)
@@ -1528,6 +1541,21 @@ export function App(props) {
1528
1541
  if (nextGoalMode !== goalModeStateRef.current) {
1529
1542
  void setGoalModeAndPrompt(nextGoalMode);
1530
1543
  }
1544
+ // Run-all: auto-start next pending task after a short delay.
1545
+ if (runAllTasksRef.current) {
1546
+ setTimeout(() => {
1547
+ const cwd = cwdRef.current;
1548
+ const next = getNextPendingTask(cwd);
1549
+ if (next) {
1550
+ markTaskInProgress(cwd, next.id);
1551
+ startTaskRef.current(next.title, next.prompt, next.id);
1552
+ }
1553
+ else {
1554
+ setRunAllTasks(false);
1555
+ log("INFO", "tasks", "Run-all complete — no more pending tasks");
1556
+ }
1557
+ }, 500);
1558
+ }
1531
1559
  // Goal loop: after the orchestrator handles a worker/verifier event,
1532
1560
  // continue the same Goal automatically until it reaches a terminal state.
1533
1561
  for (const runId of [...runningGoalIdsRef.current]) {
@@ -1949,26 +1977,11 @@ export function App(props) {
1949
1977
  ]);
1950
1978
  return;
1951
1979
  }
1952
- // Handle /goals — open goal pane
1980
+ // Handle /goals — open the input-area goal picker.
1953
1981
  if (trimmed === "/goals") {
1954
- if (props.resetUI && props.sessionStore && !agentLoop.isRunning) {
1955
- props.sessionStore.overlay = "goal";
1956
- props.sessionStore.planAutoExpand = false;
1957
- props.sessionStore.goalAutoExpand = false;
1958
- props.resetUI();
1959
- }
1960
- else {
1961
- if (props.sessionStore) {
1962
- props.sessionStore.overlay = "goal";
1963
- props.sessionStore.planAutoExpand = false;
1964
- props.sessionStore.goalAutoExpand = false;
1965
- if (agentLoop.isRunning)
1966
- props.sessionStore.pendingResetUI = true;
1967
- }
1968
- setPlanAutoExpand(false);
1969
- setGoalAutoExpand(false);
1970
- setOverlay("goal");
1971
- }
1982
+ setGoalPickerGoals(loadGoalRunsSync(displayedCwd));
1983
+ setTaskPickerOpen(false);
1984
+ setGoalPickerOpen(true);
1972
1985
  return;
1973
1986
  }
1974
1987
  // Handle prompt-template commands (built-in + custom from .gg/commands/)
@@ -2045,26 +2058,11 @@ export function App(props) {
2045
2058
  await setGoalModeAndPrompt("off");
2046
2059
  }
2047
2060
  if (paneTransition) {
2048
- goalAutoExpandRef.current = paneTransition.goalAutoExpand;
2049
- setTimeout(() => {
2050
- const resetUI = props.resetUI;
2051
- const sessionStore = props.sessionStore;
2052
- if (shouldResetUIForGoalSetupPaneTransition({
2053
- hasResetUI: resetUI !== undefined,
2054
- hasSessionStore: sessionStore !== undefined,
2055
- }) &&
2056
- resetUI &&
2057
- sessionStore) {
2058
- sessionStore.overlay = paneTransition.overlay;
2059
- sessionStore.goalAutoExpand = paneTransition.goalAutoExpand;
2060
- sessionStore.planAutoExpand = paneTransition.planAutoExpand;
2061
- resetUI();
2062
- return;
2063
- }
2064
- setGoalAutoExpand(paneTransition.goalAutoExpand);
2065
- setPlanAutoExpand(paneTransition.planAutoExpand);
2066
- setOverlay(paneTransition.overlay);
2067
- }, 300);
2061
+ goalAutoExpandRef.current = false;
2062
+ setGoalAutoExpand(false);
2063
+ setPlanAutoExpand(false);
2064
+ setGoalPickerGoals(loadGoalRunsSync(displayedCwd));
2065
+ setGoalPickerOpen(true);
2068
2066
  }
2069
2067
  }
2070
2068
  }
@@ -2415,6 +2413,8 @@ export function App(props) {
2415
2413
  return renderStatusMessage(item.id, `${BLACK_CIRCLE} `, normalizeStatusText(item.text), theme.commandColor, { bold: true });
2416
2414
  case "goal_agent_transition":
2417
2415
  return renderStatusMessage(item.id, `${BLACK_CIRCLE} `, normalizeStatusText(item.text), theme.commandColor, { bold: true });
2416
+ case "task":
2417
+ return withPrintedBoundarySpacing(renderStatusMessage(item.id, "▸ ", _jsxs(_Fragment, { children: [_jsx(Text, { color: theme.textDim, children: "Task: " }), _jsx(Text, { color: theme.commandColor, bold: true, children: item.title })] }), theme.commandColor, { bold: true }));
2418
2418
  case "model_transition":
2419
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 });
2420
2420
  case "theme_transition":
@@ -2479,18 +2479,6 @@ export function App(props) {
2479
2479
  setOverlay(kind);
2480
2480
  }
2481
2481
  }, [agentLoop.isRunning, props]);
2482
- const closeOverlay = useCallback(() => {
2483
- if (props.resetUI && props.sessionStore && !agentLoop.isRunning) {
2484
- props.sessionStore.overlay = null;
2485
- props.resetUI();
2486
- }
2487
- else {
2488
- if (props.sessionStore) {
2489
- props.sessionStore.overlay = null;
2490
- }
2491
- setOverlay(null);
2492
- }
2493
- }, [agentLoop.isRunning, overlay, props]);
2494
2482
  const runGoalSyntheticEvent = useCallback((eventText) => {
2495
2483
  const eventInfo = parseGoalSyntheticEvent(eventText);
2496
2484
  const detail = eventInfo?.kind === "worker"
@@ -3065,8 +3053,71 @@ export function App(props) {
3065
3053
  setLiveItems((prev) => [...prev, toErrorItem(err, getId(), "Goal")]);
3066
3054
  });
3067
3055
  }, [appendGoalProgress, clearGoalModeIfIdle, clearGoalStatusEntry, props.cwd]);
3056
+ const startTask = useCallback((title, prompt, taskId) => {
3057
+ const taskCwd = cwdRef.current;
3058
+ const shortId = taskId.slice(0, 8);
3059
+ const completionHint = `\n\n---\nWhen you have fully completed this task, call the tasks tool to mark it done:\n` +
3060
+ `tasks({ action: "done", id: "${shortId}" })`;
3061
+ const fullPrompt = prompt + completionHint;
3062
+ if (props.resetUI && props.sessionStore) {
3063
+ const sysMsg = messagesRef.current[0];
3064
+ const newMessages = sysMsg && sysMsg.role === "system" ? [sysMsg] : messagesRef.current.slice(0, 1);
3065
+ const taskItem = { kind: "task", title, id: getId() };
3066
+ const sm = sessionManagerRef.current;
3067
+ void (async () => {
3068
+ let newSessionPath;
3069
+ if (sm) {
3070
+ try {
3071
+ const session = await sm.create(taskCwd, currentProvider, currentModel);
3072
+ newSessionPath = session.path;
3073
+ log("INFO", "tasks", "New session for task", { path: session.path });
3074
+ }
3075
+ catch {
3076
+ // Session creation is best-effort.
3077
+ }
3078
+ }
3079
+ if (props.sessionStore)
3080
+ props.sessionStore.overlay = null;
3081
+ props.resetUI?.({
3082
+ wipeSession: true,
3083
+ messages: newMessages,
3084
+ history: [{ kind: "banner", id: "banner" }, taskItem],
3085
+ sessionPath: newSessionPath,
3086
+ pendingAction: { prompt: fullPrompt },
3087
+ });
3088
+ })();
3089
+ return;
3090
+ }
3091
+ pendingHistoryFlushRef.current = [];
3092
+ props.terminalHistoryPrinter?.clear();
3093
+ setHistory([{ kind: "banner", id: "banner" }]);
3094
+ setLiveItems([]);
3095
+ messagesRef.current = messagesRef.current.slice(0, 1);
3096
+ agentLoop.reset();
3097
+ persistedIndexRef.current = messagesRef.current.length;
3098
+ const sm = sessionManagerRef.current;
3099
+ if (sm) {
3100
+ void sm.create(taskCwd, currentProvider, currentModel).then((session) => {
3101
+ sessionPathRef.current = session.path;
3102
+ log("INFO", "tasks", "New session for task", { path: session.path });
3103
+ });
3104
+ }
3105
+ const taskItem = { kind: "task", title, id: getId() };
3106
+ setLastUserMessage(title);
3107
+ setDoneStatus(null);
3108
+ setLiveItems([taskItem]);
3109
+ void agentLoop.run(fullPrompt).catch((err) => {
3110
+ setLiveItems((prev) => [...prev, toErrorItem(err, getId())]);
3111
+ });
3112
+ }, [agentLoop, currentModel, currentProvider, props]);
3068
3113
  // Keep refs in sync for access from stale closures (onDone)
3114
+ startTaskRef.current = startTask;
3069
3115
  startGoalRunRef.current = startGoalRun;
3116
+ useEffect(() => {
3117
+ runAllTasksRef.current = runAllTasks;
3118
+ if (props.sessionStore)
3119
+ props.sessionStore.runAllTasks = runAllTasks;
3120
+ }, [runAllTasks, props.sessionStore]);
3070
3121
  useEffect(() => {
3071
3122
  agentRunningRef.current = agentLoop.isRunning;
3072
3123
  }, [agentLoop.isRunning]);
@@ -3166,7 +3217,6 @@ export function App(props) {
3166
3217
  if (props.sessionStore)
3167
3218
  props.sessionStore.runAllPixel = runAllPixel;
3168
3219
  }, [runAllPixel, props.sessionStore]);
3169
- const isGoalView = overlay === "goal";
3170
3220
  const isSkillsView = overlay === "skills";
3171
3221
  const isPlanView = overlay === "plan";
3172
3222
  const footerStatusLayout = getFooterStatusLayoutDecision({
@@ -3278,71 +3328,7 @@ export function App(props) {
3278
3328
  lastPendingHistoryItem,
3279
3329
  lastHistoryItem,
3280
3330
  });
3281
- return (_jsx(Box, { flexDirection: "column", width: columns, flexShrink: 0, flexGrow: 0, children: isGoalView ? (_jsx(GoalOverlay, { cwd: props.cwd, agentRunning: agentLoop.isRunning, autoExpandNewest: goalAutoExpand, onClose: () => {
3282
- goalAutoExpandRef.current = false;
3283
- setGoalAutoExpand(false);
3284
- if (props.sessionStore)
3285
- props.sessionStore.goalAutoExpand = false;
3286
- closeOverlay();
3287
- }, onRunGoal: (run) => {
3288
- const paneTransition = getGoalActivationPaneTransition();
3289
- goalAutoExpandRef.current = paneTransition.goalAutoExpand;
3290
- setGoalAutoExpand(paneTransition.goalAutoExpand);
3291
- setPlanAutoExpand(paneTransition.planAutoExpand);
3292
- if (props.sessionStore) {
3293
- props.sessionStore.overlay = paneTransition.overlay;
3294
- props.sessionStore.goalAutoExpand = paneTransition.goalAutoExpand;
3295
- props.sessionStore.planAutoExpand = paneTransition.planAutoExpand;
3296
- }
3297
- if (paneTransition.resetReviewScreen && props.resetUI && props.sessionStore) {
3298
- props.sessionStore.pendingGoalRun = run;
3299
- props.resetUI();
3300
- return;
3301
- }
3302
- setOverlay(paneTransition.overlay);
3303
- startGoalRun(run);
3304
- }, onVerifyGoal: (run) => {
3305
- void verifyGoalRun(run);
3306
- }, onPauseGoal: (run) => {
3307
- pauseGoalRun(run);
3308
- }, onRefineGoal: (run, feedback) => {
3309
- goalAutoExpandRef.current = true;
3310
- setGoalAutoExpand(true);
3311
- void (async () => {
3312
- try {
3313
- await setGoalModeAndPrompt("setup");
3314
- await agentLoop.run(`Refine Goal run ${run.id} (${run.title}) based on this user feedback. Update durable Goal setup only, then stop and reopen the Goal pane for review.\n\nFeedback: ${feedback}`);
3315
- }
3316
- catch (err) {
3317
- log("ERROR", "goal", err instanceof Error ? err.message : String(err));
3318
- setLiveItems((prev) => [...prev, toErrorItem(err, getId(), "Goal")]);
3319
- }
3320
- finally {
3321
- await setGoalModeAndPrompt("off");
3322
- const paneTransition = getGoalSetupFinishedPaneTransition();
3323
- goalAutoExpandRef.current = paneTransition.goalAutoExpand;
3324
- setTimeout(() => {
3325
- const resetUI = props.resetUI;
3326
- const sessionStore = props.sessionStore;
3327
- if (shouldResetUIForGoalSetupPaneTransition({
3328
- hasResetUI: resetUI !== undefined,
3329
- hasSessionStore: sessionStore !== undefined,
3330
- }) &&
3331
- resetUI &&
3332
- sessionStore) {
3333
- sessionStore.overlay = paneTransition.overlay;
3334
- sessionStore.goalAutoExpand = paneTransition.goalAutoExpand;
3335
- sessionStore.planAutoExpand = paneTransition.planAutoExpand;
3336
- resetUI();
3337
- return;
3338
- }
3339
- setGoalAutoExpand(paneTransition.goalAutoExpand);
3340
- setPlanAutoExpand(paneTransition.planAutoExpand);
3341
- setOverlay(paneTransition.overlay);
3342
- }, 300);
3343
- }
3344
- })();
3345
- } })) : isPixelView ? (_jsx(PixelOverlay, { version: props.version, agentRunning: agentLoop.isRunning, onClose: () => {
3331
+ return (_jsx(Box, { flexDirection: "column", width: columns, flexShrink: 0, flexGrow: 0, children: isPixelView ? (_jsx(PixelOverlay, { version: props.version, agentRunning: agentLoop.isRunning, onClose: () => {
3346
3332
  if (props.resetUI && props.sessionStore && !agentLoop.isRunning) {
3347
3333
  props.sessionStore.overlay = null;
3348
3334
  props.resetUI();
@@ -3496,8 +3482,48 @@ export function App(props) {
3496
3482
  log("ERROR", "error", errMsg);
3497
3483
  setLiveItems((prev) => [...prev, toErrorItem(err, getId())]);
3498
3484
  });
3499
- } })) : (_jsxs(Box, { flexDirection: "column", width: columns, flexShrink: 0, flexGrow: 0, children: [_jsxs(Box, { flexDirection: "column", flexGrow: 0, flexShrink: 1, overflowY: "hidden", children: [liveItems.map((item, index, items) => renderItem(item, index, items)), _jsx(StreamingArea, { isRunning: agentLoop.isRunning, streamingText: visibleStreamingText, streamingThinking: agentLoop.streamingThinking, thinkingMs: agentLoop.thinkingMs, reserveSpacing: shouldReserveStreamingSpacing, renderMarkdown: renderMarkdown, availableTerminalHeight: measuredLiveAreaRows, assistantMarginTop: shouldTopSpaceStreamingText ? 1 : 0, continuation: streamedAssistantFlushRef.current.flushedChars > 0 })] }), _jsxs(Box, { ref: mainControlsRef, flexDirection: "column", flexShrink: 0, flexGrow: 0, children: [hiddenQueuedCount > 0 && (_jsxs(Box, { flexDirection: "row", paddingLeft: 1, marginTop: shouldTopSpaceQueueIndicator ? 2 : 1, flexShrink: 0, children: [_jsx(Box, { width: 2, flexShrink: 0, children: _jsx(Text, { color: theme.warning, bold: true, children: "• " }) }), _jsxs(Text, { color: theme.textDim, children: [hiddenQueuedCount, " message", hiddenQueuedCount > 1 ? "s" : "", " queued"] })] })), _jsxs(Box, { flexDirection: "column", width: columns, children: [_jsx(Box, { borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: theme.textDim, width: columns, height: 0 }), _jsx(Box, { paddingLeft: 1, paddingRight: 1, width: columns, children: statusSlotVisible ? (activityVisible ? (_jsx(ActivityIndicator, { phase: agentLoop.activityPhase, elapsedMs: agentLoop.elapsedMs, runStartRef: agentLoop.runStartRef, thinkingMs: agentLoop.thinkingMs, isThinking: agentLoop.isThinking, thinkingEnabled: !!thinkingLevel, tokenEstimate: agentLoop.streamedTokenEstimate, charCountRef: agentLoop.charCountRef, realTokensAccumRef: agentLoop.realTokensAccumRef, userMessage: lastUserMessage, activeToolNames: agentLoop.activeToolCalls.map((tc) => tc.name), retryInfo: agentLoop.retryInfo, planDone: planSteps.filter((s) => s.completed).length, planTotal: planSteps.length, staticDisplay: true })) : stallStatusVisible ? (_jsx(Text, { color: theme.warning, wrap: "truncate", children: "⚠ API provider stream interrupted — retries exhausted. Your conversation is preserved." })) : doneStatus ? (_jsxs(Text, { color: theme.success, children: ["✻ ", doneStatus.verb, " ", formatDuration(doneStatus.durationMs)] })) : (_jsxs(Text, { children: [_jsx(Text, { color: theme.commandColor, children: "⠿ " }), _jsx(Text, { color: theme.textDim, children: "Ready to go.." }), !renderMarkdown && (_jsx(Text, { color: theme.warning, children: " · raw markdown mode" }))] }))) : (_jsxs(Text, { children: [_jsx(Text, { color: theme.commandColor, children: "⠿ " }), _jsx(Text, { color: theme.textDim, children: "Ready to go.." })] })) })] }), _jsx(InputArea, { onSubmit: handleSubmit, onAbort: handleAbort, disabled: agentLoop.isRunning, isActive: !taskBarFocused && !overlay, onDownAtEnd: handleFocusTaskBar, onShiftTab: handleToggleThinking, onToggleGoal: () => {
3500
- openOverlay("goal");
3485
+ } })) : (_jsxs(Box, { flexDirection: "column", width: columns, flexShrink: 0, flexGrow: 0, children: [_jsxs(Box, { flexDirection: "column", flexGrow: 0, flexShrink: 1, overflowY: "hidden", children: [uniqueItemsById(liveItems).map((item, index, items) => renderItem(item, index, items)), _jsx(StreamingArea, { isRunning: agentLoop.isRunning, streamingText: visibleStreamingText, streamingThinking: agentLoop.streamingThinking, thinkingMs: agentLoop.thinkingMs, reserveSpacing: shouldReserveStreamingSpacing, renderMarkdown: renderMarkdown, availableTerminalHeight: measuredLiveAreaRows, assistantMarginTop: shouldTopSpaceStreamingText ? 1 : 0, continuation: streamedAssistantFlushRef.current.flushedChars > 0 })] }), _jsxs(Box, { ref: mainControlsRef, flexDirection: "column", flexShrink: 0, flexGrow: 0, children: [hiddenQueuedCount > 0 && (_jsxs(Box, { flexDirection: "row", paddingLeft: 1, marginTop: shouldTopSpaceQueueIndicator ? 2 : 1, flexShrink: 0, children: [_jsx(Box, { width: 2, flexShrink: 0, children: _jsx(Text, { color: theme.warning, bold: true, children: "• " }) }), _jsxs(Text, { color: theme.textDim, children: [hiddenQueuedCount, " message", hiddenQueuedCount > 1 ? "s" : "", " queued"] })] })), _jsxs(Box, { flexDirection: "column", width: columns, children: [_jsx(Box, { borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: theme.textDim, width: columns, height: 0 }), _jsx(Box, { paddingLeft: 1, paddingRight: 1, width: columns, children: statusSlotVisible ? (activityVisible ? (_jsx(ActivityIndicator, { phase: agentLoop.activityPhase, elapsedMs: agentLoop.elapsedMs, runStartRef: agentLoop.runStartRef, thinkingMs: agentLoop.thinkingMs, isThinking: agentLoop.isThinking, thinkingEnabled: !!thinkingLevel, tokenEstimate: agentLoop.streamedTokenEstimate, charCountRef: agentLoop.charCountRef, realTokensAccumRef: agentLoop.realTokensAccumRef, userMessage: lastUserMessage, activeToolNames: agentLoop.activeToolCalls.map((tc) => tc.name), retryInfo: agentLoop.retryInfo, planDone: planSteps.filter((s) => s.completed).length, planTotal: planSteps.length, staticDisplay: true })) : stallStatusVisible ? (_jsx(Text, { color: theme.warning, wrap: "truncate", children: "⚠ API provider stream interrupted — retries exhausted. Your conversation is preserved." })) : doneStatus ? (_jsxs(Text, { color: theme.success, children: ["✻ ", doneStatus.verb, " ", formatDuration(doneStatus.durationMs)] })) : (_jsxs(Text, { children: [_jsx(Text, { color: theme.commandColor, children: "⠿ " }), _jsx(Text, { color: theme.textDim, children: "Ready to go.." }), !renderMarkdown && (_jsx(Text, { color: theme.warning, children: " · raw markdown mode" }))] }))) : (_jsxs(Text, { children: [_jsx(Text, { color: theme.commandColor, children: "⠿ " }), _jsx(Text, { color: theme.textDim, children: "Ready to go.." })] })) })] }), _jsx(InputArea, { onSubmit: handleSubmit, onAbort: handleAbort, disabled: agentLoop.isRunning, isActive: !taskBarFocused && !overlay, onDownAtEnd: handleFocusTaskBar, onShiftTab: handleToggleThinking, onToggleTasks: () => {
3486
+ setGoalPickerOpen(false);
3487
+ setTaskPickerTasks(loadTasksSync(displayedCwd));
3488
+ setTaskPickerOpen((open) => !open);
3489
+ }, taskPickerOpen: taskPickerOpen, tasks: taskPickerTasks, onCloseTaskPicker: () => setTaskPickerOpen(false), onStartTask: (task) => {
3490
+ setTaskPickerOpen(false);
3491
+ markTaskInProgress(displayedCwd, task.id);
3492
+ setTaskPickerTasks(loadTasksSync(displayedCwd));
3493
+ startTask(task.title, task.prompt, task.id);
3494
+ }, onRunAllTasks: (task) => {
3495
+ setTaskPickerOpen(false);
3496
+ setRunAllTasks(true);
3497
+ const selected = task
3498
+ ? {
3499
+ id: task.id,
3500
+ title: task.title,
3501
+ prompt: task.prompt || task.text || task.title,
3502
+ }
3503
+ : getNextPendingTask(displayedCwd);
3504
+ if (selected) {
3505
+ markTaskInProgress(displayedCwd, selected.id);
3506
+ setTaskPickerTasks(loadTasksSync(displayedCwd));
3507
+ startTask(selected.title, selected.prompt, selected.id);
3508
+ }
3509
+ }, onDeleteTask: (task) => {
3510
+ const nextTasks = loadTasksSync(displayedCwd).filter((candidate) => candidate.id !== task.id);
3511
+ saveTasksSync(displayedCwd, nextTasks);
3512
+ setTaskPickerTasks(nextTasks);
3513
+ }, goalPickerOpen: goalPickerOpen, goals: goalPickerGoals, onCloseGoalPicker: () => setGoalPickerOpen(false), onRunGoal: (run) => {
3514
+ setGoalPickerOpen(false);
3515
+ startGoalRun(run);
3516
+ }, onDeleteGoal: (run) => {
3517
+ const nextGoals = loadGoalRunsSync(displayedCwd).filter((candidate) => candidate.id !== run.id);
3518
+ saveGoalRunsSync(displayedCwd, nextGoals);
3519
+ setGoalPickerGoals(nextGoals);
3520
+ }, onPauseGoal: (run) => {
3521
+ setGoalPickerOpen(false);
3522
+ pauseGoalRun(run);
3523
+ }, onToggleGoal: () => {
3524
+ setTaskPickerOpen(false);
3525
+ setGoalPickerGoals(loadGoalRunsSync(displayedCwd));
3526
+ setGoalPickerOpen((open) => !open);
3501
3527
  }, onToggleSkills: () => {
3502
3528
  openOverlay("skills");
3503
3529
  }, onTogglePixel: () => {