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
|
@@ -1,439 +1,54 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const stats = collectDcpSessionStats(session, latestState, branch, manualModeBaseline);
|
|
12
|
-
const nudgeStats = collectDcpNudgeStats(branch, latestState?.data);
|
|
13
|
-
const activeBlocks = stats.activeBlocks ?? 0;
|
|
14
|
-
const totalBlocks = stats.totalBlocks ?? stats.activeBlocks ?? 0;
|
|
15
|
-
const totalNudgeEvents = nudgeStats.emitted + nudgeStats.reapplied + nudgeStats.upgraded;
|
|
16
|
-
const activeAnchors = NUDGE_TYPES.reduce((sum, type) => sum + nudgeStats.activeByType[type], 0);
|
|
17
|
-
const lines = [
|
|
18
|
-
"DCP Session Statistics:",
|
|
19
|
-
` Tokens saved (estimated): ${fmt(stats.tokensSaved)}`,
|
|
20
|
-
` Total pruning operations: ${fmt(stats.totalPruneCount)}`,
|
|
21
|
-
` Compression blocks active: ${activeBlocks} / ${totalBlocks} total`,
|
|
22
|
-
` Manual mode: ${stats.manualMode === true ? "on" : stats.manualMode === false ? "off" : "unknown"}`,
|
|
23
|
-
` State source: ${formatStateSource(stats.stateSource)}`,
|
|
24
|
-
"",
|
|
25
|
-
"Nudge telemetry:",
|
|
26
|
-
` Sent: ${fmt(nudgeStats.emitted)} emitted, ${fmt(nudgeStats.reapplied)} reapplied, ${fmt(nudgeStats.upgraded)} upgraded`,
|
|
27
|
-
` By type: ${NUDGE_TYPES.map((type) => `${type}=${fmt(nudgeStats.byType[type])}`).join(", ")}`,
|
|
28
|
-
` Active anchors: ${fmt(activeAnchors)}${activeAnchors > 0 ? ` (${NUDGE_TYPES.map((type) => `${type}=${fmt(nudgeStats.activeByType[type])}`).join(", ")})` : ""}`,
|
|
29
|
-
` Cleared after compress: ${fmt(nudgeStats.clearedEvents)} time${nudgeStats.clearedEvents === 1 ? "" : "s"} (${fmt(nudgeStats.clearedAnchors)} anchor${nudgeStats.clearedAnchors === 1 ? "" : "s"})`,
|
|
30
|
-
` Compliance proxy: ${fmt(nudgeStats.clearedEvents)} compress-after-nudge / ${fmt(totalNudgeEvents)} nudge event${totalNudgeEvents === 1 ? "" : "s"} (${pct(nudgeStats.clearedEvents, totalNudgeEvents)})`,
|
|
31
|
-
nudgeStats.last
|
|
32
|
-
? ` Last nudge: ${nudgeStats.last.type} ${nudgeStats.last.event} at ${formatDate(nudgeStats.last.createdAt)} (${formatContextPercent(nudgeStats.last.contextPercent)})`
|
|
33
|
-
: " Last nudge: none recorded",
|
|
34
|
-
"",
|
|
35
|
-
`Context: ${formatContextUsage(stats)}`,
|
|
36
|
-
];
|
|
37
|
-
return lines.join("\n");
|
|
38
|
-
}
|
|
39
|
-
function collectDcpSessionStats(session, latestState, branch, manualModeBaseline) {
|
|
40
|
-
const usage = session.getContextUsage();
|
|
41
|
-
const stats = {
|
|
42
|
-
runs: 0,
|
|
43
|
-
tokensSaved: 0,
|
|
44
|
-
totalPruneCount: 0,
|
|
45
|
-
items: 0,
|
|
46
|
-
summaryTokens: 0,
|
|
47
|
-
prunedTools: 0,
|
|
48
|
-
stateSource: latestState?.source ?? "tool-results",
|
|
49
|
-
...(latestState ? { manualMode: manualModeBaseline } : {}),
|
|
50
|
-
...(usage?.tokens != null ? { contextTokens: usage.tokens } : {}),
|
|
51
|
-
...(usage?.contextWindow != null ? { contextWindow: usage.contextWindow } : {}),
|
|
52
|
-
...(usage?.percent != null ? { contextPercent: usage.percent } : {}),
|
|
53
|
-
};
|
|
54
|
-
if (latestState)
|
|
55
|
-
applyDcpStateStats(stats, latestState.data);
|
|
56
|
-
for (const entry of branch) {
|
|
57
|
-
if (entry.type !== "message")
|
|
58
|
-
continue;
|
|
59
|
-
const message = entry.message;
|
|
60
|
-
if (message.role !== "toolResult")
|
|
61
|
-
continue;
|
|
62
|
-
if (normalizeToolName(message.toolName) !== "compress")
|
|
63
|
-
continue;
|
|
64
|
-
if ("isError" in message && message.isError === true)
|
|
65
|
-
continue;
|
|
66
|
-
const result = parseToolResultText(message.content);
|
|
67
|
-
if (!result)
|
|
68
|
-
continue;
|
|
69
|
-
if (result.idempotentReplay !== true) {
|
|
70
|
-
stats.runs += 1;
|
|
71
|
-
stats.tokensSaved += numberValue(result.tokensSaved) ?? 0;
|
|
72
|
-
stats.items += numberValue(result.itemCount) ?? sumDefined(numberValue(result.ranges), numberValue(result.messages)) ?? 0;
|
|
73
|
-
stats.summaryTokens += numberValue(result.totalSummaryTokens) ?? 0;
|
|
74
|
-
}
|
|
75
|
-
if (!latestState) {
|
|
76
|
-
stats.totalPruneCount = Math.max(stats.totalPruneCount, numberValue(result.totalPruneCount) ?? 0);
|
|
77
|
-
const prunedTools = numberValue(result.prunedTools);
|
|
78
|
-
if (prunedTools != null)
|
|
79
|
-
stats.prunedTools = prunedTools;
|
|
80
|
-
const activeBlocks = numberValue(result.activeBlocks);
|
|
81
|
-
const totalBlocks = numberValue(result.totalBlocks);
|
|
82
|
-
if (activeBlocks != null)
|
|
83
|
-
stats.activeBlocks = activeBlocks;
|
|
84
|
-
if (totalBlocks != null)
|
|
85
|
-
stats.totalBlocks = totalBlocks;
|
|
86
|
-
}
|
|
87
|
-
const contextTokens = numberValue(result.contextTokens);
|
|
88
|
-
const contextWindow = numberValue(result.contextWindow);
|
|
89
|
-
const contextPercent = numberValue(result.contextPercent);
|
|
90
|
-
if (stats.contextTokens == null && contextTokens != null)
|
|
91
|
-
stats.contextTokens = contextTokens;
|
|
92
|
-
if (stats.contextWindow == null && contextWindow != null)
|
|
93
|
-
stats.contextWindow = contextWindow;
|
|
94
|
-
if (stats.contextPercent == null && contextPercent != null)
|
|
95
|
-
stats.contextPercent = contextPercent;
|
|
96
|
-
}
|
|
97
|
-
if (latestState) {
|
|
98
|
-
// Journal state owns durable block/prune counts; successful compress tool
|
|
99
|
-
// results own operation gain because the journal intentionally stores only
|
|
100
|
-
// projection decisions rather than derived accounting snapshots.
|
|
101
|
-
stats.totalPruneCount = Math.max(stats.totalPruneCount, (stats.totalBlocks ?? 0) + stats.prunedTools);
|
|
102
|
-
}
|
|
103
|
-
return stats;
|
|
104
|
-
}
|
|
105
|
-
function applyDcpStateStats(stats, data) {
|
|
106
|
-
stats.tokensSaved = numberValue(data.tokensSaved) ?? stats.tokensSaved;
|
|
107
|
-
stats.tokensSaved += numberValue(data.prunedTokensSaved) ?? 0;
|
|
108
|
-
stats.totalPruneCount = numberValue(data.totalPruneCount) ?? stats.totalPruneCount;
|
|
109
|
-
const blocks = Array.isArray(data.compressionBlocks) ? data.compressionBlocks : undefined;
|
|
110
|
-
if (blocks) {
|
|
111
|
-
stats.totalBlocks = blocks.length;
|
|
112
|
-
stats.activeBlocks = blocks.filter((block) => isRecord(block) && block.active !== false).length;
|
|
113
|
-
}
|
|
114
|
-
if (Array.isArray(data.prunedToolIds))
|
|
115
|
-
stats.prunedTools = data.prunedToolIds.length;
|
|
116
|
-
if (typeof data.manualMode === "boolean")
|
|
117
|
-
stats.manualMode = data.manualMode;
|
|
118
|
-
}
|
|
119
|
-
function collectDcpNudgeStats(branch, latestState) {
|
|
120
|
-
const stats = {
|
|
121
|
-
emitted: 0,
|
|
122
|
-
reapplied: 0,
|
|
123
|
-
upgraded: 0,
|
|
124
|
-
clearedEvents: 0,
|
|
125
|
-
clearedAnchors: 0,
|
|
126
|
-
byType: { "turn": 0, "iteration": 0, "context-soft": 0, "context-strong": 0 },
|
|
127
|
-
activeByType: { "turn": 0, "iteration": 0, "context-soft": 0, "context-strong": 0 },
|
|
128
|
-
};
|
|
129
|
-
if (latestState)
|
|
130
|
-
applyActiveAnchorStats(stats, latestState);
|
|
131
|
-
for (const entry of branch) {
|
|
132
|
-
const data = customEntryData(entry, "dcp-nudge");
|
|
133
|
-
if (!data)
|
|
134
|
-
continue;
|
|
135
|
-
const event = data.event;
|
|
136
|
-
if ((event === "emitted" || event === "reapplied" || event === "upgraded") && isNudgeType(data.type)) {
|
|
137
|
-
if (event === "emitted")
|
|
138
|
-
stats.emitted += 1;
|
|
139
|
-
else if (event === "reapplied")
|
|
140
|
-
stats.reapplied += 1;
|
|
141
|
-
else
|
|
142
|
-
stats.upgraded += 1;
|
|
143
|
-
stats.byType[data.type] += 1;
|
|
144
|
-
const createdAt = numberValue(data.createdAt);
|
|
145
|
-
const contextPercent = typeof data.contextPercent === "number" || data.contextPercent === null ? data.contextPercent : undefined;
|
|
146
|
-
if (!stats.last || (createdAt ?? 0) >= (stats.last.createdAt ?? 0)) {
|
|
147
|
-
stats.last = {
|
|
148
|
-
type: data.type,
|
|
149
|
-
event,
|
|
150
|
-
...(createdAt !== undefined ? { createdAt } : {}),
|
|
151
|
-
...(contextPercent !== undefined ? { contextPercent } : {}),
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
else if (event === "cleared") {
|
|
156
|
-
stats.clearedEvents += 1;
|
|
157
|
-
stats.clearedAnchors += Math.max(0, numberValue(data.clearedAnchors) ?? 0);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
return stats;
|
|
161
|
-
}
|
|
162
|
-
function readDcpManualModeBaseline(session, configPath = join(homedir(), ".config", "pi", "pi-tools-suite.jsonc")) {
|
|
163
|
-
let enabled = false;
|
|
164
|
-
if (!existsSync(configPath))
|
|
165
|
-
return enabled;
|
|
1
|
+
import { formatDcpStatistics } from "../../../external/pi-tools-suite/src/dcp/statistics.js";
|
|
2
|
+
/** On-demand TUI path: read the full branch asynchronously without hydrating
|
|
3
|
+
* the lazy presentation manager or blocking terminal input. ACP supplies a
|
|
4
|
+
* complete branch directly to the synchronous formatter instead. */
|
|
5
|
+
export async function loadDcpStatsToast(session) {
|
|
6
|
+
const manager = session.sessionManager;
|
|
7
|
+
if (!manager.readFullBranchEntries)
|
|
8
|
+
return formatDcpStatsToast(session);
|
|
9
|
+
const sessionId = manager.getSessionId?.(), leafId = manager.getLeafId?.();
|
|
10
|
+
const model = session.model;
|
|
166
11
|
try {
|
|
167
|
-
const
|
|
168
|
-
if (
|
|
169
|
-
|
|
170
|
-
const dcp = parsed.dcp;
|
|
171
|
-
if (!isRecord(dcp))
|
|
172
|
-
return enabled;
|
|
173
|
-
const manualMode = dcp.manualMode;
|
|
174
|
-
if (isRecord(manualMode) && typeof manualMode.enabled === "boolean")
|
|
175
|
-
enabled = manualMode.enabled;
|
|
176
|
-
const overrides = dcp.modelOverrides;
|
|
177
|
-
if (!isRecord(overrides))
|
|
178
|
-
return enabled;
|
|
179
|
-
for (const override of matchingModelOverrides(overrides, dcpModelKeys(session))) {
|
|
180
|
-
const overrideManualMode = override.manualMode;
|
|
181
|
-
if (isRecord(overrideManualMode) && typeof overrideManualMode.enabled === "boolean") {
|
|
182
|
-
enabled = overrideManualMode.enabled;
|
|
183
|
-
}
|
|
12
|
+
const branch = await manager.readFullBranchEntries();
|
|
13
|
+
if (session.sessionManager !== manager || manager.getSessionId?.() !== sessionId || manager.getLeafId?.() !== leafId || session.model !== model) {
|
|
14
|
+
throw new Error("Statistics owner changed during read");
|
|
184
15
|
}
|
|
16
|
+
return formatDcpStatsToast(session, { branch });
|
|
185
17
|
}
|
|
186
18
|
catch {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
function dcpModelKeys(session) {
|
|
192
|
-
const model = session.model;
|
|
193
|
-
const provider = normalizeModelKey(model?.provider);
|
|
194
|
-
const id = normalizeModelKey(model?.id);
|
|
195
|
-
return [provider && id ? `${provider}/${id}` : undefined, id].filter((key) => key !== undefined);
|
|
196
|
-
}
|
|
197
|
-
function matchingModelOverrides(overrides, modelKeys) {
|
|
198
|
-
const candidates = modelKeys.map(normalizeModelKey).filter((key, index, all) => key !== undefined && all.indexOf(key) === index);
|
|
199
|
-
const exact = new Map();
|
|
200
|
-
const wildcard = [];
|
|
201
|
-
for (const [rawKey, value] of Object.entries(overrides)) {
|
|
202
|
-
const key = normalizeModelKey(rawKey);
|
|
203
|
-
if (!key || !isRecord(value))
|
|
204
|
-
continue;
|
|
205
|
-
if (key.includes("*") || key.includes("?"))
|
|
206
|
-
wildcard.push([key, value]);
|
|
207
|
-
else
|
|
208
|
-
exact.set(key, value);
|
|
209
|
-
}
|
|
210
|
-
const bare = candidates.filter((candidate) => !candidate.includes("/"));
|
|
211
|
-
const full = candidates.filter((candidate) => candidate.includes("/"));
|
|
212
|
-
const matches = [];
|
|
213
|
-
for (const candidate of bare)
|
|
214
|
-
for (const [pattern, value] of wildcard)
|
|
215
|
-
if (!pattern.includes("/") && globMatches(pattern, candidate))
|
|
216
|
-
matches.push(value);
|
|
217
|
-
for (const candidate of bare) {
|
|
218
|
-
const value = exact.get(candidate);
|
|
219
|
-
if (value)
|
|
220
|
-
matches.push(value);
|
|
221
|
-
}
|
|
222
|
-
for (const candidate of full)
|
|
223
|
-
for (const [pattern, value] of wildcard)
|
|
224
|
-
if (pattern.includes("/") && globMatches(pattern, candidate))
|
|
225
|
-
matches.push(value);
|
|
226
|
-
for (const candidate of full) {
|
|
227
|
-
const value = exact.get(candidate);
|
|
228
|
-
if (value)
|
|
229
|
-
matches.push(value);
|
|
230
|
-
}
|
|
231
|
-
return matches;
|
|
232
|
-
}
|
|
233
|
-
function normalizeModelKey(value) {
|
|
234
|
-
if (typeof value !== "string")
|
|
235
|
-
return undefined;
|
|
236
|
-
const trimmed = value.trim();
|
|
237
|
-
return trimmed || undefined;
|
|
238
|
-
}
|
|
239
|
-
function globMatches(pattern, candidate) {
|
|
240
|
-
let source = "^";
|
|
241
|
-
for (const char of pattern) {
|
|
242
|
-
if (char === "*")
|
|
243
|
-
source += ".*";
|
|
244
|
-
else if (char === "?")
|
|
245
|
-
source += ".";
|
|
246
|
-
else
|
|
247
|
-
source += char.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
|
|
248
|
-
}
|
|
249
|
-
return new RegExp(`${source}$`).test(candidate);
|
|
250
|
-
}
|
|
251
|
-
function applyActiveAnchorStats(stats, data) {
|
|
252
|
-
stats.activeByType = { "turn": 0, "iteration": 0, "context-soft": 0, "context-strong": 0 };
|
|
253
|
-
const anchors = Array.isArray(data.nudgeAnchors) ? data.nudgeAnchors : [];
|
|
254
|
-
for (const anchor of anchors) {
|
|
255
|
-
if (isRecord(anchor) && isNudgeType(anchor.type))
|
|
256
|
-
stats.activeByType[anchor.type] += 1;
|
|
257
|
-
}
|
|
258
|
-
const last = isRecord(data.lastNudge) ? data.lastNudge : undefined;
|
|
259
|
-
if (last && isNudgeType(last.type) && !stats.last) {
|
|
260
|
-
const contextPercent = numberValue(last.contextPercent);
|
|
261
|
-
const createdAt = numberValue(last.createdAt);
|
|
262
|
-
stats.last = {
|
|
263
|
-
type: last.type,
|
|
264
|
-
event: "emitted",
|
|
265
|
-
...(createdAt !== undefined ? { createdAt } : {}),
|
|
266
|
-
...(contextPercent !== undefined ? { contextPercent: contextPercent * 100 } : {}),
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
function resolveJournalDcpState(branch) {
|
|
271
|
-
const blocks = new Map();
|
|
272
|
-
const pruned = new Map();
|
|
273
|
-
let initialized = false;
|
|
274
|
-
let manualMode;
|
|
275
|
-
let nudgeAnchors = [];
|
|
276
|
-
let lastNudge;
|
|
277
|
-
for (const entry of branch) {
|
|
278
|
-
const operation = customEntryData(entry, "dcp-journal");
|
|
279
|
-
if (!operation || operation.schemaVersion !== 1)
|
|
280
|
-
continue;
|
|
281
|
-
if (operation.kind === "init") {
|
|
282
|
-
initialized = true;
|
|
283
|
-
continue;
|
|
284
|
-
}
|
|
285
|
-
if (!initialized || operation.kind !== "delta")
|
|
286
|
-
continue;
|
|
287
|
-
if (Array.isArray(operation.blocks)) {
|
|
288
|
-
for (const raw of operation.blocks) {
|
|
289
|
-
if (!isRecord(raw) || typeof raw.id !== "number")
|
|
290
|
-
continue;
|
|
291
|
-
blocks.set(raw.id, { ...raw });
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
if (Array.isArray(operation.blockStates)) {
|
|
295
|
-
for (const raw of operation.blockStates) {
|
|
296
|
-
if (!isRecord(raw) || typeof raw.id !== "number")
|
|
297
|
-
continue;
|
|
298
|
-
const block = blocks.get(raw.id);
|
|
299
|
-
if (!block)
|
|
300
|
-
continue;
|
|
301
|
-
if (typeof raw.active === "boolean")
|
|
302
|
-
block.active = raw.active;
|
|
303
|
-
if (typeof raw.deactivatedReason === "string")
|
|
304
|
-
block.deactivatedReason = raw.deactivatedReason;
|
|
305
|
-
}
|
|
19
|
+
let usage;
|
|
20
|
+
try {
|
|
21
|
+
usage = session.getContextUsage();
|
|
306
22
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
if (!isRecord(raw) || typeof raw.toolCallId !== "string")
|
|
310
|
-
continue;
|
|
311
|
-
pruned.set(raw.toolCallId, {
|
|
312
|
-
...(typeof raw.reason === "string" ? { reason: raw.reason } : {}),
|
|
313
|
-
tokenEstimate: Math.max(0, numberValue(raw.tokenEstimate) ?? 0),
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
if (typeof operation.manualMode === "boolean")
|
|
318
|
-
manualMode = operation.manualMode;
|
|
319
|
-
if (Array.isArray(operation.nudgeAnchors))
|
|
320
|
-
nudgeAnchors = operation.nudgeAnchors;
|
|
321
|
-
if (Object.prototype.hasOwnProperty.call(operation, "lastNudge"))
|
|
322
|
-
lastNudge = operation.lastNudge ?? undefined;
|
|
23
|
+
catch { /* Unknown. */ }
|
|
24
|
+
return formatDcpStatistics({ model: session.model, usage, historyStatus: "unavailable" });
|
|
323
25
|
}
|
|
324
|
-
if (!initialized)
|
|
325
|
-
return undefined;
|
|
326
|
-
return {
|
|
327
|
-
source: "journal",
|
|
328
|
-
data: {
|
|
329
|
-
compressionBlocks: [...blocks.values()],
|
|
330
|
-
prunedToolIds: [...pruned.keys()],
|
|
331
|
-
prunedTokensSaved: [...pruned.values()].reduce((sum, item) => sum + item.tokenEstimate, 0),
|
|
332
|
-
manualMode,
|
|
333
|
-
nudgeAnchors,
|
|
334
|
-
lastNudge,
|
|
335
|
-
},
|
|
336
|
-
};
|
|
337
26
|
}
|
|
338
|
-
|
|
339
|
-
|
|
27
|
+
/** TUI and ACP use the same passive report as /dcp stats. Opening this view
|
|
28
|
+
* never re-runs pruning, mutates history, or launches a model. */
|
|
29
|
+
export function formatDcpStatsToast(session, options = {}) {
|
|
30
|
+
let branch = [];
|
|
31
|
+
let historyStatus = "full";
|
|
340
32
|
try {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
33
|
+
if (options.branch)
|
|
34
|
+
branch = options.branch;
|
|
35
|
+
else {
|
|
36
|
+
const manager = session.sessionManager;
|
|
37
|
+
branch = manager.readFullBranchEntriesSync ? manager.readFullBranchEntriesSync() : manager.getBranch();
|
|
38
|
+
}
|
|
39
|
+
if (!Array.isArray(branch) || branch[0]?.parentId != null) {
|
|
40
|
+
historyStatus = "unavailable";
|
|
41
|
+
branch = [];
|
|
42
|
+
}
|
|
344
43
|
}
|
|
345
44
|
catch {
|
|
346
|
-
//
|
|
45
|
+
// A broken full reader is not permission to substitute the UI's lazy tail.
|
|
46
|
+
historyStatus = "unavailable";
|
|
347
47
|
}
|
|
48
|
+
let usage;
|
|
348
49
|
try {
|
|
349
|
-
|
|
350
|
-
return Array.isArray(branch) ? branch : [];
|
|
351
|
-
}
|
|
352
|
-
catch {
|
|
353
|
-
return [];
|
|
50
|
+
usage = session.getContextUsage();
|
|
354
51
|
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
if (source === "journal")
|
|
358
|
-
return "session journal";
|
|
359
|
-
return "compress tool results";
|
|
360
|
-
}
|
|
361
|
-
function customEntryData(entry, customType) {
|
|
362
|
-
if (!isRecord(entry) || entry.type !== "custom" || entry.customType !== customType)
|
|
363
|
-
return undefined;
|
|
364
|
-
return isRecord(entry.data) ? entry.data : undefined;
|
|
365
|
-
}
|
|
366
|
-
function parseToolResultText(content) {
|
|
367
|
-
const parsed = parseArgsText(textContent(content));
|
|
368
|
-
return isRecord(parsed) ? parsed : undefined;
|
|
369
|
-
}
|
|
370
|
-
function textContent(content) {
|
|
371
|
-
if (typeof content === "string")
|
|
372
|
-
return content;
|
|
373
|
-
if (!Array.isArray(content))
|
|
374
|
-
return "";
|
|
375
|
-
return content
|
|
376
|
-
.map((part) => isRecord(part) && typeof part.text === "string" ? part.text : "")
|
|
377
|
-
.filter(Boolean)
|
|
378
|
-
.join("\n");
|
|
379
|
-
}
|
|
380
|
-
function formatContextUsage(stats) {
|
|
381
|
-
const percent = stats.contextPercent;
|
|
382
|
-
const tokens = stats.contextTokens;
|
|
383
|
-
const window = stats.contextWindow;
|
|
384
|
-
const percentText = percent != null ? formatPercent(percent) : "unknown";
|
|
385
|
-
if (tokens != null && window != null)
|
|
386
|
-
return `${percentText} (${formatCompactNumber(tokens)}/${formatCompactNumber(window)})`;
|
|
387
|
-
if (tokens != null)
|
|
388
|
-
return `${formatCompactNumber(tokens)} tokens`;
|
|
389
|
-
if (window != null)
|
|
390
|
-
return `${percentText} of ${formatCompactNumber(window)}`;
|
|
391
|
-
return percentText;
|
|
392
|
-
}
|
|
393
|
-
function isNudgeType(value) {
|
|
394
|
-
return typeof value === "string" && NUDGE_TYPES.includes(value);
|
|
395
|
-
}
|
|
396
|
-
function fmt(n) {
|
|
397
|
-
return Math.round(n).toLocaleString();
|
|
398
|
-
}
|
|
399
|
-
function pct(numerator, denominator) {
|
|
400
|
-
if (denominator <= 0)
|
|
401
|
-
return "n/a";
|
|
402
|
-
return `${((numerator / denominator) * 100).toFixed(1)}%`;
|
|
403
|
-
}
|
|
404
|
-
function formatDate(ts) {
|
|
405
|
-
if (typeof ts !== "number" || !Number.isFinite(ts) || ts <= 0)
|
|
406
|
-
return "unknown time";
|
|
407
|
-
return new Date(ts).toLocaleString();
|
|
408
|
-
}
|
|
409
|
-
function formatContextPercent(value) {
|
|
410
|
-
if (value === null)
|
|
411
|
-
return "unknown context";
|
|
412
|
-
if (typeof value !== "number" || !Number.isFinite(value))
|
|
413
|
-
return "unknown context";
|
|
414
|
-
return `${value.toFixed(1)}% context`;
|
|
415
|
-
}
|
|
416
|
-
function numberValue(value) {
|
|
417
|
-
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
418
|
-
}
|
|
419
|
-
function sumDefined(...values) {
|
|
420
|
-
const defined = values.filter((value) => value != null);
|
|
421
|
-
return defined.length > 0 ? defined.reduce((sum, value) => sum + value, 0) : undefined;
|
|
422
|
-
}
|
|
423
|
-
function formatCompactNumber(value) {
|
|
424
|
-
const abs = Math.abs(value);
|
|
425
|
-
if (abs >= 1_000_000)
|
|
426
|
-
return `${trimDecimal(value / 1_000_000)}M`;
|
|
427
|
-
if (abs >= 1_000)
|
|
428
|
-
return `${trimDecimal(value / 1_000)}K`;
|
|
429
|
-
return Math.round(value).toLocaleString();
|
|
430
|
-
}
|
|
431
|
-
function formatPercent(value) {
|
|
432
|
-
return `${trimDecimal(value)}%`;
|
|
433
|
-
}
|
|
434
|
-
function trimDecimal(value) {
|
|
435
|
-
return value.toFixed(1).replace(/\.0$/, "");
|
|
436
|
-
}
|
|
437
|
-
function isRecord(value) {
|
|
438
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
52
|
+
catch { /* Explicitly unknown. */ }
|
|
53
|
+
return formatDcpStatistics({ branch, historyStatus, model: session.model, usage });
|
|
439
54
|
}
|
|
@@ -2,7 +2,7 @@ import { type Theme } from "../../theme.js";
|
|
|
2
2
|
import { type ModelColorsConfig } from "../../config.js";
|
|
3
3
|
import type { PopupMenu } from "../../ui.js";
|
|
4
4
|
import type { ScreenStyler } from "../screen/screen-styler.js";
|
|
5
|
-
import type { Entry, ModelMenuValue, PixMenuItem, PixMenuOptions, QueueMessageMenuValue, RenderedLine, ResumeMenuValue, SlashCommand,
|
|
5
|
+
import type { Entry, ModelMenuValue, ModelThinkingMenuState, PixMenuItem, PixMenuOptions, QueueMessageMenuValue, RenderedLine, ResumeMenuValue, SlashCommand, UserMessageJumpMenuValue, UserMessageMenuValue } from "../types.js";
|
|
6
6
|
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
7
7
|
export type PopupMenuRendererHost = {
|
|
8
8
|
readonly theme: Theme;
|
|
@@ -24,12 +24,12 @@ export declare class PopupMenuRenderer {
|
|
|
24
24
|
overlayPlainText(line: RenderedLine, width: number): string;
|
|
25
25
|
renderUserMessageMenu(width: number, menu: PopupMenu<UserMessageMenuValue>): RenderedLine[];
|
|
26
26
|
renderSlashCommandMenu(width: number, menu: PopupMenu<SlashCommand>): RenderedLine[];
|
|
27
|
-
renderModelMenu(width: number, menu: PopupMenu<ModelMenuValue
|
|
28
|
-
renderThinkingMenu(width: number, menu: PopupMenu<ThinkingMenuValue>): RenderedLine[];
|
|
27
|
+
renderModelMenu(width: number, menu: PopupMenu<ModelMenuValue>, state: ModelThinkingMenuState): RenderedLine[];
|
|
29
28
|
renderResumeMenu(width: number, menu: PopupMenu<ResumeMenuValue>, state: {
|
|
30
29
|
directQuery: string;
|
|
31
30
|
allSessionsLoaded: boolean;
|
|
32
31
|
loadedSessionCount: number;
|
|
32
|
+
draft: boolean;
|
|
33
33
|
}): RenderedLine[];
|
|
34
34
|
renderUserMessageJumpMenu(width: number, menu: PopupMenu<UserMessageJumpMenuValue>, directQuery: string): RenderedLine[];
|
|
35
35
|
renderQueueMessageMenu(width: number, menu: PopupMenu<QueueMessageMenuValue>): RenderedLine[];
|
|
@@ -40,10 +40,8 @@ export declare class PopupMenuRenderer {
|
|
|
40
40
|
private labelDescriptionText;
|
|
41
41
|
private userMessageActionForeground;
|
|
42
42
|
private selectableItemVariant;
|
|
43
|
-
private thinkingMenuItemSegments;
|
|
44
43
|
private modelMenuItemSegments;
|
|
45
44
|
private modelMenuItemColor;
|
|
46
|
-
private availableThinkingLevels;
|
|
47
45
|
private queueMessageItemVariant;
|
|
48
46
|
private sdkItemVariant;
|
|
49
47
|
private sdkMenuItemSegments;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { colorLine } from "../../theme.js";
|
|
2
2
|
import { stringDisplayWidth } from "../../terminal-width.js";
|
|
3
3
|
import { resolveColor, resolveModelColor } from "../../config.js";
|
|
4
|
-
import { SLASH_COMMAND_DESCRIPTION_COLUMN,
|
|
4
|
+
import { SLASH_COMMAND_DESCRIPTION_COLUMN, } from "../constants.js";
|
|
5
5
|
import { APP_ICONS } from "../icons.js";
|
|
6
6
|
import { ellipsizeDisplay, padOrTrimPlain, sanitizeText } from "./render-text.js";
|
|
7
7
|
import { modelProviderThemeColor, thinkingLevelThemeColor } from "./status-line-renderer.js";
|
|
@@ -96,8 +96,8 @@ export class PopupMenuRenderer {
|
|
|
96
96
|
}
|
|
97
97
|
return lines;
|
|
98
98
|
}
|
|
99
|
-
renderModelMenu(width, menu) {
|
|
100
|
-
const lines = [this.popupMenuHeader("Select model", width)];
|
|
99
|
+
renderModelMenu(width, menu, state) {
|
|
100
|
+
const lines = [this.popupMenuHeader(state.visibilityMode ? "Manage visible models" : "Select model & thinking", width)];
|
|
101
101
|
const visibleItems = menu.visibleItems();
|
|
102
102
|
if (!this.hasPopupActionItems(menu.items)) {
|
|
103
103
|
lines.push({
|
|
@@ -106,37 +106,55 @@ export class PopupMenuRenderer {
|
|
|
106
106
|
});
|
|
107
107
|
}
|
|
108
108
|
for (const item of visibleItems) {
|
|
109
|
-
const
|
|
110
|
-
const
|
|
109
|
+
const selectionMarker = item.selected ? "▶ " : " ";
|
|
110
|
+
const visibilityMarker = state.visibilityMode ? `${item.value.visible === false ? "○" : APP_ICONS.check} ` : "";
|
|
111
|
+
const marker = `${selectionMarker}${visibilityMarker}`;
|
|
112
|
+
const text = `${marker}${this.labelDescriptionText(item.label, item.description, width - marker.length)}`;
|
|
111
113
|
lines.push({
|
|
112
114
|
text,
|
|
113
115
|
variant: this.selectableItemVariant(item.value),
|
|
114
|
-
segments: [
|
|
116
|
+
segments: [
|
|
117
|
+
...this.modelMenuItemSegments(item.value, marker.length),
|
|
118
|
+
...this.itemHighlightSegments(item, text, marker.length),
|
|
119
|
+
],
|
|
115
120
|
target: { kind: "popup-menu", index: item.index },
|
|
116
121
|
});
|
|
117
122
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
if (state.visibilityMode && menu.selectedItem()) {
|
|
124
|
+
const selected = menu.selectedItem().value;
|
|
125
|
+
lines.push({
|
|
126
|
+
text: selected.current
|
|
127
|
+
? " Current model stays visible until another model is selected"
|
|
128
|
+
: " Enter toggles whether this model appears in model pickers",
|
|
129
|
+
variant: "muted",
|
|
130
|
+
});
|
|
131
|
+
lines.push({
|
|
132
|
+
text: " Clear all",
|
|
133
|
+
variant: "error",
|
|
134
|
+
target: { kind: "model-visibility-clear" },
|
|
135
|
+
});
|
|
136
|
+
lines.push({ text: " ↑/↓ model · Enter show/hide · Shift+Tab select", variant: "muted" });
|
|
125
137
|
}
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
const
|
|
138
|
+
else if (menu.selectedItem()) {
|
|
139
|
+
const thinkingPrefix = " Thinking ← ";
|
|
140
|
+
const thinkingSuffix = " →";
|
|
129
141
|
lines.push({
|
|
130
|
-
text
|
|
131
|
-
variant:
|
|
132
|
-
segments:
|
|
133
|
-
|
|
142
|
+
text: `${thinkingPrefix}${state.thinkingLevel}${thinkingSuffix}`,
|
|
143
|
+
variant: "normal",
|
|
144
|
+
segments: [{
|
|
145
|
+
start: thinkingPrefix.length,
|
|
146
|
+
end: thinkingPrefix.length + state.thinkingLevel.length,
|
|
147
|
+
foreground: thinkingLevelThemeColor(state.thinkingLevel, this.host.theme.colors, state.availableThinkingLevels),
|
|
148
|
+
bold: true,
|
|
149
|
+
}],
|
|
134
150
|
});
|
|
151
|
+
lines.push({ text: " ↑/↓ model · ←/→ thinking · Enter apply · Shift+Tab manage", variant: "muted" });
|
|
135
152
|
}
|
|
136
153
|
return lines;
|
|
137
154
|
}
|
|
138
155
|
renderResumeMenu(width, menu, state) {
|
|
139
|
-
const
|
|
156
|
+
const titleBase = state.draft ? "Open a conversation" : "Resume session";
|
|
157
|
+
const title = this.host.resumeLoading ? `${titleBase} ${APP_ICONS.timerSand}` : titleBase;
|
|
140
158
|
const lines = [this.popupMenuHeader(title, width)];
|
|
141
159
|
const visibleItems = menu.visibleItems();
|
|
142
160
|
if (!this.host.resumeLoading && !this.hasPopupActionItems(menu.items)) {
|
|
@@ -261,16 +279,7 @@ export class PopupMenuRenderer {
|
|
|
261
279
|
selectableItemVariant(value) {
|
|
262
280
|
return value.current ? "muted" : "normal";
|
|
263
281
|
}
|
|
264
|
-
|
|
265
|
-
const markerOffset = 2; // "▶ " or " "
|
|
266
|
-
return [{
|
|
267
|
-
start: markerOffset,
|
|
268
|
-
end: markerOffset + value.level.length,
|
|
269
|
-
foreground: thinkingLevelThemeColor(value.level, this.host.theme.colors, this.availableThinkingLevels()),
|
|
270
|
-
}];
|
|
271
|
-
}
|
|
272
|
-
modelMenuItemSegments(value) {
|
|
273
|
-
const markerOffset = 2; // "▶ " or " "
|
|
282
|
+
modelMenuItemSegments(value, markerOffset = 2) {
|
|
274
283
|
return [{
|
|
275
284
|
start: markerOffset,
|
|
276
285
|
end: markerOffset + value.ref.length,
|
|
@@ -285,10 +294,6 @@ export class PopupMenuRenderer {
|
|
|
285
294
|
? resolveColor(configuredColor, this.host.theme.colors)
|
|
286
295
|
: modelProviderThemeColor(value.model.provider, this.host.theme.colors);
|
|
287
296
|
}
|
|
288
|
-
availableThinkingLevels() {
|
|
289
|
-
const levels = this.host.session?.getAvailableThinkingLevels();
|
|
290
|
-
return Array.isArray(levels) && levels.length > 0 ? levels.map(String) : [...THINKING_LEVELS];
|
|
291
|
-
}
|
|
292
297
|
queueMessageItemVariant(value) {
|
|
293
298
|
return value === "cancel" ? "error" : "normal";
|
|
294
299
|
}
|
|
@@ -301,8 +306,8 @@ export class PopupMenuRenderer {
|
|
|
301
306
|
...this.descriptionHighlightSegments(item.description, item.descriptionHighlightRanges ?? item.value.descriptionHighlightRanges ?? [], text),
|
|
302
307
|
];
|
|
303
308
|
}
|
|
304
|
-
itemHighlightSegments(item, text) {
|
|
305
|
-
return this.highlightSegments(item.labelHighlightRanges ?? [], text,
|
|
309
|
+
itemHighlightSegments(item, text, markerOffset = 2) {
|
|
310
|
+
return this.highlightSegments(item.labelHighlightRanges ?? [], text, markerOffset);
|
|
306
311
|
}
|
|
307
312
|
highlightSegments(ranges, text, markerOffset) {
|
|
308
313
|
if (ranges.length === 0)
|