@timurproko/a1 0.1.8-dev.235df0e → 0.1.8-dev.260

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 (485) hide show
  1. package/README.md +107 -67
  2. package/bin/cli.js +28 -10
  3. package/bin/guardian.js +8 -3
  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 +2 -2
  9. package/bin/sync-pi-tui-proxy.js +97 -0
  10. package/bin/ui.js +47 -20
  11. package/bin/warmup.js +27 -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 +15 -1
  15. package/dist/cli/dispatch.js +261 -0
  16. package/dist/cli/package-messages.d.ts +26 -0
  17. package/dist/cli/package-messages.js +71 -0
  18. package/dist/{src/cli → cli}/packages.d.ts +11 -2
  19. package/dist/{src/cli → cli}/packages.js +24 -27
  20. package/dist/{src/cli → cli}/version-stats.d.ts +6 -0
  21. package/dist/cli/version-stats.js +112 -0
  22. package/dist/composition/agent-engine-bridge.d.ts +3 -0
  23. package/dist/composition/agent-engine-bridge.js +108 -0
  24. package/dist/composition/index.d.ts +3 -0
  25. package/dist/composition/index.js +3 -0
  26. package/dist/composition/owned-ui.d.ts +31 -0
  27. package/dist/composition/owned-ui.js +66 -0
  28. package/dist/composition/process.d.ts +16 -0
  29. package/dist/composition/process.js +45 -0
  30. package/dist/composition/settings-route-host.d.ts +8 -0
  31. package/dist/composition/settings-route-host.js +75 -0
  32. package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/capability-ports.d.ts +2 -2
  33. package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/domain-validation.js +22 -4
  34. package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/domain.d.ts +17 -0
  35. package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/package-ports.d.ts +6 -0
  36. package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/model.d.ts +36 -0
  37. package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/validation.js +25 -0
  38. package/dist/{src/features → features}/launch/development-launch.js +11 -3
  39. package/dist/{src/features → features}/launch/index.d.ts +0 -1
  40. package/dist/{src/features → features}/launch/index.js +0 -1
  41. package/dist/features/launch/intent.d.ts +10 -0
  42. package/dist/features/launch/intent.js +8 -0
  43. package/dist/{src/features → features}/launch/prepare-launch.d.ts +0 -2
  44. package/dist/{src/features → features}/launch/prepare-launch.js +2 -9
  45. package/dist/{src/features → features}/launch/profile-paths.d.ts +1 -2
  46. package/dist/{src/features → features}/launch/profile-paths.js +2 -8
  47. package/dist/{src/features → features}/launch/runtime-selection.d.ts +2 -5
  48. package/dist/{src/features → features}/owned-ui/customization.d.ts +3 -1
  49. package/dist/{src/features → features}/owned-ui/customization.js +3 -1
  50. package/dist/{src/features → features}/owned-ui/diagnostics.d.ts +2 -1
  51. package/dist/{src/features → features}/owned-ui/diagnostics.js +1 -0
  52. package/dist/{src/features → features}/owned-ui/index.d.ts +2 -0
  53. package/dist/{src/features → features}/owned-ui/index.js +2 -0
  54. package/dist/features/owned-ui/project-trust-prompt.d.ts +25 -0
  55. package/dist/features/owned-ui/project-trust-prompt.js +149 -0
  56. package/dist/{src/features → features}/owned-ui/run.d.ts +2 -2
  57. package/dist/{src/features → features}/owned-ui/run.js +2 -0
  58. package/dist/features/owned-ui/session-fork-prompt.d.ts +8 -0
  59. package/dist/features/owned-ui/session-fork-prompt.js +34 -0
  60. package/dist/{src/features → features}/owned-ui/settings-app.d.ts +3 -3
  61. package/dist/{src/features → features}/owned-ui/settings-app.js +103 -54
  62. package/dist/{src/features → features}/workspace/capabilities.d.ts +1 -1
  63. package/dist/{src/features → features}/workspace/reducer.d.ts +2 -1
  64. package/dist/{src/features → features}/workspace/reducer.js +2 -1
  65. package/dist/{src/features → features}/workspace/router.d.ts +2 -1
  66. package/dist/{src/features → features}/workspace/router.js +1 -0
  67. package/dist/{src/features → features}/workspace/store.d.ts +2 -1
  68. package/dist/{src/features → features}/workspace/store.js +1 -0
  69. package/dist/{src/foundation → foundation}/launch-guardian/main.d.ts +2 -0
  70. package/dist/{src/foundation → foundation}/launch-guardian/main.js +18 -5
  71. package/dist/{src/foundation → foundation}/lifecycle/index.d.ts +2 -0
  72. package/dist/{src/foundation → foundation}/lifecycle/index.js +2 -0
  73. package/dist/{src/foundation → foundation}/lifecycle/model.d.ts +1 -1
  74. package/dist/{src/foundation → foundation}/lifecycle/model.js +1 -1
  75. package/dist/foundation/lifecycle/paths.d.ts +30 -0
  76. package/dist/{src/foundation → foundation}/lifecycle/paths.js +29 -1
  77. package/dist/foundation/lifecycle/session-selection.d.ts +9 -0
  78. package/dist/foundation/lifecycle/session-selection.js +39 -0
  79. package/dist/foundation/lifecycle/supervisor-startup.d.ts +30 -0
  80. package/dist/foundation/lifecycle/supervisor-startup.js +106 -0
  81. package/dist/{src/foundation → foundation}/native-host-protocol/codec.d.ts +2 -0
  82. package/dist/{src/foundation → foundation}/native-host-protocol/codec.js +2 -0
  83. package/dist/{src/foundation → foundation}/native-host-protocol/messages.d.ts +1 -1
  84. package/dist/{src/foundation → foundation}/native-host-protocol/messages.js +1 -1
  85. package/dist/foundation/process-containment/darwin-process-inspector.d.ts +5 -0
  86. package/dist/foundation/process-containment/darwin-process-inspector.js +7 -0
  87. package/dist/{src/foundation → foundation}/process-containment/index.d.ts +1 -0
  88. package/dist/{src/foundation → foundation}/process-containment/index.js +1 -0
  89. package/dist/{src/foundation → foundation}/process-containment/linux-process-inspector.d.ts +1 -0
  90. package/dist/{src/foundation → foundation}/process-containment/linux-process-inspector.js +1 -0
  91. package/dist/{src/foundation → foundation}/process-containment/native-guardian-containment.d.ts +1 -0
  92. package/dist/{src/foundation → foundation}/process-containment/native-guardian-containment.js +1 -0
  93. package/dist/{src/foundation → foundation}/process-containment/windows-process-inspector.d.ts +3 -1
  94. package/dist/{src/foundation → foundation}/process-containment/windows-process-inspector.js +5 -2
  95. package/dist/{src/foundation → foundation}/protocol/client.d.ts +1 -0
  96. package/dist/{src/foundation → foundation}/protocol/client.js +1 -0
  97. package/dist/{src/foundation → foundation}/protocol/messages.d.ts +1 -0
  98. package/dist/{src/foundation → foundation}/protocol/messages.js +1 -0
  99. package/dist/{src/foundation → foundation}/release/bootstrap.d.ts +25 -6
  100. package/dist/{src/foundation → foundation}/release/bootstrap.js +124 -34
  101. package/dist/foundation/release/cohort-state.d.ts +156 -0
  102. package/dist/foundation/release/cohort-state.js +471 -0
  103. package/dist/foundation/release/dependency-layer.d.ts +82 -0
  104. package/dist/foundation/release/dependency-layer.js +497 -0
  105. package/dist/foundation/release/endpoints.d.ts +20 -0
  106. package/dist/foundation/release/endpoints.js +50 -0
  107. package/dist/foundation/release/immutable-platform.d.ts +6 -0
  108. package/dist/foundation/release/immutable-platform.js +13 -0
  109. package/dist/{src/foundation → foundation}/release/index.d.ts +4 -0
  110. package/dist/{src/foundation → foundation}/release/index.js +4 -0
  111. package/dist/foundation/release/release-gc.d.ts +71 -0
  112. package/dist/foundation/release/release-gc.js +734 -0
  113. package/dist/{src/foundation → foundation}/release/release-store.d.ts +7 -3
  114. package/dist/{src/foundation → foundation}/release/release-store.js +65 -13
  115. package/dist/{src/foundation → foundation}/release/release.d.ts +4 -1
  116. package/dist/{src/foundation → foundation}/release/release.js +10 -3
  117. package/dist/foundation/release/restart-certification.d.ts +67 -0
  118. package/dist/foundation/release/restart-certification.js +211 -0
  119. package/dist/{src/foundation → foundation}/release/update-transaction.d.ts +1 -0
  120. package/dist/{src/foundation → foundation}/release/update-transaction.js +1 -0
  121. package/dist/{src/foundation → foundation}/release/update.d.ts +19 -3
  122. package/dist/{src/foundation → foundation}/release/update.js +116 -56
  123. package/dist/foundation/release/warmup.d.ts +3 -0
  124. package/dist/foundation/release/warmup.js +35 -0
  125. package/dist/foundation/startup/index.d.ts +1 -0
  126. package/dist/foundation/startup/index.js +1 -0
  127. package/dist/foundation/startup/startup-runtime.d.ts +36 -0
  128. package/dist/foundation/startup/startup-runtime.js +140 -0
  129. package/dist/{src/foundation → foundation}/storage/control-store.d.ts +2 -1
  130. package/dist/{src/foundation → foundation}/storage/control-store.js +39 -4
  131. package/dist/{src/foundation → foundation}/structured-agent-runtime/backpressure.d.ts +2 -1
  132. package/dist/{src/foundation → foundation}/structured-agent-runtime/backpressure.js +2 -1
  133. package/dist/{src/foundation → foundation}/structured-agent-runtime/commands.d.ts +2 -1
  134. package/dist/{src/foundation → foundation}/structured-agent-runtime/commands.js +2 -1
  135. package/dist/{src/foundation → foundation}/structured-agent-runtime/handshake.d.ts +1 -1
  136. package/dist/{src/foundation → foundation}/structured-agent-runtime/handshake.js +1 -1
  137. package/dist/{src/foundation → foundation}/structured-agent-runtime/reconnection.d.ts +2 -1
  138. package/dist/{src/foundation → foundation}/structured-agent-runtime/reconnection.js +2 -1
  139. package/dist/{src/foundation → foundation}/structured-agent-runtime/state.d.ts +2 -1
  140. package/dist/{src/foundation → foundation}/structured-agent-runtime/state.js +2 -1
  141. package/dist/foundation/supervision/index.d.ts +3 -0
  142. package/dist/foundation/supervision/index.js +3 -0
  143. package/dist/foundation/supervision/main.d.ts +1 -0
  144. package/dist/foundation/supervision/main.js +80 -0
  145. package/dist/foundation/supervision/paths.d.ts +1 -0
  146. package/dist/foundation/supervision/paths.js +1 -0
  147. package/dist/{src/foundation → foundation}/supervision/server.d.ts +19 -1
  148. package/dist/{src/foundation → foundation}/supervision/server.js +65 -3
  149. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/components.d.ts +1 -1
  150. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/conformance.d.ts +1 -0
  151. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/conformance.js +3 -2
  152. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/index.d.ts +1 -1
  153. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/index.js +1 -1
  154. package/dist/integrations/pi/components/owned-editor-ux.d.ts +55 -0
  155. package/dist/integrations/pi/components/owned-editor-ux.js +778 -0
  156. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-components.d.ts +1 -0
  157. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-components.js +1 -0
  158. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-editor-autocomplete.js +54 -6
  159. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-extension-ui.d.ts +1 -1
  160. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-footer-status.d.ts +3 -3
  161. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-footer-status.js +49 -18
  162. package/dist/integrations/pi/components/shell-presenters-info.d.ts +36 -0
  163. package/dist/integrations/pi/components/shell-presenters-info.js +78 -0
  164. package/dist/integrations/pi/components/shell-presenters-transcript.d.ts +25 -0
  165. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-presenters-transcript.js +92 -131
  166. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-selectors-dialogs.d.ts +5 -2
  167. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-selectors-dialogs.js +2 -1
  168. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-shared-facade.d.ts +57 -8
  169. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-shared-facade.js +4 -2
  170. package/dist/integrations/pi/components/submitted-prompt-adapter.d.ts +13 -0
  171. package/dist/integrations/pi/components/submitted-prompt-adapter.js +27 -0
  172. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/adjacent/core/keybindings.d.ts +10 -2
  173. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/adjacent/core/keybindings.js +36 -3
  174. package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-footer.d.ts +1 -1
  175. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/adapter.d.ts +28 -4
  176. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/adapter.js +714 -127
  177. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/conformance.d.ts +2 -0
  178. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/conformance.js +4 -2
  179. package/dist/integrations/pi/engine/http-dispatcher.d.ts +4 -0
  180. package/dist/integrations/pi/engine/http-dispatcher.js +25 -0
  181. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/index.d.ts +4 -0
  182. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/index.js +4 -0
  183. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/model-auth-integration.d.ts +2 -1
  184. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/model-auth-integration.js +1 -0
  185. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/package-integration.d.ts +1 -1
  186. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/package-integration.js +32 -26
  187. package/dist/integrations/pi/engine/project-trust-preflight.d.ts +22 -0
  188. package/dist/integrations/pi/engine/project-trust-preflight.js +50 -0
  189. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/resource-extension-integration.d.ts +2 -1
  190. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/resource-extension-integration.js +1 -0
  191. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/runtime-integration.d.ts +19 -1
  192. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/runtime-integration.js +47 -5
  193. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/session-integration.d.ts +4 -2
  194. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/session-integration.js +19 -4
  195. package/dist/integrations/pi/engine/session-selection.d.ts +28 -0
  196. package/dist/integrations/pi/engine/session-selection.js +90 -0
  197. package/dist/integrations/pi/engine/settings-effects.d.ts +66 -0
  198. package/dist/integrations/pi/engine/settings-effects.js +249 -0
  199. package/dist/integrations/pi/engine/settings-integration.d.ts +43 -0
  200. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/settings-integration.js +102 -108
  201. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/workflow-controllers.d.ts +3 -2
  202. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/workflow-controllers.js +1 -0
  203. package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/workflows.d.ts +18 -1
  204. package/dist/integrations/pi/session-ui/clipboard-image.d.ts +11 -0
  205. package/dist/integrations/pi/session-ui/clipboard-image.js +29 -0
  206. package/dist/{src/foundation/pi-owned-ui-integration → integrations/pi/session-ui}/index.d.ts +1 -0
  207. package/dist/{src/foundation/pi-owned-ui-integration → integrations/pi/session-ui}/index.js +1 -0
  208. package/dist/integrations/pi/session-ui/prompt-chips.d.ts +23 -0
  209. package/dist/integrations/pi/session-ui/prompt-chips.js +246 -0
  210. package/dist/integrations/pi/session-ui/route-host.d.ts +5 -0
  211. package/dist/integrations/pi/session-ui/session-shell-root.d.ts +141 -0
  212. package/dist/integrations/pi/session-ui/session-shell-root.js +1158 -0
  213. package/dist/integrations/pi/session-ui/session-shell.d.ts +55 -0
  214. package/dist/{src/foundation/pi-owned-ui-integration → integrations/pi/session-ui}/session-shell.js +479 -719
  215. package/dist/integrations/pi/session-ui/session-viewport-controller.d.ts +45 -0
  216. package/dist/integrations/pi/session-ui/session-viewport-controller.js +463 -0
  217. package/dist/integrations/pi/session-ui/stream-presentation-coalescer.d.ts +29 -0
  218. package/dist/integrations/pi/session-ui/stream-presentation-coalescer.js +79 -0
  219. package/dist/integrations/pi/session-ui/system-clipboard.d.ts +20 -0
  220. package/dist/integrations/pi/session-ui/system-clipboard.js +130 -0
  221. package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/adapter.d.ts +8 -1
  222. package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/adapter.js +216 -9
  223. package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/contracts.d.ts +29 -6
  224. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +74 -0
  225. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +380 -0
  226. package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/index.d.ts +2 -0
  227. package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/index.js +2 -0
  228. package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.d.ts +39 -0
  229. package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.js +139 -0
  230. package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/presentation-adapter.d.ts +1 -1
  231. package/dist/native/darwin-arm64/manifest.json +4 -4
  232. package/dist/native/darwin-arm64/process-guardian +0 -0
  233. package/dist/native/linux-x64/manifest.json +3 -3
  234. package/dist/native/linux-x64/process-guardian +0 -0
  235. package/dist/native/win32-x64/manifest.json +3 -3
  236. package/dist/native/win32-x64/process-guardian.exe +0 -0
  237. package/dist/{src/product-identity.d.ts → product-identity.d.ts} +3 -3
  238. package/dist/{src/product-identity.js → product-identity.js} +7 -5
  239. package/dist/{src/product-identity.json → product-identity.json} +11 -6
  240. package/dist/runtime-payload-inventory.json +122328 -0
  241. package/dist/{src/foundation/ui-apps → ui/apps}/contracts.d.ts +23 -1
  242. package/dist/{src/foundation/ui-apps → ui/apps}/host.d.ts +1 -1
  243. package/dist/{src/foundation/ui-apps → ui/apps}/host.js +14 -10
  244. package/dist/{src/foundation/ui-apps → ui/apps}/registry.d.ts +1 -0
  245. package/dist/{src/foundation/ui-apps → ui/apps}/registry.js +1 -0
  246. package/dist/{src/foundation/ui-components → ui/components}/dialog-panel.js +5 -3
  247. package/dist/{src/foundation/ui-components → ui/components}/frame.d.ts +1 -0
  248. package/dist/{src/foundation/ui-components → ui/components}/frame.js +1 -0
  249. package/dist/{src/foundation/ui-components → ui/components}/index.d.ts +5 -0
  250. package/dist/{src/foundation/ui-components → ui/components}/index.js +5 -0
  251. package/dist/{src/foundation/ui-components → ui/components}/line-input.d.ts +1 -6
  252. package/dist/{src/foundation/ui-components → ui/components}/line-input.js +4 -9
  253. package/dist/{src/foundation/ui-components → ui/components}/list-view.d.ts +0 -6
  254. package/dist/{src/foundation/ui-components → ui/components}/list-view.js +14 -7
  255. package/dist/{src/foundation/ui-components → ui/components}/mouse.d.ts +10 -0
  256. package/dist/{src/foundation/ui-components → ui/components}/mouse.js +23 -0
  257. package/dist/ui/components/progress-status.d.ts +2 -0
  258. package/dist/ui/components/progress-status.js +4 -0
  259. package/dist/ui/components/prompt-row.d.ts +14 -0
  260. package/dist/ui/components/prompt-row.js +20 -0
  261. package/dist/{src/foundation/ui-components → ui/components}/scrollbar.d.ts +26 -0
  262. package/dist/{src/foundation/ui-components → ui/components}/scrollbar.js +35 -0
  263. package/dist/{src/foundation/ui-components → ui/components}/shortcuts.d.ts +1 -0
  264. package/dist/{src/foundation/ui-components → ui/components}/shortcuts.js +1 -0
  265. package/dist/ui/components/spans.d.ts +25 -0
  266. package/dist/ui/components/spans.js +257 -0
  267. package/dist/ui/components/submitted-prompt.d.ts +23 -0
  268. package/dist/ui/components/submitted-prompt.js +50 -0
  269. package/dist/{src/foundation/ui-components → ui/components}/surface.d.ts +0 -5
  270. package/dist/{src/foundation/ui-components → ui/components}/surface.js +1 -5
  271. package/dist/ui/components/text-selection.d.ts +62 -0
  272. package/dist/ui/components/text-selection.js +208 -0
  273. package/dist/{src/foundation/ui-components → ui/components}/text.d.ts +14 -0
  274. package/dist/{src/foundation/ui-components → ui/components}/text.js +66 -4
  275. package/dist/ui/components/transcript-viewport.d.ts +152 -0
  276. package/dist/ui/components/transcript-viewport.js +631 -0
  277. package/dist/ui/components/visible-hyperlinks.d.ts +19 -0
  278. package/dist/ui/components/visible-hyperlinks.js +64 -0
  279. package/dist/{src/foundation/owned-ui-settings → ui/settings}/declarations.d.ts +8 -1
  280. package/dist/{src/foundation/owned-ui-settings → ui/settings}/declarations.js +33 -5
  281. package/dist/{src/foundation/owned-ui-settings → ui/settings}/migrations.d.ts +1 -1
  282. package/dist/{src/foundation/owned-ui-settings → ui/settings}/migrations.js +21 -2
  283. package/dist/{src/foundation/owned-ui-settings → ui/settings}/sections.d.ts +6 -19
  284. package/dist/{src/foundation/owned-ui-settings → ui/settings}/sections.js +32 -23
  285. package/dist/{src/foundation/owned-ui-settings → ui/settings}/session.d.ts +9 -17
  286. package/dist/{src/foundation/owned-ui-settings → ui/settings}/session.js +65 -55
  287. package/dist/{src/foundation/owned-ui-settings → ui/settings}/store.d.ts +1 -0
  288. package/dist/{src/foundation/owned-ui-settings → ui/settings}/store.js +2 -1
  289. package/docs/architecture/boundaries.md +9 -9
  290. package/docs/architecture/code-documentation.md +77 -0
  291. package/docs/architecture/process-guardian-provenance.md +3 -1
  292. package/docs/architecture/project-structure.md +67 -30
  293. package/docs/architecture/resource-and-data-policy.md +1 -1
  294. package/docs/architecture/toolchain.md +10 -10
  295. package/docs/architecture/ui-reference-provenance.md +36 -5
  296. package/docs/ci-release-runbook.md +186 -79
  297. package/docs/features/launch-profiles.md +24 -17
  298. package/docs/manual-ghost-link-baseline.md +89 -0
  299. package/docs/manual-launch-instance-acceptance.md +12 -35
  300. package/docs/manual-owned-ui-checkpoint.md +78 -0
  301. package/docs/manual-pi-boundary-candidate.md +4 -5
  302. package/docs/manual-terminal-colour-check.md +4 -3
  303. package/docs/repository-governance-live-acceptance.md +77 -0
  304. package/package.json +37 -27
  305. package/dist/src/cli/capabilities.d.ts +0 -15
  306. package/dist/src/cli/dispatch.js +0 -177
  307. package/dist/src/cli/version-stats.js +0 -72
  308. package/dist/src/composition/index.d.ts +0 -48
  309. package/dist/src/composition/index.js +0 -258
  310. package/dist/src/features/launch/intent.d.ts +0 -7
  311. package/dist/src/features/launch/intent.js +0 -4
  312. package/dist/src/features/launch/profiles.d.ts +0 -12
  313. package/dist/src/features/launch/profiles.js +0 -26
  314. package/dist/src/foundation/lifecycle/paths.d.ts +0 -10
  315. package/dist/src/foundation/pi-component-adapter/shell-presenters-transcript.d.ts +0 -57
  316. package/dist/src/foundation/pi-engine-adapter/settings-integration.d.ts +0 -55
  317. package/dist/src/foundation/pi-owned-ui-integration/route-host.d.ts +0 -29
  318. package/dist/src/foundation/pi-owned-ui-integration/session-shell.d.ts +0 -106
  319. package/dist/src/foundation/release/cohort-state.d.ts +0 -70
  320. package/dist/src/foundation/release/cohort-state.js +0 -202
  321. package/dist/src/foundation/release/release-gc.d.ts +0 -5
  322. package/dist/src/foundation/release/release-gc.js +0 -16
  323. package/dist/src/foundation/supervision/index.d.ts +0 -3
  324. package/dist/src/foundation/supervision/index.js +0 -3
  325. package/dist/src/foundation/supervision/main.d.ts +0 -1
  326. package/dist/src/foundation/supervision/main.js +0 -41
  327. package/dist/src/foundation/supervision/paths.d.ts +0 -1
  328. package/dist/src/foundation/supervision/paths.js +0 -1
  329. package/dist/src/foundation/ui-components/spans.d.ts +0 -7
  330. package/dist/src/foundation/ui-components/spans.js +0 -59
  331. package/docs/manual-transparent-checkpoint.md +0 -53
  332. /package/dist/{src/cli → cli}/index.d.ts +0 -0
  333. /package/dist/{src/cli → cli}/index.js +0 -0
  334. /package/dist/{src/cli → cli}/version.d.ts +0 -0
  335. /package/dist/{src/cli → cli}/version.js +0 -0
  336. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/capability-ports.js +0 -0
  337. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/domain-validation.d.ts +0 -0
  338. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/domain.js +0 -0
  339. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/index.d.ts +0 -0
  340. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/index.js +0 -0
  341. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/model.d.ts +0 -0
  342. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/model.js +0 -0
  343. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/package-ports.js +0 -0
  344. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/ports.d.ts +0 -0
  345. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/ports.js +0 -0
  346. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/serialization.d.ts +0 -0
  347. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/serialization.js +0 -0
  348. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/validation.d.ts +0 -0
  349. /package/dist/{src/foundation/agent-engine-contracts → contracts/agent-engine}/validation.js +0 -0
  350. /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/extension-ui.d.ts +0 -0
  351. /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/extension-ui.js +0 -0
  352. /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/index.d.ts +0 -0
  353. /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/index.js +0 -0
  354. /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/model.js +0 -0
  355. /package/dist/{src/foundation/owned-ui-contracts → contracts/owned-ui}/validation.d.ts +0 -0
  356. /package/dist/{src/foundation/presentation-contracts → contracts/presentation}/index.d.ts +0 -0
  357. /package/dist/{src/foundation/presentation-contracts → contracts/presentation}/index.js +0 -0
  358. /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/index.d.ts +0 -0
  359. /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/index.js +0 -0
  360. /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/model.d.ts +0 -0
  361. /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/model.js +0 -0
  362. /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/validation.d.ts +0 -0
  363. /package/dist/{src/foundation/workspace-contracts → contracts/workspace}/validation.js +0 -0
  364. /package/dist/{src/features → features}/launch/development-launch.d.ts +0 -0
  365. /package/dist/{src/features → features}/launch/initialize-profile.d.ts +0 -0
  366. /package/dist/{src/features → features}/launch/initialize-profile.js +0 -0
  367. /package/dist/{src/features → features}/launch/runtime-selection.js +0 -0
  368. /package/dist/{src/features → features}/workspace/capabilities.js +0 -0
  369. /package/dist/{src/features → features}/workspace/index.d.ts +0 -0
  370. /package/dist/{src/features → features}/workspace/index.js +0 -0
  371. /package/dist/{src/features → features}/workspace/presentation.d.ts +0 -0
  372. /package/dist/{src/features → features}/workspace/presentation.js +0 -0
  373. /package/dist/{src/features → features}/workspace/reconciliation.d.ts +0 -0
  374. /package/dist/{src/features → features}/workspace/reconciliation.js +0 -0
  375. /package/dist/{src/foundation → foundation}/launch-guardian/index.d.ts +0 -0
  376. /package/dist/{src/foundation → foundation}/launch-guardian/index.js +0 -0
  377. /package/dist/{src/foundation → foundation}/lifecycle/commands.d.ts +0 -0
  378. /package/dist/{src/foundation → foundation}/lifecycle/commands.js +0 -0
  379. /package/dist/{src/foundation → foundation}/lifecycle/launch-instance.d.ts +0 -0
  380. /package/dist/{src/foundation → foundation}/lifecycle/launch-instance.js +0 -0
  381. /package/dist/{src/foundation → foundation}/native-host-protocol/evidence.d.ts +0 -0
  382. /package/dist/{src/foundation → foundation}/native-host-protocol/evidence.js +0 -0
  383. /package/dist/{src/foundation → foundation}/native-host-protocol/index.d.ts +0 -0
  384. /package/dist/{src/foundation → foundation}/native-host-protocol/index.js +0 -0
  385. /package/dist/{src/foundation → foundation}/native-host-protocol/proof-gate.d.ts +0 -0
  386. /package/dist/{src/foundation → foundation}/native-host-protocol/proof-gate.js +0 -0
  387. /package/dist/{src/foundation → foundation}/process-containment/artifact.d.ts +0 -0
  388. /package/dist/{src/foundation → foundation}/process-containment/artifact.js +0 -0
  389. /package/dist/{src/foundation → foundation}/process-containment/contracts.d.ts +0 -0
  390. /package/dist/{src/foundation → foundation}/process-containment/contracts.js +0 -0
  391. /package/dist/{src/foundation → foundation}/protocol/index.d.ts +0 -0
  392. /package/dist/{src/foundation → foundation}/protocol/index.js +0 -0
  393. /package/dist/{src/foundation → foundation}/release/cohort-selection.d.ts +0 -0
  394. /package/dist/{src/foundation → foundation}/release/cohort-selection.js +0 -0
  395. /package/dist/{src/foundation → foundation}/release/concurrency.d.ts +0 -0
  396. /package/dist/{src/foundation → foundation}/release/concurrency.js +0 -0
  397. /package/dist/{src/foundation → foundation}/release/process-cleanup.d.ts +0 -0
  398. /package/dist/{src/foundation → foundation}/release/process-cleanup.js +0 -0
  399. /package/dist/{src/foundation → foundation}/release/stable-release.d.ts +0 -0
  400. /package/dist/{src/foundation → foundation}/release/stable-release.js +0 -0
  401. /package/dist/{src/foundation → foundation}/storage/index.d.ts +0 -0
  402. /package/dist/{src/foundation → foundation}/storage/index.js +0 -0
  403. /package/dist/{src/foundation → foundation}/structured-agent-runtime/index.d.ts +0 -0
  404. /package/dist/{src/foundation → foundation}/structured-agent-runtime/index.js +0 -0
  405. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/components.js +0 -0
  406. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/resources/builtin-themes.d.ts +0 -0
  407. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/resources/builtin-themes.js +0 -0
  408. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-editor-autocomplete.d.ts +0 -0
  409. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/shell-extension-ui.js +0 -0
  410. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/theme.d.ts +0 -0
  411. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/theme.js +0 -0
  412. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/countdown-timer.d.ts +0 -0
  413. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/countdown-timer.js +0 -0
  414. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/custom-entry.d.ts +0 -0
  415. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/custom-entry.js +0 -0
  416. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/daxnuts.d.ts +0 -0
  417. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/daxnuts.js +0 -0
  418. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/earendil-announcement.d.ts +0 -0
  419. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/earendil-announcement.js +0 -0
  420. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/extension-editor.d.ts +0 -0
  421. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/extension-editor.js +0 -0
  422. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/first-time-setup.d.ts +0 -0
  423. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/first-time-setup.js +0 -0
  424. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/markdown-transform.d.ts +0 -0
  425. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/markdown-transform.js +0 -0
  426. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/mermaid.d.ts +0 -0
  427. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/mermaid.js +0 -0
  428. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/owned-editor.d.ts +0 -0
  429. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/owned-editor.js +0 -0
  430. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/scoped-models-selector.d.ts +0 -0
  431. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/scoped-models-selector.js +0 -0
  432. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-footer.js +0 -0
  433. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-selector-search.d.ts +0 -0
  434. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-selector-search.js +0 -0
  435. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-selector.d.ts +0 -0
  436. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/session-selector.js +0 -0
  437. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/skill-invocation-message.d.ts +0 -0
  438. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/skill-invocation-message.js +0 -0
  439. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/status-indicator.d.ts +0 -0
  440. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/status-indicator.js +0 -0
  441. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/tree-selector.d.ts +0 -0
  442. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/tree-selector.js +0 -0
  443. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/trust-selector.d.ts +0 -0
  444. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/components/trust-selector.js +0 -0
  445. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/external-editor.d.ts +0 -0
  446. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/external-editor.js +0 -0
  447. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/model-search.d.ts +0 -0
  448. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/model-search.js +0 -0
  449. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/theme/theme-controller.d.ts +0 -0
  450. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/theme/theme-controller.js +0 -0
  451. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/theme/theme.d.ts +0 -0
  452. /package/dist/{src/foundation/pi-component-adapter → integrations/pi/components}/upstream/theme/theme.js +0 -0
  453. /package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/pi-settings-metadata.json +0 -0
  454. /package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/public-main-entry.d.ts +0 -0
  455. /package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/public-main-entry.js +0 -0
  456. /package/dist/{src/foundation/pi-engine-adapter → integrations/pi/engine}/workflows.js +0 -0
  457. /package/dist/{src/foundation/pi-owned-ui-integration → integrations/pi/session-ui}/route-host.js +0 -0
  458. /package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/conformance.d.ts +0 -0
  459. /package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/conformance.js +0 -0
  460. /package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/contracts.js +0 -0
  461. /package/dist/{src/foundation/pi-tui-runtime-adapter → integrations/pi/tui-runtime}/presentation-adapter.js +0 -0
  462. /package/dist/{src/foundation/ui-apps → ui/apps}/contracts.js +0 -0
  463. /package/dist/{src/foundation/ui-apps → ui/apps}/index.d.ts +0 -0
  464. /package/dist/{src/foundation/ui-apps → ui/apps}/index.js +0 -0
  465. /package/dist/{src/foundation/ui-components → ui/components}/dialog-panel.d.ts +0 -0
  466. /package/dist/{src/foundation/ui-components → ui/components}/label.d.ts +0 -0
  467. /package/dist/{src/foundation/ui-components → ui/components}/label.js +0 -0
  468. /package/dist/{src/foundation/ui-components → ui/components}/list-block.d.ts +0 -0
  469. /package/dist/{src/foundation/ui-components → ui/components}/list-block.js +0 -0
  470. /package/dist/{src/foundation/ui-components → ui/components}/pane.d.ts +0 -0
  471. /package/dist/{src/foundation/ui-components → ui/components}/pane.js +0 -0
  472. /package/dist/{src/foundation/ui-components → ui/components}/revision.d.ts +0 -0
  473. /package/dist/{src/foundation/ui-components → ui/components}/revision.js +0 -0
  474. /package/dist/{src/foundation/ui-components → ui/components}/status-line.d.ts +0 -0
  475. /package/dist/{src/foundation/ui-components → ui/components}/status-line.js +0 -0
  476. /package/dist/{src/foundation/ui-components → ui/components}/stepper.d.ts +0 -0
  477. /package/dist/{src/foundation/ui-components → ui/components}/stepper.js +0 -0
  478. /package/dist/{src/foundation/ui-components → ui/components}/theme.d.ts +0 -0
  479. /package/dist/{src/foundation/ui-components → ui/components}/theme.js +0 -0
  480. /package/dist/{src/foundation/ui-components → ui/components}/value-menu.d.ts +0 -0
  481. /package/dist/{src/foundation/ui-components → ui/components}/value-menu.js +0 -0
  482. /package/dist/{src/foundation/owned-ui-settings → ui/settings}/index.d.ts +0 -0
  483. /package/dist/{src/foundation/owned-ui-settings → ui/settings}/index.js +0 -0
  484. /package/dist/{src/foundation/owned-ui-settings → ui/settings}/resolution.d.ts +0 -0
  485. /package/dist/{src/foundation/owned-ui-settings → ui/settings}/resolution.js +0 -0
