@ashx-j/lunr 0.1.7 → 0.2.1
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/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/catalog/models.json +1 -1
- package/dist/catalog/providers/amazon-bedrock.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/opencode.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/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/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/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/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/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 +3 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +18 -2
- 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 +17 -6
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +200 -120
- 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";
|
|
@@ -78,14 +79,14 @@ import { formatSubscriptionExhaustedUntil, SettingsSelectorComponent, } from "./
|
|
|
78
79
|
import { SkillInvocationMessageComponent } from "./components/skill-invocation-message.js";
|
|
79
80
|
import { BranchSummaryStatusIndicator, CompactionStatusIndicator, IdleStatus, RetryStatusIndicator, WorkingStatusIndicator, } from "./components/status-indicator.js";
|
|
80
81
|
import { updateThinkingRunTimings } from "./components/thinking-summary.js";
|
|
81
|
-
import { ToolExecutionComponent } from "./components/tool-execution.js";
|
|
82
|
+
import { applySameToolGrouping, 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 = [];
|
|
@@ -343,6 +346,7 @@ export class InteractiveMode {
|
|
|
343
346
|
this.resetExtensionUI();
|
|
344
347
|
// lunr: reset permission mode to configured default + clear session approvals
|
|
345
348
|
this.previousPermissionMode = undefined;
|
|
349
|
+
this.preGoalPermissionMode = undefined;
|
|
346
350
|
resetPermissions(this.settingsManager.getDefaultPermissionMode());
|
|
347
351
|
// lunr: clear process registry and rollback state
|
|
348
352
|
processRegistry.clearRegistry();
|
|
@@ -362,8 +366,17 @@ export class InteractiveMode {
|
|
|
362
366
|
this.version = VERSION;
|
|
363
367
|
this.ui = new TUI(new ProcessTerminal(), this.settingsManager.getShowHardwareCursor());
|
|
364
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
|
+
};
|
|
365
374
|
// lunr: register the permission-mode footer bridge (provider-side: InteractiveMode owns the state).
|
|
366
375
|
registerPermissionModeBridge(() => getPermissionMode());
|
|
376
|
+
registerPermissionModeControlBridge({
|
|
377
|
+
enterGoalAuto: () => this.enterGoalAuto(),
|
|
378
|
+
leaveGoalAuto: () => this.leaveGoalAuto(),
|
|
379
|
+
});
|
|
367
380
|
// lunr: reset permission mode to configured default on startup.
|
|
368
381
|
resetPermissions(this.settingsManager.getDefaultPermissionMode());
|
|
369
382
|
this.headerContainer = new Container();
|
|
@@ -2252,11 +2265,6 @@ export class InteractiveMode {
|
|
|
2252
2265
|
await this.handleUsageCommand();
|
|
2253
2266
|
return;
|
|
2254
2267
|
}
|
|
2255
|
-
if (text === "/token-usage") {
|
|
2256
|
-
this.editor.setText("");
|
|
2257
|
-
this.handleTokenUsageCommand();
|
|
2258
|
-
return;
|
|
2259
|
-
}
|
|
2260
2268
|
if (text === "/context") {
|
|
2261
2269
|
this.editor.setText("");
|
|
2262
2270
|
this.handleContextCommand();
|
|
@@ -2440,9 +2448,17 @@ export class InteractiveMode {
|
|
|
2440
2448
|
this.editor.addToHistory?.(text);
|
|
2441
2449
|
};
|
|
2442
2450
|
}
|
|
2443
|
-
/** Smooth streaming tick rate (
|
|
2444
|
-
static SMOOTH_STREAMING_TICK_MS =
|
|
2445
|
-
|
|
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
|
+
}
|
|
2446
2462
|
startSmoothStreaming() {
|
|
2447
2463
|
if (this.smoothStreamingTimer)
|
|
2448
2464
|
return;
|
|
@@ -2452,38 +2468,96 @@ export class InteractiveMode {
|
|
|
2452
2468
|
// Never keep the process alive just to finish a reveal animation.
|
|
2453
2469
|
this.smoothStreamingTimer.unref?.();
|
|
2454
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
|
+
}
|
|
2455
2512
|
advanceSmoothStreaming() {
|
|
2456
2513
|
const target = this.streamingTargetMessage;
|
|
2457
2514
|
if (!this.streamingComponent || !target) {
|
|
2458
2515
|
this.stopSmoothStreaming();
|
|
2459
2516
|
return;
|
|
2460
2517
|
}
|
|
2461
|
-
const
|
|
2462
|
-
|
|
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();
|
|
2463
2523
|
return;
|
|
2464
|
-
|
|
2465
|
-
// the more graphemes each tick reveals.
|
|
2524
|
+
}
|
|
2466
2525
|
const backlog = total - this.streamingDisplayedLength;
|
|
2467
|
-
this.streamingDisplayedLength +=
|
|
2526
|
+
this.streamingDisplayedLength += computeSmoothRevealStep(backlog);
|
|
2468
2527
|
if (this.streamingDisplayedLength > total) {
|
|
2469
2528
|
this.streamingDisplayedLength = total;
|
|
2470
2529
|
}
|
|
2471
|
-
this.
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
if (now - this.lastSmoothRenderAt >= 33) {
|
|
2475
|
-
this.lastSmoothRenderAt = now;
|
|
2476
|
-
this.ui.requestRender();
|
|
2530
|
+
this.paintSmoothStreamingFrame();
|
|
2531
|
+
if (this.streamingDisplayedLength >= total) {
|
|
2532
|
+
this.clearSmoothStreamingTimer();
|
|
2477
2533
|
}
|
|
2478
2534
|
}
|
|
2479
2535
|
stopSmoothStreaming() {
|
|
2480
|
-
|
|
2481
|
-
clearInterval(this.smoothStreamingTimer);
|
|
2482
|
-
this.smoothStreamingTimer = undefined;
|
|
2483
|
-
}
|
|
2536
|
+
this.clearSmoothStreamingTimer();
|
|
2484
2537
|
this.streamingTargetMessage = undefined;
|
|
2485
2538
|
this.streamingDisplayedLength = 0;
|
|
2486
|
-
|
|
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();
|
|
2487
2561
|
}
|
|
2488
2562
|
subscribeToAgent() {
|
|
2489
2563
|
this.unsubscribe = this.session.subscribe(async (event) => {
|
|
@@ -2561,7 +2635,7 @@ export class InteractiveMode {
|
|
|
2561
2635
|
this.streamingDisplayedLength = 0;
|
|
2562
2636
|
this.chatContainer.addChild(this.streamingComponent);
|
|
2563
2637
|
if (this.settingsManager.getSmoothStreaming()) {
|
|
2564
|
-
this.streamingComponent.updateContent(sliceMessageContent(this.streamingMessage, 0));
|
|
2638
|
+
this.streamingComponent.updateContent(sliceMessageContent(this.streamingMessage, 0, this.getSmoothStreamingOptions()));
|
|
2565
2639
|
}
|
|
2566
2640
|
else {
|
|
2567
2641
|
this.streamingComponent.updateContent(this.streamingMessage);
|
|
@@ -2577,40 +2651,32 @@ export class InteractiveMode {
|
|
|
2577
2651
|
// so smooth-streaming reveal lag doesn't inflate durations.
|
|
2578
2652
|
updateThinkingRunTimings(this.thinkingRunTimings, this.streamingMessage.content, Date.now(), false);
|
|
2579
2653
|
if (this.settingsManager.getSmoothStreaming()) {
|
|
2580
|
-
// Smooth streaming:
|
|
2581
|
-
//
|
|
2582
|
-
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
|
+
}
|
|
2583
2666
|
}
|
|
2584
2667
|
else {
|
|
2585
2668
|
this.streamingComponent.updateContent(this.streamingMessage);
|
|
2669
|
+
this.syncRevealedStreamingTools(this.streamingMessage, this.streamingMessage);
|
|
2670
|
+
this.ui.requestRender();
|
|
2586
2671
|
}
|
|
2587
|
-
for (const content of this.streamingMessage.content) {
|
|
2588
|
-
if (content.type === "toolCall") {
|
|
2589
|
-
if (!this.pendingTools.has(content.id)) {
|
|
2590
|
-
const component = new ToolExecutionComponent(content.name, content.id, content.arguments, {
|
|
2591
|
-
showImages: this.settingsManager.getShowImages(),
|
|
2592
|
-
imageWidthCells: this.settingsManager.getImageWidthCells(),
|
|
2593
|
-
}, this.getRegisteredToolDefinition(content.name), this.ui, this.sessionManager.getCwd());
|
|
2594
|
-
component.setExpanded(this.toolOutputExpanded);
|
|
2595
|
-
this.addToolComponentToChat(component);
|
|
2596
|
-
this.pendingTools.set(content.id, component);
|
|
2597
|
-
}
|
|
2598
|
-
else {
|
|
2599
|
-
const component = this.pendingTools.get(content.id);
|
|
2600
|
-
if (component) {
|
|
2601
|
-
component.updateArgs(content.arguments);
|
|
2602
|
-
}
|
|
2603
|
-
}
|
|
2604
|
-
}
|
|
2605
|
-
}
|
|
2606
|
-
this.ui.requestRender();
|
|
2607
2672
|
}
|
|
2608
2673
|
break;
|
|
2609
2674
|
case "message_end":
|
|
2610
2675
|
if (event.message.role === "user")
|
|
2611
2676
|
break;
|
|
2612
2677
|
if (this.streamingComponent && event.message.role === "assistant") {
|
|
2613
|
-
//
|
|
2678
|
+
// Snap remaining graphemes on end (abort/error and normal stop).
|
|
2679
|
+
// Avoids multi-second leftover animation; paint always follows below.
|
|
2614
2680
|
this.stopSmoothStreaming();
|
|
2615
2681
|
this.streamingMessage = event.message;
|
|
2616
2682
|
let errorMessage;
|
|
@@ -2653,6 +2719,18 @@ export class InteractiveMode {
|
|
|
2653
2719
|
this.ui.requestRender();
|
|
2654
2720
|
break;
|
|
2655
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
|
+
}
|
|
2656
2734
|
let component = this.pendingTools.get(event.toolCallId);
|
|
2657
2735
|
if (!component) {
|
|
2658
2736
|
component = new ToolExecutionComponent(event.toolName, event.toolCallId, event.args, {
|
|
@@ -3117,7 +3195,7 @@ export class InteractiveMode {
|
|
|
3117
3195
|
if (this.isShuttingDown)
|
|
3118
3196
|
return;
|
|
3119
3197
|
this.isShuttingDown = true;
|
|
3120
|
-
this.stopSmoothStreaming
|
|
3198
|
+
this.stopSmoothStreaming();
|
|
3121
3199
|
// Keep signal handlers registered until terminal cleanup has completed.
|
|
3122
3200
|
// `signal-exit` checks the listener list during the same SIGTERM/SIGHUP
|
|
3123
3201
|
// dispatch and re-sends the signal if only its own listeners remain.
|
|
@@ -3403,13 +3481,11 @@ export class InteractiveMode {
|
|
|
3403
3481
|
}
|
|
3404
3482
|
// lunr: consecutive same-tool grouping — a tool component appended right after
|
|
3405
3483
|
// another component for the SAME tool becomes a group continuation (no top
|
|
3406
|
-
// spacer)
|
|
3407
|
-
// Adjacency is decided at add time only
|
|
3484
|
+
// spacer / top pad); the previous card drops its bottom pad. Mixed neighbors
|
|
3485
|
+
// and singletons keep Box(1, 1). Adjacency is decided at add time only.
|
|
3408
3486
|
addToolComponentToChat(component) {
|
|
3409
3487
|
const last = this.chatContainer.children[this.chatContainer.children.length - 1];
|
|
3410
|
-
|
|
3411
|
-
component.setGroupContinuation(true);
|
|
3412
|
-
}
|
|
3488
|
+
applySameToolGrouping(last instanceof ToolExecutionComponent ? last : undefined, component);
|
|
3413
3489
|
this.chatContainer.addChild(component);
|
|
3414
3490
|
}
|
|
3415
3491
|
toggleThinkingBlockVisibility() {
|
|
@@ -3418,10 +3494,25 @@ export class InteractiveMode {
|
|
|
3418
3494
|
// Rebuild chat from session messages
|
|
3419
3495
|
this.chatContainer.clear();
|
|
3420
3496
|
this.rebuildChatFromMessages();
|
|
3421
|
-
// If streaming, re-add the streaming component with updated visibility and re-render
|
|
3497
|
+
// If streaming, re-add the streaming component with updated visibility and re-render.
|
|
3498
|
+
// Re-slice to the current revealed length so hide-thinking does not dump the tail.
|
|
3422
3499
|
if (this.streamingComponent && this.streamingMessage) {
|
|
3423
3500
|
this.streamingComponent.setHideThinkingBlock(this.hideThinkingBlock);
|
|
3424
|
-
this.
|
|
3501
|
+
if (this.settingsManager.getSmoothStreaming() && this.streamingTargetMessage) {
|
|
3502
|
+
const opts = this.getSmoothStreamingOptions();
|
|
3503
|
+
const total = countMessageGraphemes(this.streamingTargetMessage, opts);
|
|
3504
|
+
if (this.streamingDisplayedLength > total) {
|
|
3505
|
+
this.streamingDisplayedLength = total;
|
|
3506
|
+
}
|
|
3507
|
+
this.paintSmoothStreamingFrame();
|
|
3508
|
+
// Restart the timer if new (or still-pending) backlog exists.
|
|
3509
|
+
if (this.streamingDisplayedLength < total) {
|
|
3510
|
+
this.startSmoothStreaming();
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
3513
|
+
else {
|
|
3514
|
+
this.streamingComponent.updateContent(this.streamingMessage);
|
|
3515
|
+
}
|
|
3425
3516
|
this.chatContainer.addChild(this.streamingComponent);
|
|
3426
3517
|
}
|
|
3427
3518
|
this.showStatus(`Thinking blocks: ${this.hideThinkingBlock ? "hidden" : "visible"}`);
|
|
@@ -3818,7 +3909,7 @@ export class InteractiveMode {
|
|
|
3818
3909
|
this.settingsManager.setQuietStartup(enabled);
|
|
3819
3910
|
},
|
|
3820
3911
|
onSmoothStreamingChange: (enabled) => {
|
|
3821
|
-
this.
|
|
3912
|
+
this.applySmoothStreamingSettingChange(enabled);
|
|
3822
3913
|
},
|
|
3823
3914
|
onSessionRetentionDaysChange: (days) => {
|
|
3824
3915
|
this.settingsManager.setSessionRetentionDays(days);
|
|
@@ -5904,41 +5995,24 @@ export class InteractiveMode {
|
|
|
5904
5995
|
this.chatContainer.addChild(new Text(info, 1, 0));
|
|
5905
5996
|
this.ui.requestRender();
|
|
5906
5997
|
}
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
const
|
|
5910
|
-
|
|
5911
|
-
if (entry.type !== "message" || entry.message.role !== "assistant")
|
|
5912
|
-
continue;
|
|
5913
|
-
const message = entry.message;
|
|
5914
|
-
const usage = message.usage;
|
|
5915
|
-
const key = `${message.provider}/${message.responseModel ?? message.model}`;
|
|
5916
|
-
let row = perModelMap.get(key);
|
|
5917
|
-
if (!row) {
|
|
5918
|
-
row = { model: key, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };
|
|
5919
|
-
perModelMap.set(key, row);
|
|
5920
|
-
}
|
|
5921
|
-
row.input += usage.input;
|
|
5922
|
-
row.output += usage.output;
|
|
5923
|
-
row.cacheRead += usage.cacheRead;
|
|
5924
|
-
row.cacheWrite += usage.cacheWrite;
|
|
5925
|
-
row.total += usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
|
|
5926
|
-
}
|
|
5927
|
-
return Array.from(perModelMap.values()).sort((a, b) => b.total - a.total);
|
|
5928
|
-
}
|
|
5929
|
-
// lunr: 30-day history from session files on disk; best-effort — on any
|
|
5930
|
-
// error the section is simply omitted.
|
|
5931
|
-
collectUsageHistorySafe() {
|
|
5932
|
-
try {
|
|
5933
|
-
return collectUsageHistory({ sinceMs: Date.now() - 30 * 24 * 60 * 60 * 1000 });
|
|
5934
|
-
}
|
|
5935
|
-
catch {
|
|
5998
|
+
collectUsageBreakdown() {
|
|
5999
|
+
const model = this.session.model;
|
|
6000
|
+
const contextWindow = model?.contextWindow ?? 0;
|
|
6001
|
+
if (!model || contextWindow <= 0)
|
|
5936
6002
|
return undefined;
|
|
5937
|
-
|
|
6003
|
+
const tools = this.session
|
|
6004
|
+
.getActiveToolNames()
|
|
6005
|
+
.map((name) => this.session.getToolDefinition(name))
|
|
6006
|
+
.filter((definition) => definition !== undefined);
|
|
6007
|
+
return computeContextBreakdown({
|
|
6008
|
+
systemPrompt: this.session.systemPrompt,
|
|
6009
|
+
tools,
|
|
6010
|
+
messages: this.session.messages,
|
|
6011
|
+
contextWindow,
|
|
6012
|
+
});
|
|
5938
6013
|
}
|
|
5939
|
-
// lunr: /usage —
|
|
5940
|
-
// bar, and subscription plan usage
|
|
5941
|
-
// adapter). Per-model rows live in /token-usage.
|
|
6014
|
+
// lunr: /usage — current session only: token totals, live context split,
|
|
6015
|
+
// context-window bar, and subscription plan usage.
|
|
5942
6016
|
async handleUsageCommand() {
|
|
5943
6017
|
const stats = this.session.getSessionStats();
|
|
5944
6018
|
const sessionTotals = stats.tokens.total > 0
|
|
@@ -5953,24 +6027,14 @@ export class InteractiveMode {
|
|
|
5953
6027
|
const context = this.session.getContextUsage();
|
|
5954
6028
|
const provider = this.session.model?.provider;
|
|
5955
6029
|
const planResult = provider ? await getPlanUsageResult(provider, this.session.modelRuntime) : {};
|
|
5956
|
-
const
|
|
5957
|
-
const lines = renderUsageBox({ sessionTotals, context, plan: planResult.usage,
|
|
6030
|
+
const breakdown = this.collectUsageBreakdown();
|
|
6031
|
+
const lines = renderUsageBox({ sessionTotals, context, plan: planResult.usage, breakdown }, this.ui.terminal.columns);
|
|
5958
6032
|
this.chatContainer.addChild(new Spacer(1));
|
|
5959
6033
|
this.chatContainer.addChild(new Text(lines.join("\n"), 1, 0));
|
|
5960
6034
|
if (planResult.error)
|
|
5961
6035
|
this.showStatus(planResult.error);
|
|
5962
6036
|
this.ui.requestRender();
|
|
5963
6037
|
}
|
|
5964
|
-
// lunr: /token-usage — per-model token breakdown for the current session and
|
|
5965
|
-
// the last 30 days (from session files on disk).
|
|
5966
|
-
handleTokenUsageCommand() {
|
|
5967
|
-
const sessionRows = this.collectSessionModelRows();
|
|
5968
|
-
const history = this.collectUsageHistorySafe();
|
|
5969
|
-
const lines = renderTokenUsageBox({ sessionRows, history }, this.ui.terminal.columns);
|
|
5970
|
-
this.chatContainer.addChild(new Spacer(1));
|
|
5971
|
-
this.chatContainer.addChild(new Text(lines.join("\n"), 1, 0));
|
|
5972
|
-
this.ui.requestRender();
|
|
5973
|
-
}
|
|
5974
6038
|
// lunr: /context — estimated breakdown of what consumes the context window
|
|
5975
6039
|
// (system prompt incl. project context files, tool definitions, and the live
|
|
5976
6040
|
// session messages). Uses session.messages, so post-compaction renders match
|
|
@@ -5982,16 +6046,11 @@ export class InteractiveMode {
|
|
|
5982
6046
|
this.showStatus("No model selected or context window unknown.");
|
|
5983
6047
|
return;
|
|
5984
6048
|
}
|
|
5985
|
-
const
|
|
5986
|
-
|
|
5987
|
-
.
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
systemPrompt: this.session.systemPrompt,
|
|
5991
|
-
tools,
|
|
5992
|
-
messages: this.session.messages,
|
|
5993
|
-
contextWindow,
|
|
5994
|
-
});
|
|
6049
|
+
const breakdown = this.collectUsageBreakdown();
|
|
6050
|
+
if (!breakdown) {
|
|
6051
|
+
this.showStatus("No model selected or context window unknown.");
|
|
6052
|
+
return;
|
|
6053
|
+
}
|
|
5995
6054
|
const lines = renderContextBox({ breakdown, model: `${model.provider}/${model.id}` }, this.ui.terminal.columns);
|
|
5996
6055
|
this.chatContainer.addChild(new Spacer(1));
|
|
5997
6056
|
this.chatContainer.addChild(new Text(lines.join("\n"), 1, 0));
|
|
@@ -6083,6 +6142,21 @@ export class InteractiveMode {
|
|
|
6083
6142
|
this.session.setSystemPromptAppend(undefined);
|
|
6084
6143
|
}
|
|
6085
6144
|
}
|
|
6145
|
+
// lunr: `/goal` forces session auto without writing settings.
|
|
6146
|
+
enterGoalAuto() {
|
|
6147
|
+
const next = nextModeForGoal(getPermissionMode(), "enter", this.preGoalPermissionMode);
|
|
6148
|
+
this.preGoalPermissionMode = next.saved;
|
|
6149
|
+
if (next.mode !== getPermissionMode()) {
|
|
6150
|
+
this.applyPermissionMode(next.mode, { silent: true });
|
|
6151
|
+
}
|
|
6152
|
+
}
|
|
6153
|
+
leaveGoalAuto() {
|
|
6154
|
+
const next = nextModeForGoal(getPermissionMode(), "leave", this.preGoalPermissionMode);
|
|
6155
|
+
this.preGoalPermissionMode = next.saved;
|
|
6156
|
+
if (next.mode !== getPermissionMode()) {
|
|
6157
|
+
this.applyPermissionMode(next.mode, { silent: true });
|
|
6158
|
+
}
|
|
6159
|
+
}
|
|
6086
6160
|
applyPermissionMode(mode, opts) {
|
|
6087
6161
|
const prev = getPermissionMode();
|
|
6088
6162
|
if (mode === "plan" && prev !== "plan") {
|
|
@@ -6178,7 +6252,13 @@ export class InteractiveMode {
|
|
|
6178
6252
|
// lunr: approval dialog for present_plan (plan mode). The "with feedback"
|
|
6179
6253
|
// options collect a one-line note via showExtensionInput that becomes part of
|
|
6180
6254
|
// the result text the model sees. Esc/close counts as Decline.
|
|
6255
|
+
showPlanInChat(summary) {
|
|
6256
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
6257
|
+
this.chatContainer.addChild(new PlanMessageComponent(summary, this.getMarkdownThemeWithSettings(), this.outputPad, this.settingsManager.getGutterRail()));
|
|
6258
|
+
this.ui.requestRender();
|
|
6259
|
+
}
|
|
6181
6260
|
async showPlanApprovalDialog(summary) {
|
|
6261
|
+
this.showPlanInChat(summary);
|
|
6182
6262
|
return new Promise((resolve) => {
|
|
6183
6263
|
this.showSelector((done) => {
|
|
6184
6264
|
const selector = new ExtensionSelectorComponent("▶ Approve plan?", ["Approve", "Approve with feedback", "Decline", "Decline with feedback"], (option) => {
|
|
@@ -6202,7 +6282,7 @@ export class InteractiveMode {
|
|
|
6202
6282
|
}, () => {
|
|
6203
6283
|
done();
|
|
6204
6284
|
resolve("reject");
|
|
6205
|
-
}
|
|
6285
|
+
});
|
|
6206
6286
|
return { component: selector, focus: selector };
|
|
6207
6287
|
});
|
|
6208
6288
|
});
|