pi-ui-extend 1.0.42 → 1.0.45
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/README.md +41 -12
- package/dist/app/app.d.ts +10 -0
- package/dist/app/app.js +132 -8
- package/dist/app/cli/install.js +2 -2
- package/dist/app/commands/command-controller.d.ts +4 -0
- package/dist/app/commands/command-controller.js +11 -0
- package/dist/app/commands/command-git-actions.d.ts +25 -0
- package/dist/app/commands/command-git-actions.js +381 -0
- package/dist/app/commands/command-host.d.ts +3 -0
- package/dist/app/commands/command-host.js +5 -2
- package/dist/app/commands/command-model-actions.d.ts +1 -0
- package/dist/app/commands/command-model-actions.js +51 -34
- package/dist/app/commands/command-navigation-actions.d.ts +1 -0
- package/dist/app/commands/command-navigation-actions.js +16 -3
- package/dist/app/commands/command-registry.d.ts +2 -0
- package/dist/app/commands/command-registry.js +14 -0
- package/dist/app/commands/reload-context-inventory.js +12 -1
- package/dist/app/input/autocomplete-controller.js +61 -34
- package/dist/app/input/input-action-controller.d.ts +2 -0
- package/dist/app/input/input-action-controller.js +8 -1
- package/dist/app/input/input-controller.d.ts +2 -1
- package/dist/app/input/input-controller.js +7 -2
- package/dist/app/input/prompt-enhancer-controller.js +53 -35
- package/dist/app/input/voice-controller.d.ts +41 -45
- package/dist/app/input/voice-controller.js +351 -419
- package/dist/app/model/model-usage-controller.js +17 -7
- package/dist/app/model/model-usage-status.d.ts +4 -1
- package/dist/app/model/model-usage-status.js +270 -46
- package/dist/app/popup/menu-items-controller.d.ts +13 -3
- package/dist/app/popup/menu-items-controller.js +37 -21
- package/dist/app/popup/popup-action-controller.d.ts +12 -2
- package/dist/app/popup/popup-action-controller.js +77 -24
- package/dist/app/popup/popup-menu-controller.d.ts +36 -14
- package/dist/app/popup/popup-menu-controller.js +239 -69
- package/dist/app/rendering/dcp-stats.d.ts +9 -4
- package/dist/app/rendering/dcp-stats.js +40 -425
- package/dist/app/rendering/popup-menu-renderer.d.ts +3 -5
- package/dist/app/rendering/popup-menu-renderer.js +42 -37
- package/dist/app/rendering/render-controller.js +23 -2
- package/dist/app/rendering/status-line-renderer.d.ts +5 -0
- package/dist/app/rendering/status-line-renderer.js +41 -5
- package/dist/app/rendering/tab-line-renderer.js +26 -20
- package/dist/app/runtime.d.ts +12 -1
- package/dist/app/runtime.js +120 -12
- package/dist/app/screen/mouse-controller.d.ts +2 -0
- package/dist/app/screen/mouse-controller.js +17 -7
- package/dist/app/screen/status-controller.d.ts +4 -0
- package/dist/app/screen/status-controller.js +5 -0
- package/dist/app/session/lazy-session-manager.js +34 -0
- package/dist/app/session/session-event-controller.d.ts +1 -0
- package/dist/app/session/session-event-controller.js +10 -1
- package/dist/app/session/session-history.d.ts +1 -0
- package/dist/app/session/session-history.js +12 -1
- package/dist/app/session/session-lifecycle-controller.d.ts +6 -1
- package/dist/app/session/session-lifecycle-controller.js +10 -1
- package/dist/app/session/tabs-controller.d.ts +25 -1
- package/dist/app/session/tabs-controller.js +442 -153
- package/dist/app/subagents/subagents-widget-controller.d.ts +1 -0
- package/dist/app/subagents/subagents-widget-controller.js +6 -0
- package/dist/app/types.d.ts +11 -1
- package/dist/app/workspace/workspace-actions-controller.js +10 -2
- package/dist/app/workspace/workspace-undo.d.ts +1 -0
- package/dist/app/workspace/workspace-undo.js +1 -0
- package/dist/bundled-extensions/question/index.js +9 -1
- package/dist/bundled-extensions/question/remote.d.ts +4 -0
- package/dist/bundled-extensions/question/remote.js +33 -0
- package/dist/bundled-extensions/telegram-connector/bot.d.ts +43 -0
- package/dist/bundled-extensions/telegram-connector/bot.js +166 -0
- package/dist/bundled-extensions/telegram-connector/config.d.ts +8 -0
- package/dist/bundled-extensions/telegram-connector/config.js +87 -0
- package/dist/bundled-extensions/telegram-connector/coordinator.d.ts +66 -0
- package/dist/bundled-extensions/telegram-connector/coordinator.js +413 -0
- package/dist/bundled-extensions/telegram-connector/index.d.ts +3 -0
- package/dist/bundled-extensions/telegram-connector/index.js +195 -0
- package/dist/bundled-extensions/terminal-bell/index.d.ts +0 -8
- package/dist/bundled-extensions/terminal-bell/index.js +0 -76
- package/dist/bundled-extensions/workspace-undo/index.d.ts +26 -0
- package/dist/bundled-extensions/workspace-undo/index.js +191 -0
- package/dist/config.d.ts +21 -2
- package/dist/config.js +159 -32
- package/dist/default-pix-config.js +20 -10
- package/dist/schemas/pi-tools-suite-schema.d.ts +8 -4
- package/dist/schemas/pi-tools-suite-schema.js +11 -5
- package/dist/schemas/pix-schema.d.ts +12 -2
- package/dist/schemas/pix-schema.js +37 -9
- package/dist/schemas/tasks-schema.js +1 -1
- package/docs/concurrency.md +3 -1
- package/docs/desktop-mvp.md +10 -5
- package/docs/desktop-task-manager.md +81 -28
- package/docs/release.md +30 -6
- package/external/pi-tools-suite/README.md +165 -39
- package/external/pi-tools-suite/docs/session-recovery.md +7 -0
- package/external/pi-tools-suite/docs/subagent-model-pools.md +12 -5
- package/external/pi-tools-suite/docs/ui-qa-subagent.md +440 -0
- package/external/pi-tools-suite/package.json +6 -1
- package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +2 -1
- package/external/pi-tools-suite/src/antigravity-auth/constants.ts +9 -3
- package/external/pi-tools-suite/src/antigravity-auth/headers.ts +41 -3
- package/external/pi-tools-suite/src/antigravity-auth/models.ts +94 -21
- package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +5 -17
- package/external/pi-tools-suite/src/antigravity-auth/payload.ts +72 -7
- package/external/pi-tools-suite/src/antigravity-auth/stream.ts +13 -1
- package/external/pi-tools-suite/src/async-subagents/agents/implement.md +1 -1
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/browser.mjs +346 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/desktop.mjs +1038 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/tui.mjs +759 -0
- package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/scripts/browser-qa-runner.mjs +32 -23
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/chrome-devtools/chrome-devtools-provider.mjs +895 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/linux/linux-atspi.py +494 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/macos/macos-accessibility.swift +1085 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/native-terminal/bridge-client.mjs +50 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/native-terminal/native-terminal-host.mjs +501 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/windows/windows-uia.ps1 +454 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-auth.md +114 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-chrome-devtools.md +91 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-playwright.md +145 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser.md +82 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-linux.md +26 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-macos.md +29 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-windows.md +23 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop.md +54 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui-native-terminal.md +59 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui-pty.md +37 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui.md +60 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/scripts/ui-qa-runner.mjs +535 -0
- package/external/pi-tools-suite/src/async-subagents/agents/ui-qa.md +75 -0
- package/external/pi-tools-suite/src/async-subagents/commands.ts +1 -1
- package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +1 -1
- package/external/pi-tools-suite/src/async-subagents/core/agents-dir.ts +0 -2
- package/external/pi-tools-suite/src/async-subagents/core/browser-qa.ts +16 -2
- package/external/pi-tools-suite/src/async-subagents/core/config.ts +40 -17
- package/external/pi-tools-suite/src/async-subagents/core/model-selection.ts +2 -1
- package/external/pi-tools-suite/src/async-subagents/core/registry.ts +158 -33
- package/external/pi-tools-suite/src/async-subagents/core/routing.ts +7 -4
- package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +34 -24
- package/external/pi-tools-suite/src/async-subagents/core/ultrawork-auto.ts +51 -45
- package/external/pi-tools-suite/src/async-subagents/lib.ts +9 -1
- package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +0 -1
- package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +2 -2
- package/external/pi-tools-suite/src/coding-discipline/index.ts +90 -59
- package/external/pi-tools-suite/src/config.ts +14 -0
- package/external/pi-tools-suite/src/context-gateway/accounting-log.ts +282 -0
- package/external/pi-tools-suite/src/context-gateway/config.ts +66 -1
- package/external/pi-tools-suite/src/context-gateway/efficiency.ts +414 -0
- package/external/pi-tools-suite/src/context-gateway/enforcement.ts +123 -3
- package/external/pi-tools-suite/src/context-gateway/index.ts +137 -4
- package/external/pi-tools-suite/src/context-gateway/storeless-capabilities.ts +5 -5
- package/external/pi-tools-suite/src/context-gateway/telemetry.ts +2 -2
- package/external/pi-tools-suite/src/context-gateway/types.ts +8 -1
- package/external/pi-tools-suite/src/dcp/auto-compress-budget.ts +39 -4
- package/external/pi-tools-suite/src/dcp/auto-compress.ts +41 -14
- package/external/pi-tools-suite/src/dcp/commands.ts +14 -156
- package/external/pi-tools-suite/src/dcp/compress-tool.ts +8 -2
- package/external/pi-tools-suite/src/dcp/compression-progress.ts +22 -0
- package/external/pi-tools-suite/src/dcp/config.ts +58 -4
- package/external/pi-tools-suite/src/dcp/conversation-index.ts +36 -9
- package/external/pi-tools-suite/src/dcp/diagnostics.ts +41 -0
- package/external/pi-tools-suite/src/dcp/fresh-tool-results.ts +38 -0
- package/external/pi-tools-suite/src/dcp/index.ts +338 -66
- package/external/pi-tools-suite/src/dcp/journal.ts +126 -4
- package/external/pi-tools-suite/src/dcp/progress-controller.ts +2 -1
- package/external/pi-tools-suite/src/dcp/pruner-candidates.ts +92 -2
- package/external/pi-tools-suite/src/dcp/pruner-nudge.ts +36 -33
- package/external/pi-tools-suite/src/dcp/pruner.ts +1 -0
- package/external/pi-tools-suite/src/dcp/routine-pressure.ts +86 -0
- package/external/pi-tools-suite/src/dcp/state.ts +9 -0
- package/external/pi-tools-suite/src/dcp/statistics.d.ts +8 -0
- package/external/pi-tools-suite/src/dcp/statistics.js +156 -0
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +68 -16
- package/external/pi-tools-suite/src/lib/project.ts +36 -1
- package/external/pi-tools-suite/src/model-tools/index.ts +10 -7
- package/external/pi-tools-suite/src/repo-discovery/index.ts +304 -4
- package/external/pi-tools-suite/src/resource-registry/index.ts +282 -21
- package/external/pi-tools-suite/src/session-recovery/index.ts +17 -0
- package/external/pi-tools-suite/src/todo/index.ts +21 -0
- package/external/pi-tools-suite/src/todo/todo.ts +19 -1
- package/external/pi-tools-suite/src/tool-descriptions.ts +44 -15
- package/package.json +7 -8
- package/schemas/pi-tools-suite.json +44 -12
- package/schemas/pix.json +109 -15
- package/schemas/tasks.json +1 -2
- package/skills/simplify/SKILL.md +33 -5
- package/external/pi-tools-suite/docs/browser-qa-subagent.md +0 -196
- package/external/pi-tools-suite/src/async-subagents/agents/browser-qa.md +0 -598
- package/skills/playwright-cli/SKILL.md +0 -420
- package/skills/playwright-cli/references/element-attributes.md +0 -23
- package/skills/playwright-cli/references/playwright-tests.md +0 -50
- package/skills/playwright-cli/references/request-mocking.md +0 -87
- package/skills/playwright-cli/references/running-code.md +0 -241
- package/skills/playwright-cli/references/session-management.md +0 -273
- package/skills/playwright-cli/references/spec-driven-testing.md +0 -311
- package/skills/playwright-cli/references/storage-state.md +0 -290
- package/skills/playwright-cli/references/test-generation.md +0 -142
- package/skills/playwright-cli/references/tracing.md +0 -154
- package/skills/playwright-cli/references/video-recording.md +0 -147
- package/skills/spec-wiki/SKILL.md +0 -55
- package/skills/spec-wiki/evals/evals.json +0 -132
- package/skills/spec-wiki/evals/files/bootstrap/architecture/rfc-007.md +0 -14
- package/skills/spec-wiki/evals/files/bootstrap/docs/how-to-pay.md +0 -4
- package/skills/spec-wiki/evals/files/bootstrap/docs/overview.md +0 -9
- package/skills/spec-wiki/evals/files/bootstrap/requirements/payment-behavior.md +0 -17
- package/skills/spec-wiki/evals/files/bootstrap/src/payments.ts +0 -12
- package/skills/spec-wiki/evals/files/bootstrap/tests/payments.test.ts +0 -11
- package/skills/spec-wiki/evals/files/focused/src/session.ts +0 -5
- package/skills/spec-wiki/evals/files/focused/tests/session.test.ts +0 -17
- package/skills/spec-wiki/evals/files/maintenance/low-signal/incoming/capture.txt +0 -2
- package/skills/spec-wiki/evals/files/maintenance/low-signal/src/capture.ts +0 -3
- package/skills/spec-wiki/evals/files/maintenance/move/docs/old-job-contract.md +0 -18
- package/skills/spec-wiki/evals/files/maintenance/move/src/job.ts +0 -5
- package/skills/spec-wiki/evals/files/maintenance/new-relation/docs/session-refresh.md +0 -24
- package/skills/spec-wiki/evals/files/maintenance/new-relation/incoming/refresh-worker.ts +0 -7
- package/skills/spec-wiki/evals/files/maintenance/new-relation/src/session.ts +0 -7
- package/skills/spec-wiki/evals/files/maintenance/no-impact/docs/auth-contract.md +0 -17
- package/skills/spec-wiki/evals/files/maintenance/no-impact/incoming/telemetry-format.ts +0 -3
- package/skills/spec-wiki/evals/files/maintenance/no-impact/src/auth.ts +0 -3
- package/skills/spec-wiki/evals/files/query/docs/how-to-run-jobs.md +0 -4
- package/skills/spec-wiki/evals/files/query/docs/spec-overview.md +0 -9
- package/skills/spec-wiki/evals/files/query/features/jobs/cancellation-contract.md +0 -15
- package/skills/spec-wiki/evals/files/query/features/jobs/retry-spec.md +0 -6
- package/skills/spec-wiki/evals/maintenance_regression.py +0 -311
- package/skills/spec-wiki/evals/retrieval-eval.md +0 -37
- package/skills/spec-wiki/evals/retrieval-evals.json +0 -37
- package/skills/spec-wiki/evals/trigger-evals.json +0 -94
- package/skills/spec-wiki/references/bootstrap.md +0 -100
- package/skills/spec-wiki/references/focused-spec.md +0 -117
- package/skills/spec-wiki/references/index-schema.md +0 -330
- package/skills/spec-wiki/references/maintenance.md +0 -134
- package/skills/spec-wiki/references/query.md +0 -74
- package/skills/spec-wiki/scripts/spec_wiki.py +0 -2076
- /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/examples/qa-auth.example.jsonc +0 -0
- /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/examples/qa-flow.example.jsonc +0 -0
- /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/vendor/fflate.LICENSE +0 -0
- /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/vendor/fflate.mjs +0 -0
|
@@ -52,6 +52,7 @@ export class AppRenderController {
|
|
|
52
52
|
const defaultOverlayLines = menuLines.slice(0, Math.max(0, inputSeparatorRow - 1));
|
|
53
53
|
const underTabsOverlayStartRow = Math.min(rows, topReservedRows + 1);
|
|
54
54
|
const underTabsOverlayLines = menuLines.slice(0, Math.max(0, statusRow - underTabsOverlayStartRow));
|
|
55
|
+
const draftSurfaceLines = menuLines.slice(0, Math.max(0, bodyHeight));
|
|
55
56
|
const { lines: visible, metrics: scrollMetrics } = this.deps.scrollController.conversationView(columns, bodyHeight);
|
|
56
57
|
const conversationColumns = Math.max(1, Math.min(columns, scrollMetrics.viewportColumns));
|
|
57
58
|
this.deps.mouseController.syncConversationSelectionForRender(scrollMetrics.start, bodyHeight, topReservedRows, conversationColumns);
|
|
@@ -104,9 +105,27 @@ export class AppRenderController {
|
|
|
104
105
|
.filter((target) => target.kind === "new-tab")
|
|
105
106
|
.map((target) => ({ ...target, row: tabRow })));
|
|
106
107
|
}
|
|
108
|
+
const draftSurfaceMenu = popupMenuPlacement === "draft-surface"
|
|
109
|
+
? this.deps.popupMenus.getActivePopupMenu(activePopupMenu ?? this.deps.popupMenus.syncActivePopupMenu() ?? "slash")
|
|
110
|
+
: undefined;
|
|
107
111
|
for (let index = 0; index < bodyHeight; index += 1) {
|
|
108
|
-
const rendered = visible[index];
|
|
109
112
|
const row = toScreenRow(index + 1);
|
|
113
|
+
if (draftSurfaceMenu) {
|
|
114
|
+
const line = draftSurfaceLines[index];
|
|
115
|
+
if (line) {
|
|
116
|
+
const fallbackTarget = { kind: "popup-menu", index: draftSurfaceMenu.selectedIndex };
|
|
117
|
+
this.deps.mouseController.renderedTargets.set(row, line.target ?? fallbackTarget);
|
|
118
|
+
this.deps.mouseController.renderedRowTexts.set(row, this.deps.popupMenus.overlayPlainText(line, columns));
|
|
119
|
+
setRenderedBackground(row, line.backgroundOverride);
|
|
120
|
+
appendFrameOutput(row, this.renderFrameRow(row, this.deps.popupMenus.styleOverlayLine(row, line, columns)));
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
this.deps.mouseController.renderedRowTexts.set(row, "");
|
|
124
|
+
appendFrameOutput(row, this.renderFrameRow(row, this.deps.screenStyler.styleBaseLine(row, undefined, columns)));
|
|
125
|
+
}
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const rendered = visible[index];
|
|
110
129
|
if (rendered?.target)
|
|
111
130
|
this.deps.mouseController.renderedTargets.set(row, rendered.target);
|
|
112
131
|
if (rendered?.imageTargets?.length)
|
|
@@ -117,7 +136,9 @@ export class AppRenderController {
|
|
|
117
136
|
setRenderedBackground(row, rendered?.backgroundOverride);
|
|
118
137
|
appendFrameOutput(row, this.renderFrameRow(row, this.deps.screenStyler.styleBaseLine(row, rendered, conversationColumns)));
|
|
119
138
|
}
|
|
120
|
-
const loadingConversationOverlay =
|
|
139
|
+
const loadingConversationOverlay = popupMenuPlacement === "draft-surface"
|
|
140
|
+
? undefined
|
|
141
|
+
: this.renderConversationLoadingOverlay(this.deps.loadingConversationOverlayText?.(), conversationColumns, topReservedRows, bodyHeight);
|
|
121
142
|
if (loadingConversationOverlay) {
|
|
122
143
|
this.deps.mouseController.renderedRowTexts.set(loadingConversationOverlay.row, loadingConversationOverlay.text);
|
|
123
144
|
appendFrameOutput(loadingConversationOverlay.row, this.renderFrameRow(loadingConversationOverlay.row, loadingConversationOverlay.output));
|
|
@@ -7,6 +7,10 @@ export type StatusLineRendererHost = {
|
|
|
7
7
|
readonly theme: Theme;
|
|
8
8
|
readonly screenStyler: ScreenStyler;
|
|
9
9
|
readonly session: AgentSession | undefined;
|
|
10
|
+
draftModelStatus?(): {
|
|
11
|
+
modelLabel: string;
|
|
12
|
+
thinkingLabel: string;
|
|
13
|
+
} | undefined;
|
|
10
14
|
readonly modelColors?: ModelColorsConfig;
|
|
11
15
|
readonly sessionActivity: SessionActivity;
|
|
12
16
|
readonly statusDotBright: boolean;
|
|
@@ -76,6 +80,7 @@ export declare class StatusLineRenderer {
|
|
|
76
80
|
private pushContextBarSegments;
|
|
77
81
|
private modelUsageProgressColor;
|
|
78
82
|
private modelProviderColor;
|
|
83
|
+
private modelRefColor;
|
|
79
84
|
private thinkingLevelColor;
|
|
80
85
|
private statusThinkingDisplayLabel;
|
|
81
86
|
private availableThinkingLevels;
|
|
@@ -180,8 +180,16 @@ export class StatusLineRenderer {
|
|
|
180
180
|
}
|
|
181
181
|
modelTarget(statusText, row) {
|
|
182
182
|
const session = this.host.session;
|
|
183
|
-
if (!session)
|
|
184
|
-
|
|
183
|
+
if (!session) {
|
|
184
|
+
const draft = this.host.draftModelStatus?.();
|
|
185
|
+
if (!draft)
|
|
186
|
+
return undefined;
|
|
187
|
+
const marker = `${draft.modelLabel} ${APP_ICONS.lightbulb} ${draft.thinkingLabel}`;
|
|
188
|
+
const startIndex = statusText.indexOf(marker);
|
|
189
|
+
if (startIndex < 0)
|
|
190
|
+
return undefined;
|
|
191
|
+
return { row, startColumn: startIndex + 1, endColumn: startIndex + draft.modelLabel.length + 1 };
|
|
192
|
+
}
|
|
185
193
|
const label = this.host.statusModelLabel(session);
|
|
186
194
|
const marker = `${label} ${this.statusThinkingDisplayLabel(session)} `;
|
|
187
195
|
const startIndex = statusText.indexOf(marker);
|
|
@@ -191,8 +199,17 @@ export class StatusLineRenderer {
|
|
|
191
199
|
}
|
|
192
200
|
thinkingTarget(statusText, row) {
|
|
193
201
|
const session = this.host.session;
|
|
194
|
-
if (!session)
|
|
195
|
-
|
|
202
|
+
if (!session) {
|
|
203
|
+
const draft = this.host.draftModelStatus?.();
|
|
204
|
+
if (!draft)
|
|
205
|
+
return undefined;
|
|
206
|
+
const marker = `${APP_ICONS.lightbulb} ${draft.thinkingLabel}`;
|
|
207
|
+
const markerIndex = statusText.indexOf(marker);
|
|
208
|
+
if (markerIndex < 0)
|
|
209
|
+
return undefined;
|
|
210
|
+
const startIndex = markerIndex + APP_ICONS.lightbulb.length + 1;
|
|
211
|
+
return { row, startColumn: startIndex + 1, endColumn: startIndex + draft.thinkingLabel.length + 1 };
|
|
212
|
+
}
|
|
196
213
|
const label = this.statusThinkingDisplayLabel(session);
|
|
197
214
|
const marker = ` ${label} ${this.host.formatContextUsagePercent(session)}`;
|
|
198
215
|
const markerIndex = statusText.indexOf(marker);
|
|
@@ -327,8 +344,17 @@ export class StatusLineRenderer {
|
|
|
327
344
|
if (layout.modelUsageLabel)
|
|
328
345
|
this.pushModelUsageSegments(segments, statusText, layout.modelUsageLabel);
|
|
329
346
|
const session = this.host.session;
|
|
330
|
-
if (!session)
|
|
347
|
+
if (!session) {
|
|
348
|
+
const draft = this.host.draftModelStatus?.();
|
|
349
|
+
if (!draft)
|
|
350
|
+
return segments;
|
|
351
|
+
const modelStart = statusText.indexOf(`${draft.modelLabel} ${APP_ICONS.lightbulb} ${draft.thinkingLabel}`);
|
|
352
|
+
this.pushSegment(segments, modelStart, draft.modelLabel.length, this.modelRefColor(draft.modelLabel));
|
|
353
|
+
const thinkingMarkerStart = statusText.indexOf(`${APP_ICONS.lightbulb} ${draft.thinkingLabel}`);
|
|
354
|
+
const thinkingStart = thinkingMarkerStart >= 0 ? thinkingMarkerStart + APP_ICONS.lightbulb.length + 1 : -1;
|
|
355
|
+
this.pushSegment(segments, thinkingStart, draft.thinkingLabel.length, this.thinkingLevelColor(draft.thinkingLabel));
|
|
331
356
|
return segments;
|
|
357
|
+
}
|
|
332
358
|
const modelLabel = this.host.statusModelLabel(session);
|
|
333
359
|
const thinkingLabel = this.host.statusThinkingLabel(session);
|
|
334
360
|
const thinkingDisplayLabel = this.statusThinkingDisplayLabel(session);
|
|
@@ -511,6 +537,16 @@ export class StatusLineRenderer {
|
|
|
511
537
|
? resolveColor(configuredColor, this.host.theme.colors)
|
|
512
538
|
: modelProviderThemeColor(provider, this.host.theme.colors);
|
|
513
539
|
}
|
|
540
|
+
modelRefColor(ref) {
|
|
541
|
+
const slash = ref.indexOf("/");
|
|
542
|
+
const provider = slash > 0 ? ref.slice(0, slash) : undefined;
|
|
543
|
+
if (!provider)
|
|
544
|
+
return this.host.theme.colors.selectionForeground;
|
|
545
|
+
const configuredColor = this.host.modelColors ? resolveModelColor(ref, this.host.modelColors) : undefined;
|
|
546
|
+
return configuredColor
|
|
547
|
+
? resolveColor(configuredColor, this.host.theme.colors)
|
|
548
|
+
: modelProviderThemeColor(provider, this.host.theme.colors);
|
|
549
|
+
}
|
|
514
550
|
thinkingLevelColor(label) {
|
|
515
551
|
return thinkingLevelThemeColor(label, this.host.theme.colors, this.availableThinkingLevels());
|
|
516
552
|
}
|
|
@@ -29,13 +29,15 @@ export class TabLineRenderer {
|
|
|
29
29
|
const newTabPrefix = tabs.length > 0 ? separator : EMPTY_NEW_TAB_PREFIX;
|
|
30
30
|
const newTabPrefixWidth = stringDisplayWidth(newTabPrefix);
|
|
31
31
|
const tabsWidth = Math.max(0, width - newTabWidth - newTabPrefixWidth);
|
|
32
|
-
const naturalButtons = tabs.map((tab) => this.buttonLayout(tab));
|
|
32
|
+
const naturalButtons = tabs.map((tab) => this.buttonLayout(tab, undefined, tabs.length > 1 || tab.draft !== true));
|
|
33
33
|
const naturalWidth = naturalButtons.reduce((sum, button) => sum + stringDisplayWidth(button.text), 0)
|
|
34
34
|
+ separatorCount * separatorWidth;
|
|
35
35
|
const buttonMaxWidth = naturalWidth <= tabsWidth
|
|
36
36
|
? undefined
|
|
37
37
|
: Math.max(7, Math.floor(Math.max(1, tabsWidth - separatorCount * separatorWidth) / tabs.length));
|
|
38
|
-
const buttons = buttonMaxWidth === undefined
|
|
38
|
+
const buttons = buttonMaxWidth === undefined
|
|
39
|
+
? naturalButtons
|
|
40
|
+
: tabs.map((tab) => this.buttonLayout(tab, buttonMaxWidth, tabs.length > 1 || tab.draft !== true));
|
|
39
41
|
const segments = [];
|
|
40
42
|
const targets = [];
|
|
41
43
|
const separatorColumns = [];
|
|
@@ -61,15 +63,17 @@ export class TabLineRenderer {
|
|
|
61
63
|
const buttonWidth = stringDisplayWidth(button.text);
|
|
62
64
|
text += button.text;
|
|
63
65
|
this.addButtonSegments(tab, button, textOffset, segments);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
if (button.closable) {
|
|
67
|
+
const closeColumnOffset = stringDisplayWidth(button.text.slice(0, button.closeStart));
|
|
68
|
+
// Keep the more specific close target before the tab target because the
|
|
69
|
+
// tab target intentionally spans the whole button, including the close icon.
|
|
70
|
+
targets.push({
|
|
71
|
+
kind: "close",
|
|
72
|
+
tabId: tab.id,
|
|
73
|
+
startColumn: displayColumn + closeColumnOffset,
|
|
74
|
+
endColumn: displayColumn + closeColumnOffset + stringDisplayWidth(button.text.slice(button.closeStart, button.closeEnd)),
|
|
75
|
+
});
|
|
76
|
+
}
|
|
73
77
|
targets.push({
|
|
74
78
|
kind: "tab",
|
|
75
79
|
tabId: tab.id,
|
|
@@ -142,31 +146,33 @@ export class TabLineRenderer {
|
|
|
142
146
|
}
|
|
143
147
|
return chars.join("");
|
|
144
148
|
}
|
|
145
|
-
buttonLayout(tab, maxWidth) {
|
|
149
|
+
buttonLayout(tab, maxWidth, closable = true) {
|
|
146
150
|
const statusText = this.statusIndicatorIcon(tab);
|
|
147
151
|
const prefix = `${statusText} `;
|
|
148
|
-
const suffix = ` ${APP_ICONS.close}
|
|
152
|
+
const suffix = closable ? ` ${APP_ICONS.close}` : "";
|
|
149
153
|
const title = this.displayTitle(tab);
|
|
150
154
|
const naturalText = `${prefix}${title}${suffix}`;
|
|
151
155
|
const naturalWidth = stringDisplayWidth(naturalText);
|
|
152
156
|
if (maxWidth === undefined || naturalWidth <= maxWidth)
|
|
153
|
-
return this.buttonLayoutFromText(naturalText, 0, statusText.length);
|
|
157
|
+
return this.buttonLayoutFromText(naturalText, 0, statusText.length, closable);
|
|
154
158
|
const titleWidth = maxWidth - stringDisplayWidth(prefix) - stringDisplayWidth(suffix);
|
|
155
|
-
if (titleWidth <= 0)
|
|
156
|
-
return this.buttonLayoutFromText(ellipsizeDisplay(`${statusText}${APP_ICONS.close}`, maxWidth), 0, statusText.length);
|
|
157
|
-
|
|
159
|
+
if (titleWidth <= 0) {
|
|
160
|
+
return this.buttonLayoutFromText(ellipsizeDisplay(`${statusText}${closable ? APP_ICONS.close : ""}`, maxWidth), 0, statusText.length, closable);
|
|
161
|
+
}
|
|
162
|
+
return this.buttonLayoutFromText(`${prefix}${ellipsizeDisplay(title, titleWidth)}${suffix}`, 0, statusText.length, closable);
|
|
158
163
|
}
|
|
159
164
|
displayTitle(tab) {
|
|
160
165
|
return tab.isFork ? `${APP_ICONS.fork}${tab.title}` : tab.title;
|
|
161
166
|
}
|
|
162
|
-
buttonLayoutFromText(text, statusStart, statusLength) {
|
|
163
|
-
const closeStart = Math.max(0, text.lastIndexOf(APP_ICONS.close));
|
|
167
|
+
buttonLayoutFromText(text, statusStart, statusLength, closable) {
|
|
168
|
+
const closeStart = closable ? Math.max(0, text.lastIndexOf(APP_ICONS.close)) : text.length;
|
|
164
169
|
return {
|
|
165
170
|
text,
|
|
166
171
|
statusStart,
|
|
167
172
|
statusEnd: Math.min(text.length, statusStart + statusLength),
|
|
168
173
|
closeStart,
|
|
169
|
-
closeEnd: closeStart + APP_ICONS.close.length,
|
|
174
|
+
closeEnd: closable ? closeStart + APP_ICONS.close.length : closeStart,
|
|
175
|
+
closable,
|
|
170
176
|
};
|
|
171
177
|
}
|
|
172
178
|
addButtonSegments(tab, button, textOffset, segments) {
|
package/dist/app/runtime.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SessionManager, type EventBus, type AgentSessionRuntime, type AgentSessionServices, type LoadExtensionsResult, type SessionEntry } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { type PixConfig } from "../config.js";
|
|
3
|
-
import type { AppOptions, ThinkingLevel } from "./types.js";
|
|
3
|
+
import type { AppOptions, SessionModel, ThinkingLevel } from "./types.js";
|
|
4
4
|
export type PiToolsSuiteInstallAction = "installed" | "already-installed" | "existing-kept" | "missing-source";
|
|
5
5
|
export type PiToolsSuiteInstallResult = {
|
|
6
6
|
action: PiToolsSuiteInstallAction;
|
|
@@ -28,6 +28,7 @@ export type BundledSkillsInstallOptions = {
|
|
|
28
28
|
export declare function bundledQuestionExtensionPath(): string;
|
|
29
29
|
export declare function bundledSessionTitleExtensionPath(): string;
|
|
30
30
|
export declare function bundledTerminalBellExtensionPath(): string;
|
|
31
|
+
export declare function bundledTelegramConnectorExtensionPath(): string;
|
|
31
32
|
export declare function piToolsSuiteExtensionSourcePath(): string;
|
|
32
33
|
export declare function piToolsSuiteExtensionInstallPath(agentDir?: string): string;
|
|
33
34
|
export declare function bundledSkillsSourcePath(): string;
|
|
@@ -44,8 +45,18 @@ export type CreatePixRuntimeOptions = {
|
|
|
44
45
|
};
|
|
45
46
|
type RuntimeSessionManagerModelState = Pick<SessionManager, "getEntries" | "getBranch">;
|
|
46
47
|
export declare function resolvePixRuntimeModelRef(options: Pick<AppOptions, "modelRef">, sessionManager: RuntimeSessionManagerModelState, config?: PixConfig): string | undefined;
|
|
48
|
+
export declare function resolvePixRuntimeModelRefs(options: Pick<AppOptions, "modelRef">, sessionManager: RuntimeSessionManagerModelState, config?: PixConfig): string[];
|
|
47
49
|
export declare function resolvePixRuntimeInitialThinkingLevel(options: Pick<AppOptions, "modelRef">, sessionManager: RuntimeSessionManagerModelState, config: PixConfig): ThinkingLevel | undefined;
|
|
48
50
|
export declare function resolveSessionModelRefFromTail(entries: readonly SessionEntry[]): string | undefined;
|
|
49
51
|
export declare function refreshPixModelRuntimeForStartup(modelRuntime: Pick<AgentSessionServices["modelRuntime"], "refresh">): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Build the model catalogue used by UI-only draft tabs without allocating an
|
|
54
|
+
* AgentSession or session file. Drafts can therefore expose model selection
|
|
55
|
+
* while preserving lazy session materialization until the first prompt.
|
|
56
|
+
*/
|
|
57
|
+
export declare function createPixDraftModelCatalog(options: {
|
|
58
|
+
cwd: string;
|
|
59
|
+
agentDir?: string;
|
|
60
|
+
}): Promise<SessionModel[]>;
|
|
50
61
|
export declare function createPixRuntime(options: AppOptions, runtimeOptions?: CreatePixRuntimeOptions): Promise<AgentSessionRuntime>;
|
|
51
62
|
export {};
|
package/dist/app/runtime.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { access, cp, lstat, mkdir, readdir, readlink, realpath, rm, symlink } from "node:fs/promises";
|
|
2
|
+
import { access, cp, lstat, mkdir, readFile, readdir, readlink, realpath, rm, symlink } from "node:fs/promises";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, getAgentDir, SessionManager, } from "@earendil-works/pi-coding-agent";
|
|
6
|
+
import { createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, getAgentDir, ModelRuntime, SessionManager, } from "@earendil-works/pi-coding-agent";
|
|
7
7
|
import { loadPixConfig, resolveDefaultModelRef } from "../config.js";
|
|
8
8
|
import { isThinkingLevel, parseModelRef, parseScopedModelRef } from "./model/model-ref.js";
|
|
9
9
|
import { openLazySessionManager } from "./session/lazy-session-manager.js";
|
|
@@ -13,6 +13,7 @@ const BUNDLED_EXTENSIONS_DIR = resolve(dirname(fileURLToPath(import.meta.url)),
|
|
|
13
13
|
const BUNDLED_QUESTION_EXTENSION_DIR = resolve(BUNDLED_EXTENSIONS_DIR, BUNDLED_QUESTION_EXTENSION_NAME);
|
|
14
14
|
const BUNDLED_SESSION_TITLE_EXTENSION_DIR = resolve(BUNDLED_EXTENSIONS_DIR, "session-title");
|
|
15
15
|
const BUNDLED_TERMINAL_BELL_EXTENSION_DIR = resolve(BUNDLED_EXTENSIONS_DIR, "terminal-bell");
|
|
16
|
+
const BUNDLED_TELEGRAM_CONNECTOR_EXTENSION_DIR = resolve(BUNDLED_EXTENSIONS_DIR, "telegram-connector");
|
|
16
17
|
const PI_TOOLS_SUITE_SOURCE_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "../..", "external", PI_TOOLS_SUITE_EXTENSION_NAME);
|
|
17
18
|
const BUNDLED_SKILLS_SOURCE_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "../..", "skills");
|
|
18
19
|
export function bundledQuestionExtensionPath() {
|
|
@@ -24,6 +25,9 @@ export function bundledSessionTitleExtensionPath() {
|
|
|
24
25
|
export function bundledTerminalBellExtensionPath() {
|
|
25
26
|
return BUNDLED_TERMINAL_BELL_EXTENSION_DIR;
|
|
26
27
|
}
|
|
28
|
+
export function bundledTelegramConnectorExtensionPath() {
|
|
29
|
+
return BUNDLED_TELEGRAM_CONNECTOR_EXTENSION_DIR;
|
|
30
|
+
}
|
|
27
31
|
export function piToolsSuiteExtensionSourcePath() {
|
|
28
32
|
return PI_TOOLS_SUITE_SOURCE_DIR;
|
|
29
33
|
}
|
|
@@ -60,8 +64,22 @@ export async function ensurePiToolsSuiteExtensionInstalled(options = {}) {
|
|
|
60
64
|
if (await pathsReferToSameEntry(targetPath, sourcePath)) {
|
|
61
65
|
return { action: "already-installed", sourcePath, targetPath };
|
|
62
66
|
}
|
|
67
|
+
if (targetStat.isDirectory() && await isLegacyManagedPiToolsSuite(targetPath)) {
|
|
68
|
+
await rm(targetPath, { recursive: true, force: true });
|
|
69
|
+
await symlink(sourcePath, targetPath, extensionSymlinkType());
|
|
70
|
+
return { action: "installed", sourcePath, targetPath };
|
|
71
|
+
}
|
|
63
72
|
return { action: "existing-kept", sourcePath, targetPath };
|
|
64
73
|
}
|
|
74
|
+
async function isLegacyManagedPiToolsSuite(targetPath) {
|
|
75
|
+
try {
|
|
76
|
+
const packageJson = JSON.parse(await readFile(join(targetPath, "package.json"), "utf8"));
|
|
77
|
+
return packageJson.name === "pi-tools-suite-local";
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
65
83
|
export async function ensureBundledSkillsInstalled(options = {}) {
|
|
66
84
|
const sourcePath = resolve(options.sourcePath ?? bundledSkillsSourcePath());
|
|
67
85
|
const targetPath = resolve(options.targetPath ?? bundledSkillsInstallPath(options.homeDir));
|
|
@@ -98,6 +116,7 @@ export function getBundledExtensionPaths() {
|
|
|
98
116
|
bundledQuestionExtensionPath(),
|
|
99
117
|
bundledSessionTitleExtensionPath(),
|
|
100
118
|
bundledTerminalBellExtensionPath(),
|
|
119
|
+
bundledTelegramConnectorExtensionPath(),
|
|
101
120
|
].filter(extensionEntryExists);
|
|
102
121
|
}
|
|
103
122
|
export async function getBundledExtensionPathsAsync() {
|
|
@@ -105,6 +124,7 @@ export async function getBundledExtensionPathsAsync() {
|
|
|
105
124
|
bundledQuestionExtensionPath(),
|
|
106
125
|
bundledSessionTitleExtensionPath(),
|
|
107
126
|
bundledTerminalBellExtensionPath(),
|
|
127
|
+
bundledTelegramConnectorExtensionPath(),
|
|
108
128
|
].map(async (extensionPath) => await extensionEntryExistsAsync(extensionPath) ? extensionPath : undefined));
|
|
109
129
|
return paths.filter((path) => path !== undefined);
|
|
110
130
|
}
|
|
@@ -199,12 +219,32 @@ async function ensurePiToolsSuiteExtensionInstalledOnce(options = {}) {
|
|
|
199
219
|
return await pending;
|
|
200
220
|
}
|
|
201
221
|
export function resolvePixRuntimeModelRef(options, sessionManager, config = loadPixConfig()) {
|
|
222
|
+
return resolvePixRuntimeModelRefs(options, sessionManager, config)[0];
|
|
223
|
+
}
|
|
224
|
+
export function resolvePixRuntimeModelRefs(options, sessionManager, config = loadPixConfig()) {
|
|
202
225
|
if (options.modelRef)
|
|
203
|
-
return options.modelRef;
|
|
226
|
+
return [options.modelRef];
|
|
204
227
|
const existingEntryCount = sessionManager.getEntries().length;
|
|
205
|
-
if (existingEntryCount > 0)
|
|
206
|
-
|
|
207
|
-
|
|
228
|
+
if (existingEntryCount > 0) {
|
|
229
|
+
const sessionRef = resolveSessionModelRefFromTail(sessionManager.getBranch());
|
|
230
|
+
return sessionRef ? [sessionRef] : [];
|
|
231
|
+
}
|
|
232
|
+
const primary = resolveDefaultModelRef(config);
|
|
233
|
+
if (!primary)
|
|
234
|
+
return [];
|
|
235
|
+
const defaultThinking = config.defaultModel?.thinking;
|
|
236
|
+
const fallbacks = (config.defaultModel?.fallbackModels ?? []).map((modelRef) => {
|
|
237
|
+
const trimmed = modelRef.trim();
|
|
238
|
+
if (!trimmed || !defaultThinking)
|
|
239
|
+
return trimmed;
|
|
240
|
+
try {
|
|
241
|
+
return parseModelRef(trimmed).thinkingLevel === undefined ? `${trimmed}:${defaultThinking}` : trimmed;
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
return trimmed;
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
return [...new Set([primary, ...fallbacks].map((ref) => ref.trim()).filter(Boolean))];
|
|
208
248
|
}
|
|
209
249
|
export function resolvePixRuntimeInitialThinkingLevel(options, sessionManager, config) {
|
|
210
250
|
const effectiveModelRef = resolvePixRuntimeModelRef(options, sessionManager, config);
|
|
@@ -239,14 +279,52 @@ export async function refreshPixModelRuntimeForStartup(modelRuntime) {
|
|
|
239
279
|
// refreshes belong to explicit model-management flows and must not block boot.
|
|
240
280
|
await modelRuntime.refresh({ allowNetwork: false });
|
|
241
281
|
}
|
|
282
|
+
/**
|
|
283
|
+
* Build the model catalogue used by UI-only draft tabs without allocating an
|
|
284
|
+
* AgentSession or session file. Drafts can therefore expose model selection
|
|
285
|
+
* while preserving lazy session materialization until the first prompt.
|
|
286
|
+
*/
|
|
287
|
+
export async function createPixDraftModelCatalog(options) {
|
|
288
|
+
const agentDir = options.agentDir ?? getAgentDir();
|
|
289
|
+
await ensurePiToolsSuiteExtensionInstalledOnce({ agentDir });
|
|
290
|
+
const bundledExtensionPaths = await getBundledExtensionPathsAsync();
|
|
291
|
+
const modelRuntime = await ModelRuntime.create({
|
|
292
|
+
authPath: join(agentDir, "auth.json"),
|
|
293
|
+
modelsPath: join(agentDir, "models.json"),
|
|
294
|
+
allowModelNetwork: false,
|
|
295
|
+
refreshOnCreate: false,
|
|
296
|
+
});
|
|
297
|
+
const services = await createAgentSessionServices({
|
|
298
|
+
cwd: options.cwd,
|
|
299
|
+
agentDir,
|
|
300
|
+
modelRuntime,
|
|
301
|
+
resourceLoaderOptions: {
|
|
302
|
+
noContextFiles: true,
|
|
303
|
+
noSkills: true,
|
|
304
|
+
noPromptTemplates: true,
|
|
305
|
+
noThemes: true,
|
|
306
|
+
...(bundledExtensionPaths.length === 0 ? {} : {
|
|
307
|
+
additionalExtensionPaths: bundledExtensionPaths,
|
|
308
|
+
extensionsOverride: prioritizeBundledQuestionExtension,
|
|
309
|
+
}),
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
try {
|
|
313
|
+
return [...services.modelRuntime.getAvailableSnapshot()];
|
|
314
|
+
}
|
|
315
|
+
finally {
|
|
316
|
+
// Draft discovery needs extension provider registrations, but it does not
|
|
317
|
+
// own a session. Invalidate the temporary extension runtime so event-bus
|
|
318
|
+
// subscriptions and captured ctx/pi handles cannot outlive discovery.
|
|
319
|
+
services.resourceLoader.getExtensions().runtime.invalidate("Draft model catalog discovery completed");
|
|
320
|
+
}
|
|
321
|
+
}
|
|
242
322
|
export async function createPixRuntime(options, runtimeOptions = {}) {
|
|
243
323
|
const agentDir = getAgentDir();
|
|
244
324
|
const reusableServices = reusableRuntimeServices(runtimeOptions.reuseServicesFrom, options.cwd, agentDir);
|
|
245
325
|
const createRuntime = async ({ cwd, sessionManager, sessionStartEvent }) => {
|
|
246
326
|
const config = runtimeOptions.config ?? loadPixConfig(cwd);
|
|
247
|
-
const
|
|
248
|
-
const parsedModel = effectiveModelRef ? parseModelRef(effectiveModelRef) : undefined;
|
|
249
|
-
const initialThinkingLevel = resolvePixRuntimeInitialThinkingLevel(options, sessionManager, config);
|
|
327
|
+
const modelRefs = resolvePixRuntimeModelRefs(options, sessionManager, config);
|
|
250
328
|
// Only reuse services for the initial session. Session replacements
|
|
251
329
|
// (switchSession/newSession/fork) must get fresh services so extensions
|
|
252
330
|
// are re-loaded with a fresh pi — otherwise handlers capture the old,
|
|
@@ -261,10 +339,36 @@ export async function createPixRuntime(options, runtimeOptions = {}) {
|
|
|
261
339
|
...(runtimeOptions.eventBus === undefined ? {} : { eventBus: runtimeOptions.eventBus }),
|
|
262
340
|
});
|
|
263
341
|
await refreshPixModelRuntimeForStartup(services.modelRuntime);
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
342
|
+
let model;
|
|
343
|
+
let selectedRef;
|
|
344
|
+
let selectedThinkingLevel;
|
|
345
|
+
let primaryError;
|
|
346
|
+
for (let index = 0; index < modelRefs.length; index++) {
|
|
347
|
+
const modelRef = modelRefs[index];
|
|
348
|
+
let parsedModel;
|
|
349
|
+
try {
|
|
350
|
+
parsedModel = parseModelRef(modelRef);
|
|
351
|
+
}
|
|
352
|
+
catch (error) {
|
|
353
|
+
if (index === 0)
|
|
354
|
+
throw error;
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
const candidate = services.modelRuntime.getModel(parsedModel.provider, parsedModel.modelId);
|
|
358
|
+
if (!candidate) {
|
|
359
|
+
if (index === 0)
|
|
360
|
+
primaryError = new Error(`Model not found: ${parsedModel.provider}/${parsedModel.modelId}`);
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
model = candidate;
|
|
364
|
+
selectedRef = modelRef;
|
|
365
|
+
selectedThinkingLevel = parsedModel.thinkingLevel;
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
if (modelRefs.length > 0 && !model) {
|
|
369
|
+
throw primaryError ?? new Error(`No configured model is available: ${modelRefs.join(", ")}`);
|
|
267
370
|
}
|
|
371
|
+
const initialThinkingLevel = selectedThinkingLevel;
|
|
268
372
|
const enabledModelRefs = services.settingsManager.getEnabledModels();
|
|
269
373
|
const scopedModels = (enabledModelRefs ?? []).flatMap((modelRef) => {
|
|
270
374
|
const scoped = parseScopedModelRef(modelRef);
|
|
@@ -288,9 +392,13 @@ export async function createPixRuntime(options, runtimeOptions = {}) {
|
|
|
288
392
|
...(initialThinkingLevel === undefined ? {} : { thinkingLevel: initialThinkingLevel }),
|
|
289
393
|
...(scopedModels.length === 0 ? {} : { scopedModels }),
|
|
290
394
|
});
|
|
395
|
+
const modelFallbackMessage = selectedRef && selectedRef !== modelRefs[0]
|
|
396
|
+
? `Default model ${modelRefs[0]} is unavailable; using fallback ${selectedRef}.`
|
|
397
|
+
: undefined;
|
|
291
398
|
return {
|
|
292
399
|
...created,
|
|
293
400
|
services,
|
|
401
|
+
...(modelFallbackMessage ? { modelFallbackMessage } : {}),
|
|
294
402
|
// Snapshot diagnostics per-runtime. When services are reused across
|
|
295
403
|
// tabs (reuseServicesFrom), services.diagnostics is a shared array —
|
|
296
404
|
// giving each runtime its own copy prevents one tab's creation
|
|
@@ -4,7 +4,7 @@ import { horizontalPaddingLayout } from "../rendering/render-text.js";
|
|
|
4
4
|
import { orderedSelection, samePoint } from "./screen-selection.js";
|
|
5
5
|
import { openImageContent as openSystemImageContent } from "./image-opener.js";
|
|
6
6
|
import { sliceByDisplayColumns, stringDisplayWidth } from "../../terminal-width.js";
|
|
7
|
-
import {
|
|
7
|
+
import { loadDcpStatsToast } from "../rendering/dcp-stats.js";
|
|
8
8
|
import { detectFileLinks } from "./file-links.js";
|
|
9
9
|
import { openFileLink as openDetectedFileLink } from "./file-link-opener.js";
|
|
10
10
|
import { APP_ICONS } from "../icons.js";
|
|
@@ -133,10 +133,10 @@ export class AppMouseController {
|
|
|
133
133
|
return;
|
|
134
134
|
}
|
|
135
135
|
if (target?.kind === "popup-menu") {
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
136
|
+
const active = this.popupMenus.selectActivePopupMenuIndex(target.index);
|
|
137
|
+
if (active !== "model" || this.popupMenus.modelVisibilityModeActive()) {
|
|
138
|
+
void this.popupActions.submitActivePopupMenu();
|
|
139
|
+
}
|
|
140
140
|
this.showClickFlashForEvent(event);
|
|
141
141
|
return;
|
|
142
142
|
}
|
|
@@ -145,6 +145,11 @@ export class AppMouseController {
|
|
|
145
145
|
this.showClickFlashForEvent(event);
|
|
146
146
|
return;
|
|
147
147
|
}
|
|
148
|
+
if (target?.kind === "model-visibility-clear") {
|
|
149
|
+
this.popupActions.clearVisibleModels();
|
|
150
|
+
this.showClickFlashForEvent(event);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
148
153
|
if (target?.kind === "todo-panel") {
|
|
149
154
|
this.host.setTodoPanelExpanded(!this.host.getTodoPanelExpanded());
|
|
150
155
|
this.showClickFlashForEvent(event);
|
|
@@ -488,8 +493,13 @@ export class AppMouseController {
|
|
|
488
493
|
const session = this.host.runtimeSession();
|
|
489
494
|
if (!session)
|
|
490
495
|
return false;
|
|
491
|
-
const
|
|
492
|
-
|
|
496
|
+
const model = session.model;
|
|
497
|
+
const sessionId = session.sessionManager.getSessionId?.();
|
|
498
|
+
void loadDcpStatsToast(session).then((message) => {
|
|
499
|
+
if (this.host.runtimeSession() !== session || session.model !== model || session.sessionManager.getSessionId?.() !== sessionId)
|
|
500
|
+
return;
|
|
501
|
+
this.host.showToast(message, "info", { variant: "dialog" });
|
|
502
|
+
});
|
|
493
503
|
return true;
|
|
494
504
|
}
|
|
495
505
|
handleStatusModelUsageClick(event) {
|
|
@@ -7,6 +7,10 @@ export type AppStatusControllerHost = {
|
|
|
7
7
|
readonly theme: Theme;
|
|
8
8
|
readonly blinkController: AppBlinkController;
|
|
9
9
|
runtimeSession(): AgentSession | undefined;
|
|
10
|
+
draftModelStatus?(): {
|
|
11
|
+
modelLabel: string;
|
|
12
|
+
thinkingLabel: string;
|
|
13
|
+
} | undefined;
|
|
10
14
|
render(): void;
|
|
11
15
|
};
|
|
12
16
|
export declare class AppStatusController {
|
|
@@ -25,6 +25,11 @@ export class AppStatusController {
|
|
|
25
25
|
this.status = session ? this.formatSessionStatus(session) : "no session";
|
|
26
26
|
}
|
|
27
27
|
currentStatus() {
|
|
28
|
+
if (!this.host.runtimeSession() && (this.status === "new conversation" || this.status === "no session")) {
|
|
29
|
+
const draft = this.host.draftModelStatus?.();
|
|
30
|
+
if (draft)
|
|
31
|
+
return `${draft.modelLabel} ${APP_ICONS.lightbulb} ${draft.thinkingLabel}`;
|
|
32
|
+
}
|
|
28
33
|
if (!this.statusFollowsSession)
|
|
29
34
|
return this.status;
|
|
30
35
|
const session = this.host.runtimeSession();
|