@ai-agent-forge/agent-forge 0.86.4 → 0.86.5
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 +52 -0
- package/dist/bundle/.build-manifest.json +470 -404
- package/dist/bundle/chunks/anthropic-messages-ALDZBUKI.js +6 -0
- package/dist/bundle/chunks/anthropic.js +2 -2
- package/dist/bundle/chunks/{azure-openai-responses-WQCZWFAH.js → azure-openai-responses-A2WJWZFB.js} +1 -1
- package/dist/bundle/chunks/bedrock-converse-stream.js +1 -1
- package/dist/bundle/chunks/chunk-A7GUOX2H.js +141 -0
- package/dist/bundle/chunks/{chunk-WGLBM2BP.js → chunk-CIX3FSBK.js} +1 -1
- package/dist/bundle/chunks/{chunk-ZV3W36HK.js → chunk-EALJ5DDX.js} +99 -92
- package/dist/bundle/chunks/chunk-OO6DJ3CL.js +31 -0
- package/dist/bundle/chunks/{chunk-PE3XUHPT.js → chunk-SQFG22WN.js} +1 -1
- package/dist/bundle/chunks/{chunk-V46XUTK3.js → chunk-Y7HXTASS.js} +1 -1
- package/dist/bundle/chunks/{chunk-3WE3W52L.js → chunk-YJCQVXW5.js} +1 -1
- package/dist/bundle/chunks/github-copilot.js +1 -1
- package/dist/bundle/chunks/{google-generative-ai-LQ375GYC.js → google-generative-ai-NIB4WONV.js} +1 -1
- package/dist/bundle/chunks/{google-vertex-HXSRAKEZ.js → google-vertex-DCPYUEUO.js} +1 -1
- package/dist/bundle/chunks/{mistral-conversations-CSNMFR4J.js → mistral-conversations-OC6LA5XL.js} +1 -1
- package/dist/bundle/chunks/{openai-codex-responses-YDJSZOIH.js → openai-codex-responses-IO2X4Y7L.js} +1 -1
- package/dist/bundle/chunks/openai-codex.js +2 -2
- package/dist/bundle/chunks/{openai-completions-UPRWTQPT.js → openai-completions-XYQQHETU.js} +1 -1
- package/dist/bundle/chunks/{openai-responses-O63W6HQ3.js → openai-responses-DLIRN3CI.js} +1 -1
- package/dist/bundle/chunks/{openrouter-images-G3GT5BVT.js → openrouter-images-G4GQRUSG.js} +1 -1
- package/dist/bundle/chunks/openrouter.js +2 -2
- package/dist/bundle/chunks/{pi-messages-SLF355GF.js → pi-messages-E6RRYBO7.js} +1 -1
- package/dist/bundle/chunks/radius.js +2 -2
- package/dist/bundle/chunks/{virtual-modules-O72ICTMQ.js → virtual-modules-3IZ4MYSQ.js} +1 -1
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/capabilities/builtin.d.ts +4 -2
- package/dist/capabilities/builtin.d.ts.map +1 -1
- package/dist/capabilities/builtin.js +5 -1
- package/dist/capabilities/builtin.js.map +1 -1
- package/dist/cli/plugin-command.d.ts.map +1 -1
- package/dist/cli/plugin-command.js +51 -5
- package/dist/cli/plugin-command.js.map +1 -1
- package/dist/core/agent-session-compaction.d.ts.map +1 -1
- package/dist/core/agent-session-compaction.js +13 -5
- package/dist/core/agent-session-compaction.js.map +1 -1
- package/dist/core/agent-session-internal.d.ts +2 -4
- package/dist/core/agent-session-internal.d.ts.map +1 -1
- package/dist/core/agent-session-internal.js.map +1 -1
- package/dist/core/agent-session-paths.d.ts +2 -0
- package/dist/core/agent-session-paths.d.ts.map +1 -1
- package/dist/core/agent-session-paths.js +2 -1
- package/dist/core/agent-session-paths.js.map +1 -1
- package/dist/core/agent-session-projection.d.ts +10 -4
- package/dist/core/agent-session-projection.d.ts.map +1 -1
- package/dist/core/agent-session-projection.js +17 -4
- package/dist/core/agent-session-projection.js.map +1 -1
- package/dist/core/agent-session.d.ts +19 -5
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +30 -4
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/bootstrap-lifecycle.d.ts +1 -1
- package/dist/core/extensions/bootstrap-lifecycle.d.ts.map +1 -1
- package/dist/core/extensions/bootstrap-lifecycle.js +5 -2
- package/dist/core/extensions/bootstrap-lifecycle.js.map +1 -1
- package/dist/core/extensions/capability-runtime-store.d.ts.map +1 -1
- package/dist/core/extensions/capability-runtime-store.js +1 -0
- package/dist/core/extensions/capability-runtime-store.js.map +1 -1
- package/dist/core/extensions/capability-runtime-types.d.ts +16 -1
- package/dist/core/extensions/capability-runtime-types.d.ts.map +1 -1
- package/dist/core/extensions/capability-runtime-types.js.map +1 -1
- package/dist/core/extensions/capability-runtime.d.ts.map +1 -1
- package/dist/core/extensions/capability-runtime.js +38 -2
- package/dist/core/extensions/capability-runtime.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/lifecycle-catalog.d.ts +46 -0
- package/dist/core/extensions/lifecycle-catalog.d.ts.map +1 -1
- package/dist/core/extensions/lifecycle-catalog.js +101 -0
- package/dist/core/extensions/lifecycle-catalog.js.map +1 -1
- package/dist/core/extensions/provider-failover.d.ts +106 -0
- package/dist/core/extensions/provider-failover.d.ts.map +1 -0
- package/dist/core/extensions/provider-failover.js +692 -0
- package/dist/core/extensions/provider-failover.js.map +1 -0
- package/dist/core/facade/coding-agent-facade.d.ts +13 -6
- package/dist/core/facade/coding-agent-facade.d.ts.map +1 -1
- package/dist/core/facade/coding-agent-facade.js +3 -1
- package/dist/core/facade/coding-agent-facade.js.map +1 -1
- package/dist/core/keybindings.d.ts +6 -6
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +23 -6
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-config.d.ts +102 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +46 -14
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +1 -1
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +15 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +30 -18
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +4 -9
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +107 -24
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +2 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +10 -5
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +71 -5
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +138 -9
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +17 -2
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts +6 -0
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js +26 -0
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/core/tools/tool-definition.d.ts +21 -2
- package/dist/core/tools/tool-definition.d.ts.map +1 -1
- package/dist/core/tools/tool-definition.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +3 -6
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/agents-view.d.ts +57 -0
- package/dist/modes/interactive/components/agents-view.d.ts.map +1 -0
- package/dist/modes/interactive/components/agents-view.js +260 -0
- package/dist/modes/interactive/components/agents-view.js.map +1 -0
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +2 -2
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +7 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +51 -6
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +12 -2
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +1 -1
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +89 -10
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.d.ts +3 -0
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.js +9 -3
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
- package/dist/modes/interactive/components/workflow-view.d.ts +119 -0
- package/dist/modes/interactive/components/workflow-view.d.ts.map +1 -0
- package/dist/modes/interactive/components/workflow-view.js +638 -0
- package/dist/modes/interactive/components/workflow-view.js.map +1 -0
- package/dist/modes/interactive/interactive-commands.d.ts +22 -0
- package/dist/modes/interactive/interactive-commands.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-commands.js +56 -0
- package/dist/modes/interactive/interactive-commands.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +51 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +218 -37
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/interactive-selectors.d.ts +2 -4
- package/dist/modes/interactive/interactive-selectors.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-selectors.js +10 -1
- package/dist/modes/interactive/interactive-selectors.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +5 -0
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +3 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +4 -2
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +1 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/plugins/ecosystem/bootstrap-factory-manifest.d.ts.map +1 -1
- package/dist/plugins/ecosystem/bootstrap-factory-manifest.js +7 -0
- package/dist/plugins/ecosystem/bootstrap-factory-manifest.js.map +1 -1
- package/dist/plugins/ecosystem/cli.d.ts.map +1 -1
- package/dist/plugins/ecosystem/cli.js +1 -1
- package/dist/plugins/ecosystem/cli.js.map +1 -1
- package/dist/plugins/ecosystem/install-store-paths.d.ts +15 -0
- package/dist/plugins/ecosystem/install-store-paths.d.ts.map +1 -0
- package/dist/plugins/ecosystem/install-store-paths.js +58 -0
- package/dist/plugins/ecosystem/install-store-paths.js.map +1 -0
- package/dist/plugins/ecosystem/node-disk-registry-source.d.ts.map +1 -1
- package/dist/plugins/ecosystem/node-disk-registry-source.js +3 -8
- package/dist/plugins/ecosystem/node-disk-registry-source.js.map +1 -1
- package/dist/plugins/ecosystem/node-package-install-host.d.ts.map +1 -1
- package/dist/plugins/ecosystem/node-package-install-host.js +9 -1
- package/dist/plugins/ecosystem/node-package-install-host.js.map +1 -1
- package/dist/plugins/ecosystem/node-package-install-persistence.d.ts.map +1 -1
- package/dist/plugins/ecosystem/node-package-install-persistence.js +3 -8
- package/dist/plugins/ecosystem/node-package-install-persistence.js.map +1 -1
- package/dist/profile/plugin-profiles.d.ts +2 -0
- package/dist/profile/plugin-profiles.d.ts.map +1 -1
- package/dist/profile/plugin-profiles.js +4 -0
- package/dist/profile/plugin-profiles.js.map +1 -1
- package/dist/profiles/builtin-suites.d.ts +19 -2
- package/dist/profiles/builtin-suites.d.ts.map +1 -1
- package/dist/profiles/builtin-suites.js +20 -2
- package/dist/profiles/builtin-suites.js.map +1 -1
- package/dist/profiles/suite-loader.d.ts.map +1 -1
- package/dist/profiles/suite-loader.js +4 -1
- package/dist/profiles/suite-loader.js.map +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +10 -10
- package/dist/utils/clipboard.js.map +1 -1
- package/docs/keybindings.md +8 -8
- package/docs/models.md +1 -1
- package/docs/rpc.md +3 -1
- package/docs/settings.md +2 -1
- package/package.json +4 -4
- package/dist/bundle/chunks/anthropic-messages-EVFQ6MLR.js +0 -6
- package/dist/bundle/chunks/chunk-F6SBZUJG.js +0 -141
- package/dist/bundle/chunks/chunk-NUHFSC37.js +0 -25
|
@@ -14,6 +14,7 @@ import { spawn } from "child_process";
|
|
|
14
14
|
import { APP_NAME, APP_TITLE, getAgentDir, getDebugLogPath, VERSION } from "../../config.js";
|
|
15
15
|
import { parseSkillBlock } from "../../core/agent-session.js";
|
|
16
16
|
import { SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
|
|
17
|
+
import { listAgentSessions, readAgentSession } from "../../core/agent-session-store.js";
|
|
17
18
|
import { CACHE_TTL_MS, collectCacheMisses, computeCacheWaste, detectCacheMiss, } from "../../core/cache-stats.js";
|
|
18
19
|
import { DEFAULT_THINKING_LEVEL, THINKING_LEVEL_OPTIONS } from "../../core/defaults.js";
|
|
19
20
|
import { createCodingAgentFacade, createCodingAgentRuntimeFacade, } from "../../core/facade/index.js";
|
|
@@ -36,6 +37,7 @@ import { killTrackedDetachedChildren } from "../../utils/shell.js";
|
|
|
36
37
|
import { loadAllHighlightLanguages } from "../../utils/syntax-highlight.js";
|
|
37
38
|
import { ensureTool } from "../../utils/tools-manager.js";
|
|
38
39
|
import { checkForNewVersion } from "../../utils/version-check.js";
|
|
40
|
+
import { AgentsViewComponent } from "./components/agents-view.js";
|
|
39
41
|
import { ArminComponent } from "./components/armin.js";
|
|
40
42
|
import { AssistantMessageComponent } from "./components/assistant-message.js";
|
|
41
43
|
import { BashExecutionComponent } from "./components/bash-execution.js";
|
|
@@ -55,9 +57,10 @@ import { BranchSummaryStatusIndicator, CompactionStatusIndicator, IdleStatus, Re
|
|
|
55
57
|
import { ToolExecutionComponent } from "./components/tool-execution.js";
|
|
56
58
|
import { TreeSelectorComponent } from "./components/tree-selector.js";
|
|
57
59
|
import { UserMessageComponent } from "./components/user-message.js";
|
|
60
|
+
import { WorkflowViewComponent } from "./components/workflow-view.js";
|
|
58
61
|
import { editInExternalEditor } from "./external-editor.js";
|
|
59
62
|
import * as authFlow from "./interactive-auth.js";
|
|
60
|
-
import { COMMAND_DISPATCH_ENTRIES, matchCommandDispatchEntry } from "./interactive-commands.js";
|
|
63
|
+
import { COMMAND_DISPATCH_ENTRIES, filterCapabilityCommandsForCompletions, matchCommandDispatchEntry, } from "./interactive-commands.js";
|
|
61
64
|
import { createInteractionHostV1, hideDialogEditor, hideDialogInput, hideDialogSelector, isExpandable, showDialogConfirm, showDialogEditor, showDialogInput, showDialogNotify, showDialogSelector, } from "./interactive-dialog-ui.js";
|
|
62
65
|
import { buildScopeGroups, findSourceInfoForPath, formatContextPath, formatDiagnostics, formatDisplayPath, formatMetricsSnapshot, formatPathWithSource, formatScopeGroups, getCompactPackageSourceLabel, getCompactPathLabel, getDisplaySourceInfo, getScopeGroup, getShortPath, isPackageSource, } from "./interactive-format.js";
|
|
63
66
|
import { createFuzzyAutocompleteItems, formatLoginProviderCompletionDescription, getLoginProviderCompletionOptions, getLoginProviderSearchText, isAnthropicSubscriptionAuthKey, isDeadTerminalError, quoteIfNeeded, } from "./interactive-helpers.js";
|
|
@@ -136,6 +139,7 @@ export function createInteractiveTui(options) {
|
|
|
136
139
|
openUrl: openBrowser,
|
|
137
140
|
onRightClickPaste: options.onRightClickPaste,
|
|
138
141
|
copyOnSelect: options.fullscreenCopyOnSelect,
|
|
142
|
+
wheelScrollLines: options.fullscreenWheelScrollLines ?? 1,
|
|
139
143
|
copySelection: async (text) => {
|
|
140
144
|
try {
|
|
141
145
|
await copyToClipboard(text);
|
|
@@ -180,6 +184,41 @@ export function createInteractiveTuiReference(getTui) {
|
|
|
180
184
|
getPrototypeOf: () => Reflect.getPrototypeOf(getTui()),
|
|
181
185
|
});
|
|
182
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* Provider failover footer ticker (设计 供应商能力槽位与主备切换 §4.7 Phase 3):
|
|
189
|
+
* while the latest `provider.failover.state` snapshot reports an open breaker, the
|
|
190
|
+
* footer countdown must advance once per second, so re-render on a 1s interval
|
|
191
|
+
* (countdown-timer.ts 同构). The interval is created on the first refresh that
|
|
192
|
+
* sees an open state and cleared as soon as the state leaves open — no leak.
|
|
193
|
+
*/
|
|
194
|
+
export class ProviderFailoverTicker {
|
|
195
|
+
intervalId;
|
|
196
|
+
isOpen;
|
|
197
|
+
requestRender;
|
|
198
|
+
constructor(isOpen, requestRender) {
|
|
199
|
+
this.isOpen = isOpen;
|
|
200
|
+
this.requestRender = requestRender;
|
|
201
|
+
}
|
|
202
|
+
/** Re-evaluate the open state: start ticking when open, stop when not. */
|
|
203
|
+
refresh() {
|
|
204
|
+
if (this.isOpen()) {
|
|
205
|
+
if (this.intervalId === undefined) {
|
|
206
|
+
this.intervalId = setInterval(() => {
|
|
207
|
+
this.requestRender();
|
|
208
|
+
this.refresh();
|
|
209
|
+
}, 1000);
|
|
210
|
+
}
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
this.dispose();
|
|
214
|
+
}
|
|
215
|
+
dispose() {
|
|
216
|
+
if (this.intervalId !== undefined) {
|
|
217
|
+
clearInterval(this.intervalId);
|
|
218
|
+
this.intervalId = undefined;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
183
222
|
export class InteractiveMode {
|
|
184
223
|
runtimeHost;
|
|
185
224
|
renderer;
|
|
@@ -209,6 +248,12 @@ export class InteractiveMode {
|
|
|
209
248
|
activeSelectorDispose;
|
|
210
249
|
/** Host-visible for ./interactive-dialog-ui.ts (S5 slice 2). */
|
|
211
250
|
footer;
|
|
251
|
+
/**
|
|
252
|
+
* Re-renders the footer once per second while the provider failover breaker is
|
|
253
|
+
* open (§4.7 Phase 3). Optional-typed to match the prototype-based test stubs
|
|
254
|
+
* (constructor always assigns it on real instances).
|
|
255
|
+
*/
|
|
256
|
+
failoverTicker;
|
|
212
257
|
/** Host-visible for ./interactive-dialog-ui.ts (S5 slice 2). */
|
|
213
258
|
footerContainer;
|
|
214
259
|
/** Host-visible for ./interactive-dialog-ui.ts (S5 slice 2). */
|
|
@@ -338,6 +383,7 @@ export class InteractiveMode {
|
|
|
338
383
|
logDirectory: getAgentDir(),
|
|
339
384
|
onRightClickPaste: this.onRightClickPaste,
|
|
340
385
|
fullscreenCopyOnSelect: this.settingsManager.getFullscreenCopyOnSelect(),
|
|
386
|
+
fullscreenWheelScrollLines: this.settingsManager.getFullscreenWheelScrollLines(),
|
|
341
387
|
});
|
|
342
388
|
this.ui = createInteractiveTuiReference(() => this.renderer);
|
|
343
389
|
this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());
|
|
@@ -370,6 +416,7 @@ export class InteractiveMode {
|
|
|
370
416
|
this.footer.setAutoCompactEnabled(this.facade.isAutoCompactionEnabled());
|
|
371
417
|
this.footerContainer = new Container();
|
|
372
418
|
this.footerContainer.addChild(this.footer);
|
|
419
|
+
this.failoverTicker = new ProviderFailoverTicker(() => this.footer.isProviderFailoverOpen(), () => this.ui.requestRender());
|
|
373
420
|
// Load hide thinking block setting
|
|
374
421
|
this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
|
|
375
422
|
this.outputPad = this.settingsManager.getOutputPad();
|
|
@@ -483,16 +530,13 @@ export class InteractiveMode {
|
|
|
483
530
|
...(cmd.argumentHint && { argumentHint: cmd.argumentHint }),
|
|
484
531
|
}));
|
|
485
532
|
const builtinCommandNames = new Set(slashCommands.map((c) => c.name));
|
|
533
|
+
const templateCommandNames = new Set(this.facade.getPromptTemplates().map((cmd) => cmd.name));
|
|
486
534
|
const resourceSkillCommandNames = new Set(this.facade.getSkills().skills.map((skill) => `skill:${skill.name}`));
|
|
487
|
-
const capabilityCommands = capabilityRegistrations
|
|
488
|
-
.filter(({ definition }) => !builtinCommandNames.has(definition.name) &&
|
|
489
|
-
!this.facade.getPromptTemplates().some((template) => template.name === definition.name) &&
|
|
490
|
-
!resourceSkillCommandNames.has(definition.name))
|
|
491
|
-
.map(({ definition }) => ({
|
|
535
|
+
const capabilityCommands = filterCapabilityCommandsForCompletions(capabilityRegistrations.map(({ definition }) => ({
|
|
492
536
|
name: definition.name,
|
|
493
537
|
description: definition.description,
|
|
494
538
|
...(definition.argumentHint && { argumentHint: definition.argumentHint }),
|
|
495
|
-
}));
|
|
539
|
+
})), [builtinCommandNames, templateCommandNames, [...resourceSkillCommandNames]]);
|
|
496
540
|
// Build skill commands from session.skills (if enabled)
|
|
497
541
|
this.skillCommands.clear();
|
|
498
542
|
const skillCommandList = [];
|
|
@@ -589,6 +633,7 @@ export class InteractiveMode {
|
|
|
589
633
|
terminal,
|
|
590
634
|
onRightClickPaste: this.onRightClickPaste,
|
|
591
635
|
fullscreenCopyOnSelect: this.settingsManager.getFullscreenCopyOnSelect(),
|
|
636
|
+
fullscreenWheelScrollLines: this.settingsManager.getFullscreenWheelScrollLines(),
|
|
592
637
|
});
|
|
593
638
|
nextUi.setClearOnShrink(clearOnShrink);
|
|
594
639
|
nextUi.onDebug = onDebug;
|
|
@@ -683,17 +728,12 @@ export class InteractiveMode {
|
|
|
683
728
|
hint("app.exit", "to exit (empty)"),
|
|
684
729
|
hint("app.suspend", "to suspend"),
|
|
685
730
|
keyHint("tui.editor.deleteToLineEnd", "to delete to end"),
|
|
686
|
-
hint("app.thinking.cycle", "to cycle thinking level"),
|
|
687
|
-
rawKeyHint(`${keyText("app.model.cycleForward")}/${keyText("app.model.cycleBackward")}`, "to cycle models"),
|
|
688
|
-
hint("app.model.select", "to select model"),
|
|
689
731
|
hint("app.tools.expand", "to expand tools"),
|
|
690
732
|
hint("app.thinking.toggle", "to expand thinking"),
|
|
691
733
|
hint("app.editor.external", "for external editor"),
|
|
692
734
|
rawKeyHint("/", "for commands"),
|
|
693
735
|
rawKeyHint("!", "to run bash"),
|
|
694
736
|
rawKeyHint("!!", "to run bash (no context)"),
|
|
695
|
-
hint("app.message.followUp", "to queue follow-up"),
|
|
696
|
-
hint("app.message.dequeue", "to edit all queued messages"),
|
|
697
737
|
hint("app.clipboard.pasteImage", "to paste image (with text fallback)"),
|
|
698
738
|
rawKeyHint("drop files", "to attach"),
|
|
699
739
|
].join("\n");
|
|
@@ -1187,10 +1227,14 @@ export class InteractiveMode {
|
|
|
1187
1227
|
this.applyFullscreenScrollbarSetting();
|
|
1188
1228
|
if (this.renderer instanceof TuiAltScreen) {
|
|
1189
1229
|
this.renderer.setCopyOnSelect(this.settingsManager.getFullscreenCopyOnSelect());
|
|
1230
|
+
this.renderer.setWheelScrollLines(this.settingsManager.getFullscreenWheelScrollLines());
|
|
1190
1231
|
}
|
|
1191
1232
|
this.footer.setSession(this.session);
|
|
1192
1233
|
this.footer.setAutoCompactEnabled(this.facade.isAutoCompactionEnabled());
|
|
1193
1234
|
this.footerDataProvider.setCwd(this.facade.getCwd());
|
|
1235
|
+
// The switched session carries its own failover snapshot history; re-check
|
|
1236
|
+
// whether the countdown ticker is needed.
|
|
1237
|
+
this.failoverTicker?.refresh();
|
|
1194
1238
|
this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
|
|
1195
1239
|
this.outputPad = this.settingsManager.getOutputPad();
|
|
1196
1240
|
this.ui.setShowHardwareCursor(this.settingsManager.getShowHardwareCursor());
|
|
@@ -1430,7 +1474,14 @@ export class InteractiveMode {
|
|
|
1430
1474
|
// so they work correctly regardless of which editor is active
|
|
1431
1475
|
this.defaultEditor.onEscape = () => {
|
|
1432
1476
|
if (this.facade.isStreaming()) {
|
|
1433
|
-
|
|
1477
|
+
// 中断分级(设计 子代理运行时 §4.2,2026-10-02):Esc 只停当前
|
|
1478
|
+
// 回合,从不终止后台子代理(用户裁决:入口分离,同键不承载两
|
|
1479
|
+
// 个烈度)。仍有活跃子代理时状态行指路 /stop-agents。
|
|
1480
|
+
this.restoreQueuedMessagesToEditor({ abort: true, cancelAgentTasks: false });
|
|
1481
|
+
const activeAgents = this.facade.getActiveAgentTaskCount();
|
|
1482
|
+
if (activeAgents > 0) {
|
|
1483
|
+
this.showStatus(`${activeAgents} 个子代理仍在运行 · /stop-agents 终止全部(Esc 只停当前回合)`);
|
|
1484
|
+
}
|
|
1434
1485
|
}
|
|
1435
1486
|
else if (this.facade.isBashRunning()) {
|
|
1436
1487
|
this.facade.abortBash();
|
|
@@ -1472,8 +1523,11 @@ export class InteractiveMode {
|
|
|
1472
1523
|
this.defaultEditor.onAction("app.model.select", () => this.showModelSelector());
|
|
1473
1524
|
this.defaultEditor.onAction("app.tools.expand", () => this.toggleToolOutputExpansion());
|
|
1474
1525
|
this.defaultEditor.onAction("app.thinking.toggle", () => this.toggleThinkingBlockVisibility());
|
|
1475
|
-
|
|
1476
|
-
|
|
1526
|
+
// Iron law (2026-10-02 复审): the main editor must not consume
|
|
1527
|
+
// ctrl+g/ctrl+x — /editor and /copy are the single entries here. The
|
|
1528
|
+
// bindings keep their defaults for the components that have no command
|
|
1529
|
+
// channel (dialog editors' external-editor entry, the tree view's
|
|
1530
|
+
// in-place copy).
|
|
1477
1531
|
this.defaultEditor.onAction("app.message.followUp", () => this.handleFollowUp());
|
|
1478
1532
|
this.defaultEditor.onAction("app.message.dequeue", () => this.handleDequeue());
|
|
1479
1533
|
this.defaultEditor.onAction("app.session.new", () => this.handleClearCommand());
|
|
@@ -1629,13 +1683,22 @@ export class InteractiveMode {
|
|
|
1629
1683
|
if (entry.clearEditorBeforeRun)
|
|
1630
1684
|
this.setEditorText("");
|
|
1631
1685
|
const result = entry.run(this, text, arg);
|
|
1686
|
+
// Clear the submitted command text after the run — but only while the
|
|
1687
|
+
// editor still holds it. Handlers that write their own content into
|
|
1688
|
+
// the editor (/dequeue restores the queue, /editor applies external
|
|
1689
|
+
// edits) must not have that wiped (review finding: the unconditional
|
|
1690
|
+
// clear lost the restored/edited content).
|
|
1691
|
+
const clearIfUntouched = () => {
|
|
1692
|
+
if (this.editor.getText().trim() === text)
|
|
1693
|
+
this.setEditorText("");
|
|
1694
|
+
};
|
|
1632
1695
|
if (!(result instanceof Promise)) {
|
|
1633
1696
|
if (!entry.clearEditorBeforeRun)
|
|
1634
|
-
|
|
1697
|
+
clearIfUntouched();
|
|
1635
1698
|
return { pending: undefined };
|
|
1636
1699
|
}
|
|
1637
1700
|
return {
|
|
1638
|
-
pending: entry.clearEditorBeforeRun ? result : result.then(
|
|
1701
|
+
pending: entry.clearEditorBeforeRun ? result : result.then(clearIfUntouched),
|
|
1639
1702
|
};
|
|
1640
1703
|
}
|
|
1641
1704
|
setEditorText(text) {
|
|
@@ -1651,6 +1714,9 @@ export class InteractiveMode {
|
|
|
1651
1714
|
await this.init();
|
|
1652
1715
|
}
|
|
1653
1716
|
this.footer.invalidate();
|
|
1717
|
+
// Failover snapshots land as entry_appended events; the ticker decides here
|
|
1718
|
+
// whether the per-second countdown re-render is needed (§4.7 Phase 3).
|
|
1719
|
+
this.failoverTicker?.refresh();
|
|
1654
1720
|
switch (event.type) {
|
|
1655
1721
|
case "agent_start":
|
|
1656
1722
|
this.pendingTools.clear();
|
|
@@ -2463,8 +2529,8 @@ export class InteractiveMode {
|
|
|
2463
2529
|
throw error;
|
|
2464
2530
|
}
|
|
2465
2531
|
}
|
|
2466
|
-
async handleFollowUp() {
|
|
2467
|
-
const text = (this.editor.getExpandedText?.() ?? this.editor.getText()).trim();
|
|
2532
|
+
async handleFollowUp(explicitText) {
|
|
2533
|
+
const text = (explicitText ?? this.editor.getExpandedText?.() ?? this.editor.getText()).trim();
|
|
2468
2534
|
if (!text)
|
|
2469
2535
|
return;
|
|
2470
2536
|
// Queue input during compaction (extension commands execute immediately)
|
|
@@ -2475,15 +2541,19 @@ export class InteractiveMode {
|
|
|
2475
2541
|
// Alt+Enter queues a follow-up message (waits until agent finishes)
|
|
2476
2542
|
// This handles extension commands (execute immediately), prompt template expansion, and queueing
|
|
2477
2543
|
if (this.facade.isStreaming()) {
|
|
2478
|
-
|
|
2479
|
-
|
|
2544
|
+
// An explicit /queue <text> argument must not touch the editor draft.
|
|
2545
|
+
if (explicitText === undefined) {
|
|
2546
|
+
this.editor.addToHistory?.(text);
|
|
2547
|
+
this.editor.setText("");
|
|
2548
|
+
}
|
|
2480
2549
|
await this.facade.prompt(text, { streamingBehavior: "followUp" });
|
|
2481
2550
|
this.updatePendingMessagesDisplay();
|
|
2482
2551
|
this.ui.requestRender();
|
|
2483
2552
|
}
|
|
2484
2553
|
// If not streaming, Alt+Enter acts like regular Enter (trigger onSubmit)
|
|
2485
2554
|
else if (this.editor.onSubmit) {
|
|
2486
|
-
|
|
2555
|
+
if (explicitText === undefined)
|
|
2556
|
+
this.editor.setText("");
|
|
2487
2557
|
this.editor.onSubmit(text);
|
|
2488
2558
|
}
|
|
2489
2559
|
}
|
|
@@ -2496,6 +2566,22 @@ export class InteractiveMode {
|
|
|
2496
2566
|
this.showStatus(`Restored ${restored} queued message${restored > 1 ? "s" : ""} to editor`);
|
|
2497
2567
|
}
|
|
2498
2568
|
}
|
|
2569
|
+
/** /queue command host face (single-entry iron law): queues the given text for after the current run. A bare /queue cannot mirror the keybinding — the editor buffer is cleared on submit before the command runs — so it prints usage instead of silently doing nothing. */
|
|
2570
|
+
handleQueueCommand(text) {
|
|
2571
|
+
if (text === undefined || text.trim() === "") {
|
|
2572
|
+
this.showStatus("Usage: /queue <text>");
|
|
2573
|
+
return Promise.resolve();
|
|
2574
|
+
}
|
|
2575
|
+
return this.handleFollowUp(text);
|
|
2576
|
+
}
|
|
2577
|
+
/** /dequeue command host face: restore queued messages back to the editor. */
|
|
2578
|
+
handleDequeueCommand() {
|
|
2579
|
+
this.handleDequeue();
|
|
2580
|
+
}
|
|
2581
|
+
/** /editor command host face: edit the current input in an external editor. */
|
|
2582
|
+
handleEditorCommand() {
|
|
2583
|
+
return this.handleOpenExternalEditor();
|
|
2584
|
+
}
|
|
2499
2585
|
/** Host-visible for ./interactive-auth.ts (S5 slice 3). */
|
|
2500
2586
|
updateEditorBorderColor() {
|
|
2501
2587
|
if (this.isBashMode) {
|
|
@@ -2689,7 +2775,9 @@ export class InteractiveMode {
|
|
|
2689
2775
|
const text = theme.fg("dim", `Follow-up: ${message}`);
|
|
2690
2776
|
this.pendingMessagesContainer.addChild(new TruncatedText(text, 1, 0));
|
|
2691
2777
|
}
|
|
2692
|
-
|
|
2778
|
+
// The dequeue keybinding's shipped default was cleared (iron law):
|
|
2779
|
+
// /queue's sibling command is the discoverable entry.
|
|
2780
|
+
const dequeueHint = this.getAppKeyDisplay("app.message.dequeue") || "/dequeue";
|
|
2693
2781
|
const hintText = theme.fg("dim", `↳ ${dequeueHint} to edit all queued messages`);
|
|
2694
2782
|
this.pendingMessagesContainer.addChild(new TruncatedText(hintText, 1, 0));
|
|
2695
2783
|
}
|
|
@@ -2697,10 +2785,13 @@ export class InteractiveMode {
|
|
|
2697
2785
|
restoreQueuedMessagesToEditor(options) {
|
|
2698
2786
|
const { steering, followUp } = this.clearAllQueues();
|
|
2699
2787
|
const allQueued = [...steering, ...followUp];
|
|
2788
|
+
const abortOptions = options?.abort === true
|
|
2789
|
+
? { ...(options.cancelAgentTasks === undefined ? {} : { cancelAgentTasks: options.cancelAgentTasks }) }
|
|
2790
|
+
: undefined;
|
|
2700
2791
|
if (allQueued.length === 0) {
|
|
2701
2792
|
this.updatePendingMessagesDisplay();
|
|
2702
|
-
if (
|
|
2703
|
-
void this.facade.abort();
|
|
2793
|
+
if (abortOptions !== undefined) {
|
|
2794
|
+
void this.facade.abort(abortOptions);
|
|
2704
2795
|
}
|
|
2705
2796
|
return 0;
|
|
2706
2797
|
}
|
|
@@ -2709,8 +2800,8 @@ export class InteractiveMode {
|
|
|
2709
2800
|
const combinedText = [queuedText, currentText].filter((t) => t.trim()).join("\n\n");
|
|
2710
2801
|
this.editor.setText(combinedText);
|
|
2711
2802
|
this.updatePendingMessagesDisplay();
|
|
2712
|
-
if (
|
|
2713
|
-
void this.facade.abort();
|
|
2803
|
+
if (abortOptions !== undefined) {
|
|
2804
|
+
void this.facade.abort(abortOptions);
|
|
2714
2805
|
}
|
|
2715
2806
|
return allQueued.length;
|
|
2716
2807
|
}
|
|
@@ -2867,6 +2958,7 @@ export class InteractiveMode {
|
|
|
2867
2958
|
fullscreenExitOutput: this.settingsManager.getFullscreenExitOutput(),
|
|
2868
2959
|
fullscreenScrollbar: this.settingsManager.getFullscreenScrollbar(),
|
|
2869
2960
|
fullscreenCopyOnSelect: this.settingsManager.getFullscreenCopyOnSelect(),
|
|
2961
|
+
fullscreenWheelScrollLines: this.settingsManager.getFullscreenWheelScrollLines(),
|
|
2870
2962
|
warnings: this.settingsManager.getWarnings(),
|
|
2871
2963
|
}, {
|
|
2872
2964
|
onAutoCompactChange: (enabled) => {
|
|
@@ -3036,6 +3128,11 @@ export class InteractiveMode {
|
|
|
3036
3128
|
if (this.renderer instanceof TuiAltScreen)
|
|
3037
3129
|
this.renderer.setCopyOnSelect(enabled);
|
|
3038
3130
|
},
|
|
3131
|
+
onFullscreenWheelScrollLinesChange: (lines) => {
|
|
3132
|
+
this.settingsManager.setFullscreenWheelScrollLines(lines);
|
|
3133
|
+
if (this.renderer instanceof TuiAltScreen)
|
|
3134
|
+
this.renderer.setWheelScrollLines(lines);
|
|
3135
|
+
},
|
|
3039
3136
|
onWarningsChange: (warnings) => {
|
|
3040
3137
|
this.settingsManager.setWarnings(warnings);
|
|
3041
3138
|
},
|
|
@@ -3206,6 +3303,61 @@ export class InteractiveMode {
|
|
|
3206
3303
|
async handleCloneCommand() {
|
|
3207
3304
|
await selectorsFlow.handleCloneCommand(this);
|
|
3208
3305
|
}
|
|
3306
|
+
/**
|
|
3307
|
+
* Workflow overlay (§9 L2/L3): read-only panorama + transition log from
|
|
3308
|
+
* `workflow.state`/`workflow.transition` entries. Actions are forwarded to
|
|
3309
|
+
* the editor as reviewable text; the main agent executes them via
|
|
3310
|
+
* workflow_advance (rendering and execution stay separated).
|
|
3311
|
+
*/
|
|
3312
|
+
showWorkflowView() {
|
|
3313
|
+
const hasSnapshot = this.facade
|
|
3314
|
+
.getEntries()
|
|
3315
|
+
.some((entry) => entry.type === "custom" && entry.customType === "workflow.state");
|
|
3316
|
+
if (!hasSnapshot) {
|
|
3317
|
+
this.showStatus("No workflow in this session");
|
|
3318
|
+
return;
|
|
3319
|
+
}
|
|
3320
|
+
this.showSelector((done) => {
|
|
3321
|
+
const view = new WorkflowViewComponent({
|
|
3322
|
+
getEntries: () => this.facade.getEntries(),
|
|
3323
|
+
maxRows: Math.max(6, Math.floor(this.ui.terminal.rows / 2)),
|
|
3324
|
+
requestRender: () => this.ui.requestRender(),
|
|
3325
|
+
});
|
|
3326
|
+
view.onClose = done;
|
|
3327
|
+
view.onForwardAction = (text) => {
|
|
3328
|
+
done();
|
|
3329
|
+
this.editor.setText(text);
|
|
3330
|
+
this.ui.requestRender();
|
|
3331
|
+
this.showStatus("Workflow action staged — review in the editor and submit");
|
|
3332
|
+
};
|
|
3333
|
+
return { component: view, focus: view, dispose: () => view.dispose() };
|
|
3334
|
+
});
|
|
3335
|
+
}
|
|
3336
|
+
/**
|
|
3337
|
+
* Subagent session browser (/agents): read-only list + detail of this
|
|
3338
|
+
* session's subagent transcripts from the D-060 §3 on-disk store — the same
|
|
3339
|
+
* parent-scoped summary source as the subagent_list model tool (only this
|
|
3340
|
+
* session's children, newest first). Detail entries load lazily per
|
|
3341
|
+
* session; a failed load renders inside the view instead of throwing.
|
|
3342
|
+
*/
|
|
3343
|
+
showAgentsView() {
|
|
3344
|
+
const agentDir = getAgentDir();
|
|
3345
|
+
const sessions = listAgentSessions({ agentDir, parentAgentSession: this.facade.getSessionId() });
|
|
3346
|
+
if (sessions.length === 0) {
|
|
3347
|
+
this.showStatus("No subagent sessions in this session");
|
|
3348
|
+
return;
|
|
3349
|
+
}
|
|
3350
|
+
this.showSelector((done) => {
|
|
3351
|
+
const view = new AgentsViewComponent({
|
|
3352
|
+
sessions,
|
|
3353
|
+
loadEntries: (sessionId) => readAgentSession({ agentDir, sessionId }),
|
|
3354
|
+
maxRows: Math.max(6, Math.floor(this.ui.terminal.rows / 2)),
|
|
3355
|
+
requestRender: () => this.ui.requestRender(),
|
|
3356
|
+
});
|
|
3357
|
+
view.onClose = done;
|
|
3358
|
+
return { component: view, focus: view };
|
|
3359
|
+
});
|
|
3360
|
+
}
|
|
3209
3361
|
showTreeSelector(initialSelectedId) {
|
|
3210
3362
|
const tree = this.facade.getTree();
|
|
3211
3363
|
const realLeafId = this.facade.getLeafId();
|
|
@@ -3253,9 +3405,10 @@ export class InteractiveMode {
|
|
|
3253
3405
|
}
|
|
3254
3406
|
}
|
|
3255
3407
|
// The user committed to navigating: stop the active response first.
|
|
3408
|
+
// 中断分级:导航清场只停回合,不终止后台子代理(设计 §4.2)。
|
|
3256
3409
|
if (this.facade.isStreaming()) {
|
|
3257
3410
|
this.restoreQueuedMessagesToEditor();
|
|
3258
|
-
await this.facade.abort();
|
|
3411
|
+
await this.facade.abort({ cancelAgentTasks: false });
|
|
3259
3412
|
}
|
|
3260
3413
|
// Set up escape handler and status indicator if summarizing
|
|
3261
3414
|
let showingSummaryIndicator = false;
|
|
@@ -3732,6 +3885,19 @@ export class InteractiveMode {
|
|
|
3732
3885
|
this.chatContainer.addChild(new Text(theme.fg("dim", `Session name set: ${sessionName ?? name}`), 1, 0));
|
|
3733
3886
|
this.ui.requestRender();
|
|
3734
3887
|
}
|
|
3888
|
+
/**
|
|
3889
|
+
* 中断分级(设计 子代理运行时 §4.2,2026-10-02):/stop-agents 的唯一执行
|
|
3890
|
+
* 面——终止全部后台子代理(不触当前回合)。Esc 从不做这件事。
|
|
3891
|
+
*/
|
|
3892
|
+
handleStopAgentsCommand() {
|
|
3893
|
+
const active = this.facade.getActiveAgentTaskCount();
|
|
3894
|
+
if (active === 0) {
|
|
3895
|
+
this.showStatus("No background subagents running");
|
|
3896
|
+
return;
|
|
3897
|
+
}
|
|
3898
|
+
this.facade.abortAgentTasks();
|
|
3899
|
+
this.showStatus(`Terminated ${active} background subagent${active === 1 ? "" : "s"}`);
|
|
3900
|
+
}
|
|
3735
3901
|
handleSessionCommand() {
|
|
3736
3902
|
const stats = this.facade.getSessionStats();
|
|
3737
3903
|
const sessionName = this.facade.getSessionName();
|
|
@@ -3950,6 +4116,27 @@ export class InteractiveMode {
|
|
|
3950
4116
|
const followUp = this.getAppKeyDisplay("app.message.followUp");
|
|
3951
4117
|
const dequeue = this.getAppKeyDisplay("app.message.dequeue");
|
|
3952
4118
|
const pasteImage = this.getAppKeyDisplay("app.clipboard.pasteImage");
|
|
4119
|
+
// Iron law (2026-10-01): these shipped defaults were cleared in favor of
|
|
4120
|
+
// the /model command; the row only appears when a user re-configures a key.
|
|
4121
|
+
const selectModelRow = selectModel ? `| \`${selectModel}\` | Open model selector |\n` : "";
|
|
4122
|
+
// Iron law (2026-10-02 复审): ctrl+g/ctrl+x stay bound only for the
|
|
4123
|
+
// components without a command channel — the main editor ignores them
|
|
4124
|
+
// (/editor and /copy are its single entries), so the rows say where
|
|
4125
|
+
// each key actually acts.
|
|
4126
|
+
const copyMessageRow = copyMessage ? `| \`${copyMessage}\` | Copy selection in /tree |\n` : "";
|
|
4127
|
+
const externalEditorRow = externalEditor
|
|
4128
|
+
? `| \`${externalEditor}\` | External editor (inside dialog inputs) |\n`
|
|
4129
|
+
: "";
|
|
4130
|
+
// Iron law (2026-10-01): the message queue is the /queue and /dequeue
|
|
4131
|
+
// commands' feature; rows only appear when a user re-configures a key.
|
|
4132
|
+
const followUpRow = followUp ? `| \`${followUp}\` | Queue follow-up message |\n` : "";
|
|
4133
|
+
const dequeueRow = dequeue ? `| \`${dequeue}\` | Restore queued messages |\n` : "";
|
|
4134
|
+
const cycleThinkingRow = cycleThinkingLevel ? `| \`${cycleThinkingLevel}\` | Cycle thinking level |\n` : "";
|
|
4135
|
+
const cycleModelsRow = cycleModelForward || cycleModelBackward
|
|
4136
|
+
? `| \`${cycleModelForward}\` / \`${cycleModelBackward}\` | Cycle models |\n`
|
|
4137
|
+
: "";
|
|
4138
|
+
// Suspend has no default key on native Windows; hide the row there.
|
|
4139
|
+
const suspendRow = suspend ? `| \`${suspend}\` | Suspend to background |\n` : "";
|
|
3953
4140
|
const hotkeys = `
|
|
3954
4141
|
**Navigation**
|
|
3955
4142
|
| Key | Action |
|
|
@@ -3982,16 +4169,9 @@ export class InteractiveMode {
|
|
|
3982
4169
|
| \`${interrupt}\` | Cancel autocomplete / abort streaming |
|
|
3983
4170
|
| \`${clear}\` | Clear editor (first) / exit (second) |
|
|
3984
4171
|
| \`${exit}\` | Exit (when editor is empty) |
|
|
3985
|
-
| \`${
|
|
3986
|
-
| \`${cycleThinkingLevel}\` | Cycle thinking level |
|
|
3987
|
-
| \`${cycleModelForward}\` / \`${cycleModelBackward}\` | Cycle models |
|
|
3988
|
-
| \`${selectModel}\` | Open model selector |
|
|
3989
|
-
| \`${expandTools}\` | Toggle tool output expansion |
|
|
4172
|
+
${suspendRow}${cycleThinkingRow}${cycleModelsRow}${selectModelRow}| \`${expandTools}\` | Toggle tool output expansion |
|
|
3990
4173
|
| \`${toggleThinking}\` | Toggle thinking block visibility |
|
|
3991
|
-
|
|
3992
|
-
| \`${copyMessage}\` | Copy last assistant message |
|
|
3993
|
-
| \`${followUp}\` | Queue follow-up message |
|
|
3994
|
-
| \`${dequeue}\` | Restore queued messages |
|
|
4174
|
+
${externalEditorRow}${copyMessageRow}${followUpRow}${dequeueRow}
|
|
3995
4175
|
| \`${pasteImage}\` | Paste image or text from clipboard |
|
|
3996
4176
|
| \`/\` | Slash commands |
|
|
3997
4177
|
| \`!\` | Run bash command |
|
|
@@ -4120,6 +4300,7 @@ export class InteractiveMode {
|
|
|
4120
4300
|
this.themeController.disableAutoSync();
|
|
4121
4301
|
this.footer.dispose();
|
|
4122
4302
|
this.footerDataProvider.dispose();
|
|
4303
|
+
this.failoverTicker?.dispose();
|
|
4123
4304
|
if (this.unsubscribe) {
|
|
4124
4305
|
this.unsubscribe();
|
|
4125
4306
|
}
|