@kenkaiiii/ggcoder 4.3.218 → 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 +41 -21
- 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 +3 -0
- package/dist/core/goal-store.d.ts.map +1 -1
- package/dist/core/goal-store.js +64 -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/core/tasks-store.d.ts +24 -0
- package/dist/core/tasks-store.d.ts.map +1 -0
- package/dist/core/tasks-store.js +81 -0
- package/dist/core/tasks-store.js.map +1 -0
- 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 +11 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +18 -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/tasks.d.ts +16 -0
- package/dist/tools/tasks.d.ts.map +1 -0
- package/dist/tools/tasks.js +93 -0
- package/dist/tools/tasks.js.map +1 -0
- 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 +10 -6
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +660 -987
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/app-items.d.ts +7 -1
- 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 +41 -23
- package/dist/ui/app-state-persistence.test.js.map +1 -1
- package/dist/ui/chat-layout-pinning.test.js +10 -0
- package/dist/ui/chat-layout-pinning.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/Banner.js +2 -2
- package/dist/ui/components/Banner.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/GoalPickerMenu.d.ts +9 -0
- package/dist/ui/components/GoalPickerMenu.d.ts.map +1 -0
- package/dist/ui/components/GoalPickerMenu.js +37 -0
- package/dist/ui/components/GoalPickerMenu.js.map +1 -0
- 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 +16 -1
- package/dist/ui/components/InputArea.d.ts.map +1 -1
- package/dist/ui/components/InputArea.js +95 -6
- 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/TaskPickerMenu.d.ts +9 -0
- package/dist/ui/components/TaskPickerMenu.d.ts.map +1 -0
- package/dist/ui/components/TaskPickerMenu.js +33 -0
- package/dist/ui/components/TaskPickerMenu.js.map +1 -0
- 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/item-helpers.d.ts +2 -0
- package/dist/ui/item-helpers.d.ts.map +1 -1
- package/dist/ui/item-helpers.js +16 -0
- package/dist/ui/item-helpers.js.map +1 -1
- 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 -64
- 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 -27
- 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 +59 -25
- 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 +6 -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,42 +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 { GoalOverlay } from "./components/GoalOverlay.js";
|
|
31
|
-
import { PixelOverlay } from "./components/PixelOverlay.js";
|
|
32
|
-
import { SkillsOverlay } from "./components/SkillsOverlay.js";
|
|
33
|
-
import { ThemeSelector } from "./components/ThemeSelector.js";
|
|
34
|
-
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";
|
|
35
19
|
import { useTheme, useSetTheme } from "./theme/theme.js";
|
|
36
20
|
import { useTerminalTitle } from "./hooks/useTerminalTitle.js";
|
|
37
21
|
import { getGitBranch } from "../utils/git.js";
|
|
38
22
|
import { getModel, getContextWindow } from "../core/model-registry.js";
|
|
39
|
-
import { BLACK_CIRCLE } from "./constants/figures.js";
|
|
40
23
|
import { SessionManager } from "../core/session-manager.js";
|
|
41
24
|
import { appendMessagesToSession as appendSessionMessages, createCompactedSessionCheckpoint, } from "../core/session-compaction.js";
|
|
42
25
|
import { log } from "../core/logger.js";
|
|
@@ -49,64 +32,39 @@ import { PROMPT_COMMANDS, getPromptCommand } from "../core/prompt-commands.js";
|
|
|
49
32
|
import { isFirstTimeSetup, markSetupAudited, getAnnouncedLanguages, markLanguagesAnnounced, } from "../core/setup-history.js";
|
|
50
33
|
import { loadCustomCommands } from "../core/custom-commands.js";
|
|
51
34
|
import { buildSystemPrompt } from "../system-prompt.js";
|
|
52
|
-
import { detectLanguages
|
|
35
|
+
import { detectLanguages } from "../core/language-detector.js";
|
|
53
36
|
import { detectVerifyCommands } from "../core/verify-commands.js";
|
|
54
|
-
import { buildRepoMap, createRepoMapCache, } from "../core/repomap.js";
|
|
55
|
-
import { getRepoMapBudgetForContext } from "../core/repomap-budget.js";
|
|
56
|
-
import { getLatestUserText, injectRepoMapContextMessages, stripRepoMapContextMessages, } from "../core/repomap-context.js";
|
|
57
37
|
import { extractPlanSteps, findCompletedMarkers, markStepsCompleted, segmentDisplayText, stripDoneMarkers, } from "../utils/plan-steps.js";
|
|
58
38
|
import { getMCPServers } from "../core/mcp/index.js";
|
|
59
39
|
import { trimFlushedItems, flushOnTurnText, flushOnTurnEnd, flushOverflow, } from "./live-item-flush.js";
|
|
60
40
|
import { splitAssistantStreamingText } from "./utils/assistant-stream-split.js";
|
|
61
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 {
|
|
71
|
-
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";
|
|
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);
|
|
@@ -261,7 +112,11 @@ export function App(props) {
|
|
|
261
112
|
// Items from the current/last turn — rendered in the live area so they stay visible.
|
|
262
113
|
// Seed from sessionStore so Goal progress/completion rows and other live output
|
|
263
114
|
// survive pane/overlay/resize remounts before they are finalized.
|
|
264
|
-
const [liveItems, setLiveItems] = useState(() =>
|
|
115
|
+
const [liveItems, setLiveItems] = useState(() => {
|
|
116
|
+
const restoredLiveItems = uniqueItemsById(props.sessionStore?.liveItems ?? []);
|
|
117
|
+
const restoredHistoryIds = new Set(history.map((item) => item.id));
|
|
118
|
+
return removeItemsWithIds(restoredLiveItems, restoredHistoryIds);
|
|
119
|
+
});
|
|
265
120
|
// overlay seeded from sessionStore (lives across remount). Falls back to
|
|
266
121
|
// props.initialOverlay (CLI launched with one), then null.
|
|
267
122
|
const [overlay, setOverlay] = useState(props.sessionStore?.overlay ?? props.initialOverlay ?? null);
|
|
@@ -274,11 +129,19 @@ export function App(props) {
|
|
|
274
129
|
const goalContinuationFlightsRef = useRef(new Set());
|
|
275
130
|
const goalContinuationRecentChoicesRef = useRef(new Map());
|
|
276
131
|
const startGoalRunRef = useRef(() => { });
|
|
132
|
+
const [runAllTasks, setRunAllTasks] = useState(props.sessionStore?.runAllTasks ?? false);
|
|
133
|
+
const runAllTasksRef = useRef(props.sessionStore?.runAllTasks ?? false);
|
|
134
|
+
const startTaskRef = useRef(() => { });
|
|
277
135
|
const runAllPixelRef = useRef(props.sessionStore?.runAllPixel ?? false);
|
|
278
136
|
const currentPixelFixRef = useRef(null);
|
|
279
137
|
const startPixelFixRef = useRef(() => { });
|
|
280
138
|
const cwdRef = useRef(props.cwd);
|
|
281
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
|
+
});
|
|
282
145
|
const [doneStatus, setDoneStatus] = useState(props.sessionStore?.doneStatus ?? null);
|
|
283
146
|
// Suppress "done" status when a plan overlay is about to open
|
|
284
147
|
const planOverlayPendingRef = useRef(false);
|
|
@@ -291,12 +154,6 @@ export function App(props) {
|
|
|
291
154
|
const [thinkingLevel, setThinkingLevel] = useState(props.thinking);
|
|
292
155
|
const [renderMarkdown, setRenderMarkdown] = useState(true);
|
|
293
156
|
const messagesRef = useRef(props.sessionStore?.messages ?? props.messages);
|
|
294
|
-
const repoMapInjectionEnabledRef = useRef(true);
|
|
295
|
-
const repoMapDirtyRef = useRef(true);
|
|
296
|
-
const repoMapMarkdownRef = useRef("");
|
|
297
|
-
const repoMapSnapshotRef = useRef(undefined);
|
|
298
|
-
const repoMapChangedCountRef = useRef(0);
|
|
299
|
-
const repoMapCacheRef = useRef(createRepoMapCache());
|
|
300
157
|
const [planAutoExpand, setPlanAutoExpand] = useState(props.sessionStore?.planAutoExpand ?? false);
|
|
301
158
|
const [goalAutoExpand, setGoalAutoExpand] = useState(props.sessionStore?.goalAutoExpand ?? false);
|
|
302
159
|
const goalAutoExpandRef = useRef(props.sessionStore?.goalAutoExpand ?? false);
|
|
@@ -304,6 +161,7 @@ export function App(props) {
|
|
|
304
161
|
const planStepsRef = useRef(props.sessionStore?.planSteps ?? []);
|
|
305
162
|
const [planSteps, setPlanSteps] = useState(props.sessionStore?.planSteps ?? []);
|
|
306
163
|
const goalModeStateRef = useRef(goalMode);
|
|
164
|
+
const planModeStateRef = useRef(planMode);
|
|
307
165
|
// Stuck-guard for the plan-continuation follow-up nudge. Tracks how many
|
|
308
166
|
// times we've nudged the agent to continue the same step. Reset whenever a
|
|
309
167
|
// new [DONE:n] marker advances progress (see onTurnText). Caps at 2 nudges
|
|
@@ -373,58 +231,24 @@ export function App(props) {
|
|
|
373
231
|
});
|
|
374
232
|
}, [props.sessionStore]);
|
|
375
233
|
const sessionStore = props.sessionStore;
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
version: props.version,
|
|
380
|
-
model: currentModel,
|
|
381
|
-
provider: currentProvider,
|
|
382
|
-
cwd: displayedCwd,
|
|
383
|
-
});
|
|
384
|
-
useEffect(() => {
|
|
385
|
-
terminalHistoryContextRef.current = {
|
|
234
|
+
const { pendingHistoryFlushRef, streamedAssistantFlushRef, queueFlush, finalizeSubmittedUserItem, clearPendingHistory, } = useTranscriptHistory({
|
|
235
|
+
terminalHistoryPrinter: props.terminalHistoryPrinter,
|
|
236
|
+
terminalHistoryContext: {
|
|
386
237
|
theme,
|
|
387
238
|
columns,
|
|
388
239
|
version: props.version,
|
|
389
240
|
model: currentModel,
|
|
390
241
|
provider: currentProvider,
|
|
391
242
|
cwd: displayedCwd,
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
});
|
|
401
|
-
}, [props.terminalHistoryPrinter, writeStdout]);
|
|
402
|
-
const pendingHistoryFlushRef = useRef([]);
|
|
403
|
-
const streamedAssistantFlushRef = useRef({
|
|
404
|
-
flushedChars: 0,
|
|
405
|
-
text: "",
|
|
243
|
+
},
|
|
244
|
+
writeStdout,
|
|
245
|
+
sessionPathRef,
|
|
246
|
+
sessionManagerRef,
|
|
247
|
+
sessionStore,
|
|
248
|
+
history,
|
|
249
|
+
setHistory,
|
|
250
|
+
setLiveItems,
|
|
406
251
|
});
|
|
407
|
-
const [historyFlushGeneration, setHistoryFlushGeneration] = useState(0);
|
|
408
|
-
const queueFlush = useCallback((items) => {
|
|
409
|
-
const flushed = trimFlushedItems(items);
|
|
410
|
-
if (flushed.length === 0)
|
|
411
|
-
return;
|
|
412
|
-
pendingHistoryFlushRef.current = [...pendingHistoryFlushRef.current, ...flushed];
|
|
413
|
-
if (sessionStore) {
|
|
414
|
-
const queuedIds = new Set(items.map((item) => item.id));
|
|
415
|
-
sessionStore.liveItems = (sessionStore.liveItems ?? []).filter((item) => !queuedIds.has(item.id));
|
|
416
|
-
}
|
|
417
|
-
setHistoryFlushGeneration((generation) => generation + 1);
|
|
418
|
-
}, [sessionStore]);
|
|
419
|
-
const finalizeSubmittedUserItem = useCallback((item) => {
|
|
420
|
-
streamedAssistantFlushRef.current = { flushedChars: 0, text: "" };
|
|
421
|
-
setLiveItems((prev) => {
|
|
422
|
-
if (prev.length > 0)
|
|
423
|
-
queueFlush(prev);
|
|
424
|
-
queueFlush([item]);
|
|
425
|
-
return [];
|
|
426
|
-
});
|
|
427
|
-
}, [queueFlush]);
|
|
428
252
|
// Mirror runtime state choices (model/provider/thinking) into renderApp's
|
|
429
253
|
// closure so unmount/remount preserves them.
|
|
430
254
|
const onRuntimeStateChange = props.onRuntimeStateChange;
|
|
@@ -444,28 +268,6 @@ export function App(props) {
|
|
|
444
268
|
thinking: thinkingLevel,
|
|
445
269
|
});
|
|
446
270
|
}, [thinkingLevel, onRuntimeStateChange]);
|
|
447
|
-
useEffect(() => {
|
|
448
|
-
printHistoryItems(history);
|
|
449
|
-
}, [history, printHistoryItems]);
|
|
450
|
-
useEffect(() => {
|
|
451
|
-
const flushed = pendingHistoryFlushRef.current;
|
|
452
|
-
if (flushed.length === 0)
|
|
453
|
-
return;
|
|
454
|
-
pendingHistoryFlushRef.current = [];
|
|
455
|
-
printHistoryItems(flushed);
|
|
456
|
-
const flushedIds = new Set(flushed.map((item) => item.id));
|
|
457
|
-
setLiveItems((prev) => prev.filter((item) => !flushedIds.has(item.id)));
|
|
458
|
-
setHistory((prev) => {
|
|
459
|
-
const existingIds = new Set(prev.map((item) => item.id));
|
|
460
|
-
const nextItems = flushed.filter((item) => !existingIds.has(item.id));
|
|
461
|
-
if (nextItems.length === 0)
|
|
462
|
-
return prev;
|
|
463
|
-
const next = compactHistory([...prev, ...nextItems]);
|
|
464
|
-
if (sessionStore)
|
|
465
|
-
sessionStore.history = next;
|
|
466
|
-
return next;
|
|
467
|
-
});
|
|
468
|
-
}, [historyFlushGeneration, printHistoryItems, sessionStore]);
|
|
469
271
|
// Mirror session state into renderApp's closure so resetUI() can re-seed
|
|
470
272
|
// the conversation on remount. Each panel that previously did a bare ANSI
|
|
471
273
|
// screen clear (overlay open/close, plan accept/reject, /clear)
|
|
@@ -477,8 +279,10 @@ export function App(props) {
|
|
|
477
279
|
sessionStore.history = history;
|
|
478
280
|
}, [history, sessionStore]);
|
|
479
281
|
useEffect(() => {
|
|
480
|
-
if (sessionStore)
|
|
481
|
-
|
|
282
|
+
if (!sessionStore)
|
|
283
|
+
return;
|
|
284
|
+
const historyIds = new Set(historyRef.current.map((item) => item.id));
|
|
285
|
+
sessionStore.liveItems = removeItemsWithIds(uniqueItemsById(liveItems), historyIds);
|
|
482
286
|
}, [liveItems, sessionStore]);
|
|
483
287
|
useEffect(() => {
|
|
484
288
|
if (sessionStore)
|
|
@@ -509,6 +313,10 @@ export function App(props) {
|
|
|
509
313
|
if (sessionStore)
|
|
510
314
|
sessionStore.goalMode = goalMode;
|
|
511
315
|
}, [goalMode, sessionStore]);
|
|
316
|
+
useEffect(() => {
|
|
317
|
+
if (sessionStore)
|
|
318
|
+
sessionStore.planMode = planMode;
|
|
319
|
+
}, [planMode, sessionStore]);
|
|
512
320
|
// pendingAction is consumed via a useEffect AFTER agentLoop is created
|
|
513
321
|
// — see below where useAgentLoop is set up.
|
|
514
322
|
const pendingActionConsumedRef = useRef(false);
|
|
@@ -580,6 +388,11 @@ export function App(props) {
|
|
|
580
388
|
props.goalModeRef.current = goalMode;
|
|
581
389
|
}
|
|
582
390
|
}, [goalMode, props.goalModeRef]);
|
|
391
|
+
useEffect(() => {
|
|
392
|
+
planModeStateRef.current = planMode;
|
|
393
|
+
if (props.planModeRef)
|
|
394
|
+
props.planModeRef.current = planMode;
|
|
395
|
+
}, [planMode, props.planModeRef]);
|
|
583
396
|
const setActiveGoalReferences = useCallback((references) => {
|
|
584
397
|
if (props.goalReferencesRef)
|
|
585
398
|
props.goalReferencesRef.current = references;
|
|
@@ -588,7 +401,7 @@ export function App(props) {
|
|
|
588
401
|
const approvedPlanPath = options?.clearApprovedPlan
|
|
589
402
|
? undefined
|
|
590
403
|
: (options?.approvedPlanPath ?? approvedPlanPathRef.current);
|
|
591
|
-
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);
|
|
592
405
|
}, [props.skills]);
|
|
593
406
|
const replaceSystemPrompt = useCallback(async (options) => {
|
|
594
407
|
const newPrompt = await rebuildSystemPrompt(options);
|
|
@@ -629,6 +442,15 @@ export function App(props) {
|
|
|
629
442
|
setGoalMode(nextMode);
|
|
630
443
|
await replaceSystemPrompt({ ...options, goalMode: nextMode });
|
|
631
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]);
|
|
632
454
|
const clearGoalModeIfIdle = useCallback(() => {
|
|
633
455
|
setTimeout(() => {
|
|
634
456
|
if (goalModeStateRef.current === "off")
|
|
@@ -879,103 +701,46 @@ export function App(props) {
|
|
|
879
701
|
contextWindowOptions,
|
|
880
702
|
props.authStorage,
|
|
881
703
|
]);
|
|
882
|
-
const getRepoMapSignalCount = useCallback(() => {
|
|
883
|
-
return ((props.repoMapChangedFilesRef?.current.size ?? 0) +
|
|
884
|
-
(props.repoMapReadFilesRef?.current.size ?? 0));
|
|
885
|
-
}, [props.repoMapChangedFilesRef, props.repoMapReadFilesRef]);
|
|
886
|
-
const getRepoMapBudget = useCallback(() => {
|
|
887
|
-
return getRepoMapBudgetForContext({
|
|
888
|
-
messages: messagesRef.current,
|
|
889
|
-
readFileCount: props.repoMapReadFilesRef?.current.size ?? 0,
|
|
890
|
-
});
|
|
891
|
-
}, [props.repoMapReadFilesRef]);
|
|
892
|
-
const refreshRepoMap = useCallback(async (latestUserPrompt) => {
|
|
893
|
-
const rendered = await buildRepoMap({
|
|
894
|
-
cwd: cwdRef.current,
|
|
895
|
-
maxChars: getRepoMapBudget(),
|
|
896
|
-
changedFiles: [...(props.repoMapChangedFilesRef?.current ?? new Set())],
|
|
897
|
-
readFiles: [...(props.repoMapReadFilesRef?.current ?? new Set())],
|
|
898
|
-
focusTerms: latestUserPrompt ? [latestUserPrompt] : [],
|
|
899
|
-
cache: repoMapCacheRef.current,
|
|
900
|
-
});
|
|
901
|
-
repoMapMarkdownRef.current = rendered.markdown;
|
|
902
|
-
repoMapSnapshotRef.current = rendered.snapshot;
|
|
903
|
-
repoMapChangedCountRef.current = getRepoMapSignalCount();
|
|
904
|
-
repoMapDirtyRef.current = false;
|
|
905
|
-
return rendered.markdown;
|
|
906
|
-
}, [
|
|
907
|
-
getRepoMapBudget,
|
|
908
|
-
getRepoMapSignalCount,
|
|
909
|
-
props.repoMapChangedFilesRef,
|
|
910
|
-
props.repoMapReadFilesRef,
|
|
911
|
-
]);
|
|
912
|
-
const stripRepoMapMessages = useCallback((messages) => {
|
|
913
|
-
return stripRepoMapContextMessages(messages);
|
|
914
|
-
}, []);
|
|
915
|
-
const injectRepoMapContext = useCallback(async (messages) => {
|
|
916
|
-
if (!repoMapInjectionEnabledRef.current)
|
|
917
|
-
return stripRepoMapMessages(messages);
|
|
918
|
-
const stripped = stripRepoMapMessages(messages);
|
|
919
|
-
const latestUserPrompt = getLatestUserText(stripped);
|
|
920
|
-
const signalCount = getRepoMapSignalCount();
|
|
921
|
-
if (signalCount !== repoMapChangedCountRef.current)
|
|
922
|
-
repoMapDirtyRef.current = true;
|
|
923
|
-
if (repoMapDirtyRef.current || !repoMapMarkdownRef.current) {
|
|
924
|
-
await refreshRepoMap(latestUserPrompt);
|
|
925
|
-
}
|
|
926
|
-
if (!repoMapMarkdownRef.current)
|
|
927
|
-
return stripped;
|
|
928
|
-
return injectRepoMapContextMessages(stripped, repoMapMarkdownRef.current);
|
|
929
|
-
}, [props.repoMapChangedFilesRef, props.repoMapReadFilesRef, refreshRepoMap, stripRepoMapMessages]);
|
|
930
704
|
/**
|
|
931
705
|
* transformContext callback for the agent loop.
|
|
932
706
|
* Called before each LLM call and on context overflow.
|
|
933
|
-
* Compacts persistent chat only, then injects the dynamic repo map transiently.
|
|
934
707
|
*/
|
|
935
708
|
const transformContext = useCallback(async (messages, options) => {
|
|
936
|
-
const stripped = stripRepoMapMessages(messages);
|
|
937
709
|
const settings = settingsRef.current;
|
|
938
710
|
const autoCompact = settings?.get("autoCompact") ?? true;
|
|
939
711
|
const threshold = settings?.get("compactThreshold") ?? 0.8;
|
|
940
712
|
// Force-compact on context overflow regardless of settings
|
|
941
713
|
if (options?.force) {
|
|
942
|
-
const result = await compactConversation(
|
|
943
|
-
if (result !==
|
|
714
|
+
const result = await compactConversation(messages);
|
|
715
|
+
if (result !== messages) {
|
|
944
716
|
messagesRef.current = result;
|
|
945
717
|
await persistCompactedSession(result);
|
|
946
718
|
}
|
|
947
719
|
lastCompactionTimeRef.current = Date.now();
|
|
948
|
-
return
|
|
720
|
+
return result;
|
|
949
721
|
}
|
|
950
722
|
if (!autoCompact)
|
|
951
|
-
return
|
|
723
|
+
return messages;
|
|
952
724
|
// Time-based cooldown: skip if compaction ran within the last 30 seconds
|
|
953
725
|
if (Date.now() - lastCompactionTimeRef.current < 30_000) {
|
|
954
726
|
log("INFO", "compaction", `Skipping compaction — cooldown active`);
|
|
955
|
-
return
|
|
727
|
+
return messages;
|
|
956
728
|
}
|
|
957
729
|
const contextWindow = getContextWindow(currentModel, contextWindowOptions);
|
|
958
730
|
const reserveTokens = getCompactionReserveTokens(props.maxTokens);
|
|
959
731
|
const tokensFresh = lastActualTokensTimestampRef.current > lastCompactionTimeRef.current;
|
|
960
732
|
const actualTokens = lastActualTokensRef.current > 0 && tokensFresh ? lastActualTokensRef.current : undefined;
|
|
961
|
-
if (shouldCompact(
|
|
962
|
-
const result = await compactConversation(
|
|
963
|
-
if (result !==
|
|
733
|
+
if (shouldCompact(messages, contextWindow, threshold, actualTokens, reserveTokens)) {
|
|
734
|
+
const result = await compactConversation(messages);
|
|
735
|
+
if (result !== messages) {
|
|
964
736
|
messagesRef.current = result;
|
|
965
737
|
await persistCompactedSession(result);
|
|
966
738
|
}
|
|
967
739
|
lastCompactionTimeRef.current = Date.now();
|
|
968
|
-
return
|
|
740
|
+
return result;
|
|
969
741
|
}
|
|
970
|
-
return
|
|
971
|
-
}, [
|
|
972
|
-
currentModel,
|
|
973
|
-
compactConversation,
|
|
974
|
-
contextWindowOptions,
|
|
975
|
-
injectRepoMapContext,
|
|
976
|
-
persistCompactedSession,
|
|
977
|
-
stripRepoMapMessages,
|
|
978
|
-
]);
|
|
742
|
+
return messages;
|
|
743
|
+
}, [currentModel, compactConversation, contextWindowOptions, persistCompactedSession]);
|
|
979
744
|
// ── Background task bar state (external store) ──────────
|
|
980
745
|
const { bgTasks, focused: taskBarFocused, expanded: taskBarExpanded, selectedIndex: selectedTaskIndex, } = useTaskBarStore();
|
|
981
746
|
useTaskBarPolling(props.processManager);
|
|
@@ -1016,7 +781,6 @@ export function App(props) {
|
|
|
1016
781
|
transformContext,
|
|
1017
782
|
}, {
|
|
1018
783
|
onComplete: useCallback(() => {
|
|
1019
|
-
messagesRef.current = stripRepoMapMessages(messagesRef.current);
|
|
1020
784
|
persistNewMessages();
|
|
1021
785
|
// Auto-clear plan progress and approved plan when all steps are completed
|
|
1022
786
|
const steps = planStepsRef.current;
|
|
@@ -1069,7 +833,6 @@ export function App(props) {
|
|
|
1069
833
|
}
|
|
1070
834
|
}, [
|
|
1071
835
|
persistNewMessages,
|
|
1072
|
-
stripRepoMapMessages,
|
|
1073
836
|
props.cwd,
|
|
1074
837
|
props.skills,
|
|
1075
838
|
currentProvider,
|
|
@@ -1528,6 +1291,21 @@ export function App(props) {
|
|
|
1528
1291
|
if (nextGoalMode !== goalModeStateRef.current) {
|
|
1529
1292
|
void setGoalModeAndPrompt(nextGoalMode);
|
|
1530
1293
|
}
|
|
1294
|
+
// Run-all: auto-start next pending task after a short delay.
|
|
1295
|
+
if (runAllTasksRef.current) {
|
|
1296
|
+
setTimeout(() => {
|
|
1297
|
+
const cwd = cwdRef.current;
|
|
1298
|
+
const next = getNextPendingTask(cwd);
|
|
1299
|
+
if (next) {
|
|
1300
|
+
markTaskInProgress(cwd, next.id);
|
|
1301
|
+
startTaskRef.current(next.title, next.prompt, next.id);
|
|
1302
|
+
}
|
|
1303
|
+
else {
|
|
1304
|
+
setRunAllTasks(false);
|
|
1305
|
+
log("INFO", "tasks", "Run-all complete — no more pending tasks");
|
|
1306
|
+
}
|
|
1307
|
+
}, 500);
|
|
1308
|
+
}
|
|
1531
1309
|
// Goal loop: after the orchestrator handles a worker/verifier event,
|
|
1532
1310
|
// continue the same Goal automatically until it reaches a terminal state.
|
|
1533
1311
|
for (const runId of [...runningGoalIdsRef.current]) {
|
|
@@ -1819,257 +1597,101 @@ export function App(props) {
|
|
|
1819
1597
|
// has not grown.
|
|
1820
1598
|
await applyLanguageDetectionRef.current("input");
|
|
1821
1599
|
}
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
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([]);
|
|
1851
1632
|
void (async () => {
|
|
1852
1633
|
const newPrompt = await rebuildSystemPrompt({ clearApprovedPlan: true });
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
messages: [{ role: "system", content: newPrompt }],
|
|
1856
|
-
});
|
|
1634
|
+
messagesRef.current = [{ role: "system", content: newPrompt }];
|
|
1635
|
+
persistedIndexRef.current = messagesRef.current.length;
|
|
1857
1636
|
})();
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
const next = !prev;
|
|
1890
|
-
setLiveItems([
|
|
1891
|
-
{
|
|
1892
|
-
kind: "info",
|
|
1893
|
-
text: next ? "Rendered markdown mode." : "Raw markdown mode.",
|
|
1894
|
-
id: getId(),
|
|
1895
|
-
},
|
|
1896
|
-
]);
|
|
1897
|
-
return next;
|
|
1898
|
-
});
|
|
1899
|
-
return;
|
|
1900
|
-
}
|
|
1901
|
-
// Handle /clearplan — dismiss the approved plan
|
|
1902
|
-
if (trimmed === "/clearplan") {
|
|
1903
|
-
approvedPlanPathRef.current = undefined;
|
|
1904
|
-
planStepsRef.current = [];
|
|
1905
|
-
setPlanSteps([]);
|
|
1906
|
-
// Rebuild system prompt without the plan
|
|
1907
|
-
void replaceSystemPrompt({ clearApprovedPlan: true });
|
|
1908
|
-
setLiveItems([{ kind: "plan_event", event: "dismissed", id: getId() }]);
|
|
1909
|
-
return;
|
|
1910
|
-
}
|
|
1911
|
-
// Handle /map — show, refresh, or toggle dynamic repo map injection
|
|
1912
|
-
if (trimmed === "/map" ||
|
|
1913
|
-
trimmed === "/map refresh" ||
|
|
1914
|
-
trimmed === "/map on" ||
|
|
1915
|
-
trimmed === "/map off") {
|
|
1916
|
-
const action = trimmed.slice("/map".length).trim();
|
|
1917
|
-
if (action === "on") {
|
|
1918
|
-
repoMapInjectionEnabledRef.current = true;
|
|
1919
|
-
repoMapDirtyRef.current = true;
|
|
1920
|
-
setLiveItems((prev) => [
|
|
1921
|
-
...prev,
|
|
1922
|
-
{ kind: "info", text: "Dynamic repo map injection is on.", id: getId() },
|
|
1923
|
-
]);
|
|
1924
|
-
return;
|
|
1925
|
-
}
|
|
1926
|
-
if (action === "off") {
|
|
1927
|
-
repoMapInjectionEnabledRef.current = false;
|
|
1928
|
-
messagesRef.current = stripRepoMapMessages(messagesRef.current);
|
|
1929
|
-
setLiveItems((prev) => [
|
|
1930
|
-
...prev,
|
|
1931
|
-
{
|
|
1932
|
-
kind: "info",
|
|
1933
|
-
text: "Dynamic repo map injection is off for this session.",
|
|
1934
|
-
id: getId(),
|
|
1935
|
-
},
|
|
1936
|
-
]);
|
|
1937
|
-
return;
|
|
1938
|
-
}
|
|
1939
|
-
if (action === "refresh")
|
|
1940
|
-
repoMapDirtyRef.current = true;
|
|
1941
|
-
const markdown = await refreshRepoMap(getLatestUserText(messagesRef.current));
|
|
1942
|
-
setLiveItems((prev) => [
|
|
1943
|
-
...prev,
|
|
1944
|
-
{
|
|
1945
|
-
kind: "info",
|
|
1946
|
-
text: formatRepoMapCommandOutput(repoMapInjectionEnabledRef.current, markdown, action === "refresh"),
|
|
1947
|
-
id: getId(),
|
|
1948
|
-
},
|
|
1949
|
-
]);
|
|
1950
|
-
return;
|
|
1951
|
-
}
|
|
1952
|
-
// Handle /goals — open goal pane
|
|
1953
|
-
if (trimmed === "/goals") {
|
|
1954
|
-
if (props.resetUI && props.sessionStore && !agentLoop.isRunning) {
|
|
1955
|
-
props.sessionStore.overlay = "goal";
|
|
1956
|
-
props.sessionStore.planAutoExpand = false;
|
|
1957
|
-
props.sessionStore.goalAutoExpand = false;
|
|
1958
|
-
props.resetUI();
|
|
1959
|
-
}
|
|
1960
|
-
else {
|
|
1961
|
-
if (props.sessionStore) {
|
|
1962
|
-
props.sessionStore.overlay = "goal";
|
|
1963
|
-
props.sessionStore.planAutoExpand = false;
|
|
1964
|
-
props.sessionStore.goalAutoExpand = false;
|
|
1965
|
-
if (agentLoop.isRunning)
|
|
1966
|
-
props.sessionStore.pendingResetUI = true;
|
|
1967
|
-
}
|
|
1968
|
-
setPlanAutoExpand(false);
|
|
1969
|
-
setGoalAutoExpand(false);
|
|
1970
|
-
setOverlay("goal");
|
|
1971
|
-
}
|
|
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
|
+
})) {
|
|
1972
1668
|
return;
|
|
1973
1669
|
}
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
text: trimmed,
|
|
2000
|
-
imageCount: hasImages ? inputImages.length : undefined,
|
|
2001
|
-
id: getId(),
|
|
2002
|
-
};
|
|
2003
|
-
setLastUserMessage(trimmed);
|
|
2004
|
-
setDoneStatus(null);
|
|
2005
|
-
finalizeSubmittedUserItem(userItem);
|
|
2006
|
-
// Send the full prompt to the agent, with user args appended if provided
|
|
2007
|
-
try {
|
|
2008
|
-
if (isGoalSetupCommand) {
|
|
2009
|
-
goalSetupPanePendingRef.current = true;
|
|
2010
|
-
await runGoalPromptSetupSequence({
|
|
2011
|
-
userContent,
|
|
2012
|
-
fullPrompt: promptForAgent,
|
|
2013
|
-
messagesRef,
|
|
2014
|
-
setGoalModeAndPrompt,
|
|
2015
|
-
runAgent: (content) => agentLoop.run(content),
|
|
2016
|
-
onStage: appendGoalAgentTransition,
|
|
2017
|
-
});
|
|
2018
|
-
}
|
|
2019
|
-
else {
|
|
2020
|
-
await agentLoop.run(userContent);
|
|
2021
|
-
}
|
|
2022
|
-
}
|
|
2023
|
-
catch (err) {
|
|
2024
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
2025
|
-
log("ERROR", "error", msg);
|
|
2026
|
-
const isAbort = msg.includes("aborted") || msg.includes("abort");
|
|
2027
|
-
if (isGoalSetupCommand)
|
|
2028
|
-
goalSetupPanePendingRef.current = false;
|
|
2029
|
-
setLiveItems((prev) => [
|
|
2030
|
-
...prev,
|
|
2031
|
-
isAbort
|
|
2032
|
-
? { kind: "stopped", text: "Request was stopped.", id: getId() }
|
|
2033
|
-
: toErrorItem(err, getId()),
|
|
2034
|
-
]);
|
|
2035
|
-
}
|
|
2036
|
-
finally {
|
|
2037
|
-
if (isGoalSetupCommand) {
|
|
2038
|
-
setActiveGoalReferences(undefined);
|
|
2039
|
-
const paneTransition = getGoalSetupPaneTransitionAfterRun({
|
|
2040
|
-
isGoalSetupCommand,
|
|
2041
|
-
setupPanePending: goalSetupPanePendingRef.current,
|
|
2042
|
-
});
|
|
2043
|
-
goalSetupPanePendingRef.current = false;
|
|
2044
|
-
if (goalModeStateRef.current !== "off") {
|
|
2045
|
-
await setGoalModeAndPrompt("off");
|
|
2046
|
-
}
|
|
2047
|
-
if (paneTransition) {
|
|
2048
|
-
goalAutoExpandRef.current = paneTransition.goalAutoExpand;
|
|
2049
|
-
setTimeout(() => {
|
|
2050
|
-
const resetUI = props.resetUI;
|
|
2051
|
-
const sessionStore = props.sessionStore;
|
|
2052
|
-
if (shouldResetUIForGoalSetupPaneTransition({
|
|
2053
|
-
hasResetUI: resetUI !== undefined,
|
|
2054
|
-
hasSessionStore: sessionStore !== undefined,
|
|
2055
|
-
}) &&
|
|
2056
|
-
resetUI &&
|
|
2057
|
-
sessionStore) {
|
|
2058
|
-
sessionStore.overlay = paneTransition.overlay;
|
|
2059
|
-
sessionStore.goalAutoExpand = paneTransition.goalAutoExpand;
|
|
2060
|
-
sessionStore.planAutoExpand = paneTransition.planAutoExpand;
|
|
2061
|
-
resetUI();
|
|
2062
|
-
return;
|
|
2063
|
-
}
|
|
2064
|
-
setGoalAutoExpand(paneTransition.goalAutoExpand);
|
|
2065
|
-
setPlanAutoExpand(paneTransition.planAutoExpand);
|
|
2066
|
-
setOverlay(paneTransition.overlay);
|
|
2067
|
-
}, 300);
|
|
2068
|
-
}
|
|
2069
|
-
}
|
|
2070
|
-
}
|
|
2071
|
-
// Reload custom commands in case a setup command created new ones
|
|
2072
|
-
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
|
+
})) {
|
|
2073
1695
|
return;
|
|
2074
1696
|
}
|
|
2075
1697
|
// Check slash commands
|
|
@@ -2151,12 +1773,10 @@ export function App(props) {
|
|
|
2151
1773
|
props.resetUI,
|
|
2152
1774
|
props.sessionStore,
|
|
2153
1775
|
rebuildSystemPrompt,
|
|
2154
|
-
refreshRepoMap,
|
|
2155
1776
|
reloadCustomCommands,
|
|
2156
1777
|
replaceSystemPrompt,
|
|
2157
1778
|
setActiveGoalReferences,
|
|
2158
1779
|
setGoalModeAndPrompt,
|
|
2159
|
-
stripRepoMapMessages,
|
|
2160
1780
|
]);
|
|
2161
1781
|
const handleDoubleExit = useDoublePress(setExitPending, () => process.exit(0));
|
|
2162
1782
|
const handleAbort = useCallback(() => {
|
|
@@ -2345,113 +1965,21 @@ export function App(props) {
|
|
|
2345
1965
|
},
|
|
2346
1966
|
];
|
|
2347
1967
|
}, [customCommands]);
|
|
2348
|
-
const
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
lastPendingHistoryItem: pendingHistoryFlushRef.current.at(-1),
|
|
2364
|
-
lastHistoryItem: history.at(-1),
|
|
2365
|
-
}))
|
|
2366
|
-
? 1
|
|
2367
|
-
: 0;
|
|
2368
|
-
const withPrintedBoundarySpacing = (node) => shouldTopSpacePrintedBoundary ? (_jsx(Box, { flexDirection: "column", marginTop: 1, children: node }, `${item.id}-printed-boundary`)) : (node);
|
|
2369
|
-
switch (item.kind) {
|
|
2370
|
-
case "tombstone":
|
|
2371
|
-
return null;
|
|
2372
|
-
case "banner":
|
|
2373
|
-
return (_jsx(Banner, { version: props.version, model: currentModel, provider: currentProvider, cwd: displayedCwd }, item.id));
|
|
2374
|
-
case "user":
|
|
2375
|
-
return (_jsx(UserMessage, { text: item.text, imageCount: item.imageCount, pasteInfo: item.pasteInfo }, item.id));
|
|
2376
|
-
case "goal":
|
|
2377
|
-
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));
|
|
2378
|
-
case "goal_progress": {
|
|
2379
|
-
const color = goalProgressColor(item, theme);
|
|
2380
|
-
const loaderStatus = goalProgressLoaderStatus(item);
|
|
2381
|
-
const hasBody = !!item.detail ||
|
|
2382
|
-
(item.summaryRows !== undefined && item.summaryRows.length > 0) ||
|
|
2383
|
-
(item.summarySections !== undefined && item.summarySections.length > 0);
|
|
2384
|
-
const headerContentWidth = Math.max(10, columns - 3);
|
|
2385
|
-
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));
|
|
2386
|
-
}
|
|
2387
|
-
case "style_pack": {
|
|
2388
|
-
const names = item.added.map((id) => LANGUAGE_DISPLAY_NAMES[id]);
|
|
2389
|
-
const headerLabel = item.added.length > 1 ? "STYLE PACKS ACTIVE" : "STYLE PACK ACTIVE";
|
|
2390
|
-
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));
|
|
2391
|
-
}
|
|
2392
|
-
case "setup_hint":
|
|
2393
|
-
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));
|
|
2394
|
-
case "assistant":
|
|
2395
|
-
return (_jsx(AssistantMessage, { text: item.text, thinking: item.thinking, thinkingMs: item.thinkingMs, renderMarkdown: renderMarkdown, availableTerminalHeight: measuredLiveAreaRows, marginTop: assistantMarginTop }, item.id));
|
|
2396
|
-
case "tool_start":
|
|
2397
|
-
return withPrintedBoundarySpacing(_jsx(ToolExecution, { status: "running", name: item.name, args: item.args, progressOutput: item.progressOutput, animateUntil: item.animateUntil }, item.id));
|
|
2398
|
-
case "tool_done":
|
|
2399
|
-
return withPrintedBoundarySpacing(_jsx(ToolExecution, { status: "done", name: item.name, args: item.args, result: item.result, isError: item.isError, details: item.details }, item.id));
|
|
2400
|
-
case "tool_group":
|
|
2401
|
-
return withPrintedBoundarySpacing(_jsx(ToolGroupExecution, { tools: item.tools }, item.id));
|
|
2402
|
-
case "server_tool_start":
|
|
2403
|
-
return withPrintedBoundarySpacing(_jsx(ServerToolExecution, { status: "running", name: item.name, input: item.input, startedAt: item.startedAt, animateUntil: item.animateUntil }, item.id));
|
|
2404
|
-
case "server_tool_done":
|
|
2405
|
-
return withPrintedBoundarySpacing(_jsx(ServerToolExecution, { status: "done", name: item.name, input: item.input, durationMs: item.durationMs, resultType: item.resultType }, item.id));
|
|
2406
|
-
case "error": {
|
|
2407
|
-
const showMessage = item.message && item.message !== item.headline;
|
|
2408
|
-
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));
|
|
2409
|
-
}
|
|
2410
|
-
case "info":
|
|
2411
|
-
return renderStatusMessage(item.id, "○ ", item.text, theme.commandColor, { muted: true });
|
|
2412
|
-
case "update_notice":
|
|
2413
|
-
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));
|
|
2414
|
-
case "plan_transition":
|
|
2415
|
-
return renderStatusMessage(item.id, `${BLACK_CIRCLE} `, normalizeStatusText(item.text), theme.commandColor, { bold: true });
|
|
2416
|
-
case "goal_agent_transition":
|
|
2417
|
-
return renderStatusMessage(item.id, `${BLACK_CIRCLE} `, normalizeStatusText(item.text), 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;
|
|
@@ -2479,18 +2007,6 @@ export function App(props) {
|
|
|
2479
2007
|
setOverlay(kind);
|
|
2480
2008
|
}
|
|
2481
2009
|
}, [agentLoop.isRunning, props]);
|
|
2482
|
-
const closeOverlay = useCallback(() => {
|
|
2483
|
-
if (props.resetUI && props.sessionStore && !agentLoop.isRunning) {
|
|
2484
|
-
props.sessionStore.overlay = null;
|
|
2485
|
-
props.resetUI();
|
|
2486
|
-
}
|
|
2487
|
-
else {
|
|
2488
|
-
if (props.sessionStore) {
|
|
2489
|
-
props.sessionStore.overlay = null;
|
|
2490
|
-
}
|
|
2491
|
-
setOverlay(null);
|
|
2492
|
-
}
|
|
2493
|
-
}, [agentLoop.isRunning, overlay, props]);
|
|
2494
2010
|
const runGoalSyntheticEvent = useCallback((eventText) => {
|
|
2495
2011
|
const eventInfo = parseGoalSyntheticEvent(eventText);
|
|
2496
2012
|
const detail = eventInfo?.kind === "worker"
|
|
@@ -2548,6 +2064,10 @@ export function App(props) {
|
|
|
2548
2064
|
return;
|
|
2549
2065
|
}
|
|
2550
2066
|
const decision = decideGoalNextAction(latestRun);
|
|
2067
|
+
if (!shouldKeepGoalRunTrackedAfterDecision(decision)) {
|
|
2068
|
+
runningGoalIdsRef.current.delete(runId);
|
|
2069
|
+
clearGoalModeIfIdle();
|
|
2070
|
+
}
|
|
2551
2071
|
if (decision.kind === "wait")
|
|
2552
2072
|
return;
|
|
2553
2073
|
const choiceKey = getGoalContinuationChoiceKey({ runId: latestRun.id, decision });
|
|
@@ -2652,23 +2172,25 @@ export function App(props) {
|
|
|
2652
2172
|
workerId: completion.worker.id,
|
|
2653
2173
|
status: completion.status,
|
|
2654
2174
|
});
|
|
2175
|
+
const taskProgress = goalTaskProgress(run, run.tasks.find((task) => task.id === completion.worker.goalTaskId));
|
|
2655
2176
|
upsertGoalStatusEntry({
|
|
2656
2177
|
runId: run.id,
|
|
2657
|
-
label:
|
|
2178
|
+
label: run.title,
|
|
2658
2179
|
phase: completion.status === "done" ? "reviewing" : "failed",
|
|
2659
2180
|
startedAt: Date.now(),
|
|
2660
2181
|
detail: completion.status === "done" ? "reviewing result" : "task failed",
|
|
2661
2182
|
workerId: completion.worker.id,
|
|
2662
2183
|
goalNumber: goalNumberForRun(run.id),
|
|
2184
|
+
...taskProgress,
|
|
2663
2185
|
});
|
|
2664
2186
|
runGoalSyntheticEvent(eventText);
|
|
2665
2187
|
void (async () => {
|
|
2666
|
-
if (listGoalWorkers(completion.worker.
|
|
2188
|
+
if (listGoalWorkers(completion.worker.projectPath).some((worker) => worker.status === "running"))
|
|
2667
2189
|
return;
|
|
2668
2190
|
if (activeVerifierRunIdsRef.current.size > 0)
|
|
2669
2191
|
return;
|
|
2670
|
-
const runs = await loadGoalRuns(completion.worker.
|
|
2671
|
-
const queued = runs.find((item) =>
|
|
2192
|
+
const runs = await loadGoalRuns(completion.worker.projectPath);
|
|
2193
|
+
const queued = runs.find((item) => goalRunNeedsExplicitContinuationAfterWorker(item));
|
|
2672
2194
|
if (queued)
|
|
2673
2195
|
setTimeout(() => continueGoalRun(queued.id), 750);
|
|
2674
2196
|
})().catch((err) => log("ERROR", "goal", err instanceof Error ? err.message : String(err)));
|
|
@@ -2682,7 +2204,7 @@ export function App(props) {
|
|
|
2682
2204
|
useEffect(() => {
|
|
2683
2205
|
return subscribeGoalWorkerCompletions((completion) => {
|
|
2684
2206
|
void (async () => {
|
|
2685
|
-
const latestRun = (await loadGoalRuns(completion.worker.
|
|
2207
|
+
const latestRun = (await loadGoalRuns(completion.worker.projectPath)).find((item) => item.id === completion.worker.goalRunId) ?? null;
|
|
2686
2208
|
if (!latestRun) {
|
|
2687
2209
|
log("WARN", "goal", `Worker completion for unknown Goal ${completion.worker.goalRunId}`);
|
|
2688
2210
|
return;
|
|
@@ -2736,6 +2258,9 @@ export function App(props) {
|
|
|
2736
2258
|
}
|
|
2737
2259
|
const decision = decideGoalNextAction(checkedRun);
|
|
2738
2260
|
await appendGoalDecision(props.cwd, checkedRun.id, decision);
|
|
2261
|
+
if (!shouldKeepGoalRunTrackedAfterDecision(decision)) {
|
|
2262
|
+
runningGoalIdsRef.current.delete(checkedRun.id);
|
|
2263
|
+
}
|
|
2739
2264
|
if (decision.kind === "terminal") {
|
|
2740
2265
|
const terminalProgress = formatGoalTerminalProgress(checkedRun);
|
|
2741
2266
|
if (terminalProgress) {
|
|
@@ -2753,8 +2278,10 @@ export function App(props) {
|
|
|
2753
2278
|
phase: "worker_started",
|
|
2754
2279
|
title: decision.workerId
|
|
2755
2280
|
? `Goal working: ${checkedRun.title}`
|
|
2756
|
-
: `Goal
|
|
2757
|
-
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.`,
|
|
2758
2285
|
workerId: decision.workerId,
|
|
2759
2286
|
});
|
|
2760
2287
|
upsertGoalStatusEntry({
|
|
@@ -2766,6 +2293,14 @@ export function App(props) {
|
|
|
2766
2293
|
workerId: decision.workerId,
|
|
2767
2294
|
goalNumber: goalNumberForRun(checkedRun.id),
|
|
2768
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
|
+
}
|
|
2769
2304
|
return;
|
|
2770
2305
|
}
|
|
2771
2306
|
if (decision.kind === "complete") {
|
|
@@ -2787,14 +2322,38 @@ export function App(props) {
|
|
|
2787
2322
|
return;
|
|
2788
2323
|
}
|
|
2789
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
|
+
}
|
|
2790
2342
|
await updateGoalTask(props.cwd, checkedRun.id, `auto-${Date.now()}`, {
|
|
2791
2343
|
title: decision.title,
|
|
2792
2344
|
prompt: decision.prompt,
|
|
2793
2345
|
status: "pending",
|
|
2794
2346
|
});
|
|
2795
2347
|
const latestRun = (await loadGoalRuns(props.cwd)).find((item) => item.id === checkedRun.id) ?? checkedRun;
|
|
2796
|
-
await upsertGoalRun(props.cwd, { ...latestRun, status: "ready" });
|
|
2797
|
-
|
|
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);
|
|
2798
2357
|
return;
|
|
2799
2358
|
}
|
|
2800
2359
|
if (decision.kind === "blocked") {
|
|
@@ -2856,6 +2415,7 @@ export function App(props) {
|
|
|
2856
2415
|
goalTaskId: decision.task.id,
|
|
2857
2416
|
taskTitle: decision.task.title,
|
|
2858
2417
|
prompt: buildGoalTaskPromptWithReferences(checkedRun, decision.task.prompt),
|
|
2418
|
+
isolateWorktree: shouldRunGoalTaskInMainCheckout(decision.task.title) ? false : undefined,
|
|
2859
2419
|
});
|
|
2860
2420
|
const latestRun = (await loadGoalRuns(props.cwd)).find((item) => item.id === checkedRun.id) ??
|
|
2861
2421
|
runWithAttempt;
|
|
@@ -2878,17 +2438,39 @@ export function App(props) {
|
|
|
2878
2438
|
});
|
|
2879
2439
|
upsertGoalStatusEntry({
|
|
2880
2440
|
runId: checkedRun.id,
|
|
2881
|
-
label:
|
|
2441
|
+
label: checkedRun.title,
|
|
2882
2442
|
phase: "worker",
|
|
2883
2443
|
startedAt: Date.now(),
|
|
2884
2444
|
detail: "background worker running",
|
|
2885
2445
|
workerId: worker.id,
|
|
2886
2446
|
goalNumber: goalNumberForRun(checkedRun.id),
|
|
2447
|
+
...goalTaskProgress(checkedRun, decision.task),
|
|
2887
2448
|
});
|
|
2888
|
-
})().catch((err) => {
|
|
2449
|
+
})().catch(async (err) => {
|
|
2889
2450
|
clearGoalStatusEntry(run.id);
|
|
2890
2451
|
clearGoalModeIfIdle();
|
|
2891
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
|
+
}
|
|
2892
2474
|
setLiveItems((prev) => [...prev, toErrorItem(err, getId(), "Goal")]);
|
|
2893
2475
|
});
|
|
2894
2476
|
}, [
|
|
@@ -2896,6 +2478,7 @@ export function App(props) {
|
|
|
2896
2478
|
currentProvider,
|
|
2897
2479
|
currentModel,
|
|
2898
2480
|
thinkingLevel,
|
|
2481
|
+
agentLoop,
|
|
2899
2482
|
appendGoalProgress,
|
|
2900
2483
|
clearGoalModeIfIdle,
|
|
2901
2484
|
clearGoalStatusEntry,
|
|
@@ -2928,6 +2511,30 @@ export function App(props) {
|
|
|
2928
2511
|
clearGoalModeIfIdle();
|
|
2929
2512
|
return;
|
|
2930
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
|
+
}
|
|
2931
2538
|
activeVerifierRunIdsRef.current.add(run.id);
|
|
2932
2539
|
await upsertGoalRun(props.cwd, {
|
|
2933
2540
|
...run,
|
|
@@ -2952,7 +2559,7 @@ export function App(props) {
|
|
|
2952
2559
|
goalNumber: goalNumberForRun(run.id),
|
|
2953
2560
|
});
|
|
2954
2561
|
void runGoalVerifierCommand({
|
|
2955
|
-
cwd: props.cwd,
|
|
2562
|
+
cwd: run.verifier.cwd ?? props.cwd,
|
|
2956
2563
|
runId: run.id,
|
|
2957
2564
|
command: run.verifier.command,
|
|
2958
2565
|
timeoutMs: verifierTimeoutMs,
|
|
@@ -2970,6 +2577,7 @@ export function App(props) {
|
|
|
2970
2577
|
...latestRun.verifier,
|
|
2971
2578
|
description: latestRun.verifier?.description ?? "Goal verifier",
|
|
2972
2579
|
command: run.verifier?.command,
|
|
2580
|
+
...(run.verifier?.cwd ? { cwd: run.verifier.cwd } : {}),
|
|
2973
2581
|
lastResult: verification,
|
|
2974
2582
|
},
|
|
2975
2583
|
...(status === "pass"
|
|
@@ -2999,7 +2607,7 @@ export function App(props) {
|
|
|
2999
2607
|
await appendGoalDecision(props.cwd, run.id, {
|
|
3000
2608
|
kind: `verifier_${status}`,
|
|
3001
2609
|
reason: `${failureClass}: verifier exited with code ${verification.exitCode ?? 1}.`,
|
|
3002
|
-
content: `outputPath=${outputPath ?? ""}; durationMs=${durationMs}`,
|
|
2610
|
+
content: `outputPath=${outputPath ?? ""}; cwd=${run.verifier?.cwd ?? props.cwd}; durationMs=${durationMs}`,
|
|
3003
2611
|
});
|
|
3004
2612
|
appendGoalProgress({
|
|
3005
2613
|
kind: "goal_progress",
|
|
@@ -3046,11 +2654,7 @@ export function App(props) {
|
|
|
3046
2654
|
if (run.activeWorkerId)
|
|
3047
2655
|
await stopGoalWorker(run.activeWorkerId);
|
|
3048
2656
|
const latestRun = (await loadGoalRuns(props.cwd)).find((item) => item.id === run.id) ?? run;
|
|
3049
|
-
await upsertGoalRun(props.cwd,
|
|
3050
|
-
...latestRun,
|
|
3051
|
-
status: "paused",
|
|
3052
|
-
activeWorkerId: undefined,
|
|
3053
|
-
});
|
|
2657
|
+
await upsertGoalRun(props.cwd, buildGoalUserPauseRun(latestRun));
|
|
3054
2658
|
appendGoalProgress({
|
|
3055
2659
|
kind: "goal_progress",
|
|
3056
2660
|
phase: "terminal",
|
|
@@ -3065,8 +2669,70 @@ export function App(props) {
|
|
|
3065
2669
|
setLiveItems((prev) => [...prev, toErrorItem(err, getId(), "Goal")]);
|
|
3066
2670
|
});
|
|
3067
2671
|
}, [appendGoalProgress, clearGoalModeIfIdle, clearGoalStatusEntry, props.cwd]);
|
|
2672
|
+
const startTask = useCallback((title, prompt, taskId) => {
|
|
2673
|
+
const taskCwd = cwdRef.current;
|
|
2674
|
+
const shortId = taskId.slice(0, 8);
|
|
2675
|
+
const completionHint = `\n\n---\nWhen you have fully completed this task, call the tasks tool to mark it done:\n` +
|
|
2676
|
+
`tasks({ action: "done", id: "${shortId}" })`;
|
|
2677
|
+
const fullPrompt = prompt + completionHint;
|
|
2678
|
+
if (props.resetUI && props.sessionStore) {
|
|
2679
|
+
const sysMsg = messagesRef.current[0];
|
|
2680
|
+
const newMessages = sysMsg && sysMsg.role === "system" ? [sysMsg] : messagesRef.current.slice(0, 1);
|
|
2681
|
+
const taskItem = { kind: "task", title, id: getId() };
|
|
2682
|
+
const sm = sessionManagerRef.current;
|
|
2683
|
+
void (async () => {
|
|
2684
|
+
let newSessionPath;
|
|
2685
|
+
if (sm) {
|
|
2686
|
+
try {
|
|
2687
|
+
const session = await sm.create(taskCwd, currentProvider, currentModel);
|
|
2688
|
+
newSessionPath = session.path;
|
|
2689
|
+
log("INFO", "tasks", "New session for task", { path: session.path });
|
|
2690
|
+
}
|
|
2691
|
+
catch {
|
|
2692
|
+
// Session creation is best-effort.
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
if (props.sessionStore)
|
|
2696
|
+
props.sessionStore.overlay = null;
|
|
2697
|
+
props.resetUI?.({
|
|
2698
|
+
wipeSession: true,
|
|
2699
|
+
messages: newMessages,
|
|
2700
|
+
history: [{ kind: "banner", id: "banner" }, taskItem],
|
|
2701
|
+
sessionPath: newSessionPath,
|
|
2702
|
+
pendingAction: { prompt: fullPrompt },
|
|
2703
|
+
});
|
|
2704
|
+
})();
|
|
2705
|
+
return;
|
|
2706
|
+
}
|
|
2707
|
+
clearPendingHistory();
|
|
2708
|
+
setHistory([{ kind: "banner", id: "banner" }]);
|
|
2709
|
+
setLiveItems([]);
|
|
2710
|
+
messagesRef.current = messagesRef.current.slice(0, 1);
|
|
2711
|
+
agentLoop.reset();
|
|
2712
|
+
persistedIndexRef.current = messagesRef.current.length;
|
|
2713
|
+
const sm = sessionManagerRef.current;
|
|
2714
|
+
if (sm) {
|
|
2715
|
+
void sm.create(taskCwd, currentProvider, currentModel).then((session) => {
|
|
2716
|
+
sessionPathRef.current = session.path;
|
|
2717
|
+
log("INFO", "tasks", "New session for task", { path: session.path });
|
|
2718
|
+
});
|
|
2719
|
+
}
|
|
2720
|
+
const taskItem = { kind: "task", title, id: getId() };
|
|
2721
|
+
setLastUserMessage(title);
|
|
2722
|
+
setDoneStatus(null);
|
|
2723
|
+
setLiveItems([taskItem]);
|
|
2724
|
+
void agentLoop.run(fullPrompt).catch((err) => {
|
|
2725
|
+
setLiveItems((prev) => [...prev, toErrorItem(err, getId())]);
|
|
2726
|
+
});
|
|
2727
|
+
}, [agentLoop, currentModel, currentProvider, props]);
|
|
3068
2728
|
// Keep refs in sync for access from stale closures (onDone)
|
|
2729
|
+
startTaskRef.current = startTask;
|
|
3069
2730
|
startGoalRunRef.current = startGoalRun;
|
|
2731
|
+
useEffect(() => {
|
|
2732
|
+
runAllTasksRef.current = runAllTasks;
|
|
2733
|
+
if (props.sessionStore)
|
|
2734
|
+
props.sessionStore.runAllTasks = runAllTasks;
|
|
2735
|
+
}, [runAllTasks, props.sessionStore]);
|
|
3070
2736
|
useEffect(() => {
|
|
3071
2737
|
agentRunningRef.current = agentLoop.isRunning;
|
|
3072
2738
|
}, [agentLoop.isRunning]);
|
|
@@ -3091,13 +2757,6 @@ export function App(props) {
|
|
|
3091
2757
|
log("WARN", "pixel", `chdir failed: ${err.message}`);
|
|
3092
2758
|
}
|
|
3093
2759
|
cwdRef.current = prep.projectPath;
|
|
3094
|
-
repoMapDirtyRef.current = true;
|
|
3095
|
-
repoMapMarkdownRef.current = "";
|
|
3096
|
-
repoMapSnapshotRef.current = undefined;
|
|
3097
|
-
repoMapChangedCountRef.current = 0;
|
|
3098
|
-
repoMapCacheRef.current = createRepoMapCache();
|
|
3099
|
-
props.repoMapChangedFilesRef?.current.clear();
|
|
3100
|
-
props.repoMapReadFilesRef?.current.clear();
|
|
3101
2760
|
setDisplayedCwd(prep.projectPath);
|
|
3102
2761
|
let toolsForPixelFix = currentToolsRef.current;
|
|
3103
2762
|
if (props.rebuildToolsForCwd) {
|
|
@@ -3121,8 +2780,7 @@ export function App(props) {
|
|
|
3121
2780
|
});
|
|
3122
2781
|
// Now that the cwd swap is committed, reset chat. Do not clear the
|
|
3123
2782
|
// terminal here; terminal clear sequences can erase saved scrollback.
|
|
3124
|
-
|
|
3125
|
-
props.terminalHistoryPrinter?.clear();
|
|
2783
|
+
clearPendingHistory();
|
|
3126
2784
|
setHistory([{ kind: "banner", id: "banner" }]);
|
|
3127
2785
|
setLiveItems([]);
|
|
3128
2786
|
messagesRef.current = messagesRef.current.slice(0, 1);
|
|
@@ -3166,63 +2824,28 @@ export function App(props) {
|
|
|
3166
2824
|
if (props.sessionStore)
|
|
3167
2825
|
props.sessionStore.runAllPixel = runAllPixel;
|
|
3168
2826
|
}, [runAllPixel, props.sessionStore]);
|
|
3169
|
-
const isGoalView = overlay === "goal";
|
|
3170
2827
|
const isSkillsView = overlay === "skills";
|
|
3171
2828
|
const isPlanView = overlay === "plan";
|
|
3172
|
-
const footerStatusLayout =
|
|
2829
|
+
const { footerStatusLayout, activityVisible, stallStatusVisible, statusSlotVisible, mainControlsRef, measuredLiveAreaRows, } = useChatLayoutMeasurements({
|
|
2830
|
+
rows,
|
|
3173
2831
|
columns,
|
|
3174
2832
|
backgroundTaskCount: bgTasks.length,
|
|
3175
2833
|
updatePending,
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
const controlsObserverRef = useRef(null);
|
|
3183
|
-
const mainControlsRef = useCallback((node) => {
|
|
3184
|
-
if (controlsObserverRef.current) {
|
|
3185
|
-
controlsObserverRef.current.disconnect();
|
|
3186
|
-
controlsObserverRef.current = null;
|
|
3187
|
-
}
|
|
3188
|
-
if (!node || typeof ResizeObserver === "undefined")
|
|
3189
|
-
return;
|
|
3190
|
-
const observer = new ResizeObserver((entries) => {
|
|
3191
|
-
const entry = entries[0];
|
|
3192
|
-
if (!entry)
|
|
3193
|
-
return;
|
|
3194
|
-
const roundedHeight = Math.round(entry.contentRect.height);
|
|
3195
|
-
setControlsHeight((prev) => (roundedHeight !== prev ? roundedHeight : prev));
|
|
3196
|
-
});
|
|
3197
|
-
observer.observe(node);
|
|
3198
|
-
controlsObserverRef.current = observer;
|
|
3199
|
-
}, []);
|
|
3200
|
-
useEffect(() => () => controlsObserverRef.current?.disconnect(), []);
|
|
3201
|
-
const footerFitsOnOneLine = doesFooterFitOnOneLine({
|
|
3202
|
-
columns,
|
|
3203
|
-
model: currentModel,
|
|
3204
|
-
tokensIn: agentLoop.contextUsed,
|
|
2834
|
+
agentRunning: agentLoop.isRunning,
|
|
2835
|
+
activityPhase: agentLoop.activityPhase,
|
|
2836
|
+
stallError: agentLoop.stallError,
|
|
2837
|
+
doneStatus,
|
|
2838
|
+
currentModel,
|
|
2839
|
+
contextUsed: agentLoop.contextUsed,
|
|
3205
2840
|
contextWindowOptions,
|
|
3206
|
-
|
|
2841
|
+
displayedCwd,
|
|
3207
2842
|
gitBranch,
|
|
3208
2843
|
thinkingLevel,
|
|
3209
2844
|
goalMode,
|
|
3210
|
-
});
|
|
3211
|
-
const chatControlsLayout = getChatControlsLayoutDecision({
|
|
3212
|
-
rows,
|
|
3213
|
-
columns,
|
|
3214
|
-
agentRunning: agentLoop.isRunning,
|
|
3215
|
-
activityVisible,
|
|
3216
|
-
doneStatusVisible,
|
|
3217
|
-
stallStatusVisible,
|
|
3218
2845
|
exitPending,
|
|
3219
|
-
footerStatusLayout,
|
|
3220
2846
|
taskBarExpanded,
|
|
3221
2847
|
goalStatusEntryCount: goalStatusEntries.length,
|
|
3222
|
-
footerFitsOnOneLine,
|
|
3223
2848
|
});
|
|
3224
|
-
const stableControlsRows = controlsHeight > 0 ? controlsHeight : chatControlsLayout.controlsRows;
|
|
3225
|
-
const measuredLiveAreaRows = Math.max(MIN_LIVE_AREA_ROWS, rows - stableControlsRows - 1);
|
|
3226
2849
|
const isPixelView = overlay === "pixel";
|
|
3227
2850
|
const hasLiveAssistantItem = liveItems.some((item) => item.kind === "assistant");
|
|
3228
2851
|
const rawVisibleStreamingText = goalModeStateRef.current === "planner" || hasLiveAssistantItem ? "" : agentLoop.streamingText;
|
|
@@ -3240,7 +2863,7 @@ export function App(props) {
|
|
|
3240
2863
|
queueFlush([
|
|
3241
2864
|
{
|
|
3242
2865
|
kind: "assistant",
|
|
3243
|
-
text: split.flushedText,
|
|
2866
|
+
text: stripDoneMarkers(split.flushedText),
|
|
3244
2867
|
continuation: streamedAssistantFlushRef.current.flushedChars > 0,
|
|
3245
2868
|
id: getId(),
|
|
3246
2869
|
},
|
|
@@ -3256,13 +2879,20 @@ export function App(props) {
|
|
|
3256
2879
|
text: rawVisibleStreamingText,
|
|
3257
2880
|
};
|
|
3258
2881
|
}, [rawVisibleStreamingText, queueFlush]);
|
|
3259
|
-
const visibleStreamingText = rawVisibleStreamingText.slice(streamedAssistantFlushRef.current.flushedChars);
|
|
3260
|
-
const shouldReserveStreamingSpacing = agentLoop.isRunning &&
|
|
3261
|
-
!hasLiveAssistantItem &&
|
|
3262
|
-
(visibleStreamingText.trim().length > 0 || liveItems.some(isAgentSpacingItem));
|
|
2882
|
+
const visibleStreamingText = stripDoneMarkers(rawVisibleStreamingText.slice(streamedAssistantFlushRef.current.flushedChars));
|
|
3263
2883
|
const lastLiveItem = liveItems.at(-1);
|
|
3264
2884
|
const lastPendingHistoryItem = pendingHistoryFlushRef.current.at(-1);
|
|
3265
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);
|
|
3266
2896
|
const shouldTopSpaceStreamingText = shouldTopSpaceStreamingAssistant({
|
|
3267
2897
|
visibleStreamingText,
|
|
3268
2898
|
lastLiveItem,
|
|
@@ -3278,239 +2908,282 @@ export function App(props) {
|
|
|
3278
2908
|
lastPendingHistoryItem,
|
|
3279
2909
|
lastHistoryItem,
|
|
3280
2910
|
});
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
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
|
-
props.resetUI();
|
|
3372
|
-
}
|
|
3373
|
-
else {
|
|
3374
|
-
if (props.sessionStore) {
|
|
3375
|
-
props.sessionStore.overlay = null;
|
|
3376
|
-
if (agentLoop.isRunning)
|
|
3377
|
-
props.sessionStore.pendingResetUI = true;
|
|
3378
|
-
}
|
|
3379
|
-
setOverlay(null);
|
|
3380
|
-
}
|
|
3381
|
-
} })) : isPlanView ? (_jsx(PlanOverlay, { cwd: props.cwd, autoExpandNewest: planAutoExpand, onClose: () => {
|
|
3382
|
-
planOverlayPendingRef.current = false;
|
|
3383
|
-
if (props.resetUI && props.sessionStore && !agentLoop.isRunning) {
|
|
3384
|
-
props.sessionStore.overlay = null;
|
|
3385
|
-
props.sessionStore.planAutoExpand = false;
|
|
3386
|
-
props.resetUI();
|
|
3387
|
-
}
|
|
3388
|
-
else {
|
|
3389
|
-
if (props.sessionStore) {
|
|
3390
|
-
props.sessionStore.overlay = null;
|
|
3391
|
-
props.sessionStore.planAutoExpand = false;
|
|
3392
|
-
if (agentLoop.isRunning)
|
|
3393
|
-
props.sessionStore.pendingResetUI = true;
|
|
3394
|
-
}
|
|
3395
|
-
setPlanAutoExpand(false);
|
|
3396
|
-
setOverlay(null);
|
|
3397
|
-
}
|
|
3398
|
-
}, onApprove: (planPath) => {
|
|
3399
|
-
log("INFO", "plan", "Plan approved — transitioning to implementation", {
|
|
3400
|
-
planPath,
|
|
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,
|
|
3401
3001
|
});
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
const newPrompt = await rebuildSystemPrompt({
|
|
3411
|
-
approvedPlanPath: planPath,
|
|
3412
|
-
});
|
|
3413
|
-
// Create a new session file BEFORE remount so the new tree
|
|
3414
|
-
// picks it up via sessionStore.sessionPath.
|
|
3415
|
-
let newSessionPath;
|
|
3416
|
-
const sm = sessionManagerRef.current;
|
|
3417
|
-
if (sm) {
|
|
3418
|
-
const s = await sm.create(props.cwd, currentProvider, currentModel);
|
|
3419
|
-
newSessionPath = s.path;
|
|
3420
|
-
}
|
|
3421
|
-
if (props.resetUI && props.sessionStore) {
|
|
3422
|
-
// Clear the overlay so the new mount lands on the chat,
|
|
3423
|
-
// not back inside the plan pane.
|
|
3424
|
-
props.sessionStore.overlay = null;
|
|
3425
|
-
props.sessionStore.planAutoExpand = false;
|
|
3426
|
-
props.resetUI({
|
|
3427
|
-
wipeSession: true,
|
|
3428
|
-
messages: [{ role: "system", content: newPrompt }],
|
|
3429
|
-
approvedPlanPath: planPath,
|
|
3430
|
-
planSteps: steps,
|
|
3431
|
-
sessionPath: newSessionPath,
|
|
3432
|
-
pendingAction: {
|
|
3433
|
-
prompt: "The plan has been approved. Implement it now, following each step in order.",
|
|
3434
|
-
planEvent: { event: "approved" },
|
|
3435
|
-
},
|
|
3436
|
-
});
|
|
3437
|
-
return;
|
|
3438
|
-
}
|
|
3439
|
-
// Fallback path (resetUI not wired — tests). Mutate in place.
|
|
3440
|
-
approvedPlanPathRef.current = planPath;
|
|
3441
|
-
planStepsRef.current = steps;
|
|
3442
|
-
setPlanSteps(steps);
|
|
3443
|
-
pendingHistoryFlushRef.current = [];
|
|
3444
|
-
props.terminalHistoryPrinter?.clear();
|
|
3445
|
-
setHistory([{ kind: "banner", id: "banner" }]);
|
|
3446
|
-
setLiveItems([]);
|
|
3447
|
-
setPlanAutoExpand(false);
|
|
3448
|
-
setOverlay(null);
|
|
3449
|
-
messagesRef.current = [{ role: "system", content: newPrompt }];
|
|
3450
|
-
agentLoop.reset();
|
|
3451
|
-
persistedIndexRef.current = messagesRef.current.length;
|
|
3452
|
-
if (newSessionPath)
|
|
3453
|
-
sessionPathRef.current = newSessionPath;
|
|
3454
|
-
setLiveItems([
|
|
3455
|
-
{
|
|
3456
|
-
kind: "info",
|
|
3457
|
-
text: "Plan approved — starting fresh session for implementation",
|
|
3458
|
-
id: getId(),
|
|
3459
|
-
},
|
|
3460
|
-
]);
|
|
3461
|
-
setDoneStatus(null);
|
|
3462
|
-
await agentLoop.run("The plan has been approved. Implement it now, following each step in order.");
|
|
3463
|
-
}
|
|
3464
|
-
catch (err) {
|
|
3465
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3466
|
-
log("ERROR", "error", errMsg);
|
|
3467
|
-
setLiveItems((prev) => [...prev, toErrorItem(err, getId())]);
|
|
3468
|
-
}
|
|
3469
|
-
})();
|
|
3470
|
-
}, onReject: (planPath, feedback) => {
|
|
3471
|
-
planOverlayPendingRef.current = false;
|
|
3472
|
-
const rejectionMsg = `The plan at ${planPath} was rejected.\n\nFeedback: ${feedback}\n\n` +
|
|
3473
|
-
`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
|
+
}
|
|
3474
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.
|
|
3475
3013
|
props.sessionStore.overlay = null;
|
|
3476
3014
|
props.sessionStore.planAutoExpand = false;
|
|
3477
|
-
// No wipeSession — keep history and messages so the agent picks
|
|
3478
|
-
// up the rejection mid-conversation.
|
|
3479
3015
|
props.resetUI({
|
|
3016
|
+
wipeSession: true,
|
|
3017
|
+
messages: [{ role: "system", content: newPrompt }],
|
|
3018
|
+
approvedPlanPath: planPath,
|
|
3019
|
+
planSteps: steps,
|
|
3020
|
+
sessionPath: newSessionPath,
|
|
3480
3021
|
pendingAction: {
|
|
3481
|
-
prompt:
|
|
3482
|
-
planEvent: { event: "
|
|
3022
|
+
prompt: "The plan has been approved. Implement it now, following each step in order.",
|
|
3023
|
+
planEvent: { event: "approved" },
|
|
3483
3024
|
},
|
|
3484
3025
|
});
|
|
3485
3026
|
return;
|
|
3486
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([]);
|
|
3487
3035
|
setPlanAutoExpand(false);
|
|
3488
3036
|
setOverlay(null);
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
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
|
+
},
|
|
3493
3048
|
]);
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
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 })) }));
|
|
3515
3188
|
}
|
|
3516
3189
|
//# sourceMappingURL=App.js.map
|