@prestyj/cli 4.2.15 → 4.2.44
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/LICENSE +1 -1
- package/README.md +15 -7
- package/dist/cli.js +457 -225
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +66 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +22 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +184 -23
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/agents.d.ts +0 -2
- package/dist/core/agents.d.ts.map +1 -1
- package/dist/core/agents.js +1 -5
- package/dist/core/agents.js.map +1 -1
- package/dist/core/auth-storage.d.ts +2 -0
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +66 -17
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/auto-update.js +1 -1
- package/dist/core/auto-update.js.map +1 -1
- package/dist/core/compaction/compactor.d.ts +36 -5
- package/dist/core/compaction/compactor.d.ts.map +1 -1
- package/dist/core/compaction/compactor.js +222 -63
- package/dist/core/compaction/compactor.js.map +1 -1
- package/dist/core/compaction/compactor.test.js +377 -32
- package/dist/core/compaction/compactor.test.js.map +1 -1
- package/dist/core/compaction/token-estimator.d.ts +5 -0
- package/dist/core/compaction/token-estimator.d.ts.map +1 -1
- package/dist/core/compaction/token-estimator.js +41 -2
- package/dist/core/compaction/token-estimator.js.map +1 -1
- package/dist/core/compaction/token-estimator.test.js +43 -21
- package/dist/core/compaction/token-estimator.test.js.map +1 -1
- package/dist/core/event-bus.d.ts +6 -0
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js +7 -0
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/file-lock.d.ts +6 -0
- package/dist/core/file-lock.d.ts.map +1 -0
- package/dist/core/file-lock.js +76 -0
- package/dist/core/file-lock.js.map +1 -0
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/mcp/client.d.ts +0 -26
- package/dist/core/mcp/client.d.ts.map +1 -1
- package/dist/core/mcp/client.js +34 -141
- package/dist/core/mcp/client.js.map +1 -1
- package/dist/core/mcp/defaults.d.ts.map +1 -1
- package/dist/core/mcp/defaults.js +4 -8
- package/dist/core/mcp/defaults.js.map +1 -1
- package/dist/core/mcp/types.d.ts +0 -2
- package/dist/core/mcp/types.d.ts.map +1 -1
- package/dist/core/model-registry.d.ts +2 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +16 -5
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/oauth/anthropic.d.ts.map +1 -1
- package/dist/core/oauth/anthropic.js +3 -3
- package/dist/core/oauth/anthropic.js.map +1 -1
- package/dist/core/prompt-commands.d.ts.map +1 -1
- package/dist/core/prompt-commands.js +21 -0
- package/dist/core/prompt-commands.js.map +1 -1
- package/dist/core/session-manager.d.ts +34 -2
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +160 -54
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +2 -2
- package/dist/core/skills.d.ts +2 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +8 -8
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts +4 -0
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +22 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/telegram.d.ts +94 -0
- package/dist/core/telegram.d.ts.map +1 -0
- package/dist/core/telegram.js +227 -0
- package/dist/core/telegram.js.map +1 -0
- package/dist/interactive.d.ts.map +1 -1
- package/dist/interactive.js +36 -5
- package/dist/interactive.js.map +1 -1
- package/dist/modes/index.d.ts +1 -0
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/index.js +1 -0
- package/dist/modes/index.js.map +1 -1
- package/dist/modes/json-mode.d.ts.map +1 -1
- package/dist/modes/json-mode.js +2 -1
- package/dist/modes/json-mode.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +2 -1
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc-mode.d.ts +28 -0
- package/dist/modes/rpc-mode.d.ts.map +1 -0
- package/dist/modes/rpc-mode.js +146 -0
- package/dist/modes/rpc-mode.js.map +1 -0
- package/dist/modes/serve-mode.d.ts +21 -0
- package/dist/modes/serve-mode.d.ts.map +1 -0
- package/dist/modes/serve-mode.js +650 -0
- package/dist/modes/serve-mode.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 +45 -10
- package/dist/system-prompt.js.map +1 -1
- package/dist/tools/bash.d.ts +4 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +58 -5
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit.d.ts +4 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +9 -5
- 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 +28 -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/grep.d.ts +2 -1
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js +7 -8
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/index.d.ts +17 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +26 -7
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/ls.d.ts +2 -1
- package/dist/tools/ls.d.ts.map +1 -1
- package/dist/tools/ls.js +4 -4
- package/dist/tools/ls.js.map +1 -1
- package/dist/tools/operations.d.ts +39 -0
- package/dist/tools/operations.d.ts.map +1 -0
- package/dist/tools/operations.js +27 -0
- package/dist/tools/operations.js.map +1 -0
- package/dist/tools/path-utils.d.ts +5 -0
- package/dist/tools/path-utils.d.ts.map +1 -1
- package/dist/tools/path-utils.js +18 -0
- package/dist/tools/path-utils.js.map +1 -1
- package/dist/tools/plan-mode.test.d.ts +2 -0
- package/dist/tools/plan-mode.test.d.ts.map +1 -0
- package/dist/tools/plan-mode.test.js +214 -0
- package/dist/tools/plan-mode.test.js.map +1 -0
- package/dist/tools/read.d.ts +2 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +6 -5
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/skill.d.ts +10 -0
- package/dist/tools/skill.d.ts.map +1 -0
- package/dist/tools/skill.js +36 -0
- package/dist/tools/skill.js.map +1 -0
- package/dist/tools/subagent.d.ts +3 -1
- package/dist/tools/subagent.d.ts.map +1 -1
- package/dist/tools/subagent.js +46 -4
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/tasks.d.ts +2 -2
- package/dist/tools/web-fetch.d.ts.map +1 -1
- package/dist/tools/web-fetch.js +47 -0
- package/dist/tools/web-fetch.js.map +1 -1
- package/dist/tools/write.d.ts +4 -1
- package/dist/tools/write.d.ts.map +1 -1
- package/dist/tools/write.js +17 -6
- package/dist/tools/write.js.map +1 -1
- package/dist/ui/App.d.ts +35 -1
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +367 -73
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/components/ActivityIndicator.d.ts +2 -1
- package/dist/ui/components/ActivityIndicator.d.ts.map +1 -1
- package/dist/ui/components/ActivityIndicator.js +25 -4
- package/dist/ui/components/ActivityIndicator.js.map +1 -1
- package/dist/ui/components/AnimationContext.d.ts +22 -0
- package/dist/ui/components/AnimationContext.d.ts.map +1 -0
- package/dist/ui/components/AnimationContext.js +35 -0
- package/dist/ui/components/AnimationContext.js.map +1 -0
- package/dist/ui/components/AssistantMessage.d.ts.map +1 -1
- package/dist/ui/components/AssistantMessage.js +6 -1
- package/dist/ui/components/AssistantMessage.js.map +1 -1
- package/dist/ui/components/Banner.d.ts.map +1 -1
- package/dist/ui/components/Banner.js +4 -7
- package/dist/ui/components/Banner.js.map +1 -1
- package/dist/ui/components/CompactionNotice.d.ts.map +1 -1
- package/dist/ui/components/CompactionNotice.js +4 -8
- package/dist/ui/components/CompactionNotice.js.map +1 -1
- package/dist/ui/components/Footer.d.ts +2 -1
- package/dist/ui/components/Footer.d.ts.map +1 -1
- package/dist/ui/components/Footer.js +8 -15
- package/dist/ui/components/Footer.js.map +1 -1
- package/dist/ui/components/InputArea.d.ts +3 -1
- package/dist/ui/components/InputArea.d.ts.map +1 -1
- package/dist/ui/components/InputArea.js +23 -25
- package/dist/ui/components/InputArea.js.map +1 -1
- package/dist/ui/components/PlanApproval.d.ts +8 -0
- package/dist/ui/components/PlanApproval.d.ts.map +1 -0
- package/dist/ui/components/PlanApproval.js +44 -0
- package/dist/ui/components/PlanApproval.js.map +1 -0
- package/dist/ui/components/PlanBanner.d.ts +6 -0
- package/dist/ui/components/PlanBanner.d.ts.map +1 -0
- package/dist/ui/components/PlanBanner.js +14 -0
- package/dist/ui/components/PlanBanner.js.map +1 -0
- package/dist/ui/components/PlanOverlay.d.ts +11 -0
- package/dist/ui/components/PlanOverlay.d.ts.map +1 -0
- package/dist/ui/components/PlanOverlay.js +263 -0
- package/dist/ui/components/PlanOverlay.js.map +1 -0
- package/dist/ui/components/ServerToolExecution.d.ts +1 -0
- package/dist/ui/components/ServerToolExecution.d.ts.map +1 -1
- package/dist/ui/components/ServerToolExecution.js +12 -3
- package/dist/ui/components/ServerToolExecution.js.map +1 -1
- package/dist/ui/components/SkillsOverlay.d.ts +7 -0
- package/dist/ui/components/SkillsOverlay.d.ts.map +1 -0
- package/dist/ui/components/SkillsOverlay.js +158 -0
- package/dist/ui/components/SkillsOverlay.js.map +1 -0
- package/dist/ui/components/Spinner.d.ts.map +1 -1
- package/dist/ui/components/Spinner.js +5 -9
- package/dist/ui/components/Spinner.js.map +1 -1
- package/dist/ui/components/StreamingArea.d.ts +2 -1
- package/dist/ui/components/StreamingArea.d.ts.map +1 -1
- package/dist/ui/components/StreamingArea.js +11 -4
- package/dist/ui/components/StreamingArea.js.map +1 -1
- package/dist/ui/components/SubAgentPanel.d.ts.map +1 -1
- package/dist/ui/components/SubAgentPanel.js +27 -29
- package/dist/ui/components/SubAgentPanel.js.map +1 -1
- package/dist/ui/components/TaskOverlay.d.ts.map +1 -1
- package/dist/ui/components/TaskOverlay.js +7 -3
- package/dist/ui/components/TaskOverlay.js.map +1 -1
- package/dist/ui/components/ThinkingBlock.d.ts.map +1 -1
- package/dist/ui/components/ThinkingBlock.js +4 -1
- package/dist/ui/components/ThinkingBlock.js.map +1 -1
- package/dist/ui/components/ThinkingIndicator.d.ts.map +1 -1
- package/dist/ui/components/ThinkingIndicator.js +8 -33
- package/dist/ui/components/ThinkingIndicator.js.map +1 -1
- package/dist/ui/components/ToolExecution.d.ts.map +1 -1
- package/dist/ui/components/ToolExecution.js +95 -41
- package/dist/ui/components/ToolExecution.js.map +1 -1
- package/dist/ui/components/ToolGroupExecution.d.ts +7 -0
- package/dist/ui/components/ToolGroupExecution.d.ts.map +1 -0
- package/dist/ui/components/ToolGroupExecution.js +115 -0
- package/dist/ui/components/ToolGroupExecution.js.map +1 -0
- package/dist/ui/components/UserMessage.js +1 -1
- package/dist/ui/components/UserMessage.js.map +1 -1
- package/dist/ui/components/index.d.ts +1 -0
- package/dist/ui/components/index.d.ts.map +1 -1
- package/dist/ui/components/index.js +1 -0
- package/dist/ui/components/index.js.map +1 -1
- package/dist/ui/hooks/useAgentLoop.d.ts +8 -0
- package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
- package/dist/ui/hooks/useAgentLoop.js +320 -220
- package/dist/ui/hooks/useAgentLoop.js.map +1 -1
- package/dist/ui/hooks/useTerminalSize.d.ts +21 -9
- package/dist/ui/hooks/useTerminalSize.d.ts.map +1 -1
- package/dist/ui/hooks/useTerminalSize.js +33 -16
- package/dist/ui/hooks/useTerminalSize.js.map +1 -1
- package/dist/ui/hooks/useTerminalTitle.d.ts.map +1 -1
- package/dist/ui/hooks/useTerminalTitle.js +8 -16
- package/dist/ui/hooks/useTerminalTitle.js.map +1 -1
- package/dist/ui/live-item-flush.d.ts +11 -1
- package/dist/ui/live-item-flush.d.ts.map +1 -1
- package/dist/ui/live-item-flush.js +81 -2
- package/dist/ui/live-item-flush.js.map +1 -1
- package/dist/ui/login.d.ts +1 -1
- package/dist/ui/login.d.ts.map +1 -1
- package/dist/ui/login.js +30 -6
- package/dist/ui/login.js.map +1 -1
- package/dist/ui/render.d.ts +11 -0
- package/dist/ui/render.d.ts.map +1 -1
- package/dist/ui/render.js +24 -10
- package/dist/ui/render.js.map +1 -1
- package/dist/ui/sessions.d.ts +2 -0
- package/dist/ui/sessions.d.ts.map +1 -0
- package/dist/ui/sessions.js +208 -0
- package/dist/ui/sessions.js.map +1 -0
- package/dist/ui/theme/dark.json +3 -1
- package/dist/ui/theme/light.json +3 -1
- package/dist/ui/theme/theme.d.ts +2 -0
- package/dist/ui/theme/theme.d.ts.map +1 -1
- package/dist/utils/error-handler.d.ts.map +1 -1
- package/dist/utils/error-handler.js +17 -5
- package/dist/utils/error-handler.js.map +1 -1
- package/dist/utils/image.d.ts.map +1 -1
- package/dist/utils/image.js +60 -5
- package/dist/utils/image.js.map +1 -1
- package/package.json +9 -9
package/dist/ui/App.js
CHANGED
|
@@ -12,6 +12,7 @@ import { useAgentLoop } from "./hooks/useAgentLoop.js";
|
|
|
12
12
|
import { UserMessage } from "./components/UserMessage.js";
|
|
13
13
|
import { AssistantMessage } from "./components/AssistantMessage.js";
|
|
14
14
|
import { ToolExecution } from "./components/ToolExecution.js";
|
|
15
|
+
import { ToolGroupExecution } from "./components/ToolGroupExecution.js";
|
|
15
16
|
import { ServerToolExecution } from "./components/ServerToolExecution.js";
|
|
16
17
|
import { SubAgentPanel } from "./components/SubAgentPanel.js";
|
|
17
18
|
import { CompactionSpinner, CompactionDone } from "./components/CompactionNotice.js";
|
|
@@ -20,10 +21,13 @@ import { ActivityIndicator } from "./components/ActivityIndicator.js";
|
|
|
20
21
|
import { InputArea } from "./components/InputArea.js";
|
|
21
22
|
import { Footer } from "./components/Footer.js";
|
|
22
23
|
import { Banner } from "./components/Banner.js";
|
|
24
|
+
import { PlanOverlay } from "./components/PlanOverlay.js";
|
|
23
25
|
import { ModelSelector } from "./components/ModelSelector.js";
|
|
24
26
|
import { TaskOverlay } from "./components/TaskOverlay.js";
|
|
27
|
+
import { SkillsOverlay } from "./components/SkillsOverlay.js";
|
|
25
28
|
import { BackgroundTasksBar } from "./components/BackgroundTasksBar.js";
|
|
26
29
|
import { useTheme } from "./theme/theme.js";
|
|
30
|
+
import { useAnimationTick, deriveFrame } from "./components/AnimationContext.js";
|
|
27
31
|
import { useTerminalTitle } from "./hooks/useTerminalTitle.js";
|
|
28
32
|
import { getGitBranch } from "../utils/git.js";
|
|
29
33
|
import { getModel, getContextWindow } from "../core/model-registry.js";
|
|
@@ -34,28 +38,29 @@ import { shouldCompact, compact } from "../core/compaction/compactor.js";
|
|
|
34
38
|
import { estimateConversationTokens } from "../core/compaction/token-estimator.js";
|
|
35
39
|
import { PROMPT_COMMANDS, getPromptCommand } from "../core/prompt-commands.js";
|
|
36
40
|
import { loadCustomCommands } from "../core/custom-commands.js";
|
|
41
|
+
import { buildSystemPrompt } from "../system-prompt.js";
|
|
37
42
|
import { getMCPServers } from "../core/mcp/index.js";
|
|
38
|
-
import {
|
|
43
|
+
import { trimFlushedItems, flushOnTurnText, flushOnTurnEnd } from "./live-item-flush.js";
|
|
39
44
|
// ── Provider Error Hints ──────────────────────────────────
|
|
40
45
|
/** Detect provider-side errors and return a user-facing hint. */
|
|
41
46
|
function getProviderErrorHint(message) {
|
|
42
47
|
const lower = message.toLowerCase();
|
|
43
|
-
// Billing / insufficient balance (some providers use 429 for this)
|
|
44
|
-
if (lower.includes("insufficient balance") ||
|
|
45
|
-
lower.includes("quota exceeded") ||
|
|
46
|
-
lower.includes("billing")) {
|
|
47
|
-
return "Your account has insufficient balance or no active resource package. Top up your account at the provider's dashboard.";
|
|
48
|
-
}
|
|
49
48
|
if (lower.includes("overloaded") || lower.includes("engine_overloaded")) {
|
|
50
49
|
return "This is a provider-side issue — their servers are under heavy load. Try again in a moment.";
|
|
51
50
|
}
|
|
51
|
+
if (lower.includes("insufficient balance") ||
|
|
52
|
+
lower.includes("no resource package") ||
|
|
53
|
+
lower.includes("quota exceeded") ||
|
|
54
|
+
lower.includes("recharge")) {
|
|
55
|
+
return "The provider reports a billing or quota issue. Check your account balance or resource package.";
|
|
56
|
+
}
|
|
52
57
|
if (lower.includes("rate limit") ||
|
|
53
58
|
lower.includes("too many requests") ||
|
|
54
59
|
lower.includes("429")) {
|
|
55
60
|
return "You've hit the provider's rate limit. Wait a moment before retrying.";
|
|
56
61
|
}
|
|
57
62
|
if (lower.includes("502") || lower.includes("bad gateway")) {
|
|
58
|
-
return "The provider returned a server error. This is not
|
|
63
|
+
return "The provider returned a server error. This is not a ezcoder issue — try again shortly.";
|
|
59
64
|
}
|
|
60
65
|
if (lower.includes("503") || lower.includes("service unavailable")) {
|
|
61
66
|
return "The provider's service is temporarily unavailable. Try again in a moment.";
|
|
@@ -64,12 +69,39 @@ function getProviderErrorHint(message) {
|
|
|
64
69
|
return "The request to the provider timed out. Their servers may be slow — try again.";
|
|
65
70
|
}
|
|
66
71
|
if (lower.includes("500") && lower.includes("internal server error")) {
|
|
67
|
-
return "The provider experienced an internal error. This is not
|
|
72
|
+
return "The provider experienced an internal error. This is not a ezcoder issue.";
|
|
73
|
+
}
|
|
74
|
+
if (lower.includes("does not recognize the requested model") ||
|
|
75
|
+
(lower.includes("model") &&
|
|
76
|
+
(lower.includes("not exist") || lower.includes("not found") || lower.includes("no access")))) {
|
|
77
|
+
return "Use /model to switch to a different model, or check that your account has access to the requested model.";
|
|
68
78
|
}
|
|
69
79
|
return null;
|
|
70
80
|
}
|
|
71
|
-
|
|
72
|
-
|
|
81
|
+
/** Tools that get aggregated into a single compact group when concurrent. */
|
|
82
|
+
const AGGREGATABLE_TOOLS = new Set(["read", "grep", "find", "ls"]);
|
|
83
|
+
/**
|
|
84
|
+
* Cap memory by replacing old items with tiny tombstones. Ink's <Static>
|
|
85
|
+
* tracks rendered items by array length — the array must never shrink, but
|
|
86
|
+
* we can swap out heavy objects for lightweight `{ kind: "tombstone", id }`
|
|
87
|
+
* entries so GC can reclaim the original data.
|
|
88
|
+
*/
|
|
89
|
+
const MAX_LIVE_HISTORY = 200;
|
|
90
|
+
function compactHistory(items) {
|
|
91
|
+
if (items.length <= MAX_LIVE_HISTORY)
|
|
92
|
+
return items;
|
|
93
|
+
const cutoff = items.length - MAX_LIVE_HISTORY;
|
|
94
|
+
const compacted = new Array(items.length);
|
|
95
|
+
for (let i = 0; i < cutoff; i++) {
|
|
96
|
+
const it = items[i];
|
|
97
|
+
compacted[i] = it.kind === "tombstone" ? it : { kind: "tombstone", id: it.id };
|
|
98
|
+
}
|
|
99
|
+
for (let i = cutoff; i < items.length; i++) {
|
|
100
|
+
compacted[i] = items[i];
|
|
101
|
+
}
|
|
102
|
+
return compacted;
|
|
103
|
+
}
|
|
104
|
+
// flushOnTurnText, flushOnTurnEnd are imported from ./live-item-flush.ts
|
|
73
105
|
// ── Duration summary ─────────────────────────────────────
|
|
74
106
|
function formatDuration(ms) {
|
|
75
107
|
const totalSec = Math.round(ms / 1000);
|
|
@@ -211,7 +243,7 @@ export function App(props) {
|
|
|
211
243
|
useEffect(() => {
|
|
212
244
|
if (isRestoredSession && !restoredRef.current) {
|
|
213
245
|
restoredRef.current = true;
|
|
214
|
-
setHistory((prev) =>
|
|
246
|
+
setHistory((prev) => compactHistory([...prev, ...trimFlushedItems(props.initialHistory)]));
|
|
215
247
|
}
|
|
216
248
|
}, [isRestoredSession, props.initialHistory]);
|
|
217
249
|
// Items from the current/last turn — rendered in the live area so they stay visible
|
|
@@ -231,6 +263,9 @@ export function App(props) {
|
|
|
231
263
|
const [currentTools, setCurrentTools] = useState(props.tools);
|
|
232
264
|
const [thinkingEnabled, setThinkingEnabled] = useState(!!props.thinking);
|
|
233
265
|
const messagesRef = useRef(props.messages);
|
|
266
|
+
const [planMode, setPlanMode] = useState(false);
|
|
267
|
+
const [planAutoExpand, setPlanAutoExpand] = useState(false);
|
|
268
|
+
const approvedPlanPathRef = useRef(undefined);
|
|
234
269
|
const nextIdRef = useRef(0);
|
|
235
270
|
const sessionManagerRef = useRef(props.sessionsDir ? new SessionManager(props.sessionsDir) : null);
|
|
236
271
|
const sessionPathRef = useRef(props.sessionPath);
|
|
@@ -255,6 +290,56 @@ export function App(props) {
|
|
|
255
290
|
useEffect(() => {
|
|
256
291
|
reloadCustomCommands();
|
|
257
292
|
}, [reloadCustomCommands]);
|
|
293
|
+
// ── Plan mode wiring ─────────────────────────────────────
|
|
294
|
+
// Sync planModeRef with React state
|
|
295
|
+
useEffect(() => {
|
|
296
|
+
if (props.planModeRef) {
|
|
297
|
+
props.planModeRef.current = planMode;
|
|
298
|
+
}
|
|
299
|
+
}, [planMode, props.planModeRef]);
|
|
300
|
+
// Rebuild system prompt when plan mode changes
|
|
301
|
+
useEffect(() => {
|
|
302
|
+
void (async () => {
|
|
303
|
+
const newPrompt = await buildSystemPrompt(props.cwd, props.skills, planMode, approvedPlanPathRef.current);
|
|
304
|
+
if (messagesRef.current[0]?.role === "system") {
|
|
305
|
+
messagesRef.current[0] = {
|
|
306
|
+
role: "system",
|
|
307
|
+
content: newPrompt,
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
})();
|
|
311
|
+
}, [planMode, props.cwd, props.skills]);
|
|
312
|
+
// Wire onEnterPlan callback ref
|
|
313
|
+
useEffect(() => {
|
|
314
|
+
if (props.onEnterPlanRef) {
|
|
315
|
+
props.onEnterPlanRef.current = (reason) => {
|
|
316
|
+
setPlanMode(true);
|
|
317
|
+
const msg = reason ? `Plan mode activated: ${reason}` : "Plan mode activated";
|
|
318
|
+
setLiveItems((prev) => [...prev, { kind: "info", text: msg, id: getId() }]);
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
}, [props.onEnterPlanRef]);
|
|
322
|
+
// Wire onExitPlan callback ref
|
|
323
|
+
useEffect(() => {
|
|
324
|
+
if (props.onExitPlanRef) {
|
|
325
|
+
props.onExitPlanRef.current = async (planPath) => {
|
|
326
|
+
// Deactivate plan mode, store approved plan path, open pane
|
|
327
|
+
setPlanMode(false);
|
|
328
|
+
approvedPlanPathRef.current = planPath;
|
|
329
|
+
// Use setTimeout to open pane after the current tool execution completes,
|
|
330
|
+
// so the turn can finish and the UI transitions cleanly
|
|
331
|
+
setTimeout(() => {
|
|
332
|
+
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
333
|
+
setPlanAutoExpand(true);
|
|
334
|
+
setOverlay("plan");
|
|
335
|
+
}, 300);
|
|
336
|
+
return ("Plan submitted. Exiting plan mode.\n" +
|
|
337
|
+
"The plan pane is opening for user review.\n" +
|
|
338
|
+
"Plan saved at: " +
|
|
339
|
+
planPath);
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
}, [props.onExitPlanRef, stdout]);
|
|
258
343
|
const persistNewMessages = useCallback(async () => {
|
|
259
344
|
const sm = sessionManagerRef.current;
|
|
260
345
|
const sp = sessionPathRef.current;
|
|
@@ -311,6 +396,7 @@ export function App(props) {
|
|
|
311
396
|
apiKey: compactApiKey,
|
|
312
397
|
contextWindow,
|
|
313
398
|
signal: undefined,
|
|
399
|
+
approvedPlanPath: approvedPlanPathRef.current,
|
|
314
400
|
});
|
|
315
401
|
// Replace spinner with completed notice
|
|
316
402
|
setLiveItems((prev) => prev.map((item) => item.id === spinId
|
|
@@ -440,7 +526,7 @@ export function App(props) {
|
|
|
440
526
|
setLiveItems((prev) => {
|
|
441
527
|
const flushed = flushOnTurnText(prev);
|
|
442
528
|
if (flushed.length > 0) {
|
|
443
|
-
setHistory((h) =>
|
|
529
|
+
setHistory((h) => compactHistory([...h, ...trimFlushedItems(flushed)]));
|
|
444
530
|
}
|
|
445
531
|
return [{ kind: "assistant", text, thinking, thinkingMs, id: getId() }];
|
|
446
532
|
});
|
|
@@ -471,6 +557,31 @@ export function App(props) {
|
|
|
471
557
|
return [...prev, { kind: "subagent_group", agents: [newAgent], id: getId() }];
|
|
472
558
|
});
|
|
473
559
|
}
|
|
560
|
+
else if (AGGREGATABLE_TOOLS.has(name)) {
|
|
561
|
+
// Group concurrent read-only tools into a single compact item
|
|
562
|
+
setLiveItems((prev) => {
|
|
563
|
+
// Find an active tool group (has at least one running tool)
|
|
564
|
+
const groupIdx = prev.findIndex((item) => item.kind === "tool_group" &&
|
|
565
|
+
item.tools.some((t) => t.status === "running"));
|
|
566
|
+
if (groupIdx !== -1) {
|
|
567
|
+
const group = prev[groupIdx];
|
|
568
|
+
const next = [...prev];
|
|
569
|
+
next[groupIdx] = {
|
|
570
|
+
...group,
|
|
571
|
+
tools: [...group.tools, { toolCallId, name, args, status: "running" }],
|
|
572
|
+
};
|
|
573
|
+
return next;
|
|
574
|
+
}
|
|
575
|
+
return [
|
|
576
|
+
...prev,
|
|
577
|
+
{
|
|
578
|
+
kind: "tool_group",
|
|
579
|
+
tools: [{ toolCallId, name, args, status: "running" }],
|
|
580
|
+
id: getId(),
|
|
581
|
+
},
|
|
582
|
+
];
|
|
583
|
+
});
|
|
584
|
+
}
|
|
474
585
|
else {
|
|
475
586
|
setLiveItems((prev) => [
|
|
476
587
|
...prev,
|
|
@@ -533,6 +644,20 @@ export function App(props) {
|
|
|
533
644
|
}
|
|
534
645
|
else {
|
|
535
646
|
setLiveItems((prev) => {
|
|
647
|
+
// Check if this tool is in a tool_group
|
|
648
|
+
const groupIdx = prev.findIndex((item) => item.kind === "tool_group" &&
|
|
649
|
+
item.tools.some((t) => t.toolCallId === toolCallId));
|
|
650
|
+
if (groupIdx !== -1) {
|
|
651
|
+
const group = prev[groupIdx];
|
|
652
|
+
const next = [...prev];
|
|
653
|
+
next[groupIdx] = {
|
|
654
|
+
...group,
|
|
655
|
+
tools: group.tools.map((t) => t.toolCallId === toolCallId
|
|
656
|
+
? { ...t, status: "done", result, isError }
|
|
657
|
+
: t),
|
|
658
|
+
};
|
|
659
|
+
return next;
|
|
660
|
+
}
|
|
536
661
|
// Find the matching tool_start and replace it with tool_done
|
|
537
662
|
const startIdx = prev.findIndex((item) => item.kind === "tool_start" && item.toolCallId === toolCallId);
|
|
538
663
|
if (startIdx !== -1) {
|
|
@@ -618,7 +743,7 @@ export function App(props) {
|
|
|
618
743
|
setLiveItems((prev) => {
|
|
619
744
|
const { flushed, remaining } = flushOnTurnEnd(prev, stopReason);
|
|
620
745
|
if (flushed.length > 0) {
|
|
621
|
-
setHistory((h) =>
|
|
746
|
+
setHistory((h) => compactHistory([...h, ...trimFlushedItems(flushed)]));
|
|
622
747
|
}
|
|
623
748
|
return remaining;
|
|
624
749
|
});
|
|
@@ -662,11 +787,77 @@ export function App(props) {
|
|
|
662
787
|
onAborted: useCallback(() => {
|
|
663
788
|
log("WARN", "agent", "Agent run aborted by user");
|
|
664
789
|
setRunAllTasks(false);
|
|
790
|
+
setDoneStatus(null);
|
|
665
791
|
setLiveItems((prev) => {
|
|
666
|
-
const next = prev.map((item) =>
|
|
792
|
+
const next = prev.map((item) => {
|
|
793
|
+
if (item.kind === "subagent_group")
|
|
794
|
+
return { ...item, aborted: true };
|
|
795
|
+
// Convert running tools to stopped state so spinners stop
|
|
796
|
+
if (item.kind === "tool_start") {
|
|
797
|
+
return {
|
|
798
|
+
kind: "tool_done",
|
|
799
|
+
name: item.name,
|
|
800
|
+
args: item.args,
|
|
801
|
+
result: "Stopped.",
|
|
802
|
+
isError: true,
|
|
803
|
+
durationMs: 0,
|
|
804
|
+
id: item.id,
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
if (item.kind === "server_tool_start") {
|
|
808
|
+
return {
|
|
809
|
+
kind: "server_tool_done",
|
|
810
|
+
name: item.name,
|
|
811
|
+
input: item.input,
|
|
812
|
+
resultType: "aborted",
|
|
813
|
+
data: null,
|
|
814
|
+
durationMs: 0,
|
|
815
|
+
id: item.id,
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
if (item.kind === "tool_group") {
|
|
819
|
+
const tools = item.tools.map((t) => t.status === "running"
|
|
820
|
+
? { ...t, status: "done", result: "Stopped.", isError: true }
|
|
821
|
+
: t);
|
|
822
|
+
return { ...item, tools };
|
|
823
|
+
}
|
|
824
|
+
// Remove compaction spinner (compaction can't complete after abort)
|
|
825
|
+
if (item.kind === "compacting") {
|
|
826
|
+
return { kind: "tombstone", id: item.id };
|
|
827
|
+
}
|
|
828
|
+
return item;
|
|
829
|
+
});
|
|
667
830
|
return [...next, { kind: "info", text: "Request was stopped.", id: getId() }];
|
|
668
831
|
});
|
|
669
832
|
}, []),
|
|
833
|
+
onQueuedStart: useCallback((content) => {
|
|
834
|
+
// When a queued message starts processing, show it as a UserItem
|
|
835
|
+
// and flush prior items to history
|
|
836
|
+
const displayText = typeof content === "string"
|
|
837
|
+
? content
|
|
838
|
+
: content
|
|
839
|
+
.filter((c) => c.type === "text")
|
|
840
|
+
.map((c) => c.text)
|
|
841
|
+
.join("\n");
|
|
842
|
+
const imageCount = typeof content === "string"
|
|
843
|
+
? undefined
|
|
844
|
+
: content.filter((c) => c.type === "image").length || undefined;
|
|
845
|
+
setLiveItems((prev) => {
|
|
846
|
+
if (prev.length > 0) {
|
|
847
|
+
setHistory((h) => compactHistory([...h, ...trimFlushedItems(prev)]));
|
|
848
|
+
}
|
|
849
|
+
return [];
|
|
850
|
+
});
|
|
851
|
+
const userItem = {
|
|
852
|
+
kind: "user",
|
|
853
|
+
text: displayText,
|
|
854
|
+
imageCount,
|
|
855
|
+
id: getId(),
|
|
856
|
+
};
|
|
857
|
+
setLastUserMessage(displayText);
|
|
858
|
+
setDoneStatus(null);
|
|
859
|
+
setLiveItems([userItem]);
|
|
860
|
+
}, []),
|
|
670
861
|
});
|
|
671
862
|
// Phase 2 of the two-phase flush: after onDone clears liveItems (phase 1)
|
|
672
863
|
// and Ink renders the smaller live area (updating its internal line
|
|
@@ -679,7 +870,7 @@ export function App(props) {
|
|
|
679
870
|
if (pendingFlushRef.current.length > 0) {
|
|
680
871
|
const items = pendingFlushRef.current;
|
|
681
872
|
pendingFlushRef.current = [];
|
|
682
|
-
setHistory((h) =>
|
|
873
|
+
setHistory((h) => compactHistory([...h, ...trimFlushedItems(items)]));
|
|
683
874
|
}
|
|
684
875
|
});
|
|
685
876
|
// Sync terminal title with agent loop state
|
|
@@ -687,25 +878,11 @@ export function App(props) {
|
|
|
687
878
|
setTitlePhase(agentLoop.activityPhase);
|
|
688
879
|
setTitleRunning(agentLoop.isRunning);
|
|
689
880
|
}, [agentLoop.activityPhase, agentLoop.isRunning]);
|
|
690
|
-
// Animated thinking border
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
const timer = setInterval(() => {
|
|
696
|
-
setThinkingBorderFrame((f) => (f + 1) % THINKING_BORDER_COLORS.length);
|
|
697
|
-
}, 1000);
|
|
698
|
-
return () => clearInterval(timer);
|
|
699
|
-
}, [agentLoop.activityPhase]);
|
|
700
|
-
// Success flash on turn completion
|
|
701
|
-
const [doneFlash, setDoneFlash] = useState(false);
|
|
702
|
-
useEffect(() => {
|
|
703
|
-
if (doneStatus) {
|
|
704
|
-
setDoneFlash(true);
|
|
705
|
-
const timer = setTimeout(() => setDoneFlash(false), 600);
|
|
706
|
-
return () => clearTimeout(timer);
|
|
707
|
-
}
|
|
708
|
-
}, [doneStatus]);
|
|
881
|
+
// Animated thinking border — derived from global animation tick
|
|
882
|
+
const animTick = useAnimationTick();
|
|
883
|
+
const thinkingBorderFrame = agentLoop.activityPhase === "thinking"
|
|
884
|
+
? deriveFrame(animTick, 1000, THINKING_BORDER_COLORS.length)
|
|
885
|
+
: 0;
|
|
709
886
|
const handleSubmit = useCallback(async (input, inputImages = [], pasteInfo) => {
|
|
710
887
|
const trimmed = input.trim();
|
|
711
888
|
if (trimmed.startsWith("/")) {
|
|
@@ -746,6 +923,30 @@ export function App(props) {
|
|
|
746
923
|
setLiveItems([{ kind: "info", text: "Session cleared.", id: getId() }]);
|
|
747
924
|
return;
|
|
748
925
|
}
|
|
926
|
+
// Handle /plan — toggle plan mode
|
|
927
|
+
if (trimmed === "/plan" || trimmed === "/plan on") {
|
|
928
|
+
setPlanMode(true);
|
|
929
|
+
setLiveItems((prev) => [
|
|
930
|
+
...prev,
|
|
931
|
+
{ kind: "info", text: "Plan mode activated", id: getId() },
|
|
932
|
+
]);
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
if (trimmed === "/plan off") {
|
|
936
|
+
setPlanMode(false);
|
|
937
|
+
setLiveItems((prev) => [
|
|
938
|
+
...prev,
|
|
939
|
+
{ kind: "info", text: "Plan mode deactivated", id: getId() },
|
|
940
|
+
]);
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
// Handle /plans — open plan pane
|
|
944
|
+
if (trimmed === "/plans") {
|
|
945
|
+
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
946
|
+
setPlanAutoExpand(false);
|
|
947
|
+
setOverlay("plan");
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
749
950
|
// Handle prompt-template commands (built-in + custom from .ezcoder/commands/)
|
|
750
951
|
if (trimmed.startsWith("/")) {
|
|
751
952
|
const parts = trimmed.slice(1).split(" ");
|
|
@@ -759,7 +960,7 @@ export function App(props) {
|
|
|
759
960
|
// Move live items into history before starting
|
|
760
961
|
setLiveItems((prev) => {
|
|
761
962
|
if (prev.length > 0) {
|
|
762
|
-
setHistory((h) =>
|
|
963
|
+
setHistory((h) => compactHistory([...h, ...trimFlushedItems(prev)]));
|
|
763
964
|
}
|
|
764
965
|
return [];
|
|
765
966
|
});
|
|
@@ -799,31 +1000,8 @@ export function App(props) {
|
|
|
799
1000
|
return;
|
|
800
1001
|
}
|
|
801
1002
|
}
|
|
802
|
-
//
|
|
803
|
-
setLiveItems((prev) => {
|
|
804
|
-
if (prev.length > 0) {
|
|
805
|
-
setHistory((h) => pruneHistory([...h, ...prev]));
|
|
806
|
-
}
|
|
807
|
-
return [];
|
|
808
|
-
});
|
|
809
|
-
// Build display text — strip image paths, show badges instead
|
|
1003
|
+
// ── Build user content (shared by normal + queued paths) ──
|
|
810
1004
|
const hasImages = inputImages.length > 0;
|
|
811
|
-
let displayText = input;
|
|
812
|
-
if (hasImages) {
|
|
813
|
-
const { cleanText } = await extractImagePaths(input, props.cwd);
|
|
814
|
-
displayText = cleanText;
|
|
815
|
-
}
|
|
816
|
-
const userItem = {
|
|
817
|
-
kind: "user",
|
|
818
|
-
text: displayText,
|
|
819
|
-
imageCount: hasImages ? inputImages.length : undefined,
|
|
820
|
-
pasteInfo,
|
|
821
|
-
id: getId(),
|
|
822
|
-
};
|
|
823
|
-
setLastUserMessage(input);
|
|
824
|
-
setDoneStatus(null);
|
|
825
|
-
setLiveItems([userItem]);
|
|
826
|
-
// Build user content — plain string or content array with images
|
|
827
1005
|
const modelInfo = getModel(currentModel);
|
|
828
1006
|
const modelSupportsImages = modelInfo?.supportsImages ?? true;
|
|
829
1007
|
let userContent;
|
|
@@ -867,6 +1045,47 @@ export function App(props) {
|
|
|
867
1045
|
else {
|
|
868
1046
|
userContent = input;
|
|
869
1047
|
}
|
|
1048
|
+
// ── Queue message if agent is already running ──
|
|
1049
|
+
if (agentLoop.isRunning) {
|
|
1050
|
+
log("INFO", "queue", `Queued message: ${trimmed.length > 80 ? trimmed.slice(0, 80) + "..." : trimmed}`);
|
|
1051
|
+
agentLoop.queueMessage(userContent);
|
|
1052
|
+
let displayText = input;
|
|
1053
|
+
if (hasImages) {
|
|
1054
|
+
const { cleanText } = await extractImagePaths(input, props.cwd);
|
|
1055
|
+
displayText = cleanText;
|
|
1056
|
+
}
|
|
1057
|
+
const queuedItem = {
|
|
1058
|
+
kind: "queued",
|
|
1059
|
+
text: displayText,
|
|
1060
|
+
imageCount: hasImages ? inputImages.length : undefined,
|
|
1061
|
+
id: getId(),
|
|
1062
|
+
};
|
|
1063
|
+
setLiveItems((prev) => [...prev, queuedItem]);
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
// Move any remaining live items into history (Static) before starting new turn
|
|
1067
|
+
setLiveItems((prev) => {
|
|
1068
|
+
if (prev.length > 0) {
|
|
1069
|
+
setHistory((h) => compactHistory([...h, ...trimFlushedItems(prev)]));
|
|
1070
|
+
}
|
|
1071
|
+
return [];
|
|
1072
|
+
});
|
|
1073
|
+
// Build display text — strip image paths, show badges instead
|
|
1074
|
+
let displayText = input;
|
|
1075
|
+
if (hasImages) {
|
|
1076
|
+
const { cleanText } = await extractImagePaths(input, props.cwd);
|
|
1077
|
+
displayText = cleanText;
|
|
1078
|
+
}
|
|
1079
|
+
const userItem = {
|
|
1080
|
+
kind: "user",
|
|
1081
|
+
text: displayText,
|
|
1082
|
+
imageCount: hasImages ? inputImages.length : undefined,
|
|
1083
|
+
pasteInfo,
|
|
1084
|
+
id: getId(),
|
|
1085
|
+
};
|
|
1086
|
+
setLastUserMessage(input);
|
|
1087
|
+
setDoneStatus(null);
|
|
1088
|
+
setLiveItems([userItem]);
|
|
870
1089
|
// Run agent
|
|
871
1090
|
try {
|
|
872
1091
|
await agentLoop.run(userContent);
|
|
@@ -885,6 +1104,7 @@ export function App(props) {
|
|
|
885
1104
|
}, [agentLoop, props.onSlashCommand, compactConversation]);
|
|
886
1105
|
const handleAbort = useCallback(() => {
|
|
887
1106
|
if (agentLoop.isRunning) {
|
|
1107
|
+
agentLoop.clearQueue();
|
|
888
1108
|
agentLoop.abort();
|
|
889
1109
|
}
|
|
890
1110
|
else {
|
|
@@ -935,15 +1155,10 @@ export function App(props) {
|
|
|
935
1155
|
apiKey = props.credentialsByProvider?.["glm"]?.accessToken;
|
|
936
1156
|
}
|
|
937
1157
|
try {
|
|
938
|
-
const
|
|
939
|
-
const eagerConfigs = allConfigs.filter((c) => !c.lazy);
|
|
940
|
-
const lazyConfigs = allConfigs.filter((c) => c.lazy);
|
|
941
|
-
const mcpTools = await props.mcpManager.connectAll(eagerConfigs);
|
|
942
|
-
const lazyTools = props.mcpManager.registerLazy(lazyConfigs);
|
|
1158
|
+
const mcpTools = await props.mcpManager.connectAll(getMCPServers(newProvider, apiKey));
|
|
943
1159
|
setCurrentTools((prev) => [
|
|
944
1160
|
...prev.filter((t) => !t.name.startsWith("mcp__")),
|
|
945
1161
|
...mcpTools,
|
|
946
|
-
...lazyTools,
|
|
947
1162
|
]);
|
|
948
1163
|
log("INFO", "mcp", `MCP servers reconnected for provider ${newProvider}`);
|
|
949
1164
|
}
|
|
@@ -978,6 +1193,8 @@ export function App(props) {
|
|
|
978
1193
|
{ name: "compact", aliases: ["c"], description: "Compact conversation" },
|
|
979
1194
|
{ name: "clear", aliases: [], description: "Clear session and terminal" },
|
|
980
1195
|
{ name: "quit", aliases: ["q", "exit"], description: "Exit the agent" },
|
|
1196
|
+
{ name: "plan", aliases: [], description: "Toggle plan mode (on/off)" },
|
|
1197
|
+
{ name: "plans", aliases: [], description: "Open plans pane" },
|
|
981
1198
|
...PROMPT_COMMANDS.map((cmd) => ({
|
|
982
1199
|
name: cmd.name,
|
|
983
1200
|
aliases: cmd.aliases,
|
|
@@ -991,6 +1208,8 @@ export function App(props) {
|
|
|
991
1208
|
], [customCommands]);
|
|
992
1209
|
const renderItem = (item) => {
|
|
993
1210
|
switch (item.kind) {
|
|
1211
|
+
case "tombstone":
|
|
1212
|
+
return null;
|
|
994
1213
|
case "banner":
|
|
995
1214
|
return (_jsx(Banner, { version: props.version, model: props.model, provider: props.provider, cwd: props.cwd, taskCount: taskCount }, item.id));
|
|
996
1215
|
case "user":
|
|
@@ -1003,16 +1222,22 @@ export function App(props) {
|
|
|
1003
1222
|
return _jsx(ToolExecution, { status: "running", name: item.name, args: item.args }, item.id);
|
|
1004
1223
|
case "tool_done":
|
|
1005
1224
|
return (_jsx(ToolExecution, { status: "done", name: item.name, args: item.args, result: item.result, isError: item.isError }, item.id));
|
|
1225
|
+
case "tool_group":
|
|
1226
|
+
return _jsx(ToolGroupExecution, { tools: item.tools }, item.id);
|
|
1006
1227
|
case "server_tool_start":
|
|
1007
1228
|
return (_jsx(ServerToolExecution, { status: "running", name: item.name, input: item.input, startedAt: item.startedAt }, item.id));
|
|
1008
1229
|
case "server_tool_done":
|
|
1009
|
-
return (_jsx(ServerToolExecution, { status: "done", name: item.name, input: item.input, durationMs: item.durationMs }, item.id));
|
|
1230
|
+
return (_jsx(ServerToolExecution, { status: "done", name: item.name, input: item.input, durationMs: item.durationMs, resultType: item.resultType }, item.id));
|
|
1010
1231
|
case "error": {
|
|
1011
1232
|
const providerHint = getProviderErrorHint(item.message);
|
|
1012
|
-
return (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { color: theme.error, children: ["✗ ", item.message] }), providerHint && (_jsxs(Text, { color: theme.textDim, children: [" Hint: ", providerHint] }))] }, item.id));
|
|
1233
|
+
return (_jsxs(Box, { marginTop: 1, flexDirection: "column", flexShrink: 1, children: [_jsxs(Text, { color: theme.error, wrap: "wrap", children: ["✗ ", item.message] }), providerHint && (_jsxs(Text, { color: theme.textDim, wrap: "wrap", children: [" Hint: ", providerHint] }))] }, item.id));
|
|
1013
1234
|
}
|
|
1014
1235
|
case "info":
|
|
1015
|
-
return (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.textDim, children: item.text }) }, item.id));
|
|
1236
|
+
return (_jsx(Box, { marginTop: 1, flexShrink: 1, children: _jsx(Text, { color: theme.textDim, wrap: "wrap", children: item.text }) }, item.id));
|
|
1237
|
+
case "queued":
|
|
1238
|
+
return (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: theme.accent, bold: true, children: "⏳ Queued: " }), _jsxs(Text, { color: theme.text, wrap: "wrap", children: [item.text, item.imageCount
|
|
1239
|
+
? ` (+${item.imageCount} image${item.imageCount > 1 ? "s" : ""})`
|
|
1240
|
+
: ""] })] }, item.id));
|
|
1016
1241
|
case "compacting":
|
|
1017
1242
|
return _jsx(CompactionSpinner, {}, item.id);
|
|
1018
1243
|
case "compacted":
|
|
@@ -1075,7 +1300,10 @@ export function App(props) {
|
|
|
1075
1300
|
runAllTasksRef.current = runAllTasks;
|
|
1076
1301
|
}, [runAllTasks]);
|
|
1077
1302
|
const isTaskView = overlay === "tasks";
|
|
1078
|
-
|
|
1303
|
+
const isSkillsView = overlay === "skills";
|
|
1304
|
+
const isPlanView = overlay === "plan";
|
|
1305
|
+
const isOverlayView = isTaskView || isSkillsView || isPlanView;
|
|
1306
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Static, { items: isOverlayView ? [] : history, style: { width: "100%" }, children: (item) => (_jsx(Box, { flexDirection: "column", paddingRight: 1, children: renderItem(item) }, item.id)) }, `${resizeKey}-${staticKey}`), isTaskView ? (_jsx(TaskOverlay, { cwd: props.cwd, agentRunning: agentLoop.isRunning, onClose: () => {
|
|
1079
1307
|
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
1080
1308
|
setTaskCount(getTaskCount(props.cwd));
|
|
1081
1309
|
setStaticKey((k) => k + 1);
|
|
@@ -1091,11 +1319,77 @@ export function App(props) {
|
|
|
1091
1319
|
markTaskInProgress(props.cwd, next.id);
|
|
1092
1320
|
startTask(next.title, next.prompt, next.id);
|
|
1093
1321
|
}
|
|
1094
|
-
} })) :
|
|
1322
|
+
} })) : isSkillsView ? (_jsx(SkillsOverlay, { cwd: props.cwd, onClose: () => {
|
|
1323
|
+
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
1324
|
+
setStaticKey((k) => k + 1);
|
|
1325
|
+
setOverlay(null);
|
|
1326
|
+
} })) : isPlanView ? (_jsx(PlanOverlay, { cwd: props.cwd, autoExpandNewest: planAutoExpand, onClose: () => {
|
|
1327
|
+
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
1328
|
+
setStaticKey((k) => k + 1);
|
|
1329
|
+
setPlanAutoExpand(false);
|
|
1330
|
+
setOverlay(null);
|
|
1331
|
+
}, onApprove: (planPath) => {
|
|
1332
|
+
// Store approved plan path — will be injected into the new system prompt
|
|
1333
|
+
approvedPlanPathRef.current = planPath;
|
|
1334
|
+
// Clear session for a fresh context focused on the plan
|
|
1335
|
+
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
1336
|
+
setHistory([{ kind: "banner", id: "banner" }]);
|
|
1337
|
+
setLiveItems([]);
|
|
1338
|
+
setStaticKey((k) => k + 1);
|
|
1339
|
+
setPlanAutoExpand(false);
|
|
1340
|
+
setOverlay(null);
|
|
1341
|
+
// Rebuild system prompt with the approved plan, then reset the session
|
|
1342
|
+
void (async () => {
|
|
1343
|
+
const newPrompt = await buildSystemPrompt(props.cwd, props.skills, false, planPath);
|
|
1344
|
+
messagesRef.current = [{ role: "system", content: newPrompt }];
|
|
1345
|
+
agentLoop.reset();
|
|
1346
|
+
persistedIndexRef.current = messagesRef.current.length;
|
|
1347
|
+
// Create a new session file
|
|
1348
|
+
const sm = sessionManagerRef.current;
|
|
1349
|
+
if (sm) {
|
|
1350
|
+
const s = await sm.create(props.cwd, currentProvider, currentModel);
|
|
1351
|
+
sessionPathRef.current = s.path;
|
|
1352
|
+
}
|
|
1353
|
+
// Start implementation with a clean context
|
|
1354
|
+
setLiveItems([
|
|
1355
|
+
{
|
|
1356
|
+
kind: "info",
|
|
1357
|
+
text: "Plan approved — starting fresh session for implementation",
|
|
1358
|
+
id: getId(),
|
|
1359
|
+
},
|
|
1360
|
+
]);
|
|
1361
|
+
setDoneStatus(null);
|
|
1362
|
+
await agentLoop.run("The plan has been approved. Implement it now, following each step in order.");
|
|
1363
|
+
})();
|
|
1364
|
+
}, onReject: (planPath, feedback) => {
|
|
1365
|
+
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
1366
|
+
setStaticKey((k) => k + 1);
|
|
1367
|
+
setPlanAutoExpand(false);
|
|
1368
|
+
setOverlay(null);
|
|
1369
|
+
// Send rejection + feedback to the agent
|
|
1370
|
+
const msg = `The plan at ${planPath} was rejected.\n\nFeedback: ${feedback}\n\n` +
|
|
1371
|
+
`Please revise the plan based on this feedback.`;
|
|
1372
|
+
setLiveItems((prev) => [
|
|
1373
|
+
...prev,
|
|
1374
|
+
{ kind: "info", text: `Plan rejected — "${feedback}"`, id: getId() },
|
|
1375
|
+
]);
|
|
1376
|
+
void agentLoop.run(msg);
|
|
1377
|
+
} })) : (_jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: "column", flexGrow: 1, paddingRight: 1, children: [liveItems.map((item) => renderItem(item)), _jsx(StreamingArea, { isRunning: agentLoop.isRunning, streamingText: agentLoop.streamingText, streamingThinking: agentLoop.streamingThinking, showThinking: props.showThinking, thinkingMs: agentLoop.thinkingMs, planMode: planMode })] }), agentLoop.isRunning && agentLoop.activityPhase !== "idle" ? (_jsx(Box, { marginTop: 1, borderStyle: "round", borderColor: agentLoop.activityPhase === "thinking"
|
|
1095
1378
|
? THINKING_BORDER_COLORS[thinkingBorderFrame]
|
|
1096
|
-
: "transparent", paddingLeft: 1, paddingRight: 1, children: _jsx(ActivityIndicator, { phase: agentLoop.activityPhase, elapsedMs: agentLoop.elapsedMs, thinkingMs: agentLoop.thinkingMs, isThinking: agentLoop.isThinking, tokenEstimate: agentLoop.streamedTokenEstimate, userMessage: lastUserMessage, activeToolNames: agentLoop.activeToolCalls.map((tc) => tc.name) }) })) : (doneStatus && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color:
|
|
1379
|
+
: "transparent", paddingLeft: 1, paddingRight: 1, children: _jsx(ActivityIndicator, { phase: agentLoop.activityPhase, elapsedMs: agentLoop.elapsedMs, thinkingMs: agentLoop.thinkingMs, isThinking: agentLoop.isThinking, tokenEstimate: agentLoop.streamedTokenEstimate, userMessage: lastUserMessage, activeToolNames: agentLoop.activeToolCalls.map((tc) => tc.name), planMode: planMode }) })) : (doneStatus && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.success, children: ["✻ ", doneStatus.verb, " ", formatDuration(doneStatus.durationMs)] }) }))), agentLoop.queuedCount > 0 && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.accent, children: ["⏳ ", agentLoop.queuedCount, " message", agentLoop.queuedCount > 1 ? "s" : "", " queued"] }) })), _jsx(InputArea, { onSubmit: handleSubmit, onAbort: handleAbort, disabled: agentLoop.isRunning, isActive: !taskBarFocused && !overlay, onDownAtEnd: handleFocusTaskBar, onShiftTab: handleToggleThinking, onToggleTasks: () => {
|
|
1097
1380
|
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
1098
1381
|
setOverlay("tasks");
|
|
1099
|
-
},
|
|
1382
|
+
}, onToggleSkills: () => {
|
|
1383
|
+
stdout?.write("\x1b[2J\x1b[3J\x1b[H");
|
|
1384
|
+
setOverlay("skills");
|
|
1385
|
+
}, onTogglePlanMode: () => {
|
|
1386
|
+
const next = !planMode;
|
|
1387
|
+
setPlanMode(next);
|
|
1388
|
+
log("INFO", "plan", `Plan mode ${next ? "enabled" : "disabled"}`);
|
|
1389
|
+
setLiveItems((items) => [
|
|
1390
|
+
...items,
|
|
1391
|
+
{ kind: "info", text: `Plan mode ${next ? "on" : "off"}`, id: getId() },
|
|
1392
|
+
]);
|
|
1393
|
+
}, cwd: props.cwd, commands: allCommands }), overlay === "model" ? (_jsx(ModelSelector, { onSelect: handleModelSelect, onCancel: () => setOverlay(null), loggedInProviders: props.loggedInProviders ?? [currentProvider], currentModel: currentModel, currentProvider: currentProvider })) : (_jsx(Footer, { model: currentModel, tokensIn: agentLoop.contextUsed, cwd: props.cwd, gitBranch: gitBranch, thinkingEnabled: thinkingEnabled, planMode: planMode })), bgTasks.length > 0 && (_jsx(BackgroundTasksBar, { tasks: bgTasks, focused: taskBarFocused, expanded: taskBarExpanded, selectedIndex: selectedTaskIndex, onExpand: handleTaskBarExpand, onCollapse: handleTaskBarCollapse, onKill: handleTaskKill, onExit: handleTaskBarExit, onNavigate: handleTaskNavigate }))] }))] }));
|
|
1100
1394
|
}
|
|
1101
1395
|
//# sourceMappingURL=App.js.map
|