@timurproko/a1 0.1.8-dev.21b95d6 → 0.1.8-dev.224

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 (461) hide show
  1. package/README.md +107 -67
  2. package/bin/cli.js +28 -10
  3. package/bin/guardian.js +4 -1
  4. package/bin/module-identity.js +69 -35
  5. package/bin/pi-tui.d.ts +6 -0
  6. package/bin/pi-tui.js +28 -0
  7. package/bin/release-cleanup.js +8 -0
  8. package/bin/supervisor.js +1 -1
  9. package/bin/sync-pi-tui-proxy.js +97 -0
  10. package/bin/ui.js +25 -6
  11. package/bin/warmup.js +18 -0
  12. package/dist/cli/capabilities.d.ts +6 -0
  13. package/dist/{src/cli → cli}/capabilities.js +1 -1
  14. package/dist/{src/cli → cli}/dispatch.d.ts +6 -0
  15. package/dist/cli/dispatch.js +202 -0
  16. package/dist/{src/cli → cli}/packages.d.ts +10 -2
  17. package/dist/{src/cli → cli}/packages.js +24 -20
  18. package/dist/{src/cli → cli}/version-stats.d.ts +6 -0
  19. package/dist/cli/version-stats.js +112 -0
  20. package/dist/composition/agent-engine-bridge.d.ts +3 -0
  21. package/dist/composition/agent-engine-bridge.js +108 -0
  22. package/dist/composition/index.d.ts +3 -0
  23. package/dist/composition/index.js +3 -0
  24. package/dist/composition/owned-ui.d.ts +28 -0
  25. package/dist/composition/owned-ui.js +64 -0
  26. package/dist/composition/process.d.ts +16 -0
  27. package/dist/composition/process.js +45 -0
  28. package/dist/composition/settings-route-host.d.ts +8 -0
  29. package/dist/composition/settings-route-host.js +75 -0
  30. package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/capability-ports.d.ts +2 -2
  31. package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/domain-validation.js +22 -4
  32. package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/domain.d.ts +17 -0
  33. package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/model.d.ts +36 -0
  34. package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/validation.js +25 -0
  35. package/dist/{src/features → features}/launch/development-launch.js +11 -3
  36. package/dist/{src/features → features}/launch/index.d.ts +0 -1
  37. package/dist/{src/features → features}/launch/index.js +0 -1
  38. package/dist/{src/features → features}/launch/intent.d.ts +3 -2
  39. package/dist/features/launch/intent.js +3 -0
  40. package/dist/{src/features → features}/launch/prepare-launch.d.ts +0 -2
  41. package/dist/{src/features → features}/launch/prepare-launch.js +2 -9
  42. package/dist/{src/features → features}/launch/profile-paths.d.ts +1 -2
  43. package/dist/{src/features → features}/launch/profile-paths.js +2 -8
  44. package/dist/{src/features → features}/launch/runtime-selection.d.ts +2 -5
  45. package/dist/{src/features → features}/owned-ui/customization.d.ts +3 -1
  46. package/dist/{src/features → features}/owned-ui/customization.js +3 -1
  47. package/dist/{src/features → features}/owned-ui/diagnostics.d.ts +2 -1
  48. package/dist/{src/features → features}/owned-ui/diagnostics.js +1 -0
  49. package/dist/{src/features → features}/owned-ui/index.d.ts +1 -0
  50. package/dist/{src/features → features}/owned-ui/index.js +1 -0
  51. package/dist/features/owned-ui/project-trust-prompt.d.ts +25 -0
  52. package/dist/features/owned-ui/project-trust-prompt.js +149 -0
  53. package/dist/{src/features → features}/owned-ui/run.d.ts +2 -2
  54. package/dist/{src/features → features}/owned-ui/run.js +2 -0
  55. package/dist/{src/features → features}/owned-ui/settings-app.d.ts +3 -3
  56. package/dist/{src/features → features}/owned-ui/settings-app.js +103 -54
  57. package/dist/{src/features → features}/workspace/capabilities.d.ts +1 -1
  58. package/dist/{src/features → features}/workspace/reducer.d.ts +2 -1
  59. package/dist/{src/features → features}/workspace/reducer.js +2 -1
  60. package/dist/{src/features → features}/workspace/router.d.ts +2 -1
  61. package/dist/{src/features → features}/workspace/router.js +1 -0
  62. package/dist/{src/features → features}/workspace/store.d.ts +2 -1
  63. package/dist/{src/features → features}/workspace/store.js +1 -0
  64. package/dist/{src/foundation → foundation}/launch-guardian/main.js +10 -2
  65. package/dist/{src/foundation → foundation}/lifecycle/model.d.ts +1 -1
  66. package/dist/{src/foundation → foundation}/lifecycle/model.js +1 -1
  67. package/dist/foundation/lifecycle/paths.d.ts +28 -0
  68. package/dist/{src/foundation → foundation}/lifecycle/paths.js +21 -0
  69. package/dist/{src/foundation → foundation}/native-host-protocol/codec.d.ts +2 -0
  70. package/dist/{src/foundation → foundation}/native-host-protocol/codec.js +2 -0
  71. package/dist/{src/foundation → foundation}/native-host-protocol/messages.d.ts +1 -1
  72. package/dist/{src/foundation → foundation}/native-host-protocol/messages.js +1 -1
  73. package/dist/{src/foundation → foundation}/process-containment/linux-process-inspector.d.ts +1 -0
  74. package/dist/{src/foundation → foundation}/process-containment/linux-process-inspector.js +1 -0
  75. package/dist/{src/foundation → foundation}/process-containment/native-guardian-containment.d.ts +1 -0
  76. package/dist/{src/foundation → foundation}/process-containment/native-guardian-containment.js +1 -0
  77. package/dist/{src/foundation → foundation}/process-containment/windows-process-inspector.d.ts +1 -0
  78. package/dist/{src/foundation → foundation}/process-containment/windows-process-inspector.js +1 -0
  79. package/dist/{src/foundation → foundation}/protocol/client.d.ts +1 -0
  80. package/dist/{src/foundation → foundation}/protocol/client.js +1 -0
  81. package/dist/{src/foundation → foundation}/protocol/messages.d.ts +1 -0
  82. package/dist/{src/foundation → foundation}/protocol/messages.js +1 -0
  83. package/dist/{src/foundation → foundation}/release/bootstrap.d.ts +14 -4
  84. package/dist/{src/foundation → foundation}/release/bootstrap.js +66 -13
  85. package/dist/foundation/release/cohort-state.d.ts +131 -0
  86. package/dist/foundation/release/cohort-state.js +384 -0
  87. package/dist/foundation/release/dependency-layer.d.ts +78 -0
  88. package/dist/foundation/release/dependency-layer.js +487 -0
  89. package/dist/foundation/release/endpoints.d.ts +20 -0
  90. package/dist/foundation/release/endpoints.js +50 -0
  91. package/dist/{src/foundation → foundation}/release/index.d.ts +3 -0
  92. package/dist/{src/foundation → foundation}/release/index.js +3 -0
  93. package/dist/foundation/release/release-gc.d.ts +60 -0
  94. package/dist/foundation/release/release-gc.js +538 -0
  95. package/dist/{src/foundation → foundation}/release/release-store.d.ts +3 -1
  96. package/dist/{src/foundation → foundation}/release/release-store.js +60 -10
  97. package/dist/{src/foundation → foundation}/release/release.d.ts +4 -1
  98. package/dist/{src/foundation → foundation}/release/release.js +10 -3
  99. package/dist/{src/foundation → foundation}/release/update-transaction.d.ts +1 -0
  100. package/dist/{src/foundation → foundation}/release/update-transaction.js +1 -0
  101. package/dist/{src/foundation → foundation}/release/update.d.ts +19 -3
  102. package/dist/{src/foundation → foundation}/release/update.js +114 -54
  103. package/dist/foundation/release/warmup.d.ts +3 -0
  104. package/dist/foundation/release/warmup.js +35 -0
  105. package/dist/foundation/startup/index.d.ts +1 -0
  106. package/dist/foundation/startup/index.js +1 -0
  107. package/dist/foundation/startup/startup-runtime.d.ts +36 -0
  108. package/dist/foundation/startup/startup-runtime.js +140 -0
  109. package/dist/{src/foundation → foundation}/storage/control-store.d.ts +2 -1
  110. package/dist/{src/foundation → foundation}/storage/control-store.js +39 -4
  111. package/dist/{src/foundation → foundation}/structured-agent-runtime/backpressure.d.ts +2 -1
  112. package/dist/{src/foundation → foundation}/structured-agent-runtime/backpressure.js +2 -1
  113. package/dist/{src/foundation → foundation}/structured-agent-runtime/commands.d.ts +2 -1
  114. package/dist/{src/foundation → foundation}/structured-agent-runtime/commands.js +2 -1
  115. package/dist/{src/foundation → foundation}/structured-agent-runtime/handshake.d.ts +1 -1
  116. package/dist/{src/foundation → foundation}/structured-agent-runtime/handshake.js +1 -1
  117. package/dist/{src/foundation → foundation}/structured-agent-runtime/reconnection.d.ts +2 -1
  118. package/dist/{src/foundation → foundation}/structured-agent-runtime/reconnection.js +2 -1
  119. package/dist/{src/foundation → foundation}/structured-agent-runtime/state.d.ts +2 -1
  120. package/dist/{src/foundation → foundation}/structured-agent-runtime/state.js +2 -1
  121. package/dist/foundation/supervision/index.d.ts +3 -0
  122. package/dist/foundation/supervision/index.js +3 -0
  123. package/dist/{src/foundation → foundation}/supervision/main.js +11 -5
  124. package/dist/foundation/supervision/paths.d.ts +1 -0
  125. package/dist/foundation/supervision/paths.js +1 -0
  126. package/dist/{src/foundation → foundation}/supervision/server.d.ts +19 -1
  127. package/dist/{src/foundation → foundation}/supervision/server.js +51 -2
  128. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/components.d.ts +1 -1
  129. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/conformance.d.ts +1 -0
  130. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/conformance.js +3 -2
  131. package/dist/integrations/pi/components/owned-editor-ux.d.ts +55 -0
  132. package/dist/integrations/pi/components/owned-editor-ux.js +778 -0
  133. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-components.d.ts +1 -0
  134. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-components.js +1 -0
  135. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-editor-autocomplete.js +54 -6
  136. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-extension-ui.d.ts +1 -1
  137. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-footer-status.d.ts +3 -3
  138. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-footer-status.js +32 -17
  139. package/dist/integrations/pi/components/shell-presenters-info.d.ts +36 -0
  140. package/dist/integrations/pi/components/shell-presenters-info.js +78 -0
  141. package/dist/integrations/pi/components/shell-presenters-transcript.d.ts +25 -0
  142. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-presenters-transcript.js +92 -131
  143. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-selectors-dialogs.d.ts +1 -1
  144. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-shared-facade.d.ts +52 -8
  145. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-shared-facade.js +4 -2
  146. package/dist/integrations/pi/components/submitted-prompt-adapter.d.ts +13 -0
  147. package/dist/integrations/pi/components/submitted-prompt-adapter.js +27 -0
  148. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/adjacent/core/keybindings.d.ts +10 -2
  149. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/adjacent/core/keybindings.js +36 -3
  150. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-footer.d.ts +1 -1
  151. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/adapter.d.ts +22 -4
  152. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/adapter.js +289 -68
  153. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/conformance.d.ts +2 -0
  154. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/conformance.js +4 -2
  155. package/dist/integrations/pi/engine/http-dispatcher.d.ts +4 -0
  156. package/dist/integrations/pi/engine/http-dispatcher.js +25 -0
  157. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/index.d.ts +3 -0
  158. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/index.js +3 -0
  159. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/model-auth-integration.d.ts +2 -1
  160. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/model-auth-integration.js +1 -0
  161. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/package-integration.d.ts +1 -1
  162. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/package-integration.js +2 -2
  163. package/dist/integrations/pi/engine/project-trust-preflight.d.ts +22 -0
  164. package/dist/integrations/pi/engine/project-trust-preflight.js +50 -0
  165. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/resource-extension-integration.d.ts +2 -1
  166. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/resource-extension-integration.js +1 -0
  167. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/runtime-integration.d.ts +16 -1
  168. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/runtime-integration.js +39 -4
  169. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/session-integration.d.ts +4 -2
  170. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/session-integration.js +19 -4
  171. package/dist/integrations/pi/engine/settings-effects.d.ts +66 -0
  172. package/dist/integrations/pi/engine/settings-effects.js +249 -0
  173. package/dist/integrations/pi/engine/settings-integration.d.ts +43 -0
  174. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/settings-integration.js +102 -108
  175. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/workflow-controllers.d.ts +3 -2
  176. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/workflow-controllers.js +1 -0
  177. package/dist/integrations/pi/session-ui/clipboard-image.d.ts +11 -0
  178. package/dist/integrations/pi/session-ui/clipboard-image.js +29 -0
  179. package/dist/{src/foundation/pi-owned-ui-integration → integrations/pi/session-ui}/index.d.ts +1 -0
  180. package/dist/{src/foundation/pi-owned-ui-integration → integrations/pi/session-ui}/index.js +1 -0
  181. package/dist/integrations/pi/session-ui/prompt-chips.d.ts +23 -0
  182. package/dist/integrations/pi/session-ui/prompt-chips.js +246 -0
  183. package/dist/integrations/pi/session-ui/route-host.d.ts +5 -0
  184. package/dist/integrations/pi/session-ui/session-shell-root.d.ts +137 -0
  185. package/dist/integrations/pi/session-ui/session-shell-root.js +1106 -0
  186. package/dist/integrations/pi/session-ui/session-shell.d.ts +55 -0
  187. package/dist/{src/foundation/pi-owned-ui-integration → integrations/pi/session-ui}/session-shell.js +415 -775
  188. package/dist/integrations/pi/session-ui/session-viewport-controller.d.ts +44 -0
  189. package/dist/integrations/pi/session-ui/session-viewport-controller.js +434 -0
  190. package/dist/integrations/pi/session-ui/stream-presentation-coalescer.d.ts +29 -0
  191. package/dist/integrations/pi/session-ui/stream-presentation-coalescer.js +79 -0
  192. package/dist/integrations/pi/session-ui/system-clipboard.d.ts +20 -0
  193. package/dist/integrations/pi/session-ui/system-clipboard.js +130 -0
  194. package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/adapter.d.ts +8 -1
  195. package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/adapter.js +216 -9
  196. package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/contracts.d.ts +29 -6
  197. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +63 -0
  198. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +240 -0
  199. package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/index.d.ts +2 -0
  200. package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/index.js +2 -0
  201. package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.d.ts +39 -0
  202. package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.js +139 -0
  203. package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/presentation-adapter.d.ts +1 -1
  204. package/dist/native/darwin-arm64/manifest.json +1 -1
  205. package/dist/native/linux-x64/manifest.json +3 -3
  206. package/dist/native/linux-x64/process-guardian +0 -0
  207. package/dist/native/win32-x64/manifest.json +3 -3
  208. package/dist/native/win32-x64/process-guardian.exe +0 -0
  209. package/dist/{src/product-identity.d.ts → product-identity.d.ts} +3 -3
  210. package/dist/{src/product-identity.js → product-identity.js} +7 -5
  211. package/dist/{src/product-identity.json → product-identity.json} +11 -6
  212. package/dist/runtime-payload-inventory.json +122328 -0
  213. package/dist/{src/foundation/ui-apps → ui/apps}/contracts.d.ts +23 -1
  214. package/dist/{src/foundation/ui-apps → ui/apps}/host.d.ts +1 -1
  215. package/dist/{src/foundation/ui-apps → ui/apps}/host.js +14 -10
  216. package/dist/{src/foundation/ui-apps → ui/apps}/registry.d.ts +1 -0
  217. package/dist/{src/foundation/ui-apps → ui/apps}/registry.js +1 -0
  218. package/dist/{src/foundation/ui-components → ui/components}/dialog-panel.js +5 -3
  219. package/dist/{src/foundation/ui-components → ui/components}/frame.d.ts +1 -0
  220. package/dist/{src/foundation/ui-components → ui/components}/frame.js +1 -0
  221. package/dist/{src/foundation/ui-components → ui/components}/index.d.ts +4 -0
  222. package/dist/{src/foundation/ui-components → ui/components}/index.js +4 -0
  223. package/dist/{src/foundation/ui-components → ui/components}/line-input.d.ts +1 -6
  224. package/dist/{src/foundation/ui-components → ui/components}/line-input.js +4 -9
  225. package/dist/{src/foundation/ui-components → ui/components}/list-view.d.ts +0 -6
  226. package/dist/{src/foundation/ui-components → ui/components}/list-view.js +14 -7
  227. package/dist/{src/foundation/ui-components → ui/components}/mouse.d.ts +10 -0
  228. package/dist/{src/foundation/ui-components → ui/components}/mouse.js +23 -0
  229. package/dist/ui/components/progress-status.d.ts +2 -0
  230. package/dist/ui/components/progress-status.js +4 -0
  231. package/dist/ui/components/prompt-row.d.ts +14 -0
  232. package/dist/ui/components/prompt-row.js +20 -0
  233. package/dist/{src/foundation/ui-components → ui/components}/scrollbar.d.ts +26 -0
  234. package/dist/{src/foundation/ui-components → ui/components}/scrollbar.js +35 -0
  235. package/dist/{src/foundation/ui-components → ui/components}/shortcuts.d.ts +1 -0
  236. package/dist/{src/foundation/ui-components → ui/components}/shortcuts.js +1 -0
  237. package/dist/ui/components/spans.d.ts +25 -0
  238. package/dist/ui/components/spans.js +257 -0
  239. package/dist/ui/components/submitted-prompt.d.ts +23 -0
  240. package/dist/ui/components/submitted-prompt.js +50 -0
  241. package/dist/{src/foundation/ui-components → ui/components}/surface.d.ts +0 -5
  242. package/dist/{src/foundation/ui-components → ui/components}/surface.js +1 -5
  243. package/dist/ui/components/text-selection.d.ts +62 -0
  244. package/dist/ui/components/text-selection.js +235 -0
  245. package/dist/{src/foundation/ui-components → ui/components}/text.d.ts +14 -0
  246. package/dist/{src/foundation/ui-components → ui/components}/text.js +66 -4
  247. package/dist/ui/components/transcript-viewport.d.ts +146 -0
  248. package/dist/ui/components/transcript-viewport.js +629 -0
  249. package/dist/{src/foundation/owned-ui-settings → ui/settings}/declarations.d.ts +8 -1
  250. package/dist/{src/foundation/owned-ui-settings → ui/settings}/declarations.js +33 -5
  251. package/dist/{src/foundation/owned-ui-settings → ui/settings}/migrations.d.ts +1 -1
  252. package/dist/{src/foundation/owned-ui-settings → ui/settings}/migrations.js +21 -2
  253. package/dist/{src/foundation/owned-ui-settings → ui/settings}/sections.d.ts +6 -19
  254. package/dist/{src/foundation/owned-ui-settings → ui/settings}/sections.js +32 -23
  255. package/dist/{src/foundation/owned-ui-settings → ui/settings}/session.d.ts +9 -17
  256. package/dist/{src/foundation/owned-ui-settings → ui/settings}/session.js +65 -55
  257. package/dist/{src/foundation/owned-ui-settings → ui/settings}/store.d.ts +1 -0
  258. package/dist/{src/foundation/owned-ui-settings → ui/settings}/store.js +2 -1
  259. package/docs/architecture/boundaries.md +9 -9
  260. package/docs/architecture/code-documentation.md +77 -0
  261. package/docs/architecture/project-structure.md +67 -30
  262. package/docs/architecture/resource-and-data-policy.md +1 -1
  263. package/docs/architecture/toolchain.md +10 -10
  264. package/docs/architecture/ui-reference-provenance.md +34 -5
  265. package/docs/ci-release-runbook.md +186 -79
  266. package/docs/features/launch-profiles.md +10 -19
  267. package/docs/manual-launch-instance-acceptance.md +12 -35
  268. package/docs/manual-owned-ui-checkpoint.md +78 -0
  269. package/docs/manual-pi-boundary-candidate.md +4 -5
  270. package/docs/manual-terminal-colour-check.md +4 -3
  271. package/docs/repository-governance-live-acceptance.md +77 -0
  272. package/package.json +37 -27
  273. package/dist/src/cli/capabilities.d.ts +0 -15
  274. package/dist/src/cli/dispatch.js +0 -177
  275. package/dist/src/cli/version-stats.js +0 -72
  276. package/dist/src/composition/index.d.ts +0 -48
  277. package/dist/src/composition/index.js +0 -258
  278. package/dist/src/features/launch/intent.js +0 -4
  279. package/dist/src/features/launch/profiles.d.ts +0 -12
  280. package/dist/src/features/launch/profiles.js +0 -26
  281. package/dist/src/foundation/lifecycle/paths.d.ts +0 -10
  282. package/dist/src/foundation/pi-component-adapter/shell-presenters-transcript.d.ts +0 -57
  283. package/dist/src/foundation/pi-engine-adapter/settings-integration.d.ts +0 -55
  284. package/dist/src/foundation/pi-owned-ui-integration/route-host.d.ts +0 -29
  285. package/dist/src/foundation/pi-owned-ui-integration/session-shell.d.ts +0 -112
  286. package/dist/src/foundation/release/cohort-state.d.ts +0 -70
  287. package/dist/src/foundation/release/cohort-state.js +0 -202
  288. package/dist/src/foundation/release/release-gc.d.ts +0 -5
  289. package/dist/src/foundation/release/release-gc.js +0 -16
  290. package/dist/src/foundation/supervision/index.d.ts +0 -3
  291. package/dist/src/foundation/supervision/index.js +0 -3
  292. package/dist/src/foundation/supervision/paths.d.ts +0 -1
  293. package/dist/src/foundation/supervision/paths.js +0 -1
  294. package/dist/src/foundation/ui-components/spans.d.ts +0 -7
  295. package/dist/src/foundation/ui-components/spans.js +0 -59
  296. package/docs/manual-transparent-checkpoint.md +0 -53
  297. /package/dist/{src/cli → cli}/index.d.ts +0 -0
  298. /package/dist/{src/cli → cli}/index.js +0 -0
  299. /package/dist/{src/cli → cli}/version.d.ts +0 -0
  300. /package/dist/{src/cli → cli}/version.js +0 -0
  301. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/capability-ports.js +0 -0
  302. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/domain-validation.d.ts +0 -0
  303. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/domain.js +0 -0
  304. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/index.d.ts +0 -0
  305. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/index.js +0 -0
  306. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/model.d.ts +0 -0
  307. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/model.js +0 -0
  308. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/package-ports.d.ts +0 -0
  309. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/package-ports.js +0 -0
  310. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/ports.d.ts +0 -0
  311. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/ports.js +0 -0
  312. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/serialization.d.ts +0 -0
  313. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/serialization.js +0 -0
  314. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/validation.d.ts +0 -0
  315. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/validation.js +0 -0
  316. /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/extension-ui.d.ts +0 -0
  317. /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/extension-ui.js +0 -0
  318. /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/index.d.ts +0 -0
  319. /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/index.js +0 -0
  320. /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/model.js +0 -0
  321. /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/validation.d.ts +0 -0
  322. /package/dist/{src/foundation/presentation-contracts → contracts/presentation}/index.d.ts +0 -0
  323. /package/dist/{src/foundation/presentation-contracts → contracts/presentation}/index.js +0 -0
  324. /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/index.d.ts +0 -0
  325. /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/index.js +0 -0
  326. /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/model.d.ts +0 -0
  327. /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/model.js +0 -0
  328. /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/validation.d.ts +0 -0
  329. /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/validation.js +0 -0
  330. /package/dist/{src/features → features}/launch/development-launch.d.ts +0 -0
  331. /package/dist/{src/features → features}/launch/initialize-profile.d.ts +0 -0
  332. /package/dist/{src/features → features}/launch/initialize-profile.js +0 -0
  333. /package/dist/{src/features → features}/launch/runtime-selection.js +0 -0
  334. /package/dist/{src/features → features}/workspace/capabilities.js +0 -0
  335. /package/dist/{src/features → features}/workspace/index.d.ts +0 -0
  336. /package/dist/{src/features → features}/workspace/index.js +0 -0
  337. /package/dist/{src/features → features}/workspace/presentation.d.ts +0 -0
  338. /package/dist/{src/features → features}/workspace/presentation.js +0 -0
  339. /package/dist/{src/features → features}/workspace/reconciliation.d.ts +0 -0
  340. /package/dist/{src/features → features}/workspace/reconciliation.js +0 -0
  341. /package/dist/{src/foundation → foundation}/launch-guardian/index.d.ts +0 -0
  342. /package/dist/{src/foundation → foundation}/launch-guardian/index.js +0 -0
  343. /package/dist/{src/foundation → foundation}/launch-guardian/main.d.ts +0 -0
  344. /package/dist/{src/foundation → foundation}/lifecycle/commands.d.ts +0 -0
  345. /package/dist/{src/foundation → foundation}/lifecycle/commands.js +0 -0
  346. /package/dist/{src/foundation → foundation}/lifecycle/index.d.ts +0 -0
  347. /package/dist/{src/foundation → foundation}/lifecycle/index.js +0 -0
  348. /package/dist/{src/foundation → foundation}/lifecycle/launch-instance.d.ts +0 -0
  349. /package/dist/{src/foundation → foundation}/lifecycle/launch-instance.js +0 -0
  350. /package/dist/{src/foundation → foundation}/native-host-protocol/evidence.d.ts +0 -0
  351. /package/dist/{src/foundation → foundation}/native-host-protocol/evidence.js +0 -0
  352. /package/dist/{src/foundation → foundation}/native-host-protocol/index.d.ts +0 -0
  353. /package/dist/{src/foundation → foundation}/native-host-protocol/index.js +0 -0
  354. /package/dist/{src/foundation → foundation}/native-host-protocol/proof-gate.d.ts +0 -0
  355. /package/dist/{src/foundation → foundation}/native-host-protocol/proof-gate.js +0 -0
  356. /package/dist/{src/foundation → foundation}/process-containment/artifact.d.ts +0 -0
  357. /package/dist/{src/foundation → foundation}/process-containment/artifact.js +0 -0
  358. /package/dist/{src/foundation → foundation}/process-containment/contracts.d.ts +0 -0
  359. /package/dist/{src/foundation → foundation}/process-containment/contracts.js +0 -0
  360. /package/dist/{src/foundation → foundation}/process-containment/index.d.ts +0 -0
  361. /package/dist/{src/foundation → foundation}/process-containment/index.js +0 -0
  362. /package/dist/{src/foundation → foundation}/protocol/index.d.ts +0 -0
  363. /package/dist/{src/foundation → foundation}/protocol/index.js +0 -0
  364. /package/dist/{src/foundation → foundation}/release/cohort-selection.d.ts +0 -0
  365. /package/dist/{src/foundation → foundation}/release/cohort-selection.js +0 -0
  366. /package/dist/{src/foundation → foundation}/release/concurrency.d.ts +0 -0
  367. /package/dist/{src/foundation → foundation}/release/concurrency.js +0 -0
  368. /package/dist/{src/foundation → foundation}/release/process-cleanup.d.ts +0 -0
  369. /package/dist/{src/foundation → foundation}/release/process-cleanup.js +0 -0
  370. /package/dist/{src/foundation → foundation}/release/stable-release.d.ts +0 -0
  371. /package/dist/{src/foundation → foundation}/release/stable-release.js +0 -0
  372. /package/dist/{src/foundation → foundation}/storage/index.d.ts +0 -0
  373. /package/dist/{src/foundation → foundation}/storage/index.js +0 -0
  374. /package/dist/{src/foundation → foundation}/structured-agent-runtime/index.d.ts +0 -0
  375. /package/dist/{src/foundation → foundation}/structured-agent-runtime/index.js +0 -0
  376. /package/dist/{src/foundation → foundation}/supervision/main.d.ts +0 -0
  377. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/components.js +0 -0
  378. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/index.d.ts +0 -0
  379. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/index.js +0 -0
  380. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/resources/builtin-themes.d.ts +0 -0
  381. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/resources/builtin-themes.js +0 -0
  382. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-editor-autocomplete.d.ts +0 -0
  383. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-extension-ui.js +0 -0
  384. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-selectors-dialogs.js +0 -0
  385. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/theme.d.ts +0 -0
  386. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/theme.js +0 -0
  387. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/countdown-timer.d.ts +0 -0
  388. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/countdown-timer.js +0 -0
  389. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/custom-entry.d.ts +0 -0
  390. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/custom-entry.js +0 -0
  391. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/daxnuts.d.ts +0 -0
  392. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/daxnuts.js +0 -0
  393. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/earendil-announcement.d.ts +0 -0
  394. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/earendil-announcement.js +0 -0
  395. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/extension-editor.d.ts +0 -0
  396. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/extension-editor.js +0 -0
  397. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/first-time-setup.d.ts +0 -0
  398. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/first-time-setup.js +0 -0
  399. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/markdown-transform.d.ts +0 -0
  400. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/markdown-transform.js +0 -0
  401. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/mermaid.d.ts +0 -0
  402. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/mermaid.js +0 -0
  403. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/owned-editor.d.ts +0 -0
  404. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/owned-editor.js +0 -0
  405. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/scoped-models-selector.d.ts +0 -0
  406. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/scoped-models-selector.js +0 -0
  407. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-footer.js +0 -0
  408. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-selector-search.d.ts +0 -0
  409. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-selector-search.js +0 -0
  410. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-selector.d.ts +0 -0
  411. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-selector.js +0 -0
  412. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/skill-invocation-message.d.ts +0 -0
  413. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/skill-invocation-message.js +0 -0
  414. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/status-indicator.d.ts +0 -0
  415. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/status-indicator.js +0 -0
  416. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/tree-selector.d.ts +0 -0
  417. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/tree-selector.js +0 -0
  418. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/trust-selector.d.ts +0 -0
  419. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/trust-selector.js +0 -0
  420. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/external-editor.d.ts +0 -0
  421. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/external-editor.js +0 -0
  422. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/model-search.d.ts +0 -0
  423. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/model-search.js +0 -0
  424. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/theme/theme-controller.d.ts +0 -0
  425. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/theme/theme-controller.js +0 -0
  426. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/theme/theme.d.ts +0 -0
  427. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/theme/theme.js +0 -0
  428. /package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/pi-settings-metadata.json +0 -0
  429. /package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/public-main-entry.d.ts +0 -0
  430. /package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/public-main-entry.js +0 -0
  431. /package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/workflows.d.ts +0 -0
  432. /package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/workflows.js +0 -0
  433. /package/dist/{src/foundation/pi-owned-ui-integration → integrations/pi/session-ui}/route-host.js +0 -0
  434. /package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/conformance.d.ts +0 -0
  435. /package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/conformance.js +0 -0
  436. /package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/contracts.js +0 -0
  437. /package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/presentation-adapter.js +0 -0
  438. /package/dist/{src/foundation/ui-apps → ui/apps}/contracts.js +0 -0
  439. /package/dist/{src/foundation/ui-apps → ui/apps}/index.d.ts +0 -0
  440. /package/dist/{src/foundation/ui-apps → ui/apps}/index.js +0 -0
  441. /package/dist/{src/foundation/ui-components → ui/components}/dialog-panel.d.ts +0 -0
  442. /package/dist/{src/foundation/ui-components → ui/components}/label.d.ts +0 -0
  443. /package/dist/{src/foundation/ui-components → ui/components}/label.js +0 -0
  444. /package/dist/{src/foundation/ui-components → ui/components}/list-block.d.ts +0 -0
  445. /package/dist/{src/foundation/ui-components → ui/components}/list-block.js +0 -0
  446. /package/dist/{src/foundation/ui-components → ui/components}/pane.d.ts +0 -0
  447. /package/dist/{src/foundation/ui-components → ui/components}/pane.js +0 -0
  448. /package/dist/{src/foundation/ui-components → ui/components}/revision.d.ts +0 -0
  449. /package/dist/{src/foundation/ui-components → ui/components}/revision.js +0 -0
  450. /package/dist/{src/foundation/ui-components → ui/components}/status-line.d.ts +0 -0
  451. /package/dist/{src/foundation/ui-components → ui/components}/status-line.js +0 -0
  452. /package/dist/{src/foundation/ui-components → ui/components}/stepper.d.ts +0 -0
  453. /package/dist/{src/foundation/ui-components → ui/components}/stepper.js +0 -0
  454. /package/dist/{src/foundation/ui-components → ui/components}/theme.d.ts +0 -0
  455. /package/dist/{src/foundation/ui-components → ui/components}/theme.js +0 -0
  456. /package/dist/{src/foundation/ui-components → ui/components}/value-menu.d.ts +0 -0
  457. /package/dist/{src/foundation/ui-components → ui/components}/value-menu.js +0 -0
  458. /package/dist/{src/foundation/owned-ui-settings → ui/settings}/index.d.ts +0 -0
  459. /package/dist/{src/foundation/owned-ui-settings → ui/settings}/index.js +0 -0
  460. /package/dist/{src/foundation/owned-ui-settings → ui/settings}/resolution.d.ts +0 -0
  461. /package/dist/{src/foundation/owned-ui-settings → ui/settings}/resolution.js +0 -0
