@zhijiewang/openharness 2.0.0 → 2.3.0
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/README.md +4 -4
- package/dist/DeferredTool.js +3 -1
- package/dist/Tool.d.ts +1 -1
- package/dist/agents/roles.js +58 -62
- package/dist/commands/cybergotchi.d.ts +1 -1
- package/dist/commands/cybergotchi.js +30 -30
- package/dist/commands/index.js +360 -122
- package/dist/components/App.d.ts +1 -1
- package/dist/components/App.js +6 -6
- package/dist/components/CompanionFooter.d.ts +1 -1
- package/dist/components/CompanionFooter.js +6 -8
- package/dist/components/CybergotchiBubble.js +5 -5
- package/dist/components/CybergotchiPanel.d.ts +1 -1
- package/dist/components/CybergotchiPanel.js +7 -7
- package/dist/components/CybergotchiPanelConnected.js +2 -2
- package/dist/components/CybergotchiSetup.js +26 -24
- package/dist/components/CybergotchiSprite.d.ts +1 -1
- package/dist/components/CybergotchiSprite.js +8 -12
- package/dist/components/DiffView.d.ts +1 -1
- package/dist/components/DiffView.js +10 -10
- package/dist/components/ErrorBoundary.d.ts +1 -1
- package/dist/components/ErrorBoundary.js +1 -1
- package/dist/components/InitWizard.js +65 -33
- package/dist/components/Markdown.js +2 -4
- package/dist/components/Messages.js +4 -4
- package/dist/components/PermissionPrompt.d.ts +1 -1
- package/dist/components/PermissionPrompt.js +15 -17
- package/dist/components/REPL.d.ts +1 -1
- package/dist/components/REPL.js +74 -49
- package/dist/components/Spinner.js +2 -2
- package/dist/components/TextInput.js +35 -29
- package/dist/components/ToolCallDisplay.js +3 -5
- package/dist/cybergotchi/bones.d.ts +1 -1
- package/dist/cybergotchi/bones.js +8 -8
- package/dist/cybergotchi/config.d.ts +2 -2
- package/dist/cybergotchi/config.js +13 -13
- package/dist/cybergotchi/events.d.ts +5 -5
- package/dist/cybergotchi/events.js +7 -7
- package/dist/cybergotchi/needs.d.ts +2 -2
- package/dist/cybergotchi/needs.js +7 -9
- package/dist/cybergotchi/personality.d.ts +2 -2
- package/dist/cybergotchi/personality.js +2 -2
- package/dist/cybergotchi/species.d.ts +1 -1
- package/dist/cybergotchi/species.js +145 -217
- package/dist/cybergotchi/speech.d.ts +2 -2
- package/dist/cybergotchi/speech.js +43 -43
- package/dist/cybergotchi/types.d.ts +4 -4
- package/dist/cybergotchi/types.js +26 -26
- package/dist/cybergotchi/useCybergotchi.d.ts +1 -1
- package/dist/cybergotchi/useCybergotchi.js +29 -25
- package/dist/git/index.js +11 -9
- package/dist/harness/checkpoints.js +29 -21
- package/dist/harness/config.d.ts +12 -2
- package/dist/harness/config.js +15 -9
- package/dist/harness/context-warning.d.ts +1 -1
- package/dist/harness/context-warning.js +1 -1
- package/dist/harness/cost.js +1 -1
- package/dist/harness/credentials.js +13 -13
- package/dist/harness/hooks.js +7 -5
- package/dist/harness/keybindings.js +20 -18
- package/dist/harness/marketplace.d.ts +3 -3
- package/dist/harness/marketplace.js +55 -42
- package/dist/harness/memory.d.ts +23 -5
- package/dist/harness/memory.js +142 -41
- package/dist/harness/onboarding.js +30 -10
- package/dist/harness/plugins.d.ts +9 -1
- package/dist/harness/plugins.js +54 -30
- package/dist/harness/rules.js +12 -7
- package/dist/harness/sandbox.d.ts +34 -0
- package/dist/harness/sandbox.js +104 -0
- package/dist/harness/session-db.d.ts +55 -0
- package/dist/harness/session-db.js +165 -0
- package/dist/harness/session.d.ts +1 -1
- package/dist/harness/session.js +34 -15
- package/dist/harness/store.d.ts +3 -3
- package/dist/harness/store.js +6 -4
- package/dist/harness/submit-handler.d.ts +4 -4
- package/dist/harness/submit-handler.js +57 -21
- package/dist/harness/telemetry.d.ts +1 -1
- package/dist/harness/telemetry.js +23 -19
- package/dist/harness/traces.d.ts +2 -2
- package/dist/harness/traces.js +44 -33
- package/dist/harness/verification.d.ts +1 -1
- package/dist/harness/verification.js +50 -44
- package/dist/lsp/client.js +44 -40
- package/dist/main.js +100 -59
- package/dist/mcp/DeferredMcpTool.d.ts +4 -4
- package/dist/mcp/DeferredMcpTool.js +9 -5
- package/dist/mcp/McpTool.d.ts +4 -4
- package/dist/mcp/McpTool.js +8 -4
- package/dist/mcp/client.d.ts +2 -2
- package/dist/mcp/client.js +21 -21
- package/dist/mcp/loader.d.ts +1 -1
- package/dist/mcp/loader.js +17 -12
- package/dist/mcp/registry.d.ts +3 -3
- package/dist/mcp/registry.js +97 -97
- package/dist/mcp/schema.d.ts +1 -1
- package/dist/mcp/schema.js +16 -16
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +21 -21
- package/dist/mcp/types.d.ts +3 -3
- package/dist/providers/anthropic.d.ts +2 -2
- package/dist/providers/anthropic.js +10 -9
- package/dist/providers/base.d.ts +1 -1
- package/dist/providers/index.js +10 -3
- package/dist/providers/llamacpp.d.ts +2 -2
- package/dist/providers/llamacpp.js +1 -3
- package/dist/providers/ollama.d.ts +2 -2
- package/dist/providers/ollama.js +3 -4
- package/dist/providers/openai.d.ts +2 -2
- package/dist/providers/openai.js +3 -5
- package/dist/providers/openrouter.d.ts +2 -2
- package/dist/providers/router.d.ts +1 -1
- package/dist/providers/router.js +7 -7
- package/dist/query/compress.d.ts +2 -2
- package/dist/query/compress.js +22 -21
- package/dist/query/context-manager.d.ts +2 -2
- package/dist/query/context-manager.js +8 -11
- package/dist/query/errors.js +1 -1
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +30 -22
- package/dist/query/tools.js +15 -12
- package/dist/query/types.d.ts +1 -1
- package/dist/query.d.ts +1 -1
- package/dist/query.js +1 -1
- package/dist/remote/auth.d.ts +2 -2
- package/dist/remote/auth.js +8 -8
- package/dist/remote/server.d.ts +3 -3
- package/dist/remote/server.js +60 -60
- package/dist/renderer/cells.js +9 -9
- package/dist/renderer/colors.js +24 -6
- package/dist/renderer/diff.d.ts +2 -2
- package/dist/renderer/diff.js +27 -19
- package/dist/renderer/differ.d.ts +1 -1
- package/dist/renderer/differ.js +9 -9
- package/dist/renderer/image.js +19 -19
- package/dist/renderer/index.d.ts +6 -6
- package/dist/renderer/index.js +163 -93
- package/dist/renderer/input.js +66 -48
- package/dist/renderer/layout.d.ts +6 -6
- package/dist/renderer/layout.js +163 -124
- package/dist/renderer/markdown.d.ts +2 -2
- package/dist/renderer/markdown.js +173 -54
- package/dist/renderer/session-browser.d.ts +2 -2
- package/dist/renderer/session-browser.js +19 -21
- package/dist/repl.d.ts +5 -5
- package/dist/repl.js +300 -198
- package/dist/sdk/index.d.ts +8 -7
- package/dist/sdk/index.js +59 -42
- package/dist/services/AgentDispatcher.d.ts +3 -3
- package/dist/services/AgentDispatcher.js +33 -29
- package/dist/services/CronExecutor.d.ts +4 -4
- package/dist/services/CronExecutor.js +12 -8
- package/dist/services/EvaluatorLoop.d.ts +3 -3
- package/dist/services/EvaluatorLoop.js +29 -21
- package/dist/services/MetaHarness.d.ts +1 -1
- package/dist/services/MetaHarness.js +41 -33
- package/dist/services/PipelineExecutor.d.ts +1 -1
- package/dist/services/PipelineExecutor.js +23 -25
- package/dist/services/SkillExtractor.d.ts +43 -0
- package/dist/services/SkillExtractor.js +143 -0
- package/dist/services/StreamingToolExecutor.d.ts +2 -2
- package/dist/services/StreamingToolExecutor.js +11 -7
- package/dist/services/a2a.d.ts +8 -8
- package/dist/services/a2a.js +44 -34
- package/dist/services/agent-messaging.d.ts +33 -15
- package/dist/services/agent-messaging.js +65 -13
- package/dist/services/cron.js +16 -16
- package/dist/tools/AgentTool/index.d.ts +5 -2
- package/dist/tools/AgentTool/index.js +35 -15
- package/dist/tools/AskUserTool/index.js +1 -1
- package/dist/tools/BashTool/index.d.ts +2 -2
- package/dist/tools/BashTool/index.js +18 -10
- package/dist/tools/CronTool/index.d.ts +2 -2
- package/dist/tools/CronTool/index.js +30 -12
- package/dist/tools/DiagnosticsTool/index.js +28 -22
- package/dist/tools/EnterPlanModeTool/index.js +93 -14
- package/dist/tools/EnterWorktreeTool/index.js +7 -3
- package/dist/tools/ExitPlanModeTool/index.d.ts +22 -1
- package/dist/tools/ExitPlanModeTool/index.js +20 -5
- package/dist/tools/ExitWorktreeTool/index.js +11 -4
- package/dist/tools/FileEditTool/index.js +3 -5
- package/dist/tools/FileReadTool/index.js +16 -10
- package/dist/tools/FileWriteTool/index.js +2 -2
- package/dist/tools/GlobTool/index.js +5 -9
- package/dist/tools/GrepTool/index.d.ts +2 -2
- package/dist/tools/GrepTool/index.js +14 -9
- package/dist/tools/ImageReadTool/index.js +2 -2
- package/dist/tools/KillProcessTool/index.js +11 -7
- package/dist/tools/LSTool/index.js +3 -3
- package/dist/tools/MemoryTool/index.d.ts +11 -11
- package/dist/tools/MemoryTool/index.js +28 -14
- package/dist/tools/MonitorTool/index.d.ts +2 -2
- package/dist/tools/MonitorTool/index.js +24 -19
- package/dist/tools/MultiEditTool/index.js +9 -5
- package/dist/tools/NotebookEditTool/index.js +3 -3
- package/dist/tools/ParallelAgentTool/index.d.ts +4 -4
- package/dist/tools/ParallelAgentTool/index.js +12 -6
- package/dist/tools/PipelineTool/index.d.ts +4 -4
- package/dist/tools/PipelineTool/index.js +3 -3
- package/dist/tools/PowerShellTool/index.js +10 -6
- package/dist/tools/RemoteTriggerTool/index.js +8 -4
- package/dist/tools/ScheduleWakeupTool/index.d.ts +42 -0
- package/dist/tools/ScheduleWakeupTool/index.js +115 -0
- package/dist/tools/SendMessageTool/index.js +25 -7
- package/dist/tools/SessionSearchTool/index.d.ts +15 -0
- package/dist/tools/SessionSearchTool/index.js +36 -0
- package/dist/tools/SkillTool/index.d.ts +3 -0
- package/dist/tools/SkillTool/index.js +39 -9
- package/dist/tools/TaskCreateTool/index.d.ts +2 -2
- package/dist/tools/TaskCreateTool/index.js +2 -2
- package/dist/tools/TaskGetTool/index.js +2 -2
- package/dist/tools/TaskListTool/index.js +3 -5
- package/dist/tools/TaskOutputTool/index.js +2 -2
- package/dist/tools/TaskStopTool/index.js +3 -3
- package/dist/tools/TaskUpdateTool/index.d.ts +4 -4
- package/dist/tools/TaskUpdateTool/index.js +2 -2
- package/dist/tools/ToolSearchTool/index.js +9 -6
- package/dist/tools/WebFetchTool/index.js +1 -1
- package/dist/tools/WebSearchTool/index.js +2 -6
- package/dist/tools.js +31 -30
- package/dist/types/permissions.js +15 -9
- package/dist/utils/bash-safety.d.ts +1 -1
- package/dist/utils/bash-safety.js +64 -54
- package/dist/utils/diff-algorithm.d.ts +3 -3
- package/dist/utils/diff-algorithm.js +7 -7
- package/dist/utils/fs.js +3 -3
- package/dist/utils/safe-env.js +1 -1
- package/dist/utils/theme-data.d.ts +1 -1
- package/dist/utils/theme-data.js +1 -1
- package/dist/utils/theme.d.ts +1 -1
- package/dist/utils/theme.js +1 -1
- package/dist/utils/tool-summary.d.ts +1 -1
- package/dist/utils/tool-summary.js +27 -9
- package/package.json +10 -3
package/dist/renderer/index.js
CHANGED
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
* Flushed messages flow to scrollback; live area is rewritten in-place
|
|
4
4
|
* right after the scrollback content each frame (no absolute positioning gap).
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
6
|
+
import { getTheme } from "../utils/theme-data.js";
|
|
7
|
+
import { summarizeToolArgs } from "../utils/tool-summary.js";
|
|
8
|
+
import { CellGrid } from "./cells.js";
|
|
9
|
+
import { FG } from "./colors.js";
|
|
10
|
+
import { extractDiffInfo } from "./diff.js";
|
|
11
|
+
import { hideCursor, showCursor, styleToSGR, syncWrite } from "./differ.js";
|
|
12
|
+
import { startRawInput } from "./input.js";
|
|
13
|
+
import { rasterizeLive } from "./layout.js";
|
|
14
|
+
import { browserDown, browserLoadPreview, browserSearch, browserSelectedId, browserUp, createSessionBrowser, } from "./session-browser.js";
|
|
15
15
|
export class TerminalRenderer {
|
|
16
16
|
current;
|
|
17
17
|
state;
|
|
@@ -39,15 +39,15 @@ export class TerminalRenderer {
|
|
|
39
39
|
this.current = new CellGrid(w, h);
|
|
40
40
|
this.state = {
|
|
41
41
|
messages: [],
|
|
42
|
-
streamingText:
|
|
43
|
-
thinkingText:
|
|
42
|
+
streamingText: "",
|
|
43
|
+
thinkingText: "",
|
|
44
44
|
toolCalls: new Map(),
|
|
45
|
-
inputText:
|
|
45
|
+
inputText: "",
|
|
46
46
|
inputCursor: 0,
|
|
47
47
|
companionLines: null,
|
|
48
|
-
companionColor:
|
|
49
|
-
statusHints:
|
|
50
|
-
statusLine:
|
|
48
|
+
companionColor: "cyan",
|
|
49
|
+
statusHints: "exit to quit",
|
|
50
|
+
statusLine: "",
|
|
51
51
|
contextWarning: null,
|
|
52
52
|
errorText: null,
|
|
53
53
|
loading: false,
|
|
@@ -76,7 +76,7 @@ export class TerminalRenderer {
|
|
|
76
76
|
start() {
|
|
77
77
|
this.started = true;
|
|
78
78
|
// Enable SGR mouse tracking (scroll wheel support)
|
|
79
|
-
process.stdout.write(
|
|
79
|
+
process.stdout.write("\x1b[?1000h\x1b[?1006h");
|
|
80
80
|
hideCursor();
|
|
81
81
|
// Raw input
|
|
82
82
|
this.stopInput = startRawInput((key) => {
|
|
@@ -97,7 +97,7 @@ export class TerminalRenderer {
|
|
|
97
97
|
}, 500);
|
|
98
98
|
// Terminal resize
|
|
99
99
|
this.resizeHandler = () => this.handleResize();
|
|
100
|
-
process.stdout.on(
|
|
100
|
+
process.stdout.on("resize", this.resizeHandler);
|
|
101
101
|
this.render();
|
|
102
102
|
}
|
|
103
103
|
stop() {
|
|
@@ -113,7 +113,7 @@ export class TerminalRenderer {
|
|
|
113
113
|
this.renderTimer = null;
|
|
114
114
|
}
|
|
115
115
|
if (this.resizeHandler) {
|
|
116
|
-
process.stdout.off(
|
|
116
|
+
process.stdout.off("resize", this.resizeHandler);
|
|
117
117
|
this.resizeHandler = null;
|
|
118
118
|
}
|
|
119
119
|
if (this.stopInput) {
|
|
@@ -124,10 +124,10 @@ export class TerminalRenderer {
|
|
|
124
124
|
clearTimeout(t);
|
|
125
125
|
this.notifyTimers.length = 0;
|
|
126
126
|
// Restore terminal: disable mouse, show cursor, reset attributes
|
|
127
|
-
process.stdout.write(
|
|
127
|
+
process.stdout.write("\x1b[?1006l\x1b[?1000l\x1b[0m");
|
|
128
128
|
showCursor();
|
|
129
129
|
// Move past live area for clean shell prompt
|
|
130
|
-
process.stdout.write(
|
|
130
|
+
process.stdout.write("\n");
|
|
131
131
|
}
|
|
132
132
|
// ── State updates ──
|
|
133
133
|
setMessages(msgs) {
|
|
@@ -137,9 +137,18 @@ export class TerminalRenderer {
|
|
|
137
137
|
this.state.messages = msgs;
|
|
138
138
|
this.scheduleRender();
|
|
139
139
|
}
|
|
140
|
-
setStreamingText(text) {
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
setStreamingText(text) {
|
|
141
|
+
this.state.streamingText = text;
|
|
142
|
+
this.scheduleRender();
|
|
143
|
+
}
|
|
144
|
+
setThinkingText(text) {
|
|
145
|
+
this.state.thinkingText = text;
|
|
146
|
+
this.scheduleRender();
|
|
147
|
+
}
|
|
148
|
+
setError(text) {
|
|
149
|
+
this.state.errorText = text;
|
|
150
|
+
this.scheduleRender();
|
|
151
|
+
}
|
|
143
152
|
/** Show a toast notification above the input for 5 seconds */
|
|
144
153
|
notify(text) {
|
|
145
154
|
const entry = { text };
|
|
@@ -161,30 +170,67 @@ export class TerminalRenderer {
|
|
|
161
170
|
this.state.manualScroll = Math.max(0, Math.min(this.state.manualScroll + delta, 500));
|
|
162
171
|
this.scheduleRender();
|
|
163
172
|
}
|
|
164
|
-
setLoading(loading) {
|
|
165
|
-
|
|
166
|
-
|
|
173
|
+
setLoading(loading) {
|
|
174
|
+
this.state.loading = loading;
|
|
175
|
+
this.scheduleRender();
|
|
176
|
+
}
|
|
177
|
+
setInputText(text) {
|
|
178
|
+
this.state.inputText = text;
|
|
179
|
+
this.scheduleRender();
|
|
180
|
+
}
|
|
181
|
+
setInputCursor(pos) {
|
|
182
|
+
this.state.inputCursor = pos;
|
|
183
|
+
this.scheduleRender();
|
|
184
|
+
}
|
|
167
185
|
setCompanion(lines, color) {
|
|
168
186
|
this.state.companionLines = lines;
|
|
169
187
|
this.state.companionColor = color;
|
|
170
188
|
this.scheduleRender();
|
|
171
189
|
}
|
|
172
|
-
setStatusHints(text) {
|
|
173
|
-
|
|
190
|
+
setStatusHints(text) {
|
|
191
|
+
this.state.statusHints = text;
|
|
192
|
+
this.scheduleRender();
|
|
193
|
+
}
|
|
194
|
+
setBannerLines(lines) {
|
|
195
|
+
this.state.bannerLines = lines;
|
|
196
|
+
this.scheduleRender();
|
|
197
|
+
}
|
|
174
198
|
setAutocomplete(suggestions, index, descriptions) {
|
|
175
199
|
this.state.autocomplete = suggestions;
|
|
176
200
|
this.state.autocompleteDescriptions = descriptions ?? [];
|
|
177
201
|
this.state.autocompleteIndex = index;
|
|
178
202
|
this.scheduleRender();
|
|
179
203
|
}
|
|
180
|
-
setStatusLine(text) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
204
|
+
setStatusLine(text) {
|
|
205
|
+
this.state.statusLine = text;
|
|
206
|
+
this.scheduleRender();
|
|
207
|
+
}
|
|
208
|
+
setContextWarning(warning) {
|
|
209
|
+
this.state.contextWarning = warning;
|
|
210
|
+
this.scheduleRender();
|
|
211
|
+
}
|
|
212
|
+
setVimMode(mode) {
|
|
213
|
+
this.state.vimMode = mode;
|
|
214
|
+
this.scheduleRender();
|
|
215
|
+
}
|
|
216
|
+
setThinkingStartedAt(time) {
|
|
217
|
+
this.state.thinkingStartedAt = time;
|
|
218
|
+
}
|
|
219
|
+
getThinkingStartedAt() {
|
|
220
|
+
return this.state.thinkingStartedAt;
|
|
221
|
+
}
|
|
222
|
+
setLastThinkingSummary(summary) {
|
|
223
|
+
this.state.lastThinkingSummary = summary;
|
|
224
|
+
this.scheduleRender();
|
|
225
|
+
}
|
|
226
|
+
toggleThinkingExpanded() {
|
|
227
|
+
this.state.thinkingExpanded = !this.state.thinkingExpanded;
|
|
228
|
+
this.scheduleRender();
|
|
229
|
+
}
|
|
230
|
+
setTokenCount(count) {
|
|
231
|
+
this.state.tokenCount = count;
|
|
232
|
+
this.scheduleRender();
|
|
233
|
+
}
|
|
188
234
|
toggleCodeBlockExpansion() {
|
|
189
235
|
this.state.codeBlocksExpanded = !this.state.codeBlocksExpanded;
|
|
190
236
|
this.scheduleRender();
|
|
@@ -205,10 +251,10 @@ export class TerminalRenderer {
|
|
|
205
251
|
this.scheduleRender();
|
|
206
252
|
}
|
|
207
253
|
sessionBrowserUp() {
|
|
208
|
-
this.withSessionBrowser(sb => browserLoadPreview(browserUp(sb)));
|
|
254
|
+
this.withSessionBrowser((sb) => browserLoadPreview(browserUp(sb)));
|
|
209
255
|
}
|
|
210
256
|
sessionBrowserDown() {
|
|
211
|
-
this.withSessionBrowser(sb => browserLoadPreview(browserDown(sb)));
|
|
257
|
+
this.withSessionBrowser((sb) => browserLoadPreview(browserDown(sb)));
|
|
212
258
|
}
|
|
213
259
|
sessionBrowserSelect() {
|
|
214
260
|
if (!this.state.sessionBrowser)
|
|
@@ -219,11 +265,11 @@ export class TerminalRenderer {
|
|
|
219
265
|
return id;
|
|
220
266
|
}
|
|
221
267
|
sessionBrowserType(char) {
|
|
222
|
-
this.withSessionBrowser(sb => browserSearch(sb, sb.searchQuery + char));
|
|
268
|
+
this.withSessionBrowser((sb) => browserSearch(sb, sb.searchQuery + char));
|
|
223
269
|
}
|
|
224
270
|
sessionBrowserBackspace() {
|
|
225
271
|
if (this.state.sessionBrowser && this.state.sessionBrowser.searchQuery.length > 0) {
|
|
226
|
-
this.withSessionBrowser(sb => browserSearch(sb, sb.searchQuery.slice(0, -1)));
|
|
272
|
+
this.withSessionBrowser((sb) => browserSearch(sb, sb.searchQuery.slice(0, -1)));
|
|
227
273
|
}
|
|
228
274
|
}
|
|
229
275
|
isSessionBrowserOpen() {
|
|
@@ -233,12 +279,21 @@ export class TerminalRenderer {
|
|
|
233
279
|
this.state.toolCalls.set(callId, info);
|
|
234
280
|
this.scheduleRender();
|
|
235
281
|
}
|
|
236
|
-
getToolCall(callId) {
|
|
237
|
-
|
|
238
|
-
|
|
282
|
+
getToolCall(callId) {
|
|
283
|
+
return this.state.toolCalls.get(callId);
|
|
284
|
+
}
|
|
285
|
+
clearToolCalls() {
|
|
286
|
+
this.state.toolCalls.clear();
|
|
287
|
+
this.flushedToolCallIds.clear();
|
|
288
|
+
this.scheduleRender();
|
|
289
|
+
}
|
|
290
|
+
collapseAllToolCalls() {
|
|
291
|
+
this.state.expandedToolCalls.clear();
|
|
292
|
+
this.scheduleRender();
|
|
293
|
+
}
|
|
239
294
|
/** Show a question prompt and wait for text answer */
|
|
240
295
|
askQuestion(question, options) {
|
|
241
|
-
this.state.questionPrompt = { question, options: options ?? null, input:
|
|
296
|
+
this.state.questionPrompt = { question, options: options ?? null, input: "", cursor: 0 };
|
|
242
297
|
this.scheduleRender();
|
|
243
298
|
return new Promise((resolve) => {
|
|
244
299
|
this.questionResolve = resolve;
|
|
@@ -257,14 +312,14 @@ export class TerminalRenderer {
|
|
|
257
312
|
}
|
|
258
313
|
/** Cycle to next tool call and toggle its expansion */
|
|
259
314
|
cycleToolCallExpansion() {
|
|
260
|
-
const ids = [...this.state.toolCalls.keys()].filter(id => {
|
|
315
|
+
const ids = [...this.state.toolCalls.keys()].filter((id) => {
|
|
261
316
|
const tc = this.state.toolCalls.get(id);
|
|
262
|
-
return tc && tc.status !==
|
|
317
|
+
return tc && tc.status !== "running" && tc.output;
|
|
263
318
|
});
|
|
264
319
|
if (ids.length === 0)
|
|
265
320
|
return;
|
|
266
321
|
const expanded = this.state.expandedToolCalls;
|
|
267
|
-
const currentIdx = ids.findIndex(id => expanded.has(id));
|
|
322
|
+
const currentIdx = ids.findIndex((id) => expanded.has(id));
|
|
268
323
|
if (currentIdx >= 0) {
|
|
269
324
|
expanded.delete(ids[currentIdx]);
|
|
270
325
|
const nextIdx = currentIdx + 1;
|
|
@@ -280,7 +335,12 @@ export class TerminalRenderer {
|
|
|
280
335
|
/** Show permission prompt and wait for Y/N response */
|
|
281
336
|
askPermission(toolName, description, riskLevel) {
|
|
282
337
|
this.permissionPrompt = { toolName, description, riskLevel };
|
|
283
|
-
this.state.permissionBox = {
|
|
338
|
+
this.state.permissionBox = {
|
|
339
|
+
toolName,
|
|
340
|
+
description,
|
|
341
|
+
riskLevel,
|
|
342
|
+
suggestion: summarizeToolArgs(toolName, description),
|
|
343
|
+
};
|
|
284
344
|
this.state.permissionDiffInfo = extractDiffInfo(toolName, description);
|
|
285
345
|
// Auto-show diffs for file-modifying tools
|
|
286
346
|
const isFileTool = /^(Edit|Write|FileEdit|FileWrite)/i.test(toolName);
|
|
@@ -295,9 +355,7 @@ export class TerminalRenderer {
|
|
|
295
355
|
clearLiveArea() {
|
|
296
356
|
if (!this.started)
|
|
297
357
|
return;
|
|
298
|
-
const cmd = this.lastLiveLines > 0
|
|
299
|
-
? `\x1b[${this.lastLiveLines}A\r\x1b[J`
|
|
300
|
-
: '\r\x1b[J';
|
|
358
|
+
const cmd = this.lastLiveLines > 0 ? `\x1b[${this.lastLiveLines}A\r\x1b[J` : "\r\x1b[J";
|
|
301
359
|
syncWrite(cmd);
|
|
302
360
|
this.lastLiveLines = 0;
|
|
303
361
|
}
|
|
@@ -313,7 +371,7 @@ export class TerminalRenderer {
|
|
|
313
371
|
if (!this.permissionResolve)
|
|
314
372
|
return false;
|
|
315
373
|
const k = key.char.toLowerCase();
|
|
316
|
-
if (k ===
|
|
374
|
+
if (k === "y" || k === "n") {
|
|
317
375
|
const resolve = this.permissionResolve;
|
|
318
376
|
this.permissionResolve = null;
|
|
319
377
|
this.permissionPrompt = null;
|
|
@@ -321,9 +379,9 @@ export class TerminalRenderer {
|
|
|
321
379
|
this.state.permissionDiffVisible = false;
|
|
322
380
|
this.state.permissionDiffInfo = null;
|
|
323
381
|
this.scheduleRender();
|
|
324
|
-
resolve(k ===
|
|
382
|
+
resolve(k === "y");
|
|
325
383
|
}
|
|
326
|
-
else if (k ===
|
|
384
|
+
else if (k === "d" && this.state.permissionDiffInfo) {
|
|
327
385
|
this.state.permissionDiffVisible = !this.state.permissionDiffVisible;
|
|
328
386
|
this.scheduleRender();
|
|
329
387
|
}
|
|
@@ -334,7 +392,7 @@ export class TerminalRenderer {
|
|
|
334
392
|
if (!this.questionResolve || !this.state.questionPrompt)
|
|
335
393
|
return false;
|
|
336
394
|
const qp = this.state.questionPrompt;
|
|
337
|
-
if (key.name ===
|
|
395
|
+
if (key.name === "return" && qp.input.trim()) {
|
|
338
396
|
const resolve = this.questionResolve;
|
|
339
397
|
const answer = qp.input.trim();
|
|
340
398
|
this.questionResolve = null;
|
|
@@ -342,26 +400,34 @@ export class TerminalRenderer {
|
|
|
342
400
|
this.scheduleRender();
|
|
343
401
|
resolve(answer);
|
|
344
402
|
}
|
|
345
|
-
else if (key.name ===
|
|
403
|
+
else if (key.name === "backspace") {
|
|
346
404
|
if (qp.cursor > 0) {
|
|
347
|
-
this.state.questionPrompt = {
|
|
405
|
+
this.state.questionPrompt = {
|
|
406
|
+
...qp,
|
|
407
|
+
input: qp.input.slice(0, qp.cursor - 1) + qp.input.slice(qp.cursor),
|
|
408
|
+
cursor: qp.cursor - 1,
|
|
409
|
+
};
|
|
348
410
|
this.scheduleRender();
|
|
349
411
|
}
|
|
350
412
|
}
|
|
351
|
-
else if (key.name ===
|
|
413
|
+
else if (key.name === "left") {
|
|
352
414
|
if (qp.cursor > 0) {
|
|
353
415
|
this.state.questionPrompt = { ...qp, cursor: qp.cursor - 1 };
|
|
354
416
|
this.scheduleRender();
|
|
355
417
|
}
|
|
356
418
|
}
|
|
357
|
-
else if (key.name ===
|
|
419
|
+
else if (key.name === "right") {
|
|
358
420
|
if (qp.cursor < qp.input.length) {
|
|
359
421
|
this.state.questionPrompt = { ...qp, cursor: qp.cursor + 1 };
|
|
360
422
|
this.scheduleRender();
|
|
361
423
|
}
|
|
362
424
|
}
|
|
363
425
|
else if (key.char && key.char.length === 1 && !key.ctrl && !key.meta) {
|
|
364
|
-
this.state.questionPrompt = {
|
|
426
|
+
this.state.questionPrompt = {
|
|
427
|
+
...qp,
|
|
428
|
+
input: qp.input.slice(0, qp.cursor) + key.char + qp.input.slice(qp.cursor),
|
|
429
|
+
cursor: qp.cursor + 1,
|
|
430
|
+
};
|
|
365
431
|
this.scheduleRender();
|
|
366
432
|
}
|
|
367
433
|
return true;
|
|
@@ -404,16 +470,16 @@ export class TerminalRenderer {
|
|
|
404
470
|
/** Apply lightweight markdown styling to a line for scrollback output */
|
|
405
471
|
styleMarkdownLine(line) {
|
|
406
472
|
return line
|
|
407
|
-
.replace(/\*\*(.+?)\*\*/g,
|
|
408
|
-
.replace(/`([^`]+)`/g,
|
|
409
|
-
.replace(/^(#{1,3})\s+(.+)$/,
|
|
473
|
+
.replace(/\*\*(.+?)\*\*/g, "\x1b[1m$1\x1b[0m") // bold → full reset after
|
|
474
|
+
.replace(/`([^`]+)`/g, "\x1b[2m$1\x1b[0m") // inline code → dim, full reset after
|
|
475
|
+
.replace(/^(#{1,3})\s+(.+)$/, "\x1b[1m\x1b[36m$1 $2\x1b[0m"); // headings → bold cyan
|
|
410
476
|
}
|
|
411
477
|
/** Calculate the max text width for flushed scrollback output, reserving space for companion */
|
|
412
478
|
flushTextWidth() {
|
|
413
479
|
const maxWidth = process.stdout.columns ?? 80;
|
|
414
480
|
if (!this.state.companionLines)
|
|
415
481
|
return maxWidth;
|
|
416
|
-
const compWidth = Math.max(...this.state.companionLines.map(l => l.length), 0) + 2;
|
|
482
|
+
const compWidth = Math.max(...this.state.companionLines.map((l) => l.length), 0) + 2;
|
|
417
483
|
return Math.max(40, maxWidth - compWidth);
|
|
418
484
|
}
|
|
419
485
|
/** Flush completed messages to terminal scrollback (native scrollbar) */
|
|
@@ -427,34 +493,38 @@ export class TerminalRenderer {
|
|
|
427
493
|
if (this.state.loading && this.flushedMessageCount === messages.length - 1 && msg.meta?.isStreaming)
|
|
428
494
|
break;
|
|
429
495
|
const t = getTheme();
|
|
430
|
-
const colorCode = msg.role ===
|
|
431
|
-
|
|
432
|
-
|
|
496
|
+
const colorCode = msg.role === "user"
|
|
497
|
+
? `\x1b[${FG(t.user)}m\x1b[1m`
|
|
498
|
+
: msg.role === "assistant"
|
|
499
|
+
? `\x1b[${FG(t.assistant)}m`
|
|
500
|
+
: "\x1b[2m";
|
|
501
|
+
const prefixChar = msg.role === "user" ? "❯ " : msg.role === "assistant" ? "◆ " : " ";
|
|
502
|
+
const lines = msg.content.split("\n");
|
|
433
503
|
for (let i = 0; i < lines.length; i++) {
|
|
434
|
-
const styledLine = msg.role ===
|
|
435
|
-
const linePrefix = i === 0 ? prefixChar :
|
|
504
|
+
const styledLine = msg.role === "assistant" ? this.styleMarkdownLine(lines[i]) : lines[i];
|
|
505
|
+
const linePrefix = i === 0 ? prefixChar : " ";
|
|
436
506
|
const fullLine = linePrefix + styledLine;
|
|
437
|
-
process.stdout.write(colorCode + fullLine.slice(0, textWidth)
|
|
507
|
+
process.stdout.write(`${colorCode + fullLine.slice(0, textWidth)}\x1b[0m\n`);
|
|
438
508
|
}
|
|
439
509
|
// Divider after each message
|
|
440
|
-
process.stdout.write(
|
|
510
|
+
process.stdout.write(`\x1b[2m${"─".repeat(Math.min(60, textWidth))}\x1b[0m\n`);
|
|
441
511
|
this.flushedMessageCount++;
|
|
442
512
|
didFlush = true;
|
|
443
513
|
}
|
|
444
514
|
// Flush completed tool calls as single-line summaries (once each)
|
|
445
515
|
if (didFlush) {
|
|
446
516
|
for (const [callId, tc] of this.state.toolCalls) {
|
|
447
|
-
if (tc.status ===
|
|
517
|
+
if (tc.status === "running")
|
|
448
518
|
continue;
|
|
449
519
|
if (this.flushedToolCallIds.has(callId))
|
|
450
520
|
continue;
|
|
451
521
|
this.flushedToolCallIds.add(callId);
|
|
452
522
|
const t = getTheme();
|
|
453
|
-
const icon = tc.status ===
|
|
454
|
-
const summary = tc.resultSummary ? ` ${tc.resultSummary}` :
|
|
455
|
-
const elapsed = tc.startedAt ? ` · ${Math.floor((Date.now() - tc.startedAt) / 1000)}s` :
|
|
456
|
-
const toolLine = `${icon} ${tc.toolName}\x1b[0m \x1b[2m${tc.args ??
|
|
457
|
-
process.stdout.write(toolLine.slice(0, textWidth)
|
|
523
|
+
const icon = tc.status === "done" ? `\x1b[${FG(t.success)}m✓` : `\x1b[${FG(t.error)}m✗`;
|
|
524
|
+
const summary = tc.resultSummary ? ` ${tc.resultSummary}` : "";
|
|
525
|
+
const elapsed = tc.startedAt ? ` · ${Math.floor((Date.now() - tc.startedAt) / 1000)}s` : "";
|
|
526
|
+
const toolLine = `${icon} ${tc.toolName}\x1b[0m \x1b[2m${tc.args ?? ""}${summary}${elapsed}\x1b[0m`;
|
|
527
|
+
process.stdout.write(`${toolLine.slice(0, textWidth)}\n`);
|
|
458
528
|
}
|
|
459
529
|
}
|
|
460
530
|
}
|
|
@@ -464,10 +534,10 @@ export class TerminalRenderer {
|
|
|
464
534
|
for (let r = 0; r < grid.height; r++) {
|
|
465
535
|
// Find last non-space cell to avoid writing trailing whitespace
|
|
466
536
|
let lastCol = grid.width - 1;
|
|
467
|
-
while (lastCol >= 0 && grid.cells[r][lastCol].char ===
|
|
537
|
+
while (lastCol >= 0 && grid.cells[r][lastCol].char === " " && !grid.cells[r][lastCol].style.bg) {
|
|
468
538
|
lastCol--;
|
|
469
539
|
}
|
|
470
|
-
let lastStyle =
|
|
540
|
+
let lastStyle = "";
|
|
471
541
|
for (let c = 0; c <= lastCol; c++) {
|
|
472
542
|
const cell = grid.cells[r][c];
|
|
473
543
|
const sgr = styleToSGR(cell.style);
|
|
@@ -477,27 +547,27 @@ export class TerminalRenderer {
|
|
|
477
547
|
}
|
|
478
548
|
parts.push(cell.char);
|
|
479
549
|
}
|
|
480
|
-
parts.push(
|
|
550
|
+
parts.push("\x1b[0m\x1b[K"); // reset + erase to end of line (clear stale content)
|
|
481
551
|
if (r < grid.height - 1)
|
|
482
|
-
parts.push(
|
|
552
|
+
parts.push("\n");
|
|
483
553
|
}
|
|
484
|
-
return parts.join(
|
|
554
|
+
return parts.join("");
|
|
485
555
|
}
|
|
486
556
|
render() {
|
|
487
557
|
const w = process.stdout.columns ?? 80;
|
|
488
558
|
const h = process.stdout.rows ?? 24;
|
|
489
559
|
// 1. Build erase command: move UP to start of old live area, erase below.
|
|
490
|
-
let eraseCmd =
|
|
560
|
+
let eraseCmd = "";
|
|
491
561
|
if (this.lastLiveLines > 0) {
|
|
492
562
|
eraseCmd += `\x1b[${this.lastLiveLines}A`;
|
|
493
563
|
}
|
|
494
|
-
eraseCmd +=
|
|
564
|
+
eraseCmd += "\r\x1b[J";
|
|
495
565
|
// 2. Flush completed messages to scrollback
|
|
496
566
|
// If nothing to flush, we combine erase + rewrite into a single
|
|
497
567
|
// atomic syncWrite to prevent any flickering.
|
|
498
568
|
const hasFlush = this.hasPendingFlush();
|
|
499
569
|
if (hasFlush) {
|
|
500
|
-
syncWrite(
|
|
570
|
+
syncWrite(`\x1b[?25l${eraseCmd}`); // hide cursor + erase
|
|
501
571
|
this.flushMessages();
|
|
502
572
|
}
|
|
503
573
|
// 3. Calculate and render new live area
|
|
@@ -511,12 +581,12 @@ export class TerminalRenderer {
|
|
|
511
581
|
// position cursor, show cursor. Terminal paints this as a single frame.
|
|
512
582
|
const liveOutput = this.renderGridToAnsi(this.current);
|
|
513
583
|
const upFromEnd = liveHeight - 1 - cursor.cursorRow;
|
|
514
|
-
const cursorPos =
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
584
|
+
const cursorPos = `${upFromEnd > 0 ? `\x1b[${upFromEnd}A` : ""}\x1b[${cursor.cursorCol + 1}G`;
|
|
585
|
+
syncWrite((hasFlush ? "" : `\x1b[?25l${eraseCmd}`) + // hide + erase (if not already done)
|
|
586
|
+
liveOutput +
|
|
587
|
+
"\r" +
|
|
588
|
+
cursorPos +
|
|
589
|
+
"\x1b[?25h");
|
|
520
590
|
this.lastLiveLines = cursor.cursorRow;
|
|
521
591
|
}
|
|
522
592
|
/** Check if there are messages pending flush (without flushing) */
|
|
@@ -536,7 +606,7 @@ export class TerminalRenderer {
|
|
|
536
606
|
rows += this.state.bannerLines.length + 1;
|
|
537
607
|
}
|
|
538
608
|
if (this.state.loading && this.state.streamingText)
|
|
539
|
-
rows += Math.min(this.state.streamingText.split(
|
|
609
|
+
rows += Math.min(this.state.streamingText.split("\n").length, 10);
|
|
540
610
|
if (this.state.thinkingText)
|
|
541
611
|
rows += this.state.thinkingExpanded ? 10 : 1;
|
|
542
612
|
if (!this.state.loading && this.state.lastThinkingSummary)
|
|
@@ -547,7 +617,7 @@ export class TerminalRenderer {
|
|
|
547
617
|
rows += 1;
|
|
548
618
|
for (const [, tc] of this.state.toolCalls) {
|
|
549
619
|
rows += 2; // header + possible description/agent line
|
|
550
|
-
if (tc.status ===
|
|
620
|
+
if (tc.status === "running" && tc.liveOutput)
|
|
551
621
|
rows += Math.min(tc.liveOutput.length, 3);
|
|
552
622
|
}
|
|
553
623
|
if (this.state.contextWarning)
|