pi-ui-extend 1.0.41 → 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.
Files changed (236) hide show
  1. package/README.md +50 -13
  2. package/dist/app/app.d.ts +13 -0
  3. package/dist/app/app.js +158 -8
  4. package/dist/app/cli/install.js +2 -2
  5. package/dist/app/commands/command-controller.d.ts +4 -0
  6. package/dist/app/commands/command-controller.js +11 -0
  7. package/dist/app/commands/command-git-actions.d.ts +25 -0
  8. package/dist/app/commands/command-git-actions.js +381 -0
  9. package/dist/app/commands/command-host.d.ts +4 -0
  10. package/dist/app/commands/command-host.js +5 -2
  11. package/dist/app/commands/command-model-actions.d.ts +1 -0
  12. package/dist/app/commands/command-model-actions.js +57 -35
  13. package/dist/app/commands/command-navigation-actions.d.ts +1 -0
  14. package/dist/app/commands/command-navigation-actions.js +16 -3
  15. package/dist/app/commands/command-registry.d.ts +2 -0
  16. package/dist/app/commands/command-registry.js +15 -1
  17. package/dist/app/commands/command-session-actions.js +9 -3
  18. package/dist/app/commands/reload-context-inventory.d.ts +11 -0
  19. package/dist/app/commands/reload-context-inventory.js +70 -0
  20. package/dist/app/extensions/extension-actions-controller.d.ts +1 -0
  21. package/dist/app/extensions/extension-actions-controller.js +6 -0
  22. package/dist/app/extensions/subagent-catalog-state.d.ts +9 -0
  23. package/dist/app/extensions/subagent-catalog-state.js +23 -0
  24. package/dist/app/input/autocomplete-controller.js +61 -34
  25. package/dist/app/input/input-action-controller.d.ts +2 -0
  26. package/dist/app/input/input-action-controller.js +8 -1
  27. package/dist/app/input/input-controller.d.ts +2 -1
  28. package/dist/app/input/input-controller.js +7 -2
  29. package/dist/app/input/prompt-enhancer-controller.js +53 -35
  30. package/dist/app/input/voice-controller.d.ts +41 -45
  31. package/dist/app/input/voice-controller.js +351 -419
  32. package/dist/app/model/model-usage-controller.js +17 -7
  33. package/dist/app/model/model-usage-status.d.ts +4 -1
  34. package/dist/app/model/model-usage-status.js +270 -46
  35. package/dist/app/popup/menu-items-controller.d.ts +13 -3
  36. package/dist/app/popup/menu-items-controller.js +37 -21
  37. package/dist/app/popup/popup-action-controller.d.ts +12 -2
  38. package/dist/app/popup/popup-action-controller.js +77 -24
  39. package/dist/app/popup/popup-menu-controller.d.ts +36 -14
  40. package/dist/app/popup/popup-menu-controller.js +239 -69
  41. package/dist/app/rendering/dcp-stats.d.ts +9 -4
  42. package/dist/app/rendering/dcp-stats.js +40 -425
  43. package/dist/app/rendering/editor-panels.js +10 -4
  44. package/dist/app/rendering/popup-menu-renderer.d.ts +3 -5
  45. package/dist/app/rendering/popup-menu-renderer.js +42 -37
  46. package/dist/app/rendering/render-controller.js +23 -2
  47. package/dist/app/rendering/status-line-renderer.d.ts +5 -0
  48. package/dist/app/rendering/status-line-renderer.js +41 -5
  49. package/dist/app/rendering/tab-line-renderer.js +26 -20
  50. package/dist/app/runtime.d.ts +12 -1
  51. package/dist/app/runtime.js +120 -12
  52. package/dist/app/screen/mouse-controller.d.ts +2 -0
  53. package/dist/app/screen/mouse-controller.js +17 -7
  54. package/dist/app/screen/status-controller.d.ts +4 -0
  55. package/dist/app/screen/status-controller.js +5 -0
  56. package/dist/app/session/lazy-session-manager.js +34 -0
  57. package/dist/app/session/session-event-controller.d.ts +1 -0
  58. package/dist/app/session/session-event-controller.js +10 -1
  59. package/dist/app/session/session-history.d.ts +1 -0
  60. package/dist/app/session/session-history.js +12 -1
  61. package/dist/app/session/session-lifecycle-controller.d.ts +7 -1
  62. package/dist/app/session/session-lifecycle-controller.js +16 -1
  63. package/dist/app/session/tabs-controller.d.ts +26 -1
  64. package/dist/app/session/tabs-controller.js +454 -147
  65. package/dist/app/subagents/subagents-files.js +60 -1
  66. package/dist/app/subagents/subagents-model.d.ts +1 -0
  67. package/dist/app/subagents/subagents-model.js +18 -2
  68. package/dist/app/subagents/subagents-widget-controller.d.ts +1 -0
  69. package/dist/app/subagents/subagents-widget-controller.js +6 -0
  70. package/dist/app/types.d.ts +16 -1
  71. package/dist/app/workspace/workspace-actions-controller.js +10 -2
  72. package/dist/app/workspace/workspace-undo.d.ts +1 -0
  73. package/dist/app/workspace/workspace-undo.js +1 -0
  74. package/dist/bundled-extensions/question/index.js +9 -1
  75. package/dist/bundled-extensions/question/remote.d.ts +4 -0
  76. package/dist/bundled-extensions/question/remote.js +33 -0
  77. package/dist/bundled-extensions/telegram-connector/bot.d.ts +43 -0
  78. package/dist/bundled-extensions/telegram-connector/bot.js +166 -0
  79. package/dist/bundled-extensions/telegram-connector/config.d.ts +8 -0
  80. package/dist/bundled-extensions/telegram-connector/config.js +87 -0
  81. package/dist/bundled-extensions/telegram-connector/coordinator.d.ts +66 -0
  82. package/dist/bundled-extensions/telegram-connector/coordinator.js +413 -0
  83. package/dist/bundled-extensions/telegram-connector/index.d.ts +3 -0
  84. package/dist/bundled-extensions/telegram-connector/index.js +195 -0
  85. package/dist/bundled-extensions/terminal-bell/index.d.ts +0 -8
  86. package/dist/bundled-extensions/terminal-bell/index.js +0 -76
  87. package/dist/bundled-extensions/workspace-undo/index.d.ts +26 -0
  88. package/dist/bundled-extensions/workspace-undo/index.js +191 -0
  89. package/dist/config.d.ts +21 -2
  90. package/dist/config.js +159 -32
  91. package/dist/default-pix-config.js +25 -5
  92. package/dist/schemas/index.d.ts +1 -0
  93. package/dist/schemas/index.js +1 -0
  94. package/dist/schemas/pi-tools-suite-schema.d.ts +88 -62
  95. package/dist/schemas/pi-tools-suite-schema.js +54 -83
  96. package/dist/schemas/pix-schema.d.ts +19 -2
  97. package/dist/schemas/pix-schema.js +47 -5
  98. package/dist/schemas/tasks-schema.d.ts +18 -0
  99. package/dist/schemas/tasks-schema.js +41 -0
  100. package/docs/concurrency.md +9 -1
  101. package/docs/desktop-mvp.md +22 -6
  102. package/docs/desktop-task-manager.md +148 -84
  103. package/docs/release.md +30 -6
  104. package/external/pi-tools-suite/README.md +197 -84
  105. package/external/pi-tools-suite/docs/evals.md +1 -1
  106. package/external/pi-tools-suite/docs/session-recovery.md +47 -14
  107. package/external/pi-tools-suite/docs/subagent-model-pools.md +48 -30
  108. package/external/pi-tools-suite/docs/ui-qa-subagent.md +440 -0
  109. package/external/pi-tools-suite/package.json +6 -1
  110. package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +2 -1
  111. package/external/pi-tools-suite/src/antigravity-auth/constants.ts +9 -3
  112. package/external/pi-tools-suite/src/antigravity-auth/headers.ts +41 -3
  113. package/external/pi-tools-suite/src/antigravity-auth/models.ts +94 -21
  114. package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +5 -17
  115. package/external/pi-tools-suite/src/antigravity-auth/payload.ts +72 -7
  116. package/external/pi-tools-suite/src/antigravity-auth/stream.ts +13 -1
  117. package/external/pi-tools-suite/src/async-subagents/agents/frontier-review.md +23 -0
  118. package/external/pi-tools-suite/src/async-subagents/agents/implement.md +1 -1
  119. package/external/pi-tools-suite/src/async-subagents/agents/presets.jsonc +16 -0
  120. package/external/pi-tools-suite/src/async-subagents/agents/research.md +5 -3
  121. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/browser.mjs +346 -0
  122. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/desktop.mjs +1038 -0
  123. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/tui.mjs +759 -0
  124. package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/scripts/browser-qa-runner.mjs +32 -23
  125. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/chrome-devtools/chrome-devtools-provider.mjs +895 -0
  126. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/linux/linux-atspi.py +494 -0
  127. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/macos/macos-accessibility.swift +1085 -0
  128. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/native-terminal/bridge-client.mjs +50 -0
  129. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/native-terminal/native-terminal-host.mjs +501 -0
  130. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/windows/windows-uia.ps1 +454 -0
  131. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-auth.md +114 -0
  132. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-chrome-devtools.md +91 -0
  133. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-playwright.md +145 -0
  134. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser.md +82 -0
  135. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-linux.md +26 -0
  136. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-macos.md +29 -0
  137. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-windows.md +23 -0
  138. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop.md +54 -0
  139. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui-native-terminal.md +59 -0
  140. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui-pty.md +37 -0
  141. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui.md +60 -0
  142. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/scripts/ui-qa-runner.mjs +535 -0
  143. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa.md +75 -0
  144. package/external/pi-tools-suite/src/async-subagents/commands.ts +15 -71
  145. package/external/pi-tools-suite/src/async-subagents/core/activity.ts +33 -0
  146. package/external/pi-tools-suite/src/async-subagents/core/agent-catalog.ts +5 -4
  147. package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +1 -1
  148. package/external/pi-tools-suite/src/async-subagents/core/agents-dir.ts +8 -5
  149. package/external/pi-tools-suite/src/async-subagents/core/browser-qa.ts +16 -2
  150. package/external/pi-tools-suite/src/async-subagents/core/config.ts +160 -327
  151. package/external/pi-tools-suite/src/async-subagents/core/model-selection.ts +2 -1
  152. package/external/pi-tools-suite/src/async-subagents/core/registry.ts +158 -33
  153. package/external/pi-tools-suite/src/async-subagents/core/routing.ts +21 -5
  154. package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +40 -26
  155. package/external/pi-tools-suite/src/async-subagents/core/state.ts +40 -0
  156. package/external/pi-tools-suite/src/async-subagents/core/types.ts +7 -0
  157. package/external/pi-tools-suite/src/async-subagents/core/ultrawork-auto.ts +51 -45
  158. package/external/pi-tools-suite/src/async-subagents/index.ts +69 -5
  159. package/external/pi-tools-suite/src/async-subagents/lib.ts +16 -9
  160. package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +8 -5
  161. package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +5 -4
  162. package/external/pi-tools-suite/src/async-subagents/types.ts +1 -0
  163. package/external/pi-tools-suite/src/coding-discipline/index.ts +90 -59
  164. package/external/pi-tools-suite/src/config.ts +55 -1
  165. package/external/pi-tools-suite/src/context-gateway/accounting-log.ts +282 -0
  166. package/external/pi-tools-suite/src/context-gateway/config.ts +77 -3
  167. package/external/pi-tools-suite/src/context-gateway/efficiency.ts +414 -0
  168. package/external/pi-tools-suite/src/context-gateway/enforcement.ts +222 -0
  169. package/external/pi-tools-suite/src/context-gateway/index.ts +206 -19
  170. package/external/pi-tools-suite/src/context-gateway/storeless-capabilities.ts +7 -7
  171. package/external/pi-tools-suite/src/context-gateway/telemetry.ts +101 -28
  172. package/external/pi-tools-suite/src/context-gateway/types.ts +16 -3
  173. package/external/pi-tools-suite/src/context-inventory.ts +99 -0
  174. package/external/pi-tools-suite/src/dcp/auto-compress-budget.ts +39 -4
  175. package/external/pi-tools-suite/src/dcp/auto-compress.ts +128 -49
  176. package/external/pi-tools-suite/src/dcp/commands.ts +32 -156
  177. package/external/pi-tools-suite/src/dcp/compress-tool.ts +177 -46
  178. package/external/pi-tools-suite/src/dcp/compression-blocks.ts +6 -58
  179. package/external/pi-tools-suite/src/dcp/compression-preview.ts +9 -0
  180. package/external/pi-tools-suite/src/dcp/compression-progress.ts +22 -0
  181. package/external/pi-tools-suite/src/dcp/config.ts +114 -17
  182. package/external/pi-tools-suite/src/dcp/conversation-index.ts +36 -9
  183. package/external/pi-tools-suite/src/dcp/diagnostics.ts +41 -0
  184. package/external/pi-tools-suite/src/dcp/fresh-tool-results.ts +38 -0
  185. package/external/pi-tools-suite/src/dcp/index.ts +338 -66
  186. package/external/pi-tools-suite/src/dcp/journal.ts +126 -4
  187. package/external/pi-tools-suite/src/dcp/progress-controller.ts +2 -1
  188. package/external/pi-tools-suite/src/dcp/prompts.ts +83 -192
  189. package/external/pi-tools-suite/src/dcp/protected-continuity.ts +175 -0
  190. package/external/pi-tools-suite/src/dcp/pruner-candidates.ts +93 -3
  191. package/external/pi-tools-suite/src/dcp/pruner-message-ids.ts +6 -10
  192. package/external/pi-tools-suite/src/dcp/pruner-nudge.ts +36 -33
  193. package/external/pi-tools-suite/src/dcp/pruner-tools.ts +6 -3
  194. package/external/pi-tools-suite/src/dcp/pruner.ts +1 -0
  195. package/external/pi-tools-suite/src/dcp/routine-pressure.ts +86 -0
  196. package/external/pi-tools-suite/src/dcp/state.ts +9 -0
  197. package/external/pi-tools-suite/src/dcp/statistics.d.ts +8 -0
  198. package/external/pi-tools-suite/src/dcp/statistics.js +156 -0
  199. package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +78 -22
  200. package/external/pi-tools-suite/src/index.ts +7 -1
  201. package/external/pi-tools-suite/src/lib/project.ts +36 -1
  202. package/external/pi-tools-suite/src/model-tools/index.ts +10 -7
  203. package/external/pi-tools-suite/src/repo-discovery/index.ts +304 -4
  204. package/external/pi-tools-suite/src/resource-registry/index.ts +2551 -0
  205. package/external/pi-tools-suite/src/session-recovery/index.ts +17 -0
  206. package/external/pi-tools-suite/src/shell-command-policy.ts +219 -0
  207. package/external/pi-tools-suite/src/todo/index.ts +21 -0
  208. package/external/pi-tools-suite/src/todo/todo.ts +19 -1
  209. package/external/pi-tools-suite/src/tool-descriptions.ts +47 -19
  210. package/package.json +10 -9
  211. package/schemas/pi-tools-suite.json +466 -287
  212. package/schemas/pix.json +129 -11
  213. package/schemas/tasks.json +131 -0
  214. package/skills/simplify/SKILL.md +33 -5
  215. package/docs/desktop-markdown-media.md +0 -77
  216. package/external/pi-tools-suite/docs/browser-qa-subagent.md +0 -177
  217. package/external/pi-tools-suite/docs/dcp-emergency-current-turn.md +0 -102
  218. package/external/pi-tools-suite/src/async-subagents/agents/browser-qa.md +0 -598
  219. package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +0 -54
  220. package/external/pi-tools-suite/src/skill-installer/index.ts +0 -333
  221. package/skills/playwright-cli/SKILL.md +0 -420
  222. package/skills/playwright-cli/references/element-attributes.md +0 -23
  223. package/skills/playwright-cli/references/playwright-tests.md +0 -50
  224. package/skills/playwright-cli/references/request-mocking.md +0 -87
  225. package/skills/playwright-cli/references/running-code.md +0 -241
  226. package/skills/playwright-cli/references/session-management.md +0 -273
  227. package/skills/playwright-cli/references/spec-driven-testing.md +0 -311
  228. package/skills/playwright-cli/references/storage-state.md +0 -290
  229. package/skills/playwright-cli/references/test-generation.md +0 -142
  230. package/skills/playwright-cli/references/tracing.md +0 -154
  231. package/skills/playwright-cli/references/video-recording.md +0 -147
  232. package/skills/spec-lite/SKILL.md +0 -140
  233. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/examples/qa-auth.example.jsonc +0 -0
  234. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/examples/qa-flow.example.jsonc +0 -0
  235. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/vendor/fflate.LICENSE +0 -0
  236. /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 { existsSync, readFileSync } from "node:fs";
