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
@@ -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 { formatDcpStatsToast } from "../rendering/dcp-stats.js";
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 activeMenu = this.popupMenus.getActivePopupMenu(this.popupMenus.syncActivePopupMenu() ?? "slash");
137
- activeMenu.selectedIndex = target.index;
138
- activeMenu.moveSelection(0);
139
- void this.popupActions.submitActivePopupMenu();
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 message = formatDcpStatsToast(session);
492
- this.host.showToast(message, "info", { variant: "dialog" });
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();
@@ -22,6 +22,7 @@ class LazySessionManager {
22
22
  labelsById = new Map();
23
23
  leafId = null;
24
24
  hydrated;
25
+ contextManager;
25
26
  tailEntryCount;
26
27
  tailStartOffset = 0;
27
28
  constructor(sessionPath, options = {}) {
@@ -74,6 +75,7 @@ class LazySessionManager {
74
75
  this.byId.clear();
75
76
  this.labelsById.clear();
76
77
  this.leafId = null;
78
+ this.contextManager = undefined;
77
79
  mkdirSync(this.sessionDirPath, { recursive: true });
78
80
  this.sessionFilePath = join(this.sessionDirPath, `${timestamp.replace(/[:.]/g, "-")}_${sessionId}.jsonl`);
79
81
  writeFileSync(this.sessionFilePath, `${JSON.stringify(header)}\n`, "utf8");
@@ -103,6 +105,11 @@ class LazySessionManager {
103
105
  getEntries() {
104
106
  return this.hydrated?.getEntries() ?? [...this.entries];
105
107
  }
108
+ // Before hydration this is a presentation/file tail, NOT selected ancestry:
109
+ // it can omit old ancestors and contain abandoned side-branch entries.
110
+ // Lineage-sensitive callers must use readFullBranchEntries(); retry proofs
111
+ // must follow parentId, never compare positions in this cache. See
112
+ // specs/dcp-statistics.md#lazy-tail-retry-invariant and its regression tests.
106
113
  getBranch(fromId) {
107
114
  if (this.hydrated)
108
115
  return this.hydrated.getBranch(fromId);
@@ -161,6 +168,8 @@ class LazySessionManager {
161
168
  buildContextEntries() {
162
169
  if (this.hydrated)
163
170
  return this.hydrated.buildContextEntries();
171
+ if (this.tailStartOffset > 0)
172
+ return this.completeContextManager().buildContextEntries();
164
173
  const entries = this.contextEntries();
165
174
  const byId = new Map(entries.map((entry) => [entry.id, entry]));
166
175
  return buildSdkContextEntries(entries, entries.at(-1)?.id ?? null, byId);
@@ -168,6 +177,8 @@ class LazySessionManager {
168
177
  buildSessionContext() {
169
178
  if (this.hydrated)
170
179
  return this.hydrated.buildSessionContext();
180
+ if (this.tailStartOffset > 0)
181
+ return this.completeContextManager().buildSessionContext();
171
182
  const entries = this.contextEntries();
172
183
  const byId = new Map(entries.map((entry) => [entry.id, entry]));
173
184
  return buildSessionContext(entries, entries.at(-1)?.id ?? null, byId);
@@ -212,6 +223,7 @@ class LazySessionManager {
212
223
  return;
213
224
  }
214
225
  this.leafId = branchFromId;
226
+ this.contextManager = undefined;
215
227
  }
216
228
  resetLeaf() {
217
229
  if (this.hydrated) {
@@ -219,6 +231,7 @@ class LazySessionManager {
219
231
  return;
220
232
  }
221
233
  this.leafId = null;
234
+ this.contextManager = undefined;
222
235
  }
223
236
  createBranchedSession(leafId) {
224
237
  return this.hydrate().createBranchedSession(leafId);
@@ -292,9 +305,29 @@ class LazySessionManager {
292
305
  hydrate() {
293
306
  if (!this.hydrated) {
294
307
  this.hydrated = SessionManager.open(this.sessionFilePath, this.sessionDirPath, this.cwdPath);
308
+ this.contextManager = undefined;
295
309
  }
296
310
  return this.hydrated;
297
311
  }
312
+ /**
313
+ * The tail cache is a presentation/history optimisation, not a valid model
314
+ * context boundary. If older entries exist, reconstruct provider context from
315
+ * the complete persisted session while leaving the facade itself lazy so the
316
+ * UI can continue paging old history on demand.
317
+ */
318
+ completeContextManager() {
319
+ if (!this.contextManager) {
320
+ const manager = SessionManager.open(this.sessionFilePath, this.sessionDirPath, this.cwdPath);
321
+ if (this.leafId === null) {
322
+ manager.resetLeaf();
323
+ }
324
+ else if (manager.getLeafId() !== this.leafId && manager.getEntry(this.leafId)) {
325
+ manager.branch(this.leafId);
326
+ }
327
+ this.contextManager = manager;
328
+ }
329
+ return this.contextManager;
330
+ }
298
331
  async loadHeaderAsync(cwdOverride) {
299
332
  const existingHeader = await readSessionHeaderFast(this.sessionFilePath);
300
333
  if (existingHeader)
@@ -335,6 +368,7 @@ class LazySessionManager {
335
368
  this.entries.push(entry);
336
369
  this.byId.set(entry.id, entry);
337
370
  this.leafId = entry.id;
371
+ this.contextManager = undefined;
338
372
  return entry.id;
339
373
  }
340
374
  newEntry(type, payload) {
@@ -91,6 +91,7 @@ export declare class AppSessionEventController {
91
91
  private currentThinkingEntryStartedAt;
92
92
  private thinkingElapsedRenderTimer;
93
93
  private assistantMessageClosed;
94
+ private currentAssistantTerminalErrorRendered;
94
95
  private assistantTextBuffer;
95
96
  constructor(host: AppSessionEventControllerHost);
96
97
  snapshotState(): AppSessionEventControllerState;
@@ -1,7 +1,7 @@
1
1
  import { isRetryableAssistantError } from "@earendil-works/pi-ai";
2
2
  import { createId } from "../id.js";
3
3
  import { extractImageContents, renderContent, renderUserMessageContent, stringifyUnknown } from "../rendering/message-content.js";
4
- import { customMessageEntry, extensionSessionEntry, loadSessionHistoryEntries, loadSessionHistoryEntriesAsync } from "./session-history.js";
4
+ import { assistantTerminalErrorText, customMessageEntry, extensionSessionEntry, loadSessionHistoryEntries, loadSessionHistoryEntriesAsync } from "./session-history.js";
5
5
  import { sessionHistoryDisplayMessages, sessionHistoryDisplayMessagesFromEntries, sessionHistoryFullBranchEntries, sessionHistoryOlderMessagesReader } from "./pix-system-message.js";
6
6
  import { THINKING_TOOL_NAME } from "../constants.js";
7
7
  import { isRecord } from "../guards.js";
@@ -34,6 +34,7 @@ export class AppSessionEventController {
34
34
  currentThinkingEntryStartedAt;
35
35
  thinkingElapsedRenderTimer;
36
36
  assistantMessageClosed = false;
37
+ currentAssistantTerminalErrorRendered = false;
37
38
  assistantTextBuffer = "";
38
39
  constructor(host) {
39
40
  this.host = host;
@@ -586,6 +587,7 @@ export class AppSessionEventController {
586
587
  }
587
588
  if (isRecord(message) && message.role === "assistant") {
588
589
  this.assistantMessageClosed = false;
590
+ this.currentAssistantTerminalErrorRendered = false;
589
591
  this.clearCurrentAssistantState();
590
592
  }
591
593
  }
@@ -601,6 +603,12 @@ export class AppSessionEventController {
601
603
  this.renderAssistantToolCallsFromMessage(message);
602
604
  this.finishCurrentThinkingEntry();
603
605
  this.flushAssistantTextBuffer(true);
606
+ const terminalError = assistantTerminalErrorText(message);
607
+ if (terminalError && !this.currentAssistantTerminalErrorRendered) {
608
+ this.addEntry({ id: createId("error"), kind: "error", text: terminalError });
609
+ this.currentAssistantTerminalErrorRendered = true;
610
+ this.showManualRetryOnTerminalError(message);
611
+ }
604
612
  this.clearCurrentAssistantState();
605
613
  this.assistantMessageClosed = true;
606
614
  }
@@ -726,6 +734,7 @@ export class AppSessionEventController {
726
734
  this.host.setSessionActivity(this.host.runtime()?.session.isStreaming ? "running" : "idle");
727
735
  const errorText = assistantEvent.error.errorMessage ?? assistantEvent.reason;
728
736
  this.addEntry({ id: createId("error"), kind: "error", text: errorText });
737
+ this.currentAssistantTerminalErrorRendered = true;
729
738
  this.showManualRetryOnTerminalError(assistantEvent.error);
730
739
  break;
731
740
  }
@@ -41,4 +41,5 @@ export declare function customMessageEntry(message: Record<string, unknown>): En
41
41
  export declare function extensionSessionEntry(value: unknown): Extract<Entry, {
42
42
  kind: "extension-entry";
43
43
  }> | undefined;
44
+ export declare function assistantTerminalErrorText(message: Record<string, unknown>): string | undefined;
44
45
  export {};
@@ -4,6 +4,7 @@ import { isOnlyHiddenMetadata } from "../../markdown-format.js";
4
4
  import { extractImageContents, renderContent, renderUserMessageContent, stringifyUnknown } from "../rendering/message-content.js";
5
5
  import { THINKING_TOOL_NAME } from "../constants.js";
6
6
  import { PIX_EXTENSION_ENTRY_ROLE, PIX_SESSION_ENTRY_ID_FIELD, PIX_SYSTEM_DISPLAY_ENTRY_CUSTOM_TYPE, PIX_SYSTEM_MESSAGE_CUSTOM_TYPE, PIX_THINKING_LEVEL_FIELD } from "./pix-system-message.js";
7
+ import { WORKSPACE_MUTATION_ENTRY_TYPE } from "../workspace/workspace-undo.js";
7
8
  const HISTORICAL_SUBAGENTS_OBSERVATION = { showSnapshot: false };
8
9
  const DEFAULT_HISTORY_CHUNK_SIZE = 50;
9
10
  const DEFAULT_HISTORY_TAIL_MESSAGE_COUNT = 80;
@@ -189,7 +190,7 @@ export function customMessageEntry(message) {
189
190
  export function extensionSessionEntry(value) {
190
191
  if (!isRecord(value) || value.type !== "custom" || typeof value.id !== "string" || typeof value.customType !== "string")
191
192
  return undefined;
192
- if (value.customType === PIX_SYSTEM_DISPLAY_ENTRY_CUSTOM_TYPE)
193
+ if (value.customType === PIX_SYSTEM_DISPLAY_ENTRY_CUSTOM_TYPE || value.customType === WORKSPACE_MUTATION_ENTRY_TYPE)
193
194
  return undefined;
194
195
  return {
195
196
  id: `extension-entry-${value.id}`,
@@ -198,6 +199,12 @@ export function extensionSessionEntry(value) {
198
199
  expanded: false,
199
200
  };
200
201
  }
202
+ export function assistantTerminalErrorText(message) {
203
+ if (message.stopReason !== "error")
204
+ return undefined;
205
+ const errorMessage = typeof message.errorMessage === "string" ? message.errorMessage.trim() : "";
206
+ return errorMessage || "Request failed before the assistant produced a response.";
207
+ }
201
208
  function renderAssistantHistoryMessage(message, toolResults, options) {
202
209
  const content = message.content;
203
210
  if (!Array.isArray(content))
@@ -273,4 +280,8 @@ function renderAssistantHistoryMessage(message, toolResults, options) {
273
280
  if (assistantText && !isOnlyHiddenMetadata(assistantText)) {
274
281
  options.addEntry({ id: createId("assistant"), kind: "assistant", text: assistantText });
275
282
  }
283
+ const terminalError = assistantTerminalErrorText(message);
284
+ if (terminalError) {
285
+ options.addEntry({ id: createId("error"), kind: "error", text: terminalError });
286
+ }
276
287
  }
@@ -3,7 +3,12 @@ import type { InputEditor } from "../../input-editor.js";
3
3
  import type { AppOptions, Entry, PixExtensionUIContext, SessionActivity } from "../types.js";
4
4
  export type AppSessionLifecycleHost = {
5
5
  options: AppOptions;
6
- createRuntime(): Promise<AgentSessionRuntime>;
6
+ prepareStartupRuntime?(): Promise<{
7
+ sessionPath?: string;
8
+ } | null>;
9
+ createRuntime(options?: {
10
+ sessionPath?: string;
11
+ }): Promise<AgentSessionRuntime>;
7
12
  entries: Entry[];
8
13
  runtime(): AgentSessionRuntime | undefined;
9
14
  setRuntime(runtime: AgentSessionRuntime | undefined): void;
@@ -27,6 +32,7 @@ export type AppSessionLifecycleHost = {
27
32
  handleExtensionError(error: ExtensionError): void;
28
33
  handleSessionEvent(event: AgentSessionEvent): void;
29
34
  bindAgentPause(session: AgentSession): void;
35
+ contextInventoryText?(runtime: AgentSessionRuntime, heading: string): string;
30
36
  addEntry(entry: Entry): void;
31
37
  setStatus(status: string): void;
32
38
  showToast(message: string, kind: "success" | "error" | "warning" | "info"): void;
@@ -32,7 +32,16 @@ export class AppSessionLifecycleController {
32
32
  });
33
33
  try {
34
34
  await this.host.loadStartupConfig();
35
- const runtime = await this.host.createRuntime();
35
+ const startupRuntime = await this.host.prepareStartupRuntime?.();
36
+ if (startupRuntime === null) {
37
+ this.host.setRuntime(undefined);
38
+ this.host.setSessionStatus(undefined);
39
+ this.host.setSessionActivity("idle");
40
+ await this.host.restoreTabsAfterStartup();
41
+ this.host.render();
42
+ return;
43
+ }
44
+ const runtime = await this.host.createRuntime(startupRuntime ?? undefined);
36
45
  if (!this.host.isRunning()) {
37
46
  await this.host.disposeRuntimeForQuit(runtime);
38
47
  return;
@@ -44,6 +53,12 @@ export class AppSessionLifecycleController {
44
53
  await this.bindCurrentSession({ awaitExtensions: false });
45
54
  });
46
55
  await this.bindCurrentSession({ awaitExtensions: false });
56
+ if (runtime.session.messages.length === 0) {
57
+ await this.awaitCurrentSessionExtensions(runtime);
58
+ const text = this.host.contextInventoryText?.(runtime, `Empty tab ready. cwd=${runtime.cwd}`);
59
+ if (text)
60
+ this.host.addEntry({ id: createId("system"), kind: "system", text });
61
+ }
47
62
  if (runtime.modelFallbackMessage) {
48
63
  this.host.addEntry({ id: createId("system"), kind: "system", text: runtime.modelFallbackMessage });
49
64
  }
@@ -16,8 +16,11 @@ export type AppTabsControllerHost = {
16
16
  readonly maxProjectSessions?: number | (() => number | undefined);
17
17
  readonly blinkController: AppBlinkController;
18
18
  runtime(): AgentSessionRuntime | undefined;
19
- createRuntimeForNewSession(): Promise<AgentSessionRuntime>;
19
+ createRuntimeForNewSession(modelRef?: string): Promise<AgentSessionRuntime>;
20
20
  createRuntimeForSession(sessionPath: string): Promise<AgentSessionRuntime>;
21
+ draftModelOverrideRef?(): string | undefined;
22
+ resetDraftModelSelection?(): void;
23
+ deactivateRuntimeForDraft?(): void;
21
24
  awaitCurrentSessionExtensions?(runtime?: AgentSessionRuntime): Promise<void>;
22
25
  activateRuntime(runtime: AgentSessionRuntime, options?: BindCurrentSessionOptions): Promise<void>;
23
26
  disposeRuntime(runtime: AgentSessionRuntime): Promise<void>;
@@ -43,6 +46,9 @@ export type AppTabsControllerHost = {
43
46
  restoreAutoUserMessages?(messages: readonly SubmittedUserMessage[]): void;
44
47
  captureDeferredUserMessages?(): readonly SubmittedUserMessage[];
45
48
  restoreDeferredUserMessages?(messages: readonly SubmittedUserMessage[]): void;
49
+ openDraftSessionSelector?(): void;
50
+ closeDraftSessionSelector?(): void;
51
+ contextInventoryText?(runtime: AgentSessionRuntime, heading: string): string;
46
52
  addEntry(entry: Entry): void;
47
53
  showToast(message: string, kind: "success" | "error" | "warning" | "info"): void;
48
54
  render(): void;
@@ -63,6 +69,7 @@ export declare class AppTabsController {
63
69
  private readonly tabIdsNeedingHistoryReload;
64
70
  private readonly historyInvalidationGenerationByTabId;
65
71
  private readonly runtimeOwnershipGenerationByTabId;
72
+ private readonly touchedDraftTabIds;
66
73
  private readonly orphanRuntimeDisposals;
67
74
  private readonly lifecycleMutationQueue;
68
75
  private activeTabId;
@@ -73,6 +80,9 @@ export declare class AppTabsController {
73
80
  private lifecycleGeneration;
74
81
  private lifecycleMutationRunning;
75
82
  private saveTabsWriteTail;
83
+ private startupSavedTabs;
84
+ private startupPrepared;
85
+ private startupRuntimePlan;
76
86
  private restored;
77
87
  private retentionCleanupRunning;
78
88
  private retentionCleanupScheduled;
@@ -87,6 +97,9 @@ export declare class AppTabsController {
87
97
  markTerminalBellAttention(sessionPath: string | undefined): void;
88
98
  tabPanelRows(terminalRows: number): number;
89
99
  activeInputTabId(): string | undefined;
100
+ isDraftTabActive(): boolean;
101
+ openSessionPaths(): readonly string[];
102
+ noteActiveDraftInputChanged(): void;
90
103
  inputStateForTab(tabId: string | undefined): TabInputState | undefined;
91
104
  setInputStateForTab(tabId: string | undefined, state: TabInputState): Promise<void>;
92
105
  disposeInactiveRuntimes(disposeRuntime?: (runtime: AgentSessionRuntime) => Promise<void>): Promise<void>;
@@ -98,18 +111,28 @@ export declare class AppTabsController {
98
111
  save?: boolean;
99
112
  force?: boolean;
100
113
  }): void;
114
+ prepareStartupRuntime(): Promise<{
115
+ sessionPath?: string;
116
+ } | null>;
101
117
  restoreAfterStartup(): Promise<void>;
102
118
  private restoreAfterStartupMutation;
103
119
  openNewTab(): Promise<void>;
104
120
  private openNewTabMutation;
121
+ materializeActiveDraftTab(): Promise<AgentSessionRuntime | undefined>;
122
+ private materializeActiveDraftTabMutation;
123
+ openSessionInActiveDraftTab(sessionPath: string): Promise<boolean>;
124
+ private openSessionInActiveDraftTabMutation;
125
+ private ownsDraftLifecycle;
105
126
  openSessionInNewTab(sessionPath: string): Promise<boolean>;
106
127
  private openSessionInNewTabMutation;
107
128
  forkSessionEntryInNewTab(entryId: string): Promise<boolean>;
108
129
  private forkSessionEntryInNewTabMutation;
109
130
  switchToTab(tabId: string): Promise<void>;
110
131
  private switchToTabMutation;
132
+ private switchDraftToPersistedTab;
111
133
  closeTab(tabId: string): Promise<void>;
112
134
  private closeTabMutation;
135
+ private closeDraftTabMutation;
113
136
  private replaceLastTabWithNewSession;
114
137
  private loadActiveSessionHistory;
115
138
  private cancelHistoryLoad;
@@ -117,6 +140,8 @@ export declare class AppTabsController {
117
140
  private runtimeForCommand;
118
141
  private idleRuntime;
119
142
  private activeTab;
143
+ private createDraftTab;
144
+ private activateDraftTab;
120
145
  private clearStartupTabPlaceholders;
121
146
  private storeActiveRuntime;
122
147
  private storeActiveSessionView;