@@ -1,11 +1,14 @@
1
1
  import { execFile } from "node:child_process";
2
+ import { createHash } from "node:crypto";
3
+ import { existsSync } from "node:fs";
2
4
  import { mkdir, rm, writeFile } from "node:fs/promises";
3
5
  import { tmpdir } from "node:os";
4
6
  import { dirname, join, resolve } from "node:path";
5
7
  import { promisify } from "node:util";
6
- import { PRODUCT_IDENTITY } from "../../product-identity.js";
7
- import { copyToClipboard, DefaultPackageManager, getAgentDir, ProjectTrustStore, SessionManager, } from "@earendil-works/pi-coding-agent";
8
- import { OWNED_UI_EXTENSION_CONTRACT_VERSION, OWNED_UI_EXTENSION_RENDER_CALLBACKS, OWNED_UI_EXTENSION_UI_CALLBACKS, OWNED_UI_EXTENSION_UI_PROPERTIES, assertOwnedUiCommand, assertOwnedUiExtensionUiPort, assertOwnedUiSnapshot, } from "../owned-ui-contracts/index.js";
8
+ import { PRODUCT_IDENTITY } from "../../../product-identity.js";
9
+ import { configureOwnedHttpDispatcher } from "./http-dispatcher.js";
10
+ import { copyToClipboard, CredentialSynchronizationError, DefaultPackageManager, getAgentDir, ProjectTrustStore, SessionManager, VERSION, } from "@earendil-works/pi-coding-agent";
11
+ import { OWNED_UI_EXTENSION_CONTRACT_VERSION, OWNED_UI_EXTENSION_RENDER_CALLBACKS, OWNED_UI_EXTENSION_UI_CALLBACKS, OWNED_UI_EXTENSION_UI_PROPERTIES, assertOwnedUiCommand, assertOwnedUiExtensionUiPort, assertOwnedUiSnapshot, } from "../../../contracts/owned-ui/index.js";
9
12
  import { PINNED_PI_SETTINGS_CALLBACKS, PINNED_PI_WORKFLOW_COMMAND_NAMES, } from "./workflows.js";