2
- import { homedir } from "node:os";
3
- import { join } from "node:path";
4
- import { parse as parseJsonc } from "jsonc-parser";
5
- import { normalizeToolName, parseArgsText } from "../../tool-renderers/utils.js";
6
- const NUDGE_TYPES = ["turn", "iteration", "context-soft", "context-strong"];
7
- export function formatDcpStatsToast(session, options = {}) {
8
- const branch = dcpStatsBranch(session);
9
- const latestState = resolveJournalDcpState(branch);
10
- const manualModeBaseline = options.manualModeBaseline ?? readDcpManualModeBaseline(session, options.configPath);
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 parsed = parseJsonc(readFileSync(configPath, "utf8"));
168
- if (!isRecord(parsed))
169
- return enabled;
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
- return false;
188
- }
189
- return enabled;
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
- if (Array.isArray(operation.prunedTools)) {
308
- for (const raw of operation.prunedTools) {
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
- function dcpStatsBranch(session) {
339
- const manager = session.sessionManager;
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
- const full = manager.readFullBranchEntriesSync?.();
342
- if (Array.isArray(full))
343
- return full;
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
- // Fall back to the manager's currently materialized branch below.
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
- const branch = manager.getBranch();
350
- return Array.isArray(branch) ? branch : [];
351
- }
352
- catch {
353
- return [];
50
+ usage = session.getContextUsage();
354
51
  }
355
- }
356
- function formatStateSource(source) {
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 { stringDisplayWidth } from "../../terminal-width.js";
2
2
  import { SUBAGENTS_WIDGET_MAX_ROWS } from "../constants.js";
3
3
  import { ellipsizeDisplay, padOrTrimPlain, sanitizeText, wrapLine } from "./render-text.js";
4
4
  import { thinkingLevelThemeColor } from "./status-line-renderer.js";
5
- import { activeSubagentStates, formatElapsedSince, formatSubagentsPanelStats, subagentIcon, subagentModelThinkingLabel, subagentRunName, taskPreviewMap, } from "../subagents/subagents-model.js";
5
+ import { activeSubagentStates, formatElapsedSince, formatSubagentLastActivity, formatSubagentsPanelStats, subagentIcon, subagentModelThinkingLabel, subagentRunName, taskPreviewMap, } from "../subagents/subagents-model.js";
6
6
  import { formatTodoPanelStats, formatTodoTaskLine, hasOpenTodoTasks, shiftSegmentsToSlice, todoTaskLineSegments, visibleTodoTaskRows, visibleTodoTasks, } from "../todo/todo-model.js";
7
7
  export function renderTodoPanel(details, expanded, width, colors) {
8
8
  if (!details)
@@ -110,14 +110,16 @@ export function renderSubagentsPanel(state, expanded, width, colors) {
110
110
  const icon = subagentIcon(preview);
111
111
  const runLabel = visibleRun.showRunLabel ? `${subagentRunName(visibleRun.runDir)} ` : "";
112
112
  const prefix = `${runLabel}${icon} ${agent.id} ${model} `;
113
- const suffix = ` ${formatElapsedSince(agent.startedAt, now)}`;
113
+ const activity = formatSubagentLastActivity(agent.lastActivity);
114
+ const elapsed = formatElapsedSince(agent.startedAt, now);
115
+ const suffix = activity ? ` ${activity} ${elapsed}` : ` ${elapsed}`;
114
116
  const taskWidth = Math.max(8, rowWidth - stringDisplayWidth(prefix) - stringDisplayWidth(suffix));
115
117
  const taskText = ellipsizeDisplay(task, taskWidth);
116
118
  const text = `${prefix}${taskText}${suffix}`;
117
119
  lines.push({
118
120
  text: padOrTrimPlain(text, width),
119
121
  colorOverride: colors.muted,
120
- segments: subagentPanelLineSegments({ text, icon, agentId: agent.id, model, taskText, prefix, runLabel, status: agent.status }, colors),
122
+ segments: subagentPanelLineSegments({ text, icon, agentId: agent.id, model, taskText, prefix, runLabel, activity, elapsed, status: agent.status }, colors),
121
123
  target,
122
124
  });
123
125
  }
@@ -136,13 +138,17 @@ function subagentPanelLineSegments(input, colors) {
136
138
  const modelStart = input.text.indexOf(input.model, nameStart + input.agentId.length);
137
139
  const taskStart = input.prefix.length;
138
140
  const suffixStart = taskStart + input.taskText.length;
141
+ const elapsedStart = input.text.length - input.elapsed.length;
142
+ const activityStart = input.activity ? elapsedStart - input.activity.length - 1 : -1;
139
143
  return [
140
144
  ...(runLabelStart >= 0 ? [{ start: runLabelStart, end: runLabelStart + input.runLabel.length, foreground: colors.warning }] : []),
141
145
  { start: iconStart, end: iconStart + input.icon.length, foreground: subagentStatusColor(input.status, colors), bold: true },
142
146
  { start: nameStart, end: nameStart + input.agentId.length, foreground: colors.accent, bold: true },
143
147
  { start: modelStart, end: modelStart + input.model.length, foreground: colors.info },
144
148
  { start: taskStart, end: suffixStart, foreground: colors.muted },
145
- { start: suffixStart, end: input.text.length, foreground: colors.muted },
149
+ { start: suffixStart, end: input.activity ? activityStart : input.text.length, foreground: colors.muted },
150
+ ...(input.activity ? [{ start: activityStart, end: activityStart + input.activity.length, foreground: colors.info }] : []),
151
+ ...(input.activity ? [{ start: activityStart + input.activity.length, end: input.text.length, foreground: colors.muted }] : []),
146
152
  ].filter((segment) => segment.start >= 0 && segment.end > segment.start);
147
153
  }
148
154
  function subagentStatusColor(status, colors) {
@@ -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, ThinkingMenuValue, UserMessageJumpMenuValue, UserMessageMenuValue } from "../types.js";
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>): RenderedLine[];
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;