apex-code 0.0.1-alpha.7 → 0.0.1-alpha.8
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 +146 -0
- package/README.md +5 -0
- package/dist/cli/args.d.ts +3 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +15 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/concurrent-session.d.ts +3 -0
- package/dist/cli/concurrent-session.d.ts.map +1 -0
- package/dist/cli/concurrent-session.js +13 -0
- package/dist/cli/concurrent-session.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -2
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +20 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +15 -2
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +9 -6
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +4 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +6 -2
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +10 -9
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/experimental.d.ts +4 -0
- package/dist/core/experimental.d.ts.map +1 -1
- package/dist/core/experimental.js +4 -0
- package/dist/core/experimental.js.map +1 -1
- package/dist/core/extensions/types.d.ts +4 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts +1 -2
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +2 -17
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/http-idle-timeout.d.ts +12 -0
- package/dist/core/http-idle-timeout.d.ts.map +1 -0
- package/dist/core/http-idle-timeout.js +27 -0
- package/dist/core/http-idle-timeout.js.map +1 -0
- package/dist/core/keybindings.d.ts +24 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/lsp/client.d.ts +2 -0
- package/dist/core/lsp/client.d.ts.map +1 -1
- package/dist/core/lsp/client.js +27 -2
- package/dist/core/lsp/client.js.map +1 -1
- package/dist/core/model-config.d.ts +5 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +1 -0
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/sandbox/child-entry.d.ts.map +1 -1
- package/dist/core/sandbox/child-entry.js +8 -1
- package/dist/core/sandbox/child-entry.js.map +1 -1
- package/dist/core/sandbox/cli-launch.d.ts +3 -0
- package/dist/core/sandbox/cli-launch.d.ts.map +1 -1
- package/dist/core/sandbox/cli-launch.js +25 -4
- package/dist/core/sandbox/cli-launch.js.map +1 -1
- package/dist/core/sandbox/cli-supervisor.d.ts.map +1 -1
- package/dist/core/sandbox/cli-supervisor.js +53 -13
- package/dist/core/sandbox/cli-supervisor.js.map +1 -1
- package/dist/core/sandbox/linux-backend.d.ts.map +1 -1
- package/dist/core/sandbox/linux-backend.js +15 -0
- package/dist/core/sandbox/linux-backend.js.map +1 -1
- package/dist/core/sandbox/macos-backend.d.ts.map +1 -1
- package/dist/core/sandbox/macos-backend.js +24 -1
- package/dist/core/sandbox/macos-backend.js.map +1 -1
- package/dist/core/sandbox/rpc/credential-client.d.ts +20 -0
- package/dist/core/sandbox/rpc/credential-client.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/credential-client.js +170 -0
- package/dist/core/sandbox/rpc/credential-client.js.map +1 -0
- package/dist/core/sandbox/rpc/credential-proxy.d.ts +44 -0
- package/dist/core/sandbox/rpc/credential-proxy.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/credential-proxy.js +381 -0
- package/dist/core/sandbox/rpc/credential-proxy.js.map +1 -0
- package/dist/core/sandbox/supervisor.d.ts +11 -0
- package/dist/core/sandbox/supervisor.d.ts.map +1 -1
- package/dist/core/sandbox/supervisor.js.map +1 -1
- package/dist/core/sandbox/terminal-size.d.ts +40 -0
- package/dist/core/sandbox/terminal-size.d.ts.map +1 -0
- package/dist/core/sandbox/terminal-size.js +105 -0
- package/dist/core/sandbox/terminal-size.js.map +1 -0
- package/dist/core/sdk.d.ts +6 -3
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +16 -4
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-lease.d.ts +31 -0
- package/dist/core/session-lease.d.ts.map +1 -0
- package/dist/core/session-lease.js +138 -0
- package/dist/core/session-lease.js.map +1 -0
- package/dist/core/session-manager.d.ts +5 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +3 -3
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +26 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +42 -3
- 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 +2 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts +2 -2
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +47 -62
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +2 -0
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +24 -0
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +2 -0
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +1 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +2 -0
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +35 -10
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/ls.d.ts +2 -0
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +5 -1
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +2 -0
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/web-search-exa.d.ts +33 -0
- package/dist/core/tools/web-search-exa.d.ts.map +1 -0
- package/dist/core/tools/web-search-exa.js +128 -0
- package/dist/core/tools/web-search-exa.js.map +1 -0
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +2 -0
- package/dist/core/tools/write.js.map +1 -1
- package/dist/core/web-search-provider.d.ts +64 -0
- package/dist/core/web-search-provider.d.ts.map +1 -0
- package/dist/core/web-search-provider.js +149 -0
- package/dist/core/web-search-provider.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +1 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +20 -1
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +23 -0
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +105 -2
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.d.ts +7 -1
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.js +11 -2
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts +4 -0
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +24 -6
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.js +6 -3
- package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
- package/dist/modes/interactive/components/first-use-hints.d.ts +8 -0
- package/dist/modes/interactive/components/first-use-hints.d.ts.map +1 -0
- package/dist/modes/interactive/components/first-use-hints.js +22 -0
- package/dist/modes/interactive/components/first-use-hints.js.map +1 -0
- package/dist/modes/interactive/components/footer.d.ts +12 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +128 -9
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +2 -0
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +20 -5
- package/dist/modes/interactive/components/model-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 +10 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/splash-header.d.ts +29 -4
- package/dist/modes/interactive/components/splash-header.d.ts.map +1 -1
- package/dist/modes/interactive/components/splash-header.js +58 -23
- package/dist/modes/interactive/components/splash-header.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +14 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +113 -35
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tool-panel.d.ts +30 -0
- package/dist/modes/interactive/components/tool-panel.d.ts.map +1 -0
- package/dist/modes/interactive/components/tool-panel.js +130 -0
- package/dist/modes/interactive/components/tool-panel.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +44 -9
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +282 -70
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/model-catalog-refresh.d.ts +7 -0
- package/dist/modes/interactive/model-catalog-refresh.d.ts.map +1 -0
- package/dist/modes/interactive/model-catalog-refresh.js +34 -0
- package/dist/modes/interactive/model-catalog-refresh.js.map +1 -0
- package/dist/modes/interactive/theme/apex.json +62 -63
- package/dist/modes/interactive/theme/dark.json +2 -0
- package/dist/modes/interactive/theme/light.json +2 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts +10 -2
- package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-controller.js +25 -11
- package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
- package/dist/modes/interactive/theme/theme-schema.json +9 -1
- package/dist/modes/interactive/theme/theme.d.ts +14 -3
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +55 -6
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/json-event.d.ts +4 -1
- package/dist/modes/json-event.d.ts.map +1 -1
- package/dist/modes/json-event.js +5 -2
- package/dist/modes/json-event.js.map +1 -1
- package/dist/server/create-harness.d.ts.map +1 -1
- package/dist/server/create-harness.js +2 -0
- package/dist/server/create-harness.js.map +1 -1
- package/dist/themes/apex-logo.d.ts +36 -16
- package/dist/themes/apex-logo.d.ts.map +1 -1
- package/dist/themes/apex-logo.js +32 -28
- package/dist/themes/apex-logo.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +10 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +15 -20
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/compaction.md +3 -3
- package/docs/custom-provider.md +1 -1
- package/docs/environment-variables.md +1 -0
- package/docs/extensions.md +4 -0
- package/docs/json.md +6 -4
- package/docs/keybindings.md +8 -2
- package/docs/rpc.md +15 -4
- package/docs/sdk.md +9 -0
- package/docs/settings.md +63 -0
- package/docs/terminal-setup.md +23 -2
- package/docs/themes.md +24 -3
- package/docs/tui.md +2 -2
- package/docs/usage.md +3 -2
- 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/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/subagent/README.md +2 -0
- package/examples/extensions/subagent/agents.ts +40 -9
- package/examples/extensions/subagent/index.ts +21 -3
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +27 -78
- package/package.json +6 -6
|
@@ -54,6 +54,7 @@ import { EarendilAnnouncementComponent } from "./components/earendil-announcemen
|
|
|
54
54
|
import { ExtensionEditorComponent } from "./components/extension-editor.js";
|
|
55
55
|
import { ExtensionInputComponent } from "./components/extension-input.js";
|
|
56
56
|
import { ExtensionSelectorComponent } from "./components/extension-selector.js";
|
|
57
|
+
import { FirstUseHints } from "./components/first-use-hints.js";
|
|
57
58
|
import { FooterComponent, formatTokens } from "./components/footer.js";
|
|
58
59
|
import { formatKeyText, keyDisplayText, keyHint, keyText, rawKeyHint } from "./components/keybinding-hints.js";
|
|
59
60
|
import { LoginDialogComponent } from "./components/login-dialog.js";
|
|
@@ -72,6 +73,7 @@ import { TrustSelectorComponent } from "./components/trust-selector.js";
|
|
|
72
73
|
import { UserMessageComponent } from "./components/user-message.js";
|
|
73
74
|
import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
|
|
74
75
|
import { editInExternalEditor } from "./external-editor.js";
|
|
76
|
+
import { refreshModelCatalogs } from "./model-catalog-refresh.js";
|
|
75
77
|
import { getModelSearchText } from "./model-search.js";
|
|
76
78
|
import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, onThemeChange, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
|
|
77
79
|
import { InteractiveThemeController } from "./theme/theme-controller.js";
|
|
@@ -172,9 +174,21 @@ function formatLoginProviderCompletionDescription(provider) {
|
|
|
172
174
|
export function createInteractiveTui(options) {
|
|
173
175
|
const terminal = options.terminal ?? new ProcessTerminal();
|
|
174
176
|
if (options.tuiMode === "fullscreen") {
|
|
177
|
+
const styleSearchMatch = (text) => theme.bg("searchMatchBg", theme.fg("searchMatchText", text));
|
|
175
178
|
return new TuiAltScreen(terminal, options.showHardwareCursor, options.logDirectory, {
|
|
179
|
+
searchMatchStyle: (text) => theme.underline(styleSearchMatch(text)),
|
|
180
|
+
searchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),
|
|
176
181
|
openUrl: openBrowser,
|
|
177
182
|
onRightClickPaste: options.onRightClickPaste,
|
|
183
|
+
copySelection: async (text) => {
|
|
184
|
+
try {
|
|
185
|
+
await copyToClipboard(text);
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
},
|
|
178
192
|
});
|
|
179
193
|
}
|
|
180
194
|
return new TuiMainScreen(terminal, options.showHardwareCursor, options.logDirectory);
|
|
@@ -236,6 +250,14 @@ export class InteractiveMode {
|
|
|
236
250
|
footer;
|
|
237
251
|
footerContainer;
|
|
238
252
|
footerDataProvider;
|
|
253
|
+
/**
|
|
254
|
+
* Whether the startup screen is showing its counted inventory line.
|
|
255
|
+
*
|
|
256
|
+
* Warnings are only allowed to collapse into that count when the count is
|
|
257
|
+
* actually on screen. Under a quiet startup there is no splash header, so
|
|
258
|
+
* collapsing them would hide conflicts entirely rather than summarise them.
|
|
259
|
+
*/
|
|
260
|
+
startupInventoryVisible = false;
|
|
239
261
|
// Stored so the same manager can be injected into custom editors, selectors, and extension UI.
|
|
240
262
|
keybindings;
|
|
241
263
|
version;
|
|
@@ -258,6 +280,7 @@ export class InteractiveMode {
|
|
|
258
280
|
// Status line tracking (for mutating immediately-sequential status updates)
|
|
259
281
|
lastStatusSpacer = undefined;
|
|
260
282
|
lastStatusText = undefined;
|
|
283
|
+
managedToolStatusStarted = false;
|
|
261
284
|
// Streaming message tracking
|
|
262
285
|
streamingComponent = undefined;
|
|
263
286
|
streamingMessage = undefined;
|
|
@@ -279,6 +302,7 @@ export class InteractiveMode {
|
|
|
279
302
|
signalCleanupHandlers = [];
|
|
280
303
|
// Track if editor is in bash mode (text starts with !)
|
|
281
304
|
isBashMode = false;
|
|
305
|
+
firstUseHints;
|
|
282
306
|
// Track current bash execution component
|
|
283
307
|
bashComponent = undefined;
|
|
284
308
|
// Track pending bash components (shown in pending area, moved to chat on submit)
|
|
@@ -328,6 +352,14 @@ export class InteractiveMode {
|
|
|
328
352
|
get settingsManager() {
|
|
329
353
|
return this.session.settingsManager;
|
|
330
354
|
}
|
|
355
|
+
offerFirstUseHint(id) {
|
|
356
|
+
this.firstUseHints ??= new FirstUseHints(this.settingsManager.getFirstUseHints());
|
|
357
|
+
const hint = this.firstUseHints.offer(id);
|
|
358
|
+
if (!hint)
|
|
359
|
+
return;
|
|
360
|
+
this.settingsManager.setFirstUseHints(this.firstUseHints.getSeen());
|
|
361
|
+
this.chatContainer.addChild(new Text(theme.fg("dim", hint), this.outputPad, 0));
|
|
362
|
+
}
|
|
331
363
|
constructor(runtimeHost, options = {}) {
|
|
332
364
|
this.runtimeHost = runtimeHost;
|
|
333
365
|
const tuiMode = options.tuiMode ?? this.settingsManager.getTuiMode();
|
|
@@ -338,6 +370,7 @@ export class InteractiveMode {
|
|
|
338
370
|
});
|
|
339
371
|
this.runtimeHost.setRebindSession(async () => {
|
|
340
372
|
await this.rebindCurrentSession({ renderBeforeBind: true });
|
|
373
|
+
await this.themeController.applyFromSettings();
|
|
341
374
|
});
|
|
342
375
|
this.version = VERSION;
|
|
343
376
|
this.renderer = createInteractiveTui({
|
|
@@ -366,11 +399,14 @@ export class InteractiveMode {
|
|
|
366
399
|
this.defaultEditor = new CustomEditor(this.ui, getEditorTheme(), this.keybindings, {
|
|
367
400
|
paddingX: editorPaddingX,
|
|
368
401
|
autocompleteMaxVisible,
|
|
369
|
-
promptPrefix: "
|
|
402
|
+
promptPrefix: "› ",
|
|
370
403
|
promptColor: (text) => theme.fg("accent", text),
|
|
371
|
-
placeholder: "Ask anything
|
|
404
|
+
placeholder: "Ask anything",
|
|
372
405
|
placeholderColor: (text) => theme.fg("dim", text),
|
|
373
406
|
commandColor: (text) => theme.fg("accent", text),
|
|
407
|
+
surfaceColor: (text) => theme.bg("userMessageBg", text),
|
|
408
|
+
autocompleteRule: (width) => theme.fg("borderMuted", (this.settingsManager.getSymbolPreset() === "ascii" ? "-" : "┄").repeat(Math.max(1, width))),
|
|
409
|
+
autocompleteFooter: () => this.buildAutocompleteFooter(),
|
|
374
410
|
});
|
|
375
411
|
this.editor = this.defaultEditor;
|
|
376
412
|
this.editorContainer = new Container();
|
|
@@ -385,7 +421,12 @@ export class InteractiveMode {
|
|
|
385
421
|
this.outputPad = this.settingsManager.getOutputPad();
|
|
386
422
|
// Register themes from resource loader and initialize
|
|
387
423
|
setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
|
|
388
|
-
this.themeController = new InteractiveThemeController(this.ui,
|
|
424
|
+
this.themeController = new InteractiveThemeController(this.ui, {
|
|
425
|
+
getSettingsManager: () => this.settingsManager,
|
|
426
|
+
showError: (message) => this.showError(message),
|
|
427
|
+
onChanged: () => this.updateEditorBorderColor(),
|
|
428
|
+
initialThemeSetting: options.initialThemeSetting,
|
|
429
|
+
});
|
|
389
430
|
}
|
|
390
431
|
getAutocompleteSourceTag(sourceInfo) {
|
|
391
432
|
if (!sourceInfo) {
|
|
@@ -548,14 +589,14 @@ export class InteractiveMode {
|
|
|
548
589
|
tui.setLayoutRoot(this.fullscreenLayoutRoot);
|
|
549
590
|
}
|
|
550
591
|
}
|
|
551
|
-
stopInteractiveTui() {
|
|
552
|
-
if (this.renderer.mode === "fullscreen") {
|
|
592
|
+
stopInteractiveTui(fullscreenExitOutput) {
|
|
593
|
+
if (this.renderer.mode === "fullscreen" && fullscreenExitOutput === "transcript") {
|
|
553
594
|
while (this.renderer.hasOverlayEntries)
|
|
554
595
|
this.renderer.hideOverlay();
|
|
555
596
|
this.switchTuiMode("regular", false, false);
|
|
556
597
|
this.renderer.renderNow();
|
|
557
598
|
}
|
|
558
|
-
this.ui.stop();
|
|
599
|
+
this.ui.stop({ preserveScreen: this.renderer.mode === "fullscreen" });
|
|
559
600
|
}
|
|
560
601
|
switchTuiMode(mode, restoreProgress = true, startRenderer = true) {
|
|
561
602
|
const previousUi = this.renderer;
|
|
@@ -612,10 +653,9 @@ export class InteractiveMode {
|
|
|
612
653
|
this.registerSignalHandlers();
|
|
613
654
|
// Load changelog (only show new entries, skip for resumed sessions)
|
|
614
655
|
this.changelogMarkdown = this.getChangelogForDisplay();
|
|
615
|
-
//
|
|
616
|
-
//
|
|
617
|
-
|
|
618
|
-
this.fdPath = fdPath;
|
|
656
|
+
// Resolve the effective mode before mounting or focusing components. Both
|
|
657
|
+
// operations can schedule a frame even before the renderer is started.
|
|
658
|
+
await this.refreshFooterPermissionMode();
|
|
619
659
|
if (this.session.scopedModels.length > 0 && (this.options.verbose || !this.settingsManager.getQuietStartup())) {
|
|
620
660
|
const modelList = this.session.scopedModels
|
|
621
661
|
.map((sm) => {
|
|
@@ -659,9 +699,11 @@ export class InteractiveMode {
|
|
|
659
699
|
this.widgetContainerBelow,
|
|
660
700
|
this.footerContainer,
|
|
661
701
|
]);
|
|
702
|
+
// Accept text while startup completes, but only enable interrupt, exit, and submission feedback.
|
|
703
|
+
this.defaultEditor.onAction("app.clear", () => this.handleCtrlC());
|
|
704
|
+
this.defaultEditor.onCtrlD = () => this.handleCtrlD();
|
|
705
|
+
this.defaultEditor.onSubmit = (text) => this.handleStartupSubmit(text);
|
|
662
706
|
this.ui.setFocus(this.editor);
|
|
663
|
-
this.setupKeyHandlers();
|
|
664
|
-
this.setupEditorSubmitHandler();
|
|
665
707
|
// Start the UI before initializing extensions so session_start handlers can use interactive dialogs
|
|
666
708
|
this.ui.start();
|
|
667
709
|
this.isInitialized = true;
|
|
@@ -698,18 +740,42 @@ export class InteractiveMode {
|
|
|
698
740
|
this.builtInHeader = new ApexSplashHeader(this.version, () => this.session.state.model?.id, () => this.session.sessionManager.getCwd(), this.options.verbose ? expandedInstructions : undefined, {
|
|
699
741
|
topPadding: true,
|
|
700
742
|
getSymbolPreset: () => this.settingsManager.getSymbolPreset(),
|
|
743
|
+
getExtraMetadata: () => {
|
|
744
|
+
// Which branch the session is rooted on differs between launches
|
|
745
|
+
// and is not derivable from the prompt, so it belongs here. The
|
|
746
|
+
// tray carries it too, but drops it first when space is tight.
|
|
747
|
+
const branch = this.footerDataProvider.getGitBranch();
|
|
748
|
+
return branch ? [{ label: "branch", value: branch }] : [];
|
|
749
|
+
},
|
|
750
|
+
getInventory: () => this.buildStartupInventory(),
|
|
751
|
+
inventoryHint: "/resources",
|
|
752
|
+
getShortcuts: () => this.buildStartupShortcuts(),
|
|
701
753
|
getHint: () => "Apex Code can explain its own features and look up its docs.",
|
|
702
754
|
});
|
|
703
755
|
// Setup UI layout
|
|
756
|
+
this.startupInventoryVisible = true;
|
|
704
757
|
this.headerContainer.addChild(this.builtInHeader);
|
|
705
758
|
this.headerContainer.addChild(new Spacer(1));
|
|
706
759
|
}
|
|
707
760
|
else {
|
|
708
761
|
// Minimal header when silenced
|
|
762
|
+
this.startupInventoryVisible = false;
|
|
709
763
|
this.builtInHeader = new Text("", 0, 0);
|
|
710
764
|
this.headerContainer.addChild(this.builtInHeader);
|
|
711
765
|
}
|
|
712
766
|
this.ui.requestRender();
|
|
767
|
+
// Ensure fd and rg are available after mounting the TUI (downloads if missing, adds to PATH via getBinDir)
|
|
768
|
+
// so slow downloads do not make startup appear frozen.
|
|
769
|
+
// Both are needed: fd for autocomplete, rg for grep tool and bash commands.
|
|
770
|
+
const [fdPath] = await Promise.all([
|
|
771
|
+
ensureTool("fd", (status) => this.showManagedToolStatus(status)),
|
|
772
|
+
ensureTool("rg", (status) => this.showManagedToolStatus(status)),
|
|
773
|
+
]);
|
|
774
|
+
this.fdPath = fdPath;
|
|
775
|
+
// Enable the remaining input handlers only after managed-tool setup completes.
|
|
776
|
+
this.setupKeyHandlers();
|
|
777
|
+
this.setupEditorSubmitHandler();
|
|
778
|
+
this.ui.requestRender();
|
|
713
779
|
// Initialize extensions first so resources are shown before messages
|
|
714
780
|
await this.rebindCurrentSession();
|
|
715
781
|
// Render initial messages AFTER showing loaded resources
|
|
@@ -749,8 +815,7 @@ export class InteractiveMode {
|
|
|
749
815
|
if (!getApexEnvironment("APEX_CODE_OFFLINE")) {
|
|
750
816
|
const controller = new AbortController();
|
|
751
817
|
const timeout = setTimeout(() => controller.abort(), 15_000);
|
|
752
|
-
void this.session.modelRuntime
|
|
753
|
-
.refresh({ signal: controller.signal })
|
|
818
|
+
void refreshModelCatalogs(this.session.modelRuntime, controller.signal)
|
|
754
819
|
.then(() => this.updateAvailableProviderCount())
|
|
755
820
|
.catch(() => { })
|
|
756
821
|
.finally(() => clearTimeout(timeout));
|
|
@@ -1205,7 +1270,7 @@ export class InteractiveMode {
|
|
|
1205
1270
|
showLoadedResources(options) {
|
|
1206
1271
|
// Resource rendering is idempotent; chat clears no longer clear this separate container.
|
|
1207
1272
|
this.loadedResourcesContainer.clear();
|
|
1208
|
-
const showListing = options?.force || this.options.verbose
|
|
1273
|
+
const showListing = options?.force || this.options.verbose;
|
|
1209
1274
|
const showDiagnostics = showListing || options?.showDiagnosticsWhenQuiet === true;
|
|
1210
1275
|
if (!showListing && !showDiagnostics) {
|
|
1211
1276
|
return;
|
|
@@ -1322,43 +1387,105 @@ export class InteractiveMode {
|
|
|
1322
1387
|
addLoadedSection("Themes", themeCompactList, themeList);
|
|
1323
1388
|
}
|
|
1324
1389
|
}
|
|
1390
|
+
// Warnings collapse into the counted line when that line is on screen; the
|
|
1391
|
+
// full trace lives behind /resources. Errors always surface inline,
|
|
1392
|
+
// because an error means a resource did not load at all.
|
|
1325
1393
|
if (showDiagnostics) {
|
|
1326
|
-
const
|
|
1327
|
-
|
|
1328
|
-
const
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
const
|
|
1335
|
-
this.loadedResourcesContainer.addChild(new Text(`${theme.fg(
|
|
1336
|
-
this.loadedResourcesContainer.addChild(new Spacer(1));
|
|
1337
|
-
}
|
|
1338
|
-
const extensionDiagnostics = [];
|
|
1339
|
-
const extensionErrors = this.session.resourceLoader.getExtensions().errors;
|
|
1340
|
-
if (extensionErrors.length > 0) {
|
|
1341
|
-
for (const error of extensionErrors) {
|
|
1342
|
-
extensionDiagnostics.push({ type: "error", message: error.error, path: error.path });
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
const commandDiagnostics = this.session.extensionRunner.getCommandDiagnostics();
|
|
1346
|
-
extensionDiagnostics.push(...commandDiagnostics);
|
|
1347
|
-
extensionDiagnostics.push(...this.getBuiltInCommandConflictDiagnostics(this.session.extensionRunner));
|
|
1348
|
-
const shortcutDiagnostics = this.session.extensionRunner.getShortcutDiagnostics();
|
|
1349
|
-
extensionDiagnostics.push(...shortcutDiagnostics);
|
|
1350
|
-
if (extensionDiagnostics.length > 0) {
|
|
1351
|
-
const warningLines = this.formatDiagnostics(extensionDiagnostics, sourceInfos);
|
|
1352
|
-
this.loadedResourcesContainer.addChild(new Text(`${theme.fg("warning", "[Extension issues]")}\n${warningLines}`, 0, 0));
|
|
1353
|
-
this.loadedResourcesContainer.addChild(new Spacer(1));
|
|
1354
|
-
}
|
|
1355
|
-
const themeDiagnostics = themesResult.diagnostics;
|
|
1356
|
-
if (themeDiagnostics.length > 0) {
|
|
1357
|
-
const warningLines = this.formatDiagnostics(themeDiagnostics, sourceInfos);
|
|
1358
|
-
this.loadedResourcesContainer.addChild(new Text(`${theme.fg("warning", "[Theme conflicts]")}\n${warningLines}`, 0, 0));
|
|
1394
|
+
const collapseWarnings = !showListing && this.startupInventoryVisible;
|
|
1395
|
+
for (const group of this.collectDiagnosticGroups()) {
|
|
1396
|
+
const visible = collapseWarnings
|
|
1397
|
+
? group.diagnostics.filter((diagnostic) => diagnostic.type === "error")
|
|
1398
|
+
: group.diagnostics;
|
|
1399
|
+
if (visible.length === 0)
|
|
1400
|
+
continue;
|
|
1401
|
+
const severity = visible.some((diagnostic) => diagnostic.type === "error") ? "error" : "warning";
|
|
1402
|
+
const body = this.formatDiagnostics(visible, sourceInfos);
|
|
1403
|
+
this.loadedResourcesContainer.addChild(new Text(`${theme.fg(severity, `[${group.label}]`)}\n${body}`, 0, 0));
|
|
1359
1404
|
this.loadedResourcesContainer.addChild(new Spacer(1));
|
|
1360
1405
|
}
|
|
1361
1406
|
}
|
|
1407
|
+
// Counts on the startup screen are read at render time, so they fill in
|
|
1408
|
+
// once resources finish loading without an explicit refresh here.
|
|
1409
|
+
this.builtInHeader?.invalidate?.();
|
|
1410
|
+
}
|
|
1411
|
+
/**
|
|
1412
|
+
* Every startup diagnostic, grouped by the resource kind that produced it.
|
|
1413
|
+
*
|
|
1414
|
+
* One list feeds both the counted line in the header and the detailed
|
|
1415
|
+
* listing, so the two can never disagree about how many issues exist.
|
|
1416
|
+
*/
|
|
1417
|
+
collectDiagnosticGroups() {
|
|
1418
|
+
const extensionDiagnostics = [
|
|
1419
|
+
...this.session.resourceLoader
|
|
1420
|
+
.getExtensions()
|
|
1421
|
+
.errors.map((error) => ({ type: "error", message: error.error, path: error.path })),
|
|
1422
|
+
...this.session.extensionRunner.getCommandDiagnostics(),
|
|
1423
|
+
...this.getBuiltInCommandConflictDiagnostics(this.session.extensionRunner),
|
|
1424
|
+
...this.session.extensionRunner.getShortcutDiagnostics(),
|
|
1425
|
+
];
|
|
1426
|
+
return [
|
|
1427
|
+
{ label: "Skill conflicts", diagnostics: this.session.resourceLoader.getSkills().diagnostics },
|
|
1428
|
+
{ label: "Prompt conflicts", diagnostics: this.session.resourceLoader.getPrompts().diagnostics },
|
|
1429
|
+
{ label: "Extension issues", diagnostics: extensionDiagnostics },
|
|
1430
|
+
{ label: "Theme conflicts", diagnostics: this.session.resourceLoader.getThemes().diagnostics },
|
|
1431
|
+
].filter((group) => group.diagnostics.length > 0);
|
|
1432
|
+
}
|
|
1433
|
+
/**
|
|
1434
|
+
* The counted line in the ruled band under the mark.
|
|
1435
|
+
*
|
|
1436
|
+
* The startup screen used to print all 152 skill names across fifteen rows.
|
|
1437
|
+
* A count answers the only question that screen can usefully answer, which
|
|
1438
|
+
* is what got loaded; the names live behind /resources now.
|
|
1439
|
+
*/
|
|
1440
|
+
/**
|
|
1441
|
+
* The sigil row under the hint: how to reach commands, bash, and files.
|
|
1442
|
+
*
|
|
1443
|
+
* These are the only three characters that change what the composer does, so
|
|
1444
|
+
* they take the accent and everything around them stays quiet.
|
|
1445
|
+
*/
|
|
1446
|
+
/** Live keybindings under the autocomplete dropdown. */
|
|
1447
|
+
buildAutocompleteFooter() {
|
|
1448
|
+
const separator = theme.fg("borderMuted", this.settingsManager.getSymbolPreset() === "ascii" ? " - " : " · ");
|
|
1449
|
+
return [
|
|
1450
|
+
rawKeyHint(`${keyText("tui.select.up")}/${keyText("tui.select.down")}`, "move"),
|
|
1451
|
+
keyHint("tui.select.confirm", "select"),
|
|
1452
|
+
keyHint("tui.input.tab", "complete"),
|
|
1453
|
+
keyHint("tui.select.cancel", "dismiss"),
|
|
1454
|
+
].join(separator);
|
|
1455
|
+
}
|
|
1456
|
+
buildStartupShortcuts() {
|
|
1457
|
+
const entry = (sigil, label) => `${theme.fg("accent", sigil)} ${theme.fg("muted", label)}`;
|
|
1458
|
+
return [
|
|
1459
|
+
entry("/", "commands"),
|
|
1460
|
+
entry("!", "bash"),
|
|
1461
|
+
entry("@", "files"),
|
|
1462
|
+
`${theme.fg("dim", keyText("app.interrupt"))} ${theme.fg("muted", "interrupt")}`,
|
|
1463
|
+
].join(" ");
|
|
1464
|
+
}
|
|
1465
|
+
buildStartupInventory() {
|
|
1466
|
+
const counted = (total, singular) => `${total} ${singular}${total === 1 ? "" : "s"}`;
|
|
1467
|
+
const parts = [];
|
|
1468
|
+
const skills = this.session.resourceLoader.getSkills().skills.length;
|
|
1469
|
+
const extensions = this.session.resourceLoader.getExtensions().extensions.filter((e) => !e.hidden).length;
|
|
1470
|
+
const prompts = this.session.resourceLoader.getPrompts().prompts.length;
|
|
1471
|
+
if (skills > 0)
|
|
1472
|
+
parts.push(theme.fg("muted", counted(skills, "skill")));
|
|
1473
|
+
if (extensions > 0)
|
|
1474
|
+
parts.push(theme.fg("muted", counted(extensions, "extension")));
|
|
1475
|
+
if (prompts > 0)
|
|
1476
|
+
parts.push(theme.fg("muted", counted(prompts, "prompt")));
|
|
1477
|
+
const diagnostics = this.collectDiagnosticGroups().flatMap((group) => group.diagnostics);
|
|
1478
|
+
const errors = diagnostics.filter((diagnostic) => diagnostic.type === "error").length;
|
|
1479
|
+
const conflicts = diagnostics.length - errors;
|
|
1480
|
+
if (errors > 0)
|
|
1481
|
+
parts.push(theme.fg("error", counted(errors, "error")));
|
|
1482
|
+
if (conflicts > 0)
|
|
1483
|
+
parts.push(theme.fg("warning", counted(conflicts, "conflict")));
|
|
1484
|
+
if (parts.length === 0)
|
|
1485
|
+
return undefined;
|
|
1486
|
+
const ascii = this.settingsManager.getSymbolPreset() === "ascii";
|
|
1487
|
+
const separator = theme.fg("borderMuted", ascii ? " - " : " · ");
|
|
1488
|
+
return parts.join(separator);
|
|
1362
1489
|
}
|
|
1363
1490
|
/**
|
|
1364
1491
|
* Initialize the extension system with TUI-based UI context.
|
|
@@ -1446,7 +1573,7 @@ export class InteractiveMode {
|
|
|
1446
1573
|
this.applyFullscreenScrollbarSetting();
|
|
1447
1574
|
this.footer.setSession(this.session);
|
|
1448
1575
|
this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
|
|
1449
|
-
this.refreshFooterPermissionMode();
|
|
1576
|
+
void this.refreshFooterPermissionMode();
|
|
1450
1577
|
this.footerDataProvider.setCwd(this.sessionManager.getCwd());
|
|
1451
1578
|
this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
|
|
1452
1579
|
this.outputPad = this.settingsManager.getOutputPad();
|
|
@@ -1489,7 +1616,7 @@ export class InteractiveMode {
|
|
|
1489
1616
|
const message = error instanceof Error ? error.message : String(error);
|
|
1490
1617
|
this.showError(`${prefix}: ${message}`);
|
|
1491
1618
|
stopThemeWatcher();
|
|
1492
|
-
this.stop();
|
|
1619
|
+
this.stop("transcript");
|
|
1493
1620
|
process.exit(1);
|
|
1494
1621
|
}
|
|
1495
1622
|
renderCurrentSessionState() {
|
|
@@ -1499,9 +1626,14 @@ export class InteractiveMode {
|
|
|
1499
1626
|
this.compactionQueuedMessages = [];
|
|
1500
1627
|
this.streamingComponent = undefined;
|
|
1501
1628
|
this.streamingMessage = undefined;
|
|
1502
|
-
this.
|
|
1629
|
+
this.clearPendingTools();
|
|
1503
1630
|
this.renderInitialMessages();
|
|
1504
1631
|
}
|
|
1632
|
+
clearPendingTools() {
|
|
1633
|
+
for (const component of this.pendingTools.values())
|
|
1634
|
+
component.dispose();
|
|
1635
|
+
this.pendingTools.clear();
|
|
1636
|
+
}
|
|
1505
1637
|
/**
|
|
1506
1638
|
* Get a registered tool definition by name (for custom rendering).
|
|
1507
1639
|
*/
|
|
@@ -1884,7 +2016,7 @@ export class InteractiveMode {
|
|
|
1884
2016
|
/**
|
|
1885
2017
|
* Show a selector for extensions.
|
|
1886
2018
|
*/
|
|
1887
|
-
showExtensionSelector(title, options, opts) {
|
|
2019
|
+
showExtensionSelector(title, options, opts, enableSearch = false) {
|
|
1888
2020
|
return new Promise((resolve) => {
|
|
1889
2021
|
if (opts?.signal?.aborted) {
|
|
1890
2022
|
resolve(undefined);
|
|
@@ -1903,7 +2035,12 @@ export class InteractiveMode {
|
|
|
1903
2035
|
opts?.signal?.removeEventListener("abort", onAbort);
|
|
1904
2036
|
this.hideExtensionSelector();
|
|
1905
2037
|
resolve(undefined);
|
|
1906
|
-
}, {
|
|
2038
|
+
}, {
|
|
2039
|
+
tui: this.ui,
|
|
2040
|
+
timeout: opts?.timeout,
|
|
2041
|
+
onToggleToolsExpanded: () => this.toggleToolOutputExpansion(),
|
|
2042
|
+
enableSearch,
|
|
2043
|
+
});
|
|
1907
2044
|
this.disposeActiveSelector();
|
|
1908
2045
|
this.editorContainer.clear();
|
|
1909
2046
|
this.editorContainer.addChild(this.extensionSelector);
|
|
@@ -1922,6 +2059,40 @@ export class InteractiveMode {
|
|
|
1922
2059
|
this.ui.setFocus(this.editor);
|
|
1923
2060
|
this.ui.requestRender();
|
|
1924
2061
|
}
|
|
2062
|
+
async showConfigurationIndex() {
|
|
2063
|
+
const choice = await this.showExtensionSelector("Configuration", [
|
|
2064
|
+
"Settings",
|
|
2065
|
+
"Model",
|
|
2066
|
+
"Model cycling",
|
|
2067
|
+
"Provider login",
|
|
2068
|
+
"Provider logout",
|
|
2069
|
+
"Project trust",
|
|
2070
|
+
"Resources, extensions, and MCP adapters",
|
|
2071
|
+
], undefined, true);
|
|
2072
|
+
switch (choice) {
|
|
2073
|
+
case "Settings":
|
|
2074
|
+
await this.showSettingsSelector();
|
|
2075
|
+
break;
|
|
2076
|
+
case "Model":
|
|
2077
|
+
this.showModelSelector();
|
|
2078
|
+
break;
|
|
2079
|
+
case "Model cycling":
|
|
2080
|
+
await this.showModelsSelector();
|
|
2081
|
+
break;
|
|
2082
|
+
case "Provider login":
|
|
2083
|
+
await this.handleLoginCommand();
|
|
2084
|
+
break;
|
|
2085
|
+
case "Provider logout":
|
|
2086
|
+
this.showOAuthSelector("logout");
|
|
2087
|
+
break;
|
|
2088
|
+
case "Project trust":
|
|
2089
|
+
this.showTrustSelector();
|
|
2090
|
+
break;
|
|
2091
|
+
case "Resources, extensions, and MCP adapters":
|
|
2092
|
+
this.showStatus("Run apex-code config to manage resources, extensions, and MCP adapters.");
|
|
2093
|
+
break;
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
1925
2096
|
/**
|
|
1926
2097
|
* Show a confirmation dialog for extensions.
|
|
1927
2098
|
*/
|
|
@@ -2233,6 +2404,8 @@ export class InteractiveMode {
|
|
|
2233
2404
|
const wasBashMode = this.isBashMode;
|
|
2234
2405
|
this.isBashMode = text.trimStart().startsWith("!");
|
|
2235
2406
|
if (wasBashMode !== this.isBashMode) {
|
|
2407
|
+
if (this.isBashMode)
|
|
2408
|
+
this.offerFirstUseHint("bash");
|
|
2236
2409
|
this.updateEditorBorderColor();
|
|
2237
2410
|
}
|
|
2238
2411
|
};
|
|
@@ -2281,6 +2454,10 @@ export class InteractiveMode {
|
|
|
2281
2454
|
// Silently ignore clipboard errors (may not have permission, etc.)
|
|
2282
2455
|
}
|
|
2283
2456
|
}
|
|
2457
|
+
handleStartupSubmit(text) {
|
|
2458
|
+
this.editor.setText(text);
|
|
2459
|
+
this.showStatus("Startup is still in progress");
|
|
2460
|
+
}
|
|
2284
2461
|
setupEditorSubmitHandler() {
|
|
2285
2462
|
this.defaultEditor.onSubmit = async (text) => {
|
|
2286
2463
|
text = text.trim();
|
|
@@ -2294,6 +2471,11 @@ export class InteractiveMode {
|
|
|
2294
2471
|
this.pendingPermissionModeWrite = undefined;
|
|
2295
2472
|
}
|
|
2296
2473
|
// Handle commands
|
|
2474
|
+
if (text === "/config") {
|
|
2475
|
+
this.editor.setText("");
|
|
2476
|
+
await this.showConfigurationIndex();
|
|
2477
|
+
return;
|
|
2478
|
+
}
|
|
2297
2479
|
if (text === "/settings") {
|
|
2298
2480
|
this.editor.setText("");
|
|
2299
2481
|
await this.showSettingsSelector();
|
|
@@ -2345,6 +2527,11 @@ export class InteractiveMode {
|
|
|
2345
2527
|
this.editor.setText("");
|
|
2346
2528
|
return;
|
|
2347
2529
|
}
|
|
2530
|
+
if (text === "/resources") {
|
|
2531
|
+
this.showLoadedResources({ force: true });
|
|
2532
|
+
this.editor.setText("");
|
|
2533
|
+
return;
|
|
2534
|
+
}
|
|
2348
2535
|
if (text === "/hotkeys") {
|
|
2349
2536
|
this.handleHotkeysCommand();
|
|
2350
2537
|
this.editor.setText("");
|
|
@@ -2485,7 +2672,7 @@ export class InteractiveMode {
|
|
|
2485
2672
|
this.footer.invalidate();
|
|
2486
2673
|
switch (event.type) {
|
|
2487
2674
|
case "agent_start":
|
|
2488
|
-
this.
|
|
2675
|
+
this.clearPendingTools();
|
|
2489
2676
|
if (this.settingsManager.getShowTerminalProgress()) {
|
|
2490
2677
|
this.ui.terminal.setProgress(true);
|
|
2491
2678
|
}
|
|
@@ -2537,6 +2724,7 @@ export class InteractiveMode {
|
|
|
2537
2724
|
this.streamingMessage = event.message;
|
|
2538
2725
|
this.chatContainer.addChild(this.streamingComponent);
|
|
2539
2726
|
this.streamingComponent.updateContent(this.streamingMessage, true);
|
|
2727
|
+
this.updateEditorBorderColor();
|
|
2540
2728
|
this.ui.requestRender();
|
|
2541
2729
|
}
|
|
2542
2730
|
break;
|
|
@@ -2554,6 +2742,7 @@ export class InteractiveMode {
|
|
|
2554
2742
|
component.setExpanded(this.toolOutputExpanded);
|
|
2555
2743
|
this.chatContainer.addChild(component);
|
|
2556
2744
|
this.pendingTools.set(content.id, component);
|
|
2745
|
+
this.offerFirstUseHint("tool-expand");
|
|
2557
2746
|
}
|
|
2558
2747
|
else {
|
|
2559
2748
|
const component = this.pendingTools.get(content.id);
|
|
@@ -2591,7 +2780,7 @@ export class InteractiveMode {
|
|
|
2591
2780
|
isError: true,
|
|
2592
2781
|
});
|
|
2593
2782
|
}
|
|
2594
|
-
this.
|
|
2783
|
+
this.clearPendingTools();
|
|
2595
2784
|
}
|
|
2596
2785
|
else {
|
|
2597
2786
|
// Args are now complete - trigger diff computation for edit tools
|
|
@@ -2603,6 +2792,7 @@ export class InteractiveMode {
|
|
|
2603
2792
|
this.streamingComponent = undefined;
|
|
2604
2793
|
this.streamingMessage = undefined;
|
|
2605
2794
|
this.footer.invalidate();
|
|
2795
|
+
this.updateEditorBorderColor();
|
|
2606
2796
|
}
|
|
2607
2797
|
this.ui.requestRender();
|
|
2608
2798
|
break;
|
|
@@ -2651,7 +2841,7 @@ export class InteractiveMode {
|
|
|
2651
2841
|
this.streamingComponent = undefined;
|
|
2652
2842
|
this.streamingMessage = undefined;
|
|
2653
2843
|
}
|
|
2654
|
-
this.
|
|
2844
|
+
this.clearPendingTools();
|
|
2655
2845
|
this.ui.requestRender();
|
|
2656
2846
|
break;
|
|
2657
2847
|
case "agent_settled":
|
|
@@ -2763,6 +2953,19 @@ export class InteractiveMode {
|
|
|
2763
2953
|
: message.content.filter((c) => c.type === "text");
|
|
2764
2954
|
return textBlocks.map((c) => c.text).join("");
|
|
2765
2955
|
}
|
|
2956
|
+
/** Show a managed-tool status update in the chat. */
|
|
2957
|
+
showManagedToolStatus(status) {
|
|
2958
|
+
if (!this.managedToolStatusStarted) {
|
|
2959
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2960
|
+
this.managedToolStatusStarted = true;
|
|
2961
|
+
}
|
|
2962
|
+
const message = status.type === "warning" ? `Warning: ${status.message}` : status.message;
|
|
2963
|
+
const color = status.type === "warning" ? "warning" : "dim";
|
|
2964
|
+
this.chatContainer.addChild(new Text(theme.fg(color, message), 1, 0));
|
|
2965
|
+
this.lastStatusSpacer = undefined;
|
|
2966
|
+
this.lastStatusText = undefined;
|
|
2967
|
+
this.ui.requestRender();
|
|
2968
|
+
}
|
|
2766
2969
|
/**
|
|
2767
2970
|
* Show a status message in the chat.
|
|
2768
2971
|
*
|
|
@@ -2883,7 +3086,7 @@ export class InteractiveMode {
|
|
|
2883
3086
|
}
|
|
2884
3087
|
}
|
|
2885
3088
|
renderSessionItems(items, options = {}) {
|
|
2886
|
-
this.
|
|
3089
|
+
this.clearPendingTools();
|
|
2887
3090
|
const renderedPendingTools = new Map();
|
|
2888
3091
|
// Cache-miss notices are not persisted; re-derive them from the full entry
|
|
2889
3092
|
// list and re-inject them after the assistant messages that paid for them.
|
|
@@ -3241,6 +3444,7 @@ export class InteractiveMode {
|
|
|
3241
3444
|
this.editor.addToHistory?.(text);
|
|
3242
3445
|
this.editor.setText("");
|
|
3243
3446
|
await this.session.prompt(text, { streamingBehavior: "followUp" });
|
|
3447
|
+
this.offerFirstUseHint("queue");
|
|
3244
3448
|
this.updatePendingMessagesDisplay();
|
|
3245
3449
|
this.ui.requestRender();
|
|
3246
3450
|
}
|
|
@@ -3260,6 +3464,7 @@ export class InteractiveMode {
|
|
|
3260
3464
|
}
|
|
3261
3465
|
}
|
|
3262
3466
|
updateEditorBorderColor() {
|
|
3467
|
+
this.defaultEditor.setModeLabel(this.isBashMode ? "bash" : this.session.isStreaming ? "busy" : undefined);
|
|
3263
3468
|
if (this.isBashMode) {
|
|
3264
3469
|
this.editor.borderColor = theme.getBashModeBorderColor();
|
|
3265
3470
|
}
|
|
@@ -3320,6 +3525,7 @@ export class InteractiveMode {
|
|
|
3320
3525
|
this.showStatus(`Tool output: ${expanded ? "expanded" : "collapsed"}`);
|
|
3321
3526
|
}
|
|
3322
3527
|
toggleThinkingBlockVisibility() {
|
|
3528
|
+
this.offerFirstUseHint("thinking");
|
|
3323
3529
|
this.hideThinkingBlock = !this.hideThinkingBlock;
|
|
3324
3530
|
this.settingsManager.setHideThinkingBlock(this.hideThinkingBlock);
|
|
3325
3531
|
// Rebuild chat from session messages
|
|
@@ -3610,11 +3816,12 @@ export class InteractiveMode {
|
|
|
3610
3816
|
* between two sessions in one process. A footer left showing the old value
|
|
3611
3817
|
* would under-report bypassPermissions, which is the one direction that matters.
|
|
3612
3818
|
*/
|
|
3613
|
-
refreshFooterPermissionMode() {
|
|
3614
|
-
|
|
3615
|
-
|
|
3819
|
+
async refreshFooterPermissionMode() {
|
|
3820
|
+
const resolution = await this.session.getPermissionMode();
|
|
3821
|
+
this.footer.setPermissionMode(resolution?.mode ?? "default");
|
|
3822
|
+
if (this.isInitialized) {
|
|
3616
3823
|
this.ui.requestRender();
|
|
3617
|
-
}
|
|
3824
|
+
}
|
|
3618
3825
|
}
|
|
3619
3826
|
/**
|
|
3620
3827
|
* Writes the mode to user scope, then reports what is actually in force. The
|
|
@@ -3664,7 +3871,7 @@ export class InteractiveMode {
|
|
|
3664
3871
|
httpIdleTimeoutMs: this.settingsManager.getHttpIdleTimeoutMs(),
|
|
3665
3872
|
thinkingLevel: this.session.thinkingLevel,
|
|
3666
3873
|
availableThinkingLevels: this.session.getAvailableThinkingLevels(),
|
|
3667
|
-
currentTheme: this.
|
|
3874
|
+
currentTheme: this.themeController.getThemeSelection() || "dark",
|
|
3668
3875
|
terminalTheme: this.themeController.getTerminalTheme(),
|
|
3669
3876
|
availableThemes: getAvailableThemes(),
|
|
3670
3877
|
hideThinkingBlock: this.hideThinkingBlock,
|
|
@@ -3684,6 +3891,7 @@ export class InteractiveMode {
|
|
|
3684
3891
|
clearOnShrink: this.settingsManager.getClearOnShrink(),
|
|
3685
3892
|
showTerminalProgress: this.settingsManager.getShowTerminalProgress(),
|
|
3686
3893
|
tuiMode: this.ui.mode,
|
|
3894
|
+
fullscreenExitOutput: this.settingsManager.getFullscreenExitOutput(),
|
|
3687
3895
|
fullscreenScrollbar: this.settingsManager.getFullscreenScrollbar(),
|
|
3688
3896
|
warnings: this.settingsManager.getWarnings(),
|
|
3689
3897
|
}, {
|
|
@@ -3739,7 +3947,7 @@ export class InteractiveMode {
|
|
|
3739
3947
|
},
|
|
3740
3948
|
onThemeChange: (themeSetting) => {
|
|
3741
3949
|
this.settingsManager.setTheme(themeSetting);
|
|
3742
|
-
void this.themeController.
|
|
3950
|
+
void this.themeController.setThemeSetting(themeSetting);
|
|
3743
3951
|
},
|
|
3744
3952
|
onThemePreview: (themeName) => this.themeController.preview(themeName),
|
|
3745
3953
|
onHideThinkingBlockChange: (hidden) => {
|
|
@@ -3841,6 +4049,9 @@ export class InteractiveMode {
|
|
|
3841
4049
|
this.statusContainer.clear();
|
|
3842
4050
|
this.showStatus(`TUI mode: ${mode}`);
|
|
3843
4051
|
},
|
|
4052
|
+
onFullscreenExitOutputChange: (output) => {
|
|
4053
|
+
this.settingsManager.setFullscreenExitOutput(output);
|
|
4054
|
+
},
|
|
3844
4055
|
onFullscreenScrollbarChange: (mode) => {
|
|
3845
4056
|
this.settingsManager.setFullscreenScrollbar(mode);
|
|
3846
4057
|
this.applyFullscreenScrollbarSetting();
|
|
@@ -3893,7 +4104,7 @@ export class InteractiveMode {
|
|
|
3893
4104
|
controller.abort();
|
|
3894
4105
|
}, 15_000);
|
|
3895
4106
|
try {
|
|
3896
|
-
const result = await this.session.modelRuntime
|
|
4107
|
+
const result = await refreshModelCatalogs(this.session.modelRuntime, controller.signal);
|
|
3897
4108
|
if (result.aborted && timedOut) {
|
|
3898
4109
|
this.showWarning("Model refresh timed out; searching cached models.");
|
|
3899
4110
|
}
|
|
@@ -3981,7 +4192,7 @@ export class InteractiveMode {
|
|
|
3981
4192
|
onSelect: (selection) => {
|
|
3982
4193
|
trustStore.setMany(selection.updates);
|
|
3983
4194
|
done();
|
|
3984
|
-
this.showStatus(`Saved trust decision: ${selection.trusted ? "trusted" : "untrusted"}. Restart
|
|
4195
|
+
this.showStatus(`Saved trust decision: ${selection.trusted ? "trusted" : "untrusted"}. Restart ${APP_NAME} for this to take effect.`);
|
|
3985
4196
|
},
|
|
3986
4197
|
onCancel: () => {
|
|
3987
4198
|
done();
|
|
@@ -4081,8 +4292,7 @@ export class InteractiveMode {
|
|
|
4081
4292
|
this.ui.requestRender();
|
|
4082
4293
|
},
|
|
4083
4294
|
});
|
|
4084
|
-
void this.session.modelRuntime
|
|
4085
|
-
.refresh({ signal: controller.signal })
|
|
4295
|
+
void refreshModelCatalogs(this.session.modelRuntime, controller.signal)
|
|
4086
4296
|
.then((result) => {
|
|
4087
4297
|
if (disposed)
|
|
4088
4298
|
return;
|
|
@@ -4873,7 +5083,9 @@ export class InteractiveMode {
|
|
|
4873
5083
|
this.showStatus(`Session exported to: ${filePath}`);
|
|
4874
5084
|
}
|
|
4875
5085
|
else {
|
|
4876
|
-
const filePath = await this.session.exportToHtml(outputPath
|
|
5086
|
+
const filePath = await this.session.exportToHtml(outputPath, {
|
|
5087
|
+
themeName: theme.name,
|
|
5088
|
+
});
|
|
4877
5089
|
this.showStatus(`Session exported to: ${filePath}`);
|
|
4878
5090
|
}
|
|
4879
5091
|
}
|
|
@@ -5415,7 +5627,7 @@ export class InteractiveMode {
|
|
|
5415
5627
|
// Ignore, will be emitted as an event
|
|
5416
5628
|
}
|
|
5417
5629
|
}
|
|
5418
|
-
stop() {
|
|
5630
|
+
stop(fullscreenExitOutput = this.settingsManager.getFullscreenExitOutput()) {
|
|
5419
5631
|
this.disposeActiveSelector();
|
|
5420
5632
|
if (this.settingsManager.getShowTerminalProgress()) {
|
|
5421
5633
|
this.ui.terminal.setProgress(false);
|
|
@@ -5429,7 +5641,7 @@ export class InteractiveMode {
|
|
|
5429
5641
|
this.unsubscribe();
|
|
5430
5642
|
}
|
|
5431
5643
|
if (this.isInitialized) {
|
|
5432
|
-
this.stopInteractiveTui();
|
|
5644
|
+
this.stopInteractiveTui(fullscreenExitOutput);
|
|
5433
5645
|
this.isInitialized = false;
|
|
5434
5646
|
}
|
|
5435
5647
|
this.unregisterSignalHandlers();
|