10
13
  import { createPiRuntimeIntegration } from "./runtime-integration.js";
11
14
  import { PiSessionCommandIntegration } from "./session-integration.js";
@@ -16,19 +19,71 @@ const execFileAsync = promisify(execFile);
16
19
  * that a streaming burst never holds input, large enough that an ordinary turn is one
17
20
  * batch.
18
21
  */
19
- const EVENT_DELIVERY_BATCH = 16;
22
+ // Performance: deliver at most one engine event per event-loop turn. Transcript updates can
23
+ // be expensive in long sessions; a larger synchronous batch starves terminal
24
+ // input and makes an in-progress mouse selection appear frozen.
25
+ const EVENT_DELIVERY_BATCH = 1;
26
+ const TOOL_UPDATE_COALESCE_MS = 50;
27
+ const AUTH_REFRESH_TIMEOUT_MS = 15_000;
28
+ // Provenance: Pi 0.84.2 core/model-resolver.ts defaultModelPerProvider.
29
+ const PINNED_DEFAULT_MODEL_BY_PROVIDER = Object.freeze({
30
+ "amazon-bedrock": "us.anthropic.claude-opus-4-6-v1",
31
+ "ant-ling": "Ring-2.6-1T",
32
+ anthropic: "claude-opus-4-8",
33
+ openai: "gpt-5.5",
34
+ "azure-openai-responses": "gpt-5.4",
35
+ "openai-codex": "gpt-5.5",
36
+ radius: "auto",
37
+ nvidia: "nvidia/nemotron-3-super-120b-a12b",
38
+ deepseek: "deepseek-v4-pro",
39
+ google: "gemini-3.1-pro-preview",
40
+ "google-vertex": "gemini-3.1-pro-preview",
41
+ "github-copilot": "gpt-5.4",
42
+ openrouter: "moonshotai/kimi-k2.6",
43
+ "vercel-ai-gateway": "zai/glm-5.1",
44
+ xai: "grok-4.5",
45
+ groq: "openai/gpt-oss-120b",
46
+ cerebras: "zai-glm-4.7",
47
+ zai: "glm-5.1",
48
+ "zai-coding-cn": "glm-5.1",
49
+ mistral: "devstral-medium-latest",
50
+ minimax: "MiniMax-M2.7",
51
+ "minimax-cn": "MiniMax-M2.7",
52
+ moonshotai: "kimi-k2.6",
53
+ "moonshotai-cn": "kimi-k2.6",
54
+ huggingface: "moonshotai/Kimi-K2.6",
55
+ fireworks: "accounts/fireworks/models/kimi-k2p6",
56
+ together: "moonshotai/Kimi-K2.6",
57
+ baseten: "zai-org/GLM-5.2",
58
+ opencode: "kimi-k2.6",
59
+ "opencode-go": "kimi-k2.6",
60
+ "kimi-coding": "kimi-for-coding",
61
+ "cloudflare-workers-ai": "@cf/moonshotai/kimi-k2.6",
62
+ "cloudflare-ai-gateway": "workers-ai/@cf/moonshotai/kimi-k2.6",
63
+ "qwen-token-plan": "qwen3.7-max",
64
+ "qwen-token-plan-cn": "qwen3.7-max",
65
+ "qwen-token-plan-individual": "qwen3.8-max",
66
+ xiaomi: "mimo-v2.5-pro",
67
+ "xiaomi-token-plan-cn": "mimo-v2.5-pro",
68
+ "xiaomi-token-plan-ams": "mimo-v2.5-pro",
69
+ "xiaomi-token-plan-sgp": "mimo-v2.5-pro",
70
+ });
20
71
  const DEFAULT_SURFACE = {
21
72
  columns: 100,
22
73
  rows: 32,
23
74
  focusedRegion: "editor",
24
75
  hardwareCursor: false,
25
76
  };