@@ -1,723 +1,14 @@
1
- import { MOUSE_TRACKING_OFF, MOUSE_TRACKING_ON, parseMouseInput } from "../ui-components/index.js";
2
- import { PINNED_PI_HIDDEN_COMMAND_NAMES, PINNED_PI_WORKFLOW_COMMAND_NAMES, } from "../pi-engine-adapter/index.js";
3
- import { createPiExtensionUiBridge, createPiQueuedInputStatus, createPiShellArmin, createPiShellAuthProviderSelector, createPiShellChangelog, createPiShellDaxnuts, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellEditor, createPiShellExtensionSelector, createPiShellFooter, createPiShellHeader, createPiShellHotkeys, createPiShellLoadedResources, createPiShellLoginDialog, createPiShellModelSelector, createPiShellOperationLoader, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionInfo, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellStatus, createPiShellTranscriptComponent, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, onPiThemeChange, piTheme, renderPiShellPackageUpdateNotice, renderPiShellStartupDiagnostic, renderPiShellStatusText, renderPiShellTranscriptBlock, } from "../pi-component-adapter/index.js";
4
- import { PiTuiRuntimeAdapter, } from "../pi-tui-runtime-adapter/index.js";
5
- export class OwnedUiSessionShellRoot {
6
- editor;
7
- header;
8
- resources;
9
- #cwd;
10
- #transcript = new Map();
11
- #blocksById = new Map();
12
- #renderedRows = new Map();
13
- #themeUnsubscribe;
14
- #transcriptOrder = [];
15
- #view;
16
- #status;
17
- #footer;
18
- #queued;
19
- #extensionRenderers;
20
- #componentRuntime;
21
- #toolsExpanded = false;
22
- #thinkingVisible = true;
23
- #workflowTranscriptSequence = 0;
24
- #workflowStatusAnchors = new Map();
25
- #workflowStatusMessages = new Map();
26
- #lastWorkflowStatusId;
27
- #inputSurface;
28
- #extensionHeader = null;
29
- #extensionFooter = null;
30
- #extensionWidgets = new Map();
31
- #extensionStatuses = new Map();
32
- #extensionWorkingMessage;
33
- #extensionWorkingVisible = true;
34
- constructor(view, cwd, handlers, startup = {}, agentDir, extensionRenderers = {
35
- getMessageRenderer: () => undefined,
36
- getToolDefinition: () => undefined,
37
- }) {
38
- this.#view = view;
39
- this.#cwd = cwd;
40
- this.#extensionRenderers = extensionRenderers;
41
- this.#componentRuntime = handlers;
42
- this.header = createPiShellHeader(startup);
43
- this.resources = createPiShellLoadedResources(startup.resources ?? [], startup.expanded ?? false);
44
- this.#status = createPiShellStatus(view, handlers);
45
- this.#footer = createPiShellFooter(this.#viewWithExtensionStatuses(view), cwd);
46
- this.#queued = createPiQueuedInputStatus(view.editor.queuedSubmissions);
47
- this.editor = createPiShellEditor({
48
- ...handlers,
49
- cwd,
50
- ...(agentDir === undefined ? {} : { agentDir }),
51
- onToolsExpand: () => this.#setToolsExpanded(!this.#toolsExpanded),
52
- });
53
- this.editor.setThinkingLevel(view.thinkingLevel);
54
- this.#inputSurface = this.editor;
55
- for (const block of view.transcript) {
56
- if (block.kind === "user")
57
- this.editor.addToHistory(block.text);
58
- }
59
- this.#syncTranscript(view.transcript);
60
- // Colours come from the active theme, so rendered rows outlive their revision only
61
- // until the theme under them changes.
62
- this.#themeUnsubscribe = onPiThemeChange(() => this.#renderedRows.clear());
63
- }
64
- update(view) {
65
- this.#view = view;
66
- this.#status.update(view);
67
- this.#footer.update(this.#viewWithExtensionStatuses(view));
68
- this.#queued.update(view.editor.queuedSubmissions);
69
- this.#syncTranscript(view.transcript);
70
- this.editor.setSubmitEnabled(view.lifecycle !== "stopping" && view.lifecycle !== "stopped" && view.lifecycle !== "failed");
71
- this.editor.setThinkingLevel(view.thinkingLevel);
72
- this.invalidate();
73
- }
74
- /**
75
- * Applies one block: its component is created or updated in place and the order grows
76
- * only when the block is new. A streamed chunk costs one component update rather than a
77
- * walk of the whole transcript.
78
- */
79
- applyTranscriptBlock(block) {
80
- this.#blocksById.set(block.id, block);
81
- const component = this.#transcript.get(block.id);
82
- if (component === undefined) {
83
- const created = createPiShellTranscriptComponent(block, this.#cwd, this.#extensionRenderers);
84
- created.setExpanded(this.#toolsExpanded);
85
- this.#transcript.set(block.id, created);
86
- this.#transcriptOrder.push(block.id);
87
- }
88
- else if (component.revision !== block.revision) {
89
- component.update(block);
90
- }
91
- this.invalidate();
92
- }
93
- render(width) {
94
- const queued = this.#view.editor.queuedSubmissions.length === 0 ? [] : this.#queued.render(width);
95
- return [
96
- ...this.#renderDocument(width),
97
- ...queued,
98
- ...this.#renderStatus(width),
99
- ...this.#renderWidgets("aboveEditor", width),
100
- ...this.#inputSurface.render(width),
101
- ...this.#renderWidgets("belowEditor", width),
102
- ...this.#renderFooter(width),
103
- ];
104
- }
105
- layoutRoot() {
106
- const document = layoutPort(width => this.#renderDocument(width), () => this.invalidate());
107
- const queued = layoutPort(width => this.#view.editor.queuedSubmissions.length === 0 ? [] : this.#queued.render(width), () => this.#queued.invalidate());
108
- const aboveWidgets = layoutPort(width => this.#renderWidgets("aboveEditor", width), () => this.#invalidateExtensions());
109
- const status = layoutPort(width => this.#renderStatus(width), () => this.#status.invalidate());
110
- const editor = layoutPort(width => this.#inputSurface.render(width), () => this.#inputSurface.invalidate(), data => this.#inputSurface.handleInput?.(data));
111
- const belowWidgets = layoutPort(width => this.#renderWidgets("belowEditor", width), () => this.#invalidateExtensions());
112
- const footer = layoutPort(width => this.#renderFooter(width), () => (this.#extensionFooter ?? this.#footer).invalidate());
113
- return {
114
- type: "stack",
115
- direction: "vertical",
116
- children: [
117
- {
118
- basis: 0,
119
- grow: 1,
120
- shrink: 1,
121
- minSize: 1,
122
- node: {
123
- type: "scroll",
124
- id: "transcript",
125
- follow: "end",
126
- primary: true,
127
- overscroll: "chain",
128
- scrollbar: "auto",
129
- scrollbarStyle: text => piTheme().bg("scrollbarThumb", text),
130
- child: { type: "component", component: document },
131
- },
132
- },
133
- {
134
- basis: "auto",
135
- grow: 0,
136
- shrink: 1,
137
- minSize: 1,
138
- node: {
139
- type: "stack",
140
- direction: "vertical",
141
- children: [
142
- { shrink: 1, minSize: 0, node: { type: "component", component: queued } },
143
- { shrink: 1, minSize: 0, node: { type: "component", component: status } },
144
- { shrink: 1, minSize: 0, node: { type: "component", component: aboveWidgets } },
145
- { shrink: 1, minSize: 3, node: { type: "component", component: editor } },
146
- { shrink: 1, minSize: 0, node: { type: "component", component: belowWidgets } },
147
- { shrink: 1, minSize: 1, node: { type: "component", component: footer } },
148
- ],
149
- },
150
- },
151
- ],
152
- };
153
- }
154
- #renderDocument(width) {
155
- const transcript = this.#transcriptOrder.flatMap((id, index) => {
156
- const block = this.#blocksById.get(id);
157
- if (!this.#thinkingVisible && block?.kind === "thinking")
158
- return [];
159
- const rows = this.#blockRows(id, block, width);
160
- if (index > 0 && block?.kind === "user")
161
- return ["", ...rows];
162
- return rows;
163
- });
164
- const diagnostics = this.#view.diagnostics;
165
- const startupRows = diagnostics
166
- .filter(diagnostic => diagnostic.code === "engine-startup")
167
- .flatMap(diagnostic => renderPiShellStartupDiagnostic(diagnostic, width));
168
- const packageUpdateRows = diagnostics
169
- .filter(diagnostic => diagnostic.code === "package-updates")
170
- .flatMap(diagnostic => renderPiShellPackageUpdateNotice(diagnostic.message.split("\n").filter(line => line.startsWith("- ")).map(line => line.slice(2)), width));
171
- const diagnosticRows = diagnostics
172
- .filter(diagnostic => diagnostic.code !== "engine-startup" && diagnostic.code !== "package-updates")
173
- .slice(-3)
174
- .flatMap(diagnostic => renderPiShellTranscriptBlock({
175
- id: `diagnostic-${diagnostic.sequence}`,
176
- kind: diagnostic.severity === "error" ? "error" : "system",
177
- status: "finalized",
178
- revision: diagnostic.sequence,
179
- title: diagnostic.code,
180
- text: diagnostic.message,
181
- payload: {},
182
- }, width, this.#cwd));
183
- const resourceRows = [...this.resources.render(width)];
184
- if (resourceRows.at(-1) === "")
185
- resourceRows.pop();
186
- return [
187
- ...startupRows,
188
- ...(this.#extensionHeader ?? this.header).render(width),
189
- ...resourceRows,
190
- ...transcript,
191
- ...packageUpdateRows,
192
- ...diagnosticRows,
193
- ];
194
- }
195
- /**
196
- * Rows for one transcript block. A finalized block renders once for a given revision
197
- * and width and is reused after that, so a frame costs what changed rather than what
198
- * the session has accumulated. A live block, and anything the shell drives itself, is
199
- * rendered every time because its content is still moving.
200
- */
201
- #blockRows(id, block, width) {
202
- const component = this.#transcript.get(id);
203
- if (component === undefined)
204
- return [];
205
- if (block === undefined || block.status !== "finalized")
206
- return component.render(width);
207
- const cached = this.#renderedRows.get(id);
208
- if (cached && cached.width === width && cached.revision === block.revision)
209
- return cached.rows;
210
- const rows = component.render(width);
211
- this.#renderedRows.set(id, { width, revision: block.revision, rows });
212
- return rows;
213
- }
214
- transcriptComponent(id) {
215
- return this.#transcript.get(id);
216
- }
217
- appendWorkflowStatus(message) {
218
- const previousId = this.#lastWorkflowStatusId;
219
- if (previousId !== undefined
220
- && this.#transcriptOrder.at(-1) === previousId
221
- && this.#workflowStatusAnchors.get(previousId) === this.#view.transcript.length) {
222
- this.#workflowStatusMessages.set(previousId, message);
223
- this.invalidate();
224
- return;
225
- }
226
- const id = this.#appendAnchoredWorkflowComponent(width => [
227
- "",
228
- ...renderPiShellStatusText(this.#workflowStatusMessages.get(id) ?? message, width),
229
- ]);
230
- this.#workflowStatusMessages.set(id, message);
231
- this.#lastWorkflowStatusId = id;
232
- }
233
- appendWorkflowResult(result) {
234
- if (result.command === "reload" && result.outcome === "completed") {
235
- this.appendWorkflowStatus(result.message);
236
- return;
237
- }
238
- if (result.command === "session" && result.outcome === "completed" && result.presentation?.kind === "session-info") {
239
- this.#lastWorkflowStatusId = undefined;
240
- const sessionInfo = createPiShellSessionInfo(result.presentation);
241
- this.#appendAnchoredWorkflowComponent(width => sessionInfo.render(width), () => sessionInfo.dispose?.());
242
- return;
243
- }
244
- if (result.command === "hotkeys" && result.outcome === "completed") {
245
- this.#lastWorkflowStatusId = undefined;
246
- const hotkeys = createPiShellHotkeys();
247
- this.#appendAnchoredWorkflowComponent(width => hotkeys.render(width), () => hotkeys.dispose?.());
248
- return;
249
- }
250
- if (result.command === "changelog" && result.outcome === "completed") {
251
- this.#lastWorkflowStatusId = undefined;
252
- const changelog = createPiShellChangelog(result.detail ?? "No changelog entries found.");
253
- this.#appendAnchoredWorkflowComponent(width => changelog.render(width), () => changelog.dispose?.());
254
- return;
255
- }
256
- if (result.outcome === "failed") {
257
- this.#lastWorkflowStatusId = undefined;
258
- const warning = (result.command === "name" && result.message.startsWith("Usage:"))
259
- || (result.command === "reload" && result.message.startsWith("Wait for "));
260
- const prefix = warning ? "Warning" : "Error";
261
- const color = warning ? "warning" : "error";
262
- this.#appendAnchoredWorkflowComponent(() => ["", ` ${piTheme().fg(color, `${prefix}: ${result.message}`)}`]);
263
- return;
264
- }
265
- if (result.outcome === "completed" && (result.command === "quit" || result.command === "compact"))
266
- return;
267
- if (result.command === "new" && result.outcome === "completed") {
268
- this.#lastWorkflowStatusId = undefined;
269
- this.#appendAnchoredWorkflowComponent(() => ["", ` ${piTheme().fg("accent", result.message)}`]);
270
- return;
271
- }
272
- if (result.command === "name" && result.outcome === "completed") {
273
- this.#lastWorkflowStatusId = undefined;
274
- this.#appendAnchoredWorkflowComponent(() => [
275
- ...(result.detail ? ["", ` ${piTheme().fg("warning", `Warning: ${result.detail}`)}`] : []),
276
- "",
277
- ` ${piTheme().fg("dim", result.message)}`,
278
- ]);
279
- return;
280
- }
281
- if (result.command === "debug" && result.outcome === "completed") {
282
- this.#lastWorkflowStatusId = undefined;
283
- this.#appendAnchoredWorkflowComponent(() => [
284
- "",
285
- ` ${piTheme().fg("accent", result.message)}`,
286
- ...(result.detail ? [` ${piTheme().fg("muted", result.detail)}`] : []),
287
- ]);
288
- return;
289
- }
290
- if (result.command === "arminsayshi" && result.outcome === "completed") {
291
- this.#lastWorkflowStatusId = undefined;
292
- const armin = createPiShellArmin(this.#componentRuntime);
293
- this.#appendAnchoredWorkflowComponent(width => ["", ...armin.render(width)], () => armin.dispose?.());
294
- return;
295
- }
296
- if (result.command === "dementedelves" && result.outcome === "completed") {
297
- this.#lastWorkflowStatusId = undefined;
298
- const announcement = createPiShellEarendilAnnouncement();
299
- this.#appendAnchoredWorkflowComponent(width => ["", ...announcement.render(width)], () => announcement.dispose?.());
300
- return;
301
- }
302
- const message = result.command === "share" && result.detail
303
- ? `${result.message}\nGist: ${result.detail}`
304
- : result.message;
305
- this.appendWorkflowStatus(message);
306
- }
307
- appendDaxnuts() {
308
- this.#lastWorkflowStatusId = undefined;
309
- const daxnuts = createPiShellDaxnuts(this.#componentRuntime);
310
- this.#appendAnchoredWorkflowComponent(width => ["", ...daxnuts.render(width)], () => daxnuts.dispose?.());
311
- }
312
- toggleThinkingVisibility() {
313
- this.#thinkingVisible = !this.#thinkingVisible;
314
- this.invalidate();
315
- }
316
- get toolsExpanded() {
317
- return this.#toolsExpanded;
318
- }
319
- setToolsExpanded(expanded) {
320
- this.#setToolsExpanded(expanded);
321
- }
322
- setExtensionWidget(key, component, placement) {
323
- const previous = this.#extensionWidgets.get(key)?.component;
324
- if (component === null)
325
- this.#extensionWidgets.delete(key);
326
- else
327
- this.#extensionWidgets.set(key, { component, placement });
328
- if (previous !== undefined && previous !== component)
329
- previous.dispose?.();
330
- this.invalidate();
331
- }
332
- setExtensionHeader(component) {
333
- if (this.#extensionHeader !== component)
334
- this.#extensionHeader?.dispose?.();
335
- this.#extensionHeader = component;
336
- this.invalidate();
337
- }
338
- setExtensionFooter(component) {
339
- if (this.#extensionFooter !== component)
340
- this.#extensionFooter?.dispose?.();
341
- this.#extensionFooter = component;
342
- this.invalidate();
343
- }
344
- setExtensionStatus(key, text) {
345
- if (text === undefined)
346
- this.#extensionStatuses.delete(key);
347
- else
348
- this.#extensionStatuses.set(key, text);
349
- this.#footer.update(this.#viewWithExtensionStatuses(this.#view));
350
- this.invalidate();
351
- }
352
- setExtensionWorking(message, visible = this.#extensionWorkingVisible) {
353
- this.#extensionWorkingMessage = message;
354
- this.#extensionWorkingVisible = visible;
355
- this.#status.setWorkingOverride(visible ? message : undefined);
356
- this.invalidate();
357
- }
358
- resetWorkflowPresentation() {
359
- for (const id of this.#workflowStatusAnchors.keys()) {
360
- this.#transcript.get(id)?.dispose?.();
361
- this.#transcript.delete(id);
362
- }
363
- this.#workflowStatusAnchors.clear();
364
- this.#workflowStatusMessages.clear();
365
- this.#transcriptOrder = this.#transcriptOrder.filter(id => !id.startsWith("workflow-status-"));
366
- this.#lastWorkflowStatusId = undefined;
367
- this.invalidate();
368
- }
369
- resetExtensionUi() {
370
- this.#extensionHeader?.dispose?.();
371
- this.#extensionFooter?.dispose?.();
372
- for (const { component } of this.#extensionWidgets.values())
373
- component.dispose?.();
374
- this.#extensionHeader = null;
375
- this.#extensionFooter = null;
376
- this.#extensionWidgets.clear();
377
- this.#extensionStatuses.clear();
378
- this.#extensionWorkingMessage = undefined;
379
- this.#status.setWorkingOverride(undefined);
380
- this.#footer.update(this.#viewWithExtensionStatuses(this.#view));
381
- // An extension renderer may have drawn transcript blocks that are now unrendered by it.
382
- this.#renderedRows.clear();
383
- this.invalidate();
384
- }
385
- addExtensionNotification(message, type) {
386
- if (type === "info") {
387
- this.appendWorkflowStatus(message);
388
- return;
389
- }
390
- this.#lastWorkflowStatusId = undefined;
391
- const prefix = type === "warning" ? "Warning" : "Error";
392
- this.#appendAnchoredWorkflowComponent(() => ["", ` ${piTheme().fg(type, `${prefix}: ${message}`)}`]);
393
- }
394
- extensionFooterData() {
395
- return {
396
- getGitBranch: () => this.#view.status.footer?.branch ?? null,
397
- getExtensionStatuses: () => new Map(this.#extensionStatuses),
398
- getAvailableProviderCount: () => this.#view.status.footer?.availableProviderCount ?? 1,
399
- onBranchChange: () => () => { },
400
- };
401
- }
402
- setInputSurface(component, disposePrevious = true) {
403
- const next = component ?? this.editor;
404
- if (next === this.#inputSurface)
405
- return;
406
- this.#inputSurface.setFocused?.(false);
407
- if (disposePrevious && this.#inputSurface !== this.editor)
408
- this.#inputSurface.dispose?.();
409
- this.#inputSurface = next;
410
- this.#inputSurface.setFocused?.(true);
411
- this.invalidate();
412
- }
413
- handleInput(data) {
414
- this.#inputSurface.handleInput?.(data);
415
- }
416
- invalidate() {
417
- this.header.invalidate();
418
- this.resources.invalidate();
419
- for (const component of this.#transcript.values())
420
- component.invalidate();
421
- this.editor.invalidate();
422
- if (this.#inputSurface !== this.editor)
423
- this.#inputSurface.invalidate();
424
- this.#invalidateExtensions();
425
- this.#status.invalidate();
426
- this.#footer.invalidate();
427
- this.#queued.invalidate();
428
- }
429
- setFocused(focused) {
430
- this.#inputSurface.setFocused?.(focused);
431
- }
432
- dispose() {
433
- this.#themeUnsubscribe();
434
- this.header.dispose?.();
435
- this.resources.dispose?.();
436
- for (const component of this.#transcript.values())
437
- component.dispose?.();
438
- this.#transcript.clear();
439
- this.#renderedRows.clear();
440
- if (this.#inputSurface !== this.editor)
441
- this.#inputSurface.dispose?.();
442
- this.#extensionHeader?.dispose?.();
443
- this.#extensionFooter?.dispose?.();
444
- for (const { component } of this.#extensionWidgets.values())
445
- component.dispose?.();
446
- this.#extensionWidgets.clear();
447
- this.editor.dispose?.();
448
- this.#status.dispose?.();
449
- this.#footer.dispose?.();
450
- this.#queued.dispose?.();
451
- }
452
- #syncTranscript(blocks) {
453
- this.#blocksById.clear();
454
- for (const block of blocks)
455
- this.#blocksById.set(block.id, block);
456
- const nextIds = new Set(blocks.map(block => block.id));
457
- for (const [id, component] of this.#transcript) {
458
- if (id.startsWith("workflow-status-") || nextIds.has(id))
459
- continue;
460
- component.dispose?.();
461
- this.#transcript.delete(id);
462
- this.#renderedRows.delete(id);
463
- }
464
- for (const block of blocks) {
465
- const component = this.#transcript.get(block.id);
466
- if (component === undefined) {
467
- const created = createPiShellTranscriptComponent(block, this.#cwd, this.#extensionRenderers);
468
- created.setExpanded(this.#toolsExpanded);
469
- this.#transcript.set(block.id, created);
470
- }
471
- else if (component.revision !== block.revision) {
472
- component.update(block);
473
- }
474
- }
475
- const statusIds = [...this.#workflowStatusAnchors.keys()];
476
- const order = [];
477
- for (let index = 0; index <= blocks.length; index += 1) {
478
- for (const statusId of statusIds) {
479
- if (this.#workflowStatusAnchors.get(statusId) === index)
480
- order.push(statusId);
481
- }
482
- const block = blocks[index];
483
- if (block !== undefined)
484
- order.push(block.id);
485
- }
486
- const placed = new Set(order);
487
- for (const statusId of statusIds) {
488
- if (!placed.has(statusId))
489
- order.push(statusId);
490
- }
491
- this.#transcriptOrder = order;
492
- }
493
- #appendAnchoredWorkflowComponent(render, dispose) {
494
- this.#workflowTranscriptSequence += 1;
495
- const id = `workflow-status-${this.#workflowTranscriptSequence}`;
496
- const component = {
497
- id,
498
- revision: 1,
499
- render,
500
- handleInput() { },
501
- invalidate() { },
502
- update() { },
503
- setExpanded() { },
504
- ...(dispose === undefined ? {} : { dispose }),
505
- };
506
- this.#transcript.set(id, component);
507
- this.#workflowStatusAnchors.set(id, this.#view.transcript.length);
508
- this.#transcriptOrder.push(id);
509
- this.invalidate();
510
- return id;
511
- }
512
- #renderWidgets(placement, width) {
513
- const rows = [...this.#extensionWidgets.values()]
514
- .filter(widget => widget.placement === placement)
515
- .flatMap(widget => widget.component.render(width));
516
- return placement === "aboveEditor" ? ["", ...rows] : rows;
517
- }
518
- #renderStatus(width) {
519
- return this.#status.render(width);
520
- }
521
- #renderFooter(width) {
522
- return (this.#extensionFooter ?? this.#footer).render(width);
523
- }
524
- #viewWithExtensionStatuses(view) {
525
- const footer = view.status.footer;
526
- const statuses = new Map(footer?.extensionStatuses ?? []);
527
- for (const [key, text] of this.#extensionStatuses)
528
- statuses.set(key, text);
529
- return {
530
- ...view,
531
- status: {
532
- ...view.status,
533
- footer: {
534
- branch: footer?.branch ?? null,
535
- sessionName: footer?.sessionName ?? null,
536
- availableProviderCount: footer?.availableProviderCount ?? 1,
537
- extensionStatuses: [...statuses],
538
- },
539
- },
540
- };
541
- }
542
- #invalidateExtensions() {
543
- this.#extensionHeader?.invalidate();
544
- this.#extensionFooter?.invalidate();
545
- for (const { component } of this.#extensionWidgets.values())
546
- component.invalidate();
547
- }
548
- #setToolsExpanded(expanded) {
549
- this.#toolsExpanded = expanded;
550
- this.header.setExpanded(expanded);
551
- this.resources.setExpanded(expanded);
552
- for (const component of this.#transcript.values())
553
- component.setExpanded(expanded);
554
- // Expansion changes what a block draws without changing its revision.
555
- this.#renderedRows.clear();
556
- this.invalidate();
557
- }
558
- }
559
- function layoutPort(render, invalidate, handleInput) {
560
- return {
561
- render,
562
- invalidate,
563
- ...(handleInput === undefined ? {} : { handleInput }),
564
- };
565
- }
566
- function shellResourceEntries(backend) {
567
- const resources = backend.nonVisualResources().map(resource => ({
568
- section: resource.kind === "skill"
569
- ? "Skills"
570
- : resource.kind === "prompt-template"
571
- ? "Prompts"
572
- : resource.kind === "theme"
573
- ? "Themes"
574
- : "Context",
575
- label: resource.kind === "prompt-template"
576
- ? `/${resource.label}`
577
- : resource.kind === "agent-context" || resource.kind === "system-prompt"
578
- ? compactResourceLabel(resource.sourcePath ?? resource.label)
579
- : resource.label,
580
- sourcePath: resource.sourcePath,
581
- diagnostic: resource.diagnostic,
582
- }));
583
- const extensions = backend.extensionResources().filter(extension => !extension.hidden);
584
- const loadedExtensions = extensions.filter(extension => extension.diagnostic === null);
585
- const extensionLabels = compactExtensionLabels(loadedExtensions);
586
- for (const extension of extensions) {
587
- const labelIndex = loadedExtensions.indexOf(extension);
588
- resources.push({
589
- section: "Extensions",
590
- label: extensionLabels[labelIndex] ?? compactResourceLabel(extension.sourcePath ?? extension.resolvedPath ?? "extension"),
591
- sourcePath: extension.sourcePath ?? extension.resolvedPath,
592
- diagnostic: extension.diagnostic,
593
- });
594
- }
595
- return resources;
596
- }
597
- function compactResourceLabel(path) {
598
- const segments = compactPathSegments(path);
599
- const leaf = segments.at(-1) ?? path;
600
- if ((leaf === "index.ts" || leaf === "index.js") && segments.length > 1)
601
- return segments.at(-2) ?? leaf;
602
- return leaf;
603
- }
604
- /**
605
- * Pinned from InteractiveMode's compact extension-label helpers at Pi commit
606
- * 914cf1472e715297caa30db4b9535d534a9eb718. The source metadata crosses an
607
- * A1-owned boundary first; the owned shell never inspects Pi's private root.
608
- */
609
- function compactExtensionLabels(extensions) {
610
- const localExtensions = extensions
611
- .filter(extension => !isPackageExtensionSource(extension.sourceInfo))
612
- .map(extension => {
613
- const path = extension.sourcePath ?? extension.resolvedPath ?? "extension";
614
- const segments = compactPathSegments(path);
615
- if (segments.length > 1 && (segments.at(-1) === "index.ts" || segments.at(-1) === "index.js"))
616
- segments.pop();
617
- return { extension, segments };
618
- });
619
- return extensions.map(extension => {
620
- const resourcePath = extension.sourcePath ?? extension.resolvedPath ?? "extension";
621
- if (isPackageExtensionSource(extension.sourceInfo)) {
622
- return compactPackageExtensionLabel(resourcePath, extension.sourceInfo);
623
- }
624
- const localIndex = localExtensions.findIndex(item => item.extension === extension);
625
- const segments = localExtensions[localIndex]?.segments;
626
- if (!segments || segments.length === 0)
627
- return compactResourceLabel(resourcePath);
628
- for (let count = 1; count <= segments.length; count += 1) {
629
- const candidate = segments.slice(-count).join("/");
630
- if (localExtensions.every((item, itemIndex) => itemIndex === localIndex || item.segments.slice(-count).join("/") !== candidate)) {
631
- return candidate;
632
- }
633
- }
634
- return segments.join("/");
635
- });
636
- }
637
- function compactPackageExtensionLabel(resourcePath, sourceInfo) {
638
- const sourceLabel = compactPackageSourceLabel(sourceInfo.source);
639
- if (!sourceLabel)
640
- return compactResourceLabel(resourcePath);
641
- const shortPath = shortPackagePath(resourcePath, sourceInfo).replaceAll("\\", "/");
642
- const packagePath = shortPath.startsWith("extensions/") ? shortPath.slice("extensions/".length) : shortPath;
643
- const slash = packagePath.lastIndexOf("/");
644
- const fileName = slash < 0 ? packagePath : packagePath.slice(slash + 1);
645
- const directory = slash < 0 ? "" : packagePath.slice(0, slash);
646
- const extension = fileName.lastIndexOf(".");
647
- const name = extension <= 0 ? fileName : fileName.slice(0, extension);
648
- if (name === "index")
649
- return !directory || directory === "." ? sourceLabel : `${sourceLabel}:${directory}`;
650
- return `${sourceLabel}:${packagePath}`;
651
- }
652
- function compactPackageSourceLabel(source) {
653
- if (source.startsWith("npm:"))
654
- return source.slice("npm:".length) || source;
655
- if (!source.startsWith("git:"))
656
- return source;
657
- const gitSource = source.slice("git:".length).trim();
658
- let repositoryPath;
659
- const scpLike = gitSource.match(/^git@[^:]+:(.+)$/);
660
- if (scpLike?.[1]) {
661
- repositoryPath = scpLike[1];
662
- }
663
- else if (/^[a-z]+:\/\//i.test(gitSource)) {
664
- try {
665
- repositoryPath = new URL(gitSource).pathname.replace(/^\/+/, "");
666
- }
667
- catch {
668
- return source;
669
- }
670
- }
671
- else {
672
- const slash = gitSource.indexOf("/");
673
- if (slash >= 0)
674
- repositoryPath = gitSource.slice(slash + 1);
675
- }
676
- if (!repositoryPath)
677
- return source;
678
- const ref = repositoryPath.indexOf("@");
679
- const withoutRef = ref < 0 ? repositoryPath : repositoryPath.slice(0, ref);
680
- return withoutRef.replace(/\.git$/, "") || source;
681
- }
682
- function shortPackagePath(resourcePath, sourceInfo) {
683
- const fullPath = normalizeResourcePath(resourcePath);
684
- const baseDir = sourceInfo.baseDir === null ? undefined : normalizeResourcePath(sourceInfo.baseDir).replace(/\/$/, "");
685
- if (baseDir) {
686
- const npmRoot = baseDir.match(/^(.*\/node_modules)\/(@?[^/]+(?:\/[^/]+)?)$/);
687
- if (npmRoot?.[1] && fullPath.startsWith(`${npmRoot[1]}/`))
688
- return relativeResourcePath(baseDir, fullPath);
689
- if (fullPath === baseDir)
690
- return ".";
691
- if (fullPath.startsWith(`${baseDir}/`))
692
- return fullPath.slice(baseDir.length + 1);
693
- }
694
- const npmMatch = fullPath.match(/node_modules\/(@?[^/]+(?:\/[^/]+)?)\/(.*)/);
695
- if (npmMatch?.[2] && sourceInfo.source.startsWith("npm:"))
696
- return npmMatch[2];
697
- const gitMatch = fullPath.match(/git\/[^/]+\/[^/]+\/(.*)/);
698
- if (gitMatch?.[1] && sourceInfo.source.startsWith("git:"))
699
- return gitMatch[1];
700
- return resourcePath;
701
- }
702
- function relativeResourcePath(from, to) {
703
- const fromSegments = from.split("/").filter(Boolean);
704
- const toSegments = to.split("/").filter(Boolean);
705
- let common = 0;
706
- while (common < fromSegments.length && common < toSegments.length
707
- && fromSegments[common]?.toLowerCase() === toSegments[common]?.toLowerCase())
708
- common += 1;
709
- return [...fromSegments.slice(common).map(() => ".."), ...toSegments.slice(common)].join("/") || ".";
710
- }
711
- function compactPathSegments(path) {
712
- return normalizeResourcePath(path).split("/").filter(segment => segment.length > 0 && segment !== "~");
713
- }
714
- function normalizeResourcePath(path) {
715
- return path.replaceAll("\\", "/");
716
- }
717
- function isPackageExtensionSource(sourceInfo) {
718
- const source = sourceInfo?.source ?? "";
719
- return source.startsWith("npm:") || source.startsWith("git:");
720
- }
1
+ import { PRODUCT_TEXT } from "../../../product-identity.js";
2
+ import { MOUSE_TRACKING_OFF, MOUSE_TRACKING_ON, parseMouseInput } from "../../../ui/components/index.js";
3
+ import { PINNED_PI_HIDDEN_COMMAND_NAMES, PINNED_PI_WORKFLOW_COMMAND_NAMES, } from "../engine/index.js";
4
+ import { createPiExtensionUiBridge, createPiQueuedInputStatus, createPiShellArmin, createPiShellAuthProviderSelector, createPiShellChangelog, createPiShellDaxnuts, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellEditor, createPiShellExtensionSelector, createPiShellFooter, createPiShellHeader, createPiShellHotkeys, createPiShellLoadedResources, createPiShellLoginDialog, createPiShellModelSelector, createPiShellOperationLoader, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionInfo, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellStatus, createPiShellTranscriptComponent, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, onPiThemeChange, piTheme, renderPiShellPackageUpdateNotice, renderPiShellStartupDiagnostic, renderPiShellStatusText, renderPiShellTranscriptBlock, } from "../components/index.js";
5
+ import { DamageAwareTerminalAdapter, PiTuiRuntimeAdapter, classifyPiTuiInput, } from "../tui-runtime/index.js";
6
+ import { canonicalizeClipboardImage } from "./clipboard-image.js";
7
+ import { STREAM_PRESENTATION_INTERVAL_MS, StreamPresentationCoalescer, } from "./stream-presentation-coalescer.js";
8
+ import { preloadSystemClipboard, readSystemClipboardContent, writeSystemClipboardText, } from "./system-clipboard.js";
9
+ import { OwnedUiSessionShellRoot, shellResourceEntries, } from "./session-shell-root.js";
10
+ export { OwnedUiSessionShellRoot } from "./session-shell-root.js";
11
+ /** Coordinates backend, owned presentation, and Pi TUI lifecycles for one interactive session. */
721
12
  export class OwnedUiSessionShell {
722
13
  backend;
723
14
  root;
@@ -729,14 +20,28 @@ export class OwnedUiSessionShell {
729
20
  #stopped;
730
21
  #resolveStopped;
731
22
  #dialogId;
23
+ #dialogSource;
732
24
  #routeHost;
733
25
  #dialogHandle;
734
26
  #sequence = 0;
735
27
  #started = false;
736
28
  #disposed = false;
737
29
  #pointerReporting = false;
30
+ #customViewport;
31
+ #damageTerminal;
32
+ #streamPresentation;
33
+ #removeViewportPreInput;
34
+ #unsubscribeSettings;
35
+ #unbindPiSettings;
36
+ #unbindTerminalSettings;
37
+ #unbindShutdownSettings;
38
+ #terminalProgressEnabled = false;
39
+ #showImages = true;
40
+ #imageWidthCells = 80;
41
+ #fullscreenExitOutput = "transcript";
738
42
  #compactionQueue = [];
739
43
  #lastClearTime = 0;
44
+ #lastEscapeTime = 0;
740
45
  #activeLoginDialog;
741
46
  #sessionGeneration;
742
47
  constructor(options) {
@@ -744,14 +49,26 @@ export class OwnedUiSessionShell {
744
49
  this.#sessionGeneration = this.backend.sessionGeneration;
745
50
  this.#cwd = options.cwd;
746
51
  this.#routeHost = options.routeHost ?? null;
52
+ this.#customViewport = options.sessionLayout === "custom-viewport";
53
+ if (this.#customViewport && options.clipboard === undefined)
54
+ preloadSystemClipboard();
747
55
  this.#stopped = new Promise(resolve => {
748
56
  this.#resolveStopped = resolve;
749
57
  });
750
58
  let runtime;
59
+ let damageTerminal;
60
+ let streamPresentation;
61
+ let pendingClipboardWrite = Promise.resolve();
751
62
  this.root = new OwnedUiSessionShellRoot(this.backend.view(), options.cwd, {
752
63
  getColumns: () => runtime?.viewport().columns ?? options.terminal?.columns ?? 80,
753
64
  getRows: () => runtime?.viewport().rows ?? options.terminal?.rows ?? 24,
754
- requestRender: () => runtime?.requestRender(),
65
+ requestRender: force => runtime?.requestRender(force),
66
+ onViewportFrame: frame => damageTerminal?.arm(frame.descriptor, {
67
+ overlayActive: runtime?.hasOverlay() ?? false,
68
+ selectionActive: this.root.hasActiveSelection(),
69
+ replacementSurfaceActive: !this.root.usesDefaultInputSurface(),
70
+ }),
71
+ enableDockInputReuse: options.inputPresentation?.viewportReuse !== false,
755
72
  onSubmit: text => { void this.submit(text); },
756
73
  onInterrupt: () => { void this.interrupt(); },
757
74
  onClear: () => { void this.clearOrExit(); },
@@ -766,19 +83,187 @@ export class OwnedUiSessionShell {
766
83
  onMessageCopy: () => { void this.runWorkflow({ command: "copy", argument: "" }); },
767
84
  onFollowUp: () => { void this.queueFollowUp(); },
768
85
  onDequeue: () => this.restoreQueuedInput(),
86
+ onCopyText: text => {
87
+ runtime?.writeControl(`\u001b]52;c;${Buffer.from(text, "utf8").toString("base64")}\u0007`);
88
+ const write = options.clipboard === undefined
89
+ ? writeSystemClipboardText(text)
90
+ : options.clipboard.writeText?.(text) ?? Promise.resolve();
91
+ pendingClipboardWrite = write.catch(() => { });
92
+ },
93
+ readClipboardContent: async () => {
94
+ await pendingClipboardWrite;
95
+ if (options.clipboard === undefined)
96
+ return readSystemClipboardContent();
97
+ try {
98
+ const image = await options.clipboard.readImage?.();
99
+ if (image !== null && image !== undefined) {
100
+ const canonical = canonicalizeClipboardImage(image);
101
+ if (canonical !== null)
102
+ return { kind: "image", ...canonical };
103
+ }
104
+ }
105
+ catch {
106
+ // Compatibility: treat an unavailable or malformed image as text-capable clipboard input.
107
+ }
108
+ const text = await options.clipboard.readText();
109
+ return text === null ? null : { kind: "text", text };
110
+ },
769
111
  }, {
770
112
  ...options.startup,
771
113
  resources: options.startup?.resources ?? shellResourceEntries(this.backend),
772
114
  }, this.backend.agentDir, {
773
115
  getMessageRenderer: customType => this.backend.pinnedMessageRenderer(customType),
774
116
  getToolDefinition: toolName => this.backend.pinnedToolDefinition(toolName),
117
+ }, options.sessionLayout, {
118
+ resolve: assetId => this.backend.resolveTranscriptImage(assetId),
775
119
  });
776
- const tuiMode = this.backend.disposed ? "regular" : this.backend.pinnedSettingsSnapshot().tuiMode;
777
- const runtimeOptions = options.terminal === undefined
778
- ? { root: this.root, mode: tuiMode, layoutRoot: this.root.layoutRoot(), hardwareCursor: this.backend.view().terminal.hardwareCursor }
779
- : { root: this.root, mode: tuiMode, layoutRoot: this.root.layoutRoot(), terminal: options.terminal, hardwareCursor: this.backend.view().terminal.hardwareCursor };
120
+ // Invariant: bare A1 owns a bounded viewport and therefore always runs on the alternate
121
+ // fullscreen surface. The pinned comparison profiles still honor Pi's mode.
122
+ const tuiMode = this.#customViewport
123
+ ? "fullscreen"
124
+ : this.backend.disposed ? "regular" : this.backend.pinnedSettingsSnapshot().tuiMode;
125
+ const runtimeOptions = {
126
+ root: this.root,
127
+ mode: tuiMode,
128
+ ...(this.#customViewport ? {
129
+ decorateTerminal: (terminal) => {
130
+ damageTerminal = new DamageAwareTerminalAdapter(terminal, {
131
+ regionalScroll: process.env.TERM !== "dumb",
132
+ onResize: () => streamPresentation?.noteImmediatePresentation(),
133
+ });
134
+ return damageTerminal;
135
+ },
136
+ ...(options.inputPresentation?.coordination === false ? {} : {
137
+ inputCoordination: {
138
+ classify: (data, focusedOverlay) => classifyPiTuiInput(data, focusedOverlay ?? this.root.inputCoordinationSurface()),
139
+ onReceipt: () => streamPresentation?.noteImmediatePresentation(),
140
+ ...(options.inputPresentation?.scheduler === undefined
141
+ ? {}
142
+ : { scheduler: options.inputPresentation.scheduler }),
143
+ },
144
+ }),
145
+ } : { layoutRoot: this.root.layoutRoot() }),
146
+ ...(options.inputPresentation?.onEvent === undefined ? {} : {
147
+ inputDiagnostics: {
148
+ onEvent: options.inputPresentation.onEvent,
149
+ ...(options.inputPresentation.now === undefined ? {} : { now: options.inputPresentation.now }),
150
+ },
151
+ }),
152
+ ...(options.terminal === undefined ? {} : { terminal: options.terminal }),
153
+ hardwareCursor: this.backend.view().terminal.hardwareCursor,
154
+ };
780
155
  runtime = new PiTuiRuntimeAdapter(runtimeOptions);
781
156
  this.runtime = runtime;
157
+ this.#damageTerminal = damageTerminal ?? null;
158
+ const presentationInterval = options.streamPresentation?.intervalMs ?? STREAM_PRESENTATION_INTERVAL_MS;
159
+ streamPresentation = options.streamPresentation?.scheduler === undefined
160
+ ? new StreamPresentationCoalescer(() => this.runtime.requestRender(), presentationInterval)
161
+ : new StreamPresentationCoalescer(() => this.runtime.requestRender(), presentationInterval, options.streamPresentation.scheduler);
162
+ this.#streamPresentation = streamPresentation;
163
+ const initialPiSettings = this.backend.pinnedSettingsSnapshot();
164
+ this.runtime.setHardwareCursor(initialPiSettings.showHardwareCursor);
165
+ this.runtime.setClearOnShrink(initialPiSettings.clearOnShrink);
166
+ this.#terminalProgressEnabled = initialPiSettings.showTerminalProgress;
167
+ this.#fullscreenExitOutput = initialPiSettings.fullscreenExitOutput;
168
+ this.#unbindShutdownSettings = this.backend.bindSettingsOwner("shutdown", {
169
+ fullscreenExitOutput: { apply() { } },
170
+ });
171
+ this.#unbindTerminalSettings = this.backend.bindSettingsOwner("terminal", {
172
+ showHardwareCursor: { apply: value => {
173
+ if (typeof value !== "boolean")
174
+ throw new TypeError("Hardware cursor setting is invalid");
175
+ this.runtime.setHardwareCursor(value);
176
+ } },
177
+ clearOnShrink: { apply: value => {
178
+ if (typeof value !== "boolean")
179
+ throw new TypeError("Clear-on-shrink setting is invalid");
180
+ this.runtime.setClearOnShrink(value);
181
+ } },
182
+ showTerminalProgress: { apply: value => {
183
+ if (typeof value !== "boolean")
184
+ throw new TypeError("Terminal progress setting is invalid");
185
+ this.#terminalProgressEnabled = value;
186
+ this.#syncTerminalProgress(this.view());
187
+ } },
188
+ });
189
+ this.#removeViewportPreInput = this.#customViewport
190
+ ? this.runtime.addPreInputListener(data => {
191
+ if (options.inputPresentation?.coordination === false)
192
+ this.#streamPresentation.noteImmediatePresentation();
193
+ // Invariant: an overlay or editor-replacement screen owns its entire pointer
194
+ // surface. Letting the transcript pre-router inspect those reports
195
+ // steals settings value menus and numeric +/- controls before the
196
+ // settings app can receive them.
197
+ if (this.runtime.hasOverlay() || !this.root.usesDefaultInputSurface())
198
+ return undefined;
199
+ const routed = this.root.handleViewportPreInput(data, true);
200
+ if (routed.copyText !== undefined) {
201
+ this.runtime.writeControl(`\u001b]52;c;${Buffer.from(routed.copyText, "utf8").toString("base64")}\u0007`);
202
+ }
203
+ if (!routed.consumed)
204
+ return routed.data === data ? undefined : { data: routed.data };
205
+ return routed.data.length === 0 ? { consume: true } : { data: routed.data };
206
+ })
207
+ : () => { };
208
+ const applyViewportSettings = () => {
209
+ const snapshot = options.viewportSettings?.snapshot();
210
+ this.root.setViewportConfig(snapshot ?? {
211
+ scrollbarAppearance: "auto",
212
+ scrollbarStyle: "thin",
213
+ scrollbarSpeed: "normal",
214
+ });
215
+ };
216
+ applyViewportSettings();
217
+ this.#unsubscribeSettings = this.#customViewport && options.viewportSettings
218
+ ? options.viewportSettings.onChange(settings => this.root.setViewportConfig(settings))
219
+ : () => { };
220
+ this.root.setEditorPaddingX(initialPiSettings.editorPaddingX);
221
+ this.root.setAutocompleteMaxVisible(initialPiSettings.autocompleteMaxVisible);
222
+ this.root.setOutputPad(initialPiSettings.outputPad);
223
+ this.root.setHideThinkingBlock(initialPiSettings.hideThinkingBlock);
224
+ this.root.setMermaidRenderingMode(initialPiSettings.mermaidRenderingMode);
225
+ this.#showImages = initialPiSettings.showImages;
226
+ this.#imageWidthCells = initialPiSettings.imageWidthCells;
227
+ this.root.setImagePresentation(this.#showImages, this.#imageWidthCells);
228
+ this.#unbindPiSettings = this.backend.bindSettingsOwner("shell", {
229
+ editorPaddingX: { apply: value => {
230
+ if (typeof value !== "number")
231
+ throw new TypeError("Editor padding is invalid");
232
+ this.root.setEditorPaddingX(value);
233
+ } },
234
+ autocompleteMaxVisible: { apply: value => {
235
+ if (typeof value !== "number")
236
+ throw new TypeError("Autocomplete maximum is invalid");
237
+ this.root.setAutocompleteMaxVisible(value);
238
+ } },
239
+ outputPad: { apply: value => {
240
+ if (value !== 0 && value !== 1)
241
+ throw new TypeError("Output padding is invalid");
242
+ this.root.setOutputPad(value);
243
+ } },
244
+ hideThinkingBlock: { apply: value => {
245
+ if (typeof value !== "boolean")
246
+ throw new TypeError("Thinking-block visibility is invalid");
247
+ this.root.setHideThinkingBlock(value);
248
+ } },
249
+ mermaidRenderingMode: { apply: value => {
250
+ if (value !== "off" && value !== "final" && value !== "streaming")
251
+ throw new TypeError("Mermaid mode is invalid");
252
+ this.root.setMermaidRenderingMode(value);
253
+ } },
254
+ showImages: { apply: value => {
255
+ if (typeof value !== "boolean")
256
+ throw new TypeError("Image visibility is invalid");
257
+ this.#showImages = value;
258
+ this.root.setImagePresentation(this.#showImages, this.#imageWidthCells);
259
+ } },
260
+ imageWidthCells: { apply: value => {
261
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1)
262
+ throw new TypeError("Image width is invalid");
263
+ this.#imageWidthCells = value;
264
+ this.root.setImagePresentation(this.#showImages, this.#imageWidthCells);
265
+ } },
266
+ });
782
267
  this.#extensionBridge = createPiExtensionUiBridge({
783
268
  runtime: {
784
269
  getColumns: () => this.runtime.viewport().columns,
@@ -786,7 +271,7 @@ export class OwnedUiSessionShell {
786
271
  requestRender: () => this.runtime.requestRender(),
787
272
  },
788
273
  agentDir: this.backend.agentDir,
789
- setInputSurface: component => this.root.setInputSurface(component),
274
+ setInputSurface: component => this.root.setInputSurface(component, true, "opaque"),
790
275
  showOverlay: (component, overlayOptions) => this.runtime.showOverlay(component, overlayOptions),
791
276
  listenInput: handler => this.runtime.addInputListener(handler),
792
277
  replaceWidget: (key, component, placement) => this.root.setExtensionWidget(key, component, placement),
@@ -800,7 +285,7 @@ export class OwnedUiSessionShell {
800
285
  setEditorText: text => this.root.editor.setText(text),
801
286
  pasteToEditor: text => this.root.editor.insertText(text),
802
287
  addAutocompleteProvider: factory => this.root.editor.addAutocompleteProvider(factory),
803
- setCustomEditor: component => this.root.setInputSurface(component),
288
+ setCustomEditor: component => this.root.setInputSurface(component, true, "opaque"),
804
289
  getFooterData: () => this.root.extensionFooterData(),
805
290
  getToolsExpanded: () => this.root.toolsExpanded,
806
291
  setToolsExpanded: expanded => this.root.setToolsExpanded(expanded),
@@ -813,12 +298,18 @@ export class OwnedUiSessionShell {
813
298
  });
814
299
  this.root.editor.setAutocompleteCommands(this.backend.workflowAutocompleteCommands());
815
300
  this.#unsubscribe = this.backend.onEvent(event => {
816
- // A streamed chunk names one block, and touching only that block is what keeps the
301
+ // Performance: a streamed chunk names one block, and touching only that block is what keeps the
817
302
  // cost of a chunk the same in a long session as in a new one. Everything else
818
303
  // resynchronizes the view, which is cheap next to re-reading the transcript.
304
+ if (event.type === "agent-run-started")
305
+ this.root.resumeViewportFollowing();
306
+ if (event.type === "assistant-message-completed")
307
+ this.root.noteCompletedAssistantMessage();
308
+ const semanticOnly = event.type === "agent-run-started" || event.type === "assistant-message-completed";
819
309
  const view = event.type === "transcript-block" && this.#sessionGeneration === this.backend.sessionGeneration
820
310
  ? this.#syncBlock(event.block)
821
- : this.#syncView();
311
+ : semanticOnly ? this.view() : this.#syncView();
312
+ this.#syncTerminalProgress(view);
822
313
  if (view.lifecycle === "ready" && this.#compactionQueue.length > 0)
823
314
  void this.#flushCompactionQueue();
824
315
  if (event.type === "session-lifecycle" && event.lifecycle === "stopped")
@@ -830,11 +321,17 @@ export class OwnedUiSessionShell {
830
321
  view() {
831
322
  return this.backend.view();
832
323
  }
324
+ damagePresentationDecision() {
325
+ return this.#damageTerminal?.lastDecision ?? null;
326
+ }
833
327
  start() {
834
328
  if (this.#started)
835
329
  return;
836
330
  this.#started = true;
837
331
  this.runtime.start();
332
+ this.#syncTerminalProgress(this.view());
333
+ if (this.#customViewport)
334
+ this.#setPointerReporting(true);
838
335
  void this.backend.bindExtensionUi(this.#extensionBridge.context, () => { void this.shutdown(); });
839
336
  this.#syncView();
840
337
  }
@@ -847,16 +344,18 @@ export class OwnedUiSessionShell {
847
344
  return this.#stopped;
848
345
  }
849
346
  async submit(text) {
850
- const input = text.trim();
851
- if (!input)
347
+ const displayInput = text.trim();
348
+ if (!displayInput)
852
349
  return { outcome: "completed", diagnostic: null };
853
- if (input.startsWith("/"))
854
- return this.#slashCommand(input);
350
+ if (displayInput.startsWith("/"))
351
+ return this.#slashCommand(displayInput);
352
+ const prepared = this.root.preparePromptSubmission(displayInput);
353
+ const input = prepared.text.trim();
855
354
  if (input.startsWith("!")) {
856
355
  const excludeFromContext = input.startsWith("!!");
857
356
  const command = input.slice(excludeFromContext ? 2 : 1).trim();
858
357
  if (command) {
859
- this.root.editor.addToHistory(input);
358
+ this.root.editor.addToHistory(displayInput);
860
359
  try {
861
360
  const result = await this.backend.executeBashWorkflow(command, excludeFromContext);
862
361
  const workflow = {
@@ -878,19 +377,25 @@ export class OwnedUiSessionShell {
878
377
  }
879
378
  }
880
379
  if (this.view().status.workingMessage?.startsWith("Compacting") === true) {
881
- this.root.editor.addToHistory(input);
882
- this.#compactionQueue.push({ text: input, type: "steer" });
380
+ this.root.editor.addToHistory(displayInput);
381
+ this.#compactionQueue.push({
382
+ text: input,
383
+ type: "steer",
384
+ ...(prepared.images.length === 0 ? {} : { images: prepared.images }),
385
+ });
883
386
  this.root.appendWorkflowResult({ command: "compact", outcome: "completed", message: `Queued during compaction: ${input}` });
884
387
  this.runtime.requestRender();
885
388
  return { outcome: "completed", diagnostic: null };
886
389
  }
887
390
  const type = this.view().lifecycle === "busy" ? "steer" : "prompt";
888
- this.root.editor.addToHistory(input);
391
+ this.root.editor.addToHistory(displayInput);
392
+ this.root.resumeViewportFollowing();
889
393
  return this.#execute({
890
394
  type,
891
395
  correlationId: this.#correlation(type),
892
396
  sessionId: this.backend.sessionId,
893
397
  text: input,
398
+ ...(prepared.images.length === 0 ? {} : { images: prepared.images }),
894
399
  });
895
400
  }
896
401
  async clearOrExit(now = Date.now()) {
@@ -901,14 +406,29 @@ export class OwnedUiSessionShell {
901
406
  this.runtime.requestRender();
902
407
  return { outcome: "completed", diagnostic: null };
903
408
  }
904
- async interrupt() {
409
+ async interrupt(now = Date.now()) {
905
410
  if (this.view().lifecycle === "busy")
906
411
  return this.abort();
907
- if (this.root.editor.getText().length > 0) {
412
+ if (this.root.editor.getText().trim().length > 0) {
908
413
  this.root.editor.setText("");
909
414
  return { outcome: "completed", diagnostic: null };
910
415
  }
911
- return { outcome: "rejected", diagnostic: "nothing to interrupt" };
416
+ // Compatibility: match Pi: with an empty editor, two escapes inside 500 ms open the
417
+ // configured session navigator. The first escape intentionally does not
418
+ // interrupt or mutate the prompt.
419
+ const action = this.backend.pinnedSettingsSnapshot().doubleEscapeAction;
420
+ if (action === "none")
421
+ return { outcome: "rejected", diagnostic: "nothing to interrupt" };
422
+ if (now - this.#lastEscapeTime < 500) {
423
+ this.#lastEscapeTime = 0;
424
+ if (action === "tree")
425
+ this.showTreeSelector();
426
+ else
427
+ this.showForkSelector();
428
+ return { outcome: "completed", diagnostic: null };
429
+ }
430
+ this.#lastEscapeTime = now;
431
+ return { outcome: "completed", diagnostic: null };
912
432
  }
913
433
  async abort() {
914
434
  return this.#execute(this.#simple("abort"));
@@ -960,13 +480,20 @@ export class OwnedUiSessionShell {
960
480
  return workflowAdapterResult(result);
961
481
  }
962
482
  async queueFollowUp() {
963
- const text = this.root.editor.getText().trim();
964
- if (!text)
483
+ const displayInput = this.root.editor.getText().trim();
484
+ if (!displayInput)
965
485
  return rejected("nothing to queue");
966
- this.root.editor.addToHistory(text);
486
+ const prepared = this.root.preparePromptSubmission(displayInput);
487
+ const text = prepared.text.trim();
488
+ this.root.editor.addToHistory(displayInput);
967
489
  this.root.editor.setText("");
490
+ this.root.resumeViewportFollowing();
968
491
  if (this.view().status.workingMessage?.startsWith("Compacting") === true) {
969
- this.#compactionQueue.push({ text, type: "follow-up" });
492
+ this.#compactionQueue.push({
493
+ text,
494
+ type: "follow-up",
495
+ ...(prepared.images.length === 0 ? {} : { images: prepared.images }),
496
+ });
970
497
  return { outcome: "completed", diagnostic: null };
971
498
  }
972
499
  return this.#execute({
@@ -974,6 +501,7 @@ export class OwnedUiSessionShell {
974
501
  correlationId: this.#correlation("follow-up"),
975
502
  sessionId: this.backend.sessionId,
976
503
  text,
504
+ ...(prepared.images.length === 0 ? {} : { images: prepared.images }),
977
505
  });
978
506
  }
979
507
  restoreQueuedInput() {
@@ -986,6 +514,8 @@ export class OwnedUiSessionShell {
986
514
  }
987
515
  showSelector(title, options, onSelect, onCancel) {
988
516
  this.#dialogHandle?.hide();
517
+ this.#dialogSource = "local";
518
+ this.#dialogId = undefined;
989
519
  const component = createPiShellSelector({
990
520
  title,
991
521
  options,
@@ -993,14 +523,21 @@ export class OwnedUiSessionShell {
993
523
  onSelect(id);
994
524
  this.#dialogHandle?.hide();
995
525
  this.#dialogHandle = undefined;
526
+ this.#dialogSource = undefined;
996
527
  },
997
528
  onCancel: () => {
998
529
  this.#dialogHandle?.hide();
999
530
  this.#dialogHandle = undefined;
531
+ this.#dialogSource = undefined;
1000
532
  onCancel?.();
1001
533
  },
1002
534
  });
1003
- const handle = this.runtime.showOverlay(component, { width: "70%", maxHeight: "80%", anchor: "center" });
535
+ const handle = this.runtime.showOverlay(component, {
536
+ width: "70%",
537
+ maxHeight: "80%",
538
+ anchor: "center",
539
+ inputCoordination: "owned",
540
+ });
1004
541
  this.#dialogHandle = handle;
1005
542
  return handle;
1006
543
  }
@@ -1200,6 +737,10 @@ export class OwnedUiSessionShell {
1200
737
  return;
1201
738
  }
1202
739
  if (callback === "onTuiModeChange") {
740
+ // Invariant: the custom bare-A1 surface is permanently fullscreen; this callback
741
+ // remains available only to pinned comparison profiles.
742
+ if (this.#customViewport)
743
+ return;
1203
744
  if (value !== "regular" && value !== "fullscreen")
1204
745
  return;
1205
746
  if (!this.runtime.switchMode(value)) {
@@ -1207,13 +748,18 @@ export class OwnedUiSessionShell {
1207
748
  this.runtime.requestRender();
1208
749
  return;
1209
750
  }
751
+ if (this.#customViewport) {
752
+ this.#pointerReporting = false;
753
+ this.#setPointerReporting(true);
754
+ }
1210
755
  }
1211
- const result = this.backend.applyPinnedSettingValue(callback, value);
1212
- if (result.outcome === "failed")
1213
- this.root.appendWorkflowResult(result);
1214
- else if (callback === "onTuiModeChange")
1215
- this.root.appendWorkflowStatus(`TUI mode: ${value}`);
1216
- this.runtime.requestRender();
756
+ void this.backend.applyPinnedSettingValue(callback, value).then(result => {
757
+ if (result.outcome === "failed")
758
+ this.root.appendWorkflowResult(result);
759
+ else if (callback === "onTuiModeChange")
760
+ this.root.appendWorkflowStatus(`TUI mode: ${value}`);
761
+ this.runtime.requestRender();
762
+ });
1217
763
  },
1218
764
  onCancel: close,
1219
765
  });
@@ -1412,50 +958,84 @@ export class OwnedUiSessionShell {
1412
958
  this.root.appendDaxnuts();
1413
959
  }
1414
960
  }
1415
- /**
1416
- * Turns terminal pointer reporting on for a screen that reads the pointer, and off for
1417
- * every path that ends it. While it is on the terminal hands A1 the wheel and the
1418
- * button instead of scrolling and selecting itself, so leaving it on outlives the
1419
- * screen that wanted it and takes the terminal's own scrolling and selection with it.
1420
- */
1421
- #setPointerReporting(enabled) {
1422
- if (this.#pointerReporting === enabled)
961
+ // Invariant: pointer reporting is disabled on every path that ends the owning screen.
962
+ #setPointerReporting(enabled, forceOff = false) {
963
+ const effective = forceOff ? false : this.#customViewport || enabled;
964
+ if (this.#pointerReporting === effective)
1423
965
  return;
1424
- this.#pointerReporting = enabled;
966
+ this.#pointerReporting = effective;
1425
967
  if (!this.runtime.active)
1426
968
  return;
1427
- this.runtime.writeControl(enabled ? MOUSE_TRACKING_ON : MOUSE_TRACKING_OFF);
969
+ this.runtime.writeControl(effective ? MOUSE_TRACKING_ON : MOUSE_TRACKING_OFF);
1428
970
  }
1429
971
  async dispose() {
1430
972
  if (this.#disposed)
1431
973
  return;
1432
974
  this.#disposed = true;
1433
- this.#setPointerReporting(false);
975
+ this.root.clearViewportPointerState();
976
+ this.#setPointerReporting(false, true);
977
+ this.#removeViewportPreInput();
978
+ this.#streamPresentation.dispose();
979
+ this.#unsubscribeSettings();
980
+ const exitMode = this.backend.disposed
981
+ ? this.#fullscreenExitOutput
982
+ : this.backend.pinnedSettingsSnapshot().fullscreenExitOutput;
983
+ const exitTranscript = this.root.exitTranscript(this.runtime.viewport().columns);
984
+ const resume = this.backend.currentSessionResumeMetadata();
985
+ const resumeHint = resume === null
986
+ ? ""
987
+ : `${dim("To resume this session:")} ${formatSessionResumeCommand(resume)}`;
988
+ const fullscreenExitText = this.runtime.mode !== "fullscreen"
989
+ ? ""
990
+ : exitMode === "resume-hint"
991
+ ? resumeHint
992
+ : [exitTranscript, resumeHint].filter(Boolean).join("\n\n");
993
+ this.#unbindPiSettings();
994
+ this.#unbindTerminalSettings();
995
+ this.#unbindShutdownSettings();
1434
996
  this.#unsubscribe();
1435
997
  this.#dialogHandle?.hide();
1436
998
  await this.backend.unbindExtensionUi();
1437
999
  this.#extensionBridge.dispose();
1438
1000
  await this.runtime.dispose();
1001
+ if (fullscreenExitText.length > 0)
1002
+ this.runtime.writeAfterStop(`${fullscreenExitText}\n`);
1439
1003
  }
1440
- /**
1441
- * Applies one transcript block without re-reading the session. The listeners still hear
1442
- * the view they would have heard, so nothing downstream can tell the difference.
1443
- */
1004
+ // Invariant: incremental block updates notify listeners with the same complete view contract.
1444
1005
  #syncBlock(block) {
1445
1006
  this.root.applyTranscriptBlock(block);
1446
- this.runtime.requestRender();
1007
+ if (this.#customViewport && block.status === "live" && isCoalescedStreamBlock(block.kind)) {
1008
+ this.#streamPresentation.request();
1009
+ }
1010
+ else if (this.#streamPresentation.pending) {
1011
+ this.#streamPresentation.flush();
1012
+ }
1013
+ else {
1014
+ this.#streamPresentation.presentImmediate();
1015
+ }
1447
1016
  const view = this.view();
1448
1017
  for (const listener of this.#listeners)
1449
1018
  listener(view);
1450
1019
  return view;
1451
1020
  }
1021
+ #syncTerminalProgress(view) {
1022
+ if (!this.runtime.active)
1023
+ return;
1024
+ this.runtime.setTerminalProgress(this.#terminalProgressEnabled && view.lifecycle === "busy");
1025
+ }
1452
1026
  #syncView() {
1027
+ this.#streamPresentation.noteImmediatePresentation();
1453
1028
  const view = this.view();
1454
1029
  if (this.backend.sessionGeneration !== this.#sessionGeneration) {
1455
1030
  this.#sessionGeneration = this.backend.sessionGeneration;
1456
1031
  this.#activeLoginDialog = undefined;
1457
1032
  this.#extensionBridge.reset();
1458
- // A replaced session takes its screens with it, pointer reporting included.
1033
+ // Invariant: a replaced session takes its transient viewport and owned-route state with it.
1034
+ this.root.resetViewport();
1035
+ this.#dialogHandle?.hide();
1036
+ this.#dialogHandle = undefined;
1037
+ this.#dialogId = undefined;
1038
+ this.#dialogSource = undefined;
1459
1039
  this.#setPointerReporting(false);
1460
1040
  this.root.setInputSurface(null);
1461
1041
  this.root.resetExtensionUi();
@@ -1475,10 +1055,11 @@ export class OwnedUiSessionShell {
1475
1055
  if (!this.runtime.active)
1476
1056
  return { outcome: "failed", diagnostic: "runtime is not active" };
1477
1057
  this.#dialogHandle?.hide();
1478
- // Any-event reporting: hover and drag are what the screen is driven by, and
1058
+ this.#dialogSource = "route";
1059
+ // Protocol: any-event reporting: hover and drag are what the screen is driven by, and
1479
1060
  // it also stops the terminal treating a drag as a text selection.
1480
1061
  this.#setPointerReporting(true);
1481
- // The interrupt chord is global, so it is watched on raw input rather than
1062
+ // Protocol: the interrupt chord is global, so it is watched on raw input rather than
1482
1063
  // through the overlay: the pinned shell handles that key before an overlay
1483
1064
  // ever sees it, which is why an owned screen must not rely on being asked.
1484
1065
  let armedAt = 0;
@@ -1494,17 +1075,36 @@ export class OwnedUiSessionShell {
1494
1075
  }
1495
1076
  armedAt = now;
1496
1077
  this.runtime.requestRender();
1497
- // The presented screen owns the chord, so the pinned shell never sees a
1078
+ // Invariant: the presented screen owns the chord, so the pinned shell never sees a
1498
1079
  // stray interrupt while it is up.
1499
1080
  return { consume: true };
1500
1081
  });
1082
+ let removeSurfacePreInput = () => { };
1501
1083
  const closeSurface = () => {
1084
+ removeSurfacePreInput();
1502
1085
  removeInterruptWatch();
1503
1086
  this.#setPointerReporting(false);
1504
1087
  this.#dialogHandle?.hide();
1505
1088
  this.#dialogHandle = undefined;
1506
1089
  this.#dialogId = undefined;
1090
+ this.#dialogSource = undefined;
1507
1091
  };
1092
+ // Compatibility: fullscreen Pi owns a fallback text-selection layer before focused overlay
1093
+ // components see pointer input. Route every mouse report to the owned screen
1094
+ // at the pre-input boundary so dropdowns, value hover, and numeric +/- work,
1095
+ // and consume even unhandled reports so settings content is never selected.
1096
+ removeSurfacePreInput = this.runtime.addPreInputListener(data => {
1097
+ const { events, rest } = parseMouseInput(data);
1098
+ if (events.length === 0)
1099
+ return undefined;
1100
+ for (const event of events)
1101
+ surface.handleMouse(event);
1102
+ if (surface.isClosed())
1103
+ closeSurface();
1104
+ else
1105
+ this.runtime.requestRender();
1106
+ return rest.length === 0 ? { consume: true } : { data: rest };
1107
+ });
1508
1108
  const rows = () => Math.max(1, this.runtime.viewport().rows);
1509
1109
  const component = {
1510
1110
  render: (width) => [...surface.render(Math.max(1, width), rows())],
@@ -1527,7 +1127,12 @@ export class OwnedUiSessionShell {
1527
1127
  closeSurface();
1528
1128
  void this.shutdown();
1529
1129
  });
1530
- this.#dialogHandle = this.runtime.showOverlay(component, { width: "100%", maxHeight: "100%", anchor: "top-left" });
1130
+ this.#dialogHandle = this.runtime.showOverlay(component, {
1131
+ width: "100%",
1132
+ maxHeight: "100%",
1133
+ anchor: "top-left",
1134
+ inputCoordination: "owned",
1135
+ });
1531
1136
  this.#dialogId = surface.id;
1532
1137
  return { outcome: "completed", diagnostic: null };
1533
1138
  }
@@ -1535,25 +1140,40 @@ export class OwnedUiSessionShell {
1535
1140
  if (!this.runtime.active)
1536
1141
  return;
1537
1142
  if (dialog === null) {
1143
+ // Invariant: locally owned routes (notably /settings) are independent of backend
1144
+ // lifecycle/status events and remain open while an agent is working.
1145
+ if (this.#dialogSource !== "backend")
1146
+ return;
1538
1147
  this.#dialogHandle?.hide();
1539
1148
  this.#dialogHandle = undefined;
1540
1149
  this.#dialogId = undefined;
1150
+ this.#dialogSource = undefined;
1541
1151
  return;
1542
1152
  }
1543
- if (this.#dialogId === dialog.id)
1153
+ if (this.#dialogSource === "backend" && this.#dialogId === dialog.id)
1544
1154
  return;
1545
1155
  this.#dialogHandle?.hide();
1156
+ this.#dialogSource = "backend";
1546
1157
  const component = createPiShellDialog(dialog, {
1547
1158
  onSelect: () => {
1548
1159
  this.#dialogHandle?.hide();
1549
1160
  this.#dialogHandle = undefined;
1161
+ this.#dialogId = undefined;
1162
+ this.#dialogSource = undefined;
1550
1163
  },
1551
1164
  onCancel: () => {
1552
1165
  this.#dialogHandle?.hide();
1553
1166
  this.#dialogHandle = undefined;
1167
+ this.#dialogId = undefined;
1168
+ this.#dialogSource = undefined;
1554
1169
  },
1555
1170
  });
1556
- this.#dialogHandle = this.runtime.showOverlay(component, { width: "70%", maxHeight: "80%", anchor: "center" });
1171
+ this.#dialogHandle = this.runtime.showOverlay(component, {
1172
+ width: "70%",
1173
+ maxHeight: "80%",
1174
+ anchor: "center",
1175
+ inputCoordination: "owned",
1176
+ });
1557
1177
  this.#dialogId = dialog.id;
1558
1178
  }
1559
1179
  async #slashCommand(text) {
@@ -1565,8 +1185,9 @@ export class OwnedUiSessionShell {
1565
1185
  return this.#openOwnedRoute(name);
1566
1186
  if (isWorkflowRoute(name))
1567
1187
  return this.runWorkflow({ command: name, argument });
1568
- // Unknown slash input, prompt templates, skills, and extension commands remain Pi prompt input.
1188
+ // Compatibility: unknown slash input, prompt templates, skills, and extension commands remain Pi prompt input.
1569
1189
  this.root.editor.addToHistory(text);
1190
+ this.root.resumeViewportFollowing();
1570
1191
  return this.#execute({
1571
1192
  type: this.view().lifecycle === "busy" ? "steer" : "prompt",
1572
1193
  correlationId: this.#correlation("prompt-command"),
@@ -1688,6 +1309,7 @@ export class OwnedUiSessionShell {
1688
1309
  correlationId: this.#correlation(`compaction-${item.type}`),
1689
1310
  sessionId: this.backend.sessionId,
1690
1311
  text: item.text,
1312
+ ...(item.images === undefined ? {} : { images: item.images }),
1691
1313
  });
1692
1314
  }
1693
1315
  }
@@ -1702,6 +1324,24 @@ export class OwnedUiSessionShell {
1702
1324
  return `pi-shell-${prefix}-${this.#sequence}`;
1703
1325
  }
1704
1326
  }
1327
+ function isCoalescedStreamBlock(kind) {
1328
+ return kind === "assistant" || kind === "thinking" || kind === "tool-call" || kind === "tool-result";
1329
+ }
1330
+ export function formatSessionResumeCommand(metadata) {
1331
+ const args = [PRODUCT_TEXT.commandName];
1332
+ if (!metadata.usesDefaultSessionDir)
1333
+ args.push("--session-dir", quoteCommandArgument(metadata.sessionDir));
1334
+ args.push("--session", metadata.sessionId);
1335
+ return args.join(" ");
1336
+ }
1337
+ export function quoteCommandArgument(value) {
1338
+ if (value.length > 0 && !/[^a-zA-Z0-9_\-./~:@]/.test(value))
1339
+ return value;
1340
+ return `'${value.replaceAll("'", `'\\''`)}'`;
1341
+ }
1342
+ function dim(value) {
1343
+ return `\u001b[2m${value}\u001b[22m`;
1344
+ }
1705
1345
  function modelReference(model) {
1706
1346
  if (typeof model !== "object" || model === null)
1707
1347
  return "";