@ashx-j/lunr 0.1.6 → 0.2.0
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/dist/builtin-extensions/index.d.ts.map +1 -1
- package/dist/builtin-extensions/index.js +3 -6
- package/dist/builtin-extensions/index.js.map +1 -1
- package/dist/builtin-extensions/lunr-todos.d.ts +7 -6
- package/dist/builtin-extensions/lunr-todos.d.ts.map +1 -1
- package/dist/builtin-extensions/lunr-todos.js +24 -11
- package/dist/builtin-extensions/lunr-todos.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.js +9 -5
- package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js +31 -3
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.d.ts +1 -0
- package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js +14 -4
- package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js +4 -3
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +12 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +3 -0
- package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +8 -0
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js +22 -2
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/index.js +38 -230
- package/dist/builtin-extensions/pi-web-access/index.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/render-search-chrome.d.ts +35 -0
- package/dist/builtin-extensions/pi-web-access/render-search-chrome.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-web-access/render-search-chrome.js +93 -0
- package/dist/builtin-extensions/pi-web-access/render-search-chrome.js.map +1 -0
- package/dist/bun/cli.d.ts.map +1 -1
- package/dist/bun/cli.js +1 -0
- package/dist/bun/cli.js.map +1 -1
- package/dist/catalog/models.json +1 -1
- package/dist/catalog/providers/cerebras.json +1 -1
- package/dist/catalog/providers/cloudflare-ai-gateway.json +1 -1
- package/dist/catalog/providers/cloudflare-workers-ai.json +1 -1
- package/dist/catalog/providers/huggingface.json +1 -1
- package/dist/catalog/providers/opencode-go.json +1 -1
- package/dist/catalog/providers/openrouter.json +1 -1
- package/dist/catalog/providers/vercel-ai-gateway.json +1 -1
- package/dist/catalog/publication.json +3 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -1
- package/dist/cli.js.map +1 -1
- package/dist/core/context-breakdown.d.ts +21 -1
- package/dist/core/context-breakdown.d.ts.map +1 -1
- package/dist/core/context-breakdown.js +58 -2
- package/dist/core/context-breakdown.js.map +1 -1
- package/dist/core/extensions/host-static.d.ts +2 -0
- package/dist/core/extensions/host-static.d.ts.map +1 -0
- package/dist/core/extensions/host-static.js +15 -0
- package/dist/core/extensions/host-static.js.map +1 -0
- package/dist/core/extensions/loader.d.ts +10 -0
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +75 -34
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/official-catalog.d.ts.map +1 -1
- package/dist/core/official-catalog.js +11 -4
- package/dist/core/official-catalog.js.map +1 -1
- package/dist/core/package-manager.d.ts +3 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +11 -5
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/permission-mode-control.d.ts +27 -0
- package/dist/core/permission-mode-control.d.ts.map +1 -0
- package/dist/core/permission-mode-control.js +39 -0
- package/dist/core/permission-mode-control.js.map +1 -0
- package/dist/core/resource-loader.d.ts +4 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +13 -5
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- 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 +0 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/timings.d.ts +1 -0
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js +6 -1
- package/dist/core/timings.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +14 -20
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts +2 -0
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js +10 -0
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/main.d.ts +8 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +63 -16
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +3 -0
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +1 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +1 -0
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/context-view.d.ts.map +1 -1
- package/dist/modes/interactive/components/context-view.js +3 -1
- package/dist/modes/interactive/components/context-view.js.map +1 -1
- package/dist/modes/interactive/components/plan-message.d.ts +17 -0
- package/dist/modes/interactive/components/plan-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/plan-message.js +43 -0
- package/dist/modes/interactive/components/plan-message.js.map +1 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +1 -1
- package/dist/modes/interactive/components/settings-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 +3 -1
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/usage-view.d.ts +4 -25
- package/dist/modes/interactive/components/usage-view.d.ts.map +1 -1
- package/dist/modes/interactive/components/usage-view.js +17 -69
- package/dist/modes/interactive/components/usage-view.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts +1 -0
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +1 -0
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +23 -6
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +267 -148
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/smooth-streaming.d.ts +39 -6
- package/dist/modes/interactive/smooth-streaming.d.ts.map +1 -1
- package/dist/modes/interactive/smooth-streaming.js +160 -73
- package/dist/modes/interactive/smooth-streaming.js.map +1 -1
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +7 -3
- package/dist/utils/clipboard-image.js.map +1 -1
- package/docs/terminal-setup.md +13 -0
- 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/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
|
@@ -27,6 +27,7 @@ import { defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScop
|
|
|
27
27
|
import { refreshModelCandidatesForInit } from "../../core/model-runtime.js";
|
|
28
28
|
import { getModelTiersBridge } from "../../core/model-tiers.js";
|
|
29
29
|
import { registerPermissionModeBridge } from "../../core/permission-mode.js";
|
|
30
|
+
import { nextModeForGoal, registerPermissionModeControlBridge } from "../../core/permission-mode-control.js";
|
|
30
31
|
import { AUTO_MODE_ADDENDUM, getPermissionMode, nextPermissionMode, registerApprovalHandler, resetPermissions, restorePermissionModeAfterPlan, setPermissionMode, } from "../../core/permissions.js";
|
|
31
32
|
import { PLAN_MODE_ADDENDUM } from "../../core/plan-mode.js";
|
|
32
33
|
import * as processRegistry from "../../core/process-registry.js";
|
|
@@ -37,9 +38,8 @@ import { SessionManager, sessionEntryToContextMessages } from "../../core/sessio
|
|
|
37
38
|
import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
|
|
38
39
|
// lunr: moved to core/swarm.ts so the gateway can reuse it.
|
|
39
40
|
import { buildSwarmPrompt } from "../../core/swarm.js";
|
|
40
|
-
import { hasTrustRequiringProjectResources, ProjectTrustStore } from "../../core/trust-manager.js";
|
|
41
|
-
import { collectUsageHistory } from "../../core/usage-history.js";
|
|
42
41
|
import { time } from "../../core/timings.js";
|
|
42
|
+
import { hasTrustRequiringProjectResources, ProjectTrustStore } from "../../core/trust-manager.js";
|
|
43
43
|
import { getPlanUsageResult } from "../../core/usage-service.js";
|
|
44
44
|
import { modelToUserEntry } from "../../core/user-models.js";
|
|
45
45
|
import { copyToClipboard, readClipboardText } from "../../utils/clipboard.js";
|
|
@@ -71,6 +71,7 @@ import { formatKeyText, keyDisplayText, keyText } from "./components/keybinding-
|
|
|
71
71
|
import { LoginDialogComponent } from "./components/login-dialog.js";
|
|
72
72
|
import { ModelSelectorComponent } from "./components/model-selector.js";
|
|
73
73
|
import { formatAuthSelectorProviderType, OAuthSelectorComponent, } from "./components/oauth-selector.js";
|
|
74
|
+
import { PlanMessageComponent } from "./components/plan-message.js";
|
|
74
75
|
import { ProcessesSelectorComponent } from "./components/processes-selector.js";
|
|
75
76
|
import { ScopedModelsSelectorComponent } from "./components/scoped-models-selector.js";
|
|
76
77
|
import { SessionSelectorComponent } from "./components/session-selector.js";
|
|
@@ -81,11 +82,11 @@ import { updateThinkingRunTimings } from "./components/thinking-summary.js";
|
|
|
81
82
|
import { ToolExecutionComponent } from "./components/tool-execution.js";
|
|
82
83
|
import { TreeSelectorComponent } from "./components/tree-selector.js";
|
|
83
84
|
import { TrustSelectorComponent } from "./components/trust-selector.js";
|
|
84
|
-
import {
|
|
85
|
+
import { renderUsageBox } from "./components/usage-view.js";
|
|
85
86
|
import { UserMessageComponent } from "./components/user-message.js";
|
|
86
87
|
import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
|
|
87
88
|
import { getModelSearchText } from "./model-search.js";
|
|
88
|
-
import { countMessageGraphemes, sliceMessageContent } from "./smooth-streaming.js";
|
|
89
|
+
import { computeSmoothRevealStep, countGraphemesBeforeToolCall, countMessageGraphemes, sliceMessageContent, } from "./smooth-streaming.js";
|
|
89
90
|
import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, getThemeName, onThemeChange, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
|
|
90
91
|
import { InteractiveThemeController } from "./theme/theme-controller.js";
|
|
91
92
|
function isExpandable(obj) {
|
|
@@ -253,7 +254,6 @@ export class InteractiveMode {
|
|
|
253
254
|
// flashing whole chunks.
|
|
254
255
|
streamingTargetMessage = undefined;
|
|
255
256
|
streamingDisplayedLength = 0;
|
|
256
|
-
lastSmoothRenderAt = 0;
|
|
257
257
|
smoothStreamingTimer = undefined;
|
|
258
258
|
// Tool execution tracking: toolCallId -> component
|
|
259
259
|
pendingTools = new Map();
|
|
@@ -282,6 +282,9 @@ export class InteractiveMode {
|
|
|
282
282
|
// lunr: mode in effect before the current plan stretch. Used by approve / `/plan off`
|
|
283
283
|
// / `/plan <text>` to leave plan. Shift+Tab and `/mode` pick the destination themselves.
|
|
284
284
|
previousPermissionMode;
|
|
285
|
+
// lunr: mode in effect before `/goal` forced session auto. Dedicated so it cannot
|
|
286
|
+
// collide with plan restore.
|
|
287
|
+
preGoalPermissionMode;
|
|
285
288
|
// lunr: in-memory redo stack for /undo and /redo (leaf entry ids; cleared on any
|
|
286
289
|
// new user message, never persisted — undone turns reappear after restart)
|
|
287
290
|
redoStack = [];
|
|
@@ -321,6 +324,7 @@ export class InteractiveMode {
|
|
|
321
324
|
autoTrustOnReloadCwd;
|
|
322
325
|
themeController;
|
|
323
326
|
deferredBuiltinsAttached = false;
|
|
327
|
+
deferredBuiltinAttachPromise;
|
|
324
328
|
// Convenience accessors
|
|
325
329
|
get session() {
|
|
326
330
|
return this.runtimeHost.session;
|
|
@@ -342,6 +346,7 @@ export class InteractiveMode {
|
|
|
342
346
|
this.resetExtensionUI();
|
|
343
347
|
// lunr: reset permission mode to configured default + clear session approvals
|
|
344
348
|
this.previousPermissionMode = undefined;
|
|
349
|
+
this.preGoalPermissionMode = undefined;
|
|
345
350
|
resetPermissions(this.settingsManager.getDefaultPermissionMode());
|
|
346
351
|
// lunr: clear process registry and rollback state
|
|
347
352
|
processRegistry.clearRegistry();
|
|
@@ -361,8 +366,17 @@ export class InteractiveMode {
|
|
|
361
366
|
this.version = VERSION;
|
|
362
367
|
this.ui = new TUI(new ProcessTerminal(), this.settingsManager.getShowHardwareCursor());
|
|
363
368
|
this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());
|
|
369
|
+
this.ui.onTextSelected = (text) => {
|
|
370
|
+
void copyToClipboard(text)
|
|
371
|
+
.then(() => this.showStatus("Copied."))
|
|
372
|
+
.catch((error) => this.showError(error instanceof Error ? error.message : String(error)));
|
|
373
|
+
};
|
|
364
374
|
// lunr: register the permission-mode footer bridge (provider-side: InteractiveMode owns the state).
|
|
365
375
|
registerPermissionModeBridge(() => getPermissionMode());
|
|
376
|
+
registerPermissionModeControlBridge({
|
|
377
|
+
enterGoalAuto: () => this.enterGoalAuto(),
|
|
378
|
+
leaveGoalAuto: () => this.leaveGoalAuto(),
|
|
379
|
+
});
|
|
366
380
|
// lunr: reset permission mode to configured default on startup.
|
|
367
381
|
resetPermissions(this.settingsManager.getDefaultPermissionMode());
|
|
368
382
|
this.headerContainer = new Container();
|
|
@@ -626,13 +640,15 @@ export class InteractiveMode {
|
|
|
626
640
|
this.headerContainer.addChild(this.builtInHeader);
|
|
627
641
|
}
|
|
628
642
|
this.ui.requestRender();
|
|
629
|
-
//
|
|
643
|
+
// Light extensions first so resources are shown before messages. Deferred
|
|
644
|
+
// MCP/LSP/web/intercom attach in the background so the editor is usable.
|
|
630
645
|
await this.rebindCurrentSession();
|
|
631
646
|
time("rebindCurrentSession");
|
|
632
|
-
await this.attachDeferredBuiltinExtensions();
|
|
633
|
-
time("attachDeferredBuiltins");
|
|
634
|
-
// Render initial messages AFTER showing loaded resources
|
|
635
647
|
this.renderInitialMessages();
|
|
648
|
+
this.deferredBuiltinAttachPromise = this.attachDeferredBuiltinExtensions();
|
|
649
|
+
void this.deferredBuiltinAttachPromise.then(() => {
|
|
650
|
+
time("attachDeferredBuiltins");
|
|
651
|
+
});
|
|
636
652
|
// Set up theme file watcher
|
|
637
653
|
onThemeChange(() => {
|
|
638
654
|
this.ui.invalidate();
|
|
@@ -643,9 +659,16 @@ export class InteractiveMode {
|
|
|
643
659
|
this.footerDataProvider.onBranchChange(() => {
|
|
644
660
|
this.ui.requestRender();
|
|
645
661
|
});
|
|
646
|
-
//
|
|
647
|
-
|
|
648
|
-
|
|
662
|
+
// Cache-only footer snapshot. Do not hold time-to-type on it.
|
|
663
|
+
void this.updateAvailableProviderCount().then(() => {
|
|
664
|
+
time("updateAvailableProviderCount");
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
/** Wait until deferred builtins have attached (or were skipped). */
|
|
668
|
+
async waitForDeferredBuiltins() {
|
|
669
|
+
if (this.deferredBuiltinAttachPromise) {
|
|
670
|
+
await this.deferredBuiltinAttachPromise;
|
|
671
|
+
}
|
|
649
672
|
}
|
|
650
673
|
/**
|
|
651
674
|
* Update terminal title with session name and cwd.
|
|
@@ -665,7 +688,9 @@ export class InteractiveMode {
|
|
|
665
688
|
* Initializes the UI, shows warnings, processes initial messages, and starts the interactive loop.
|
|
666
689
|
*/
|
|
667
690
|
async run() {
|
|
668
|
-
|
|
691
|
+
if (!this.isInitialized) {
|
|
692
|
+
await this.init();
|
|
693
|
+
}
|
|
669
694
|
// Check tmux keyboard setup asynchronously
|
|
670
695
|
this.checkTmuxKeyboardSetup().then((warning) => {
|
|
671
696
|
if (warning) {
|
|
@@ -688,7 +713,7 @@ export class InteractiveMode {
|
|
|
688
713
|
// Process initial messages
|
|
689
714
|
if (initialMessage) {
|
|
690
715
|
try {
|
|
691
|
-
await this.
|
|
716
|
+
await this.promptAfterDeferredBuiltins(initialMessage, { images: initialImages });
|
|
692
717
|
}
|
|
693
718
|
catch (error) {
|
|
694
719
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
@@ -698,7 +723,7 @@ export class InteractiveMode {
|
|
|
698
723
|
if (initialMessages) {
|
|
699
724
|
for (const message of initialMessages) {
|
|
700
725
|
try {
|
|
701
|
-
await this.
|
|
726
|
+
await this.promptAfterDeferredBuiltins(message);
|
|
702
727
|
}
|
|
703
728
|
catch (error) {
|
|
704
729
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
@@ -710,7 +735,7 @@ export class InteractiveMode {
|
|
|
710
735
|
while (true) {
|
|
711
736
|
const userInput = await this.getUserInput();
|
|
712
737
|
try {
|
|
713
|
-
await this.
|
|
738
|
+
await this.promptAfterDeferredBuiltins(userInput);
|
|
714
739
|
}
|
|
715
740
|
catch (error) {
|
|
716
741
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
@@ -1203,19 +1228,41 @@ export class InteractiveMode {
|
|
|
1203
1228
|
/**
|
|
1204
1229
|
* Initialize the extension system with TUI-based UI context.
|
|
1205
1230
|
*/
|
|
1231
|
+
async awaitDeferredBuiltinsForPrompt() {
|
|
1232
|
+
await this.waitForDeferredBuiltins();
|
|
1233
|
+
}
|
|
1234
|
+
async promptAfterDeferredBuiltins(text, options) {
|
|
1235
|
+
await this.awaitDeferredBuiltinsForPrompt();
|
|
1236
|
+
await this.session.prompt(text, options);
|
|
1237
|
+
}
|
|
1238
|
+
async sendUserMessageAfterDeferredBuiltins(text) {
|
|
1239
|
+
await this.awaitDeferredBuiltinsForPrompt();
|
|
1240
|
+
await this.session.sendUserMessage(text);
|
|
1241
|
+
}
|
|
1206
1242
|
async attachDeferredBuiltinExtensions() {
|
|
1207
1243
|
if (this.deferredBuiltinsAttached || !this.options.deferredBuiltinFactories) {
|
|
1208
1244
|
return;
|
|
1209
1245
|
}
|
|
1210
|
-
this.
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1246
|
+
this.setExtensionStatus("deferred-builtins", "loading tools…");
|
|
1247
|
+
try {
|
|
1248
|
+
const factories = await this.options.deferredBuiltinFactories();
|
|
1249
|
+
this.deferredBuiltinsAttached = true;
|
|
1250
|
+
if (factories.length === 0)
|
|
1251
|
+
return;
|
|
1252
|
+
this.options.onDeferredBuiltinsAttached?.(factories);
|
|
1253
|
+
await this.session.attachInlineExtensions(factories);
|
|
1254
|
+
this.setupAutocompleteProvider();
|
|
1255
|
+
this.setupExtensionShortcuts(this.session.extensionRunner);
|
|
1256
|
+
this.showLoadedResources({ force: false, showDiagnosticsWhenQuiet: true });
|
|
1257
|
+
}
|
|
1258
|
+
catch (error) {
|
|
1259
|
+
this.deferredBuiltinsAttached = false;
|
|
1260
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1261
|
+
this.showError(`Failed to load deferred extensions: ${message}`);
|
|
1262
|
+
}
|
|
1263
|
+
finally {
|
|
1264
|
+
this.setExtensionStatus("deferred-builtins", undefined);
|
|
1265
|
+
}
|
|
1219
1266
|
}
|
|
1220
1267
|
async bindCurrentSessionExtensions() {
|
|
1221
1268
|
const uiContext = this.createExtensionUIContext();
|
|
@@ -1329,6 +1376,7 @@ export class InteractiveMode {
|
|
|
1329
1376
|
return bootRows;
|
|
1330
1377
|
}
|
|
1331
1378
|
async rebindCurrentSession(options = {}) {
|
|
1379
|
+
await this.waitForDeferredBuiltins();
|
|
1332
1380
|
this.unsubscribe?.();
|
|
1333
1381
|
this.unsubscribe = undefined;
|
|
1334
1382
|
this.applyRuntimeSettings();
|
|
@@ -1345,7 +1393,7 @@ export class InteractiveMode {
|
|
|
1345
1393
|
await this.bindCurrentSessionExtensions();
|
|
1346
1394
|
this.subscribeToAgent();
|
|
1347
1395
|
}
|
|
1348
|
-
|
|
1396
|
+
void this.updateAvailableProviderCount();
|
|
1349
1397
|
this.updateEditorBorderColor();
|
|
1350
1398
|
this.updateTerminalTitle();
|
|
1351
1399
|
}
|
|
@@ -2217,11 +2265,6 @@ export class InteractiveMode {
|
|
|
2217
2265
|
await this.handleUsageCommand();
|
|
2218
2266
|
return;
|
|
2219
2267
|
}
|
|
2220
|
-
if (text === "/token-usage") {
|
|
2221
|
-
this.editor.setText("");
|
|
2222
|
-
this.handleTokenUsageCommand();
|
|
2223
|
-
return;
|
|
2224
|
-
}
|
|
2225
2268
|
if (text === "/context") {
|
|
2226
2269
|
this.editor.setText("");
|
|
2227
2270
|
this.handleContextCommand();
|
|
@@ -2376,7 +2419,7 @@ export class InteractiveMode {
|
|
|
2376
2419
|
if (this.isExtensionCommand(text)) {
|
|
2377
2420
|
this.editor.addToHistory?.(text);
|
|
2378
2421
|
this.editor.setText("");
|
|
2379
|
-
await this.
|
|
2422
|
+
await this.promptAfterDeferredBuiltins(text);
|
|
2380
2423
|
}
|
|
2381
2424
|
else {
|
|
2382
2425
|
this.queueCompactionMessage(text, "steer");
|
|
@@ -2388,7 +2431,7 @@ export class InteractiveMode {
|
|
|
2388
2431
|
if (this.session.isStreaming) {
|
|
2389
2432
|
this.editor.addToHistory?.(text);
|
|
2390
2433
|
this.editor.setText("");
|
|
2391
|
-
await this.
|
|
2434
|
+
await this.promptAfterDeferredBuiltins(text, { streamingBehavior: "steer" });
|
|
2392
2435
|
this.updatePendingMessagesDisplay();
|
|
2393
2436
|
this.ui.requestRender();
|
|
2394
2437
|
return;
|
|
@@ -2405,9 +2448,17 @@ export class InteractiveMode {
|
|
|
2405
2448
|
this.editor.addToHistory?.(text);
|
|
2406
2449
|
};
|
|
2407
2450
|
}
|
|
2408
|
-
/** Smooth streaming tick rate (
|
|
2409
|
-
static SMOOTH_STREAMING_TICK_MS =
|
|
2410
|
-
|
|
2451
|
+
/** Smooth streaming tick rate (~30 FPS). TUI already caps paints at 16ms. */
|
|
2452
|
+
static SMOOTH_STREAMING_TICK_MS = 33;
|
|
2453
|
+
getSmoothStreamingOptions() {
|
|
2454
|
+
return { hideThinking: this.hideThinkingBlock };
|
|
2455
|
+
}
|
|
2456
|
+
clearSmoothStreamingTimer() {
|
|
2457
|
+
if (this.smoothStreamingTimer) {
|
|
2458
|
+
clearInterval(this.smoothStreamingTimer);
|
|
2459
|
+
this.smoothStreamingTimer = undefined;
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2411
2462
|
startSmoothStreaming() {
|
|
2412
2463
|
if (this.smoothStreamingTimer)
|
|
2413
2464
|
return;
|
|
@@ -2417,38 +2468,96 @@ export class InteractiveMode {
|
|
|
2417
2468
|
// Never keep the process alive just to finish a reveal animation.
|
|
2418
2469
|
this.smoothStreamingTimer.unref?.();
|
|
2419
2470
|
}
|
|
2471
|
+
/** Apply the current reveal slice and sync tool cards that have crossed the frontier. */
|
|
2472
|
+
paintSmoothStreamingFrame() {
|
|
2473
|
+
const target = this.streamingTargetMessage;
|
|
2474
|
+
if (!this.streamingComponent || !target)
|
|
2475
|
+
return;
|
|
2476
|
+
const opts = this.getSmoothStreamingOptions();
|
|
2477
|
+
const revealed = sliceMessageContent(target, this.streamingDisplayedLength, opts);
|
|
2478
|
+
this.streamingComponent.updateContent(revealed);
|
|
2479
|
+
this.syncRevealedStreamingTools(revealed, target);
|
|
2480
|
+
this.ui.requestRender();
|
|
2481
|
+
}
|
|
2482
|
+
/**
|
|
2483
|
+
* Create/update tool cards only once smooth streaming has revealed past their
|
|
2484
|
+
* leading text. Args always come from the full target so later arg deltas apply.
|
|
2485
|
+
*/
|
|
2486
|
+
syncRevealedStreamingTools(revealed, target) {
|
|
2487
|
+
const revealedIds = new Set();
|
|
2488
|
+
for (const block of revealed.content) {
|
|
2489
|
+
if (block.type === "toolCall")
|
|
2490
|
+
revealedIds.add(block.id);
|
|
2491
|
+
}
|
|
2492
|
+
for (const content of target.content) {
|
|
2493
|
+
if (content.type !== "toolCall" || !revealedIds.has(content.id))
|
|
2494
|
+
continue;
|
|
2495
|
+
if (!this.pendingTools.has(content.id)) {
|
|
2496
|
+
const component = new ToolExecutionComponent(content.name, content.id, content.arguments, {
|
|
2497
|
+
showImages: this.settingsManager.getShowImages(),
|
|
2498
|
+
imageWidthCells: this.settingsManager.getImageWidthCells(),
|
|
2499
|
+
}, this.getRegisteredToolDefinition(content.name), this.ui, this.sessionManager.getCwd());
|
|
2500
|
+
component.setExpanded(this.toolOutputExpanded);
|
|
2501
|
+
this.addToolComponentToChat(component);
|
|
2502
|
+
this.pendingTools.set(content.id, component);
|
|
2503
|
+
}
|
|
2504
|
+
else {
|
|
2505
|
+
const component = this.pendingTools.get(content.id);
|
|
2506
|
+
if (component) {
|
|
2507
|
+
component.updateArgs(content.arguments);
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2420
2512
|
advanceSmoothStreaming() {
|
|
2421
2513
|
const target = this.streamingTargetMessage;
|
|
2422
2514
|
if (!this.streamingComponent || !target) {
|
|
2423
2515
|
this.stopSmoothStreaming();
|
|
2424
2516
|
return;
|
|
2425
2517
|
}
|
|
2426
|
-
const
|
|
2427
|
-
|
|
2518
|
+
const opts = this.getSmoothStreamingOptions();
|
|
2519
|
+
const total = countMessageGraphemes(target, opts);
|
|
2520
|
+
if (this.streamingDisplayedLength >= total) {
|
|
2521
|
+
// Caught up: stop the interval until new backlog arrives.
|
|
2522
|
+
this.clearSmoothStreamingTimer();
|
|
2428
2523
|
return;
|
|
2429
|
-
|
|
2430
|
-
// the more graphemes each tick reveals.
|
|
2524
|
+
}
|
|
2431
2525
|
const backlog = total - this.streamingDisplayedLength;
|
|
2432
|
-
this.streamingDisplayedLength +=
|
|
2526
|
+
this.streamingDisplayedLength += computeSmoothRevealStep(backlog);
|
|
2433
2527
|
if (this.streamingDisplayedLength > total) {
|
|
2434
2528
|
this.streamingDisplayedLength = total;
|
|
2435
2529
|
}
|
|
2436
|
-
this.
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
if (now - this.lastSmoothRenderAt >= 33) {
|
|
2440
|
-
this.lastSmoothRenderAt = now;
|
|
2441
|
-
this.ui.requestRender();
|
|
2530
|
+
this.paintSmoothStreamingFrame();
|
|
2531
|
+
if (this.streamingDisplayedLength >= total) {
|
|
2532
|
+
this.clearSmoothStreamingTimer();
|
|
2442
2533
|
}
|
|
2443
2534
|
}
|
|
2444
2535
|
stopSmoothStreaming() {
|
|
2445
|
-
|
|
2446
|
-
clearInterval(this.smoothStreamingTimer);
|
|
2447
|
-
this.smoothStreamingTimer = undefined;
|
|
2448
|
-
}
|
|
2536
|
+
this.clearSmoothStreamingTimer();
|
|
2449
2537
|
this.streamingTargetMessage = undefined;
|
|
2450
2538
|
this.streamingDisplayedLength = 0;
|
|
2451
|
-
|
|
2539
|
+
}
|
|
2540
|
+
/** Apply a mid-stream smoothStreaming setting change without rewinding. */
|
|
2541
|
+
applySmoothStreamingSettingChange(enabled) {
|
|
2542
|
+
this.settingsManager.setSmoothStreaming(enabled);
|
|
2543
|
+
if (!this.streamingComponent || !this.streamingMessage)
|
|
2544
|
+
return;
|
|
2545
|
+
if (enabled) {
|
|
2546
|
+
// Already showing the full live message; start the reveal cursor at the
|
|
2547
|
+
// current visible length so the next deltas only animate new text.
|
|
2548
|
+
this.streamingTargetMessage = this.streamingMessage;
|
|
2549
|
+
const opts = this.getSmoothStreamingOptions();
|
|
2550
|
+
this.streamingDisplayedLength = countMessageGraphemes(this.streamingMessage, opts);
|
|
2551
|
+
this.paintSmoothStreamingFrame();
|
|
2552
|
+
this.startSmoothStreaming();
|
|
2553
|
+
return;
|
|
2554
|
+
}
|
|
2555
|
+
// Off: stop the timer and snap to the full target immediately.
|
|
2556
|
+
this.clearSmoothStreamingTimer();
|
|
2557
|
+
this.streamingTargetMessage = this.streamingMessage;
|
|
2558
|
+
this.streamingComponent.updateContent(this.streamingMessage);
|
|
2559
|
+
this.syncRevealedStreamingTools(this.streamingMessage, this.streamingMessage);
|
|
2560
|
+
this.ui.requestRender();
|
|
2452
2561
|
}
|
|
2453
2562
|
subscribeToAgent() {
|
|
2454
2563
|
this.unsubscribe = this.session.subscribe(async (event) => {
|
|
@@ -2526,7 +2635,7 @@ export class InteractiveMode {
|
|
|
2526
2635
|
this.streamingDisplayedLength = 0;
|
|
2527
2636
|
this.chatContainer.addChild(this.streamingComponent);
|
|
2528
2637
|
if (this.settingsManager.getSmoothStreaming()) {
|
|
2529
|
-
this.streamingComponent.updateContent(sliceMessageContent(this.streamingMessage, 0));
|
|
2638
|
+
this.streamingComponent.updateContent(sliceMessageContent(this.streamingMessage, 0, this.getSmoothStreamingOptions()));
|
|
2530
2639
|
}
|
|
2531
2640
|
else {
|
|
2532
2641
|
this.streamingComponent.updateContent(this.streamingMessage);
|
|
@@ -2542,40 +2651,32 @@ export class InteractiveMode {
|
|
|
2542
2651
|
// so smooth-streaming reveal lag doesn't inflate durations.
|
|
2543
2652
|
updateThinkingRunTimings(this.thinkingRunTimings, this.streamingMessage.content, Date.now(), false);
|
|
2544
2653
|
if (this.settingsManager.getSmoothStreaming()) {
|
|
2545
|
-
// Smooth streaming:
|
|
2546
|
-
//
|
|
2547
|
-
this.
|
|
2654
|
+
// Smooth streaming: timer reveals graphemes. Tool cards sync from
|
|
2655
|
+
// the reveal frontier so they don't jump ahead of leading text.
|
|
2656
|
+
const opts = this.getSmoothStreamingOptions();
|
|
2657
|
+
const total = countMessageGraphemes(this.streamingMessage, opts);
|
|
2658
|
+
if (this.streamingDisplayedLength >= total) {
|
|
2659
|
+
// Already caught up (or tool-only): paint so new tool cards appear.
|
|
2660
|
+
this.paintSmoothStreamingFrame();
|
|
2661
|
+
this.clearSmoothStreamingTimer();
|
|
2662
|
+
}
|
|
2663
|
+
else {
|
|
2664
|
+
this.startSmoothStreaming();
|
|
2665
|
+
}
|
|
2548
2666
|
}
|
|
2549
2667
|
else {
|
|
2550
2668
|
this.streamingComponent.updateContent(this.streamingMessage);
|
|
2669
|
+
this.syncRevealedStreamingTools(this.streamingMessage, this.streamingMessage);
|
|
2670
|
+
this.ui.requestRender();
|
|
2551
2671
|
}
|
|
2552
|
-
for (const content of this.streamingMessage.content) {
|
|
2553
|
-
if (content.type === "toolCall") {
|
|
2554
|
-
if (!this.pendingTools.has(content.id)) {
|
|
2555
|
-
const component = new ToolExecutionComponent(content.name, content.id, content.arguments, {
|
|
2556
|
-
showImages: this.settingsManager.getShowImages(),
|
|
2557
|
-
imageWidthCells: this.settingsManager.getImageWidthCells(),
|
|
2558
|
-
}, this.getRegisteredToolDefinition(content.name), this.ui, this.sessionManager.getCwd());
|
|
2559
|
-
component.setExpanded(this.toolOutputExpanded);
|
|
2560
|
-
this.addToolComponentToChat(component);
|
|
2561
|
-
this.pendingTools.set(content.id, component);
|
|
2562
|
-
}
|
|
2563
|
-
else {
|
|
2564
|
-
const component = this.pendingTools.get(content.id);
|
|
2565
|
-
if (component) {
|
|
2566
|
-
component.updateArgs(content.arguments);
|
|
2567
|
-
}
|
|
2568
|
-
}
|
|
2569
|
-
}
|
|
2570
|
-
}
|
|
2571
|
-
this.ui.requestRender();
|
|
2572
2672
|
}
|
|
2573
2673
|
break;
|
|
2574
2674
|
case "message_end":
|
|
2575
2675
|
if (event.message.role === "user")
|
|
2576
2676
|
break;
|
|
2577
2677
|
if (this.streamingComponent && event.message.role === "assistant") {
|
|
2578
|
-
//
|
|
2678
|
+
// Snap remaining graphemes on end (abort/error and normal stop).
|
|
2679
|
+
// Avoids multi-second leftover animation; paint always follows below.
|
|
2579
2680
|
this.stopSmoothStreaming();
|
|
2580
2681
|
this.streamingMessage = event.message;
|
|
2581
2682
|
let errorMessage;
|
|
@@ -2618,6 +2719,18 @@ export class InteractiveMode {
|
|
|
2618
2719
|
this.ui.requestRender();
|
|
2619
2720
|
break;
|
|
2620
2721
|
case "tool_execution_start": {
|
|
2722
|
+
// If smooth streaming still has leading text unrevealed, flush through
|
|
2723
|
+
// this tool so the running card is not hidden behind the typewriter.
|
|
2724
|
+
if (this.settingsManager.getSmoothStreaming() &&
|
|
2725
|
+
this.streamingTargetMessage &&
|
|
2726
|
+
this.streamingComponent) {
|
|
2727
|
+
const opts = this.getSmoothStreamingOptions();
|
|
2728
|
+
const needed = countGraphemesBeforeToolCall(this.streamingTargetMessage, event.toolCallId, opts);
|
|
2729
|
+
if (this.streamingDisplayedLength < needed) {
|
|
2730
|
+
this.streamingDisplayedLength = needed;
|
|
2731
|
+
}
|
|
2732
|
+
this.paintSmoothStreamingFrame();
|
|
2733
|
+
}
|
|
2621
2734
|
let component = this.pendingTools.get(event.toolCallId);
|
|
2622
2735
|
if (!component) {
|
|
2623
2736
|
component = new ToolExecutionComponent(event.toolName, event.toolCallId, event.args, {
|
|
@@ -3082,7 +3195,7 @@ export class InteractiveMode {
|
|
|
3082
3195
|
if (this.isShuttingDown)
|
|
3083
3196
|
return;
|
|
3084
3197
|
this.isShuttingDown = true;
|
|
3085
|
-
this.stopSmoothStreaming
|
|
3198
|
+
this.stopSmoothStreaming();
|
|
3086
3199
|
// Keep signal handlers registered until terminal cleanup has completed.
|
|
3087
3200
|
// `signal-exit` checks the listener list during the same SIGTERM/SIGHUP
|
|
3088
3201
|
// dispatch and re-sends the signal if only its own listeners remain.
|
|
@@ -3272,7 +3385,7 @@ export class InteractiveMode {
|
|
|
3272
3385
|
if (this.isExtensionCommand(text)) {
|
|
3273
3386
|
this.editor.addToHistory?.(text);
|
|
3274
3387
|
this.editor.setText("");
|
|
3275
|
-
await this.
|
|
3388
|
+
await this.promptAfterDeferredBuiltins(text);
|
|
3276
3389
|
}
|
|
3277
3390
|
else {
|
|
3278
3391
|
this.queueCompactionMessage(text, "followUp");
|
|
@@ -3284,7 +3397,7 @@ export class InteractiveMode {
|
|
|
3284
3397
|
if (this.session.isStreaming) {
|
|
3285
3398
|
this.editor.addToHistory?.(text);
|
|
3286
3399
|
this.editor.setText("");
|
|
3287
|
-
await this.
|
|
3400
|
+
await this.promptAfterDeferredBuiltins(text, { streamingBehavior: "followUp" });
|
|
3288
3401
|
this.updatePendingMessagesDisplay();
|
|
3289
3402
|
this.ui.requestRender();
|
|
3290
3403
|
}
|
|
@@ -3383,10 +3496,25 @@ export class InteractiveMode {
|
|
|
3383
3496
|
// Rebuild chat from session messages
|
|
3384
3497
|
this.chatContainer.clear();
|
|
3385
3498
|
this.rebuildChatFromMessages();
|
|
3386
|
-
// If streaming, re-add the streaming component with updated visibility and re-render
|
|
3499
|
+
// If streaming, re-add the streaming component with updated visibility and re-render.
|
|
3500
|
+
// Re-slice to the current revealed length so hide-thinking does not dump the tail.
|
|
3387
3501
|
if (this.streamingComponent && this.streamingMessage) {
|
|
3388
3502
|
this.streamingComponent.setHideThinkingBlock(this.hideThinkingBlock);
|
|
3389
|
-
this.
|
|
3503
|
+
if (this.settingsManager.getSmoothStreaming() && this.streamingTargetMessage) {
|
|
3504
|
+
const opts = this.getSmoothStreamingOptions();
|
|
3505
|
+
const total = countMessageGraphemes(this.streamingTargetMessage, opts);
|
|
3506
|
+
if (this.streamingDisplayedLength > total) {
|
|
3507
|
+
this.streamingDisplayedLength = total;
|
|
3508
|
+
}
|
|
3509
|
+
this.paintSmoothStreamingFrame();
|
|
3510
|
+
// Restart the timer if new (or still-pending) backlog exists.
|
|
3511
|
+
if (this.streamingDisplayedLength < total) {
|
|
3512
|
+
this.startSmoothStreaming();
|
|
3513
|
+
}
|
|
3514
|
+
}
|
|
3515
|
+
else {
|
|
3516
|
+
this.streamingComponent.updateContent(this.streamingMessage);
|
|
3517
|
+
}
|
|
3390
3518
|
this.chatContainer.addChild(this.streamingComponent);
|
|
3391
3519
|
}
|
|
3392
3520
|
this.showStatus(`Thinking blocks: ${this.hideThinkingBlock ? "hidden" : "visible"}`);
|
|
@@ -3561,7 +3689,7 @@ export class InteractiveMode {
|
|
|
3561
3689
|
// When retry is pending, queue messages for the retry turn
|
|
3562
3690
|
for (const message of queuedMessages) {
|
|
3563
3691
|
if (this.isExtensionCommand(message.text)) {
|
|
3564
|
-
await this.
|
|
3692
|
+
await this.promptAfterDeferredBuiltins(message.text);
|
|
3565
3693
|
}
|
|
3566
3694
|
else if (message.mode === "followUp") {
|
|
3567
3695
|
await this.session.followUp(message.text);
|
|
@@ -3578,7 +3706,7 @@ export class InteractiveMode {
|
|
|
3578
3706
|
if (firstPromptIndex === -1) {
|
|
3579
3707
|
// All extension commands - execute them all
|
|
3580
3708
|
for (const message of queuedMessages) {
|
|
3581
|
-
await this.
|
|
3709
|
+
await this.promptAfterDeferredBuiltins(message.text);
|
|
3582
3710
|
}
|
|
3583
3711
|
return;
|
|
3584
3712
|
}
|
|
@@ -3587,16 +3715,16 @@ export class InteractiveMode {
|
|
|
3587
3715
|
const firstPrompt = queuedMessages[firstPromptIndex];
|
|
3588
3716
|
const rest = queuedMessages.slice(firstPromptIndex + 1);
|
|
3589
3717
|
for (const message of preCommands) {
|
|
3590
|
-
await this.
|
|
3718
|
+
await this.promptAfterDeferredBuiltins(message.text);
|
|
3591
3719
|
}
|
|
3592
3720
|
// Send first prompt (starts streaming)
|
|
3593
|
-
const promptPromise = this.
|
|
3721
|
+
const promptPromise = this.promptAfterDeferredBuiltins(firstPrompt.text).catch((error) => {
|
|
3594
3722
|
restoreQueue(error);
|
|
3595
3723
|
});
|
|
3596
3724
|
// Queue remaining messages
|
|
3597
3725
|
for (const message of rest) {
|
|
3598
3726
|
if (this.isExtensionCommand(message.text)) {
|
|
3599
|
-
await this.
|
|
3727
|
+
await this.promptAfterDeferredBuiltins(message.text);
|
|
3600
3728
|
}
|
|
3601
3729
|
else if (message.mode === "followUp") {
|
|
3602
3730
|
await this.session.followUp(message.text);
|
|
@@ -3783,7 +3911,7 @@ export class InteractiveMode {
|
|
|
3783
3911
|
this.settingsManager.setQuietStartup(enabled);
|
|
3784
3912
|
},
|
|
3785
3913
|
onSmoothStreamingChange: (enabled) => {
|
|
3786
|
-
this.
|
|
3914
|
+
this.applySmoothStreamingSettingChange(enabled);
|
|
3787
3915
|
},
|
|
3788
3916
|
onSessionRetentionDaysChange: (days) => {
|
|
3789
3917
|
this.settingsManager.setSessionRetentionDays(days);
|
|
@@ -5282,7 +5410,7 @@ export class InteractiveMode {
|
|
|
5282
5410
|
return;
|
|
5283
5411
|
}
|
|
5284
5412
|
}
|
|
5285
|
-
await this.
|
|
5413
|
+
await this.sendUserMessageAfterDeferredBuiltins(prompt);
|
|
5286
5414
|
this.showStatus("AGENTS.md is being generated — run /reload when it finishes to load it into context.");
|
|
5287
5415
|
}
|
|
5288
5416
|
async handleSwarmCommand(task) {
|
|
@@ -5301,7 +5429,7 @@ export class InteractiveMode {
|
|
|
5301
5429
|
}
|
|
5302
5430
|
this.swarmMode = true;
|
|
5303
5431
|
this.setExtensionStatus("swarm", "swarm ● active");
|
|
5304
|
-
await this.
|
|
5432
|
+
await this.sendUserMessageAfterDeferredBuiltins(buildSwarmPrompt(task));
|
|
5305
5433
|
}
|
|
5306
5434
|
async handleResearchCommand(args) {
|
|
5307
5435
|
const usage = "Usage: /research [--depth N] [--breadth N] <question> — deep research with cited sources. depth: reflection rounds (1-4, default 2); breadth: subtopics per round (1-8, default 4).";
|
|
@@ -5349,7 +5477,7 @@ export class InteractiveMode {
|
|
|
5349
5477
|
}
|
|
5350
5478
|
this.researchMode = true;
|
|
5351
5479
|
this.setExtensionStatus("research", "research ● active");
|
|
5352
|
-
await this.
|
|
5480
|
+
await this.sendUserMessageAfterDeferredBuiltins(buildResearchPrompt(question, depth, breadth));
|
|
5353
5481
|
}
|
|
5354
5482
|
// lunr: /undo persists the rewind by forking the session to just before the
|
|
5355
5483
|
// last user message (same fork mechanism /rollback uses, but WITHOUT touching files).
|
|
@@ -5869,41 +5997,24 @@ export class InteractiveMode {
|
|
|
5869
5997
|
this.chatContainer.addChild(new Text(info, 1, 0));
|
|
5870
5998
|
this.ui.requestRender();
|
|
5871
5999
|
}
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
const
|
|
5875
|
-
|
|
5876
|
-
if (entry.type !== "message" || entry.message.role !== "assistant")
|
|
5877
|
-
continue;
|
|
5878
|
-
const message = entry.message;
|
|
5879
|
-
const usage = message.usage;
|
|
5880
|
-
const key = `${message.provider}/${message.responseModel ?? message.model}`;
|
|
5881
|
-
let row = perModelMap.get(key);
|
|
5882
|
-
if (!row) {
|
|
5883
|
-
row = { model: key, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };
|
|
5884
|
-
perModelMap.set(key, row);
|
|
5885
|
-
}
|
|
5886
|
-
row.input += usage.input;
|
|
5887
|
-
row.output += usage.output;
|
|
5888
|
-
row.cacheRead += usage.cacheRead;
|
|
5889
|
-
row.cacheWrite += usage.cacheWrite;
|
|
5890
|
-
row.total += usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
|
|
5891
|
-
}
|
|
5892
|
-
return Array.from(perModelMap.values()).sort((a, b) => b.total - a.total);
|
|
5893
|
-
}
|
|
5894
|
-
// lunr: 30-day history from session files on disk; best-effort — on any
|
|
5895
|
-
// error the section is simply omitted.
|
|
5896
|
-
collectUsageHistorySafe() {
|
|
5897
|
-
try {
|
|
5898
|
-
return collectUsageHistory({ sinceMs: Date.now() - 30 * 24 * 60 * 60 * 1000 });
|
|
5899
|
-
}
|
|
5900
|
-
catch {
|
|
6000
|
+
collectUsageBreakdown() {
|
|
6001
|
+
const model = this.session.model;
|
|
6002
|
+
const contextWindow = model?.contextWindow ?? 0;
|
|
6003
|
+
if (!model || contextWindow <= 0)
|
|
5901
6004
|
return undefined;
|
|
5902
|
-
|
|
6005
|
+
const tools = this.session
|
|
6006
|
+
.getActiveToolNames()
|
|
6007
|
+
.map((name) => this.session.getToolDefinition(name))
|
|
6008
|
+
.filter((definition) => definition !== undefined);
|
|
6009
|
+
return computeContextBreakdown({
|
|
6010
|
+
systemPrompt: this.session.systemPrompt,
|
|
6011
|
+
tools,
|
|
6012
|
+
messages: this.session.messages,
|
|
6013
|
+
contextWindow,
|
|
6014
|
+
});
|
|
5903
6015
|
}
|
|
5904
|
-
// lunr: /usage —
|
|
5905
|
-
// bar, and subscription plan usage
|
|
5906
|
-
// adapter). Per-model rows live in /token-usage.
|
|
6016
|
+
// lunr: /usage — current session only: token totals, live context split,
|
|
6017
|
+
// context-window bar, and subscription plan usage.
|
|
5907
6018
|
async handleUsageCommand() {
|
|
5908
6019
|
const stats = this.session.getSessionStats();
|
|
5909
6020
|
const sessionTotals = stats.tokens.total > 0
|
|
@@ -5918,24 +6029,14 @@ export class InteractiveMode {
|
|
|
5918
6029
|
const context = this.session.getContextUsage();
|
|
5919
6030
|
const provider = this.session.model?.provider;
|
|
5920
6031
|
const planResult = provider ? await getPlanUsageResult(provider, this.session.modelRuntime) : {};
|
|
5921
|
-
const
|
|
5922
|
-
const lines = renderUsageBox({ sessionTotals, context, plan: planResult.usage,
|
|
6032
|
+
const breakdown = this.collectUsageBreakdown();
|
|
6033
|
+
const lines = renderUsageBox({ sessionTotals, context, plan: planResult.usage, breakdown }, this.ui.terminal.columns);
|
|
5923
6034
|
this.chatContainer.addChild(new Spacer(1));
|
|
5924
6035
|
this.chatContainer.addChild(new Text(lines.join("\n"), 1, 0));
|
|
5925
6036
|
if (planResult.error)
|
|
5926
6037
|
this.showStatus(planResult.error);
|
|
5927
6038
|
this.ui.requestRender();
|
|
5928
6039
|
}
|
|
5929
|
-
// lunr: /token-usage — per-model token breakdown for the current session and
|
|
5930
|
-
// the last 30 days (from session files on disk).
|
|
5931
|
-
handleTokenUsageCommand() {
|
|
5932
|
-
const sessionRows = this.collectSessionModelRows();
|
|
5933
|
-
const history = this.collectUsageHistorySafe();
|
|
5934
|
-
const lines = renderTokenUsageBox({ sessionRows, history }, this.ui.terminal.columns);
|
|
5935
|
-
this.chatContainer.addChild(new Spacer(1));
|
|
5936
|
-
this.chatContainer.addChild(new Text(lines.join("\n"), 1, 0));
|
|
5937
|
-
this.ui.requestRender();
|
|
5938
|
-
}
|
|
5939
6040
|
// lunr: /context — estimated breakdown of what consumes the context window
|
|
5940
6041
|
// (system prompt incl. project context files, tool definitions, and the live
|
|
5941
6042
|
// session messages). Uses session.messages, so post-compaction renders match
|
|
@@ -5947,16 +6048,11 @@ export class InteractiveMode {
|
|
|
5947
6048
|
this.showStatus("No model selected or context window unknown.");
|
|
5948
6049
|
return;
|
|
5949
6050
|
}
|
|
5950
|
-
const
|
|
5951
|
-
|
|
5952
|
-
.
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
systemPrompt: this.session.systemPrompt,
|
|
5956
|
-
tools,
|
|
5957
|
-
messages: this.session.messages,
|
|
5958
|
-
contextWindow,
|
|
5959
|
-
});
|
|
6051
|
+
const breakdown = this.collectUsageBreakdown();
|
|
6052
|
+
if (!breakdown) {
|
|
6053
|
+
this.showStatus("No model selected or context window unknown.");
|
|
6054
|
+
return;
|
|
6055
|
+
}
|
|
5960
6056
|
const lines = renderContextBox({ breakdown, model: `${model.provider}/${model.id}` }, this.ui.terminal.columns);
|
|
5961
6057
|
this.chatContainer.addChild(new Spacer(1));
|
|
5962
6058
|
this.chatContainer.addChild(new Text(lines.join("\n"), 1, 0));
|
|
@@ -5982,7 +6078,7 @@ export class InteractiveMode {
|
|
|
5982
6078
|
else {
|
|
5983
6079
|
this.applyPermissionMode("plan");
|
|
5984
6080
|
}
|
|
5985
|
-
await this.
|
|
6081
|
+
await this.sendUserMessageAfterDeferredBuiltins(args);
|
|
5986
6082
|
return;
|
|
5987
6083
|
}
|
|
5988
6084
|
const next = sub === "" ? !inPlan : sub === "on";
|
|
@@ -6048,6 +6144,21 @@ export class InteractiveMode {
|
|
|
6048
6144
|
this.session.setSystemPromptAppend(undefined);
|
|
6049
6145
|
}
|
|
6050
6146
|
}
|
|
6147
|
+
// lunr: `/goal` forces session auto without writing settings.
|
|
6148
|
+
enterGoalAuto() {
|
|
6149
|
+
const next = nextModeForGoal(getPermissionMode(), "enter", this.preGoalPermissionMode);
|
|
6150
|
+
this.preGoalPermissionMode = next.saved;
|
|
6151
|
+
if (next.mode !== getPermissionMode()) {
|
|
6152
|
+
this.applyPermissionMode(next.mode, { silent: true });
|
|
6153
|
+
}
|
|
6154
|
+
}
|
|
6155
|
+
leaveGoalAuto() {
|
|
6156
|
+
const next = nextModeForGoal(getPermissionMode(), "leave", this.preGoalPermissionMode);
|
|
6157
|
+
this.preGoalPermissionMode = next.saved;
|
|
6158
|
+
if (next.mode !== getPermissionMode()) {
|
|
6159
|
+
this.applyPermissionMode(next.mode, { silent: true });
|
|
6160
|
+
}
|
|
6161
|
+
}
|
|
6051
6162
|
applyPermissionMode(mode, opts) {
|
|
6052
6163
|
const prev = getPermissionMode();
|
|
6053
6164
|
if (mode === "plan" && prev !== "plan") {
|
|
@@ -6143,7 +6254,13 @@ export class InteractiveMode {
|
|
|
6143
6254
|
// lunr: approval dialog for present_plan (plan mode). The "with feedback"
|
|
6144
6255
|
// options collect a one-line note via showExtensionInput that becomes part of
|
|
6145
6256
|
// the result text the model sees. Esc/close counts as Decline.
|
|
6257
|
+
showPlanInChat(summary) {
|
|
6258
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
6259
|
+
this.chatContainer.addChild(new PlanMessageComponent(summary, this.getMarkdownThemeWithSettings(), this.outputPad, this.settingsManager.getGutterRail()));
|
|
6260
|
+
this.ui.requestRender();
|
|
6261
|
+
}
|
|
6146
6262
|
async showPlanApprovalDialog(summary) {
|
|
6263
|
+
this.showPlanInChat(summary);
|
|
6147
6264
|
return new Promise((resolve) => {
|
|
6148
6265
|
this.showSelector((done) => {
|
|
6149
6266
|
const selector = new ExtensionSelectorComponent("▶ Approve plan?", ["Approve", "Approve with feedback", "Decline", "Decline with feedback"], (option) => {
|
|
@@ -6167,7 +6284,7 @@ export class InteractiveMode {
|
|
|
6167
6284
|
}, () => {
|
|
6168
6285
|
done();
|
|
6169
6286
|
resolve("reject");
|
|
6170
|
-
}
|
|
6287
|
+
});
|
|
6171
6288
|
return { component: selector, focus: selector };
|
|
6172
6289
|
});
|
|
6173
6290
|
});
|
|
@@ -6383,6 +6500,8 @@ ${cycleThinkingLevel ? `| \`${cycleThinkingLevel}\` | Cycle thinking level |\n`
|
|
|
6383
6500
|
async handleClearCommand() {
|
|
6384
6501
|
this.clearStatusIndicator();
|
|
6385
6502
|
this.ui.setChatScroll(0);
|
|
6503
|
+
// Deferred factories must be on builtinRoster before /new rebuilds the runtime.
|
|
6504
|
+
await this.waitForDeferredBuiltins();
|
|
6386
6505
|
// lunr: preserve the in-flight model across /new so a /model switch survives.
|
|
6387
6506
|
const previousModel = this.session.model;
|
|
6388
6507
|
try {
|