@kenkaiiii/ggcoder 4.3.219 → 4.3.220
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.d.ts.map +1 -1
- package/dist/cli.js +39 -20
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-session-compaction.test.js +0 -7
- package/dist/core/agent-session-compaction.test.js.map +1 -1
- package/dist/core/agent-session.d.ts +0 -23
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +4 -98
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/continue-replay-inventory.test.js +1 -1
- package/dist/core/continue-replay-inventory.test.js.map +1 -1
- package/dist/core/goal-controller.d.ts +2 -0
- package/dist/core/goal-controller.d.ts.map +1 -1
- package/dist/core/goal-controller.js +152 -8
- package/dist/core/goal-controller.js.map +1 -1
- package/dist/core/goal-controller.test.js +232 -3
- package/dist/core/goal-controller.test.js.map +1 -1
- package/dist/core/goal-overhead-harness.d.ts +33 -0
- package/dist/core/goal-overhead-harness.d.ts.map +1 -0
- package/dist/core/goal-overhead-harness.js +268 -0
- package/dist/core/goal-overhead-harness.js.map +1 -0
- package/dist/core/goal-store.d.ts +1 -0
- package/dist/core/goal-store.d.ts.map +1 -1
- package/dist/core/goal-store.js +43 -3
- package/dist/core/goal-store.js.map +1 -1
- package/dist/core/goal-store.test.js +13 -0
- package/dist/core/goal-store.test.js.map +1 -1
- package/dist/core/goal-worker.d.ts.map +1 -1
- package/dist/core/goal-worker.js +17 -11
- package/dist/core/goal-worker.js.map +1 -1
- package/dist/core/goal-worker.test.js +41 -8
- package/dist/core/goal-worker.test.js.map +1 -1
- package/dist/core/goal-worktree.d.ts +21 -0
- package/dist/core/goal-worktree.d.ts.map +1 -1
- package/dist/core/goal-worktree.js +71 -1
- package/dist/core/goal-worktree.js.map +1 -1
- package/dist/core/goal-worktree.test.js +127 -2
- package/dist/core/goal-worktree.test.js.map +1 -1
- package/dist/core/prompt-commands.js +2 -2
- package/dist/core/prompt-commands.test.js +1 -1
- package/dist/core/prompt-commands.test.js.map +1 -1
- package/dist/core/runtime-mode.d.ts +7 -0
- package/dist/core/runtime-mode.d.ts.map +1 -1
- package/dist/core/runtime-mode.js +6 -0
- package/dist/core/runtime-mode.js.map +1 -1
- package/dist/core/session-manager.d.ts +3 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +16 -0
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-restore-display.test.js +84 -0
- package/dist/core/session-restore-display.test.js.map +1 -1
- package/dist/core/slash-commands.d.ts +0 -2
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +0 -15
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/system-prompt.d.ts +1 -1
- package/dist/system-prompt.d.ts.map +1 -1
- package/dist/system-prompt.js +13 -1
- package/dist/system-prompt.js.map +1 -1
- package/dist/system-prompt.test.js +2 -2
- package/dist/system-prompt.test.js.map +1 -1
- package/dist/tools/bash.d.ts +2 -0
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +5 -2
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit.d.ts +2 -0
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +16 -3
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/enter-plan.d.ts +8 -0
- package/dist/tools/enter-plan.d.ts.map +1 -0
- package/dist/tools/enter-plan.js +27 -0
- package/dist/tools/enter-plan.js.map +1 -0
- package/dist/tools/exit-plan.d.ts +8 -0
- package/dist/tools/exit-plan.d.ts.map +1 -0
- package/dist/tools/exit-plan.js +35 -0
- package/dist/tools/exit-plan.js.map +1 -0
- package/dist/tools/goals.d.ts +8 -5
- package/dist/tools/goals.d.ts.map +1 -1
- package/dist/tools/goals.js +124 -49
- package/dist/tools/goals.js.map +1 -1
- package/dist/tools/goals.test.js +356 -11
- package/dist/tools/goals.test.js.map +1 -1
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +15 -4
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/plan-mode.test.js +63 -24
- package/dist/tools/plan-mode.test.js.map +1 -1
- package/dist/tools/prompt-hints.d.ts.map +1 -1
- package/dist/tools/prompt-hints.js +4 -0
- package/dist/tools/prompt-hints.js.map +1 -1
- package/dist/tools/subagent.d.ts +3 -1
- package/dist/tools/subagent.d.ts.map +1 -1
- package/dist/tools/subagent.js +5 -2
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/write.d.ts +2 -0
- package/dist/tools/write.d.ts.map +1 -1
- package/dist/tools/write.js +23 -3
- package/dist/tools/write.js.map +1 -1
- package/dist/ui/App.d.ts +9 -6
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +572 -925
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/app-items.d.ts +1 -0
- package/dist/ui/app-items.d.ts.map +1 -1
- package/dist/ui/app-items.js +1 -1
- package/dist/ui/app-items.js.map +1 -1
- package/dist/ui/app-state-persistence.test.js +22 -22
- package/dist/ui/app-state-persistence.test.js.map +1 -1
- package/dist/ui/components/AssistantMessage.test.js +7 -7
- package/dist/ui/components/AssistantMessage.test.js.map +1 -1
- package/dist/ui/components/BackgroundTasksBar.d.ts.map +1 -1
- package/dist/ui/components/BackgroundTasksBar.js +6 -6
- package/dist/ui/components/BackgroundTasksBar.js.map +1 -1
- package/dist/ui/components/ChatFooterPane.d.ts +29 -0
- package/dist/ui/components/ChatFooterPane.d.ts.map +1 -0
- package/dist/ui/components/ChatFooterPane.js +16 -0
- package/dist/ui/components/ChatFooterPane.js.map +1 -0
- package/dist/ui/components/ChatInputStack.d.ts +34 -0
- package/dist/ui/components/ChatInputStack.d.ts.map +1 -0
- package/dist/ui/components/ChatInputStack.js +9 -0
- package/dist/ui/components/ChatInputStack.js.map +1 -0
- package/dist/ui/components/ChatLayout.d.ts +23 -0
- package/dist/ui/components/ChatLayout.d.ts.map +1 -0
- package/dist/ui/components/ChatLayout.js +16 -0
- package/dist/ui/components/ChatLayout.js.map +1 -0
- package/dist/ui/components/ChatLivePane.d.ts +18 -0
- package/dist/ui/components/ChatLivePane.d.ts.map +1 -0
- package/dist/ui/components/ChatLivePane.js +8 -0
- package/dist/ui/components/ChatLivePane.js.map +1 -0
- package/dist/ui/components/ChatScreen.d.ts +118 -0
- package/dist/ui/components/ChatScreen.d.ts.map +1 -0
- package/dist/ui/components/ChatScreen.js +14 -0
- package/dist/ui/components/ChatScreen.js.map +1 -0
- package/dist/ui/components/ChatStatusRow.d.ts +34 -0
- package/dist/ui/components/ChatStatusRow.d.ts.map +1 -0
- package/dist/ui/components/ChatStatusRow.js +11 -0
- package/dist/ui/components/ChatStatusRow.js.map +1 -0
- package/dist/ui/components/CompactionNotice.d.ts +4 -2
- package/dist/ui/components/CompactionNotice.d.ts.map +1 -1
- package/dist/ui/components/CompactionNotice.js +4 -4
- package/dist/ui/components/CompactionNotice.js.map +1 -1
- package/dist/ui/components/Footer.d.ts +6 -3
- package/dist/ui/components/Footer.d.ts.map +1 -1
- package/dist/ui/components/Footer.js +14 -4
- package/dist/ui/components/Footer.js.map +1 -1
- package/dist/ui/components/FooterStatusRow.d.ts +20 -0
- package/dist/ui/components/FooterStatusRow.d.ts.map +1 -0
- package/dist/ui/components/FooterStatusRow.js +10 -0
- package/dist/ui/components/FooterStatusRow.js.map +1 -0
- package/dist/ui/components/FullScreenOverlayRouter.d.ts +19 -0
- package/dist/ui/components/FullScreenOverlayRouter.d.ts.map +1 -0
- package/dist/ui/components/FullScreenOverlayRouter.js +18 -0
- package/dist/ui/components/FullScreenOverlayRouter.js.map +1 -0
- package/dist/ui/components/GoalOverlay.d.ts +2 -1
- package/dist/ui/components/GoalOverlay.d.ts.map +1 -1
- package/dist/ui/components/GoalOverlay.js +11 -6
- package/dist/ui/components/GoalOverlay.js.map +1 -1
- package/dist/ui/components/GoalStatusBar.d.ts +2 -0
- package/dist/ui/components/GoalStatusBar.d.ts.map +1 -1
- package/dist/ui/components/GoalStatusBar.js +27 -11
- package/dist/ui/components/GoalStatusBar.js.map +1 -1
- package/dist/ui/components/GoalStatusBar.test.d.ts +2 -0
- package/dist/ui/components/GoalStatusBar.test.d.ts.map +1 -0
- package/dist/ui/components/GoalStatusBar.test.js +17 -0
- package/dist/ui/components/GoalStatusBar.test.js.map +1 -0
- package/dist/ui/components/InputArea.d.ts.map +1 -1
- package/dist/ui/components/InputArea.js +6 -5
- package/dist/ui/components/InputArea.js.map +1 -1
- package/dist/ui/components/PlanOverlay.d.ts +7 -0
- package/dist/ui/components/PlanOverlay.d.ts.map +1 -1
- package/dist/ui/components/PlanOverlay.js +16 -2
- package/dist/ui/components/PlanOverlay.js.map +1 -1
- package/dist/ui/components/PlanOverlay.test.d.ts +2 -0
- package/dist/ui/components/PlanOverlay.test.d.ts.map +1 -0
- package/dist/ui/components/PlanOverlay.test.js +24 -0
- package/dist/ui/components/PlanOverlay.test.js.map +1 -0
- package/dist/ui/components/QueueIndicator.d.ts +9 -0
- package/dist/ui/components/QueueIndicator.d.ts.map +1 -0
- package/dist/ui/components/QueueIndicator.js +9 -0
- package/dist/ui/components/QueueIndicator.js.map +1 -0
- package/dist/ui/components/ServerToolExecution.d.ts +2 -0
- package/dist/ui/components/ServerToolExecution.d.ts.map +1 -1
- package/dist/ui/components/ServerToolExecution.js +3 -2
- package/dist/ui/components/ServerToolExecution.js.map +1 -1
- package/dist/ui/components/StreamingArea.js +1 -1
- package/dist/ui/components/StreamingArea.js.map +1 -1
- package/dist/ui/components/SubAgentPanel.d.ts +2 -1
- package/dist/ui/components/SubAgentPanel.d.ts.map +1 -1
- package/dist/ui/components/SubAgentPanel.js +2 -2
- package/dist/ui/components/SubAgentPanel.js.map +1 -1
- package/dist/ui/components/ToolExecution.d.ts +2 -0
- package/dist/ui/components/ToolExecution.d.ts.map +1 -1
- package/dist/ui/components/ToolExecution.js +13 -11
- package/dist/ui/components/ToolExecution.js.map +1 -1
- package/dist/ui/components/ToolGroupExecution.d.ts +2 -1
- package/dist/ui/components/ToolGroupExecution.d.ts.map +1 -1
- package/dist/ui/components/ToolGroupExecution.js +5 -3
- package/dist/ui/components/ToolGroupExecution.js.map +1 -1
- package/dist/ui/duration-format.d.ts +2 -0
- package/dist/ui/duration-format.d.ts.map +1 -0
- package/dist/ui/duration-format.js +9 -0
- package/dist/ui/duration-format.js.map +1 -0
- package/dist/ui/duration-summary.d.ts +2 -0
- package/dist/ui/duration-summary.d.ts.map +1 -0
- package/dist/ui/duration-summary.js +66 -0
- package/dist/ui/duration-summary.js.map +1 -0
- package/dist/ui/error-item.d.ts +8 -0
- package/dist/ui/error-item.d.ts.map +1 -0
- package/dist/ui/error-item.js +32 -0
- package/dist/ui/error-item.js.map +1 -0
- package/dist/ui/footer-status-layout.test.js +4 -3
- package/dist/ui/footer-status-layout.test.js.map +1 -1
- package/dist/ui/goal-events.d.ts +1 -0
- package/dist/ui/goal-events.d.ts.map +1 -1
- package/dist/ui/goal-events.js +2 -1
- package/dist/ui/goal-events.js.map +1 -1
- package/dist/ui/goal-events.test.js +16 -0
- package/dist/ui/goal-events.test.js.map +1 -1
- package/dist/ui/goal-lifecycle-orchestration.test.js +105 -18
- package/dist/ui/goal-lifecycle-orchestration.test.js.map +1 -1
- package/dist/ui/goal-progress.d.ts +1 -1
- package/dist/ui/goal-progress.d.ts.map +1 -1
- package/dist/ui/goal-progress.js +11 -13
- package/dist/ui/goal-progress.js.map +1 -1
- package/dist/ui/goal-run-helpers.d.ts +16 -0
- package/dist/ui/goal-run-helpers.d.ts.map +1 -0
- package/dist/ui/goal-run-helpers.js +61 -0
- package/dist/ui/goal-run-helpers.js.map +1 -0
- package/dist/ui/goal-status-bar.test.js +8 -6
- package/dist/ui/goal-status-bar.test.js.map +1 -1
- package/dist/ui/hooks/useChatLayoutMeasurements.d.ts +38 -0
- package/dist/ui/hooks/useChatLayoutMeasurements.d.ts.map +1 -0
- package/dist/ui/hooks/useChatLayoutMeasurements.js +71 -0
- package/dist/ui/hooks/useChatLayoutMeasurements.js.map +1 -0
- package/dist/ui/hooks/useGoalPickerController.d.ts +22 -0
- package/dist/ui/hooks/useGoalPickerController.d.ts.map +1 -0
- package/dist/ui/hooks/useGoalPickerController.js +35 -0
- package/dist/ui/hooks/useGoalPickerController.js.map +1 -0
- package/dist/ui/hooks/useTaskPickerController.d.ts +19 -0
- package/dist/ui/hooks/useTaskPickerController.d.ts.map +1 -0
- package/dist/ui/hooks/useTaskPickerController.js +41 -0
- package/dist/ui/hooks/useTaskPickerController.js.map +1 -0
- package/dist/ui/hooks/useTranscriptHistory.d.ts +34 -0
- package/dist/ui/hooks/useTranscriptHistory.d.ts.map +1 -0
- package/dist/ui/hooks/useTranscriptHistory.js +96 -0
- package/dist/ui/hooks/useTranscriptHistory.js.map +1 -0
- package/dist/ui/layout-decisions.d.ts +3 -12
- package/dist/ui/layout-decisions.d.ts.map +1 -1
- package/dist/ui/layout-decisions.js +7 -65
- package/dist/ui/layout-decisions.js.map +1 -1
- package/dist/ui/prompt-routing.d.ts.map +1 -1
- package/dist/ui/prompt-routing.js +36 -2
- package/dist/ui/prompt-routing.js.map +1 -1
- package/dist/ui/prompt-routing.test.d.ts +2 -0
- package/dist/ui/prompt-routing.test.d.ts.map +1 -0
- package/dist/ui/prompt-routing.test.js +48 -0
- package/dist/ui/prompt-routing.test.js.map +1 -0
- package/dist/ui/render.d.ts +9 -6
- package/dist/ui/render.d.ts.map +1 -1
- package/dist/ui/render.js +3 -2
- package/dist/ui/render.js.map +1 -1
- package/dist/ui/slash-command-images.test.js +3 -2
- package/dist/ui/slash-command-images.test.js.map +1 -1
- package/dist/ui/submit-prompt-command.d.ts +40 -0
- package/dist/ui/submit-prompt-command.d.ts.map +1 -0
- package/dist/ui/submit-prompt-command.js +92 -0
- package/dist/ui/submit-prompt-command.js.map +1 -0
- package/dist/ui/submit-slash-commands.d.ts +13 -0
- package/dist/ui/submit-slash-commands.d.ts.map +1 -0
- package/dist/ui/submit-slash-commands.js +36 -0
- package/dist/ui/submit-slash-commands.js.map +1 -0
- package/dist/ui/terminal-history-format.d.ts +1 -0
- package/dist/ui/terminal-history-format.d.ts.map +1 -1
- package/dist/ui/terminal-history-format.js +2 -1
- package/dist/ui/terminal-history-format.js.map +1 -1
- package/dist/ui/terminal-history-spacing.d.ts +1 -2
- package/dist/ui/terminal-history-spacing.d.ts.map +1 -1
- package/dist/ui/terminal-history-spacing.js +1 -28
- package/dist/ui/terminal-history-spacing.js.map +1 -1
- package/dist/ui/terminal-history-status-renderers.d.ts +2 -2
- package/dist/ui/terminal-history-status-renderers.d.ts.map +1 -1
- package/dist/ui/terminal-history-status-renderers.js +36 -17
- package/dist/ui/terminal-history-status-renderers.js.map +1 -1
- package/dist/ui/terminal-history.d.ts.map +1 -1
- package/dist/ui/terminal-history.js +55 -24
- package/dist/ui/terminal-history.js.map +1 -1
- package/dist/ui/terminal-history.test.js +2 -1
- package/dist/ui/terminal-history.test.js.map +1 -1
- package/dist/ui/tool-group-summary.d.ts +2 -2
- package/dist/ui/tool-group-summary.d.ts.map +1 -1
- package/dist/ui/tool-group-summary.js +18 -18
- package/dist/ui/tool-group-summary.js.map +1 -1
- package/dist/ui/transcript/GoalRows.d.ts +10 -0
- package/dist/ui/transcript/GoalRows.d.ts.map +1 -0
- package/dist/ui/transcript/GoalRows.js +35 -0
- package/dist/ui/transcript/GoalRows.js.map +1 -0
- package/dist/ui/transcript/MiscRows.d.ts +23 -0
- package/dist/ui/transcript/MiscRows.d.ts.map +1 -0
- package/dist/ui/transcript/MiscRows.js +41 -0
- package/dist/ui/transcript/MiscRows.js.map +1 -0
- package/dist/ui/transcript/StatusRow.d.ts +14 -0
- package/dist/ui/transcript/StatusRow.d.ts.map +1 -0
- package/dist/ui/transcript/StatusRow.js +14 -0
- package/dist/ui/transcript/StatusRow.js.map +1 -0
- package/dist/ui/transcript/ToolRows.d.ts +20 -0
- package/dist/ui/transcript/ToolRows.d.ts.map +1 -0
- package/dist/ui/transcript/ToolRows.js +25 -0
- package/dist/ui/transcript/ToolRows.js.map +1 -0
- package/dist/ui/transcript/TranscriptItemFrame.d.ts +8 -0
- package/dist/ui/transcript/TranscriptItemFrame.d.ts.map +1 -0
- package/dist/ui/transcript/TranscriptItemFrame.js +9 -0
- package/dist/ui/transcript/TranscriptItemFrame.js.map +1 -0
- package/dist/ui/transcript/TranscriptRenderer.d.ts +22 -0
- package/dist/ui/transcript/TranscriptRenderer.d.ts.map +1 -0
- package/dist/ui/transcript/TranscriptRenderer.js +84 -0
- package/dist/ui/transcript/TranscriptRenderer.js.map +1 -0
- package/dist/ui/transcript/presentation.d.ts +76 -0
- package/dist/ui/transcript/presentation.d.ts.map +1 -0
- package/dist/ui/transcript/presentation.js +109 -0
- package/dist/ui/transcript/presentation.js.map +1 -0
- package/dist/ui/transcript/spacing.d.ts +29 -0
- package/dist/ui/transcript/spacing.d.ts.map +1 -0
- package/dist/ui/transcript/spacing.js +91 -0
- package/dist/ui/transcript/spacing.js.map +1 -0
- package/dist/ui/transcript/spacing.test.d.ts +2 -0
- package/dist/ui/transcript/spacing.test.d.ts.map +1 -0
- package/dist/ui/transcript/spacing.test.js +21 -0
- package/dist/ui/transcript/spacing.test.js.map +1 -0
- package/dist/ui/transcript/tool-presentation.d.ts +12 -0
- package/dist/ui/transcript/tool-presentation.d.ts.map +1 -0
- package/dist/ui/transcript/tool-presentation.js +55 -0
- package/dist/ui/transcript/tool-presentation.js.map +1 -0
- package/dist/ui/tui-history-parity.test.js +3 -2
- package/dist/ui/tui-history-parity.test.js.map +1 -1
- package/dist/utils/plan-steps.d.ts.map +1 -1
- package/dist/utils/plan-steps.js +5 -1
- package/dist/utils/plan-steps.js.map +1 -1
- package/dist/utils/plan-steps.test.d.ts +2 -0
- package/dist/utils/plan-steps.test.d.ts.map +1 -0
- package/dist/utils/plan-steps.test.js +16 -0
- package/dist/utils/plan-steps.test.js.map +1 -0
- package/package.json +6 -6
- package/dist/core/repomap-budget.d.ts +0 -7
- package/dist/core/repomap-budget.d.ts.map +0 -1
- package/dist/core/repomap-budget.js +0 -10
- package/dist/core/repomap-budget.js.map +0 -1
- package/dist/core/repomap-budget.test.d.ts +0 -2
- package/dist/core/repomap-budget.test.d.ts.map +0 -1
- package/dist/core/repomap-budget.test.js +0 -26
- package/dist/core/repomap-budget.test.js.map +0 -1
- package/dist/core/repomap-context.d.ts +0 -11
- package/dist/core/repomap-context.d.ts.map +0 -1
- package/dist/core/repomap-context.js +0 -68
- package/dist/core/repomap-context.js.map +0 -1
- package/dist/core/repomap-context.test.d.ts +0 -2
- package/dist/core/repomap-context.test.d.ts.map +0 -1
- package/dist/core/repomap-context.test.js +0 -47
- package/dist/core/repomap-context.test.js.map +0 -1
- package/dist/core/repomap.d.ts +0 -74
- package/dist/core/repomap.d.ts.map +0 -1
- package/dist/core/repomap.js +0 -906
- package/dist/core/repomap.js.map +0 -1
- package/dist/core/repomap.test.d.ts +0 -2
- package/dist/core/repomap.test.d.ts.map +0 -1
- package/dist/core/repomap.test.js +0 -494
- package/dist/core/repomap.test.js.map +0 -1
package/dist/ui/App.js
CHANGED
|
@@ -1,41 +1,25 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import React, { useState, useRef, useCallback, useEffect, useMemo } from "react";
|
|
3
|
-
import { Box,
|
|
3
|
+
import { Box, useStdout } from "ink";
|
|
4
4
|
import { useTerminalSize } from "./hooks/useTerminalSize.js";
|
|
5
|
+
import { useChatLayoutMeasurements } from "./hooks/useChatLayoutMeasurements.js";
|
|
6
|
+
import { useTaskPickerController } from "./hooks/useTaskPickerController.js";
|
|
7
|
+
import { useGoalPickerController } from "./hooks/useGoalPickerController.js";
|
|
5
8
|
import { useDoublePress } from "./hooks/useDoublePress.js";
|
|
6
9
|
import { useTaskBarStore, useTaskBarPolling, focusTaskBar, exitTaskBar, expandTaskBar, collapseTaskBar, navigateTaskBar, killTask, } from "./stores/taskbar-store.js";
|
|
7
10
|
import { playNotificationSound } from "../utils/sound.js";
|
|
8
|
-
import {
|
|
11
|
+
import {} from "@kenkaiiii/gg-ai";
|
|
9
12
|
import { extractImagePaths } from "../utils/image.js";
|
|
10
|
-
import { buildGoalReferenceContext, formatGoalReferencesForPrompt, } from "../core/goal-references.js";
|
|
11
13
|
import { useAgentLoop } from "./hooks/useAgentLoop.js";
|
|
12
|
-
import {
|
|
13
|
-
import { AssistantMessage } from "./components/AssistantMessage.js";
|
|
14
|
-
import { ToolExecution } from "./components/ToolExecution.js";
|
|
15
|
-
import { ToolUseLoader } from "./components/ToolUseLoader.js";
|
|
16
|
-
import { ToolGroupExecution } from "./components/ToolGroupExecution.js";
|
|
17
|
-
import { ServerToolExecution } from "./components/ServerToolExecution.js";
|
|
18
|
-
import { MessageResponse } from "./components/MessageResponse.js";
|
|
19
|
-
import { SubAgentPanel } from "./components/SubAgentPanel.js";
|
|
20
|
-
import { CompactionSpinner, CompactionDone } from "./components/CompactionNotice.js";
|
|
14
|
+
import { useTranscriptHistory } from "./hooks/useTranscriptHistory.js";
|
|
21
15
|
import { createWebSearchTool } from "../tools/web-search.js";
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import { Footer, doesFooterFitOnOneLine } from "./components/Footer.js";
|
|
26
|
-
import { GoalStatusBar, reconcileGoalStatusEntriesWithRuns, removeGoalStatusEntry, syncGoalStatusEntries, } from "./components/GoalStatusBar.js";
|
|
27
|
-
import { Banner } from "./components/Banner.js";
|
|
28
|
-
import { PlanOverlay } from "./components/PlanOverlay.js";
|
|
29
|
-
import { ModelSelector } from "./components/ModelSelector.js";
|
|
30
|
-
import { PixelOverlay } from "./components/PixelOverlay.js";
|
|
31
|
-
import { SkillsOverlay } from "./components/SkillsOverlay.js";
|
|
32
|
-
import { ThemeSelector } from "./components/ThemeSelector.js";
|
|
33
|
-
import { BackgroundTasksBar, getFooterStatusLayoutDecision, } from "./components/BackgroundTasksBar.js";
|
|
16
|
+
import { ChatScreen } from "./components/ChatScreen.js";
|
|
17
|
+
import { FullScreenOverlayRouter } from "./components/FullScreenOverlayRouter.js";
|
|
18
|
+
import { reconcileGoalStatusEntriesWithRuns, removeGoalStatusEntry, syncGoalStatusEntries, } from "./components/GoalStatusBar.js";
|
|
34
19
|
import { useTheme, useSetTheme } from "./theme/theme.js";
|
|
35
20
|
import { useTerminalTitle } from "./hooks/useTerminalTitle.js";
|
|
36
21
|
import { getGitBranch } from "../utils/git.js";
|
|
37
22
|
import { getModel, getContextWindow } from "../core/model-registry.js";
|
|
38
|
-
import { BLACK_CIRCLE } from "./constants/figures.js";
|
|
39
23
|
import { SessionManager } from "../core/session-manager.js";
|
|
40
24
|
import { appendMessagesToSession as appendSessionMessages, createCompactedSessionCheckpoint, } from "../core/session-compaction.js";
|
|
41
25
|
import { log } from "../core/logger.js";
|
|
@@ -48,65 +32,39 @@ import { PROMPT_COMMANDS, getPromptCommand } from "../core/prompt-commands.js";
|
|
|
48
32
|
import { isFirstTimeSetup, markSetupAudited, getAnnouncedLanguages, markLanguagesAnnounced, } from "../core/setup-history.js";
|
|
49
33
|
import { loadCustomCommands } from "../core/custom-commands.js";
|
|
50
34
|
import { buildSystemPrompt } from "../system-prompt.js";
|
|
51
|
-
import { detectLanguages
|
|
35
|
+
import { detectLanguages } from "../core/language-detector.js";
|
|
52
36
|
import { detectVerifyCommands } from "../core/verify-commands.js";
|
|
53
|
-
import { buildRepoMap, createRepoMapCache, } from "../core/repomap.js";
|
|
54
|
-
import { getRepoMapBudgetForContext } from "../core/repomap-budget.js";
|
|
55
|
-
import { getLatestUserText, injectRepoMapContextMessages, stripRepoMapContextMessages, } from "../core/repomap-context.js";
|
|
56
37
|
import { extractPlanSteps, findCompletedMarkers, markStepsCompleted, segmentDisplayText, stripDoneMarkers, } from "../utils/plan-steps.js";
|
|
57
38
|
import { getMCPServers } from "../core/mcp/index.js";
|
|
58
39
|
import { trimFlushedItems, flushOnTurnText, flushOnTurnEnd, flushOverflow, } from "./live-item-flush.js";
|
|
59
40
|
import { splitAssistantStreamingText } from "./utils/assistant-stream-split.js";
|
|
60
|
-
import { appendGoalDecision, appendGoalEvidence, formatGoalBlockingPrerequisites, goalHasBlockingPrerequisites, loadGoalRuns,
|
|
61
|
-
import { getNextPendingTask,
|
|
41
|
+
import { appendGoalDecision, appendGoalEvidence, formatGoalBlockingPrerequisites, goalHasBlockingPrerequisites, loadGoalRuns, reconcileActiveGoalRuns, updateGoalTask, upsertGoalRun, } from "../core/goal-store.js";
|
|
42
|
+
import { getNextPendingTask, markTaskInProgress } from "../core/tasks-store.js";
|
|
62
43
|
import { canCompleteGoalRun, decideGoalNextAction } from "../core/goal-controller.js";
|
|
63
44
|
import { runGoalPrerequisiteChecks } from "../core/goal-prerequisites.js";
|
|
64
45
|
import { runGoalVerifierCommand } from "../core/goal-verifier.js";
|
|
46
|
+
import { checkGoalWorktreeIntegration, isGoalWorktreeDirtyError } from "../core/goal-worktree.js";
|
|
65
47
|
import { listGoalWorkers, startGoalWorker, stopGoalWorker, subscribeGoalWorkerCompletions, } from "../core/goal-worker.js";
|
|
66
48
|
import { formatGoalVerifierCompletionEvent, formatGoalWorkerCompletionEvent, isGoalSyntheticEvent, parseGoalSyntheticEvent, } from "./goal-events.js";
|
|
67
|
-
import { buildUserContentWithAttachments
|
|
49
|
+
import { buildUserContentWithAttachments } from "./prompt-routing.js";
|
|
50
|
+
import { submitPromptCommand } from "./submit-prompt-command.js";
|
|
51
|
+
import { handleUiSlashCommand } from "./submit-slash-commands.js";
|
|
68
52
|
import { getNextThinkingLevel, isThinkingLevelSupported } from "./thinking-level.js";
|
|
69
|
-
import { appendGoalProgressDraft, completedItemsWithDurableGoalTerminalProgress, formatGoalTerminalProgress, formatGoalWorkerFinishedTitle, getGoalContinuationChoiceKey, goalTerminalProgressId, routeGoalSyntheticEvent, summarizeGoalCompletion,
|
|
70
|
-
import {
|
|
53
|
+
import { appendGoalProgressDraft, completedItemsWithDurableGoalTerminalProgress, formatGoalTerminalProgress, formatGoalWorkerFinishedTitle, getGoalContinuationChoiceKey, goalTerminalProgressId, routeGoalSyntheticEvent, summarizeGoalCompletion, } from "./goal-progress.js";
|
|
54
|
+
import { getDoneFlushDecision, nextGoalModeAfterAgentDone, shouldTopSpaceAfterPrintedAgentBoundary, shouldTopSpaceStreamingAssistant, } from "./layout-decisions.js";
|
|
55
|
+
import { isTranscriptSpacingItem } from "./transcript/spacing.js";
|
|
56
|
+
import { renderTranscriptItem } from "./transcript/TranscriptRenderer.js";
|
|
57
|
+
import { formatDuration } from "./duration-format.js";
|
|
58
|
+
import { pickDurationVerb } from "./duration-summary.js";
|
|
59
|
+
import { toErrorItem } from "./error-item.js";
|
|
60
|
+
import { buildGoalDirtyWorktreePauseRun, buildGoalDirtyWorktreeUserPrompt, buildGoalTaskPromptWithReferences, buildGoalUserPauseRun, goalDirtyWorktreeInfoText, goalRunNeedsExplicitContinuationAfterWorker, goalTaskProgress, shouldKeepGoalRunTrackedAfterDecision, shouldRunGoalTaskInMainCheckout, } from "./goal-run-helpers.js";
|
|
71
61
|
import { compactHistory, getNextGeneratedItemId, isActiveItem, isSameAssistantText, normalizeAssistantText, partitionCompleted, pinStreamingTextBeforeToolBoundary, removeItemsWithIds, uniqueItemsById, } from "./item-helpers.js";
|
|
72
62
|
export { buildGoalSetupPromptFromPlanner, buildUserContentWithAttachments, collectAssistantTextSince, isGoalPromptCommandName, routePromptCommandInput, runGoalPromptSetupSequence, } from "./prompt-routing.js";
|
|
73
63
|
export { getNextThinkingLevel } from "./thinking-level.js";
|
|
74
64
|
export { appendGoalProgressDraft, completedItemsWithDurableGoalTerminalProgress, formatGoalTerminalProgress, getGoalContinuationChoiceKey, routeGoalSyntheticEvent, truncateGoalProgressText, } from "./goal-progress.js";
|
|
75
65
|
export { getChatControlsLayoutDecision, getDoneFlushDecision, getGoalActivationPaneTransition, getGoalSetupFinishedPaneTransition, getGoalSetupPaneTransitionAfterRun, getScrollStabilizationDecision, getStaticHistoryKey, hasParagraphBreakLiveUserMessage, isTallLiveUserMessage, nextGoalModeAfterAgentDone, shouldHideHistoryForOverlayView, shouldHideStaticItemsForOverlayView, shouldResetUIForGoalSetupPaneTransition, shouldStabilizeOverlayPaneRerender, shouldTopSpaceAfterPrintedAgentBoundary, shouldTopSpaceAssistantAfterToolBoundary, shouldTopSpaceStreamingAssistant, } from "./layout-decisions.js";
|
|
76
66
|
export { getNextGeneratedItemId, isActiveItem, partitionCompleted, pinStreamingTextBeforeToolBoundary, } from "./item-helpers.js";
|
|
77
|
-
|
|
78
|
-
const GGCODER_BUG_REPORT_URL = "github.com/kenkaiiii/gg-framework/issues";
|
|
79
|
-
/**
|
|
80
|
-
* Build an ErrorItem from any thrown value. Centralises headline / message /
|
|
81
|
-
* guidance extraction so every error answers the same question for the user:
|
|
82
|
-
* "Should I retry, or is this a ggcoder bug to report?"
|
|
83
|
-
*/
|
|
84
|
-
function toErrorItem(err, id, contextPrefix) {
|
|
85
|
-
const f = formatError(err);
|
|
86
|
-
const headline = contextPrefix ? `${contextPrefix} — ${f.headline}` : f.headline;
|
|
87
|
-
// For ggcoder bugs, swap the generic "see /help" guidance for an actual URL
|
|
88
|
-
// so users have a clear place to send the report.
|
|
89
|
-
const guidance = f.source === "ggcoder"
|
|
90
|
-
? `This looks like a ggcoder bug — please send it to the dev at ${GGCODER_BUG_REPORT_URL}.`
|
|
91
|
-
: f.guidance;
|
|
92
|
-
// Mirror every user-visible error into ~/.gg/debug.log so reports can be
|
|
93
|
-
// diagnosed even after the terminal scrollback is gone.
|
|
94
|
-
log("ERROR", "ui-error", headline, {
|
|
95
|
-
source: f.source,
|
|
96
|
-
message: f.message,
|
|
97
|
-
...(f.provider ? { provider: f.provider } : {}),
|
|
98
|
-
...(f.statusCode != null ? { statusCode: String(f.statusCode) } : {}),
|
|
99
|
-
...(f.requestId ? { requestId: f.requestId } : {}),
|
|
100
|
-
...(err instanceof Error && err.stack ? { stack: err.stack } : {}),
|
|
101
|
-
});
|
|
102
|
-
return {
|
|
103
|
-
kind: "error",
|
|
104
|
-
headline,
|
|
105
|
-
message: f.message,
|
|
106
|
-
guidance,
|
|
107
|
-
id,
|
|
108
|
-
};
|
|
109
|
-
}
|
|
67
|
+
export { buildGoalDirtyWorktreePauseRun, buildGoalDirtyWorktreeUserPrompt, buildGoalUserPauseRun, goalDirtyWorktreeInfoText, goalRunNeedsExplicitContinuationAfterWorker, shouldKeepGoalRunTrackedAfterDecision, shouldRunGoalTaskInMainCheckout, } from "./goal-run-helpers.js";
|
|
110
68
|
/** Tools that get aggregated into a single compact group when possible. */
|
|
111
69
|
const AGGREGATABLE_TOOLS = new Set([
|
|
112
70
|
"read",
|
|
@@ -118,114 +76,6 @@ const AGGREGATABLE_TOOLS = new Set([
|
|
|
118
76
|
"mcp__kencode-search__discoverRepos",
|
|
119
77
|
]);
|
|
120
78
|
const RUNNING_INDICATOR_ANIMATION_MS = 1_200;
|
|
121
|
-
function buildGoalTaskPromptWithReferences(run, taskPrompt) {
|
|
122
|
-
if (taskPrompt.includes("## Goal References (MANDATORY)"))
|
|
123
|
-
return taskPrompt;
|
|
124
|
-
const references = formatGoalReferencesForPrompt(run.references ?? []);
|
|
125
|
-
return references ? `${references}\n\n${taskPrompt}` : taskPrompt;
|
|
126
|
-
}
|
|
127
|
-
function goalProgressLoaderStatus(item) {
|
|
128
|
-
if (item.status === "failed" || item.status === "fail" || item.status === "blocked") {
|
|
129
|
-
return "error";
|
|
130
|
-
}
|
|
131
|
-
if (item.phase === "worker_finished" ||
|
|
132
|
-
item.phase === "verifier_finished" ||
|
|
133
|
-
item.phase === "terminal") {
|
|
134
|
-
return "done";
|
|
135
|
-
}
|
|
136
|
-
return "running";
|
|
137
|
-
}
|
|
138
|
-
function goalProgressColor(item, theme) {
|
|
139
|
-
const isError = item.status === "failed" || item.status === "fail" || item.status === "blocked";
|
|
140
|
-
if (isError)
|
|
141
|
-
return theme.error;
|
|
142
|
-
if (item.phase === "worker_finished" || item.phase === "terminal")
|
|
143
|
-
return theme.success;
|
|
144
|
-
if (item.phase === "verifier_finished" || item.phase === "verifier_started")
|
|
145
|
-
return theme.accent;
|
|
146
|
-
if (item.phase === "orchestrator_reviewing" || item.phase === "orchestrator_working") {
|
|
147
|
-
return theme.secondary;
|
|
148
|
-
}
|
|
149
|
-
if (item.phase === "continuing")
|
|
150
|
-
return theme.warning;
|
|
151
|
-
return theme.primary;
|
|
152
|
-
}
|
|
153
|
-
// ── Duration summary ─────────────────────────────────────
|
|
154
|
-
function formatDuration(ms) {
|
|
155
|
-
const totalSec = Math.round(ms / 1000);
|
|
156
|
-
if (totalSec < 60)
|
|
157
|
-
return `${totalSec}s`;
|
|
158
|
-
const min = Math.floor(totalSec / 60);
|
|
159
|
-
const sec = totalSec % 60;
|
|
160
|
-
return sec > 0 ? `${min}m ${sec}s` : `${min}m`;
|
|
161
|
-
}
|
|
162
|
-
function pickDurationVerb(toolsUsed) {
|
|
163
|
-
const has = (name) => toolsUsed.includes(name);
|
|
164
|
-
const hasAny = (...names) => names.some(has);
|
|
165
|
-
const writing = has("edit") || has("write");
|
|
166
|
-
const reading = has("read") || has("grep") || has("find") || has("ls");
|
|
167
|
-
// Multi-tool combos (most specific first)
|
|
168
|
-
if (has("subagent") && writing)
|
|
169
|
-
return "Orchestrated changes for";
|
|
170
|
-
if (has("subagent"))
|
|
171
|
-
return "Delegated work for";
|
|
172
|
-
if (has("web-fetch") && writing)
|
|
173
|
-
return "Researched & coded for";
|
|
174
|
-
if (has("web-fetch") && reading)
|
|
175
|
-
return "Researched for";
|
|
176
|
-
if (has("web-fetch"))
|
|
177
|
-
return "Fetched the web for";
|
|
178
|
-
if (has("bash") && writing)
|
|
179
|
-
return "Built & ran for";
|
|
180
|
-
if (has("edit") && has("write"))
|
|
181
|
-
return "Crafted code for";
|
|
182
|
-
if (has("edit") && has("bash"))
|
|
183
|
-
return "Refactored & tested for";
|
|
184
|
-
if (has("edit") && reading)
|
|
185
|
-
return "Refactored for";
|
|
186
|
-
if (has("edit"))
|
|
187
|
-
return "Refactored for";
|
|
188
|
-
if (has("write") && has("bash"))
|
|
189
|
-
return "Wrote & ran for";
|
|
190
|
-
if (has("write") && reading)
|
|
191
|
-
return "Wrote code for";
|
|
192
|
-
if (has("write"))
|
|
193
|
-
return "Wrote code for";
|
|
194
|
-
if (has("bash") && has("grep"))
|
|
195
|
-
return "Hacked away for";
|
|
196
|
-
if (has("bash") && reading)
|
|
197
|
-
return "Ran & investigated for";
|
|
198
|
-
if (has("bash"))
|
|
199
|
-
return "Executed commands for";
|
|
200
|
-
if (hasAny("task-output", "task-stop"))
|
|
201
|
-
return "Managed background processes for";
|
|
202
|
-
if (has("grep") && has("read"))
|
|
203
|
-
return "Investigated for";
|
|
204
|
-
if (has("grep") && has("find"))
|
|
205
|
-
return "Scoured the codebase for";
|
|
206
|
-
if (has("grep"))
|
|
207
|
-
return "Searched for";
|
|
208
|
-
if (has("read") && has("find"))
|
|
209
|
-
return "Explored for";
|
|
210
|
-
if (has("read"))
|
|
211
|
-
return "Studied the code for";
|
|
212
|
-
if (has("find") || has("ls"))
|
|
213
|
-
return "Browsed files for";
|
|
214
|
-
// No tools used — pure text response
|
|
215
|
-
const phrases = [
|
|
216
|
-
"Pondered for",
|
|
217
|
-
"Thought for",
|
|
218
|
-
"Reasoned for",
|
|
219
|
-
"Mulled it over for",
|
|
220
|
-
"Noodled on it for",
|
|
221
|
-
"Brewed up a response in",
|
|
222
|
-
"Cooked up an answer in",
|
|
223
|
-
"Worked out a reply in",
|
|
224
|
-
"Channeled wisdom for",
|
|
225
|
-
"Conjured a response in",
|
|
226
|
-
];
|
|
227
|
-
return phrases[Math.floor(Math.random() * phrases.length)];
|
|
228
|
-
}
|
|
229
79
|
// ── App Component ──────────────────────────────────────────
|
|
230
80
|
export function App(props) {
|
|
231
81
|
const theme = useTheme();
|
|
@@ -236,6 +86,7 @@ export function App(props) {
|
|
|
236
86
|
const [lastUserMessage, setLastUserMessage] = useState("");
|
|
237
87
|
const [exitPending, setExitPending] = useState(false);
|
|
238
88
|
const [goalMode, setGoalMode] = useState(props.sessionStore?.goalMode ?? props.goalModeRef?.current ?? "off");
|
|
89
|
+
const [planMode, setPlanMode] = useState(props.sessionStore?.planMode ?? props.planModeRef?.current ?? false);
|
|
239
90
|
// Terminal title — updated later after agentLoop is created
|
|
240
91
|
// (hoisted here so the hook is always called in the same order)
|
|
241
92
|
const [titleRunning, setTitleRunning] = useState(false);
|
|
@@ -279,10 +130,6 @@ export function App(props) {
|
|
|
279
130
|
const goalContinuationRecentChoicesRef = useRef(new Map());
|
|
280
131
|
const startGoalRunRef = useRef(() => { });
|
|
281
132
|
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
133
|
const runAllTasksRef = useRef(props.sessionStore?.runAllTasks ?? false);
|
|
287
134
|
const startTaskRef = useRef(() => { });
|
|
288
135
|
const runAllPixelRef = useRef(props.sessionStore?.runAllPixel ?? false);
|
|
@@ -290,6 +137,11 @@ export function App(props) {
|
|
|
290
137
|
const startPixelFixRef = useRef(() => { });
|
|
291
138
|
const cwdRef = useRef(props.cwd);
|
|
292
139
|
const [displayedCwd, setDisplayedCwd] = useState(props.cwd);
|
|
140
|
+
const taskPicker = useTaskPickerController({
|
|
141
|
+
displayedCwd,
|
|
142
|
+
onStartTask: (title, prompt, taskId) => startTaskRef.current(title, prompt, taskId),
|
|
143
|
+
onRunAllTasksChange: setRunAllTasks,
|
|
144
|
+
});
|
|
293
145
|
const [doneStatus, setDoneStatus] = useState(props.sessionStore?.doneStatus ?? null);
|
|
294
146
|
// Suppress "done" status when a plan overlay is about to open
|
|
295
147
|
const planOverlayPendingRef = useRef(false);
|
|
@@ -302,12 +154,6 @@ export function App(props) {
|
|
|
302
154
|
const [thinkingLevel, setThinkingLevel] = useState(props.thinking);
|
|
303
155
|
const [renderMarkdown, setRenderMarkdown] = useState(true);
|
|
304
156
|
const messagesRef = useRef(props.sessionStore?.messages ?? props.messages);
|
|
305
|
-
const repoMapInjectionEnabledRef = useRef(true);
|
|
306
|
-
const repoMapDirtyRef = useRef(true);
|
|
307
|
-
const repoMapMarkdownRef = useRef("");
|
|
308
|
-
const repoMapSnapshotRef = useRef(undefined);
|
|
309
|
-
const repoMapChangedCountRef = useRef(0);
|
|
310
|
-
const repoMapCacheRef = useRef(createRepoMapCache());
|
|
311
157
|
const [planAutoExpand, setPlanAutoExpand] = useState(props.sessionStore?.planAutoExpand ?? false);
|
|
312
158
|
const [goalAutoExpand, setGoalAutoExpand] = useState(props.sessionStore?.goalAutoExpand ?? false);
|
|
313
159
|
const goalAutoExpandRef = useRef(props.sessionStore?.goalAutoExpand ?? false);
|
|
@@ -315,6 +161,7 @@ export function App(props) {
|
|
|
315
161
|
const planStepsRef = useRef(props.sessionStore?.planSteps ?? []);
|
|
316
162
|
const [planSteps, setPlanSteps] = useState(props.sessionStore?.planSteps ?? []);
|
|
317
163
|
const goalModeStateRef = useRef(goalMode);
|
|
164
|
+
const planModeStateRef = useRef(planMode);
|
|
318
165
|
// Stuck-guard for the plan-continuation follow-up nudge. Tracks how many
|
|
319
166
|
// times we've nudged the agent to continue the same step. Reset whenever a
|
|
320
167
|
// new [DONE:n] marker advances progress (see onTurnText). Caps at 2 nudges
|
|
@@ -384,58 +231,24 @@ export function App(props) {
|
|
|
384
231
|
});
|
|
385
232
|
}, [props.sessionStore]);
|
|
386
233
|
const sessionStore = props.sessionStore;
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
version: props.version,
|
|
391
|
-
model: currentModel,
|
|
392
|
-
provider: currentProvider,
|
|
393
|
-
cwd: displayedCwd,
|
|
394
|
-
});
|
|
395
|
-
useEffect(() => {
|
|
396
|
-
terminalHistoryContextRef.current = {
|
|
234
|
+
const { pendingHistoryFlushRef, streamedAssistantFlushRef, queueFlush, finalizeSubmittedUserItem, clearPendingHistory, } = useTranscriptHistory({
|
|
235
|
+
terminalHistoryPrinter: props.terminalHistoryPrinter,
|
|
236
|
+
terminalHistoryContext: {
|
|
397
237
|
theme,
|
|
398
238
|
columns,
|
|
399
239
|
version: props.version,
|
|
400
240
|
model: currentModel,
|
|
401
241
|
provider: currentProvider,
|
|
402
242
|
cwd: displayedCwd,
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
});
|
|
412
|
-
}, [props.terminalHistoryPrinter, writeStdout]);
|
|
413
|
-
const pendingHistoryFlushRef = useRef([]);
|
|
414
|
-
const streamedAssistantFlushRef = useRef({
|
|
415
|
-
flushedChars: 0,
|
|
416
|
-
text: "",
|
|
243
|
+
},
|
|
244
|
+
writeStdout,
|
|
245
|
+
sessionPathRef,
|
|
246
|
+
sessionManagerRef,
|
|
247
|
+
sessionStore,
|
|
248
|
+
history,
|
|
249
|
+
setHistory,
|
|
250
|
+
setLiveItems,
|
|
417
251
|
});
|
|
418
|
-
const [historyFlushGeneration, setHistoryFlushGeneration] = useState(0);
|
|
419
|
-
const queueFlush = useCallback((items) => {
|
|
420
|
-
const flushed = trimFlushedItems(items);
|
|
421
|
-
if (flushed.length === 0)
|
|
422
|
-
return;
|
|
423
|
-
pendingHistoryFlushRef.current = [...pendingHistoryFlushRef.current, ...flushed];
|
|
424
|
-
if (sessionStore) {
|
|
425
|
-
const queuedIds = new Set(items.map((item) => item.id));
|
|
426
|
-
sessionStore.liveItems = removeItemsWithIds(uniqueItemsById(sessionStore.liveItems ?? []), queuedIds);
|
|
427
|
-
}
|
|
428
|
-
setHistoryFlushGeneration((generation) => generation + 1);
|
|
429
|
-
}, [sessionStore]);
|
|
430
|
-
const finalizeSubmittedUserItem = useCallback((item) => {
|
|
431
|
-
streamedAssistantFlushRef.current = { flushedChars: 0, text: "" };
|
|
432
|
-
setLiveItems((prev) => {
|
|
433
|
-
if (prev.length > 0)
|
|
434
|
-
queueFlush(prev);
|
|
435
|
-
queueFlush([item]);
|
|
436
|
-
return [];
|
|
437
|
-
});
|
|
438
|
-
}, [queueFlush]);
|
|
439
252
|
// Mirror runtime state choices (model/provider/thinking) into renderApp's
|
|
440
253
|
// closure so unmount/remount preserves them.
|
|
441
254
|
const onRuntimeStateChange = props.onRuntimeStateChange;
|
|
@@ -455,28 +268,6 @@ export function App(props) {
|
|
|
455
268
|
thinking: thinkingLevel,
|
|
456
269
|
});
|
|
457
270
|
}, [thinkingLevel, onRuntimeStateChange]);
|
|
458
|
-
useEffect(() => {
|
|
459
|
-
printHistoryItems(history);
|
|
460
|
-
}, [history, printHistoryItems]);
|
|
461
|
-
useEffect(() => {
|
|
462
|
-
const flushed = pendingHistoryFlushRef.current;
|
|
463
|
-
if (flushed.length === 0)
|
|
464
|
-
return;
|
|
465
|
-
pendingHistoryFlushRef.current = [];
|
|
466
|
-
printHistoryItems(flushed);
|
|
467
|
-
const flushedIds = new Set(flushed.map((item) => item.id));
|
|
468
|
-
setLiveItems((prev) => prev.filter((item) => !flushedIds.has(item.id)));
|
|
469
|
-
setHistory((prev) => {
|
|
470
|
-
const existingIds = new Set(prev.map((item) => item.id));
|
|
471
|
-
const nextItems = flushed.filter((item) => !existingIds.has(item.id));
|
|
472
|
-
if (nextItems.length === 0)
|
|
473
|
-
return prev;
|
|
474
|
-
const next = compactHistory([...prev, ...nextItems]);
|
|
475
|
-
if (sessionStore)
|
|
476
|
-
sessionStore.history = next;
|
|
477
|
-
return next;
|
|
478
|
-
});
|
|
479
|
-
}, [historyFlushGeneration, printHistoryItems, sessionStore]);
|
|
480
271
|
// Mirror session state into renderApp's closure so resetUI() can re-seed
|
|
481
272
|
// the conversation on remount. Each panel that previously did a bare ANSI
|
|
482
273
|
// screen clear (overlay open/close, plan accept/reject, /clear)
|
|
@@ -522,6 +313,10 @@ export function App(props) {
|
|
|
522
313
|
if (sessionStore)
|
|
523
314
|
sessionStore.goalMode = goalMode;
|
|
524
315
|
}, [goalMode, sessionStore]);
|
|
316
|
+
useEffect(() => {
|
|
317
|
+
if (sessionStore)
|
|
318
|
+
sessionStore.planMode = planMode;
|
|
319
|
+
}, [planMode, sessionStore]);
|
|
525
320
|
// pendingAction is consumed via a useEffect AFTER agentLoop is created
|
|
526
321
|
// — see below where useAgentLoop is set up.
|
|
527
322
|
const pendingActionConsumedRef = useRef(false);
|
|
@@ -593,6 +388,11 @@ export function App(props) {
|
|
|
593
388
|
props.goalModeRef.current = goalMode;
|
|
594
389
|
}
|
|
595
390
|
}, [goalMode, props.goalModeRef]);
|
|
391
|
+
useEffect(() => {
|
|
392
|
+
planModeStateRef.current = planMode;
|
|
393
|
+
if (props.planModeRef)
|
|
394
|
+
props.planModeRef.current = planMode;
|
|
395
|
+
}, [planMode, props.planModeRef]);
|
|
596
396
|
const setActiveGoalReferences = useCallback((references) => {
|
|
597
397
|
if (props.goalReferencesRef)
|
|
598
398
|
props.goalReferencesRef.current = references;
|
|
@@ -601,7 +401,7 @@ export function App(props) {
|
|
|
601
401
|
const approvedPlanPath = options?.clearApprovedPlan
|
|
602
402
|
? undefined
|
|
603
403
|
: (options?.approvedPlanPath ?? approvedPlanPathRef.current);
|
|
604
|
-
return buildSystemPrompt(options?.cwd ?? cwdRef.current, props.skills,
|
|
404
|
+
return buildSystemPrompt(options?.cwd ?? cwdRef.current, props.skills, options?.planMode ?? planModeStateRef.current, approvedPlanPath, (options?.tools ?? currentToolsRef.current).map((tool) => tool.name), options?.activeLanguages ?? injectedLanguagesRef.current, options?.goalMode ?? goalModeStateRef.current);
|
|
605
405
|
}, [props.skills]);
|
|
606
406
|
const replaceSystemPrompt = useCallback(async (options) => {
|
|
607
407
|
const newPrompt = await rebuildSystemPrompt(options);
|
|
@@ -642,6 +442,15 @@ export function App(props) {
|
|
|
642
442
|
setGoalMode(nextMode);
|
|
643
443
|
await replaceSystemPrompt({ ...options, goalMode: nextMode });
|
|
644
444
|
}, [props.goalModeRef, props.sessionStore, replaceSystemPrompt]);
|
|
445
|
+
const setPlanModeAndPrompt = useCallback(async (nextMode) => {
|
|
446
|
+
planModeStateRef.current = nextMode;
|
|
447
|
+
if (props.planModeRef)
|
|
448
|
+
props.planModeRef.current = nextMode;
|
|
449
|
+
if (props.sessionStore)
|
|
450
|
+
props.sessionStore.planMode = nextMode;
|
|
451
|
+
setPlanMode(nextMode);
|
|
452
|
+
await replaceSystemPrompt({ planMode: nextMode });
|
|
453
|
+
}, [props.planModeRef, props.sessionStore, replaceSystemPrompt]);
|
|
645
454
|
const clearGoalModeIfIdle = useCallback(() => {
|
|
646
455
|
setTimeout(() => {
|
|
647
456
|
if (goalModeStateRef.current === "off")
|
|
@@ -892,103 +701,46 @@ export function App(props) {
|
|
|
892
701
|
contextWindowOptions,
|
|
893
702
|
props.authStorage,
|
|
894
703
|
]);
|
|
895
|
-
const getRepoMapSignalCount = useCallback(() => {
|
|
896
|
-
return ((props.repoMapChangedFilesRef?.current.size ?? 0) +
|
|
897
|
-
(props.repoMapReadFilesRef?.current.size ?? 0));
|
|
898
|
-
}, [props.repoMapChangedFilesRef, props.repoMapReadFilesRef]);
|
|
899
|
-
const getRepoMapBudget = useCallback(() => {
|
|
900
|
-
return getRepoMapBudgetForContext({
|
|
901
|
-
messages: messagesRef.current,
|
|
902
|
-
readFileCount: props.repoMapReadFilesRef?.current.size ?? 0,
|
|
903
|
-
});
|
|
904
|
-
}, [props.repoMapReadFilesRef]);
|
|
905
|
-
const refreshRepoMap = useCallback(async (latestUserPrompt) => {
|
|
906
|
-
const rendered = await buildRepoMap({
|
|
907
|
-
cwd: cwdRef.current,
|
|
908
|
-
maxChars: getRepoMapBudget(),
|
|
909
|
-
changedFiles: [...(props.repoMapChangedFilesRef?.current ?? new Set())],
|
|
910
|
-
readFiles: [...(props.repoMapReadFilesRef?.current ?? new Set())],
|
|
911
|
-
focusTerms: latestUserPrompt ? [latestUserPrompt] : [],
|
|
912
|
-
cache: repoMapCacheRef.current,
|
|
913
|
-
});
|
|
914
|
-
repoMapMarkdownRef.current = rendered.markdown;
|
|
915
|
-
repoMapSnapshotRef.current = rendered.snapshot;
|
|
916
|
-
repoMapChangedCountRef.current = getRepoMapSignalCount();
|
|
917
|
-
repoMapDirtyRef.current = false;
|
|
918
|
-
return rendered.markdown;
|
|
919
|
-
}, [
|
|
920
|
-
getRepoMapBudget,
|
|
921
|
-
getRepoMapSignalCount,
|
|
922
|
-
props.repoMapChangedFilesRef,
|
|
923
|
-
props.repoMapReadFilesRef,
|
|
924
|
-
]);
|
|
925
|
-
const stripRepoMapMessages = useCallback((messages) => {
|
|
926
|
-
return stripRepoMapContextMessages(messages);
|
|
927
|
-
}, []);
|
|
928
|
-
const injectRepoMapContext = useCallback(async (messages) => {
|
|
929
|
-
if (!repoMapInjectionEnabledRef.current)
|
|
930
|
-
return stripRepoMapMessages(messages);
|
|
931
|
-
const stripped = stripRepoMapMessages(messages);
|
|
932
|
-
const latestUserPrompt = getLatestUserText(stripped);
|
|
933
|
-
const signalCount = getRepoMapSignalCount();
|
|
934
|
-
if (signalCount !== repoMapChangedCountRef.current)
|
|
935
|
-
repoMapDirtyRef.current = true;
|
|
936
|
-
if (repoMapDirtyRef.current || !repoMapMarkdownRef.current) {
|
|
937
|
-
await refreshRepoMap(latestUserPrompt);
|
|
938
|
-
}
|
|
939
|
-
if (!repoMapMarkdownRef.current)
|
|
940
|
-
return stripped;
|
|
941
|
-
return injectRepoMapContextMessages(stripped, repoMapMarkdownRef.current);
|
|
942
|
-
}, [props.repoMapChangedFilesRef, props.repoMapReadFilesRef, refreshRepoMap, stripRepoMapMessages]);
|
|
943
704
|
/**
|
|
944
705
|
* transformContext callback for the agent loop.
|
|
945
706
|
* Called before each LLM call and on context overflow.
|
|
946
|
-
* Compacts persistent chat only, then injects the dynamic repo map transiently.
|
|
947
707
|
*/
|
|
948
708
|
const transformContext = useCallback(async (messages, options) => {
|
|
949
|
-
const stripped = stripRepoMapMessages(messages);
|
|
950
709
|
const settings = settingsRef.current;
|
|
951
710
|
const autoCompact = settings?.get("autoCompact") ?? true;
|
|
952
711
|
const threshold = settings?.get("compactThreshold") ?? 0.8;
|
|
953
712
|
// Force-compact on context overflow regardless of settings
|
|
954
713
|
if (options?.force) {
|
|
955
|
-
const result = await compactConversation(
|
|
956
|
-
if (result !==
|
|
714
|
+
const result = await compactConversation(messages);
|
|
715
|
+
if (result !== messages) {
|
|
957
716
|
messagesRef.current = result;
|
|
958
717
|
await persistCompactedSession(result);
|
|
959
718
|
}
|
|
960
719
|
lastCompactionTimeRef.current = Date.now();
|
|
961
|
-
return
|
|
720
|
+
return result;
|
|
962
721
|
}
|
|
963
722
|
if (!autoCompact)
|
|
964
|
-
return
|
|
723
|
+
return messages;
|
|
965
724
|
// Time-based cooldown: skip if compaction ran within the last 30 seconds
|
|
966
725
|
if (Date.now() - lastCompactionTimeRef.current < 30_000) {
|
|
967
726
|
log("INFO", "compaction", `Skipping compaction — cooldown active`);
|
|
968
|
-
return
|
|
727
|
+
return messages;
|
|
969
728
|
}
|
|
970
729
|
const contextWindow = getContextWindow(currentModel, contextWindowOptions);
|
|
971
730
|
const reserveTokens = getCompactionReserveTokens(props.maxTokens);
|
|
972
731
|
const tokensFresh = lastActualTokensTimestampRef.current > lastCompactionTimeRef.current;
|
|
973
732
|
const actualTokens = lastActualTokensRef.current > 0 && tokensFresh ? lastActualTokensRef.current : undefined;
|
|
974
|
-
if (shouldCompact(
|
|
975
|
-
const result = await compactConversation(
|
|
976
|
-
if (result !==
|
|
733
|
+
if (shouldCompact(messages, contextWindow, threshold, actualTokens, reserveTokens)) {
|
|
734
|
+
const result = await compactConversation(messages);
|
|
735
|
+
if (result !== messages) {
|
|
977
736
|
messagesRef.current = result;
|
|
978
737
|
await persistCompactedSession(result);
|
|
979
738
|
}
|
|
980
739
|
lastCompactionTimeRef.current = Date.now();
|
|
981
|
-
return
|
|
740
|
+
return result;
|
|
982
741
|
}
|
|
983
|
-
return
|
|
984
|
-
}, [
|
|
985
|
-
currentModel,
|
|
986
|
-
compactConversation,
|
|
987
|
-
contextWindowOptions,
|
|
988
|
-
injectRepoMapContext,
|
|
989
|
-
persistCompactedSession,
|
|
990
|
-
stripRepoMapMessages,
|
|
991
|
-
]);
|
|
742
|
+
return messages;
|
|
743
|
+
}, [currentModel, compactConversation, contextWindowOptions, persistCompactedSession]);
|
|
992
744
|
// ── Background task bar state (external store) ──────────
|
|
993
745
|
const { bgTasks, focused: taskBarFocused, expanded: taskBarExpanded, selectedIndex: selectedTaskIndex, } = useTaskBarStore();
|
|
994
746
|
useTaskBarPolling(props.processManager);
|
|
@@ -1029,7 +781,6 @@ export function App(props) {
|
|
|
1029
781
|
transformContext,
|
|
1030
782
|
}, {
|
|
1031
783
|
onComplete: useCallback(() => {
|
|
1032
|
-
messagesRef.current = stripRepoMapMessages(messagesRef.current);
|
|
1033
784
|
persistNewMessages();
|
|
1034
785
|
// Auto-clear plan progress and approved plan when all steps are completed
|
|
1035
786
|
const steps = planStepsRef.current;
|
|
@@ -1082,7 +833,6 @@ export function App(props) {
|
|
|
1082
833
|
}
|
|
1083
834
|
}, [
|
|
1084
835
|
persistNewMessages,
|
|
1085
|
-
stripRepoMapMessages,
|
|
1086
836
|
props.cwd,
|
|
1087
837
|
props.skills,
|
|
1088
838
|
currentProvider,
|
|
@@ -1847,227 +1597,101 @@ export function App(props) {
|
|
|
1847
1597
|
// has not grown.
|
|
1848
1598
|
await applyLanguageDetectionRef.current("input");
|
|
1849
1599
|
}
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1600
|
+
if (await handleUiSlashCommand(trimmed, {
|
|
1601
|
+
openModelSelector: () => setOverlay("model"),
|
|
1602
|
+
compactConversation: async () => {
|
|
1603
|
+
const ac = new AbortController();
|
|
1604
|
+
compactionAbortRef.current = ac;
|
|
1605
|
+
const compacted = await compactConversation(messagesRef.current, ac.signal);
|
|
1606
|
+
if (!ac.signal.aborted && compacted !== messagesRef.current) {
|
|
1607
|
+
messagesRef.current = compacted;
|
|
1608
|
+
await persistCompactedSession(compacted);
|
|
1609
|
+
}
|
|
1610
|
+
if (compactionAbortRef.current === ac)
|
|
1611
|
+
compactionAbortRef.current = null;
|
|
1612
|
+
},
|
|
1613
|
+
quit: () => process.exit(0),
|
|
1614
|
+
clearSession: () => {
|
|
1615
|
+
if (props.resetUI) {
|
|
1616
|
+
void (async () => {
|
|
1617
|
+
const newPrompt = await rebuildSystemPrompt({ clearApprovedPlan: true });
|
|
1618
|
+
props.resetUI?.({
|
|
1619
|
+
wipeSession: true,
|
|
1620
|
+
messages: [{ role: "system", content: newPrompt }],
|
|
1621
|
+
});
|
|
1622
|
+
})();
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
clearPendingHistory();
|
|
1626
|
+
setHistory([{ kind: "banner", id: "banner" }]);
|
|
1627
|
+
setLiveItems([]);
|
|
1628
|
+
setDoneStatus(null);
|
|
1629
|
+
approvedPlanPathRef.current = undefined;
|
|
1630
|
+
planStepsRef.current = [];
|
|
1631
|
+
setPlanSteps([]);
|
|
1879
1632
|
void (async () => {
|
|
1880
1633
|
const newPrompt = await rebuildSystemPrompt({ clearApprovedPlan: true });
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
messages: [{ role: "system", content: newPrompt }],
|
|
1884
|
-
});
|
|
1634
|
+
messagesRef.current = [{ role: "system", content: newPrompt }];
|
|
1635
|
+
persistedIndexRef.current = messagesRef.current.length;
|
|
1885
1636
|
})();
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
const next = !prev;
|
|
1918
|
-
setLiveItems([
|
|
1919
|
-
{
|
|
1920
|
-
kind: "info",
|
|
1921
|
-
text: next ? "Rendered markdown mode." : "Raw markdown mode.",
|
|
1922
|
-
id: getId(),
|
|
1923
|
-
},
|
|
1924
|
-
]);
|
|
1925
|
-
return next;
|
|
1926
|
-
});
|
|
1927
|
-
return;
|
|
1928
|
-
}
|
|
1929
|
-
// Handle /clearplan — dismiss the approved plan
|
|
1930
|
-
if (trimmed === "/clearplan") {
|
|
1931
|
-
approvedPlanPathRef.current = undefined;
|
|
1932
|
-
planStepsRef.current = [];
|
|
1933
|
-
setPlanSteps([]);
|
|
1934
|
-
// Rebuild system prompt without the plan
|
|
1935
|
-
void replaceSystemPrompt({ clearApprovedPlan: true });
|
|
1936
|
-
setLiveItems([{ kind: "plan_event", event: "dismissed", id: getId() }]);
|
|
1937
|
-
return;
|
|
1938
|
-
}
|
|
1939
|
-
// Handle /map — show, refresh, or toggle dynamic repo map injection
|
|
1940
|
-
if (trimmed === "/map" ||
|
|
1941
|
-
trimmed === "/map refresh" ||
|
|
1942
|
-
trimmed === "/map on" ||
|
|
1943
|
-
trimmed === "/map off") {
|
|
1944
|
-
const action = trimmed.slice("/map".length).trim();
|
|
1945
|
-
if (action === "on") {
|
|
1946
|
-
repoMapInjectionEnabledRef.current = true;
|
|
1947
|
-
repoMapDirtyRef.current = true;
|
|
1948
|
-
setLiveItems((prev) => [
|
|
1949
|
-
...prev,
|
|
1950
|
-
{ kind: "info", text: "Dynamic repo map injection is on.", id: getId() },
|
|
1951
|
-
]);
|
|
1952
|
-
return;
|
|
1953
|
-
}
|
|
1954
|
-
if (action === "off") {
|
|
1955
|
-
repoMapInjectionEnabledRef.current = false;
|
|
1956
|
-
messagesRef.current = stripRepoMapMessages(messagesRef.current);
|
|
1957
|
-
setLiveItems((prev) => [
|
|
1958
|
-
...prev,
|
|
1959
|
-
{
|
|
1960
|
-
kind: "info",
|
|
1961
|
-
text: "Dynamic repo map injection is off for this session.",
|
|
1962
|
-
id: getId(),
|
|
1963
|
-
},
|
|
1964
|
-
]);
|
|
1965
|
-
return;
|
|
1966
|
-
}
|
|
1967
|
-
if (action === "refresh")
|
|
1968
|
-
repoMapDirtyRef.current = true;
|
|
1969
|
-
const markdown = await refreshRepoMap(getLatestUserText(messagesRef.current));
|
|
1970
|
-
setLiveItems((prev) => [
|
|
1971
|
-
...prev,
|
|
1972
|
-
{
|
|
1973
|
-
kind: "info",
|
|
1974
|
-
text: formatRepoMapCommandOutput(repoMapInjectionEnabledRef.current, markdown, action === "refresh"),
|
|
1975
|
-
id: getId(),
|
|
1976
|
-
},
|
|
1977
|
-
]);
|
|
1978
|
-
return;
|
|
1979
|
-
}
|
|
1980
|
-
// Handle /goals — open the input-area goal picker.
|
|
1981
|
-
if (trimmed === "/goals") {
|
|
1982
|
-
setGoalPickerGoals(loadGoalRunsSync(displayedCwd));
|
|
1983
|
-
setTaskPickerOpen(false);
|
|
1984
|
-
setGoalPickerOpen(true);
|
|
1637
|
+
agentLoop.reset();
|
|
1638
|
+
setSessionTitle(undefined);
|
|
1639
|
+
sessionTitleGeneratedRef.current = false;
|
|
1640
|
+
setLiveItems([{ kind: "info", text: "Session cleared.", id: getId() }]);
|
|
1641
|
+
},
|
|
1642
|
+
openThemeSelector: () => setOverlay("theme"),
|
|
1643
|
+
toggleMarkdown: () => {
|
|
1644
|
+
setRenderMarkdown((prev) => {
|
|
1645
|
+
const next = !prev;
|
|
1646
|
+
setLiveItems([
|
|
1647
|
+
{
|
|
1648
|
+
kind: "info",
|
|
1649
|
+
text: next ? "Rendered markdown mode." : "Raw markdown mode.",
|
|
1650
|
+
id: getId(),
|
|
1651
|
+
},
|
|
1652
|
+
]);
|
|
1653
|
+
return next;
|
|
1654
|
+
});
|
|
1655
|
+
},
|
|
1656
|
+
clearApprovedPlan: () => {
|
|
1657
|
+
approvedPlanPathRef.current = undefined;
|
|
1658
|
+
planStepsRef.current = [];
|
|
1659
|
+
setPlanSteps([]);
|
|
1660
|
+
void replaceSystemPrompt({ clearApprovedPlan: true });
|
|
1661
|
+
setLiveItems([{ kind: "plan_event", event: "dismissed", id: getId() }]);
|
|
1662
|
+
},
|
|
1663
|
+
openGoalsPicker: () => {
|
|
1664
|
+
taskPicker.close();
|
|
1665
|
+
goalPicker.openPicker();
|
|
1666
|
+
},
|
|
1667
|
+
})) {
|
|
1985
1668
|
return;
|
|
1986
1669
|
}
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
text: trimmed,
|
|
2013
|
-
imageCount: hasImages ? inputImages.length : undefined,
|
|
2014
|
-
id: getId(),
|
|
2015
|
-
};
|
|
2016
|
-
setLastUserMessage(trimmed);
|
|
2017
|
-
setDoneStatus(null);
|
|
2018
|
-
finalizeSubmittedUserItem(userItem);
|
|
2019
|
-
// Send the full prompt to the agent, with user args appended if provided
|
|
2020
|
-
try {
|
|
2021
|
-
if (isGoalSetupCommand) {
|
|
2022
|
-
goalSetupPanePendingRef.current = true;
|
|
2023
|
-
await runGoalPromptSetupSequence({
|
|
2024
|
-
userContent,
|
|
2025
|
-
fullPrompt: promptForAgent,
|
|
2026
|
-
messagesRef,
|
|
2027
|
-
setGoalModeAndPrompt,
|
|
2028
|
-
runAgent: (content) => agentLoop.run(content),
|
|
2029
|
-
onStage: appendGoalAgentTransition,
|
|
2030
|
-
});
|
|
2031
|
-
}
|
|
2032
|
-
else {
|
|
2033
|
-
await agentLoop.run(userContent);
|
|
2034
|
-
}
|
|
2035
|
-
}
|
|
2036
|
-
catch (err) {
|
|
2037
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
2038
|
-
log("ERROR", "error", msg);
|
|
2039
|
-
const isAbort = msg.includes("aborted") || msg.includes("abort");
|
|
2040
|
-
if (isGoalSetupCommand)
|
|
2041
|
-
goalSetupPanePendingRef.current = false;
|
|
2042
|
-
setLiveItems((prev) => [
|
|
2043
|
-
...prev,
|
|
2044
|
-
isAbort
|
|
2045
|
-
? { kind: "stopped", text: "Request was stopped.", id: getId() }
|
|
2046
|
-
: toErrorItem(err, getId()),
|
|
2047
|
-
]);
|
|
2048
|
-
}
|
|
2049
|
-
finally {
|
|
2050
|
-
if (isGoalSetupCommand) {
|
|
2051
|
-
setActiveGoalReferences(undefined);
|
|
2052
|
-
const paneTransition = getGoalSetupPaneTransitionAfterRun({
|
|
2053
|
-
isGoalSetupCommand,
|
|
2054
|
-
setupPanePending: goalSetupPanePendingRef.current,
|
|
2055
|
-
});
|
|
2056
|
-
goalSetupPanePendingRef.current = false;
|
|
2057
|
-
if (goalModeStateRef.current !== "off") {
|
|
2058
|
-
await setGoalModeAndPrompt("off");
|
|
2059
|
-
}
|
|
2060
|
-
if (paneTransition) {
|
|
2061
|
-
goalAutoExpandRef.current = false;
|
|
2062
|
-
setGoalAutoExpand(false);
|
|
2063
|
-
setPlanAutoExpand(false);
|
|
2064
|
-
setGoalPickerGoals(loadGoalRunsSync(displayedCwd));
|
|
2065
|
-
setGoalPickerOpen(true);
|
|
2066
|
-
}
|
|
2067
|
-
}
|
|
2068
|
-
}
|
|
2069
|
-
// Reload custom commands in case a setup command created new ones
|
|
2070
|
-
reloadCustomCommands();
|
|
1670
|
+
if (await submitPromptCommand({
|
|
1671
|
+
trimmed,
|
|
1672
|
+
inputImages,
|
|
1673
|
+
cwd: props.cwd,
|
|
1674
|
+
currentModel,
|
|
1675
|
+
customCommands,
|
|
1676
|
+
messagesRef,
|
|
1677
|
+
goalSetupPanePendingRef,
|
|
1678
|
+
goalModeStateRef,
|
|
1679
|
+
goalAutoExpandRef,
|
|
1680
|
+
setActiveGoalReferences,
|
|
1681
|
+
setLastUserMessage,
|
|
1682
|
+
setDoneStatus,
|
|
1683
|
+
finalizeSubmittedUserItem,
|
|
1684
|
+
setGoalModeAndPrompt,
|
|
1685
|
+
runAgent: (content) => agentLoop.run(content),
|
|
1686
|
+
appendGoalAgentTransition,
|
|
1687
|
+
setLiveItems,
|
|
1688
|
+
getId,
|
|
1689
|
+
setGoalAutoExpand,
|
|
1690
|
+
setPlanAutoExpand,
|
|
1691
|
+
closeTaskPicker: taskPicker.close,
|
|
1692
|
+
openGoalPicker: goalPicker.openPicker,
|
|
1693
|
+
reloadCustomCommands,
|
|
1694
|
+
})) {
|
|
2071
1695
|
return;
|
|
2072
1696
|
}
|
|
2073
1697
|
// Check slash commands
|
|
@@ -2149,12 +1773,10 @@ export function App(props) {
|
|
|
2149
1773
|
props.resetUI,
|
|
2150
1774
|
props.sessionStore,
|
|
2151
1775
|
rebuildSystemPrompt,
|
|
2152
|
-
refreshRepoMap,
|
|
2153
1776
|
reloadCustomCommands,
|
|
2154
1777
|
replaceSystemPrompt,
|
|
2155
1778
|
setActiveGoalReferences,
|
|
2156
1779
|
setGoalModeAndPrompt,
|
|
2157
|
-
stripRepoMapMessages,
|
|
2158
1780
|
]);
|
|
2159
1781
|
const handleDoubleExit = useDoublePress(setExitPending, () => process.exit(0));
|
|
2160
1782
|
const handleAbort = useCallback(() => {
|
|
@@ -2343,115 +1965,21 @@ export function App(props) {
|
|
|
2343
1965
|
},
|
|
2344
1966
|
];
|
|
2345
1967
|
}, [customCommands]);
|
|
2346
|
-
const
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
lastPendingHistoryItem: pendingHistoryFlushRef.current.at(-1),
|
|
2362
|
-
lastHistoryItem: history.at(-1),
|
|
2363
|
-
}))
|
|
2364
|
-
? 1
|
|
2365
|
-
: 0;
|
|
2366
|
-
const withPrintedBoundarySpacing = (node) => shouldTopSpacePrintedBoundary ? (_jsx(Box, { flexDirection: "column", marginTop: 1, children: node }, `${item.id}-printed-boundary`)) : (node);
|
|
2367
|
-
switch (item.kind) {
|
|
2368
|
-
case "tombstone":
|
|
2369
|
-
return null;
|
|
2370
|
-
case "banner":
|
|
2371
|
-
return (_jsx(Banner, { version: props.version, model: currentModel, provider: currentProvider, cwd: displayedCwd }, item.id));
|
|
2372
|
-
case "user":
|
|
2373
|
-
return (_jsx(UserMessage, { text: item.text, imageCount: item.imageCount, pasteInfo: item.pasteInfo }, item.id));
|
|
2374
|
-
case "goal":
|
|
2375
|
-
return (_jsx(Box, { paddingLeft: 1, 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: truncateGoalProgressText(item.title) }), item.workerId ? _jsxs(Text, { color: theme.textDim, children: [" \u00B7 worker ", item.workerId] }) : null] }) }, item.id));
|
|
2376
|
-
case "goal_progress": {
|
|
2377
|
-
const color = goalProgressColor(item, theme);
|
|
2378
|
-
const loaderStatus = goalProgressLoaderStatus(item);
|
|
2379
|
-
const hasBody = !!item.detail ||
|
|
2380
|
-
(item.summaryRows !== undefined && item.summaryRows.length > 0) ||
|
|
2381
|
-
(item.summarySections !== undefined && item.summarySections.length > 0);
|
|
2382
|
-
const headerContentWidth = Math.max(10, columns - 3);
|
|
2383
|
-
return withPrintedBoundarySpacing(_jsxs(Box, { flexDirection: "column", paddingLeft: 1, marginTop: 1, flexShrink: 1, children: [_jsxs(Box, { flexDirection: "row", children: [_jsx(ToolUseLoader, { status: loaderStatus, staticDisplay: true, color: color }), _jsx(Box, { flexGrow: 1, width: headerContentWidth, children: _jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: color, bold: true, children: truncateGoalProgressText(item.title) }), item.workerId ? (_jsxs(Text, { color: theme.textDim, children: [" \u00B7 worker ", item.workerId] })) : null] }) })] }), hasBody ? (_jsx(MessageResponse, { children: _jsxs(Box, { flexDirection: "column", flexShrink: 1, children: [item.detail ? (_jsx(Text, { color: theme.textDim, wrap: "wrap", children: truncateGoalProgressText(item.detail) })) : null, item.summaryRows?.map((row) => (_jsxs(Text, { wrap: "truncate", children: [_jsx(Text, { color: theme.textDim, children: row.label.padEnd(12) }), _jsx(Text, { color: theme.text, children: truncateGoalProgressText(row.value) }), row.detail ? (_jsxs(Text, { color: theme.textDim, children: [" \u00B7 ", truncateGoalProgressText(row.detail)] })) : null] }, row.label))), item.summarySections?.map((section) => (_jsxs(Box, { flexDirection: "column", marginTop: 1, flexShrink: 1, children: [_jsx(Text, { color: theme.textDim, bold: true, children: section.title }), section.lines.map((line, sectionLineIndex) => (_jsx(Text, { color: theme.text, wrap: "wrap", children: `• ${truncateGoalProgressText(line)}` }, `${section.title}-${sectionLineIndex}`)))] }, section.title)))] }) })) : null] }, item.id));
|
|
2384
|
-
}
|
|
2385
|
-
case "style_pack": {
|
|
2386
|
-
const names = item.added.map((id) => LANGUAGE_DISPLAY_NAMES[id]);
|
|
2387
|
-
const headerLabel = item.added.length > 1 ? "STYLE PACKS ACTIVE" : "STYLE PACK ACTIVE";
|
|
2388
|
-
return (_jsx(Box, { paddingLeft: 1, marginTop: 1, flexShrink: 1, children: _jsxs(Box, { flexShrink: 1, flexDirection: "column", borderStyle: "round", borderColor: theme.language, paddingX: 1, children: [_jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: theme.language, bold: true, children: "◆ " }), _jsx(Text, { color: theme.language, bold: true, children: headerLabel })] }), _jsx(Text, { color: theme.text, bold: true, wrap: "wrap", children: names.join(", ") }), item.showSetupHint && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: theme.textMuted, children: "Tip: run " }), _jsx(Text, { color: theme.language, bold: true, children: "/setup" }), _jsx(Text, { color: theme.textMuted, children: " to audit this project against the active pack(s)" })] }) }))] }) }, item.id));
|
|
2389
|
-
}
|
|
2390
|
-
case "setup_hint":
|
|
2391
|
-
return (_jsx(Box, { paddingLeft: 1, marginTop: 1, flexShrink: 1, children: _jsxs(Box, { flexShrink: 1, flexDirection: "column", borderStyle: "round", borderColor: theme.language, paddingX: 1, children: [_jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: theme.language, bold: true, children: "◆ " }), _jsx(Text, { color: theme.language, bold: true, children: "NO STYLE PACKS DETECTED" })] }), _jsx(Text, { color: theme.textMuted, wrap: "wrap", children: "This directory has no recognized language manifest at its root." }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: theme.textMuted, children: "Tip: run " }), _jsx(Text, { color: theme.language, bold: true, children: "/setup" }), _jsx(Text, { color: theme.textMuted, children: " to audit project hygiene or bootstrap a new project from scratch" })] }) })] }) }, item.id));
|
|
2392
|
-
case "assistant":
|
|
2393
|
-
return (_jsx(AssistantMessage, { text: item.text, thinking: item.thinking, thinkingMs: item.thinkingMs, renderMarkdown: renderMarkdown, availableTerminalHeight: measuredLiveAreaRows, marginTop: assistantMarginTop }, item.id));
|
|
2394
|
-
case "tool_start":
|
|
2395
|
-
return withPrintedBoundarySpacing(_jsx(ToolExecution, { status: "running", name: item.name, args: item.args, progressOutput: item.progressOutput, animateUntil: item.animateUntil }, item.id));
|
|
2396
|
-
case "tool_done":
|
|
2397
|
-
return withPrintedBoundarySpacing(_jsx(ToolExecution, { status: "done", name: item.name, args: item.args, result: item.result, isError: item.isError, details: item.details }, item.id));
|
|
2398
|
-
case "tool_group":
|
|
2399
|
-
return withPrintedBoundarySpacing(_jsx(ToolGroupExecution, { tools: item.tools }, item.id));
|
|
2400
|
-
case "server_tool_start":
|
|
2401
|
-
return withPrintedBoundarySpacing(_jsx(ServerToolExecution, { status: "running", name: item.name, input: item.input, startedAt: item.startedAt, animateUntil: item.animateUntil }, item.id));
|
|
2402
|
-
case "server_tool_done":
|
|
2403
|
-
return withPrintedBoundarySpacing(_jsx(ServerToolExecution, { status: "done", name: item.name, input: item.input, durationMs: item.durationMs, resultType: item.resultType }, item.id));
|
|
2404
|
-
case "error": {
|
|
2405
|
-
const showMessage = item.message && item.message !== item.headline;
|
|
2406
|
-
return (_jsxs(Box, { flexDirection: "row", paddingLeft: 1, marginTop: 1, flexShrink: 1, children: [_jsx(Box, { width: 2, flexShrink: 0, children: _jsx(Text, { color: theme.error, bold: true, children: "✗ " }) }), _jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Text, { color: theme.error, wrap: "wrap", children: item.headline }), showMessage && (_jsx(Text, { color: theme.textDim, wrap: "wrap", children: item.message })), _jsx(Text, { color: theme.textDim, wrap: "wrap", children: `→ ${item.guidance}` })] })] }, item.id));
|
|
2407
|
-
}
|
|
2408
|
-
case "info":
|
|
2409
|
-
return renderStatusMessage(item.id, "○ ", item.text, theme.commandColor, { muted: true });
|
|
2410
|
-
case "update_notice":
|
|
2411
|
-
return (_jsx(Box, { paddingLeft: 1, marginTop: 1, flexShrink: 1, children: _jsx(Box, { flexShrink: 1, borderStyle: "round", borderColor: theme.commandColor, paddingX: 1, children: _jsxs(Text, { color: theme.commandColor, bold: true, wrap: "wrap", children: ["✨ ", item.text] }) }) }, item.id));
|
|
2412
|
-
case "plan_transition":
|
|
2413
|
-
return renderStatusMessage(item.id, `${BLACK_CIRCLE} `, normalizeStatusText(item.text), theme.commandColor, { bold: true });
|
|
2414
|
-
case "goal_agent_transition":
|
|
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
|
-
case "model_transition":
|
|
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
|
-
case "theme_transition":
|
|
2421
|
-
return renderStatusMessage(item.id, "◐ ", _jsxs(_Fragment, { children: [_jsx(Text, { color: theme.textDim, children: "Theme switched to " }), _jsx(Text, { color: theme.commandColor, bold: true, children: item.themeName })] }), theme.commandColor, { bold: true });
|
|
2422
|
-
case "plan_event": {
|
|
2423
|
-
// Plan-domain status changes (approve / reject / dismiss). Use the
|
|
2424
|
-
// command accent so transient TUI status rows share one purple voice.
|
|
2425
|
-
const label = item.event === "approved"
|
|
2426
|
-
? "Plan approved"
|
|
2427
|
-
: item.event === "rejected"
|
|
2428
|
-
? "Plan rejected"
|
|
2429
|
-
: "Plan dismissed";
|
|
2430
|
-
return renderStatusMessage(item.id, "○ ", _jsxs(_Fragment, { children: [_jsx(Text, { children: label }), item.detail ? _jsx(Text, { color: theme.textDim, children: ` — "${item.detail}"` }) : null] }), theme.commandColor, { bold: true });
|
|
2431
|
-
}
|
|
2432
|
-
case "stopped":
|
|
2433
|
-
// Cancellation / abort acknowledgement (ESC, auto-setup cancel, etc.).
|
|
2434
|
-
// Muted dim treatment — this is an ack, not a state change worth a
|
|
2435
|
-
// gradient. Glyph `⊘` reads as "stop" without being alarming.
|
|
2436
|
-
return renderStatusMessage(item.id, "⊘ ", normalizeStatusText(item.text), theme.commandColor, { bold: true });
|
|
2437
|
-
case "step_done":
|
|
2438
|
-
return (_jsx(Box, { paddingLeft: 1, 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));
|
|
2439
|
-
case "queued": {
|
|
2440
|
-
const suffix = item.imageCount
|
|
2441
|
-
? ` (+${item.imageCount} image${item.imageCount > 1 ? "s" : ""})`
|
|
2442
|
-
: "";
|
|
2443
|
-
return withPrintedBoundarySpacing(_jsxs(Box, { flexDirection: "row", paddingLeft: 1, marginTop: 1, flexShrink: 1, children: [_jsx(Box, { width: 2, flexShrink: 0, children: _jsx(Text, { color: theme.warning, bold: true, children: "• " }) }), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: _jsxs(Text, { color: theme.text, wrap: "wrap", children: [_jsx(Text, { color: theme.textDim, children: "Queued: " }), item.text || "(empty)", suffix] }) })] }, item.id));
|
|
2444
|
-
}
|
|
2445
|
-
case "compacting":
|
|
2446
|
-
return _jsx(CompactionSpinner, { staticDisplay: true }, item.id);
|
|
2447
|
-
case "compacted":
|
|
2448
|
-
return (_jsx(CompactionDone, { originalCount: item.originalCount, newCount: item.newCount, tokensBefore: item.tokensBefore, tokensAfter: item.tokensAfter }, item.id));
|
|
2449
|
-
case "duration":
|
|
2450
|
-
return (_jsx(Box, { paddingLeft: 1, marginTop: 1, children: _jsxs(Text, { color: theme.textDim, children: ["✻ ", item.verb, " ", formatDuration(item.durationMs)] }) }, item.id));
|
|
2451
|
-
case "subagent_group":
|
|
2452
|
-
return withPrintedBoundarySpacing(_jsx(SubAgentPanel, { agents: item.agents, aborted: item.aborted }, item.id));
|
|
2453
|
-
}
|
|
2454
|
-
};
|
|
1968
|
+
const renderItem = (item, index, items) => renderTranscriptItem({
|
|
1969
|
+
item,
|
|
1970
|
+
index,
|
|
1971
|
+
items,
|
|
1972
|
+
pendingHistoryFlushLastItem: pendingHistoryFlushRef.current.at(-1),
|
|
1973
|
+
historyLastItem: history.at(-1),
|
|
1974
|
+
version: props.version,
|
|
1975
|
+
currentModel,
|
|
1976
|
+
currentProvider,
|
|
1977
|
+
displayedCwd,
|
|
1978
|
+
columns,
|
|
1979
|
+
theme,
|
|
1980
|
+
renderMarkdown,
|
|
1981
|
+
measuredLiveAreaRows,
|
|
1982
|
+
});
|
|
2455
1983
|
const openOverlay = useCallback((kind) => {
|
|
2456
1984
|
if (props.resetUI && props.sessionStore && !agentLoop.isRunning) {
|
|
2457
1985
|
props.sessionStore.overlay = kind;
|
|
@@ -2536,6 +2064,10 @@ export function App(props) {
|
|
|
2536
2064
|
return;
|
|
2537
2065
|
}
|
|
2538
2066
|
const decision = decideGoalNextAction(latestRun);
|
|
2067
|
+
if (!shouldKeepGoalRunTrackedAfterDecision(decision)) {
|
|
2068
|
+
runningGoalIdsRef.current.delete(runId);
|
|
2069
|
+
clearGoalModeIfIdle();
|
|
2070
|
+
}
|
|
2539
2071
|
if (decision.kind === "wait")
|
|
2540
2072
|
return;
|
|
2541
2073
|
const choiceKey = getGoalContinuationChoiceKey({ runId: latestRun.id, decision });
|
|
@@ -2640,23 +2172,25 @@ export function App(props) {
|
|
|
2640
2172
|
workerId: completion.worker.id,
|
|
2641
2173
|
status: completion.status,
|
|
2642
2174
|
});
|
|
2175
|
+
const taskProgress = goalTaskProgress(run, run.tasks.find((task) => task.id === completion.worker.goalTaskId));
|
|
2643
2176
|
upsertGoalStatusEntry({
|
|
2644
2177
|
runId: run.id,
|
|
2645
|
-
label:
|
|
2178
|
+
label: run.title,
|
|
2646
2179
|
phase: completion.status === "done" ? "reviewing" : "failed",
|
|
2647
2180
|
startedAt: Date.now(),
|
|
2648
2181
|
detail: completion.status === "done" ? "reviewing result" : "task failed",
|
|
2649
2182
|
workerId: completion.worker.id,
|
|
2650
2183
|
goalNumber: goalNumberForRun(run.id),
|
|
2184
|
+
...taskProgress,
|
|
2651
2185
|
});
|
|
2652
2186
|
runGoalSyntheticEvent(eventText);
|
|
2653
2187
|
void (async () => {
|
|
2654
|
-
if (listGoalWorkers(completion.worker.
|
|
2188
|
+
if (listGoalWorkers(completion.worker.projectPath).some((worker) => worker.status === "running"))
|
|
2655
2189
|
return;
|
|
2656
2190
|
if (activeVerifierRunIdsRef.current.size > 0)
|
|
2657
2191
|
return;
|
|
2658
|
-
const runs = await loadGoalRuns(completion.worker.
|
|
2659
|
-
const queued = runs.find((item) =>
|
|
2192
|
+
const runs = await loadGoalRuns(completion.worker.projectPath);
|
|
2193
|
+
const queued = runs.find((item) => goalRunNeedsExplicitContinuationAfterWorker(item));
|
|
2660
2194
|
if (queued)
|
|
2661
2195
|
setTimeout(() => continueGoalRun(queued.id), 750);
|
|
2662
2196
|
})().catch((err) => log("ERROR", "goal", err instanceof Error ? err.message : String(err)));
|
|
@@ -2670,7 +2204,7 @@ export function App(props) {
|
|
|
2670
2204
|
useEffect(() => {
|
|
2671
2205
|
return subscribeGoalWorkerCompletions((completion) => {
|
|
2672
2206
|
void (async () => {
|
|
2673
|
-
const latestRun = (await loadGoalRuns(completion.worker.
|
|
2207
|
+
const latestRun = (await loadGoalRuns(completion.worker.projectPath)).find((item) => item.id === completion.worker.goalRunId) ?? null;
|
|
2674
2208
|
if (!latestRun) {
|
|
2675
2209
|
log("WARN", "goal", `Worker completion for unknown Goal ${completion.worker.goalRunId}`);
|
|
2676
2210
|
return;
|
|
@@ -2724,6 +2258,9 @@ export function App(props) {
|
|
|
2724
2258
|
}
|
|
2725
2259
|
const decision = decideGoalNextAction(checkedRun);
|
|
2726
2260
|
await appendGoalDecision(props.cwd, checkedRun.id, decision);
|
|
2261
|
+
if (!shouldKeepGoalRunTrackedAfterDecision(decision)) {
|
|
2262
|
+
runningGoalIdsRef.current.delete(checkedRun.id);
|
|
2263
|
+
}
|
|
2727
2264
|
if (decision.kind === "terminal") {
|
|
2728
2265
|
const terminalProgress = formatGoalTerminalProgress(checkedRun);
|
|
2729
2266
|
if (terminalProgress) {
|
|
@@ -2741,8 +2278,10 @@ export function App(props) {
|
|
|
2741
2278
|
phase: "worker_started",
|
|
2742
2279
|
title: decision.workerId
|
|
2743
2280
|
? `Goal working: ${checkedRun.title}`
|
|
2744
|
-
: `Goal
|
|
2745
|
-
detail: decision.
|
|
2281
|
+
: `Goal needs orchestration: ${checkedRun.title}`,
|
|
2282
|
+
detail: decision.workerId
|
|
2283
|
+
? decision.reason
|
|
2284
|
+
: `${decision.reason} Asking the orchestrator to unblock or revise the Goal plan.`,
|
|
2746
2285
|
workerId: decision.workerId,
|
|
2747
2286
|
});
|
|
2748
2287
|
upsertGoalStatusEntry({
|
|
@@ -2754,6 +2293,14 @@ export function App(props) {
|
|
|
2754
2293
|
workerId: decision.workerId,
|
|
2755
2294
|
goalNumber: goalNumberForRun(checkedRun.id),
|
|
2756
2295
|
});
|
|
2296
|
+
if (!decision.workerId) {
|
|
2297
|
+
const eventText = `Goal continuation is waiting with no active worker for Goal ${checkedRun.id} (${checkedRun.title}).\n` +
|
|
2298
|
+
`Reason: ${decision.reason}\n\n` +
|
|
2299
|
+
`Inspect the durable Goal state with the goals tool, resolve blocked dependencies by creating or updating concrete worker tasks, and then continue the Goal. If no local/free action can proceed, record an explicit blocker with exact user instructions. Do not stop after only explaining the state.`;
|
|
2300
|
+
setLastUserMessage("");
|
|
2301
|
+
setDoneStatus(null);
|
|
2302
|
+
await agentLoop.run(eventText);
|
|
2303
|
+
}
|
|
2757
2304
|
return;
|
|
2758
2305
|
}
|
|
2759
2306
|
if (decision.kind === "complete") {
|
|
@@ -2775,14 +2322,38 @@ export function App(props) {
|
|
|
2775
2322
|
return;
|
|
2776
2323
|
}
|
|
2777
2324
|
if (decision.kind === "create_task") {
|
|
2325
|
+
const latestRunBeforeCreate = (await loadGoalRuns(props.cwd)).find((item) => item.id === checkedRun.id) ?? checkedRun;
|
|
2326
|
+
const existingSameTitleTask = latestRunBeforeCreate.tasks.find((item) => item.title === decision.title);
|
|
2327
|
+
if (existingSameTitleTask) {
|
|
2328
|
+
const runWithExistingTask = await upsertGoalRun(props.cwd, {
|
|
2329
|
+
...latestRunBeforeCreate,
|
|
2330
|
+
status: "ready",
|
|
2331
|
+
});
|
|
2332
|
+
appendGoalProgress({
|
|
2333
|
+
kind: "goal_progress",
|
|
2334
|
+
phase: "continuing",
|
|
2335
|
+
title: `Goal task already exists: ${decision.title}`,
|
|
2336
|
+
detail: "Reusing the existing Goal task instead of creating a duplicate.",
|
|
2337
|
+
status: "ready",
|
|
2338
|
+
});
|
|
2339
|
+
startGoalRunRef.current(runWithExistingTask);
|
|
2340
|
+
return;
|
|
2341
|
+
}
|
|
2778
2342
|
await updateGoalTask(props.cwd, checkedRun.id, `auto-${Date.now()}`, {
|
|
2779
2343
|
title: decision.title,
|
|
2780
2344
|
prompt: decision.prompt,
|
|
2781
2345
|
status: "pending",
|
|
2782
2346
|
});
|
|
2783
2347
|
const latestRun = (await loadGoalRuns(props.cwd)).find((item) => item.id === checkedRun.id) ?? checkedRun;
|
|
2784
|
-
await upsertGoalRun(props.cwd, { ...latestRun, status: "ready" });
|
|
2785
|
-
|
|
2348
|
+
const runWithTask = await upsertGoalRun(props.cwd, { ...latestRun, status: "ready" });
|
|
2349
|
+
appendGoalProgress({
|
|
2350
|
+
kind: "goal_progress",
|
|
2351
|
+
phase: "continuing",
|
|
2352
|
+
title: `Goal task created: ${decision.title}`,
|
|
2353
|
+
detail: "Starting the new Goal task now.",
|
|
2354
|
+
status: "ready",
|
|
2355
|
+
});
|
|
2356
|
+
startGoalRunRef.current(runWithTask);
|
|
2786
2357
|
return;
|
|
2787
2358
|
}
|
|
2788
2359
|
if (decision.kind === "blocked") {
|
|
@@ -2844,6 +2415,7 @@ export function App(props) {
|
|
|
2844
2415
|
goalTaskId: decision.task.id,
|
|
2845
2416
|
taskTitle: decision.task.title,
|
|
2846
2417
|
prompt: buildGoalTaskPromptWithReferences(checkedRun, decision.task.prompt),
|
|
2418
|
+
isolateWorktree: shouldRunGoalTaskInMainCheckout(decision.task.title) ? false : undefined,
|
|
2847
2419
|
});
|
|
2848
2420
|
const latestRun = (await loadGoalRuns(props.cwd)).find((item) => item.id === checkedRun.id) ??
|
|
2849
2421
|
runWithAttempt;
|
|
@@ -2866,17 +2438,39 @@ export function App(props) {
|
|
|
2866
2438
|
});
|
|
2867
2439
|
upsertGoalStatusEntry({
|
|
2868
2440
|
runId: checkedRun.id,
|
|
2869
|
-
label:
|
|
2441
|
+
label: checkedRun.title,
|
|
2870
2442
|
phase: "worker",
|
|
2871
2443
|
startedAt: Date.now(),
|
|
2872
2444
|
detail: "background worker running",
|
|
2873
2445
|
workerId: worker.id,
|
|
2874
2446
|
goalNumber: goalNumberForRun(checkedRun.id),
|
|
2447
|
+
...goalTaskProgress(checkedRun, decision.task),
|
|
2875
2448
|
});
|
|
2876
|
-
})().catch((err) => {
|
|
2449
|
+
})().catch(async (err) => {
|
|
2877
2450
|
clearGoalStatusEntry(run.id);
|
|
2878
2451
|
clearGoalModeIfIdle();
|
|
2879
2452
|
log("ERROR", "goal", err instanceof Error ? err.message : String(err));
|
|
2453
|
+
if (isGoalWorktreeDirtyError(err)) {
|
|
2454
|
+
const latestRun = (await loadGoalRuns(props.cwd)).find((item) => item.id === run.id) ?? run;
|
|
2455
|
+
const pausedRun = await upsertGoalRun(props.cwd, buildGoalDirtyWorktreePauseRun(latestRun, err));
|
|
2456
|
+
runningGoalIdsRef.current.delete(pausedRun.id);
|
|
2457
|
+
appendGoalProgress({
|
|
2458
|
+
kind: "goal_progress",
|
|
2459
|
+
phase: "terminal",
|
|
2460
|
+
title: `Goal paused: ${pausedRun.title}`,
|
|
2461
|
+
detail: "Working tree has uncommitted changes; waiting for the user to choose commit, stash, or pause.",
|
|
2462
|
+
status: "paused",
|
|
2463
|
+
});
|
|
2464
|
+
setLiveItems((prev) => [
|
|
2465
|
+
...prev,
|
|
2466
|
+
{ kind: "info", text: goalDirtyWorktreeInfoText(), id: getId() },
|
|
2467
|
+
]);
|
|
2468
|
+
void agentLoop.run(buildGoalDirtyWorktreeUserPrompt(err)).catch((agentErr) => {
|
|
2469
|
+
log("ERROR", "goal", agentErr instanceof Error ? agentErr.message : String(agentErr));
|
|
2470
|
+
setLiveItems((prev) => [...prev, toErrorItem(agentErr, getId(), "Goal")]);
|
|
2471
|
+
});
|
|
2472
|
+
return;
|
|
2473
|
+
}
|
|
2880
2474
|
setLiveItems((prev) => [...prev, toErrorItem(err, getId(), "Goal")]);
|
|
2881
2475
|
});
|
|
2882
2476
|
}, [
|
|
@@ -2884,6 +2478,7 @@ export function App(props) {
|
|
|
2884
2478
|
currentProvider,
|
|
2885
2479
|
currentModel,
|
|
2886
2480
|
thinkingLevel,
|
|
2481
|
+
agentLoop,
|
|
2887
2482
|
appendGoalProgress,
|
|
2888
2483
|
clearGoalModeIfIdle,
|
|
2889
2484
|
clearGoalStatusEntry,
|
|
@@ -2916,6 +2511,30 @@ export function App(props) {
|
|
|
2916
2511
|
clearGoalModeIfIdle();
|
|
2917
2512
|
return;
|
|
2918
2513
|
}
|
|
2514
|
+
const integration = await checkGoalWorktreeIntegration(props.cwd, run);
|
|
2515
|
+
if (!integration.ok) {
|
|
2516
|
+
const runWithEvidence = (await appendGoalEvidence(props.cwd, run.id, {
|
|
2517
|
+
kind: "summary",
|
|
2518
|
+
label: "Goal worktree integration required",
|
|
2519
|
+
content: integration.summary,
|
|
2520
|
+
})) ?? run;
|
|
2521
|
+
await upsertGoalRun(props.cwd, {
|
|
2522
|
+
...runWithEvidence,
|
|
2523
|
+
status: "blocked",
|
|
2524
|
+
blockers: Array.from(new Set([...runWithEvidence.blockers, integration.summary])),
|
|
2525
|
+
});
|
|
2526
|
+
appendGoalProgress({
|
|
2527
|
+
kind: "goal_progress",
|
|
2528
|
+
phase: "terminal",
|
|
2529
|
+
title: `Goal blocked before verifier: ${run.title}`,
|
|
2530
|
+
detail: integration.summary,
|
|
2531
|
+
status: "blocked",
|
|
2532
|
+
});
|
|
2533
|
+
runningGoalIdsRef.current.delete(run.id);
|
|
2534
|
+
clearGoalStatusEntry(run.id);
|
|
2535
|
+
clearGoalModeIfIdle();
|
|
2536
|
+
return;
|
|
2537
|
+
}
|
|
2919
2538
|
activeVerifierRunIdsRef.current.add(run.id);
|
|
2920
2539
|
await upsertGoalRun(props.cwd, {
|
|
2921
2540
|
...run,
|
|
@@ -2940,7 +2559,7 @@ export function App(props) {
|
|
|
2940
2559
|
goalNumber: goalNumberForRun(run.id),
|
|
2941
2560
|
});
|
|
2942
2561
|
void runGoalVerifierCommand({
|
|
2943
|
-
cwd: props.cwd,
|
|
2562
|
+
cwd: run.verifier.cwd ?? props.cwd,
|
|
2944
2563
|
runId: run.id,
|
|
2945
2564
|
command: run.verifier.command,
|
|
2946
2565
|
timeoutMs: verifierTimeoutMs,
|
|
@@ -2958,6 +2577,7 @@ export function App(props) {
|
|
|
2958
2577
|
...latestRun.verifier,
|
|
2959
2578
|
description: latestRun.verifier?.description ?? "Goal verifier",
|
|
2960
2579
|
command: run.verifier?.command,
|
|
2580
|
+
...(run.verifier?.cwd ? { cwd: run.verifier.cwd } : {}),
|
|
2961
2581
|
lastResult: verification,
|
|
2962
2582
|
},
|
|
2963
2583
|
...(status === "pass"
|
|
@@ -2987,7 +2607,7 @@ export function App(props) {
|
|
|
2987
2607
|
await appendGoalDecision(props.cwd, run.id, {
|
|
2988
2608
|
kind: `verifier_${status}`,
|
|
2989
2609
|
reason: `${failureClass}: verifier exited with code ${verification.exitCode ?? 1}.`,
|
|
2990
|
-
content: `outputPath=${outputPath ?? ""}; durationMs=${durationMs}`,
|
|
2610
|
+
content: `outputPath=${outputPath ?? ""}; cwd=${run.verifier?.cwd ?? props.cwd}; durationMs=${durationMs}`,
|
|
2991
2611
|
});
|
|
2992
2612
|
appendGoalProgress({
|
|
2993
2613
|
kind: "goal_progress",
|
|
@@ -3034,11 +2654,7 @@ export function App(props) {
|
|
|
3034
2654
|
if (run.activeWorkerId)
|
|
3035
2655
|
await stopGoalWorker(run.activeWorkerId);
|
|
3036
2656
|
const latestRun = (await loadGoalRuns(props.cwd)).find((item) => item.id === run.id) ?? run;
|
|
3037
|
-
await upsertGoalRun(props.cwd,
|
|
3038
|
-
...latestRun,
|
|
3039
|
-
status: "paused",
|
|
3040
|
-
activeWorkerId: undefined,
|
|
3041
|
-
});
|
|
2657
|
+
await upsertGoalRun(props.cwd, buildGoalUserPauseRun(latestRun));
|
|
3042
2658
|
appendGoalProgress({
|
|
3043
2659
|
kind: "goal_progress",
|
|
3044
2660
|
phase: "terminal",
|
|
@@ -3088,8 +2704,7 @@ export function App(props) {
|
|
|
3088
2704
|
})();
|
|
3089
2705
|
return;
|
|
3090
2706
|
}
|
|
3091
|
-
|
|
3092
|
-
props.terminalHistoryPrinter?.clear();
|
|
2707
|
+
clearPendingHistory();
|
|
3093
2708
|
setHistory([{ kind: "banner", id: "banner" }]);
|
|
3094
2709
|
setLiveItems([]);
|
|
3095
2710
|
messagesRef.current = messagesRef.current.slice(0, 1);
|
|
@@ -3142,13 +2757,6 @@ export function App(props) {
|
|
|
3142
2757
|
log("WARN", "pixel", `chdir failed: ${err.message}`);
|
|
3143
2758
|
}
|
|
3144
2759
|
cwdRef.current = prep.projectPath;
|
|
3145
|
-
repoMapDirtyRef.current = true;
|
|
3146
|
-
repoMapMarkdownRef.current = "";
|
|
3147
|
-
repoMapSnapshotRef.current = undefined;
|
|
3148
|
-
repoMapChangedCountRef.current = 0;
|
|
3149
|
-
repoMapCacheRef.current = createRepoMapCache();
|
|
3150
|
-
props.repoMapChangedFilesRef?.current.clear();
|
|
3151
|
-
props.repoMapReadFilesRef?.current.clear();
|
|
3152
2760
|
setDisplayedCwd(prep.projectPath);
|
|
3153
2761
|
let toolsForPixelFix = currentToolsRef.current;
|
|
3154
2762
|
if (props.rebuildToolsForCwd) {
|
|
@@ -3172,8 +2780,7 @@ export function App(props) {
|
|
|
3172
2780
|
});
|
|
3173
2781
|
// Now that the cwd swap is committed, reset chat. Do not clear the
|
|
3174
2782
|
// terminal here; terminal clear sequences can erase saved scrollback.
|
|
3175
|
-
|
|
3176
|
-
props.terminalHistoryPrinter?.clear();
|
|
2783
|
+
clearPendingHistory();
|
|
3177
2784
|
setHistory([{ kind: "banner", id: "banner" }]);
|
|
3178
2785
|
setLiveItems([]);
|
|
3179
2786
|
messagesRef.current = messagesRef.current.slice(0, 1);
|
|
@@ -3219,60 +2826,26 @@ export function App(props) {
|
|
|
3219
2826
|
}, [runAllPixel, props.sessionStore]);
|
|
3220
2827
|
const isSkillsView = overlay === "skills";
|
|
3221
2828
|
const isPlanView = overlay === "plan";
|
|
3222
|
-
const footerStatusLayout =
|
|
2829
|
+
const { footerStatusLayout, activityVisible, stallStatusVisible, statusSlotVisible, mainControlsRef, measuredLiveAreaRows, } = useChatLayoutMeasurements({
|
|
2830
|
+
rows,
|
|
3223
2831
|
columns,
|
|
3224
2832
|
backgroundTaskCount: bgTasks.length,
|
|
3225
2833
|
updatePending,
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
const controlsObserverRef = useRef(null);
|
|
3233
|
-
const mainControlsRef = useCallback((node) => {
|
|
3234
|
-
if (controlsObserverRef.current) {
|
|
3235
|
-
controlsObserverRef.current.disconnect();
|
|
3236
|
-
controlsObserverRef.current = null;
|
|
3237
|
-
}
|
|
3238
|
-
if (!node || typeof ResizeObserver === "undefined")
|
|
3239
|
-
return;
|
|
3240
|
-
const observer = new ResizeObserver((entries) => {
|
|
3241
|
-
const entry = entries[0];
|
|
3242
|
-
if (!entry)
|
|
3243
|
-
return;
|
|
3244
|
-
const roundedHeight = Math.round(entry.contentRect.height);
|
|
3245
|
-
setControlsHeight((prev) => (roundedHeight !== prev ? roundedHeight : prev));
|
|
3246
|
-
});
|
|
3247
|
-
observer.observe(node);
|
|
3248
|
-
controlsObserverRef.current = observer;
|
|
3249
|
-
}, []);
|
|
3250
|
-
useEffect(() => () => controlsObserverRef.current?.disconnect(), []);
|
|
3251
|
-
const footerFitsOnOneLine = doesFooterFitOnOneLine({
|
|
3252
|
-
columns,
|
|
3253
|
-
model: currentModel,
|
|
3254
|
-
tokensIn: agentLoop.contextUsed,
|
|
2834
|
+
agentRunning: agentLoop.isRunning,
|
|
2835
|
+
activityPhase: agentLoop.activityPhase,
|
|
2836
|
+
stallError: agentLoop.stallError,
|
|
2837
|
+
doneStatus,
|
|
2838
|
+
currentModel,
|
|
2839
|
+
contextUsed: agentLoop.contextUsed,
|
|
3255
2840
|
contextWindowOptions,
|
|
3256
|
-
|
|
2841
|
+
displayedCwd,
|
|
3257
2842
|
gitBranch,
|
|
3258
2843
|
thinkingLevel,
|
|
3259
2844
|
goalMode,
|
|
3260
|
-
});
|
|
3261
|
-
const chatControlsLayout = getChatControlsLayoutDecision({
|
|
3262
|
-
rows,
|
|
3263
|
-
columns,
|
|
3264
|
-
agentRunning: agentLoop.isRunning,
|
|
3265
|
-
activityVisible,
|
|
3266
|
-
doneStatusVisible,
|
|
3267
|
-
stallStatusVisible,
|
|
3268
2845
|
exitPending,
|
|
3269
|
-
footerStatusLayout,
|
|
3270
2846
|
taskBarExpanded,
|
|
3271
2847
|
goalStatusEntryCount: goalStatusEntries.length,
|
|
3272
|
-
footerFitsOnOneLine,
|
|
3273
2848
|
});
|
|
3274
|
-
const stableControlsRows = controlsHeight > 0 ? controlsHeight : chatControlsLayout.controlsRows;
|
|
3275
|
-
const measuredLiveAreaRows = Math.max(MIN_LIVE_AREA_ROWS, rows - stableControlsRows - 1);
|
|
3276
2849
|
const isPixelView = overlay === "pixel";
|
|
3277
2850
|
const hasLiveAssistantItem = liveItems.some((item) => item.kind === "assistant");
|
|
3278
2851
|
const rawVisibleStreamingText = goalModeStateRef.current === "planner" || hasLiveAssistantItem ? "" : agentLoop.streamingText;
|
|
@@ -3290,7 +2863,7 @@ export function App(props) {
|
|
|
3290
2863
|
queueFlush([
|
|
3291
2864
|
{
|
|
3292
2865
|
kind: "assistant",
|
|
3293
|
-
text: split.flushedText,
|
|
2866
|
+
text: stripDoneMarkers(split.flushedText),
|
|
3294
2867
|
continuation: streamedAssistantFlushRef.current.flushedChars > 0,
|
|
3295
2868
|
id: getId(),
|
|
3296
2869
|
},
|
|
@@ -3306,13 +2879,20 @@ export function App(props) {
|
|
|
3306
2879
|
text: rawVisibleStreamingText,
|
|
3307
2880
|
};
|
|
3308
2881
|
}, [rawVisibleStreamingText, queueFlush]);
|
|
3309
|
-
const visibleStreamingText = rawVisibleStreamingText.slice(streamedAssistantFlushRef.current.flushedChars);
|
|
3310
|
-
const shouldReserveStreamingSpacing = agentLoop.isRunning &&
|
|
3311
|
-
!hasLiveAssistantItem &&
|
|
3312
|
-
(visibleStreamingText.trim().length > 0 || liveItems.some(isAgentSpacingItem));
|
|
2882
|
+
const visibleStreamingText = stripDoneMarkers(rawVisibleStreamingText.slice(streamedAssistantFlushRef.current.flushedChars));
|
|
3313
2883
|
const lastLiveItem = liveItems.at(-1);
|
|
3314
2884
|
const lastPendingHistoryItem = pendingHistoryFlushRef.current.at(-1);
|
|
3315
2885
|
const lastHistoryItem = history.at(-1);
|
|
2886
|
+
const previousTranscriptItem = lastPendingHistoryItem ?? lastHistoryItem;
|
|
2887
|
+
const isAwaitingAssistantAfterUser = agentLoop.isRunning &&
|
|
2888
|
+
!hasLiveAssistantItem &&
|
|
2889
|
+
visibleStreamingText.trim().length === 0 &&
|
|
2890
|
+
(lastLiveItem?.kind === "user" || (!lastLiveItem && previousTranscriptItem?.kind === "user"));
|
|
2891
|
+
const shouldReserveStreamingSpacing = agentLoop.isRunning &&
|
|
2892
|
+
!hasLiveAssistantItem &&
|
|
2893
|
+
(visibleStreamingText.trim().length > 0 ||
|
|
2894
|
+
liveItems.some(isTranscriptSpacingItem) ||
|
|
2895
|
+
isAwaitingAssistantAfterUser);
|
|
3316
2896
|
const shouldTopSpaceStreamingText = shouldTopSpaceStreamingAssistant({
|
|
3317
2897
|
visibleStreamingText,
|
|
3318
2898
|
lastLiveItem,
|
|
@@ -3328,215 +2908,282 @@ export function App(props) {
|
|
|
3328
2908
|
lastPendingHistoryItem,
|
|
3329
2909
|
lastHistoryItem,
|
|
3330
2910
|
});
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
2911
|
+
const handleCloseRemountableOverlay = () => {
|
|
2912
|
+
if (props.resetUI && props.sessionStore && !agentLoop.isRunning) {
|
|
2913
|
+
props.sessionStore.overlay = null;
|
|
2914
|
+
props.resetUI();
|
|
2915
|
+
return;
|
|
2916
|
+
}
|
|
2917
|
+
if (props.sessionStore) {
|
|
2918
|
+
props.sessionStore.overlay = null;
|
|
2919
|
+
if (agentLoop.isRunning)
|
|
2920
|
+
props.sessionStore.pendingResetUI = true;
|
|
2921
|
+
}
|
|
2922
|
+
setOverlay(null);
|
|
2923
|
+
};
|
|
2924
|
+
const handleEnterPlanMode = useCallback(async (reason) => {
|
|
2925
|
+
await setPlanModeAndPrompt(true);
|
|
2926
|
+
const detail = reason ? `Plan mode ON — ${reason}` : "Plan mode ON";
|
|
2927
|
+
setLiveItems((prev) => [
|
|
2928
|
+
...prev,
|
|
2929
|
+
{ kind: "plan_transition", text: detail, id: getId(), active: true },
|
|
2930
|
+
]);
|
|
2931
|
+
}, [setPlanModeAndPrompt]);
|
|
2932
|
+
const handleExitPlanMode = useCallback(async (planPath) => {
|
|
2933
|
+
await setPlanModeAndPrompt(false);
|
|
2934
|
+
planOverlayPendingRef.current = true;
|
|
2935
|
+
setPlanAutoExpand(true);
|
|
2936
|
+
if (props.sessionStore) {
|
|
2937
|
+
props.sessionStore.overlay = "plan";
|
|
2938
|
+
props.sessionStore.planAutoExpand = true;
|
|
2939
|
+
}
|
|
2940
|
+
setOverlay("plan");
|
|
2941
|
+
setLiveItems((prev) => [
|
|
2942
|
+
...prev,
|
|
2943
|
+
{
|
|
2944
|
+
kind: "plan_transition",
|
|
2945
|
+
text: `Plan ready for review: ${planPath}`,
|
|
2946
|
+
id: getId(),
|
|
2947
|
+
active: false,
|
|
2948
|
+
},
|
|
2949
|
+
]);
|
|
2950
|
+
return "Plan submitted for user review. Wait for the user to approve, reject, or dismiss it before implementing.";
|
|
2951
|
+
}, [props.sessionStore, setPlanModeAndPrompt]);
|
|
2952
|
+
useEffect(() => {
|
|
2953
|
+
if (!props.planCallbacks)
|
|
2954
|
+
return;
|
|
2955
|
+
props.planCallbacks.onEnterPlan = handleEnterPlanMode;
|
|
2956
|
+
props.planCallbacks.onExitPlan = handleExitPlanMode;
|
|
2957
|
+
}, [handleEnterPlanMode, handleExitPlanMode, props.planCallbacks]);
|
|
2958
|
+
const handleClosePlanOverlay = () => {
|
|
2959
|
+
planOverlayPendingRef.current = false;
|
|
2960
|
+
if (props.resetUI && props.sessionStore && !agentLoop.isRunning) {
|
|
2961
|
+
props.sessionStore.overlay = null;
|
|
2962
|
+
props.sessionStore.planAutoExpand = false;
|
|
2963
|
+
props.resetUI();
|
|
2964
|
+
return;
|
|
2965
|
+
}
|
|
2966
|
+
if (props.sessionStore) {
|
|
2967
|
+
props.sessionStore.overlay = null;
|
|
2968
|
+
props.sessionStore.planAutoExpand = false;
|
|
2969
|
+
if (agentLoop.isRunning)
|
|
2970
|
+
props.sessionStore.pendingResetUI = true;
|
|
2971
|
+
}
|
|
2972
|
+
setPlanAutoExpand(false);
|
|
2973
|
+
setOverlay(null);
|
|
2974
|
+
};
|
|
2975
|
+
const handlePixelFixOne = (entry) => {
|
|
2976
|
+
setOverlay(null);
|
|
2977
|
+
startPixelFix(entry.errorId);
|
|
2978
|
+
};
|
|
2979
|
+
const handlePixelFixAll = (entries) => {
|
|
2980
|
+
const first = entries.find((entry) => entry.status === "open") ?? entries[0];
|
|
2981
|
+
if (!first)
|
|
2982
|
+
return;
|
|
2983
|
+
setOverlay(null);
|
|
2984
|
+
setRunAllPixel(true);
|
|
2985
|
+
startPixelFix(first.errorId);
|
|
2986
|
+
};
|
|
2987
|
+
const handleApprovePlan = (planPath) => {
|
|
2988
|
+
log("INFO", "plan", "Plan approved — transitioning to implementation", {
|
|
2989
|
+
planPath,
|
|
2990
|
+
});
|
|
2991
|
+
planOverlayPendingRef.current = false;
|
|
2992
|
+
void (async () => {
|
|
2993
|
+
try {
|
|
2994
|
+
// Read plan steps for progress tracking — handed to the new
|
|
2995
|
+
// mount via sessionStore.planSteps below.
|
|
2996
|
+
const planContent = await import("node:fs/promises").then(({ readFile }) => readFile(planPath, "utf-8"));
|
|
2997
|
+
const steps = extractPlanSteps(planContent);
|
|
2998
|
+
// Build the new system prompt with the approved plan baked in.
|
|
2999
|
+
const newPrompt = await rebuildSystemPrompt({
|
|
3000
|
+
approvedPlanPath: planPath,
|
|
3387
3001
|
});
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
const newPrompt = await rebuildSystemPrompt({
|
|
3397
|
-
approvedPlanPath: planPath,
|
|
3398
|
-
});
|
|
3399
|
-
// Create a new session file BEFORE remount so the new tree
|
|
3400
|
-
// picks it up via sessionStore.sessionPath.
|
|
3401
|
-
let newSessionPath;
|
|
3402
|
-
const sm = sessionManagerRef.current;
|
|
3403
|
-
if (sm) {
|
|
3404
|
-
const s = await sm.create(props.cwd, currentProvider, currentModel);
|
|
3405
|
-
newSessionPath = s.path;
|
|
3406
|
-
}
|
|
3407
|
-
if (props.resetUI && props.sessionStore) {
|
|
3408
|
-
// Clear the overlay so the new mount lands on the chat,
|
|
3409
|
-
// not back inside the plan pane.
|
|
3410
|
-
props.sessionStore.overlay = null;
|
|
3411
|
-
props.sessionStore.planAutoExpand = false;
|
|
3412
|
-
props.resetUI({
|
|
3413
|
-
wipeSession: true,
|
|
3414
|
-
messages: [{ role: "system", content: newPrompt }],
|
|
3415
|
-
approvedPlanPath: planPath,
|
|
3416
|
-
planSteps: steps,
|
|
3417
|
-
sessionPath: newSessionPath,
|
|
3418
|
-
pendingAction: {
|
|
3419
|
-
prompt: "The plan has been approved. Implement it now, following each step in order.",
|
|
3420
|
-
planEvent: { event: "approved" },
|
|
3421
|
-
},
|
|
3422
|
-
});
|
|
3423
|
-
return;
|
|
3424
|
-
}
|
|
3425
|
-
// Fallback path (resetUI not wired — tests). Mutate in place.
|
|
3426
|
-
approvedPlanPathRef.current = planPath;
|
|
3427
|
-
planStepsRef.current = steps;
|
|
3428
|
-
setPlanSteps(steps);
|
|
3429
|
-
pendingHistoryFlushRef.current = [];
|
|
3430
|
-
props.terminalHistoryPrinter?.clear();
|
|
3431
|
-
setHistory([{ kind: "banner", id: "banner" }]);
|
|
3432
|
-
setLiveItems([]);
|
|
3433
|
-
setPlanAutoExpand(false);
|
|
3434
|
-
setOverlay(null);
|
|
3435
|
-
messagesRef.current = [{ role: "system", content: newPrompt }];
|
|
3436
|
-
agentLoop.reset();
|
|
3437
|
-
persistedIndexRef.current = messagesRef.current.length;
|
|
3438
|
-
if (newSessionPath)
|
|
3439
|
-
sessionPathRef.current = newSessionPath;
|
|
3440
|
-
setLiveItems([
|
|
3441
|
-
{
|
|
3442
|
-
kind: "info",
|
|
3443
|
-
text: "Plan approved — starting fresh session for implementation",
|
|
3444
|
-
id: getId(),
|
|
3445
|
-
},
|
|
3446
|
-
]);
|
|
3447
|
-
setDoneStatus(null);
|
|
3448
|
-
await agentLoop.run("The plan has been approved. Implement it now, following each step in order.");
|
|
3449
|
-
}
|
|
3450
|
-
catch (err) {
|
|
3451
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3452
|
-
log("ERROR", "error", errMsg);
|
|
3453
|
-
setLiveItems((prev) => [...prev, toErrorItem(err, getId())]);
|
|
3454
|
-
}
|
|
3455
|
-
})();
|
|
3456
|
-
}, onReject: (planPath, feedback) => {
|
|
3457
|
-
planOverlayPendingRef.current = false;
|
|
3458
|
-
const rejectionMsg = `The plan at ${planPath} was rejected.\n\nFeedback: ${feedback}\n\n` +
|
|
3459
|
-
`Please revise the plan based on this feedback.`;
|
|
3002
|
+
// Create a new session file BEFORE remount so the new tree
|
|
3003
|
+
// picks it up via sessionStore.sessionPath.
|
|
3004
|
+
let newSessionPath;
|
|
3005
|
+
const sm = sessionManagerRef.current;
|
|
3006
|
+
if (sm) {
|
|
3007
|
+
const s = await sm.create(props.cwd, currentProvider, currentModel);
|
|
3008
|
+
newSessionPath = s.path;
|
|
3009
|
+
}
|
|
3460
3010
|
if (props.resetUI && props.sessionStore) {
|
|
3011
|
+
// Clear the overlay so the new mount lands on the chat,
|
|
3012
|
+
// not back inside the plan pane.
|
|
3461
3013
|
props.sessionStore.overlay = null;
|
|
3462
3014
|
props.sessionStore.planAutoExpand = false;
|
|
3463
|
-
// No wipeSession — keep history and messages so the agent picks
|
|
3464
|
-
// up the rejection mid-conversation.
|
|
3465
3015
|
props.resetUI({
|
|
3016
|
+
wipeSession: true,
|
|
3017
|
+
messages: [{ role: "system", content: newPrompt }],
|
|
3018
|
+
approvedPlanPath: planPath,
|
|
3019
|
+
planSteps: steps,
|
|
3020
|
+
sessionPath: newSessionPath,
|
|
3466
3021
|
pendingAction: {
|
|
3467
|
-
prompt:
|
|
3468
|
-
planEvent: { event: "
|
|
3022
|
+
prompt: "The plan has been approved. Implement it now, following each step in order.",
|
|
3023
|
+
planEvent: { event: "approved" },
|
|
3469
3024
|
},
|
|
3470
3025
|
});
|
|
3471
3026
|
return;
|
|
3472
3027
|
}
|
|
3028
|
+
// Fallback path (resetUI not wired — tests). Mutate in place.
|
|
3029
|
+
approvedPlanPathRef.current = planPath;
|
|
3030
|
+
planStepsRef.current = steps;
|
|
3031
|
+
setPlanSteps(steps);
|
|
3032
|
+
clearPendingHistory();
|
|
3033
|
+
setHistory([{ kind: "banner", id: "banner" }]);
|
|
3034
|
+
setLiveItems([]);
|
|
3473
3035
|
setPlanAutoExpand(false);
|
|
3474
3036
|
setOverlay(null);
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3037
|
+
messagesRef.current = [{ role: "system", content: newPrompt }];
|
|
3038
|
+
agentLoop.reset();
|
|
3039
|
+
persistedIndexRef.current = messagesRef.current.length;
|
|
3040
|
+
if (newSessionPath)
|
|
3041
|
+
sessionPathRef.current = newSessionPath;
|
|
3042
|
+
setLiveItems([
|
|
3043
|
+
{
|
|
3044
|
+
kind: "info",
|
|
3045
|
+
text: "Plan approved — starting fresh session for implementation",
|
|
3046
|
+
id: getId(),
|
|
3047
|
+
},
|
|
3479
3048
|
]);
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3049
|
+
setDoneStatus(null);
|
|
3050
|
+
await agentLoop.run("The plan has been approved. Implement it now, following each step in order.");
|
|
3051
|
+
}
|
|
3052
|
+
catch (err) {
|
|
3053
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3054
|
+
log("ERROR", "error", errMsg);
|
|
3055
|
+
setLiveItems((prev) => [...prev, toErrorItem(err, getId())]);
|
|
3056
|
+
}
|
|
3057
|
+
})();
|
|
3058
|
+
};
|
|
3059
|
+
const handleRejectPlan = (planPath, feedback) => {
|
|
3060
|
+
planOverlayPendingRef.current = false;
|
|
3061
|
+
const rejectionMsg = `The plan at ${planPath} was rejected.\n\nFeedback: ${feedback}\n\n` +
|
|
3062
|
+
`Please revise the plan based on this feedback.`;
|
|
3063
|
+
if (props.resetUI && props.sessionStore) {
|
|
3064
|
+
props.sessionStore.overlay = null;
|
|
3065
|
+
props.sessionStore.planAutoExpand = false;
|
|
3066
|
+
// No wipeSession — keep history and messages so the agent picks
|
|
3067
|
+
// up the rejection mid-conversation.
|
|
3068
|
+
props.resetUI({
|
|
3069
|
+
pendingAction: {
|
|
3070
|
+
prompt: rejectionMsg,
|
|
3071
|
+
planEvent: { event: "rejected", detail: feedback },
|
|
3072
|
+
},
|
|
3073
|
+
});
|
|
3074
|
+
return;
|
|
3075
|
+
}
|
|
3076
|
+
setPlanAutoExpand(false);
|
|
3077
|
+
setOverlay(null);
|
|
3078
|
+
setDoneStatus(null);
|
|
3079
|
+
setLiveItems((prev) => [
|
|
3080
|
+
...prev,
|
|
3081
|
+
{ kind: "info", text: `Plan rejected — "${feedback}"`, id: getId() },
|
|
3082
|
+
]);
|
|
3083
|
+
void agentLoop.run(rejectionMsg).catch((err) => {
|
|
3084
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3085
|
+
log("ERROR", "error", errMsg);
|
|
3086
|
+
setLiveItems((prev) => [...prev, toErrorItem(err, getId())]);
|
|
3087
|
+
});
|
|
3088
|
+
};
|
|
3089
|
+
const handleRunGoalFromPicker = (run) => {
|
|
3090
|
+
setDoneStatus(null);
|
|
3091
|
+
appendGoalProgress({
|
|
3092
|
+
kind: "goal_progress",
|
|
3093
|
+
phase: "continuing",
|
|
3094
|
+
title: `Goal run requested: ${run.title}`,
|
|
3095
|
+
detail: "Enter pressed in Ctrl+G; starting the Goal orchestrator.",
|
|
3096
|
+
status: run.status,
|
|
3097
|
+
});
|
|
3098
|
+
log("INFO", "goal", `Goal run requested from Ctrl+G: ${run.title}`, { id: run.id });
|
|
3099
|
+
void (async () => {
|
|
3100
|
+
const latestRun = (await loadGoalRuns(props.cwd)).find((item) => item.id === run.id) ?? run;
|
|
3101
|
+
const requestedAt = new Date().toISOString();
|
|
3102
|
+
const runWithContinuation = await upsertGoalRun(props.cwd, {
|
|
3103
|
+
...latestRun,
|
|
3104
|
+
status: latestRun.status === "running" || latestRun.status === "verifying"
|
|
3105
|
+
? latestRun.status
|
|
3106
|
+
: "ready",
|
|
3107
|
+
continueRequestedAt: requestedAt,
|
|
3108
|
+
blockers: goalHasBlockingPrerequisites(latestRun) ? latestRun.blockers : [],
|
|
3109
|
+
evidence: [
|
|
3110
|
+
...latestRun.evidence,
|
|
3111
|
+
{
|
|
3112
|
+
id: `goal-rerun-${requestedAt}`,
|
|
3113
|
+
kind: "summary",
|
|
3114
|
+
label: "Goal rerun requested",
|
|
3115
|
+
content: "Continuation requested from Ctrl+G; the orchestrator will choose the next eligible Goal action.",
|
|
3116
|
+
createdAt: requestedAt,
|
|
3117
|
+
},
|
|
3118
|
+
],
|
|
3119
|
+
});
|
|
3120
|
+
startGoalRun(runWithContinuation);
|
|
3121
|
+
})().catch((err) => {
|
|
3122
|
+
log("ERROR", "goal", err instanceof Error ? err.message : String(err));
|
|
3123
|
+
setLiveItems((prev) => [...prev, toErrorItem(err, getId(), "Goal")]);
|
|
3124
|
+
});
|
|
3125
|
+
};
|
|
3126
|
+
const handleDeleteGoalSideEffects = async (run) => {
|
|
3127
|
+
runningGoalIdsRef.current.delete(run.id);
|
|
3128
|
+
const latestRun = (await loadGoalRuns(props.cwd)).find((item) => item.id === run.id) ?? run;
|
|
3129
|
+
if (latestRun.activeWorkerId)
|
|
3130
|
+
await stopGoalWorker(latestRun.activeWorkerId);
|
|
3131
|
+
clearGoalStatusEntry(run.id);
|
|
3132
|
+
clearGoalModeIfIdle();
|
|
3133
|
+
};
|
|
3134
|
+
const handleGoalPickerError = (err) => {
|
|
3135
|
+
log("ERROR", "goal", err instanceof Error ? err.message : String(err));
|
|
3136
|
+
setLiveItems((prev) => [...prev, toErrorItem(err, getId(), "Goal")]);
|
|
3137
|
+
};
|
|
3138
|
+
const goalPicker = useGoalPickerController({
|
|
3139
|
+
cwd: props.cwd,
|
|
3140
|
+
onRunGoal: handleRunGoalFromPicker,
|
|
3141
|
+
onDeleteGoalSideEffects: handleDeleteGoalSideEffects,
|
|
3142
|
+
onPauseGoal: pauseGoalRun,
|
|
3143
|
+
onError: handleGoalPickerError,
|
|
3144
|
+
});
|
|
3145
|
+
const handleToggleTasks = () => {
|
|
3146
|
+
goalPicker.close();
|
|
3147
|
+
taskPicker.toggle();
|
|
3148
|
+
};
|
|
3149
|
+
const handleToggleGoalPicker = () => {
|
|
3150
|
+
taskPicker.close();
|
|
3151
|
+
goalPicker.toggle();
|
|
3152
|
+
};
|
|
3153
|
+
const fullScreenOverlay = isPixelView
|
|
3154
|
+
? "pixel"
|
|
3155
|
+
: isSkillsView
|
|
3156
|
+
? "skills"
|
|
3157
|
+
: isPlanView
|
|
3158
|
+
? "plan"
|
|
3159
|
+
: null;
|
|
3160
|
+
return (_jsx(Box, { flexDirection: "column", width: columns, flexShrink: 0, flexGrow: 0, children: fullScreenOverlay ? (_jsx(FullScreenOverlayRouter, { overlay: fullScreenOverlay, version: props.version, cwd: props.cwd, agentRunning: agentLoop.isRunning, planAutoExpand: planAutoExpand, onClosePixel: handleCloseRemountableOverlay, onPixelFixOne: handlePixelFixOne, onPixelFixAll: handlePixelFixAll, onCloseSkills: handleCloseRemountableOverlay, onClosePlan: handleClosePlanOverlay, onApprovePlan: handleApprovePlan, onRejectPlan: handleRejectPlan })) : (_jsx(ChatScreen, { columns: columns, liveItems: uniqueItemsById(liveItems), renderItem: renderItem, isRunning: agentLoop.isRunning, visibleStreamingText: visibleStreamingText, streamingThinking: agentLoop.streamingThinking, thinkingMs: agentLoop.thinkingMs, reserveStreamingSpacing: shouldReserveStreamingSpacing, renderMarkdown: renderMarkdown, measuredLiveAreaRows: measuredLiveAreaRows, assistantMarginTop: shouldTopSpaceStreamingText ? 1 : 0, streamingContinuation: streamedAssistantFlushRef.current.flushedChars > 0, controlsRef: mainControlsRef, hiddenQueuedCount: hiddenQueuedCount, queueIndicatorMarginTop: shouldTopSpaceQueueIndicator ? 2 : 1, theme: theme, statusSlotVisible: statusSlotVisible, activityVisible: activityVisible, stallStatusVisible: stallStatusVisible, doneStatus: doneStatus, activityPhase: agentLoop.activityPhase, elapsedMs: agentLoop.elapsedMs, runStartRef: agentLoop.runStartRef, isThinking: agentLoop.isThinking, thinkingLevel: thinkingLevel, tokenEstimate: agentLoop.streamedTokenEstimate, charCountRef: agentLoop.charCountRef, realTokensAccumRef: agentLoop.realTokensAccumRef, lastUserMessage: lastUserMessage, activeToolNames: agentLoop.activeToolCalls.map((tc) => tc.name), retryInfo: agentLoop.retryInfo, planDone: planSteps.filter((s) => s.completed).length, planTotal: planSteps.length, formatDuration: formatDuration, inputControls: {
|
|
3161
|
+
onSubmit: handleSubmit,
|
|
3162
|
+
onAbort: handleAbort,
|
|
3163
|
+
inputActive: !taskBarFocused && !overlay,
|
|
3164
|
+
onDownAtEnd: handleFocusTaskBar,
|
|
3165
|
+
onShiftTab: handleToggleThinking,
|
|
3166
|
+
onToggleTasks: handleToggleTasks,
|
|
3167
|
+
onToggleGoal: handleToggleGoalPicker,
|
|
3168
|
+
onToggleSkills: () => openOverlay("skills"),
|
|
3169
|
+
onTogglePixel: () => openOverlay("pixel"),
|
|
3170
|
+
onToggleMarkdown: () => setRenderMarkdown((prev) => !prev),
|
|
3171
|
+
cwd: props.cwd,
|
|
3172
|
+
commands: allCommands,
|
|
3173
|
+
}, taskPicker: {
|
|
3174
|
+
open: taskPicker.open,
|
|
3175
|
+
tasks: taskPicker.tasks,
|
|
3176
|
+
onClose: taskPicker.close,
|
|
3177
|
+
onStart: taskPicker.start,
|
|
3178
|
+
onRunAll: taskPicker.runAll,
|
|
3179
|
+
onDelete: taskPicker.deleteTask,
|
|
3180
|
+
}, goalPicker: {
|
|
3181
|
+
open: goalPicker.open,
|
|
3182
|
+
goals: goalPicker.goals,
|
|
3183
|
+
onClose: goalPicker.close,
|
|
3184
|
+
onRun: goalPicker.run,
|
|
3185
|
+
onDelete: goalPicker.deleteGoal,
|
|
3186
|
+
onPause: goalPicker.pause,
|
|
3187
|
+
}, overlay: overlay, onModelSelect: handleModelSelect, onModelCancel: () => setOverlay(null), loggedInProviders: props.loggedInProviders ?? [currentProvider], currentModel: currentModel, currentProvider: currentProvider, onThemeSelect: handleThemeSelect, onThemeCancel: () => setOverlay(null), currentTheme: theme.name, contextUsed: agentLoop.contextUsed, contextWindowOptions: contextWindowOptions, displayedCwd: displayedCwd, gitBranch: gitBranch, goalMode: goalMode, planMode: planMode, exitPending: exitPending, goalStatusEntries: goalStatusEntries, footerStatusLayout: footerStatusLayout, backgroundTasks: bgTasks, taskBarFocused: taskBarFocused, taskBarExpanded: taskBarExpanded, selectedTaskIndex: selectedTaskIndex, onTaskBarExpand: handleTaskBarExpand, onTaskBarCollapse: handleTaskBarCollapse, onTaskKill: handleTaskKill, onTaskBarExit: handleTaskBarExit, onTaskNavigate: handleTaskNavigate })) }));
|
|
3541
3188
|
}
|
|
3542
3189
|
//# sourceMappingURL=App.js.map
|