lsd-pi 1.1.4 → 1.1.6
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 +2 -1
- package/dist/headless-ui.js +2 -0
- package/dist/onboarding.js +11 -8
- package/dist/resources/extensions/async-jobs/async-bash-tool.js +14 -0
- package/dist/resources/extensions/async-jobs/await-tool.js +14 -0
- package/dist/resources/extensions/async-jobs/cancel-job-tool.js +7 -0
- package/dist/resources/extensions/cache-timer/index.js +5 -0
- package/dist/resources/extensions/codex-rotate/IMPLEMENTATION.md +18 -13
- package/dist/resources/extensions/codex-rotate/README.md +9 -3
- package/dist/resources/extensions/codex-rotate/commands.js +15 -8
- package/dist/resources/extensions/codex-rotate/index.js +17 -8
- package/dist/resources/extensions/memory/auto-extract.js +196 -80
- package/dist/resources/extensions/memory/dream.js +86 -19
- package/dist/resources/extensions/shared/rtk.js +89 -87
- package/dist/resources/extensions/subagent/index.js +33 -7
- package/dist/startup-model-validation.js +12 -2
- package/dist/update-check.js +2 -2
- package/dist/update-cmd.js +3 -3
- package/dist/welcome-screen.js +43 -14
- package/package.json +3 -2
- package/packages/pi-coding-agent/dist/core/agent-session.clear-queue.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/agent-session.clear-queue.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session.clear-queue.test.js +46 -0
- package/packages/pi-coding-agent/dist/core/agent-session.clear-queue.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +8 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +43 -4
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/keybindings.d.ts +1 -1
- package/packages/pi-coding-agent/dist/core/keybindings.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/keybindings.js +2 -0
- package/packages/pi-coding-agent/dist/core/keybindings.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/pty-executor.d.ts +48 -0
- package/packages/pi-coding-agent/dist/core/pty-executor.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/pty-executor.js +173 -0
- package/packages/pi-coding-agent/dist/core/pty-executor.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +16 -3
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +18 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tool-approval.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tool-approval.js +2 -2
- package/packages/pi-coding-agent/dist/core/tool-approval.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.js +23 -2
- package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/pty.d.ts +50 -0
- package/packages/pi-coding-agent/dist/core/tools/pty.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/pty.js +289 -0
- package/packages/pi-coding-agent/dist/core/tools/pty.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +36 -22
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts +3 -5
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +23 -62
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.js +1 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.js +1 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.d.ts +39 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.js +182 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +6 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +36 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +2 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +106 -77
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +2 -5
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +4 -13
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts +11 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +49 -13
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +27 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +251 -39
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +2 -2
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
- package/packages/pi-coding-agent/dist/utils/terminal-screen.d.ts +10 -0
- package/packages/pi-coding-agent/dist/utils/terminal-screen.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/utils/terminal-screen.js +67 -0
- package/packages/pi-coding-agent/dist/utils/terminal-screen.js.map +1 -0
- package/packages/pi-coding-agent/dist/utils/terminal-serializer.d.ts +7 -0
- package/packages/pi-coding-agent/dist/utils/terminal-serializer.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/utils/terminal-serializer.js +67 -0
- package/packages/pi-coding-agent/dist/utils/terminal-serializer.js.map +1 -0
- package/packages/pi-coding-agent/package.json +9 -4
- package/packages/pi-coding-agent/src/core/agent-session.clear-queue.test.ts +50 -0
- package/packages/pi-coding-agent/src/core/agent-session.ts +50 -4
- package/packages/pi-coding-agent/src/core/extensions/types.ts +1 -1
- package/packages/pi-coding-agent/src/core/keybindings.ts +4 -1
- package/packages/pi-coding-agent/src/core/pty-executor.ts +229 -0
- package/packages/pi-coding-agent/src/core/sdk.ts +16 -3
- package/packages/pi-coding-agent/src/core/settings-manager.ts +27 -0
- package/packages/pi-coding-agent/src/core/tool-approval.ts +2 -2
- package/packages/pi-coding-agent/src/core/tools/index.ts +35 -2
- package/packages/pi-coding-agent/src/core/tools/pty.ts +354 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +37 -24
- package/packages/pi-coding-agent/src/modes/interactive/components/bash-execution.ts +22 -70
- package/packages/pi-coding-agent/src/modes/interactive/components/branch-summary-message.ts +1 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/compaction-summary-message.ts +1 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/embedded-terminal.ts +224 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +45 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +2 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +104 -81
- package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +5 -19
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +55 -13
- package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +2 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +3 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +296 -48
- package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +2 -2
- package/packages/pi-coding-agent/src/utils/terminal-screen.ts +77 -0
- package/packages/pi-coding-agent/src/utils/terminal-serializer.ts +72 -0
- package/packages/pi-tui/dist/components/__tests__/editor-dropped-image.test.d.ts +2 -0
- package/packages/pi-tui/dist/components/__tests__/editor-dropped-image.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/__tests__/editor-dropped-image.test.js +105 -0
- package/packages/pi-tui/dist/components/__tests__/editor-dropped-image.test.js.map +1 -0
- package/packages/pi-tui/dist/components/editor.d.ts +4 -0
- package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/editor.js +57 -3
- package/packages/pi-tui/dist/components/editor.js.map +1 -1
- package/packages/pi-tui/dist/components/loader.d.ts +26 -6
- package/packages/pi-tui/dist/components/loader.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/loader.js +178 -18
- package/packages/pi-tui/dist/components/loader.js.map +1 -1
- package/packages/pi-tui/src/components/editor.ts +65 -3
- package/packages/pi-tui/src/components/loader.ts +196 -19
- package/pkg/dist/modes/interactive/theme/themes.js +2 -2
- package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/async-jobs/async-bash-tool.ts +13 -0
- package/src/resources/extensions/async-jobs/await-tool.ts +13 -0
- package/src/resources/extensions/async-jobs/cancel-job-tool.ts +8 -0
- package/src/resources/extensions/cache-timer/index.ts +102 -96
- package/src/resources/extensions/codex-rotate/IMPLEMENTATION.md +18 -13
- package/src/resources/extensions/codex-rotate/README.md +9 -3
- package/src/resources/extensions/codex-rotate/commands.ts +335 -329
- package/src/resources/extensions/codex-rotate/index.ts +85 -75
- package/src/resources/extensions/memory/auto-extract.ts +330 -204
- package/src/resources/extensions/memory/dream.ts +88 -21
- package/src/resources/extensions/memory/tests/auto-extract.test.ts +200 -144
- package/src/resources/extensions/shared/rtk.js +112 -0
- package/src/resources/extensions/subagent/index.ts +35 -6
|
@@ -69,12 +69,14 @@ import {
|
|
|
69
69
|
type PermissionMode,
|
|
70
70
|
} from "../../core/tool-approval.js";
|
|
71
71
|
import type { TruncationResult } from "../../core/tools/truncate.js";
|
|
72
|
+
import { isPtyAvailable } from "../../core/pty-executor.js";
|
|
72
73
|
import { getChangelogPath, getNewEntries, parseChangelog } from "../../utils/changelog.js";
|
|
73
74
|
import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
|
|
74
75
|
import { resetStdinForTui } from "../../utils/reset-stdin.js";
|
|
75
76
|
import { ensureTool } from "../../utils/tools-manager.js";
|
|
76
77
|
import { AssistantMessageComponent } from "./components/assistant-message.js";
|
|
77
78
|
import { BashExecutionComponent } from "./components/bash-execution.js";
|
|
79
|
+
import { EmbeddedTerminalComponent } from "./components/embedded-terminal.js";
|
|
78
80
|
import { BorderedLoader } from "./components/bordered-loader.js";
|
|
79
81
|
import { BranchSummaryMessageComponent } from "./components/branch-summary-message.js";
|
|
80
82
|
import { CompactionSummaryMessageComponent } from "./components/compaction-summary-message.js";
|
|
@@ -137,6 +139,8 @@ interface Expandable {
|
|
|
137
139
|
setExpanded(expanded: boolean): void;
|
|
138
140
|
}
|
|
139
141
|
|
|
142
|
+
type InteractiveBashComponent = BashExecutionComponent | EmbeddedTerminalComponent;
|
|
143
|
+
|
|
140
144
|
function isExpandable(obj: unknown): obj is Expandable {
|
|
141
145
|
return typeof obj === "object" && obj !== null && "setExpanded" in obj && typeof obj.setExpanded === "function";
|
|
142
146
|
}
|
|
@@ -192,7 +196,24 @@ export class InteractiveMode {
|
|
|
192
196
|
private onInputCallback?: (text: string) => void;
|
|
193
197
|
private loadingAnimation: Loader | undefined = undefined;
|
|
194
198
|
private pendingWorkingMessage: string | undefined = undefined;
|
|
195
|
-
private readonly defaultWorkingMessage = "
|
|
199
|
+
private readonly defaultWorkingMessage = "Cooking…";
|
|
200
|
+
private readonly workingMessages = [
|
|
201
|
+
"Cooking…",
|
|
202
|
+
"Vibing…",
|
|
203
|
+
"Brainworming…",
|
|
204
|
+
"Scheming…",
|
|
205
|
+
"Spelunking…",
|
|
206
|
+
"Manifesting…",
|
|
207
|
+
"Yak shaving…",
|
|
208
|
+
"Grokking…",
|
|
209
|
+
"Pondering…",
|
|
210
|
+
"Going deep…",
|
|
211
|
+
"Caffeinating…",
|
|
212
|
+
"On a mission…",
|
|
213
|
+
"Consulting the void…",
|
|
214
|
+
"Doing the thing…",
|
|
215
|
+
"Almost maybe…",
|
|
216
|
+
];
|
|
196
217
|
|
|
197
218
|
private lastSigintTime = 0;
|
|
198
219
|
private lastEscapeTime = 0;
|
|
@@ -208,6 +229,7 @@ export class InteractiveMode {
|
|
|
208
229
|
|
|
209
230
|
// Tool execution tracking: toolCallId -> component
|
|
210
231
|
private pendingTools = new Map<string, ToolExecutionComponent>();
|
|
232
|
+
private agentPtyComponents = new Map<string, EmbeddedTerminalComponent>();
|
|
211
233
|
|
|
212
234
|
// Tool output expansion state
|
|
213
235
|
private toolOutputExpanded = false;
|
|
@@ -215,6 +237,10 @@ export class InteractiveMode {
|
|
|
215
237
|
// Thinking block visibility state
|
|
216
238
|
private hideThinkingBlock = false;
|
|
217
239
|
|
|
240
|
+
// Pin last prompt feature
|
|
241
|
+
private pinLastPromptEnabled = false;
|
|
242
|
+
private lastSentPromptText: string | undefined = undefined;
|
|
243
|
+
|
|
218
244
|
// Skill commands: command name -> skill file path
|
|
219
245
|
private skillCommands = new Map<string, string>();
|
|
220
246
|
|
|
@@ -225,10 +251,12 @@ export class InteractiveMode {
|
|
|
225
251
|
private isBashMode = false;
|
|
226
252
|
|
|
227
253
|
// Track current bash execution component
|
|
228
|
-
private bashComponent:
|
|
254
|
+
private bashComponent: InteractiveBashComponent | undefined = undefined;
|
|
229
255
|
|
|
230
256
|
// Track pending bash components (shown in pending area, moved to chat on submit)
|
|
231
|
-
private pendingBashComponents:
|
|
257
|
+
private pendingBashComponents: InteractiveBashComponent[] = [];
|
|
258
|
+
private focusedEmbeddedTerminal?: EmbeddedTerminalComponent;
|
|
259
|
+
private lastEmbeddedTerminalSize?: { cols: number; rows: number };
|
|
232
260
|
|
|
233
261
|
// Auto-compaction state
|
|
234
262
|
private autoCompactionLoader: Loader | undefined = undefined;
|
|
@@ -312,6 +340,9 @@ export class InteractiveMode {
|
|
|
312
340
|
// Load hide thinking block setting
|
|
313
341
|
this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
|
|
314
342
|
|
|
343
|
+
// Load pin last prompt setting
|
|
344
|
+
this.pinLastPromptEnabled = this.settingsManager.getPinLastPrompt();
|
|
345
|
+
|
|
315
346
|
// Register themes from resource loader and initialize
|
|
316
347
|
setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
|
|
317
348
|
initTheme(this.settingsManager.getTheme(), true, this.settingsManager.getThemeAccent());
|
|
@@ -618,6 +649,7 @@ export class InteractiveMode {
|
|
|
618
649
|
// Main interactive loop
|
|
619
650
|
while (true) {
|
|
620
651
|
const userInput = await this.getUserInput();
|
|
652
|
+
this.recordLastSentPrompt(userInput);
|
|
621
653
|
try {
|
|
622
654
|
await this.session.prompt(userInput);
|
|
623
655
|
} catch (error: unknown) {
|
|
@@ -1403,9 +1435,7 @@ export class InteractiveMode {
|
|
|
1403
1435
|
this.defaultEditor.onExtensionShortcut = undefined;
|
|
1404
1436
|
this.updateTerminalTitle();
|
|
1405
1437
|
if (this.loadingAnimation) {
|
|
1406
|
-
this.loadingAnimation.
|
|
1407
|
-
`${this.defaultWorkingMessage} (${appKey(this.keybindings, "interrupt")} to interrupt)`,
|
|
1408
|
-
);
|
|
1438
|
+
this.loadingAnimation.setCycleMessages(this.workingMessages);
|
|
1409
1439
|
}
|
|
1410
1440
|
}
|
|
1411
1441
|
|
|
@@ -1415,6 +1445,44 @@ export class InteractiveMode {
|
|
|
1415
1445
|
/**
|
|
1416
1446
|
* Render all extension widgets to the widget container.
|
|
1417
1447
|
*/
|
|
1448
|
+
/**
|
|
1449
|
+
* Record the last user-sent prompt text and refresh the widget.
|
|
1450
|
+
* Called from the main loop and input-controller paths.
|
|
1451
|
+
*/
|
|
1452
|
+
recordLastSentPrompt(text: string): void {
|
|
1453
|
+
// Ignore slash commands and bash commands — they're not "topic" prompts
|
|
1454
|
+
const trimmed = text.trim();
|
|
1455
|
+
if (trimmed.startsWith("/") || trimmed.startsWith("!")) return;
|
|
1456
|
+
this.lastSentPromptText = trimmed;
|
|
1457
|
+
this.updatePinLastPromptWidget();
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
/**
|
|
1461
|
+
* Update (or remove) the "pin last prompt" widget above the editor.
|
|
1462
|
+
* Uses the built-in extension widget slot with key "__pin-last-prompt__".
|
|
1463
|
+
*/
|
|
1464
|
+
private updatePinLastPromptWidget(): void {
|
|
1465
|
+
const key = "__pin-last-prompt__";
|
|
1466
|
+
if (!this.pinLastPromptEnabled || !this.lastSentPromptText) {
|
|
1467
|
+
// Remove widget
|
|
1468
|
+
const existing = this.extensionWidgetsAbove.get(key);
|
|
1469
|
+
if (existing?.dispose) existing.dispose();
|
|
1470
|
+
this.extensionWidgetsAbove.delete(key);
|
|
1471
|
+
} else {
|
|
1472
|
+
// Truncate to one line (max 200 chars) to keep it compact
|
|
1473
|
+
const raw = this.lastSentPromptText.replace(/[\r\n]+/g, " ").trim();
|
|
1474
|
+
const label = raw.length > 200 ? raw.slice(0, 197) + "…" : raw;
|
|
1475
|
+
const container = new Container();
|
|
1476
|
+
container.addChild(new Text(
|
|
1477
|
+
theme.fg("dim", ` last prompt: ${label}`),
|
|
1478
|
+
0,
|
|
1479
|
+
0,
|
|
1480
|
+
));
|
|
1481
|
+
this.extensionWidgetsAbove.set(key, container);
|
|
1482
|
+
}
|
|
1483
|
+
this.renderWidgets();
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1418
1486
|
private renderWidgets(): void {
|
|
1419
1487
|
if (!this.widgetContainerAbove || !this.widgetContainerBelow) return;
|
|
1420
1488
|
this.renderWidgetContainer(this.widgetContainerAbove, this.extensionWidgetsAbove, true, true);
|
|
@@ -1619,6 +1687,13 @@ export class InteractiveMode {
|
|
|
1619
1687
|
return result === "Yes";
|
|
1620
1688
|
}
|
|
1621
1689
|
|
|
1690
|
+
/** Reset the loading animation back to cycling working messages. */
|
|
1691
|
+
private resetLoadingMessage(): void {
|
|
1692
|
+
if (this.loadingAnimation) {
|
|
1693
|
+
this.loadingAnimation.setCycleMessages(this.workingMessages);
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1622
1697
|
private _registerApprovalHandlers(): void {
|
|
1623
1698
|
const classifierService = new ClassifierService(this.session.modelRegistry.authStorage);
|
|
1624
1699
|
|
|
@@ -1629,9 +1704,7 @@ export class InteractiveMode {
|
|
|
1629
1704
|
const title = `Allow ${actionLabel}: ${request.path}\n${request.message}`;
|
|
1630
1705
|
const approved = await this._showApprovalConfirm(title);
|
|
1631
1706
|
|
|
1632
|
-
|
|
1633
|
-
this.loadingAnimation.setMessage(this.defaultWorkingMessage);
|
|
1634
|
-
}
|
|
1707
|
+
this.resetLoadingMessage();
|
|
1635
1708
|
return approved;
|
|
1636
1709
|
});
|
|
1637
1710
|
|
|
@@ -1671,9 +1744,7 @@ export class InteractiveMode {
|
|
|
1671
1744
|
});
|
|
1672
1745
|
|
|
1673
1746
|
if (decision.approved) {
|
|
1674
|
-
|
|
1675
|
-
this.loadingAnimation.setMessage(this.defaultWorkingMessage);
|
|
1676
|
-
}
|
|
1747
|
+
this.resetLoadingMessage();
|
|
1677
1748
|
return true;
|
|
1678
1749
|
}
|
|
1679
1750
|
|
|
@@ -1681,9 +1752,7 @@ export class InteractiveMode {
|
|
|
1681
1752
|
const title = `Classifier ${decision.source === "rule" ? "blocked" : "did not approve"} ${request.toolName}\n${decision.reason}\n${argsPreview}`;
|
|
1682
1753
|
const approved = await this._showApprovalConfirm(title);
|
|
1683
1754
|
|
|
1684
|
-
|
|
1685
|
-
this.loadingAnimation.setMessage(this.defaultWorkingMessage);
|
|
1686
|
-
}
|
|
1755
|
+
this.resetLoadingMessage();
|
|
1687
1756
|
return approved;
|
|
1688
1757
|
});
|
|
1689
1758
|
|
|
@@ -1693,9 +1762,7 @@ export class InteractiveMode {
|
|
|
1693
1762
|
`${request.message}\n${request.command}`,
|
|
1694
1763
|
["Allow once", "Allow for session", "Deny"],
|
|
1695
1764
|
);
|
|
1696
|
-
|
|
1697
|
-
this.loadingAnimation.setMessage(this.defaultWorkingMessage);
|
|
1698
|
-
}
|
|
1765
|
+
this.resetLoadingMessage();
|
|
1699
1766
|
if (result === "Allow once") return "allow-once";
|
|
1700
1767
|
if (result === "Allow for session") return "allow-session";
|
|
1701
1768
|
return "deny";
|
|
@@ -2027,6 +2094,7 @@ export class InteractiveMode {
|
|
|
2027
2094
|
this.defaultEditor.onAction("cycleModelBackward", () => this.cycleModel("backward"));
|
|
2028
2095
|
this.defaultEditor.onAction("cyclePermissionMode", () => this.cyclePermissionMode());
|
|
2029
2096
|
this.defaultEditor.onAction("showHotkeys", () => showHotkeys(this.getSlashCommandContext()));
|
|
2097
|
+
this.defaultEditor.onAction("terminalFocus", () => this.toggleEmbeddedTerminalFocus());
|
|
2030
2098
|
|
|
2031
2099
|
// Global debug handler on TUI (works regardless of focus)
|
|
2032
2100
|
this.ui.onDebug = () => this.handleDebugCommand();
|
|
@@ -2382,6 +2450,7 @@ export class InteractiveMode {
|
|
|
2382
2450
|
this.hideThinkingBlock,
|
|
2383
2451
|
this.getMarkdownThemeWithSettings(),
|
|
2384
2452
|
this.settingsManager.getTimestampFormat(),
|
|
2453
|
+
this.session.thinkingLevel || "off",
|
|
2385
2454
|
);
|
|
2386
2455
|
this.chatContainer.addChild(assistantComponent);
|
|
2387
2456
|
break;
|
|
@@ -2727,9 +2796,42 @@ export class InteractiveMode {
|
|
|
2727
2796
|
for (const component of this.pendingBashComponents) {
|
|
2728
2797
|
component.setExpanded(expanded);
|
|
2729
2798
|
}
|
|
2799
|
+
this.updateEditorExpandHint();
|
|
2730
2800
|
this.ui.requestRender();
|
|
2731
2801
|
}
|
|
2732
2802
|
|
|
2803
|
+
/** Append/remove the "ctrl+o to expand" hint in the editor bottom border. */
|
|
2804
|
+
updateEditorExpandHint(): void {
|
|
2805
|
+
const expandKey = appKey(this.keybindings, "expandTools");
|
|
2806
|
+
const collapseHint = `${theme.fg("dim", expandKey)}${theme.fg("muted", " collapse")}`;
|
|
2807
|
+
const expandHint = `${theme.fg("dim", expandKey)}${theme.fg("muted", " : verbose")}`;
|
|
2808
|
+
// The base hint set during agent_start
|
|
2809
|
+
const enterKey = theme.fg("dim", "↵");
|
|
2810
|
+
const followUpKey = theme.fg("dim", appKey(this.keybindings, "followUp"));
|
|
2811
|
+
const steerLabel = theme.fg("muted", " steer");
|
|
2812
|
+
const queueLabel = theme.fg("muted", " queue");
|
|
2813
|
+
const baseHint = `${enterKey}${steerLabel} ${followUpKey}${queueLabel}`;
|
|
2814
|
+
|
|
2815
|
+
// Check if there are any expandable tool outputs in the chat
|
|
2816
|
+
const hasToolOutputs =
|
|
2817
|
+
this.chatContainer.children.some(isExpandable) ||
|
|
2818
|
+
!!this.bashComponent ||
|
|
2819
|
+
this.pendingBashComponents.length > 0;
|
|
2820
|
+
|
|
2821
|
+
const activeHint = this.toolOutputExpanded ? collapseHint : expandHint;
|
|
2822
|
+
|
|
2823
|
+
if (this.loadingAnimation) {
|
|
2824
|
+
// Agent is running — always show expand/collapse hint when there are tool outputs
|
|
2825
|
+
this.defaultEditor.bottomHint = hasToolOutputs
|
|
2826
|
+
? `${baseHint} ${activeHint}`
|
|
2827
|
+
: baseHint;
|
|
2828
|
+
} else if (hasToolOutputs) {
|
|
2829
|
+
// Idle — show expand/collapse hint so user knows ctrl+o works
|
|
2830
|
+
this.defaultEditor.bottomHint = activeHint;
|
|
2831
|
+
}
|
|
2832
|
+
// If no tool outputs and idle, leave bottomHint as-is (cleared by agent_end)
|
|
2833
|
+
}
|
|
2834
|
+
|
|
2733
2835
|
private toggleThinkingBlockVisibility(): void {
|
|
2734
2836
|
this.hideThinkingBlock = !this.hideThinkingBlock;
|
|
2735
2837
|
this.settingsManager.setHideThinkingBlock(this.hideThinkingBlock);
|
|
@@ -3063,6 +3165,7 @@ export class InteractiveMode {
|
|
|
3063
3165
|
enableSkillCommands: this.settingsManager.getEnableSkillCommands(),
|
|
3064
3166
|
codexRotate: this.settingsManager.getCodexRotate(),
|
|
3065
3167
|
cacheTimer: this.settingsManager.getCacheTimer(),
|
|
3168
|
+
pinLastPrompt: this.settingsManager.getPinLastPrompt(),
|
|
3066
3169
|
steeringMode: this.session.steeringMode,
|
|
3067
3170
|
followUpMode: this.session.followUpMode,
|
|
3068
3171
|
transport: this.settingsManager.getTransport(),
|
|
@@ -3086,6 +3189,8 @@ export class InteractiveMode {
|
|
|
3086
3189
|
toolOutputMode: this.settingsManager.getToolOutputMode(),
|
|
3087
3190
|
rtk: this.settingsManager.getRtk(),
|
|
3088
3191
|
editorScheme: this.settingsManager.getEditorScheme(),
|
|
3192
|
+
autoDream: this.settingsManager.getAutoDream(),
|
|
3193
|
+
autoMemory: this.settingsManager.getAutoMemory(),
|
|
3089
3194
|
sandboxEnabled: this.settingsManager.getSandboxSettings().enabled ?? true,
|
|
3090
3195
|
sandboxNetworkMode: this.settingsManager.getSandboxSettings().networkMode
|
|
3091
3196
|
?? (this.settingsManager.getSandboxSettings().networkEnabled === true ? "allow" : this.settingsManager.getSandboxSettings().networkEnabled === false ? "deny" : "ask"),
|
|
@@ -3171,10 +3276,24 @@ export class InteractiveMode {
|
|
|
3171
3276
|
this.settingsManager.setCacheTimer(enabled);
|
|
3172
3277
|
this.showStatus(`Cache timer: ${enabled ? "enabled" : "disabled"}`);
|
|
3173
3278
|
},
|
|
3279
|
+
onPinLastPromptChange: (enabled) => {
|
|
3280
|
+
this.settingsManager.setPinLastPrompt(enabled);
|
|
3281
|
+
this.pinLastPromptEnabled = enabled;
|
|
3282
|
+
this.updatePinLastPromptWidget();
|
|
3283
|
+
this.showStatus(`Pin last prompt: ${enabled ? "enabled" : "disabled"}`);
|
|
3284
|
+
},
|
|
3174
3285
|
onRtkChange: (enabled) => {
|
|
3175
3286
|
this.settingsManager.setRtk(enabled);
|
|
3176
3287
|
this.showStatus(`RTK: ${enabled ? "enabled" : "disabled"} (restart required)`);
|
|
3177
3288
|
},
|
|
3289
|
+
onAutoDreamChange: (enabled) => {
|
|
3290
|
+
this.settingsManager.setAutoDream(enabled);
|
|
3291
|
+
this.showStatus(`Auto dream: ${enabled ? "enabled" : "disabled"}`);
|
|
3292
|
+
},
|
|
3293
|
+
onAutoMemoryChange: (enabled) => {
|
|
3294
|
+
this.settingsManager.setAutoMemory(enabled);
|
|
3295
|
+
this.showStatus(`Auto memory: ${enabled ? "enabled" : "disabled"}`);
|
|
3296
|
+
},
|
|
3178
3297
|
onSteeringModeChange: (mode) => {
|
|
3179
3298
|
this.session.setSteeringMode(mode);
|
|
3180
3299
|
},
|
|
@@ -3579,7 +3698,7 @@ export class InteractiveMode {
|
|
|
3579
3698
|
this.chatContainer.addChild(new Spacer(1));
|
|
3580
3699
|
summaryLoader = new Loader(
|
|
3581
3700
|
this.ui,
|
|
3582
|
-
(spinner) => theme.fg("
|
|
3701
|
+
(spinner) => theme.fg("text", spinner),
|
|
3583
3702
|
(text) => theme.fg("muted", text),
|
|
3584
3703
|
`Summarizing branch... (${appKey(this.keybindings, "interrupt")} to cancel)`,
|
|
3585
3704
|
);
|
|
@@ -4042,6 +4161,78 @@ export class InteractiveMode {
|
|
|
4042
4161
|
}
|
|
4043
4162
|
}
|
|
4044
4163
|
|
|
4164
|
+
private getAgentPtyComponent(sessionId: string): EmbeddedTerminalComponent | undefined {
|
|
4165
|
+
return this.agentPtyComponents.get(sessionId);
|
|
4166
|
+
}
|
|
4167
|
+
|
|
4168
|
+
private ensureAgentPtyComponent(sessionId: string, command?: string): EmbeddedTerminalComponent {
|
|
4169
|
+
let component = this.agentPtyComponents.get(sessionId);
|
|
4170
|
+
if (component) return component;
|
|
4171
|
+
|
|
4172
|
+
component = new EmbeddedTerminalComponent(
|
|
4173
|
+
command || `PTY ${sessionId}`,
|
|
4174
|
+
this.ui,
|
|
4175
|
+
this.settingsManager.getToolOutputMode(),
|
|
4176
|
+
appKey(this.keybindings, "terminalFocus"),
|
|
4177
|
+
false,
|
|
4178
|
+
);
|
|
4179
|
+
component.setStatusOverride(theme.fg("muted", "Agent-controlled terminal session"));
|
|
4180
|
+
component.setExpanded(true);
|
|
4181
|
+
this.chatContainer.addChild(component);
|
|
4182
|
+
this.agentPtyComponents.set(sessionId, component);
|
|
4183
|
+
return component;
|
|
4184
|
+
}
|
|
4185
|
+
|
|
4186
|
+
private updateAgentPtyComponent(
|
|
4187
|
+
sessionId: string,
|
|
4188
|
+
options?: { command?: string; screenText?: string; completed?: boolean; cancelled?: boolean; exitCode?: number },
|
|
4189
|
+
): void {
|
|
4190
|
+
const component = this.ensureAgentPtyComponent(sessionId, options?.command);
|
|
4191
|
+
if (options?.screenText !== undefined) {
|
|
4192
|
+
component.setScreenText(options.screenText);
|
|
4193
|
+
}
|
|
4194
|
+
if (options?.completed) {
|
|
4195
|
+
component.setStatusOverride(undefined);
|
|
4196
|
+
component.setComplete(options.exitCode, options.cancelled ?? false);
|
|
4197
|
+
} else {
|
|
4198
|
+
component.setStatusOverride(theme.fg("muted", "Agent-controlled terminal session"));
|
|
4199
|
+
}
|
|
4200
|
+
}
|
|
4201
|
+
|
|
4202
|
+
private clearAgentPtyComponents(): void {
|
|
4203
|
+
for (const [, component] of this.agentPtyComponents) {
|
|
4204
|
+
this.chatContainer.removeChild(component);
|
|
4205
|
+
}
|
|
4206
|
+
this.agentPtyComponents.clear();
|
|
4207
|
+
}
|
|
4208
|
+
|
|
4209
|
+
private focusEmbeddedTerminal(component: EmbeddedTerminalComponent): void {
|
|
4210
|
+
this.focusedEmbeddedTerminal = component;
|
|
4211
|
+
this.lastEmbeddedTerminalSize = undefined;
|
|
4212
|
+
this.ui.setFocus(component);
|
|
4213
|
+
component.invalidate();
|
|
4214
|
+
this.ui.requestRender();
|
|
4215
|
+
}
|
|
4216
|
+
|
|
4217
|
+
private releaseEmbeddedTerminalFocus(): void {
|
|
4218
|
+
const focused = this.focusedEmbeddedTerminal;
|
|
4219
|
+
this.focusedEmbeddedTerminal = undefined;
|
|
4220
|
+
this.lastEmbeddedTerminalSize = undefined;
|
|
4221
|
+
this.ui.setFocus(this.editor);
|
|
4222
|
+
focused?.invalidate();
|
|
4223
|
+
this.ui.requestRender();
|
|
4224
|
+
}
|
|
4225
|
+
|
|
4226
|
+
private toggleEmbeddedTerminalFocus(): void {
|
|
4227
|
+
if (this.focusedEmbeddedTerminal) {
|
|
4228
|
+
this.releaseEmbeddedTerminalFocus();
|
|
4229
|
+
return;
|
|
4230
|
+
}
|
|
4231
|
+
if (this.bashComponent instanceof EmbeddedTerminalComponent) {
|
|
4232
|
+
this.focusEmbeddedTerminal(this.bashComponent);
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
|
|
4045
4236
|
private async handleBashCommand(command: string, excludeFromContext = false, displayCommand?: string, loginShell?: boolean): Promise<void> {
|
|
4046
4237
|
const extensionRunner = this.session.extensionRunner;
|
|
4047
4238
|
const label = displayCommand || command;
|
|
@@ -4061,7 +4252,7 @@ export class InteractiveMode {
|
|
|
4061
4252
|
const result = eventResult.result;
|
|
4062
4253
|
|
|
4063
4254
|
// Create UI component for display
|
|
4064
|
-
|
|
4255
|
+
const component = new BashExecutionComponent(
|
|
4065
4256
|
label,
|
|
4066
4257
|
this.ui,
|
|
4067
4258
|
excludeFromContext,
|
|
@@ -4069,19 +4260,20 @@ export class InteractiveMode {
|
|
|
4069
4260
|
isRtkEnabled(),
|
|
4070
4261
|
result.sandboxed ?? false,
|
|
4071
4262
|
);
|
|
4072
|
-
this.bashComponent
|
|
4263
|
+
this.bashComponent = component;
|
|
4264
|
+
component.setExpanded(this.toolOutputExpanded);
|
|
4073
4265
|
if (this.session.isStreaming) {
|
|
4074
|
-
this.pendingMessagesContainer.addChild(
|
|
4075
|
-
this.pendingBashComponents.push(
|
|
4266
|
+
this.pendingMessagesContainer.addChild(component);
|
|
4267
|
+
this.pendingBashComponents.push(component);
|
|
4076
4268
|
} else {
|
|
4077
|
-
this.chatContainer.addChild(
|
|
4269
|
+
this.chatContainer.addChild(component);
|
|
4078
4270
|
}
|
|
4079
4271
|
|
|
4080
4272
|
// Show output and complete
|
|
4081
4273
|
if (result.output) {
|
|
4082
|
-
|
|
4274
|
+
component.appendOutput(result.output);
|
|
4083
4275
|
}
|
|
4084
|
-
|
|
4276
|
+
component.setComplete(
|
|
4085
4277
|
result.exitCode,
|
|
4086
4278
|
result.cancelled,
|
|
4087
4279
|
result.truncated ? ({ truncated: true, content: result.output } as TruncationResult) : undefined,
|
|
@@ -4098,7 +4290,60 @@ export class InteractiveMode {
|
|
|
4098
4290
|
|
|
4099
4291
|
// Normal execution path (possibly with custom operations)
|
|
4100
4292
|
const isDeferred = this.session.isStreaming;
|
|
4101
|
-
|
|
4293
|
+
const canUsePty = !eventResult?.operations && await isPtyAvailable();
|
|
4294
|
+
|
|
4295
|
+
if (canUsePty) {
|
|
4296
|
+
const component = new EmbeddedTerminalComponent(
|
|
4297
|
+
label,
|
|
4298
|
+
this.ui,
|
|
4299
|
+
this.settingsManager.getToolOutputMode(),
|
|
4300
|
+
appKey(this.keybindings, "terminalFocus"),
|
|
4301
|
+
excludeFromContext,
|
|
4302
|
+
);
|
|
4303
|
+
this.bashComponent = component;
|
|
4304
|
+
component.setExpanded(this.toolOutputExpanded);
|
|
4305
|
+
|
|
4306
|
+
if (isDeferred) {
|
|
4307
|
+
this.pendingMessagesContainer.addChild(component);
|
|
4308
|
+
this.pendingBashComponents.push(component);
|
|
4309
|
+
} else {
|
|
4310
|
+
this.chatContainer.addChild(component);
|
|
4311
|
+
}
|
|
4312
|
+
this.ui.requestRender();
|
|
4313
|
+
|
|
4314
|
+
try {
|
|
4315
|
+
const session = await this.session.executeBashInteractive(command, {
|
|
4316
|
+
onChunk: (chunk) => {
|
|
4317
|
+
component.appendOutput(chunk);
|
|
4318
|
+
this.ui.requestRender();
|
|
4319
|
+
},
|
|
4320
|
+
cols: Math.max(40, this.ui.terminal.columns - 2),
|
|
4321
|
+
rows: Math.max(10, this.ui.terminal.rows - 8),
|
|
4322
|
+
loginShell,
|
|
4323
|
+
});
|
|
4324
|
+
component.setHandle(session.handle, () => this.releaseEmbeddedTerminalFocus());
|
|
4325
|
+
if (!isDeferred) {
|
|
4326
|
+
this.focusEmbeddedTerminal(component);
|
|
4327
|
+
}
|
|
4328
|
+
|
|
4329
|
+
const result = await session.result;
|
|
4330
|
+
component.setComplete(result.exitCode, result.cancelled);
|
|
4331
|
+
this.session.recordBashResult(command, result, { excludeFromContext });
|
|
4332
|
+
} catch (error) {
|
|
4333
|
+
component.setComplete(undefined, false);
|
|
4334
|
+
this.showError(`Bash command failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
4335
|
+
} finally {
|
|
4336
|
+
if (this.focusedEmbeddedTerminal === component) {
|
|
4337
|
+
this.releaseEmbeddedTerminalFocus();
|
|
4338
|
+
}
|
|
4339
|
+
this.session.clearBashAbortController();
|
|
4340
|
+
this.bashComponent = undefined;
|
|
4341
|
+
this.ui.requestRender();
|
|
4342
|
+
}
|
|
4343
|
+
return;
|
|
4344
|
+
}
|
|
4345
|
+
|
|
4346
|
+
const fallbackComponent = new BashExecutionComponent(
|
|
4102
4347
|
label,
|
|
4103
4348
|
this.ui,
|
|
4104
4349
|
excludeFromContext,
|
|
@@ -4106,15 +4351,16 @@ export class InteractiveMode {
|
|
|
4106
4351
|
isRtkEnabled(),
|
|
4107
4352
|
false,
|
|
4108
4353
|
);
|
|
4109
|
-
this.bashComponent
|
|
4354
|
+
this.bashComponent = fallbackComponent;
|
|
4355
|
+
fallbackComponent.setExpanded(this.toolOutputExpanded);
|
|
4110
4356
|
|
|
4111
4357
|
if (isDeferred) {
|
|
4112
4358
|
// Show in pending area when agent is streaming
|
|
4113
|
-
this.pendingMessagesContainer.addChild(
|
|
4114
|
-
this.pendingBashComponents.push(
|
|
4359
|
+
this.pendingMessagesContainer.addChild(fallbackComponent);
|
|
4360
|
+
this.pendingBashComponents.push(fallbackComponent);
|
|
4115
4361
|
} else {
|
|
4116
4362
|
// Show in chat immediately when agent is idle
|
|
4117
|
-
this.chatContainer.addChild(
|
|
4363
|
+
this.chatContainer.addChild(fallbackComponent);
|
|
4118
4364
|
}
|
|
4119
4365
|
this.ui.requestRender();
|
|
4120
4366
|
|
|
@@ -4122,27 +4368,21 @@ export class InteractiveMode {
|
|
|
4122
4368
|
const result = await this.session.executeBash(
|
|
4123
4369
|
command,
|
|
4124
4370
|
(chunk) => {
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
this.ui.requestRender();
|
|
4128
|
-
}
|
|
4371
|
+
fallbackComponent.appendOutput(chunk);
|
|
4372
|
+
this.ui.requestRender();
|
|
4129
4373
|
},
|
|
4130
4374
|
{ excludeFromContext, operations: eventResult?.operations, loginShell },
|
|
4131
4375
|
);
|
|
4132
4376
|
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
);
|
|
4141
|
-
}
|
|
4377
|
+
fallbackComponent.setComplete(
|
|
4378
|
+
result.exitCode,
|
|
4379
|
+
result.cancelled,
|
|
4380
|
+
result.truncated ? ({ truncated: true, content: result.output } as TruncationResult) : undefined,
|
|
4381
|
+
result.fullOutputPath,
|
|
4382
|
+
result.sandboxed,
|
|
4383
|
+
);
|
|
4142
4384
|
} catch (error) {
|
|
4143
|
-
|
|
4144
|
-
this.bashComponent.setComplete(undefined, false);
|
|
4145
|
-
}
|
|
4385
|
+
fallbackComponent.setComplete(undefined, false);
|
|
4146
4386
|
this.showError(`Bash command failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
4147
4387
|
}
|
|
4148
4388
|
|
|
@@ -4170,7 +4410,7 @@ export class InteractiveMode {
|
|
|
4170
4410
|
const label = isAuto ? `Auto-compacting context... ${cancelHint}` : `Compacting context... ${cancelHint}`;
|
|
4171
4411
|
const compactingLoader = new Loader(
|
|
4172
4412
|
this.ui,
|
|
4173
|
-
(spinner) => theme.fg("
|
|
4413
|
+
(spinner) => theme.fg("text", spinner),
|
|
4174
4414
|
(text) => theme.fg("muted", text),
|
|
4175
4415
|
label,
|
|
4176
4416
|
);
|
|
@@ -4208,6 +4448,14 @@ export class InteractiveMode {
|
|
|
4208
4448
|
|
|
4209
4449
|
requestRender(force = false): void {
|
|
4210
4450
|
if (!this.isInitialized) return;
|
|
4451
|
+
if (this.focusedEmbeddedTerminal) {
|
|
4452
|
+
const cols = Math.max(40, this.ui.terminal.columns - 2);
|
|
4453
|
+
const rows = Math.max(10, this.ui.terminal.rows - 8);
|
|
4454
|
+
if (!this.lastEmbeddedTerminalSize || this.lastEmbeddedTerminalSize.cols !== cols || this.lastEmbeddedTerminalSize.rows !== rows) {
|
|
4455
|
+
this.focusedEmbeddedTerminal.resize(cols, rows);
|
|
4456
|
+
this.lastEmbeddedTerminalSize = { cols, rows };
|
|
4457
|
+
}
|
|
4458
|
+
}
|
|
4211
4459
|
this.ui.requestRender(force);
|
|
4212
4460
|
}
|
|
4213
4461
|
|
|
@@ -99,7 +99,7 @@ const dark: ThemeJson = {
|
|
|
99
99
|
thinkingHigh: "blueXhigh",
|
|
100
100
|
thinkingXhigh: "cyan",
|
|
101
101
|
|
|
102
|
-
bashMode: "
|
|
102
|
+
bashMode: "accent",
|
|
103
103
|
},
|
|
104
104
|
export: {
|
|
105
105
|
pageBg: "#18181e",
|
|
@@ -195,7 +195,7 @@ const light: ThemeJson = {
|
|
|
195
195
|
thinkingHigh: "blueHigh",
|
|
196
196
|
thinkingXhigh: "blueXhigh",
|
|
197
197
|
|
|
198
|
-
bashMode: "
|
|
198
|
+
bashMode: "accent",
|
|
199
199
|
},
|
|
200
200
|
export: {
|
|
201
201
|
pageBg: "#f8f8f8",
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import xtermPkg from "@xterm/headless";
|
|
2
|
+
|
|
3
|
+
const { Terminal } = xtermPkg;
|
|
4
|
+
|
|
5
|
+
export type HeadlessTerminal = InstanceType<typeof Terminal>;
|
|
6
|
+
|
|
7
|
+
export function createHeadlessTerminal(cols = 80, rows = 24, scrollback = 5000): HeadlessTerminal {
|
|
8
|
+
return new Terminal({
|
|
9
|
+
cols: Math.max(20, cols),
|
|
10
|
+
rows: Math.max(5, rows),
|
|
11
|
+
scrollback,
|
|
12
|
+
allowProposedApi: true,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function findLastContentLine(terminal: HeadlessTerminal, startLine = 0): number {
|
|
17
|
+
const buffer = terminal.buffer.active;
|
|
18
|
+
for (let i = buffer.length - 1; i >= startLine; i--) {
|
|
19
|
+
const line = buffer.getLine(i);
|
|
20
|
+
if (line && line.translateToString(true).length > 0) {
|
|
21
|
+
return i;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return -1;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function collectTerminalLines(terminal: HeadlessTerminal, startLine: number, endLineInclusive: number): string[] {
|
|
28
|
+
const buffer = terminal.buffer.active;
|
|
29
|
+
if (endLineInclusive < startLine) return [];
|
|
30
|
+
|
|
31
|
+
const lines: string[] = [];
|
|
32
|
+
for (let i = startLine; i <= endLineInclusive; i++) {
|
|
33
|
+
const line = buffer.getLine(i);
|
|
34
|
+
if (!line) {
|
|
35
|
+
lines.push("");
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let trimRight = true;
|
|
40
|
+
if (i + 1 <= endLineInclusive) {
|
|
41
|
+
const nextLine = buffer.getLine(i + 1);
|
|
42
|
+
if (nextLine?.isWrapped) {
|
|
43
|
+
trimRight = false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const lineContent = line.translateToString(trimRight);
|
|
48
|
+
if (line.isWrapped && lines.length > 0) {
|
|
49
|
+
lines[lines.length - 1] += lineContent;
|
|
50
|
+
} else {
|
|
51
|
+
lines.push(lineContent);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return lines;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function snapshotTerminalLines(terminal: HeadlessTerminal, startLine = 0): string[] {
|
|
59
|
+
const lastContentLine = findLastContentLine(terminal, startLine);
|
|
60
|
+
if (lastContentLine < startLine) return [];
|
|
61
|
+
return collectTerminalLines(terminal, startLine, lastContentLine);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function snapshotTerminalViewport(terminal: HeadlessTerminal): string[] {
|
|
65
|
+
const buffer = terminal.buffer.active;
|
|
66
|
+
const start = buffer.viewportY;
|
|
67
|
+
const end = Math.max(start, start + terminal.rows - 1);
|
|
68
|
+
return collectTerminalLines(terminal, start, end);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function snapshotTerminalViewportText(terminal: HeadlessTerminal): string {
|
|
72
|
+
return snapshotTerminalViewport(terminal).join("\n").replace(/[ \t]+$/gmu, "");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function snapshotTerminalBufferText(terminal: HeadlessTerminal): string {
|
|
76
|
+
return snapshotTerminalLines(terminal).join("\n");
|
|
77
|
+
}
|