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
|
@@ -18,12 +18,14 @@ import { resolveModelScope } from "../../core/model-resolver.js";
|
|
|
18
18
|
import { SessionManager } from "../../core/session-manager.js";
|
|
19
19
|
import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
|
|
20
20
|
import { getPermissionMode, setPermissionMode, setFileChangeApprovalHandler, setClassifierHandler, setNetworkApprovalHandler, } from "../../core/tool-approval.js";
|
|
21
|
+
import { isPtyAvailable } from "../../core/pty-executor.js";
|
|
21
22
|
import { getChangelogPath, getNewEntries, parseChangelog } from "../../utils/changelog.js";
|
|
22
23
|
import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
|
|
23
24
|
import { resetStdinForTui } from "../../utils/reset-stdin.js";
|
|
24
25
|
import { ensureTool } from "../../utils/tools-manager.js";
|
|
25
26
|
import { AssistantMessageComponent } from "./components/assistant-message.js";
|
|
26
27
|
import { BashExecutionComponent } from "./components/bash-execution.js";
|
|
28
|
+
import { EmbeddedTerminalComponent } from "./components/embedded-terminal.js";
|
|
27
29
|
import { BorderedLoader } from "./components/bordered-loader.js";
|
|
28
30
|
import { BranchSummaryMessageComponent } from "./components/branch-summary-message.js";
|
|
29
31
|
import { CompactionSummaryMessageComponent } from "./components/compaction-summary-message.js";
|
|
@@ -79,7 +81,24 @@ export class InteractiveMode {
|
|
|
79
81
|
this.isInitialized = false;
|
|
80
82
|
this.loadingAnimation = undefined;
|
|
81
83
|
this.pendingWorkingMessage = undefined;
|
|
82
|
-
this.defaultWorkingMessage = "
|
|
84
|
+
this.defaultWorkingMessage = "Cooking…";
|
|
85
|
+
this.workingMessages = [
|
|
86
|
+
"Cooking…",
|
|
87
|
+
"Vibing…",
|
|
88
|
+
"Brainworming…",
|
|
89
|
+
"Scheming…",
|
|
90
|
+
"Spelunking…",
|
|
91
|
+
"Manifesting…",
|
|
92
|
+
"Yak shaving…",
|
|
93
|
+
"Grokking…",
|
|
94
|
+
"Pondering…",
|
|
95
|
+
"Going deep…",
|
|
96
|
+
"Caffeinating…",
|
|
97
|
+
"On a mission…",
|
|
98
|
+
"Consulting the void…",
|
|
99
|
+
"Doing the thing…",
|
|
100
|
+
"Almost maybe…",
|
|
101
|
+
];
|
|
83
102
|
this.lastSigintTime = 0;
|
|
84
103
|
this.lastEscapeTime = 0;
|
|
85
104
|
this.changelogMarkdown = undefined;
|
|
@@ -91,10 +110,14 @@ export class InteractiveMode {
|
|
|
91
110
|
this.streamingMessage = undefined;
|
|
92
111
|
// Tool execution tracking: toolCallId -> component
|
|
93
112
|
this.pendingTools = new Map();
|
|
113
|
+
this.agentPtyComponents = new Map();
|
|
94
114
|
// Tool output expansion state
|
|
95
115
|
this.toolOutputExpanded = false;
|
|
96
116
|
// Thinking block visibility state
|
|
97
117
|
this.hideThinkingBlock = false;
|
|
118
|
+
// Pin last prompt feature
|
|
119
|
+
this.pinLastPromptEnabled = false;
|
|
120
|
+
this.lastSentPromptText = undefined;
|
|
98
121
|
// Skill commands: command name -> skill file path
|
|
99
122
|
this.skillCommands = new Map();
|
|
100
123
|
// Track if editor is in bash mode (text starts with !)
|
|
@@ -157,6 +180,8 @@ export class InteractiveMode {
|
|
|
157
180
|
this.footerDataProvider.setSandboxStatus("disabled");
|
|
158
181
|
// Load hide thinking block setting
|
|
159
182
|
this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
|
|
183
|
+
// Load pin last prompt setting
|
|
184
|
+
this.pinLastPromptEnabled = this.settingsManager.getPinLastPrompt();
|
|
160
185
|
// Register themes from resource loader and initialize
|
|
161
186
|
setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
|
|
162
187
|
initTheme(this.settingsManager.getTheme(), true, this.settingsManager.getThemeAccent());
|
|
@@ -419,6 +444,7 @@ export class InteractiveMode {
|
|
|
419
444
|
// Main interactive loop
|
|
420
445
|
while (true) {
|
|
421
446
|
const userInput = await this.getUserInput();
|
|
447
|
+
this.recordLastSentPrompt(userInput);
|
|
422
448
|
try {
|
|
423
449
|
await this.session.prompt(userInput);
|
|
424
450
|
}
|
|
@@ -1085,7 +1111,7 @@ export class InteractiveMode {
|
|
|
1085
1111
|
this.defaultEditor.onExtensionShortcut = undefined;
|
|
1086
1112
|
this.updateTerminalTitle();
|
|
1087
1113
|
if (this.loadingAnimation) {
|
|
1088
|
-
this.loadingAnimation.
|
|
1114
|
+
this.loadingAnimation.setCycleMessages(this.workingMessages);
|
|
1089
1115
|
}
|
|
1090
1116
|
}
|
|
1091
1117
|
// Maximum total widget lines to prevent viewport overflow
|
|
@@ -1093,6 +1119,41 @@ export class InteractiveMode {
|
|
|
1093
1119
|
/**
|
|
1094
1120
|
* Render all extension widgets to the widget container.
|
|
1095
1121
|
*/
|
|
1122
|
+
/**
|
|
1123
|
+
* Record the last user-sent prompt text and refresh the widget.
|
|
1124
|
+
* Called from the main loop and input-controller paths.
|
|
1125
|
+
*/
|
|
1126
|
+
recordLastSentPrompt(text) {
|
|
1127
|
+
// Ignore slash commands and bash commands — they're not "topic" prompts
|
|
1128
|
+
const trimmed = text.trim();
|
|
1129
|
+
if (trimmed.startsWith("/") || trimmed.startsWith("!"))
|
|
1130
|
+
return;
|
|
1131
|
+
this.lastSentPromptText = trimmed;
|
|
1132
|
+
this.updatePinLastPromptWidget();
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Update (or remove) the "pin last prompt" widget above the editor.
|
|
1136
|
+
* Uses the built-in extension widget slot with key "__pin-last-prompt__".
|
|
1137
|
+
*/
|
|
1138
|
+
updatePinLastPromptWidget() {
|
|
1139
|
+
const key = "__pin-last-prompt__";
|
|
1140
|
+
if (!this.pinLastPromptEnabled || !this.lastSentPromptText) {
|
|
1141
|
+
// Remove widget
|
|
1142
|
+
const existing = this.extensionWidgetsAbove.get(key);
|
|
1143
|
+
if (existing?.dispose)
|
|
1144
|
+
existing.dispose();
|
|
1145
|
+
this.extensionWidgetsAbove.delete(key);
|
|
1146
|
+
}
|
|
1147
|
+
else {
|
|
1148
|
+
// Truncate to one line (max 200 chars) to keep it compact
|
|
1149
|
+
const raw = this.lastSentPromptText.replace(/[\r\n]+/g, " ").trim();
|
|
1150
|
+
const label = raw.length > 200 ? raw.slice(0, 197) + "…" : raw;
|
|
1151
|
+
const container = new Container();
|
|
1152
|
+
container.addChild(new Text(theme.fg("dim", ` last prompt: ${label}`), 0, 0));
|
|
1153
|
+
this.extensionWidgetsAbove.set(key, container);
|
|
1154
|
+
}
|
|
1155
|
+
this.renderWidgets();
|
|
1156
|
+
}
|
|
1096
1157
|
renderWidgets() {
|
|
1097
1158
|
if (!this.widgetContainerAbove || !this.widgetContainerBelow)
|
|
1098
1159
|
return;
|
|
@@ -1253,6 +1314,12 @@ export class InteractiveMode {
|
|
|
1253
1314
|
const result = await this.showExtensionSelector(`${title}\n${message}`, ["Yes", "No"], opts);
|
|
1254
1315
|
return result === "Yes";
|
|
1255
1316
|
}
|
|
1317
|
+
/** Reset the loading animation back to cycling working messages. */
|
|
1318
|
+
resetLoadingMessage() {
|
|
1319
|
+
if (this.loadingAnimation) {
|
|
1320
|
+
this.loadingAnimation.setCycleMessages(this.workingMessages);
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1256
1323
|
_registerApprovalHandlers() {
|
|
1257
1324
|
const classifierService = new ClassifierService(this.session.modelRegistry.authStorage);
|
|
1258
1325
|
setFileChangeApprovalHandler(async (request) => {
|
|
@@ -1260,9 +1327,7 @@ export class InteractiveMode {
|
|
|
1260
1327
|
const actionLabel = request.action.toUpperCase();
|
|
1261
1328
|
const title = `Allow ${actionLabel}: ${request.path}\n${request.message}`;
|
|
1262
1329
|
const approved = await this._showApprovalConfirm(title);
|
|
1263
|
-
|
|
1264
|
-
this.loadingAnimation.setMessage(this.defaultWorkingMessage);
|
|
1265
|
-
}
|
|
1330
|
+
this.resetLoadingMessage();
|
|
1266
1331
|
return approved;
|
|
1267
1332
|
});
|
|
1268
1333
|
setClassifierHandler(async (request) => {
|
|
@@ -1299,25 +1364,19 @@ export class InteractiveMode {
|
|
|
1299
1364
|
sessionId: this.session.sessionId,
|
|
1300
1365
|
});
|
|
1301
1366
|
if (decision.approved) {
|
|
1302
|
-
|
|
1303
|
-
this.loadingAnimation.setMessage(this.defaultWorkingMessage);
|
|
1304
|
-
}
|
|
1367
|
+
this.resetLoadingMessage();
|
|
1305
1368
|
return true;
|
|
1306
1369
|
}
|
|
1307
1370
|
this.loadingAnimation?.setMessage("Waiting for approval…");
|
|
1308
1371
|
const title = `Classifier ${decision.source === "rule" ? "blocked" : "did not approve"} ${request.toolName}\n${decision.reason}\n${argsPreview}`;
|
|
1309
1372
|
const approved = await this._showApprovalConfirm(title);
|
|
1310
|
-
|
|
1311
|
-
this.loadingAnimation.setMessage(this.defaultWorkingMessage);
|
|
1312
|
-
}
|
|
1373
|
+
this.resetLoadingMessage();
|
|
1313
1374
|
return approved;
|
|
1314
1375
|
});
|
|
1315
1376
|
setNetworkApprovalHandler(async (request) => {
|
|
1316
1377
|
this.loadingAnimation?.setMessage("Waiting for network approval…");
|
|
1317
1378
|
const result = await this.showExtensionSelector(`${request.message}\n${request.command}`, ["Allow once", "Allow for session", "Deny"]);
|
|
1318
|
-
|
|
1319
|
-
this.loadingAnimation.setMessage(this.defaultWorkingMessage);
|
|
1320
|
-
}
|
|
1379
|
+
this.resetLoadingMessage();
|
|
1321
1380
|
if (result === "Allow once")
|
|
1322
1381
|
return "allow-once";
|
|
1323
1382
|
if (result === "Allow for session")
|
|
@@ -1606,6 +1665,7 @@ export class InteractiveMode {
|
|
|
1606
1665
|
this.defaultEditor.onAction("cycleModelBackward", () => this.cycleModel("backward"));
|
|
1607
1666
|
this.defaultEditor.onAction("cyclePermissionMode", () => this.cyclePermissionMode());
|
|
1608
1667
|
this.defaultEditor.onAction("showHotkeys", () => showHotkeys(this.getSlashCommandContext()));
|
|
1668
|
+
this.defaultEditor.onAction("terminalFocus", () => this.toggleEmbeddedTerminalFocus());
|
|
1609
1669
|
// Global debug handler on TUI (works regardless of focus)
|
|
1610
1670
|
this.ui.onDebug = () => this.handleDebugCommand();
|
|
1611
1671
|
this.defaultEditor.onAction("selectModel", () => this.showModelSelector());
|
|
@@ -1921,7 +1981,7 @@ export class InteractiveMode {
|
|
|
1921
1981
|
break;
|
|
1922
1982
|
}
|
|
1923
1983
|
case "assistant": {
|
|
1924
|
-
const assistantComponent = new AssistantMessageComponent(message, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.settingsManager.getTimestampFormat());
|
|
1984
|
+
const assistantComponent = new AssistantMessageComponent(message, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.settingsManager.getTimestampFormat(), this.session.thinkingLevel || "off");
|
|
1925
1985
|
this.chatContainer.addChild(assistantComponent);
|
|
1926
1986
|
break;
|
|
1927
1987
|
}
|
|
@@ -2225,8 +2285,37 @@ export class InteractiveMode {
|
|
|
2225
2285
|
for (const component of this.pendingBashComponents) {
|
|
2226
2286
|
component.setExpanded(expanded);
|
|
2227
2287
|
}
|
|
2288
|
+
this.updateEditorExpandHint();
|
|
2228
2289
|
this.ui.requestRender();
|
|
2229
2290
|
}
|
|
2291
|
+
/** Append/remove the "ctrl+o to expand" hint in the editor bottom border. */
|
|
2292
|
+
updateEditorExpandHint() {
|
|
2293
|
+
const expandKey = appKey(this.keybindings, "expandTools");
|
|
2294
|
+
const collapseHint = `${theme.fg("dim", expandKey)}${theme.fg("muted", " collapse")}`;
|
|
2295
|
+
const expandHint = `${theme.fg("dim", expandKey)}${theme.fg("muted", " : verbose")}`;
|
|
2296
|
+
// The base hint set during agent_start
|
|
2297
|
+
const enterKey = theme.fg("dim", "↵");
|
|
2298
|
+
const followUpKey = theme.fg("dim", appKey(this.keybindings, "followUp"));
|
|
2299
|
+
const steerLabel = theme.fg("muted", " steer");
|
|
2300
|
+
const queueLabel = theme.fg("muted", " queue");
|
|
2301
|
+
const baseHint = `${enterKey}${steerLabel} ${followUpKey}${queueLabel}`;
|
|
2302
|
+
// Check if there are any expandable tool outputs in the chat
|
|
2303
|
+
const hasToolOutputs = this.chatContainer.children.some(isExpandable) ||
|
|
2304
|
+
!!this.bashComponent ||
|
|
2305
|
+
this.pendingBashComponents.length > 0;
|
|
2306
|
+
const activeHint = this.toolOutputExpanded ? collapseHint : expandHint;
|
|
2307
|
+
if (this.loadingAnimation) {
|
|
2308
|
+
// Agent is running — always show expand/collapse hint when there are tool outputs
|
|
2309
|
+
this.defaultEditor.bottomHint = hasToolOutputs
|
|
2310
|
+
? `${baseHint} ${activeHint}`
|
|
2311
|
+
: baseHint;
|
|
2312
|
+
}
|
|
2313
|
+
else if (hasToolOutputs) {
|
|
2314
|
+
// Idle — show expand/collapse hint so user knows ctrl+o works
|
|
2315
|
+
this.defaultEditor.bottomHint = activeHint;
|
|
2316
|
+
}
|
|
2317
|
+
// If no tool outputs and idle, leave bottomHint as-is (cleared by agent_end)
|
|
2318
|
+
}
|
|
2230
2319
|
toggleThinkingBlockVisibility() {
|
|
2231
2320
|
this.hideThinkingBlock = !this.hideThinkingBlock;
|
|
2232
2321
|
this.settingsManager.setHideThinkingBlock(this.hideThinkingBlock);
|
|
@@ -2517,6 +2606,7 @@ export class InteractiveMode {
|
|
|
2517
2606
|
enableSkillCommands: this.settingsManager.getEnableSkillCommands(),
|
|
2518
2607
|
codexRotate: this.settingsManager.getCodexRotate(),
|
|
2519
2608
|
cacheTimer: this.settingsManager.getCacheTimer(),
|
|
2609
|
+
pinLastPrompt: this.settingsManager.getPinLastPrompt(),
|
|
2520
2610
|
steeringMode: this.session.steeringMode,
|
|
2521
2611
|
followUpMode: this.session.followUpMode,
|
|
2522
2612
|
transport: this.settingsManager.getTransport(),
|
|
@@ -2540,6 +2630,8 @@ export class InteractiveMode {
|
|
|
2540
2630
|
toolOutputMode: this.settingsManager.getToolOutputMode(),
|
|
2541
2631
|
rtk: this.settingsManager.getRtk(),
|
|
2542
2632
|
editorScheme: this.settingsManager.getEditorScheme(),
|
|
2633
|
+
autoDream: this.settingsManager.getAutoDream(),
|
|
2634
|
+
autoMemory: this.settingsManager.getAutoMemory(),
|
|
2543
2635
|
sandboxEnabled: this.settingsManager.getSandboxSettings().enabled ?? true,
|
|
2544
2636
|
sandboxNetworkMode: this.settingsManager.getSandboxSettings().networkMode
|
|
2545
2637
|
?? (this.settingsManager.getSandboxSettings().networkEnabled === true ? "allow" : this.settingsManager.getSandboxSettings().networkEnabled === false ? "deny" : "ask"),
|
|
@@ -2593,10 +2685,24 @@ export class InteractiveMode {
|
|
|
2593
2685
|
this.settingsManager.setCacheTimer(enabled);
|
|
2594
2686
|
this.showStatus(`Cache timer: ${enabled ? "enabled" : "disabled"}`);
|
|
2595
2687
|
},
|
|
2688
|
+
onPinLastPromptChange: (enabled) => {
|
|
2689
|
+
this.settingsManager.setPinLastPrompt(enabled);
|
|
2690
|
+
this.pinLastPromptEnabled = enabled;
|
|
2691
|
+
this.updatePinLastPromptWidget();
|
|
2692
|
+
this.showStatus(`Pin last prompt: ${enabled ? "enabled" : "disabled"}`);
|
|
2693
|
+
},
|
|
2596
2694
|
onRtkChange: (enabled) => {
|
|
2597
2695
|
this.settingsManager.setRtk(enabled);
|
|
2598
2696
|
this.showStatus(`RTK: ${enabled ? "enabled" : "disabled"} (restart required)`);
|
|
2599
2697
|
},
|
|
2698
|
+
onAutoDreamChange: (enabled) => {
|
|
2699
|
+
this.settingsManager.setAutoDream(enabled);
|
|
2700
|
+
this.showStatus(`Auto dream: ${enabled ? "enabled" : "disabled"}`);
|
|
2701
|
+
},
|
|
2702
|
+
onAutoMemoryChange: (enabled) => {
|
|
2703
|
+
this.settingsManager.setAutoMemory(enabled);
|
|
2704
|
+
this.showStatus(`Auto memory: ${enabled ? "enabled" : "disabled"}`);
|
|
2705
|
+
},
|
|
2600
2706
|
onSteeringModeChange: (mode) => {
|
|
2601
2707
|
this.session.setSteeringMode(mode);
|
|
2602
2708
|
},
|
|
@@ -2949,7 +3055,7 @@ export class InteractiveMode {
|
|
|
2949
3055
|
this.session.abortBranchSummary();
|
|
2950
3056
|
};
|
|
2951
3057
|
this.chatContainer.addChild(new Spacer(1));
|
|
2952
|
-
summaryLoader = new Loader(this.ui, (spinner) => theme.fg("
|
|
3058
|
+
summaryLoader = new Loader(this.ui, (spinner) => theme.fg("text", spinner), (text) => theme.fg("muted", text), `Summarizing branch... (${appKey(this.keybindings, "interrupt")} to cancel)`);
|
|
2953
3059
|
this.statusContainer.addChild(summaryLoader);
|
|
2954
3060
|
this.ui.requestRender();
|
|
2955
3061
|
}
|
|
@@ -3344,6 +3450,63 @@ export class InteractiveMode {
|
|
|
3344
3450
|
this.handleDaxnuts();
|
|
3345
3451
|
}
|
|
3346
3452
|
}
|
|
3453
|
+
getAgentPtyComponent(sessionId) {
|
|
3454
|
+
return this.agentPtyComponents.get(sessionId);
|
|
3455
|
+
}
|
|
3456
|
+
ensureAgentPtyComponent(sessionId, command) {
|
|
3457
|
+
let component = this.agentPtyComponents.get(sessionId);
|
|
3458
|
+
if (component)
|
|
3459
|
+
return component;
|
|
3460
|
+
component = new EmbeddedTerminalComponent(command || `PTY ${sessionId}`, this.ui, this.settingsManager.getToolOutputMode(), appKey(this.keybindings, "terminalFocus"), false);
|
|
3461
|
+
component.setStatusOverride(theme.fg("muted", "Agent-controlled terminal session"));
|
|
3462
|
+
component.setExpanded(true);
|
|
3463
|
+
this.chatContainer.addChild(component);
|
|
3464
|
+
this.agentPtyComponents.set(sessionId, component);
|
|
3465
|
+
return component;
|
|
3466
|
+
}
|
|
3467
|
+
updateAgentPtyComponent(sessionId, options) {
|
|
3468
|
+
const component = this.ensureAgentPtyComponent(sessionId, options?.command);
|
|
3469
|
+
if (options?.screenText !== undefined) {
|
|
3470
|
+
component.setScreenText(options.screenText);
|
|
3471
|
+
}
|
|
3472
|
+
if (options?.completed) {
|
|
3473
|
+
component.setStatusOverride(undefined);
|
|
3474
|
+
component.setComplete(options.exitCode, options.cancelled ?? false);
|
|
3475
|
+
}
|
|
3476
|
+
else {
|
|
3477
|
+
component.setStatusOverride(theme.fg("muted", "Agent-controlled terminal session"));
|
|
3478
|
+
}
|
|
3479
|
+
}
|
|
3480
|
+
clearAgentPtyComponents() {
|
|
3481
|
+
for (const [, component] of this.agentPtyComponents) {
|
|
3482
|
+
this.chatContainer.removeChild(component);
|
|
3483
|
+
}
|
|
3484
|
+
this.agentPtyComponents.clear();
|
|
3485
|
+
}
|
|
3486
|
+
focusEmbeddedTerminal(component) {
|
|
3487
|
+
this.focusedEmbeddedTerminal = component;
|
|
3488
|
+
this.lastEmbeddedTerminalSize = undefined;
|
|
3489
|
+
this.ui.setFocus(component);
|
|
3490
|
+
component.invalidate();
|
|
3491
|
+
this.ui.requestRender();
|
|
3492
|
+
}
|
|
3493
|
+
releaseEmbeddedTerminalFocus() {
|
|
3494
|
+
const focused = this.focusedEmbeddedTerminal;
|
|
3495
|
+
this.focusedEmbeddedTerminal = undefined;
|
|
3496
|
+
this.lastEmbeddedTerminalSize = undefined;
|
|
3497
|
+
this.ui.setFocus(this.editor);
|
|
3498
|
+
focused?.invalidate();
|
|
3499
|
+
this.ui.requestRender();
|
|
3500
|
+
}
|
|
3501
|
+
toggleEmbeddedTerminalFocus() {
|
|
3502
|
+
if (this.focusedEmbeddedTerminal) {
|
|
3503
|
+
this.releaseEmbeddedTerminalFocus();
|
|
3504
|
+
return;
|
|
3505
|
+
}
|
|
3506
|
+
if (this.bashComponent instanceof EmbeddedTerminalComponent) {
|
|
3507
|
+
this.focusEmbeddedTerminal(this.bashComponent);
|
|
3508
|
+
}
|
|
3509
|
+
}
|
|
3347
3510
|
async handleBashCommand(command, excludeFromContext = false, displayCommand, loginShell) {
|
|
3348
3511
|
const extensionRunner = this.session.extensionRunner;
|
|
3349
3512
|
const label = displayCommand || command;
|
|
@@ -3360,20 +3523,21 @@ export class InteractiveMode {
|
|
|
3360
3523
|
if (eventResult?.result) {
|
|
3361
3524
|
const result = eventResult.result;
|
|
3362
3525
|
// Create UI component for display
|
|
3363
|
-
|
|
3364
|
-
this.bashComponent
|
|
3526
|
+
const component = new BashExecutionComponent(label, this.ui, excludeFromContext, this.settingsManager.getToolOutputMode(), isRtkEnabled(), result.sandboxed ?? false);
|
|
3527
|
+
this.bashComponent = component;
|
|
3528
|
+
component.setExpanded(this.toolOutputExpanded);
|
|
3365
3529
|
if (this.session.isStreaming) {
|
|
3366
|
-
this.pendingMessagesContainer.addChild(
|
|
3367
|
-
this.pendingBashComponents.push(
|
|
3530
|
+
this.pendingMessagesContainer.addChild(component);
|
|
3531
|
+
this.pendingBashComponents.push(component);
|
|
3368
3532
|
}
|
|
3369
3533
|
else {
|
|
3370
|
-
this.chatContainer.addChild(
|
|
3534
|
+
this.chatContainer.addChild(component);
|
|
3371
3535
|
}
|
|
3372
3536
|
// Show output and complete
|
|
3373
3537
|
if (result.output) {
|
|
3374
|
-
|
|
3538
|
+
component.appendOutput(result.output);
|
|
3375
3539
|
}
|
|
3376
|
-
|
|
3540
|
+
component.setComplete(result.exitCode, result.cancelled, result.truncated ? { truncated: true, content: result.output } : undefined, result.fullOutputPath, result.sandboxed);
|
|
3377
3541
|
// Record the result in session
|
|
3378
3542
|
this.session.recordBashResult(command, result, { excludeFromContext });
|
|
3379
3543
|
this.bashComponent = undefined;
|
|
@@ -3382,33 +3546,73 @@ export class InteractiveMode {
|
|
|
3382
3546
|
}
|
|
3383
3547
|
// Normal execution path (possibly with custom operations)
|
|
3384
3548
|
const isDeferred = this.session.isStreaming;
|
|
3385
|
-
|
|
3386
|
-
|
|
3549
|
+
const canUsePty = !eventResult?.operations && await isPtyAvailable();
|
|
3550
|
+
if (canUsePty) {
|
|
3551
|
+
const component = new EmbeddedTerminalComponent(label, this.ui, this.settingsManager.getToolOutputMode(), appKey(this.keybindings, "terminalFocus"), excludeFromContext);
|
|
3552
|
+
this.bashComponent = component;
|
|
3553
|
+
component.setExpanded(this.toolOutputExpanded);
|
|
3554
|
+
if (isDeferred) {
|
|
3555
|
+
this.pendingMessagesContainer.addChild(component);
|
|
3556
|
+
this.pendingBashComponents.push(component);
|
|
3557
|
+
}
|
|
3558
|
+
else {
|
|
3559
|
+
this.chatContainer.addChild(component);
|
|
3560
|
+
}
|
|
3561
|
+
this.ui.requestRender();
|
|
3562
|
+
try {
|
|
3563
|
+
const session = await this.session.executeBashInteractive(command, {
|
|
3564
|
+
onChunk: (chunk) => {
|
|
3565
|
+
component.appendOutput(chunk);
|
|
3566
|
+
this.ui.requestRender();
|
|
3567
|
+
},
|
|
3568
|
+
cols: Math.max(40, this.ui.terminal.columns - 2),
|
|
3569
|
+
rows: Math.max(10, this.ui.terminal.rows - 8),
|
|
3570
|
+
loginShell,
|
|
3571
|
+
});
|
|
3572
|
+
component.setHandle(session.handle, () => this.releaseEmbeddedTerminalFocus());
|
|
3573
|
+
if (!isDeferred) {
|
|
3574
|
+
this.focusEmbeddedTerminal(component);
|
|
3575
|
+
}
|
|
3576
|
+
const result = await session.result;
|
|
3577
|
+
component.setComplete(result.exitCode, result.cancelled);
|
|
3578
|
+
this.session.recordBashResult(command, result, { excludeFromContext });
|
|
3579
|
+
}
|
|
3580
|
+
catch (error) {
|
|
3581
|
+
component.setComplete(undefined, false);
|
|
3582
|
+
this.showError(`Bash command failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3583
|
+
}
|
|
3584
|
+
finally {
|
|
3585
|
+
if (this.focusedEmbeddedTerminal === component) {
|
|
3586
|
+
this.releaseEmbeddedTerminalFocus();
|
|
3587
|
+
}
|
|
3588
|
+
this.session.clearBashAbortController();
|
|
3589
|
+
this.bashComponent = undefined;
|
|
3590
|
+
this.ui.requestRender();
|
|
3591
|
+
}
|
|
3592
|
+
return;
|
|
3593
|
+
}
|
|
3594
|
+
const fallbackComponent = new BashExecutionComponent(label, this.ui, excludeFromContext, this.settingsManager.getToolOutputMode(), isRtkEnabled(), false);
|
|
3595
|
+
this.bashComponent = fallbackComponent;
|
|
3596
|
+
fallbackComponent.setExpanded(this.toolOutputExpanded);
|
|
3387
3597
|
if (isDeferred) {
|
|
3388
3598
|
// Show in pending area when agent is streaming
|
|
3389
|
-
this.pendingMessagesContainer.addChild(
|
|
3390
|
-
this.pendingBashComponents.push(
|
|
3599
|
+
this.pendingMessagesContainer.addChild(fallbackComponent);
|
|
3600
|
+
this.pendingBashComponents.push(fallbackComponent);
|
|
3391
3601
|
}
|
|
3392
3602
|
else {
|
|
3393
3603
|
// Show in chat immediately when agent is idle
|
|
3394
|
-
this.chatContainer.addChild(
|
|
3604
|
+
this.chatContainer.addChild(fallbackComponent);
|
|
3395
3605
|
}
|
|
3396
3606
|
this.ui.requestRender();
|
|
3397
3607
|
try {
|
|
3398
3608
|
const result = await this.session.executeBash(command, (chunk) => {
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
this.ui.requestRender();
|
|
3402
|
-
}
|
|
3609
|
+
fallbackComponent.appendOutput(chunk);
|
|
3610
|
+
this.ui.requestRender();
|
|
3403
3611
|
}, { excludeFromContext, operations: eventResult?.operations, loginShell });
|
|
3404
|
-
|
|
3405
|
-
this.bashComponent.setComplete(result.exitCode, result.cancelled, result.truncated ? { truncated: true, content: result.output } : undefined, result.fullOutputPath, result.sandboxed);
|
|
3406
|
-
}
|
|
3612
|
+
fallbackComponent.setComplete(result.exitCode, result.cancelled, result.truncated ? { truncated: true, content: result.output } : undefined, result.fullOutputPath, result.sandboxed);
|
|
3407
3613
|
}
|
|
3408
3614
|
catch (error) {
|
|
3409
|
-
|
|
3410
|
-
this.bashComponent.setComplete(undefined, false);
|
|
3411
|
-
}
|
|
3615
|
+
fallbackComponent.setComplete(undefined, false);
|
|
3412
3616
|
this.showError(`Bash command failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3413
3617
|
}
|
|
3414
3618
|
this.bashComponent = undefined;
|
|
@@ -3430,7 +3634,7 @@ export class InteractiveMode {
|
|
|
3430
3634
|
this.chatContainer.addChild(new Spacer(1));
|
|
3431
3635
|
const cancelHint = `(${appKey(this.keybindings, "interrupt")} to cancel)`;
|
|
3432
3636
|
const label = isAuto ? `Auto-compacting context... ${cancelHint}` : `Compacting context... ${cancelHint}`;
|
|
3433
|
-
const compactingLoader = new Loader(this.ui, (spinner) => theme.fg("
|
|
3637
|
+
const compactingLoader = new Loader(this.ui, (spinner) => theme.fg("text", spinner), (text) => theme.fg("muted", text), label);
|
|
3434
3638
|
this.statusContainer.addChild(compactingLoader);
|
|
3435
3639
|
this.ui.requestRender();
|
|
3436
3640
|
let result;
|
|
@@ -3463,6 +3667,14 @@ export class InteractiveMode {
|
|
|
3463
3667
|
requestRender(force = false) {
|
|
3464
3668
|
if (!this.isInitialized)
|
|
3465
3669
|
return;
|
|
3670
|
+
if (this.focusedEmbeddedTerminal) {
|
|
3671
|
+
const cols = Math.max(40, this.ui.terminal.columns - 2);
|
|
3672
|
+
const rows = Math.max(10, this.ui.terminal.rows - 8);
|
|
3673
|
+
if (!this.lastEmbeddedTerminalSize || this.lastEmbeddedTerminalSize.cols !== cols || this.lastEmbeddedTerminalSize.rows !== rows) {
|
|
3674
|
+
this.focusedEmbeddedTerminal.resize(cols, rows);
|
|
3675
|
+
this.lastEmbeddedTerminalSize = { cols, rows };
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3466
3678
|
this.ui.requestRender(force);
|
|
3467
3679
|
}
|
|
3468
3680
|
stop() {
|