77
+ /** Owns the pinned Pi session lifecycle and translates its events into the neutral agent-engine contract. */
26
78
  export class PiEngineAdapter {
27
79
  #runtimeFactory;
28
80
  #checkPackageUpdates;
29
81
  #cwd;
30
82
  #agentDir;
31
83
  #sessionId;
84
+ #sessionPath;
85
+ #sessionSelection;
86
+ #sessionForkPrompt;
32
87
  #workflowHost;
33
88
  #workflowInteraction;
34
89
  #listeners = new Set();
@@ -59,9 +114,15 @@ export class PiEngineAdapter {
59
114
  #activeCommandIds = [];
60
115
  #completedCommands = new Map();
61
116
  #transcript = [];
117
+ #transcriptIndex = new Map();
118
+ #transcriptSnapshot;
62
119
  #messageBlockIds = new WeakMap();
63
120
  #messageFallbackIds = new Map();
64
121
  #toolBlockIds = new Map();
122
+ #transcriptImageAssets = new Map();
123
+ #pendingToolUpdates = new Map();
124
+ #toolUpdateFlush = null;
125
+ #usageCache;
65
126
  #nextBlockSequence = 0;
66
127
  #diagnostics = [];
67
128
  #eventQueue = [];
@@ -75,12 +136,18 @@ export class PiEngineAdapter {
75
136
  #extensionShutdown;
76
137
  #extensionBound = false;
77
138
  #disposed = false;
78
- /** Lists the themes installed on this machine, when the caller supplies one. */
139
+ #settingsProductMode;
140
+ #projectTrustPrompt;
141
+ #settingsIntegration;
142
+ #settingsIntegrationManager;
79
143
  #availableThemes;
80
144
  constructor(options = {}) {
81
145
  this.#cwd = options.cwd ?? process.cwd();
82
146
  this.#agentDir = options.agentDir ?? getAgentDir();
83
147
  this.#sessionId = options.sessionId ?? "owned-session-1";
148
+ this.#sessionPath = options.sessionPath;
149
+ this.#sessionSelection = options.sessionSelection;
150
+ this.#sessionForkPrompt = options.sessionForkPrompt;
84
151
  this.#runtimeFactory = options.createRuntime ?? createDefaultPiRuntime;
85
152
  this.#checkPackageUpdates = options.checkPackageUpdates
86
153
  ?? (options.createRuntime
@@ -88,6 +155,8 @@ export class PiEngineAdapter {
88
155
  : settingsManager => checkDefaultPiPackageUpdates(this.#cwd, this.#agentDir, settingsManager));
89
156
  this.#workflowHost = options.workflowHost ?? defaultWorkflowHost();
90
157
  this.#availableThemes = options.availableThemes ?? null;
158
+ this.#settingsProductMode = options.settingsProductMode ?? "bare";
159
+ this.#projectTrustPrompt = options.projectTrustPrompt;
91
160
  this.#workflowInteraction = { prompt: async () => null, notify() { } };
92
161
  }
93
162
  setWorkflowInteractionHost(interaction) {
@@ -99,9 +168,35 @@ export class PiEngineAdapter {
99
168
  get sessionGeneration() {
100
169
  return this.#sessionGeneration;
101
170
  }
171
+ get cwd() {
172
+ return this.#runtime?.cwd ?? this.#cwd;
173
+ }
102
174
  get agentDir() {
103
175
  return this.#agentDir;
104
176
  }
177
+ resolveTranscriptImage(assetId) {
178
+ return this.#transcriptImageAssets.get(assetId) ?? null;
179
+ }
180
+ currentSessionFile() {
181
+ const value = this.#session?.sessionManager?.getSessionFile?.();
182
+ return typeof value === "string" && value.length > 0 ? value : null;
183
+ }
184
+ currentSessionResumeMetadata() {
185
+ const manager = this.#session?.sessionManager;
186
+ if (manager === undefined
187
+ || typeof manager.isPersisted !== "function"
188
+ || typeof manager.getSessionId !== "function"
189
+ || typeof manager.getSessionDir !== "function"
190
+ || typeof manager.usesDefaultSessionDir !== "function"
191
+ || manager.isPersisted() !== true
192
+ || !existsSync(this.currentSessionFile() ?? ""))
193
+ return null;
194
+ const sessionId = manager.getSessionId();
195
+ const sessionDir = manager.getSessionDir();
196
+ if (!sessionId || !sessionDir)
197
+ return null;
198
+ return { sessionId, sessionDir, usesDefaultSessionDir: manager.usesDefaultSessionDir() };
199
+ }
105
200
  get disposed() {
106
201
  return this.#disposed;
107
202
  }
@@ -112,12 +207,17 @@ export class PiEngineAdapter {
112
207
  cwd: this.#cwd,
113
208
  agentDir: this.#agentDir,
114
209
  sessionId: this.#sessionId,
210
+ ...(this.#sessionPath === undefined ? {} : { sessionPath: this.#sessionPath }),
211
+ ...(this.#sessionSelection === undefined ? {} : { sessionSelection: this.#sessionSelection }),
212
+ ...(this.#sessionForkPrompt === undefined ? {} : { sessionForkPrompt: this.#sessionForkPrompt }),
213
+ ...(this.#projectTrustPrompt === undefined ? {} : { projectTrustPrompt: this.#projectTrustPrompt }),
115
214
  }).catch(error => {
116
215
  this.#lifecycle = "failed";
117
216
  this.#addDiagnostic("error", "engine-startup", error instanceof Error ? error.message : String(error), false);
118
217
  throw error;
119
218
  });
120
219
  this.#runtime = runtime;
220
+ this.#cwd = runtime.cwd ?? this.#cwd;
121
221
  this.#gitBranch = await readGitBranch(this.#cwd);
122
222
  this.#terminal = {
123
223
  ...this.#terminal,
@@ -131,6 +231,7 @@ export class PiEngineAdapter {
131
231
  this.#addDiagnostic(diagnostic.type === "error" ? "error" : diagnostic.type === "warning" ? "warning" : "info", "engine-startup", diagnostic.message, diagnostic.type !== "error");
132
232
  }
133
233
  this.#bindSession(runtime.session);
234
+ await this.#announceChangelog(runtime.services.settingsManager);
134
235
  this.#lifecycle = "ready";
135
236
  this.#editor = { ...this.#editor, submitEnabled: true };
136
237
  this.#emitEvent({ type: "session-lifecycle", lifecycle: "ready", reason: null });
@@ -138,6 +239,19 @@ export class PiEngineAdapter {
138
239
  void this.#announcePackageUpdates(runtime.services.settingsManager);
139
240
  return this.view();
140
241
  }
242
+ async #announceChangelog(settingsManager) {
243
+ if (!settingsManager || typeof settingsManager.getLastChangelogVersion !== "function"
244
+ || typeof settingsManager.setLastChangelogVersion !== "function")
245
+ return;
246
+ if (settingsManager.getLastChangelogVersion() === VERSION)
247
+ return;
248
+ const markdown = await this.#workflowHost.readChangelog().catch(() => "");
249
+ if (markdown.trim().length > 0) {
250
+ this.#addDiagnostic("info", settingsManager.getCollapseChangelog() ? "changelog-collapsed" : "changelog-expanded", markdown, true);
251
+ }
252
+ settingsManager.setLastChangelogVersion(VERSION);
253
+ await settingsManager.flush();
254
+ }
141
255
  async #announcePackageUpdates(settingsManager) {
142
256
  if (process.env.PI_OFFLINE)
143
257
  return;
@@ -160,6 +274,7 @@ export class PiEngineAdapter {
160
274
  return () => this.#listeners.delete(listener);
161
275
  }
162
276
  async flushEvents() {
277
+ this.#flushPendingToolUpdates();
163
278
  while (this.#eventQueueProcessing)
164
279
  await this.#eventQueueProcessing;
165
280
  }
@@ -398,7 +513,8 @@ export class PiEngineAdapter {
398
513
  const session = this.#requireWorkflowSession();
399
514
  const result = await requireCapability(session.cycleModel, "cycleModel").call(session, direction);
400
515
  if (!isRecord(result) || !isRecord(result.model)) {
401
- return workflowResult("model", "cancelled", "No other model is available");
516
+ const scoped = Array.isArray(session.scopedModels) && session.scopedModels.length > 0;
517
+ return workflowResult("model", "completed", scoped ? "Only one model in scope" : "Only one model available", undefined, "status");
402
518
  }
403
519
  const model = readModel(result.model);
404
520
  if (model)
@@ -406,7 +522,11 @@ export class PiEngineAdapter {
406
522
  if (result.thinkingLevel !== undefined)
407
523
  this.#thinkingLevel = readThinkingLevel(result.thinkingLevel);
408
524
  this.#emitView();
409
- return workflowResult("model", "completed", model ? `Selected model ${model.providerId}/${model.modelId}` : "Selected model");
525
+ const modelName = stringProperty(result.model, "name") ?? stringProperty(result.model, "id") ?? "model";
526
+ const reasoning = result.model.reasoning === true;
527
+ const thinking = this.#thinkingLevel;
528
+ const suffix = reasoning && thinking !== "off" ? ` (thinking: ${thinking})` : "";
529
+ return workflowResult("model", "completed", `Switched to ${modelName}${suffix}`, undefined, "status");
410
530
  }
411
531
  catch (error) {
412
532
  return workflowResult("model", "failed", error instanceof Error ? error.message : String(error));
@@ -422,16 +542,107 @@ export class PiEngineAdapter {
422
542
  /** Settings port for the live runtime, or null before the runtime is available. */
423
543
  settingsPort() {
424
544
  const settings = this.#runtime?.services.settingsManager;
425
- if (!settings)
545
+ if (!settings || typeof settings.getCompactionEnabled !== "function")
426
546
  return null;
427
- const session = this.#runtime?.session;
428
- return new PiSettingsIntegration(settings, {
429
- ...(this.#availableThemes === null ? {} : { themes: this.#availableThemes }),
430
- thinkingLevels: () => {
431
- const levels = session?.getAvailableThinkingLevels?.();
432
- return Array.isArray(levels) ? levels.map(level => String(level)) : [];
433
- },
434
- });
547
+ if (this.#settingsIntegration === undefined || this.#settingsIntegrationManager !== settings) {
548
+ this.#settingsIntegrationManager = settings;
549
+ configureOwnedHttpDispatcher(settings.getHttpIdleTimeoutMs());
550
+ this.#settingsIntegration = new PiSettingsIntegration(settings, {
551
+ ...(this.#availableThemes === null ? {} : { themes: this.#availableThemes }),
552
+ thinkingLevels: () => {
553
+ const levels = this.#runtime?.session?.getAvailableThinkingLevels?.();
554
+ return Array.isArray(levels) ? levels.map(level => String(level)) : [];
555
+ },
556
+ productMode: this.#settingsProductMode,
557
+ });
558
+ this.#settingsIntegration.bindOwner("shell", {
559
+ enableSkillCommands: { apply: value => {
560
+ if (typeof value !== "boolean")
561
+ throw new TypeError("Skill commands value is invalid");
562
+ settings.setEnableSkillCommands(value);
563
+ } },
564
+ doubleEscapeAction: { apply: value => {
565
+ if (value !== "tree" && value !== "fork" && value !== "none")
566
+ throw new TypeError("Double-Escape action is invalid");
567
+ settings.setDoubleEscapeAction(value);
568
+ } },
569
+ treeFilterMode: { apply: value => {
570
+ if (value !== "default" && value !== "no-tools" && value !== "user-only" && value !== "labeled-only" && value !== "all")
571
+ throw new TypeError("Tree filter mode is invalid");
572
+ settings.setTreeFilterMode(value);
573
+ } },
574
+ showCacheMissNotices: { apply: value => {
575
+ if (typeof value !== "boolean")
576
+ throw new TypeError("Cache-miss notice setting is invalid");
577
+ settings.setShowCacheMissNotices(value);
578
+ } },
579
+ });
580
+ this.#settingsIntegration.bindOwner("startup", {
581
+ // Invariant: deferred application is the owner operation: the next preflight reads
582
+ // the persisted default before constructing project-backed services.
583
+ defaultProjectTrust: { apply() { } },
584
+ collapseChangelog: { apply() { } },
585
+ });
586
+ this.#settingsIntegration.bindOwner("agent", {
587
+ autoCompact: { apply: value => {
588
+ if (typeof value !== "boolean")
589
+ throw new TypeError("Auto compact value is invalid");
590
+ this.#requireWorkflowSession().setAutoCompactionEnabled(value);
591
+ } },
592
+ autoResizeImages: { apply: value => {
593
+ if (typeof value !== "boolean")
594
+ throw new TypeError("Auto-resize images value is invalid");
595
+ settings.setImageAutoResize(value);
596
+ } },
597
+ blockImages: { apply: value => {
598
+ if (typeof value !== "boolean")
599
+ throw new TypeError("Block images value is invalid");
600
+ settings.setBlockImages(value);
601
+ } },
602
+ steeringMode: { apply: value => {
603
+ if (value !== "all" && value !== "one-at-a-time")
604
+ throw new TypeError("Steering mode is invalid");
605
+ this.#requireWorkflowSession().setSteeringMode(value);
606
+ } },
607
+ followUpMode: { apply: value => {
608
+ if (value !== "all" && value !== "one-at-a-time")
609
+ throw new TypeError("Follow-up mode is invalid");
610
+ this.#requireWorkflowSession().setFollowUpMode(value);
611
+ } },
612
+ transport: { apply: value => {
613
+ if (value !== "sse" && value !== "websocket" && value !== "websocket-cached" && value !== "auto")
614
+ throw new TypeError("Transport is invalid");
615
+ this.#requireWorkflowSession().agent.transport = value;
616
+ } },
617
+ httpIdleTimeoutMs: { apply: value => {
618
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)
619
+ throw new TypeError("HTTP idle timeout is invalid");
620
+ // Invariant: provider streaming reads the manager per request; global fetch uses
621
+ // the matching owned dispatcher and zero maps to disabled semantics.
622
+ configureOwnedHttpDispatcher(value);
623
+ settings.setHttpIdleTimeoutMs(value);
624
+ } },
625
+ thinkingLevel: { apply: value => {
626
+ if (!isThinkingLevel(value))
627
+ throw new TypeError("Thinking level is invalid");
628
+ this.#requireWorkflowSession().setThinkingLevel(value);
629
+ this.#thinkingLevel = readThinkingLevel(this.#requireWorkflowSession().thinkingLevel);
630
+ this.#emitView();
631
+ } },
632
+ warnings: { apply: value => {
633
+ if (!isRecord(value) || Object.values(value).some(flag => typeof flag !== "boolean"))
634
+ throw new TypeError("Warnings setting is invalid");
635
+ settings.setWarnings(value);
636
+ } },
637
+ });
638
+ }
639
+ return this.#settingsIntegration;
640
+ }
641
+ bindSettingsOwner(owner, handlers) {
642
+ const settings = this.settingsPort();
643
+ if (settings === null)
644
+ return () => { };
645
+ return settings.bindOwner(owner, handlers);
435
646
  }
436
647
  pinnedModelSelectorContext() {
437
648
  const session = this.#requireWorkflowSession();
@@ -708,9 +919,9 @@ export class PiEngineAdapter {
708
919
  excludeFromContext,
709
920
  };
710
921
  }
711
- applyPinnedSettingValue(callback, value) {
922
+ async applyPinnedSettingValue(callback, value) {
712
923
  try {
713
- return this.#applyPinnedSetting(callback, value, true);
924
+ return await this.#applyPinnedSetting(callback, value, true);
714
925
  }
715
926
  catch (error) {
716
927
  return workflowResult("settings", "failed", error instanceof Error ? error.message : String(error));
@@ -724,7 +935,13 @@ export class PiEngineAdapter {
724
935
  const message = error instanceof Error ? error.message : String(error);
725
936
  const contextualMessage = request.command === "export"
726
937
  ? `Failed to export session: ${message}`
727
- : request.command === "reload" ? `Reload failed: ${message}` : message;
938
+ : request.command === "import"
939
+ ? `Failed to import session: ${message}`
940
+ : request.command === "new"
941
+ ? `Failed to create session: ${message}`
942
+ : request.command === "resume"
943
+ ? `Failed to resume session: ${message}`
944
+ : request.command === "reload" ? `Reload failed: ${message}` : message;
728
945
  return workflowResult(request.command, "failed", contextualMessage);
729
946
  }
730
947
  }
@@ -734,13 +951,13 @@ export class PiEngineAdapter {
734
951
  sessionId: this.#sessionId,
735
952
  revision: this.#viewRevision,
736
953
  lifecycle: this.#lifecycle,
737
- transcript: this.#transcript.map(block => ({ ...block })),
954
+ transcript: this.#transcriptSnapshot ??= Object.freeze([...this.#transcript]),
738
955
  editor: { ...this.#editor, queuedSubmissions: [...this.#editor.queuedSubmissions] },
739
956
  status: {
740
957
  ...this.#status,
741
958
  diagnostics: [...this.#status.diagnostics],
742
959
  badges: [...this.#status.badges],
743
- usage: this.#readUsage(),
960
+ usage: this.#usageCache ??= this.#readUsage(),
744
961
  footer: {
745
962
  branch: this.#gitBranch,
746
963
  sessionName: this.#session?.sessionManager?.getSessionName() ?? null,
@@ -806,6 +1023,12 @@ export class PiEngineAdapter {
806
1023
  if (this.#disposed)
807
1024
  return;
808
1025
  this.#disposed = true;
1026
+ if (this.#toolUpdateFlush !== null) {
1027
+ clearTimeout(this.#toolUpdateFlush);
1028
+ this.#toolUpdateFlush = null;
1029
+ }
1030
+ this.#pendingToolUpdates.clear();
1031
+ this.#transcriptImageAssets.clear();
809
1032
  this.#extensionBound = false;
810
1033
  this.#extensionUi = undefined;
811
1034
  this.#extensionShutdown = undefined;
@@ -831,22 +1054,66 @@ export class PiEngineAdapter {
831
1054
  case "settings": {
832
1055
  if (!selection)
833
1056
  return workflowResult(request.command, "failed", "Settings requires the owned settings controller");
834
- return this.#applyPinnedSetting(selection);
1057
+ return await this.#applyPinnedSetting(selection);
835
1058
  }
836
1059
  case "model": {
837
1060
  const reference = selection ?? argument;
838
1061
  if (!reference)
839
1062
  return workflowResult(request.command, "failed", "Model requires the owned model controller");
840
- const [providerId, modelId] = reference.split("/", 2);
841
- if (!providerId || !modelId)
842
- return workflowResult(request.command, "failed", "Model requires provider/model");
843
- const model = runtime.services.modelRuntime.getModel(providerId, modelId);
844
- if (!model)
845
- return workflowResult(request.command, "failed", `Model is unavailable: ${reference}`);
1063
+ const scopedModels = Array.isArray(session.scopedModels)
1064
+ ? session.scopedModels.map(item => item.model)
1065
+ : [];
1066
+ let availableModels = scopedModels.length > 0
1067
+ ? scopedModels
1068
+ : [...(runtime.services.modelRuntime.getAvailableSnapshot?.() ?? [])];
1069
+ let model = findExactWorkflowModel(reference, availableModels);
1070
+ const messages = [];
1071
+ if (model === undefined && scopedModels.length === 0) {
1072
+ messages.push({ kind: "status", message: "Refreshing model catalogs…" });
1073
+ const controller = new AbortController();
1074
+ let timedOut = false;
1075
+ const timeout = setTimeout(() => {
1076
+ timedOut = true;
1077
+ controller.abort();
1078
+ }, AUTH_REFRESH_TIMEOUT_MS);
1079
+ try {
1080
+ const refreshed = await runtime.services.modelRuntime.refresh?.({ signal: controller.signal });
1081
+ if (isRecord(refreshed) && refreshed.aborted === true && timedOut) {
1082
+ messages.push({ kind: "warning", message: "Model refresh timed out; searching cached models." });
1083
+ }
1084
+ else if (isRecord(refreshed) && refreshed.errors instanceof Map && refreshed.errors.size > 0) {
1085
+ messages.push({ kind: "warning", message: `Could not refresh ${[...refreshed.errors.keys()].join(", ")}; searching cached models.` });
1086
+ }
1087
+ }
1088
+ catch (error) {
1089
+ messages.push({
1090
+ kind: "warning",
1091
+ message: timedOut
1092
+ ? "Model refresh timed out; searching cached models."
1093
+ : `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`,
1094
+ });
1095
+ }
1096
+ finally {
1097
+ clearTimeout(timeout);
1098
+ }
1099
+ availableModels = [...(runtime.services.modelRuntime.getAvailableSnapshot?.() ?? [])];
1100
+ model = findExactWorkflowModel(reference, availableModels);
1101
+ }
1102
+ if (model === undefined) {
1103
+ return {
1104
+ ...workflowResult(request.command, "requires-selection", "Select a model", reference, "silent"),
1105
+ ...(messages.length === 0 ? {} : { messages: Object.freeze(messages) }),
1106
+ };
1107
+ }
846
1108
  await session.setModel(model);
1109
+ const providerId = stringProperty(model, "provider") ?? "unknown";
1110
+ const modelId = stringProperty(model, "id") ?? reference;
847
1111
  this.#activeModel = { providerId, modelId, displayName: stringProperty(model, "name") ?? modelId };
848
1112
  this.#emitView();
849
- return workflowResult(request.command, "completed", `Model: ${modelId}`);
1113
+ const resultMessage = { kind: "status", message: `Model: ${modelId}` };
1114
+ return messages.length === 0
1115
+ ? workflowResult(request.command, "completed", resultMessage.message)
1116
+ : workflowResult(request.command, "completed", resultMessage.message, undefined, "status", [...messages, resultMessage]);
850
1117
  }
851
1118
  case "scoped-models": {
852
1119
  if (!selection)
@@ -873,27 +1140,71 @@ export class PiEngineAdapter {
873
1140
  return workflowConfirmation(request.command, `Replace current session with ${path}?`);
874
1141
  }
875
1142
  if (!request.confirmed)
876
- return workflowResult(request.command, "cancelled", "Import cancelled");
877
- const result = await requireCapability(runtime.importFromJsonl, "importFromJsonl").call(runtime, path);
878
- if (isRecord(result) && result.cancelled === true)
879
- return workflowResult(request.command, "cancelled", "Import cancelled");
880
- return workflowResult(request.command, "completed", `Session imported from: ${path}`);
1143
+ return workflowResult(request.command, "cancelled", "Import cancelled", undefined, "status");
1144
+ try {
1145
+ const result = await requireCapability(runtime.importFromJsonl, "importFromJsonl").call(runtime, path, request.cwdOverride);
1146
+ if (isRecord(result) && result.cancelled === true)
1147
+ return workflowResult(request.command, "cancelled", "Import cancelled", undefined, "status");
1148
+ return workflowResult(request.command, "completed", `Session imported from: ${path}`);
1149
+ }
1150
+ catch (error) {
1151
+ const issue = isRecord(error) && isRecord(error.issue) ? error.issue : undefined;
1152
+ const fallbackCwd = issue === undefined ? undefined : stringProperty(issue, "fallbackCwd");
1153
+ if (fallbackCwd !== undefined && request.cwdOverride === undefined) {
1154
+ const sessionCwd = stringProperty(issue, "sessionCwd") ?? "the session working directory";
1155
+ return workflowConfirmation(request.command, `cwd from session file does not exist\n${sessionCwd}\n\ncontinue in current cwd\n${fallbackCwd}`, fallbackCwd);
1156
+ }
1157
+ throw error;
1158
+ }
881
1159
  }
882
1160
  case "share": {
883
- const auth = await this.#workflowHost.runCommand("gh", ["auth", "status"]).catch(error => {
884
- throw new Error(`GitHub CLI is unavailable or not logged in: ${error instanceof Error ? error.message : String(error)}`);
885
- });
886
- if (auth.stderr && !auth.stdout)
887
- throw new Error(auth.stderr.trim());
1161
+ const signal = request.signal;
1162
+ if (signal?.aborted)
1163
+ return workflowResult(request.command, "cancelled", "Share cancelled", undefined, "status");
1164
+ let auth;
1165
+ try {
1166
+ auth = await this.#workflowHost.runCommand("gh", ["auth", "status"], signal === undefined ? undefined : { signal });
1167
+ }
1168
+ catch (error) {
1169
+ if (signal?.aborted || isAbortError(error))
1170
+ return workflowResult(request.command, "cancelled", "Share cancelled", undefined, "status");
1171
+ const message = commandMissing(error)
1172
+ ? "GitHub CLI (gh) is not installed. Install it from https://cli.github.com/"
1173
+ : "GitHub CLI is not logged in. Run 'gh auth login' first.";
1174
+ return workflowResult(request.command, "failed", message);
1175
+ }
1176
+ if (auth.stderr && !auth.stdout) {
1177
+ return workflowResult(request.command, "failed", "GitHub CLI is not logged in. Run 'gh auth login' first.");
1178
+ }
888
1179
  const temporary = join(tmpdir(), `${PRODUCT_IDENTITY.filesystem.temporaryPrefix}pi-session-${process.pid}.html`);
889
1180
  try {
890
- await requireCapability(session.exportToHtml, "exportToHtml").call(session, temporary);
891
- const gist = await this.#workflowHost.runCommand("gh", ["gist", "create", "--public=false", temporary]);
1181
+ try {
1182
+ await requireCapability(session.exportToHtml, "exportToHtml").call(session, temporary);
1183
+ }
1184
+ catch (error) {
1185
+ return workflowResult(request.command, "failed", `Failed to export session: ${errorMessage(error, "Unknown error")}`);
1186
+ }
1187
+ if (signal?.aborted)
1188
+ return workflowResult(request.command, "cancelled", "Share cancelled", undefined, "status");
1189
+ let gist;
1190
+ try {
1191
+ gist = await this.#workflowHost.runCommand("gh", ["gist", "create", "--public=false", temporary], signal === undefined ? undefined : { signal });
1192
+ }
1193
+ catch (error) {
1194
+ if (signal?.aborted || isAbortError(error))
1195
+ return workflowResult(request.command, "cancelled", "Share cancelled", undefined, "status");
1196
+ return workflowResult(request.command, "failed", `Failed to create gist: ${errorMessage(error, "Unknown error")}`);
1197
+ }
1198
+ if (signal?.aborted)
1199
+ return workflowResult(request.command, "cancelled", "Share cancelled", undefined, "status");
1200
+ if (gist.stderr && !gist.stdout) {
1201
+ return workflowResult(request.command, "failed", `Failed to create gist: ${gist.stderr.trim() || "Unknown error"}`);
1202
+ }
892
1203
  const gistUrl = gist.stdout.trim();
893
1204
  const gistId = gistUrl.split("/").at(-1);
894
1205
  if (!gistId)
895
- throw new Error("Failed to parse gist ID from gh output");
896
- return workflowResult(request.command, "completed", `Share URL: https://pi.gptscript.ai/gist/${gistId}`, gistUrl);
1206
+ return workflowResult(request.command, "failed", "Failed to parse gist ID from gh output");
1207
+ return workflowResult(request.command, "completed", `Share URL: ${shareViewerUrl(gistId)}`, gistUrl);
897
1208
  }
898
1209
  finally {
899
1210
  await rm(temporary, { force: true });
@@ -912,7 +1223,7 @@ export class PiEngineAdapter {
912
1223
  const current = manager?.getSessionName();
913
1224
  return typeof current === "string"
914
1225
  ? workflowResult(request.command, "completed", `Session name: ${current}`)
915
- : workflowResult(request.command, "failed", "Usage: /name <name>");
1226
+ : workflowResult(request.command, "failed", "Usage: /name <name>", undefined, "warning");
916
1227
  }
917
1228
  requireCapability(session.setSessionName, "setSessionName").call(session, argument);
918
1229
  const normalized = manager?.getSessionName();
@@ -939,17 +1250,17 @@ export class PiEngineAdapter {
939
1250
  return workflowResult(request.command, "failed", "Fork requires the owned user-message controller");
940
1251
  const result = await requireCapability(runtime.fork, "fork").call(runtime, selection, { position: "before" });
941
1252
  if (isRecord(result) && result.cancelled === true)
942
- return workflowResult(request.command, "cancelled", "Fork cancelled");
1253
+ return workflowResult(request.command, "cancelled", "Fork cancelled", undefined, "silent");
943
1254
  return workflowResult(request.command, "completed", "Forked to new session");
944
1255
  }
945
1256
  case "clone": {
946
1257
  const manager = session.sessionManager;
947
1258
  const leaf = manager?.getLeafId?.();
948
1259
  if (typeof leaf !== "string")
949
- return workflowResult(request.command, "failed", "No session position is available to clone");
1260
+ return workflowResult(request.command, "completed", "Nothing to clone yet", undefined, "status");
950
1261
  const result = await requireCapability(runtime.fork, "fork").call(runtime, leaf, { position: "at" });
951
1262
  if (isRecord(result) && result.cancelled === true)
952
- return workflowResult(request.command, "cancelled", "Clone cancelled");
1263
+ return workflowResult(request.command, "cancelled", "Clone cancelled", undefined, "silent");
953
1264
  return workflowResult(request.command, "completed", "Cloned to new session");
954
1265
  }
955
1266
  case "tree": {
@@ -963,9 +1274,9 @@ export class PiEngineAdapter {
963
1274
  ...(request.treeSummary.customInstructions === undefined ? {} : { customInstructions: request.treeSummary.customInstructions }),
964
1275
  });
965
1276
  if (isRecord(result) && result.aborted === true)
966
- return workflowResult(request.command, "cancelled", "Branch summarization cancelled");
1277
+ return workflowResult(request.command, "cancelled", "Branch summarization cancelled", undefined, "status");
967
1278
  if (isRecord(result) && result.cancelled === true)
968
- return workflowResult(request.command, "cancelled", "Navigation cancelled");
1279
+ return workflowResult(request.command, "cancelled", "Navigation cancelled", undefined, "status");
969
1280
  return workflowResult(request.command, "completed", "Navigated to selected point");
970
1281
  }
971
1282
  case "trust": {
@@ -1000,6 +1311,7 @@ export class PiEngineAdapter {
1000
1311
  const authType = authTypeValue === "api_key" ? "api_key" : "oauth";
1001
1312
  const provider = modelRuntime.getProvider?.(providerId);
1002
1313
  const providerName = stringProperty(provider, "name") ?? providerId;
1314
+ const previousModel = session.model;
1003
1315
  this.#workflowInteraction.startLogin?.({ providerId, providerName, authType });
1004
1316
  try {
1005
1317
  await requireCapability(modelRuntime.login, "login").call(modelRuntime, providerId, authType, {
@@ -1037,25 +1349,40 @@ export class PiEngineAdapter {
1037
1349
  });
1038
1350
  }
1039
1351
  catch (error) {
1040
- if (error instanceof Error && error.message === "Login cancelled")
1041
- return workflowResult(request.command, "cancelled", "Login cancelled");
1042
- throw error;
1352
+ if (error instanceof Error && error.message === "Login cancelled") {
1353
+ return workflowResult(request.command, "cancelled", "Login cancelled", undefined, "silent");
1354
+ }
1355
+ const detail = error instanceof Error ? error.message : String(error);
1356
+ const actionLabel = authType === "api_key" ? `Saved API key for ${providerName}` : `Logged in to ${providerName}`;
1357
+ const message = error instanceof CredentialSynchronizationError
1358
+ ? `${actionLabel}, but local model state could not be synchronized: ${detail}`
1359
+ : authType === "api_key"
1360
+ ? `Failed to save API key for ${providerName}: ${detail}`
1361
+ : `Failed to login to ${providerName}: ${detail}`;
1362
+ return workflowResult(request.command, "failed", message);
1043
1363
  }
1044
1364
  finally {
1045
1365
  this.#workflowInteraction.finishLogin?.();
1046
1366
  }
1047
- this.#reconcileActiveModelAvailability();
1048
- this.#emitView();
1049
- return workflowResult(request.command, "completed", `Logged in to ${providerName}. Credentials saved to ${join(this.#agentDir, "auth.json")}`);
1367
+ return await this.#completeProviderAuthentication(providerId, providerName, authType, previousModel);
1050
1368
  }
1051
1369
  case "logout": {
1052
1370
  if (!selection)
1053
1371
  return workflowResult(request.command, "failed", "Logout requires the owned authentication controller");
1054
1372
  const [credentialType = "oauth", providerId = selection] = selection.includes(":") ? selection.split(":", 2) : ["oauth", selection];
1055
1373
  const modelRuntime = runtime.services.modelRuntime;
1056
- await requireCapability(modelRuntime.logout, "logout").call(modelRuntime, providerId, { signal: AbortSignal.timeout(15_000) });
1057
1374
  const provider = modelRuntime.getProvider?.(providerId);
1058
1375
  const providerName = stringProperty(provider, "name") ?? providerId;
1376
+ try {
1377
+ await requireCapability(modelRuntime.logout, "logout").call(modelRuntime, providerId, { signal: AbortSignal.timeout(15_000) });
1378
+ }
1379
+ catch (error) {
1380
+ const detail = error instanceof Error ? error.message : String(error);
1381
+ const message = error instanceof CredentialSynchronizationError
1382
+ ? `Credentials removed for ${providerName}, but local model state could not be synchronized: ${detail}`
1383
+ : `Logout failed: ${detail}`;
1384
+ return workflowResult(request.command, "failed", message);
1385
+ }
1059
1386
  this.#reconcileActiveModelAvailability();
1060
1387
  this.#emitView();
1061
1388
  return workflowResult(request.command, "completed", credentialType === "api_key"
@@ -1065,12 +1392,17 @@ export class PiEngineAdapter {
1065
1392
  case "new": {
1066
1393
  const result = await runtime.newSession();
1067
1394
  if (isRecord(result) && result.cancelled === true)
1068
- return workflowResult(request.command, "cancelled", "New session cancelled");
1069
- return workflowResult(request.command, "completed", "✓ New session started");
1395
+ return workflowResult(request.command, "cancelled", "New session cancelled", undefined, "silent");
1396
+ return workflowResult(request.command, "completed", "✓ New session started", undefined, "accent");
1070
1397
  }
1071
1398
  case "compact": {
1072
- await session.compact(argument || undefined);
1073
- return workflowResult(request.command, "completed", "Compaction requested");
1399
+ try {
1400
+ await session.compact(argument || undefined);
1401
+ return workflowResult(request.command, "completed", "Compaction requested", undefined, "silent");
1402
+ }
1403
+ catch (error) {
1404
+ return workflowResult(request.command, "failed", error instanceof Error ? error.message : String(error), undefined, "silent");
1405
+ }
1074
1406
  }
1075
1407
  case "resume": {
1076
1408
  if (!selection && !argument)
@@ -1079,7 +1411,7 @@ export class PiEngineAdapter {
1079
1411
  try {
1080
1412
  const result = await runtime.switchSession(sessionPath);
1081
1413
  if (isRecord(result) && result.cancelled === true)
1082
- return workflowResult(request.command, "cancelled", "Resume cancelled");
1414
+ return workflowResult(request.command, "cancelled", "Resume cancelled", undefined, "silent");
1083
1415
  return workflowResult(request.command, "completed", "Resumed session");
1084
1416
  }
1085
1417
  catch (error) {
@@ -1092,18 +1424,18 @@ export class PiEngineAdapter {
1092
1424
  return workflowConfirmation(request.command, `cwd from session file does not exist\n${sessionCwd}\n\ncontinue in current cwd\n${fallbackCwd}`);
1093
1425
  }
1094
1426
  if (!request.confirmed)
1095
- return workflowResult(request.command, "cancelled", "Resume cancelled");
1427
+ return workflowResult(request.command, "cancelled", "Resume cancelled", undefined, "status");
1096
1428
  const result = await runtime.switchSession(sessionPath, { cwdOverride: fallbackCwd });
1097
1429
  if (isRecord(result) && result.cancelled === true)
1098
- return workflowResult(request.command, "cancelled", "Resume cancelled");
1430
+ return workflowResult(request.command, "cancelled", "Resume cancelled", undefined, "silent");
1099
1431
  return workflowResult(request.command, "completed", "Resumed session in current cwd");
1100
1432
  }
1101
1433
  }
1102
1434
  case "reload": {
1103
1435
  if (session.isStreaming)
1104
- return workflowResult(request.command, "failed", "Wait for the current response to finish before reloading.");
1436
+ return workflowResult(request.command, "failed", "Wait for the current response to finish before reloading.", undefined, "warning");
1105
1437
  if (session.isCompacting)
1106
- return workflowResult(request.command, "failed", "Wait for compaction to finish before reloading.");
1438
+ return workflowResult(request.command, "failed", "Wait for compaction to finish before reloading.", undefined, "warning");
1107
1439
  await requireCapability(session.reload, "reload").call(session);
1108
1440
  await this.#bindExtensionUiToSession();
1109
1441
  return workflowResult(request.command, "completed", "Reloaded keybindings, extensions, skills, prompts, themes, and context files");
@@ -1124,6 +1456,91 @@ export class PiEngineAdapter {
1124
1456
  return workflowResult(request.command, "completed", "Demented elves announcement");
1125
1457
  }
1126
1458
  }
1459
+ async #completeProviderAuthentication(providerId, providerName, authType, previousModel) {
1460
+ const session = this.#requireWorkflowSession();
1461
+ const modelRuntime = this.#runtime?.services.modelRuntime;
1462
+ if (!modelRuntime)
1463
+ throw new Error("engine runtime is unavailable");
1464
+ const actionLabel = authType === "oauth" ? `Logged in to ${providerName}` : `Saved API key for ${providerName}`;
1465
+ let selectedModelId;
1466
+ let selectionError;
1467
+ if (isUnknownModel(previousModel)) {
1468
+ const available = modelRuntime.getAvailableSnapshot?.() ?? [];
1469
+ const providerModels = available.filter(model => stringProperty(model, "provider") === providerId);
1470
+ const defaultModelId = PINNED_DEFAULT_MODEL_BY_PROVIDER[providerId];
1471
+ if (defaultModelId === undefined) {
1472
+ selectionError = `${actionLabel}, but no default model is configured for provider "${providerId}". Use /model to select a model.`;
1473
+ }
1474
+ else if (providerModels.length === 0) {
1475
+ selectionError = `${actionLabel}, but no models are available for that provider. Use /model to select a model.`;
1476
+ }
1477
+ else {
1478
+ const selectedModel = providerModels.find(model => stringProperty(model, "id") === defaultModelId);
1479
+ if (selectedModel === undefined) {
1480
+ selectionError = `${actionLabel}, but its default model "${defaultModelId}" is not available. Use /model to select a model.`;
1481
+ }
1482
+ else {
1483
+ try {
1484
+ await session.setModel(selectedModel);
1485
+ selectedModelId = stringProperty(selectedModel, "id") ?? defaultModelId;
1486
+ this.#activeModel = {
1487
+ providerId,
1488
+ modelId: selectedModelId,
1489
+ displayName: stringProperty(selectedModel, "name") ?? selectedModelId,
1490
+ };
1491
+ }
1492
+ catch (error) {
1493
+ selectionError = `${actionLabel}, but selecting its default model failed: ${error instanceof Error ? error.message : String(error)}. Use /model to select a model.`;
1494
+ }
1495
+ }
1496
+ }
1497
+ }
1498
+ this.#reconcileActiveModelAvailability();
1499
+ this.#emitView();
1500
+ const status = `${actionLabel}.${selectedModelId ? ` Selected ${selectedModelId}.` : ""} Credentials saved to ${join(this.#agentDir, "auth.json")}`;
1501
+ const messages = [
1502
+ { kind: "status", message: status },
1503
+ ...(selectionError === undefined ? [] : [{ kind: "error", message: selectionError }]),
1504
+ ];
1505
+ this.#scheduleAuthenticatedProviderRefresh(providerId, actionLabel);
1506
+ return workflowResult("login", "completed", status, undefined, "status", messages);
1507
+ }
1508
+ #scheduleAuthenticatedProviderRefresh(providerId, actionLabel) {
1509
+ const runtime = this.#runtime;
1510
+ const refresh = runtime?.services.modelRuntime.refresh;
1511
+ if (!runtime || typeof refresh !== "function")
1512
+ return;
1513
+ const generation = this.#sessionGeneration;
1514
+ const publish = this.#workflowInteraction.publish;
1515
+ const controller = new AbortController();
1516
+ const timeout = setTimeout(() => controller.abort(), AUTH_REFRESH_TIMEOUT_MS);
1517
+ // Compatibility: post-authentication refresh starts on the next turn so its notices follow the saved-credential status.
1518
+ void new Promise(resolve => setTimeout(resolve, 0))
1519
+ .then(() => refresh.call(runtime.services.modelRuntime, { providers: [providerId], signal: controller.signal }))
1520
+ .then(result => {
1521
+ if (this.#disposed || this.#sessionGeneration !== generation)
1522
+ return;
1523
+ if (isRecord(result) && result.aborted === true) {
1524
+ publish?.({ kind: "warning", message: `${actionLabel}, but its model catalog refresh timed out; using cached models.` });
1525
+ }
1526
+ else if (isRecord(result) && result.errors instanceof Map && result.errors.size > 0) {
1527
+ publish?.({ kind: "warning", message: `${actionLabel}, but its model catalog could not be refreshed; using cached models.` });
1528
+ }
1529
+ this.#reconcileActiveModelAvailability();
1530
+ this.#emitView();
1531
+ })
1532
+ .catch(error => {
1533
+ if (this.#disposed || this.#sessionGeneration !== generation)
1534
+ return;
1535
+ publish?.({
1536
+ kind: "warning",
1537
+ message: controller.signal.aborted
1538
+ ? `${actionLabel}, but its model catalog refresh timed out; using cached models.`
1539
+ : `${actionLabel}, but its model catalog could not be refreshed: ${error instanceof Error ? error.message : String(error)}`,
1540
+ });
1541
+ })
1542
+ .finally(() => clearTimeout(timeout));
1543
+ }
1127
1544
  #requireWorkflowSession() {
1128
1545
  if (this.#disposed || !this.#runtime || !this.#session)
1129
1546
  throw new Error("engine adapter is not running");
@@ -1221,7 +1638,7 @@ export class PiEngineAdapter {
1221
1638
  return [];
1222
1639
  return sessionInfoOptions(await SessionManager.list(cwd, typeof sessionDir === "string" ? sessionDir : undefined));
1223
1640
  }
1224
- #applyPinnedSetting(selection, selectedValue, hasSelectedValue = false) {
1641
+ async #applyPinnedSetting(selection, selectedValue, hasSelectedValue = false) {
1225
1642
  if (!PINNED_PI_SETTINGS_CALLBACKS.includes(selection)) {
1226
1643
  return workflowResult("settings", "failed", `Unknown setting callback: ${selection}`);
1227
1644
  }
@@ -1268,29 +1685,19 @@ export class PiEngineAdapter {
1268
1685
  selectedValue = currentValues[callback];
1269
1686
  hasSelectedValue = true;
1270
1687
  }
1271
- const settings = this.#runtime?.services.settingsManager;
1272
- if (!settings)
1688
+ const port = this.settingsPort();
1689
+ if (port === null)
1273
1690
  return workflowResult("settings", "failed", "Settings are unavailable");
1274
- const session = this.#requireWorkflowSession();
1275
- if (callback === "onThinkingLevelChange") {
1276
- if (!isThinkingLevel(selectedValue))
1277
- return workflowResult("settings", "failed", "Thinking level is invalid");
1278
- session.setThinkingLevel(selectedValue);
1279
- }
1280
- else {
1281
- const key = settingKeyForCallback(callback);
1282
- if (key === null)
1283
- return workflowResult("settings", "failed", `${settingLabel(callback)} is unavailable in this runtime`);
1284
- const value = agentJsonValue(selectedValue);
1285
- if (callback === "onAutoCompactChange") {
1286
- if (typeof value !== "boolean")
1287
- return workflowResult("settings", "failed", "Auto compact value is invalid");
1288
- session.setAutoCompactionEnabled?.(value);
1289
- }
1290
- new PiSettingsIntegration(settings).writeSettingNow(key, value);
1691
+ const key = settingKeyForCallback(callback);
1692
+ if (key === null)
1693
+ return workflowResult("settings", "failed", `${settingLabel(callback)} is unavailable in this runtime`);
1694
+ const result = await port.writeSetting(key, agentJsonValue(selectedValue));
1695
+ if (result.status === "failed" || result.status === "unavailable") {
1696
+ return workflowResult("settings", "failed", result.failure ?? result.limitationReason ?? `${settingLabel(callback)} is unavailable`);
1291
1697
  }
1292
1698
  this.#emitView();
1293
- return workflowResult("settings", "completed", `${settingLabel(callback)}: ${String(selectedValue)}`);
1699
+ const suffix = result.status === "deferred" ? ` (${result.application})` : "";
1700
+ return workflowResult("settings", "completed", `${settingLabel(callback)}: ${String(selectedValue)}${suffix}`);
1294
1701
  }
1295
1702
  async #perform(command) {
1296
1703
  const runtime = this.#runtime;
@@ -1305,7 +1712,11 @@ export class PiEngineAdapter {
1305
1712
  case "retry":
1306
1713
  case "compact": {
1307
1714
  const result = await this.#sessionCommands?.execute(command.type === "prompt" || command.type === "steer" || command.type === "follow-up"
1308
- ? { type: command.type, text: command.text }
1715
+ ? {
1716
+ type: command.type,
1717
+ text: command.text,
1718
+ ...(command.images === undefined ? {} : { images: [...command.images] }),
1719
+ }
1309
1720
  : { type: command.type });
1310
1721
  if (!result || result.outcome === "rejected" || result.outcome === "failed") {
1311
1722
  throw new Error(command.type === "retry" ? "no previous prompt is available to retry" : `Pi session command failed: ${command.type}`);
@@ -1365,6 +1776,7 @@ export class PiEngineAdapter {
1365
1776
  this.#activeModel = readModel(session.model);
1366
1777
  this.#reconcileActiveModelAvailability();
1367
1778
  this.#thinkingLevel = readThinkingLevel(session.thinkingLevel);
1779
+ this.#transcriptImageAssets.clear();
1368
1780
  this.#rebuildTranscript(session.messages, "finalized");
1369
1781
  this.#unsubscribe = session.subscribe(event => this.#handlePiEvent(event));
1370
1782
  if (this.#extensionUi !== undefined)
@@ -1414,7 +1826,24 @@ export class PiEngineAdapter {
1414
1826
  this.#emitView();
1415
1827
  }
1416
1828
  }
1417
- /** Shows the state named by `kind`, which becomes the state a later end can clear. */
1829
+ // Performance: replacing the transcript rebuilds its index so block lookup stays constant-time.
1830
+ #setTranscript(blocks) {
1831
+ this.#transcript = blocks;
1832
+ this.#transcriptIndex.clear();
1833
+ for (const [index, block] of blocks.entries())
1834
+ this.#transcriptIndex.set(block.id, index);
1835
+ const retainedAssets = new Set(blocks.flatMap(block => block.imageReferences?.map(reference => reference.assetId) ?? []));
1836
+ for (const assetId of this.#transcriptImageAssets.keys()) {
1837
+ if (!retainedAssets.has(assetId))
1838
+ this.#transcriptImageAssets.delete(assetId);
1839
+ }
1840
+ this.#transcriptSnapshot = undefined;
1841
+ }
1842
+ #transcriptBlock(id) {
1843
+ const index = this.#transcriptIndex.get(id);
1844
+ return index === undefined ? undefined : this.#transcript[index];
1845
+ }
1846
+ // Invariant: the named work state is the only state a matching end may clear.
1418
1847
  #enterWorkState(kind, message) {
1419
1848
  const wasBusy = this.#lifecycle === "busy";
1420
1849
  this.#statusKind = kind;
@@ -1424,21 +1853,16 @@ export class PiEngineAdapter {
1424
1853
  this.#emitEvent({ type: "session-lifecycle", lifecycle: "busy", reason: null });
1425
1854
  this.#emitEvent({ type: "status", status: this.#status });
1426
1855
  }
1427
- /**
1428
- * Ends one named state. A state the shell is not in is left alone, so a finished
1429
- * compaction or retry cannot clear the working state it never replaced. While the run
1430
- * continues, ending either of those returns to working rather than to idle.
1431
- */
1856
+ // Invariant: ending retry or compaction cannot clear a different active work state.
1432
1857
  #endWorkState(kind) {
1433
1858
  if (this.#statusKind !== kind)
1434
1859
  return;
1435
1860
  if (this.#agentRunActive) {
1436
- this.#enterWorkState("working", "Working...");
1861
+ this.#enterWorkState("working", "Working");
1437
1862
  return;
1438
1863
  }
1439
1864
  this.#leaveWorkStates();
1440
1865
  }
1441
- /** Leaves every work state and reports the session idle. */
1442
1866
  #leaveWorkStates() {
1443
1867
  this.#statusKind = null;
1444
1868
  this.#lifecycle = "ready";
@@ -1449,28 +1873,41 @@ export class PiEngineAdapter {
1449
1873
  #handlePiEvent(event) {
1450
1874
  if (!isRecord(event) || typeof event.type !== "string")
1451
1875
  return;
1876
+ // Invariant: usage moves at message and lifecycle boundaries, not with stream chunks, so the
1877
+ // two streaming event kinds keep the memo and everything else drops it.
1878
+ if (event.type !== "message_update" && event.type !== "tool_execution_update")
1879
+ this.#usageCache = undefined;
1452
1880
  switch (event.type) {
1453
1881
  case "agent_start":
1454
1882
  this.#agentRunActive = true;
1455
- this.#enterWorkState("working", "Working...");
1883
+ this.#emitEvent({ type: "agent-run-started" });
1884
+ this.#enterWorkState("working", "Working");
1456
1885
  return;
1457
1886
  case "message_start":
1458
1887
  this.#upsertMessageBlock(event.message, "live");
1459
1888
  return;
1460
1889
  case "message_update": {
1461
- const block = this.#upsertMessageBlock(event.message, "live");
1462
- if (block && isRecord(event.assistantMessageEvent) && typeof event.assistantMessageEvent.delta === "string") {
1463
- this.#upsertTranscriptBlock({
1464
- ...block,
1465
- text: block.text.endsWith(event.assistantMessageEvent.delta)
1466
- ? block.text
1467
- : `${block.text}${event.assistantMessageEvent.delta}`,
1468
- });
1890
+ const delta = isRecord(event.assistantMessageEvent) && typeof event.assistantMessageEvent.delta === "string"
1891
+ ? event.assistantMessageEvent.delta
1892
+ : undefined;
1893
+ // Invariant: the delta is folded in before the block is stored, so a chunk is one update to
1894
+ // one block rather than a store without the delta followed by a store with it.
1895
+ const blocks = this.#messageBlocks(event.message, "live", this.#transcript.length);
1896
+ for (const [index, block] of blocks.entries()) {
1897
+ this.#upsertTranscriptBlock(index === 0 && delta !== undefined && !block.text.endsWith(delta)
1898
+ ? { ...block, text: `${block.text}${delta}` }
1899
+ : block);
1469
1900
  }
1470
1901
  return;
1471
1902
  }
1472
1903
  case "message_end":
1473
1904
  this.#upsertMessageBlock(event.message, "finalized");
1905
+ // Compatibility: preserve the same semantic boundary v2 counted. Transcript block
1906
+ // finalization is intentionally not a substitute: rebuilds, retries,
1907
+ // thinking parts, and tool rows can all finalize independently.
1908
+ if (isRecord(event.message) && event.message.role === "assistant") {
1909
+ this.#emitEvent({ type: "assistant-message-completed" });
1910
+ }
1474
1911
  return;
1475
1912
  case "turn_end":
1476
1913
  this.#upsertMessageBlock(event.message, "finalized");
@@ -1480,10 +1917,17 @@ export class PiEngineAdapter {
1480
1917
  }
1481
1918
  return;
1482
1919
  case "tool_execution_start":
1483
- case "tool_execution_update":
1484
- case "tool_execution_end":
1920
+ case "tool_execution_end": {
1921
+ // Concurrency: the end supersedes any update still waiting on the coalescing timer.
1922
+ const toolCallId = stringValue(event.toolCallId);
1923
+ if (toolCallId !== undefined)
1924
+ this.#pendingToolUpdates.delete(toolCallId);
1485
1925
  this.#upsertToolExecutionBlock(event);
1486
1926
  return;
1927
+ }
1928
+ case "tool_execution_update":
1929
+ this.#coalesceToolExecutionUpdate(event);
1930
+ return;
1487
1931
  case "agent_settled":
1488
1932
  case "agent_end": {
1489
1933
  if (event.type === "agent_end" && event.willRetry === true)
@@ -1494,8 +1938,8 @@ export class PiEngineAdapter {
1494
1938
  if (finalMessages.length > 0)
1495
1939
  this.#rebuildTranscript(finalMessages, "finalized");
1496
1940
  else
1497
- this.#transcript = this.#transcript.map(block => block.status === "live" ? { ...block, status: "finalized" } : block);
1498
- // Ending a turn leaves the working state, as the recorded pinned baseline does, but
1941
+ this.#setTranscript(this.#transcript.map(block => block.status === "live" ? { ...block, status: "finalized" } : block));
1942
+ // Compatibility: ending a turn leaves the working state, as the recorded pinned baseline does, but
1499
1943
  // it leaves only that state: a compaction or retry being shown outlives the turn
1500
1944
  // that ended under it. Settlement ends the run, and with it every state — the
1501
1945
  // engine ends a turn for each continuation it makes and settles once.
@@ -1521,13 +1965,13 @@ export class PiEngineAdapter {
1521
1965
  return;
1522
1966
  }
1523
1967
  case "auto_retry_start":
1524
- this.#enterWorkState("retry", "Retrying");
1968
+ this.#enterWorkState("retry", "Retrying");
1525
1969
  return;
1526
1970
  case "auto_retry_end":
1527
1971
  this.#endWorkState("retry");
1528
1972
  return;
1529
1973
  case "compaction_start":
1530
- this.#enterWorkState("compaction", "Compacting");
1974
+ this.#enterWorkState("compaction", "Compacting");
1531
1975
  return;
1532
1976
  case "compaction_end":
1533
1977
  this.#endWorkState("compaction");
@@ -1620,7 +2064,13 @@ export class PiEngineAdapter {
1620
2064
  }
1621
2065
  }
1622
2066
  }
1623
- this.#transcript = blocks;
2067
+ // Performance: an authoritative rebuild restates most of what is already there. Reusing the block
2068
+ // that already says it keeps its revision, and with it the rows the shell rendered for
2069
+ // it — otherwise every turn that ends re-renders the whole session.
2070
+ this.#setTranscript(blocks.map(block => {
2071
+ const existing = this.#transcriptBlock(block.id);
2072
+ return existing !== undefined && sameBlockContent(existing, block) ? existing : block;
2073
+ }));
1624
2074
  }
1625
2075
  #upsertMessageBlock(message, status) {
1626
2076
  const blocks = this.#messageBlocks(message, status, this.#transcript.length);
@@ -1641,7 +2091,12 @@ export class PiEngineAdapter {
1641
2091
  revision: this.#nextBlockRevision(baseId),
1642
2092
  title: "User",
1643
2093
  text: textFromContent(message.content),
1644
- payload: { role: "user", imageCount: contentImageCount(message.content) },
2094
+ imageReferences: this.#imageReferences(message.content, "user"),
2095
+ payload: {
2096
+ role: "user",
2097
+ imageCount: contentImageCount(message.content),
2098
+ timestamp: typeof message.timestamp === "number" && Number.isFinite(message.timestamp) ? message.timestamp : null,
2099
+ },
1645
2100
  }];
1646
2101
  }
1647
2102
  if (message.role === "bashExecution") {
@@ -1705,7 +2160,7 @@ export class PiEngineAdapter {
1705
2160
  : this.#toolBlockIds.get(toolCallId) ?? `tool-${toolCallId}`;
1706
2161
  if (toolCallId !== undefined)
1707
2162
  this.#toolBlockIds.set(toolCallId, blockId);
1708
- const existing = this.#transcript.find(block => block.id === blockId);
2163
+ const existing = this.#transcriptBlock(blockId);
1709
2164
  const existingPayload = isRecord(existing?.payload) ? existing.payload : undefined;
1710
2165
  return [{
1711
2166
  id: blockId,
@@ -1714,6 +2169,7 @@ export class PiEngineAdapter {
1714
2169
  revision: this.#nextBlockRevision(blockId),
1715
2170
  title: stringValue(message.toolName) ?? existing?.title ?? "Tool result",
1716
2171
  text: textFromContent(message.content),
2172
+ imageReferences: this.#imageReferences(message.content, "tool-result"),
1717
2173
  payload: {
1718
2174
  ...(existingPayload ?? {}),
1719
2175
  role: "toolResult",
@@ -1769,6 +2225,48 @@ export class PiEngineAdapter {
1769
2225
  }
1770
2226
  return blocks;
1771
2227
  }
2228
+ #imageReferences(content, source) {
2229
+ if (!Array.isArray(content))
2230
+ return [];
2231
+ const references = [];
2232
+ for (const item of content) {
2233
+ if (references.length >= 16 || !isRecord(item) || item.type !== "image"
2234
+ || typeof item.data !== "string" || item.data.length === 0
2235
+ || typeof item.mimeType !== "string" || !/^image\/[a-z0-9.+-]+$/i.test(item.mimeType))
2236
+ continue;
2237
+ const byteLength = Buffer.from(item.data, "base64").byteLength;
2238
+ if (byteLength < 1 || byteLength > 20 * 1024 * 1024)
2239
+ continue;
2240
+ const assetId = `image-${createHash("sha256").update(item.mimeType).update("\0").update(item.data).digest("hex").slice(0, 24)}`;
2241
+ this.#transcriptImageAssets.set(assetId, { type: "image", data: item.data, mimeType: item.mimeType });
2242
+ references.push({ assetId, mimeType: item.mimeType, byteLength, source });
2243
+ }
2244
+ return references;
2245
+ }
2246
+ // Performance: coalescing each tool to its newest chunk bounds work by frames, not stream events.
2247
+ #coalesceToolExecutionUpdate(event) {
2248
+ const toolCallId = stringValue(event.toolCallId);
2249
+ if (!toolCallId)
2250
+ return;
2251
+ this.#pendingToolUpdates.set(toolCallId, event);
2252
+ this.#toolUpdateFlush ??= setTimeout(() => {
2253
+ this.#toolUpdateFlush = null;
2254
+ if (!this.#disposed)
2255
+ this.#flushPendingToolUpdates();
2256
+ }, TOOL_UPDATE_COALESCE_MS);
2257
+ }
2258
+ #flushPendingToolUpdates() {
2259
+ if (this.#toolUpdateFlush !== null) {
2260
+ clearTimeout(this.#toolUpdateFlush);
2261
+ this.#toolUpdateFlush = null;
2262
+ }
2263
+ if (this.#pendingToolUpdates.size === 0)
2264
+ return;
2265
+ const pending = [...this.#pendingToolUpdates.values()];
2266
+ this.#pendingToolUpdates.clear();
2267
+ for (const update of pending)
2268
+ this.#upsertToolExecutionBlock(update);
2269
+ }
1772
2270
  #upsertToolExecutionBlock(event) {
1773
2271
  const toolCallId = stringValue(event.toolCallId);
1774
2272
  if (!toolCallId)
@@ -1788,7 +2286,9 @@ export class PiEngineAdapter {
1788
2286
  toolCallId,
1789
2287
  toolName: stringValue(event.toolName) ?? "unknown",
1790
2288
  arguments: jsonSummary(event.args),
1791
- result: jsonSummary(source),
2289
+ // Performance: a partial result repeats the whole accumulated output on every chunk;
2290
+ // summarizing it each time would cost quadratic work over the stream.
2291
+ result: ended ? jsonSummary(source) : { summary: "", json: null },
1792
2292
  partialResult: event.type === "tool_execution_update",
1793
2293
  argsComplete: ended,
1794
2294
  isError: event.isError === true,
@@ -1796,11 +2296,20 @@ export class PiEngineAdapter {
1796
2296
  });
1797
2297
  }
1798
2298
  #upsertTranscriptBlock(block) {
1799
- const index = this.#transcript.findIndex(existing => existing.id === block.id);
1800
- if (index >= 0)
2299
+ const index = this.#transcriptIndex.get(block.id);
2300
+ if (index !== undefined) {
2301
+ const existing = this.#transcript[index];
2302
+ // Performance: nothing is emitted for a block that repeats itself, and keeping the
2303
+ // revision keeps the rows it already rendered.
2304
+ if (existing !== undefined && sameBlockContent(existing, block))
2305
+ return;
1801
2306
  this.#transcript[index] = block;
1802
- else
2307
+ }
2308
+ else {
2309
+ this.#transcriptIndex.set(block.id, this.#transcript.length);
1803
2310
  this.#transcript.push(block);
2311
+ }
2312
+ this.#transcriptSnapshot = undefined;
1804
2313
  this.#emitEvent({ type: "transcript-block", block });
1805
2314
  }
1806
2315
  #messageBlockId(message, fallbackIndex, status, occurrence) {
@@ -1818,7 +2327,7 @@ export class PiEngineAdapter {
1818
2327
  }
1819
2328
  }
1820
2329
  else {
1821
- id = [...cached].reverse().find(candidate => this.#transcript.some(block => block.id === candidate && block.status === "live"));
2330
+ id = [...cached].reverse().find(candidate => this.#transcriptBlock(candidate)?.status === "live");
1822
2331
  if (id === undefined && status === "finalized")
1823
2332
  id = cached.at(-1);
1824
2333
  if (id === undefined) {
@@ -1831,7 +2340,7 @@ export class PiEngineAdapter {
1831
2340
  return id;
1832
2341
  }
1833
2342
  #nextBlockRevision(id) {
1834
- const existing = this.#transcript.find(block => block.id === id);
2343
+ const existing = this.#transcriptBlock(id);
1835
2344
  if (existing)
1836
2345
  return existing.revision + 1;
1837
2346
  this.#nextBlockSequence += 1;
@@ -1862,6 +2371,7 @@ export class PiEngineAdapter {
1862
2371
  this.#emitEvent({ type: "diagnostic", diagnostic });
1863
2372
  }
1864
2373
  #emitView() {
2374
+ this.#usageCache = undefined;
1865
2375
  this.#viewRevision += 1;
1866
2376
  this.#emitEvent({ type: "session-view", view: this.view() });
1867
2377
  }
@@ -1906,7 +2416,7 @@ export class PiEngineAdapter {
1906
2416
  }
1907
2417
  }
1908
2418
  deliveredSinceYield += 1;
1909
- // A microtask chain runs to exhaustion before the loop turns, so a streaming
2419
+ // Concurrency: a microtask chain runs to exhaustion before the loop turns, so a streaming
1910
2420
  // burst would hold typed input, pointer reports, and timed indicators until it
1911
2421
  // drained. Yielding on a macrotask hands those their turn between batches.
1912
2422
  if (deliveredSinceYield >= EVENT_DELIVERY_BATCH && this.#eventQueue.length > 0) {
@@ -1950,7 +2460,14 @@ export async function createPiEngineAdapter(options = {}) {
1950
2460
  return adapter;
1951
2461
  }
1952
2462
  async function createDefaultPiRuntime(input) {
1953
- return createPiRuntimeIntegration({ cwd: input.cwd, agentDir: input.agentDir });
2463
+ return createPiRuntimeIntegration({
2464
+ cwd: input.cwd,
2465
+ agentDir: input.agentDir,
2466
+ ...(input.sessionPath === undefined ? {} : { sessionPath: input.sessionPath }),
2467
+ ...(input.sessionSelection === undefined ? {} : { sessionSelection: input.sessionSelection }),
2468
+ ...(input.sessionForkPrompt === undefined ? {} : { sessionForkPrompt: input.sessionForkPrompt }),
2469
+ ...(input.projectTrustPrompt === undefined ? {} : { projectTrustPrompt: input.projectTrustPrompt }),
2470
+ });
1954
2471
  }
1955
2472
  async function checkDefaultPiPackageUpdates(cwd, agentDir, settingsManager) {
1956
2473
  const packageManager = new DefaultPackageManager({ cwd, agentDir, settingsManager });
@@ -2073,8 +2590,8 @@ function pinnedCacheWaste(entries, modelRuntime) {
2073
2590
  function defaultWorkflowHost() {
2074
2591
  return {
2075
2592
  copyText: copyToClipboard,
2076
- async runCommand(command, arguments_) {
2077
- const result = await execFileAsync(command, [...arguments_], { encoding: "utf8" });
2593
+ async runCommand(command, arguments_, options) {
2594
+ const result = await execFileAsync(command, [...arguments_], { encoding: "utf8", signal: options?.signal });
2078
2595
  return { stdout: result.stdout, stderr: result.stderr };
2079
2596
  },
2080
2597
  readChangelog: async () => "No changelog entries found.",
@@ -2110,14 +2627,22 @@ function workflowLoginNotification(event) {
2110
2627
  }
2111
2628
  return { type: event.type === "waiting" ? "waiting" : "progress", message };
2112
2629
  }
2113
- function workflowResult(command, outcome, message, detail) {
2114
- return { command, outcome, message, ...(detail === undefined ? {} : { detail }) };
2630
+ function workflowResult(command, outcome, message, detail, messageKind, messages) {
2631
+ return {
2632
+ command,
2633
+ outcome,
2634
+ message,
2635
+ ...(detail === undefined ? {} : { detail }),
2636
+ ...(messageKind === undefined ? {} : { messageKind }),
2637
+ ...(messages === undefined ? {} : { messages: Object.freeze([...messages]) }),
2638
+ };
2115
2639
  }
2116
- function workflowConfirmation(command, message) {
2640
+ function workflowConfirmation(command, message, detail) {
2117
2641
  return {
2118
2642
  command,
2119
2643
  outcome: "requires-confirmation",
2120
2644
  message,
2645
+ ...(detail === undefined ? {} : { detail }),
2121
2646
  selectorTitle: "Confirm",
2122
2647
  options: [
2123
2648
  { id: "yes", label: "Yes" },
@@ -2125,6 +2650,19 @@ function workflowConfirmation(command, message) {
2125
2650
  ],
2126
2651
  };
2127
2652
  }
2653
+ function errorMessage(error, fallback) {
2654
+ return error instanceof Error ? error.message : fallback;
2655
+ }
2656
+ function commandMissing(error) {
2657
+ return isRecord(error) && error.code === "ENOENT";
2658
+ }
2659
+ function isAbortError(error) {
2660
+ return isRecord(error) && (error.name === "AbortError" || error.code === "ABORT_ERR");
2661
+ }
2662
+ function shareViewerUrl(gistId) {
2663
+ const baseUrl = process.env.PI_SHARE_VIEWER_URL || "https://pi.dev/session/";
2664
+ return `${baseUrl}#${gistId}`;
2665
+ }
2128
2666
  function workflowOptions(value, idKey, labelKey) {
2129
2667
  if (!Array.isArray(value))
2130
2668
  return [];
@@ -2176,7 +2714,7 @@ function settingKeyForCallback(callback) {
2176
2714
  onAutoCompactChange: "autoCompact", onShowImagesChange: "showImages", onImageWidthCellsChange: "imageWidthCells",
2177
2715
  onAutoResizeImagesChange: "autoResizeImages", onBlockImagesChange: "blockImages", onEnableSkillCommandsChange: "enableSkillCommands",
2178
2716
  onSteeringModeChange: "steeringMode", onFollowUpModeChange: "followUpMode", onTransportChange: "transport",
2179
- onHttpIdleTimeoutMsChange: "httpIdleTimeoutMs", onThemeChange: "theme", onThemePreview: "theme",
2717
+ onHttpIdleTimeoutMsChange: "httpIdleTimeoutMs", onThinkingLevelChange: "thinkingLevel", onThemeChange: "theme", onThemePreview: "theme",
2180
2718
  onHideThinkingBlockChange: "hideThinkingBlock", onMermaidRenderingModeChange: "mermaidRenderingMode",
2181
2719
  onShowCacheMissNoticesChange: "showCacheMissNotices", onCollapseChangelogChange: "collapseChangelog",
2182
2720
  onEnableInstallTelemetryChange: "enableInstallTelemetry", onQuietStartupChange: "quietStartup",
@@ -2343,6 +2881,24 @@ function stringProperty(value, key) {
2343
2881
  const item = value[key];
2344
2882
  return typeof item === "string" && item.length > 0 ? item : undefined;
2345
2883
  }
2884
+ function isUnknownModel(value) {
2885
+ return isRecord(value)
2886
+ && value.provider === "unknown"
2887
+ && value.id === "unknown"
2888
+ && value.api === "unknown";
2889
+ }
2890
+ function findExactWorkflowModel(reference, models) {
2891
+ const normalized = reference.trim().toLowerCase();
2892
+ const canonical = models.filter(model => {
2893
+ const provider = stringProperty(model, "provider");
2894
+ const id = stringProperty(model, "id");
2895
+ return provider !== undefined && id !== undefined && `${provider}/${id}`.toLowerCase() === normalized;
2896
+ });
2897
+ if (canonical.length === 1)
2898
+ return canonical[0];
2899
+ const bare = models.filter(model => stringProperty(model, "id")?.toLowerCase() === normalized);
2900
+ return bare.length === 1 ? bare[0] : undefined;
2901
+ }
2346
2902
  /**
2347
2903
  * Some ecosystem extensions retain a `pi-<name>` slash-command alias beside
2348
2904
  * their unprefixed command. A1 presents the product-neutral command once while
@@ -2367,6 +2923,37 @@ function compactResourceLabel(path) {
2367
2923
  const segments = path.replaceAll("\\", "/").split("/").filter(Boolean);
2368
2924
  return segments.at(-1) ?? path;
2369
2925
  }
2926
+ /**
2927
+ * Whether two blocks say the same thing. A block that says what it already said is not a
2928
+ * new revision: the shell renders a block once per revision, so bumping one it did not
2929
+ * need re-renders it for nothing.
2930
+ */
2931
+ function sameBlockContent(left, right) {
2932
+ return left.kind === right.kind
2933
+ && left.status === right.status
2934
+ && left.title === right.title
2935
+ && left.text === right.text
2936
+ && sameValue(left.payload, right.payload)
2937
+ && sameValue(left.imageReferences ?? [], right.imageReferences ?? []);
2938
+ }
2939
+ function sameValue(left, right) {
2940
+ if (left === right)
2941
+ return true;
2942
+ if (typeof left !== typeof right || left === null || right === null)
2943
+ return false;
2944
+ if (Array.isArray(left) || Array.isArray(right)) {
2945
+ if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length)
2946
+ return false;
2947
+ return left.every((value, index) => sameValue(value, right[index]));
2948
+ }
2949
+ if (typeof left !== "object")
2950
+ return false;
2951
+ const leftKeys = Object.keys(left);
2952
+ const rightRecord = right;
2953
+ if (leftKeys.length !== Object.keys(rightRecord).length)
2954
+ return false;
2955
+ return leftKeys.every(key => Object.hasOwn(rightRecord, key) && sameValue(left[key], rightRecord[key]));
2956
+ }
2370
2957
  function textFromContent(content) {
2371
2958
  if (typeof content === "string")
2372
2959
  return content;