@openeryc/pi-coding-agent 0.75.53 → 0.75.55
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/CHANGELOG.md +24 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-session.d.ts +4 -2
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +30 -30
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/mcp/client.d.ts +16 -7
- package/dist/core/mcp/client.d.ts.map +1 -1
- package/dist/core/mcp/client.js +135 -107
- package/dist/core/mcp/client.js.map +1 -1
- package/dist/core/mcp/manager.d.ts +12 -1
- package/dist/core/mcp/manager.d.ts.map +1 -1
- package/dist/core/mcp/manager.js +88 -34
- package/dist/core/mcp/manager.js.map +1 -1
- package/dist/core/mcp/types.d.ts +0 -13
- package/dist/core/mcp/types.d.ts.map +1 -1
- package/dist/core/mcp/types.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +3 -1
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +1 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +18 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +360 -427
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +5 -1
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/utils/sleep.d.ts.map +1 -1
- package/dist/utils/sleep.js +7 -3
- package/dist/utils/sleep.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
|
@@ -153,6 +153,8 @@ export class InteractiveMode {
|
|
|
153
153
|
hideThinkingBlock = false;
|
|
154
154
|
// Skill commands: command name -> skill file path
|
|
155
155
|
skillCommands = new Map();
|
|
156
|
+
/** Slash command dispatch map */
|
|
157
|
+
commands = new Map();
|
|
156
158
|
// Agent subscription unsubscribe function
|
|
157
159
|
unsubscribe;
|
|
158
160
|
signalCleanupHandlers = [];
|
|
@@ -478,6 +480,7 @@ export class InteractiveMode {
|
|
|
478
480
|
this.ui.setFocus(this.editor);
|
|
479
481
|
this.setupKeyHandlers();
|
|
480
482
|
this.setupEditorSubmitHandler();
|
|
483
|
+
this.initCommands();
|
|
481
484
|
// Start the UI before initializing extensions so session_start handlers can use interactive dialogs
|
|
482
485
|
this.ui.start();
|
|
483
486
|
this.isInitialized = true;
|
|
@@ -528,23 +531,29 @@ export class InteractiveMode {
|
|
|
528
531
|
await this.goalRunnerContinuationLoop(resumedGoal);
|
|
529
532
|
}
|
|
530
533
|
// Start version check asynchronously
|
|
531
|
-
checkForNewPiVersion(this.version)
|
|
534
|
+
checkForNewPiVersion(this.version)
|
|
535
|
+
.then((newRelease) => {
|
|
532
536
|
if (newRelease) {
|
|
533
537
|
this.showNewVersionNotification(newRelease);
|
|
534
538
|
}
|
|
535
|
-
})
|
|
539
|
+
})
|
|
540
|
+
.catch(() => { });
|
|
536
541
|
// Start package update check asynchronously
|
|
537
|
-
this.checkForPackageUpdates()
|
|
542
|
+
this.checkForPackageUpdates()
|
|
543
|
+
.then((updates) => {
|
|
538
544
|
if (updates.length > 0) {
|
|
539
545
|
this.showPackageUpdateNotification(updates);
|
|
540
546
|
}
|
|
541
|
-
})
|
|
547
|
+
})
|
|
548
|
+
.catch(() => { });
|
|
542
549
|
// Check tmux keyboard setup asynchronously
|
|
543
|
-
this.checkTmuxKeyboardSetup()
|
|
550
|
+
this.checkTmuxKeyboardSetup()
|
|
551
|
+
.then((warning) => {
|
|
544
552
|
if (warning) {
|
|
545
553
|
this.showWarning(warning);
|
|
546
554
|
}
|
|
547
|
-
})
|
|
555
|
+
})
|
|
556
|
+
.catch(() => { });
|
|
548
557
|
// Show startup warnings
|
|
549
558
|
const { migratedProviders, modelFallbackMessage, initialMessage, initialImages, initialMessages } = this.options;
|
|
550
559
|
if (migratedProviders && migratedProviders.length > 0) {
|
|
@@ -1999,6 +2008,60 @@ export class InteractiveMode {
|
|
|
1999
2008
|
// Silently ignore clipboard errors (may not have permission, etc.)
|
|
2000
2009
|
}
|
|
2001
2010
|
}
|
|
2011
|
+
initCommands() {
|
|
2012
|
+
const noArg = (fn) => async (_text) => {
|
|
2013
|
+
this.editor.setText("");
|
|
2014
|
+
await fn();
|
|
2015
|
+
};
|
|
2016
|
+
this.commands.set("/settings", noArg(() => this.showSettingsSelector()));
|
|
2017
|
+
this.commands.set("/scoped-models", noArg(() => this.showModelsSelector()));
|
|
2018
|
+
this.commands.set("/model", async (text) => {
|
|
2019
|
+
const s = text.startsWith("/model ") ? text.slice(7).trim() : undefined;
|
|
2020
|
+
this.editor.setText("");
|
|
2021
|
+
await this.handleModelCommand(s);
|
|
2022
|
+
});
|
|
2023
|
+
this.commands.set("/export", async (text) => {
|
|
2024
|
+
await this.handleExportCommand(text);
|
|
2025
|
+
this.editor.setText("");
|
|
2026
|
+
});
|
|
2027
|
+
this.commands.set("/import", async (text) => {
|
|
2028
|
+
await this.handleImportCommand(text);
|
|
2029
|
+
this.editor.setText("");
|
|
2030
|
+
});
|
|
2031
|
+
this.commands.set("/share", noArg(() => this.handleShareCommand()));
|
|
2032
|
+
this.commands.set("/copy", noArg(() => this.handleCopyCommand()));
|
|
2033
|
+
this.commands.set("/name", async (text) => {
|
|
2034
|
+
this.handleNameCommand(text);
|
|
2035
|
+
this.editor.setText("");
|
|
2036
|
+
});
|
|
2037
|
+
this.commands.set("/session", noArg(() => this.handleSessionCommand()));
|
|
2038
|
+
this.commands.set("/mcp", noArg(() => this.showMcpSelector()));
|
|
2039
|
+
this.commands.set("/skills", noArg(() => this.showSkillsSelector()));
|
|
2040
|
+
this.commands.set("/usage", noArg(() => this.handleUsageCommand()));
|
|
2041
|
+
this.commands.set("/memory", noArg(() => this.handleMemoryCommand()));
|
|
2042
|
+
this.commands.set("/changelog", noArg(() => this.handleChangelogCommand()));
|
|
2043
|
+
this.commands.set("/hotkeys", noArg(() => this.handleHotkeysCommand()));
|
|
2044
|
+
this.commands.set("/fork", noArg(() => this.showUserMessageSelector()));
|
|
2045
|
+
this.commands.set("/clone", noArg(() => this.handleCloneCommand()));
|
|
2046
|
+
this.commands.set("/tree", noArg(() => this.showTreeSelector()));
|
|
2047
|
+
this.commands.set("/login", noArg(() => this.showOAuthSelector("login")));
|
|
2048
|
+
this.commands.set("/logout", noArg(() => this.showOAuthSelector("logout")));
|
|
2049
|
+
this.commands.set("/new", noArg(() => this.handleClearCommand()));
|
|
2050
|
+
this.commands.set("/compact", async (text) => {
|
|
2051
|
+
const ci = text.startsWith("/compact ") ? text.slice(9).trim() : undefined;
|
|
2052
|
+
this.editor.setText("");
|
|
2053
|
+
await this.handleCompactCommand(ci);
|
|
2054
|
+
});
|
|
2055
|
+
this.commands.set("/cleanup", noArg(() => this.handleCleanupCommand()));
|
|
2056
|
+
this.commands.set("/reload", noArg(() => this.handleReloadCommand()));
|
|
2057
|
+
this.commands.set("/debug", noArg(() => this.handleDebugCommand()));
|
|
2058
|
+
this.commands.set("/arminsayshi", noArg(() => this.handleArminSaysHi()));
|
|
2059
|
+
this.commands.set("/dementedelves", noArg(() => this.handleDementedDelves()));
|
|
2060
|
+
this.commands.set("/daxnuts", noArg(() => this.handleDaxnuts()));
|
|
2061
|
+
this.commands.set("/resume", noArg(() => this.showSessionSelector()));
|
|
2062
|
+
this.commands.set("/update", noArg(() => this.handleUpdateCommand()));
|
|
2063
|
+
this.commands.set("/quit", noArg(() => this.shutdown()));
|
|
2064
|
+
}
|
|
2002
2065
|
setupEditorSubmitHandler() {
|
|
2003
2066
|
this.defaultEditor.onSubmit = async (text) => {
|
|
2004
2067
|
text = text.trim();
|
|
@@ -2006,171 +2069,22 @@ export class InteractiveMode {
|
|
|
2006
2069
|
return;
|
|
2007
2070
|
// Add to history before handling (so up/down can recall commands too)
|
|
2008
2071
|
this.editor.addToHistory?.(text);
|
|
2009
|
-
// Handle commands
|
|
2010
|
-
if (text
|
|
2011
|
-
this.showSettingsSelector();
|
|
2012
|
-
this.editor.setText("");
|
|
2013
|
-
return;
|
|
2014
|
-
}
|
|
2015
|
-
if (text === "/scoped-models") {
|
|
2016
|
-
this.editor.setText("");
|
|
2017
|
-
await this.showModelsSelector();
|
|
2018
|
-
return;
|
|
2019
|
-
}
|
|
2020
|
-
if (text === "/model" || text.startsWith("/model ")) {
|
|
2021
|
-
const searchTerm = text.startsWith("/model ") ? text.slice(7).trim() : undefined;
|
|
2022
|
-
this.editor.setText("");
|
|
2023
|
-
await this.handleModelCommand(searchTerm);
|
|
2024
|
-
return;
|
|
2025
|
-
}
|
|
2026
|
-
if (text === "/export" || text.startsWith("/export ")) {
|
|
2027
|
-
await this.handleExportCommand(text);
|
|
2028
|
-
this.editor.setText("");
|
|
2029
|
-
return;
|
|
2030
|
-
}
|
|
2031
|
-
if (text === "/import" || text.startsWith("/import ")) {
|
|
2032
|
-
await this.handleImportCommand(text);
|
|
2033
|
-
this.editor.setText("");
|
|
2034
|
-
return;
|
|
2035
|
-
}
|
|
2036
|
-
if (text === "/share") {
|
|
2037
|
-
await this.handleShareCommand();
|
|
2038
|
-
this.editor.setText("");
|
|
2039
|
-
return;
|
|
2040
|
-
}
|
|
2041
|
-
if (text === "/copy") {
|
|
2042
|
-
await this.handleCopyCommand();
|
|
2043
|
-
this.editor.setText("");
|
|
2044
|
-
return;
|
|
2045
|
-
}
|
|
2046
|
-
if (text === "/name" || text.startsWith("/name ")) {
|
|
2047
|
-
this.handleNameCommand(text);
|
|
2048
|
-
this.editor.setText("");
|
|
2049
|
-
return;
|
|
2050
|
-
}
|
|
2051
|
-
if (text === "/goal" || text.startsWith("/goal ")) {
|
|
2072
|
+
// Handle commands (with special case for /goal which continues to submit)
|
|
2073
|
+
if (text.startsWith("/goal")) {
|
|
2052
2074
|
const goal = text.replace(/^\/goal\s*/, "").trim();
|
|
2053
2075
|
this.handleGoalCommand(text);
|
|
2054
|
-
if (goal)
|
|
2055
|
-
text = goal; // Submit goal text as the user message, not /goal prefix
|
|
2056
|
-
// Don't return — let the normal submit flow run so the agent starts working
|
|
2057
|
-
}
|
|
2058
|
-
if (text === "/session") {
|
|
2059
|
-
this.handleSessionCommand();
|
|
2060
|
-
this.editor.setText("");
|
|
2061
|
-
return;
|
|
2062
|
-
}
|
|
2063
|
-
if (text === "/mcp") {
|
|
2064
|
-
this.editor.setText("");
|
|
2065
|
-
this.showMcpSelector();
|
|
2066
|
-
return;
|
|
2067
|
-
}
|
|
2068
|
-
if (text.startsWith("/mcp ")) {
|
|
2069
|
-
const name = text.slice(5).trim();
|
|
2070
|
-
this.editor.setText("");
|
|
2071
|
-
this.showMcpSelector(name);
|
|
2072
|
-
return;
|
|
2073
|
-
}
|
|
2074
|
-
if (text === "/skills") {
|
|
2075
|
-
this.editor.setText("");
|
|
2076
|
-
this.showSkillsSelector();
|
|
2077
|
-
return;
|
|
2078
|
-
}
|
|
2079
|
-
if (text === "/usage") {
|
|
2080
|
-
this.editor.setText("");
|
|
2081
|
-
await this.handleUsageCommand();
|
|
2082
|
-
return;
|
|
2083
|
-
}
|
|
2084
|
-
if (text === "/memory") {
|
|
2085
|
-
this.handleMemoryCommand();
|
|
2086
|
-
this.editor.setText("");
|
|
2087
|
-
return;
|
|
2088
|
-
}
|
|
2089
|
-
if (text === "/changelog") {
|
|
2090
|
-
this.handleChangelogCommand();
|
|
2091
|
-
this.editor.setText("");
|
|
2092
|
-
return;
|
|
2093
|
-
}
|
|
2094
|
-
if (text === "/hotkeys") {
|
|
2095
|
-
this.handleHotkeysCommand();
|
|
2096
|
-
this.editor.setText("");
|
|
2097
|
-
return;
|
|
2098
|
-
}
|
|
2099
|
-
if (text === "/fork") {
|
|
2100
|
-
this.showUserMessageSelector();
|
|
2101
|
-
this.editor.setText("");
|
|
2102
|
-
return;
|
|
2103
|
-
}
|
|
2104
|
-
if (text === "/clone") {
|
|
2105
|
-
this.editor.setText("");
|
|
2106
|
-
await this.handleCloneCommand();
|
|
2107
|
-
return;
|
|
2108
|
-
}
|
|
2109
|
-
if (text === "/tree") {
|
|
2110
|
-
this.showTreeSelector();
|
|
2111
|
-
this.editor.setText("");
|
|
2112
|
-
return;
|
|
2113
|
-
}
|
|
2114
|
-
if (text === "/login") {
|
|
2115
|
-
this.showOAuthSelector("login");
|
|
2116
|
-
this.editor.setText("");
|
|
2117
|
-
return;
|
|
2118
|
-
}
|
|
2119
|
-
if (text === "/logout") {
|
|
2120
|
-
this.showOAuthSelector("logout");
|
|
2121
|
-
this.editor.setText("");
|
|
2122
|
-
return;
|
|
2123
|
-
}
|
|
2124
|
-
if (text === "/new") {
|
|
2125
2076
|
this.editor.setText("");
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
}
|
|
2129
|
-
if (text === "/compact" || text.startsWith("/compact ")) {
|
|
2130
|
-
const customInstructions = text.startsWith("/compact ") ? text.slice(9).trim() : undefined;
|
|
2131
|
-
this.editor.setText("");
|
|
2132
|
-
await this.handleCompactCommand(customInstructions);
|
|
2133
|
-
return;
|
|
2134
|
-
}
|
|
2135
|
-
if (text === "/cleanup") {
|
|
2136
|
-
this.editor.setText("");
|
|
2137
|
-
await this.handleCleanupCommand();
|
|
2138
|
-
return;
|
|
2139
|
-
}
|
|
2140
|
-
if (text === "/reload") {
|
|
2141
|
-
this.editor.setText("");
|
|
2142
|
-
await this.handleReloadCommand();
|
|
2143
|
-
return;
|
|
2144
|
-
}
|
|
2145
|
-
if (text === "/debug") {
|
|
2146
|
-
this.handleDebugCommand();
|
|
2147
|
-
this.editor.setText("");
|
|
2148
|
-
return;
|
|
2149
|
-
}
|
|
2150
|
-
if (text === "/arminsayshi") {
|
|
2151
|
-
this.handleArminSaysHi();
|
|
2152
|
-
this.editor.setText("");
|
|
2153
|
-
return;
|
|
2154
|
-
}
|
|
2155
|
-
if (text === "/dementedelves") {
|
|
2156
|
-
this.handleDementedDelves();
|
|
2157
|
-
this.editor.setText("");
|
|
2158
|
-
return;
|
|
2159
|
-
}
|
|
2160
|
-
if (text === "/resume") {
|
|
2161
|
-
this.showSessionSelector();
|
|
2162
|
-
this.editor.setText("");
|
|
2163
|
-
return;
|
|
2164
|
-
}
|
|
2165
|
-
if (text === "/update") {
|
|
2166
|
-
this.editor.setText("");
|
|
2167
|
-
await this.handleUpdateCommand();
|
|
2168
|
-
return;
|
|
2077
|
+
if (goal)
|
|
2078
|
+
text = goal;
|
|
2169
2079
|
}
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2080
|
+
else {
|
|
2081
|
+
const spaceIdx = text.indexOf(" ");
|
|
2082
|
+
const cmd = spaceIdx === -1 ? text : text.slice(0, spaceIdx);
|
|
2083
|
+
const handler = this.commands.get(cmd);
|
|
2084
|
+
if (handler) {
|
|
2085
|
+
await handler(text);
|
|
2086
|
+
return;
|
|
2087
|
+
}
|
|
2174
2088
|
}
|
|
2175
2089
|
// Handle bash command (! for normal, !! for excluded from context)
|
|
2176
2090
|
if (text.startsWith("!")) {
|
|
@@ -2232,280 +2146,299 @@ export class InteractiveMode {
|
|
|
2232
2146
|
this.footer.invalidate();
|
|
2233
2147
|
switch (event.type) {
|
|
2234
2148
|
case "agent_start":
|
|
2235
|
-
this.
|
|
2236
|
-
if (this.settingsManager.getShowTerminalProgress()) {
|
|
2237
|
-
this.ui.terminal.setProgress(true);
|
|
2238
|
-
}
|
|
2239
|
-
// Restore main escape handler if retry handler is still active
|
|
2240
|
-
// (retry success event fires later, but we need main handler now)
|
|
2241
|
-
if (this.retryEscapeHandler) {
|
|
2242
|
-
this.defaultEditor.onEscape = this.retryEscapeHandler;
|
|
2243
|
-
this.retryEscapeHandler = undefined;
|
|
2244
|
-
}
|
|
2245
|
-
if (this.retryCountdown) {
|
|
2246
|
-
this.retryCountdown.dispose();
|
|
2247
|
-
this.retryCountdown = undefined;
|
|
2248
|
-
}
|
|
2249
|
-
if (this.retryLoader) {
|
|
2250
|
-
this.retryLoader.stop();
|
|
2251
|
-
this.retryLoader = undefined;
|
|
2252
|
-
}
|
|
2253
|
-
this.stopWorkingLoader();
|
|
2254
|
-
if (this.workingVisible) {
|
|
2255
|
-
this.loadingAnimation = this.createWorkingLoader();
|
|
2256
|
-
this.statusContainer.addChild(this.loadingAnimation);
|
|
2257
|
-
}
|
|
2258
|
-
this.ui.requestRender();
|
|
2149
|
+
this._handleAgentStart();
|
|
2259
2150
|
break;
|
|
2260
2151
|
case "queue_update":
|
|
2261
|
-
this.
|
|
2262
|
-
this.ui.requestRender();
|
|
2152
|
+
this._handleQueueUpdate();
|
|
2263
2153
|
break;
|
|
2264
2154
|
case "session_info_changed":
|
|
2265
|
-
this.
|
|
2266
|
-
this.footer.invalidate();
|
|
2267
|
-
this.ui.requestRender();
|
|
2155
|
+
this._handleSessionInfoChanged();
|
|
2268
2156
|
break;
|
|
2269
2157
|
case "thinking_level_changed":
|
|
2270
|
-
this.
|
|
2271
|
-
this.updateEditorBorderColor();
|
|
2158
|
+
this._handleThinkingLevelChanged();
|
|
2272
2159
|
break;
|
|
2273
2160
|
case "message_start":
|
|
2274
|
-
|
|
2275
|
-
this.addMessageToChat(event.message);
|
|
2276
|
-
this.ui.requestRender();
|
|
2277
|
-
}
|
|
2278
|
-
else if (event.message.role === "user") {
|
|
2279
|
-
this.addMessageToChat(event.message);
|
|
2280
|
-
this.updatePendingMessagesDisplay();
|
|
2281
|
-
this.ui.requestRender();
|
|
2282
|
-
}
|
|
2283
|
-
else if (event.message.role === "assistant") {
|
|
2284
|
-
this.streamingComponent = new AssistantMessageComponent(undefined, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel);
|
|
2285
|
-
this.streamingMessage = event.message;
|
|
2286
|
-
this.chatContainer.addChild(this.streamingComponent);
|
|
2287
|
-
this.streamingComponent.updateContent(this.streamingMessage);
|
|
2288
|
-
this.ui.requestRender();
|
|
2289
|
-
}
|
|
2161
|
+
this._handleMessageStart(event);
|
|
2290
2162
|
break;
|
|
2291
2163
|
case "message_update":
|
|
2292
|
-
|
|
2293
|
-
this.streamingMessage = event.message;
|
|
2294
|
-
this.streamingComponent.updateContent(this.streamingMessage);
|
|
2295
|
-
for (const content of this.streamingMessage.content) {
|
|
2296
|
-
if (content.type === "toolCall") {
|
|
2297
|
-
if (!this.pendingTools.has(content.id)) {
|
|
2298
|
-
const component = new ToolExecutionComponent(content.name, content.id, content.arguments, {
|
|
2299
|
-
showImages: this.settingsManager.getShowImages(),
|
|
2300
|
-
imageWidthCells: this.settingsManager.getImageWidthCells(),
|
|
2301
|
-
}, this.getRegisteredToolDefinition(content.name), this.ui, this.sessionManager.getCwd());
|
|
2302
|
-
component.setExpanded(this.toolOutputExpanded);
|
|
2303
|
-
this.chatContainer.addChild(component);
|
|
2304
|
-
this.pendingTools.set(content.id, component);
|
|
2305
|
-
}
|
|
2306
|
-
else {
|
|
2307
|
-
const component = this.pendingTools.get(content.id);
|
|
2308
|
-
if (component) {
|
|
2309
|
-
component.updateArgs(content.arguments);
|
|
2310
|
-
}
|
|
2311
|
-
}
|
|
2312
|
-
}
|
|
2313
|
-
}
|
|
2314
|
-
this.ui.requestRender();
|
|
2315
|
-
}
|
|
2164
|
+
this._handleMessageUpdate(event);
|
|
2316
2165
|
break;
|
|
2317
2166
|
case "message_end":
|
|
2318
|
-
|
|
2319
|
-
break;
|
|
2320
|
-
if (this.streamingComponent && event.message.role === "assistant") {
|
|
2321
|
-
this.streamingMessage = event.message;
|
|
2322
|
-
let errorMessage;
|
|
2323
|
-
if (this.streamingMessage.stopReason === "aborted") {
|
|
2324
|
-
const retryAttempt = this.session.retryAttempt;
|
|
2325
|
-
errorMessage =
|
|
2326
|
-
retryAttempt > 0
|
|
2327
|
-
? `Aborted after ${retryAttempt} retry attempt${retryAttempt > 1 ? "s" : ""}`
|
|
2328
|
-
: "Operation aborted";
|
|
2329
|
-
this.streamingMessage.errorMessage = errorMessage;
|
|
2330
|
-
}
|
|
2331
|
-
this.streamingComponent.updateContent(this.streamingMessage);
|
|
2332
|
-
if (this.streamingMessage.stopReason === "aborted" || this.streamingMessage.stopReason === "error") {
|
|
2333
|
-
if (!errorMessage) {
|
|
2334
|
-
errorMessage = this.streamingMessage.errorMessage || "Error";
|
|
2335
|
-
}
|
|
2336
|
-
for (const [, component] of this.pendingTools.entries()) {
|
|
2337
|
-
component.updateResult({
|
|
2338
|
-
content: [{ type: "text", text: errorMessage }],
|
|
2339
|
-
isError: true,
|
|
2340
|
-
});
|
|
2341
|
-
}
|
|
2342
|
-
this.pendingTools.clear();
|
|
2343
|
-
}
|
|
2344
|
-
else {
|
|
2345
|
-
// Args are now complete - trigger diff computation for edit tools
|
|
2346
|
-
for (const [, component] of this.pendingTools.entries()) {
|
|
2347
|
-
component.setArgsComplete();
|
|
2348
|
-
}
|
|
2349
|
-
}
|
|
2350
|
-
this.streamingComponent = undefined;
|
|
2351
|
-
this.streamingMessage = undefined;
|
|
2352
|
-
this.footer.invalidate();
|
|
2353
|
-
}
|
|
2354
|
-
this.ui.requestRender();
|
|
2167
|
+
this._handleMessageEnd(event);
|
|
2355
2168
|
break;
|
|
2356
|
-
case "tool_execution_start":
|
|
2357
|
-
|
|
2358
|
-
if (!component) {
|
|
2359
|
-
component = new ToolExecutionComponent(event.toolName, event.toolCallId, event.args, {
|
|
2360
|
-
showImages: this.settingsManager.getShowImages(),
|
|
2361
|
-
imageWidthCells: this.settingsManager.getImageWidthCells(),
|
|
2362
|
-
}, this.getRegisteredToolDefinition(event.toolName), this.ui, this.sessionManager.getCwd());
|
|
2363
|
-
component.setExpanded(this.toolOutputExpanded);
|
|
2364
|
-
this.chatContainer.addChild(component);
|
|
2365
|
-
this.pendingTools.set(event.toolCallId, component);
|
|
2366
|
-
}
|
|
2367
|
-
component.markExecutionStarted();
|
|
2368
|
-
this.ui.requestRender();
|
|
2169
|
+
case "tool_execution_start":
|
|
2170
|
+
this._handleToolExecutionStart(event);
|
|
2369
2171
|
break;
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
const component = this.pendingTools.get(event.toolCallId);
|
|
2373
|
-
if (component) {
|
|
2374
|
-
component.updateResult({ ...event.partialResult, isError: false }, true);
|
|
2375
|
-
this.ui.requestRender();
|
|
2376
|
-
}
|
|
2172
|
+
case "tool_execution_update":
|
|
2173
|
+
this._handleToolExecutionUpdate(event);
|
|
2377
2174
|
break;
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
const component = this.pendingTools.get(event.toolCallId);
|
|
2381
|
-
if (component) {
|
|
2382
|
-
component.updateResult({ ...event.result, isError: event.isError });
|
|
2383
|
-
this.pendingTools.delete(event.toolCallId);
|
|
2384
|
-
this.ui.requestRender();
|
|
2385
|
-
}
|
|
2175
|
+
case "tool_execution_end":
|
|
2176
|
+
this._handleToolExecutionEnd(event);
|
|
2386
2177
|
break;
|
|
2387
|
-
}
|
|
2388
2178
|
case "agent_end":
|
|
2389
|
-
|
|
2390
|
-
this.ui.terminal.setProgress(false);
|
|
2391
|
-
}
|
|
2392
|
-
if (this.loadingAnimation) {
|
|
2393
|
-
this.loadingAnimation.stop();
|
|
2394
|
-
this.loadingAnimation = undefined;
|
|
2395
|
-
this.statusContainer.clear();
|
|
2396
|
-
}
|
|
2397
|
-
if (this.streamingComponent) {
|
|
2398
|
-
this.chatContainer.removeChild(this.streamingComponent);
|
|
2399
|
-
this.streamingComponent = undefined;
|
|
2400
|
-
this.streamingMessage = undefined;
|
|
2401
|
-
}
|
|
2402
|
-
this.pendingTools.clear();
|
|
2403
|
-
await this.checkShutdownRequested();
|
|
2404
|
-
this.ui.requestRender();
|
|
2179
|
+
await this._handleAgentEnd();
|
|
2405
2180
|
break;
|
|
2406
|
-
case "compaction_start":
|
|
2407
|
-
|
|
2408
|
-
this.ui.terminal.setProgress(true);
|
|
2409
|
-
}
|
|
2410
|
-
// Keep editor active; submissions are queued during compaction.
|
|
2411
|
-
this.autoCompactionEscapeHandler = this.defaultEditor.onEscape;
|
|
2412
|
-
this.defaultEditor.onEscape = () => {
|
|
2413
|
-
this.session.abortCompaction();
|
|
2414
|
-
};
|
|
2415
|
-
this.statusContainer.clear();
|
|
2416
|
-
const cancelHint = `(${keyText("app.interrupt")} to cancel)`;
|
|
2417
|
-
const label = event.reason === "manual"
|
|
2418
|
-
? `Compacting context... ${cancelHint}`
|
|
2419
|
-
: `${event.reason === "overflow" ? "Context overflow detected, " : ""}Auto-compacting... ${cancelHint}`;
|
|
2420
|
-
this.autoCompactionLoader = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), label);
|
|
2421
|
-
this.statusContainer.addChild(this.autoCompactionLoader);
|
|
2422
|
-
this.ui.requestRender();
|
|
2181
|
+
case "compaction_start":
|
|
2182
|
+
this._handleCompactionStart(event);
|
|
2423
2183
|
break;
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
if (this.settingsManager.getShowTerminalProgress()) {
|
|
2427
|
-
this.ui.terminal.setProgress(false);
|
|
2428
|
-
}
|
|
2429
|
-
if (this.autoCompactionEscapeHandler) {
|
|
2430
|
-
this.defaultEditor.onEscape = this.autoCompactionEscapeHandler;
|
|
2431
|
-
this.autoCompactionEscapeHandler = undefined;
|
|
2432
|
-
}
|
|
2433
|
-
if (this.autoCompactionLoader) {
|
|
2434
|
-
this.autoCompactionLoader.stop();
|
|
2435
|
-
this.autoCompactionLoader = undefined;
|
|
2436
|
-
this.statusContainer.clear();
|
|
2437
|
-
}
|
|
2438
|
-
if (event.aborted) {
|
|
2439
|
-
if (event.reason === "manual") {
|
|
2440
|
-
this.showError("Compaction cancelled");
|
|
2441
|
-
}
|
|
2442
|
-
else {
|
|
2443
|
-
this.showStatus("Auto-compaction cancelled");
|
|
2444
|
-
}
|
|
2445
|
-
}
|
|
2446
|
-
else if (event.result) {
|
|
2447
|
-
this.chatContainer.clear();
|
|
2448
|
-
this.rebuildChatFromMessages();
|
|
2449
|
-
this.addMessageToChat(createCompactionSummaryMessage(event.result.summary, event.result.tokensBefore, new Date().toISOString()));
|
|
2450
|
-
this.footer.invalidate();
|
|
2451
|
-
}
|
|
2452
|
-
else if (event.errorMessage) {
|
|
2453
|
-
if (event.reason === "manual") {
|
|
2454
|
-
this.showError(event.errorMessage);
|
|
2455
|
-
}
|
|
2456
|
-
else {
|
|
2457
|
-
this.chatContainer.addChild(new Spacer(1));
|
|
2458
|
-
this.chatContainer.addChild(new Text(theme.fg("error", event.errorMessage), 1, 0));
|
|
2459
|
-
}
|
|
2460
|
-
}
|
|
2461
|
-
void this.flushCompactionQueue({ willRetry: event.willRetry });
|
|
2462
|
-
this.ui.requestRender();
|
|
2184
|
+
case "compaction_end":
|
|
2185
|
+
this._handleCompactionEnd(event);
|
|
2463
2186
|
break;
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
// Set up escape to abort retry
|
|
2467
|
-
this.retryEscapeHandler = this.defaultEditor.onEscape;
|
|
2468
|
-
this.defaultEditor.onEscape = () => {
|
|
2469
|
-
this.session.abortRetry();
|
|
2470
|
-
};
|
|
2471
|
-
// Show retry indicator
|
|
2472
|
-
this.statusContainer.clear();
|
|
2473
|
-
this.retryCountdown?.dispose();
|
|
2474
|
-
const retryMessage = (seconds) => `Retrying (${event.attempt}/${event.maxAttempts}) in ${seconds}s... (${keyText("app.interrupt")} to cancel)`;
|
|
2475
|
-
this.retryLoader = new Loader(this.ui, (spinner) => theme.fg("warning", spinner), (text) => theme.fg("muted", text), retryMessage(Math.ceil(event.delayMs / 1000)));
|
|
2476
|
-
this.retryCountdown = new CountdownTimer(event.delayMs, this.ui, (seconds) => {
|
|
2477
|
-
this.retryLoader?.setMessage(retryMessage(seconds));
|
|
2478
|
-
}, () => {
|
|
2479
|
-
this.retryCountdown = undefined;
|
|
2480
|
-
});
|
|
2481
|
-
this.statusContainer.addChild(this.retryLoader);
|
|
2482
|
-
this.ui.requestRender();
|
|
2187
|
+
case "auto_retry_start":
|
|
2188
|
+
this._handleAutoRetryStart(event);
|
|
2483
2189
|
break;
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
// Restore escape handler
|
|
2487
|
-
if (this.retryEscapeHandler) {
|
|
2488
|
-
this.defaultEditor.onEscape = this.retryEscapeHandler;
|
|
2489
|
-
this.retryEscapeHandler = undefined;
|
|
2490
|
-
}
|
|
2491
|
-
if (this.retryCountdown) {
|
|
2492
|
-
this.retryCountdown.dispose();
|
|
2493
|
-
this.retryCountdown = undefined;
|
|
2494
|
-
}
|
|
2495
|
-
// Stop loader
|
|
2496
|
-
if (this.retryLoader) {
|
|
2497
|
-
this.retryLoader.stop();
|
|
2498
|
-
this.retryLoader = undefined;
|
|
2499
|
-
this.statusContainer.clear();
|
|
2500
|
-
}
|
|
2501
|
-
// Show error only on final failure (success shows normal response)
|
|
2502
|
-
if (!event.success) {
|
|
2503
|
-
this.showError(`Retry failed after ${event.attempt} attempts: ${event.finalError || "Unknown error"}`);
|
|
2504
|
-
}
|
|
2505
|
-
this.ui.requestRender();
|
|
2190
|
+
case "auto_retry_end":
|
|
2191
|
+
this._handleAutoRetryEnd(event);
|
|
2506
2192
|
break;
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
_handleAgentStart() {
|
|
2196
|
+
this.pendingTools.clear();
|
|
2197
|
+
if (this.settingsManager.getShowTerminalProgress()) {
|
|
2198
|
+
this.ui.terminal.setProgress(true);
|
|
2199
|
+
}
|
|
2200
|
+
if (this.retryEscapeHandler) {
|
|
2201
|
+
this.defaultEditor.onEscape = this.retryEscapeHandler;
|
|
2202
|
+
this.retryEscapeHandler = undefined;
|
|
2203
|
+
}
|
|
2204
|
+
if (this.retryCountdown) {
|
|
2205
|
+
this.retryCountdown.dispose();
|
|
2206
|
+
this.retryCountdown = undefined;
|
|
2207
|
+
}
|
|
2208
|
+
if (this.retryLoader) {
|
|
2209
|
+
this.retryLoader.stop();
|
|
2210
|
+
this.retryLoader = undefined;
|
|
2211
|
+
}
|
|
2212
|
+
this.stopWorkingLoader();
|
|
2213
|
+
if (this.workingVisible) {
|
|
2214
|
+
this.loadingAnimation = this.createWorkingLoader();
|
|
2215
|
+
this.statusContainer.addChild(this.loadingAnimation);
|
|
2216
|
+
}
|
|
2217
|
+
this.ui.requestRender();
|
|
2218
|
+
}
|
|
2219
|
+
_handleQueueUpdate() {
|
|
2220
|
+
this.updatePendingMessagesDisplay();
|
|
2221
|
+
this.ui.requestRender();
|
|
2222
|
+
}
|
|
2223
|
+
_handleSessionInfoChanged() {
|
|
2224
|
+
this.updateTerminalTitle();
|
|
2225
|
+
this.footer.invalidate();
|
|
2226
|
+
this.ui.requestRender();
|
|
2227
|
+
}
|
|
2228
|
+
_handleThinkingLevelChanged() {
|
|
2229
|
+
this.footer.invalidate();
|
|
2230
|
+
this.updateEditorBorderColor();
|
|
2231
|
+
}
|
|
2232
|
+
_handleMessageStart(event) {
|
|
2233
|
+
if (event.message.role === "custom") {
|
|
2234
|
+
this.addMessageToChat(event.message);
|
|
2235
|
+
this.ui.requestRender();
|
|
2236
|
+
}
|
|
2237
|
+
else if (event.message.role === "user") {
|
|
2238
|
+
this.addMessageToChat(event.message);
|
|
2239
|
+
this.updatePendingMessagesDisplay();
|
|
2240
|
+
this.ui.requestRender();
|
|
2241
|
+
}
|
|
2242
|
+
else if (event.message.role === "assistant") {
|
|
2243
|
+
this.streamingComponent = new AssistantMessageComponent(undefined, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel);
|
|
2244
|
+
this.streamingMessage = event.message;
|
|
2245
|
+
this.chatContainer.addChild(this.streamingComponent);
|
|
2246
|
+
this.streamingComponent.updateContent(this.streamingMessage);
|
|
2247
|
+
this.ui.requestRender();
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
_handleMessageUpdate(event) {
|
|
2251
|
+
if (!this.streamingComponent || event.message.role !== "assistant")
|
|
2252
|
+
return;
|
|
2253
|
+
this.streamingMessage = event.message;
|
|
2254
|
+
this.streamingComponent.updateContent(this.streamingMessage);
|
|
2255
|
+
for (const content of this.streamingMessage.content) {
|
|
2256
|
+
if (content.type !== "toolCall")
|
|
2257
|
+
continue;
|
|
2258
|
+
if (!this.pendingTools.has(content.id)) {
|
|
2259
|
+
const component = new ToolExecutionComponent(content.name, content.id, content.arguments, {
|
|
2260
|
+
showImages: this.settingsManager.getShowImages(),
|
|
2261
|
+
imageWidthCells: this.settingsManager.getImageWidthCells(),
|
|
2262
|
+
}, this.getRegisteredToolDefinition(content.name), this.ui, this.sessionManager.getCwd());
|
|
2263
|
+
component.setExpanded(this.toolOutputExpanded);
|
|
2264
|
+
this.chatContainer.addChild(component);
|
|
2265
|
+
this.pendingTools.set(content.id, component);
|
|
2266
|
+
}
|
|
2267
|
+
else {
|
|
2268
|
+
this.pendingTools.get(content.id)?.updateArgs(content.arguments);
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
this.ui.requestRender();
|
|
2272
|
+
}
|
|
2273
|
+
_handleMessageEnd(event) {
|
|
2274
|
+
if (event.message.role === "user")
|
|
2275
|
+
return;
|
|
2276
|
+
if (!this.streamingComponent || event.message.role !== "assistant")
|
|
2277
|
+
return;
|
|
2278
|
+
this.streamingMessage = event.message;
|
|
2279
|
+
let errorMessage;
|
|
2280
|
+
if (this.streamingMessage.stopReason === "aborted") {
|
|
2281
|
+
const retryAttempt = this.session.retryAttempt;
|
|
2282
|
+
errorMessage =
|
|
2283
|
+
retryAttempt > 0
|
|
2284
|
+
? `Aborted after ${retryAttempt} retry attempt${retryAttempt > 1 ? "s" : ""}`
|
|
2285
|
+
: "Operation aborted";
|
|
2286
|
+
this.streamingMessage.errorMessage = errorMessage;
|
|
2287
|
+
}
|
|
2288
|
+
this.streamingComponent.updateContent(this.streamingMessage);
|
|
2289
|
+
if (this.streamingMessage.stopReason === "aborted" || this.streamingMessage.stopReason === "error") {
|
|
2290
|
+
const msg = errorMessage || this.streamingMessage.errorMessage || "Error";
|
|
2291
|
+
for (const [, component] of this.pendingTools.entries()) {
|
|
2292
|
+
component.updateResult({ content: [{ type: "text", text: msg }], isError: true });
|
|
2293
|
+
}
|
|
2294
|
+
this.pendingTools.clear();
|
|
2295
|
+
}
|
|
2296
|
+
else {
|
|
2297
|
+
for (const [, component] of this.pendingTools.entries()) {
|
|
2298
|
+
component.setArgsComplete();
|
|
2507
2299
|
}
|
|
2508
2300
|
}
|
|
2301
|
+
this.streamingComponent = undefined;
|
|
2302
|
+
this.streamingMessage = undefined;
|
|
2303
|
+
this.footer.invalidate();
|
|
2304
|
+
this.ui.requestRender();
|
|
2305
|
+
}
|
|
2306
|
+
_handleToolExecutionStart(event) {
|
|
2307
|
+
let component = this.pendingTools.get(event.toolCallId);
|
|
2308
|
+
if (!component) {
|
|
2309
|
+
component = new ToolExecutionComponent(event.toolName, event.toolCallId, event.args, {
|
|
2310
|
+
showImages: this.settingsManager.getShowImages(),
|
|
2311
|
+
imageWidthCells: this.settingsManager.getImageWidthCells(),
|
|
2312
|
+
}, this.getRegisteredToolDefinition(event.toolName), this.ui, this.sessionManager.getCwd());
|
|
2313
|
+
component.setExpanded(this.toolOutputExpanded);
|
|
2314
|
+
this.chatContainer.addChild(component);
|
|
2315
|
+
this.pendingTools.set(event.toolCallId, component);
|
|
2316
|
+
}
|
|
2317
|
+
component.markExecutionStarted();
|
|
2318
|
+
this.ui.requestRender();
|
|
2319
|
+
}
|
|
2320
|
+
_handleToolExecutionUpdate(event) {
|
|
2321
|
+
const component = this.pendingTools.get(event.toolCallId);
|
|
2322
|
+
if (component) {
|
|
2323
|
+
component.updateResult({ ...event.partialResult, isError: false }, true);
|
|
2324
|
+
this.ui.requestRender();
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
_handleToolExecutionEnd(event) {
|
|
2328
|
+
const component = this.pendingTools.get(event.toolCallId);
|
|
2329
|
+
if (component) {
|
|
2330
|
+
component.updateResult({ ...event.result, isError: event.isError });
|
|
2331
|
+
this.pendingTools.delete(event.toolCallId);
|
|
2332
|
+
this.ui.requestRender();
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
async _handleAgentEnd() {
|
|
2336
|
+
if (this.settingsManager.getShowTerminalProgress()) {
|
|
2337
|
+
this.ui.terminal.setProgress(false);
|
|
2338
|
+
}
|
|
2339
|
+
if (this.loadingAnimation) {
|
|
2340
|
+
this.loadingAnimation.stop();
|
|
2341
|
+
this.loadingAnimation = undefined;
|
|
2342
|
+
this.statusContainer.clear();
|
|
2343
|
+
}
|
|
2344
|
+
if (this.streamingComponent) {
|
|
2345
|
+
this.chatContainer.removeChild(this.streamingComponent);
|
|
2346
|
+
this.streamingComponent = undefined;
|
|
2347
|
+
this.streamingMessage = undefined;
|
|
2348
|
+
}
|
|
2349
|
+
this.pendingTools.clear();
|
|
2350
|
+
await this.checkShutdownRequested();
|
|
2351
|
+
this.ui.requestRender();
|
|
2352
|
+
}
|
|
2353
|
+
_handleCompactionStart(event) {
|
|
2354
|
+
if (this.settingsManager.getShowTerminalProgress()) {
|
|
2355
|
+
this.ui.terminal.setProgress(true);
|
|
2356
|
+
}
|
|
2357
|
+
this.autoCompactionEscapeHandler = this.defaultEditor.onEscape;
|
|
2358
|
+
this.defaultEditor.onEscape = () => {
|
|
2359
|
+
this.session.abortCompaction();
|
|
2360
|
+
};
|
|
2361
|
+
this.statusContainer.clear();
|
|
2362
|
+
const cancelHint = `(${keyText("app.interrupt")} to cancel)`;
|
|
2363
|
+
const label = event.reason === "manual"
|
|
2364
|
+
? `Compacting context... ${cancelHint}`
|
|
2365
|
+
: `${event.reason === "overflow" ? "Context overflow detected, " : ""}Auto-compacting... ${cancelHint}`;
|
|
2366
|
+
this.autoCompactionLoader = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), label);
|
|
2367
|
+
this.statusContainer.addChild(this.autoCompactionLoader);
|
|
2368
|
+
this.ui.requestRender();
|
|
2369
|
+
}
|
|
2370
|
+
_handleCompactionEnd(event) {
|
|
2371
|
+
if (this.settingsManager.getShowTerminalProgress()) {
|
|
2372
|
+
this.ui.terminal.setProgress(false);
|
|
2373
|
+
}
|
|
2374
|
+
if (this.autoCompactionEscapeHandler) {
|
|
2375
|
+
this.defaultEditor.onEscape = this.autoCompactionEscapeHandler;
|
|
2376
|
+
this.autoCompactionEscapeHandler = undefined;
|
|
2377
|
+
}
|
|
2378
|
+
if (this.autoCompactionLoader) {
|
|
2379
|
+
this.autoCompactionLoader.stop();
|
|
2380
|
+
this.autoCompactionLoader = undefined;
|
|
2381
|
+
this.statusContainer.clear();
|
|
2382
|
+
}
|
|
2383
|
+
if (event.aborted) {
|
|
2384
|
+
if (event.reason === "manual") {
|
|
2385
|
+
this.showError("Compaction cancelled");
|
|
2386
|
+
}
|
|
2387
|
+
else {
|
|
2388
|
+
this.showStatus("Auto-compaction cancelled");
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
else if (event.result) {
|
|
2392
|
+
this.chatContainer.clear();
|
|
2393
|
+
this.rebuildChatFromMessages();
|
|
2394
|
+
this.addMessageToChat(createCompactionSummaryMessage(event.result.summary, event.result.tokensBefore, new Date().toISOString()));
|
|
2395
|
+
this.footer.invalidate();
|
|
2396
|
+
}
|
|
2397
|
+
else if (event.errorMessage) {
|
|
2398
|
+
if (event.reason === "manual") {
|
|
2399
|
+
this.showError(event.errorMessage);
|
|
2400
|
+
}
|
|
2401
|
+
else {
|
|
2402
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2403
|
+
this.chatContainer.addChild(new Text(theme.fg("error", event.errorMessage), 1, 0));
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
void this.flushCompactionQueue({ willRetry: event.willRetry });
|
|
2407
|
+
this.ui.requestRender();
|
|
2408
|
+
}
|
|
2409
|
+
_handleAutoRetryStart(event) {
|
|
2410
|
+
this.retryEscapeHandler = this.defaultEditor.onEscape;
|
|
2411
|
+
this.defaultEditor.onEscape = () => {
|
|
2412
|
+
this.session.abortRetry();
|
|
2413
|
+
};
|
|
2414
|
+
this.statusContainer.clear();
|
|
2415
|
+
this.retryCountdown?.dispose();
|
|
2416
|
+
const retryMessage = (seconds) => `Retrying (${event.attempt}/${event.maxAttempts}) in ${seconds}s... (${keyText("app.interrupt")} to cancel)`;
|
|
2417
|
+
this.retryLoader = new Loader(this.ui, (spinner) => theme.fg("warning", spinner), (text) => theme.fg("muted", text), retryMessage(Math.ceil(event.delayMs / 1000)));
|
|
2418
|
+
this.retryCountdown = new CountdownTimer(event.delayMs, this.ui, (seconds) => this.retryLoader?.setMessage(retryMessage(seconds)), () => {
|
|
2419
|
+
this.retryCountdown = undefined;
|
|
2420
|
+
});
|
|
2421
|
+
this.statusContainer.addChild(this.retryLoader);
|
|
2422
|
+
this.ui.requestRender();
|
|
2423
|
+
}
|
|
2424
|
+
_handleAutoRetryEnd(event) {
|
|
2425
|
+
if (this.retryEscapeHandler) {
|
|
2426
|
+
this.defaultEditor.onEscape = this.retryEscapeHandler;
|
|
2427
|
+
this.retryEscapeHandler = undefined;
|
|
2428
|
+
}
|
|
2429
|
+
if (this.retryCountdown) {
|
|
2430
|
+
this.retryCountdown.dispose();
|
|
2431
|
+
this.retryCountdown = undefined;
|
|
2432
|
+
}
|
|
2433
|
+
if (this.retryLoader) {
|
|
2434
|
+
this.retryLoader.stop();
|
|
2435
|
+
this.retryLoader = undefined;
|
|
2436
|
+
this.statusContainer.clear();
|
|
2437
|
+
}
|
|
2438
|
+
if (!event.success) {
|
|
2439
|
+
this.showError(`Retry failed after ${event.attempt} attempts: ${event.finalError || "Unknown error"}`);
|
|
2440
|
+
}
|
|
2441
|
+
this.ui.requestRender();
|
|
2509
2442
|
}
|
|
2510
2443
|
/** Extract text content from a user message */
|
|
2511
2444
|
getUserMessageText(message) {
|
|
@@ -4612,7 +4545,7 @@ export class InteractiveMode {
|
|
|
4612
4545
|
this.chatContainer.addChild(new Text(info, 1, 0));
|
|
4613
4546
|
this.ui.requestRender();
|
|
4614
4547
|
}
|
|
4615
|
-
showMcpSelector(highlightServer) {
|
|
4548
|
+
async showMcpSelector(highlightServer) {
|
|
4616
4549
|
const buildItems = () => {
|
|
4617
4550
|
// Fetch fresh config each time so toggle reflects immediately
|
|
4618
4551
|
const configured = this.settingsManager.getMcpServers();
|
|
@@ -4635,22 +4568,22 @@ export class InteractiveMode {
|
|
|
4635
4568
|
};
|
|
4636
4569
|
});
|
|
4637
4570
|
};
|
|
4638
|
-
const items = buildItems();
|
|
4639
|
-
if (items.length === 0) {
|
|
4640
|
-
this.chatContainer.addChild(new Spacer(1));
|
|
4641
|
-
this.chatContainer.addChild(new Text(`${theme.bold("MCP Servers")}\n\n${theme.fg("dim", "No MCP servers configured. Add 'mcpServers' to settings.json.")}`, 1, 0));
|
|
4642
|
-
this.ui.requestRender();
|
|
4643
|
-
return;
|
|
4644
|
-
}
|
|
4645
4571
|
if (highlightServer) {
|
|
4646
4572
|
const configured = this.settingsManager.getMcpServers();
|
|
4647
4573
|
const server = configured?.[highlightServer];
|
|
4648
4574
|
if (server) {
|
|
4649
4575
|
const currentEnabled = server.enabled !== false;
|
|
4650
4576
|
this.settingsManager.setMcpServerEnabled(highlightServer, !currentEnabled);
|
|
4651
|
-
this.session.reloadMcp();
|
|
4577
|
+
await this.session.reloadMcp(highlightServer);
|
|
4652
4578
|
}
|
|
4653
4579
|
}
|
|
4580
|
+
const items = buildItems();
|
|
4581
|
+
if (items.length === 0) {
|
|
4582
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
4583
|
+
this.chatContainer.addChild(new Text(`${theme.bold("MCP Servers")}\n\n${theme.fg("dim", "No MCP servers configured. Add 'mcpServers' to settings.json.")}`, 1, 0));
|
|
4584
|
+
this.ui.requestRender();
|
|
4585
|
+
return;
|
|
4586
|
+
}
|
|
4654
4587
|
this.showSelector((done) => {
|
|
4655
4588
|
const selector = new McpSelectorComponent("MCP Servers", items, async (name) => {
|
|
4656
4589
|
const cur = this.settingsManager.getMcpServers();
|
|
@@ -4658,7 +4591,7 @@ export class InteractiveMode {
|
|
|
4658
4591
|
if (!server)
|
|
4659
4592
|
return;
|
|
4660
4593
|
this.settingsManager.setMcpServerEnabled(name, server.enabled === false);
|
|
4661
|
-
await this.session.reloadMcp();
|
|
4594
|
+
await this.session.reloadMcp(name);
|
|
4662
4595
|
selector.setItems(buildItems());
|
|
4663
4596
|
}, () => done());
|
|
4664
4597
|
return { component: selector, focus: selector };
|