p-spore 0.1.3 → 0.1.42

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 (396) hide show
  1. package/README.md +110 -15
  2. package/dist/agents/agent-manager.d.ts +3 -3
  3. package/dist/agents/agent-manager.d.ts.map +1 -1
  4. package/dist/agents/agent-manager.js +3 -2
  5. package/dist/agents/agent-manager.js.map +1 -1
  6. package/dist/application/agent-application.d.ts +29 -5
  7. package/dist/application/agent-application.d.ts.map +1 -1
  8. package/dist/application/agent-application.js +444 -14
  9. package/dist/application/agent-application.js.map +1 -1
  10. package/dist/articles/diff.d.ts +70 -0
  11. package/dist/articles/diff.d.ts.map +1 -0
  12. package/dist/articles/diff.js +330 -0
  13. package/dist/articles/diff.js.map +1 -0
  14. package/dist/articles/docx.d.ts +10 -0
  15. package/dist/articles/docx.d.ts.map +1 -0
  16. package/dist/articles/docx.js +219 -0
  17. package/dist/articles/docx.js.map +1 -0
  18. package/dist/articles/draft.d.ts +18 -0
  19. package/dist/articles/draft.d.ts.map +1 -0
  20. package/dist/articles/draft.js +88 -0
  21. package/dist/articles/draft.js.map +1 -0
  22. package/dist/articles/edits.d.ts +24 -0
  23. package/dist/articles/edits.d.ts.map +1 -0
  24. package/dist/articles/edits.js +54 -0
  25. package/dist/articles/edits.js.map +1 -0
  26. package/dist/articles/html.d.ts +10 -0
  27. package/dist/articles/html.d.ts.map +1 -0
  28. package/dist/articles/html.js +122 -0
  29. package/dist/articles/html.js.map +1 -0
  30. package/dist/articles/plain.d.ts +8 -0
  31. package/dist/articles/plain.d.ts.map +1 -0
  32. package/dist/articles/plain.js +69 -0
  33. package/dist/articles/plain.js.map +1 -0
  34. package/dist/articles/preview.d.ts +43 -0
  35. package/dist/articles/preview.d.ts.map +1 -0
  36. package/dist/articles/preview.js +90 -0
  37. package/dist/articles/preview.js.map +1 -0
  38. package/dist/articles/publish.d.ts +10 -0
  39. package/dist/articles/publish.d.ts.map +1 -0
  40. package/dist/articles/publish.js +54 -0
  41. package/dist/articles/publish.js.map +1 -0
  42. package/dist/articles/receipt.d.ts +16 -0
  43. package/dist/articles/receipt.d.ts.map +1 -0
  44. package/dist/articles/receipt.js +27 -0
  45. package/dist/articles/receipt.js.map +1 -0
  46. package/dist/articles/store.d.ts +78 -0
  47. package/dist/articles/store.d.ts.map +1 -0
  48. package/dist/articles/store.js +308 -0
  49. package/dist/articles/store.js.map +1 -0
  50. package/dist/articles/tool.d.ts +14 -0
  51. package/dist/articles/tool.d.ts.map +1 -0
  52. package/dist/articles/tool.js +175 -0
  53. package/dist/articles/tool.js.map +1 -0
  54. package/dist/articles/writer-context.d.ts +5 -0
  55. package/dist/articles/writer-context.d.ts.map +1 -0
  56. package/dist/articles/writer-context.js +56 -0
  57. package/dist/articles/writer-context.js.map +1 -0
  58. package/dist/articles/writer.d.ts +18 -0
  59. package/dist/articles/writer.d.ts.map +1 -0
  60. package/dist/articles/writer.js +79 -0
  61. package/dist/articles/writer.js.map +1 -0
  62. package/dist/articles/zip-write.d.ts +11 -0
  63. package/dist/articles/zip-write.d.ts.map +1 -0
  64. package/dist/articles/zip-write.js +63 -0
  65. package/dist/articles/zip-write.js.map +1 -0
  66. package/dist/bundled-skills/README.md +17 -0
  67. package/dist/bundled-skills/aegis-search/SKILL.md +59 -0
  68. package/dist/bundled-skills/aegis-search/bin/.gitkeep +0 -0
  69. package/dist/bundled-skills/article-canvas/SKILL.md +28 -0
  70. package/dist/bundled-skills/binaries.json +23 -0
  71. package/dist/bundled-skills/doc-extract/SKILL.md +56 -0
  72. package/dist/cli/main.d.ts.map +1 -1
  73. package/dist/cli/main.js +407 -54
  74. package/dist/cli/main.js.map +1 -1
  75. package/dist/config/config.d.ts +43 -0
  76. package/dist/config/config.d.ts.map +1 -1
  77. package/dist/config/config.js +150 -6
  78. package/dist/config/config.js.map +1 -1
  79. package/dist/daemon/daemon.d.ts.map +1 -1
  80. package/dist/daemon/daemon.js +80 -11
  81. package/dist/daemon/daemon.js.map +1 -1
  82. package/dist/desktop/application.d.ts +4 -0
  83. package/dist/desktop/application.d.ts.map +1 -0
  84. package/dist/desktop/application.js +1688 -0
  85. package/dist/desktop/application.js.map +1 -0
  86. package/dist/desktop/article-export.d.ts +12 -0
  87. package/dist/desktop/article-export.d.ts.map +1 -0
  88. package/dist/desktop/article-export.js +87 -0
  89. package/dist/desktop/article-export.js.map +1 -0
  90. package/dist/desktop/assets/pspore.png +0 -0
  91. package/dist/desktop/assets/pspore.svg +24 -0
  92. package/dist/desktop/conversation-smoke.d.ts +5 -0
  93. package/dist/desktop/conversation-smoke.d.ts.map +1 -0
  94. package/dist/desktop/conversation-smoke.js +93 -0
  95. package/dist/desktop/conversation-smoke.js.map +1 -0
  96. package/dist/desktop/daemon-bridge.d.ts.map +1 -1
  97. package/dist/desktop/daemon-bridge.js +62 -17
  98. package/dist/desktop/daemon-bridge.js.map +1 -1
  99. package/dist/desktop/ipc-contract.d.ts +124 -0
  100. package/dist/desktop/ipc-contract.d.ts.map +1 -1
  101. package/dist/desktop/ipc-contract.js +191 -12
  102. package/dist/desktop/ipc-contract.js.map +1 -1
  103. package/dist/desktop/local-path.d.ts +15 -0
  104. package/dist/desktop/local-path.d.ts.map +1 -0
  105. package/dist/desktop/local-path.js +59 -0
  106. package/dist/desktop/local-path.js.map +1 -0
  107. package/dist/desktop/main.d.ts +10 -1
  108. package/dist/desktop/main.d.ts.map +1 -1
  109. package/dist/desktop/main.js +122 -530
  110. package/dist/desktop/main.js.map +1 -1
  111. package/dist/desktop/native-startup.d.ts +4 -0
  112. package/dist/desktop/native-startup.d.ts.map +1 -0
  113. package/dist/desktop/native-startup.js +57 -0
  114. package/dist/desktop/native-startup.js.map +1 -0
  115. package/dist/desktop/preload.cjs +28 -1
  116. package/dist/desktop/renderer/article-panel.d.ts +45 -0
  117. package/dist/desktop/renderer/article-panel.d.ts.map +1 -0
  118. package/dist/desktop/renderer/article-panel.js +1126 -0
  119. package/dist/desktop/renderer/article-panel.js.map +1 -0
  120. package/dist/desktop/renderer/article-split.d.ts +32 -0
  121. package/dist/desktop/renderer/article-split.d.ts.map +1 -0
  122. package/dist/desktop/renderer/article-split.js +48 -0
  123. package/dist/desktop/renderer/article-split.js.map +1 -0
  124. package/dist/desktop/renderer/composer-file-references.d.ts +23 -0
  125. package/dist/desktop/renderer/composer-file-references.d.ts.map +1 -0
  126. package/dist/desktop/renderer/composer-file-references.js +51 -0
  127. package/dist/desktop/renderer/composer-file-references.js.map +1 -0
  128. package/dist/desktop/renderer/conversation.d.ts +23 -0
  129. package/dist/desktop/renderer/conversation.d.ts.map +1 -0
  130. package/dist/desktop/renderer/conversation.js +66 -0
  131. package/dist/desktop/renderer/conversation.js.map +1 -0
  132. package/dist/desktop/renderer/dom-reconcile.d.ts +15 -0
  133. package/dist/desktop/renderer/dom-reconcile.d.ts.map +1 -0
  134. package/dist/desktop/renderer/dom-reconcile.js +33 -0
  135. package/dist/desktop/renderer/dom-reconcile.js.map +1 -0
  136. package/dist/desktop/renderer/index.html +211 -34
  137. package/dist/desktop/renderer/indexing-view.d.ts +22 -0
  138. package/dist/desktop/renderer/indexing-view.d.ts.map +1 -0
  139. package/dist/desktop/renderer/indexing-view.js +115 -0
  140. package/dist/desktop/renderer/indexing-view.js.map +1 -0
  141. package/dist/desktop/renderer/markdown-view.d.ts +23 -0
  142. package/dist/desktop/renderer/markdown-view.d.ts.map +1 -0
  143. package/dist/desktop/renderer/markdown-view.js +436 -0
  144. package/dist/desktop/renderer/markdown-view.js.map +1 -0
  145. package/dist/desktop/renderer/motion.d.ts +67 -0
  146. package/dist/desktop/renderer/motion.d.ts.map +1 -0
  147. package/dist/desktop/renderer/motion.js +296 -0
  148. package/dist/desktop/renderer/motion.js.map +1 -0
  149. package/dist/desktop/renderer/renderer.js +1793 -155
  150. package/dist/desktop/renderer/renderer.js.map +1 -1
  151. package/dist/desktop/renderer/session-find.d.ts +18 -0
  152. package/dist/desktop/renderer/session-find.d.ts.map +1 -0
  153. package/dist/desktop/renderer/session-find.js +29 -0
  154. package/dist/desktop/renderer/session-find.js.map +1 -0
  155. package/dist/desktop/renderer/session-search.d.ts +30 -0
  156. package/dist/desktop/renderer/session-search.d.ts.map +1 -0
  157. package/dist/desktop/renderer/session-search.js +296 -0
  158. package/dist/desktop/renderer/session-search.js.map +1 -0
  159. package/dist/desktop/renderer/store.d.ts +35 -8
  160. package/dist/desktop/renderer/store.d.ts.map +1 -1
  161. package/dist/desktop/renderer/store.js +95 -78
  162. package/dist/desktop/renderer/store.js.map +1 -1
  163. package/dist/desktop/renderer/styles.css +910 -268
  164. package/dist/desktop/renderer/vendor/gsap.min.js +11 -0
  165. package/dist/desktop/renderer-diagnostics.d.ts +5 -0
  166. package/dist/desktop/renderer-diagnostics.d.ts.map +1 -0
  167. package/dist/desktop/renderer-diagnostics.js +47 -0
  168. package/dist/desktop/renderer-diagnostics.js.map +1 -0
  169. package/dist/desktop/security.d.ts +26 -1
  170. package/dist/desktop/security.d.ts.map +1 -1
  171. package/dist/desktop/security.js +26 -6
  172. package/dist/desktop/security.js.map +1 -1
  173. package/dist/desktop/smoke-fixture.d.ts.map +1 -1
  174. package/dist/desktop/smoke-fixture.js +77 -3
  175. package/dist/desktop/smoke-fixture.js.map +1 -1
  176. package/dist/desktop/splash/index.html +25 -0
  177. package/dist/desktop/splash/splash.d.ts +2 -0
  178. package/dist/desktop/splash/splash.d.ts.map +1 -0
  179. package/dist/desktop/splash/splash.js +8 -0
  180. package/dist/desktop/splash/splash.js.map +1 -0
  181. package/dist/desktop/splash/styles.css +24 -0
  182. package/dist/desktop/system-open.d.ts +16 -0
  183. package/dist/desktop/system-open.d.ts.map +1 -0
  184. package/dist/desktop/system-open.js +90 -0
  185. package/dist/desktop/system-open.js.map +1 -0
  186. package/dist/desktop/validation.d.ts.map +1 -1
  187. package/dist/desktop/validation.js +99 -21
  188. package/dist/desktop/validation.js.map +1 -1
  189. package/dist/desktop/window-security.d.ts +4 -0
  190. package/dist/desktop/window-security.d.ts.map +1 -0
  191. package/dist/desktop/window-security.js +26 -0
  192. package/dist/desktop/window-security.js.map +1 -0
  193. package/dist/documents/errors.d.ts +12 -0
  194. package/dist/documents/errors.d.ts.map +1 -0
  195. package/dist/documents/errors.js +12 -0
  196. package/dist/documents/errors.js.map +1 -0
  197. package/dist/documents/extract.d.ts +37 -0
  198. package/dist/documents/extract.d.ts.map +1 -0
  199. package/dist/documents/extract.js +90 -0
  200. package/dist/documents/extract.js.map +1 -0
  201. package/dist/documents/legacy-office.d.ts +23 -0
  202. package/dist/documents/legacy-office.d.ts.map +1 -0
  203. package/dist/documents/legacy-office.js +439 -0
  204. package/dist/documents/legacy-office.js.map +1 -0
  205. package/dist/documents/ole.d.ts +20 -0
  206. package/dist/documents/ole.d.ts.map +1 -0
  207. package/dist/documents/ole.js +189 -0
  208. package/dist/documents/ole.js.map +1 -0
  209. package/dist/documents/ooxml.d.ts +23 -0
  210. package/dist/documents/ooxml.d.ts.map +1 -0
  211. package/dist/documents/ooxml.js +197 -0
  212. package/dist/documents/ooxml.js.map +1 -0
  213. package/dist/documents/pdf.d.ts +20 -0
  214. package/dist/documents/pdf.d.ts.map +1 -0
  215. package/dist/documents/pdf.js +95 -0
  216. package/dist/documents/pdf.js.map +1 -0
  217. package/dist/documents/plain-text.d.ts +22 -0
  218. package/dist/documents/plain-text.d.ts.map +1 -0
  219. package/dist/documents/plain-text.js +57 -0
  220. package/dist/documents/plain-text.js.map +1 -0
  221. package/dist/documents/zip.d.ts +15 -0
  222. package/dist/documents/zip.d.ts.map +1 -0
  223. package/dist/documents/zip.js +137 -0
  224. package/dist/documents/zip.js.map +1 -0
  225. package/dist/domain/indexing.d.ts +60 -0
  226. package/dist/domain/indexing.d.ts.map +1 -0
  227. package/dist/domain/indexing.js +13 -0
  228. package/dist/domain/indexing.js.map +1 -0
  229. package/dist/domain/runtime.d.ts +133 -5
  230. package/dist/domain/runtime.d.ts.map +1 -1
  231. package/dist/gateway/local-gateway.d.ts +8 -0
  232. package/dist/gateway/local-gateway.d.ts.map +1 -1
  233. package/dist/gateway/local-gateway.js +105 -21
  234. package/dist/gateway/local-gateway.js.map +1 -1
  235. package/dist/indexing/index-service.d.ts +56 -0
  236. package/dist/indexing/index-service.d.ts.map +1 -0
  237. package/dist/indexing/index-service.js +262 -0
  238. package/dist/indexing/index-service.js.map +1 -0
  239. package/dist/kernel/loop.d.ts +10 -0
  240. package/dist/kernel/loop.d.ts.map +1 -1
  241. package/dist/kernel/loop.js +6 -2
  242. package/dist/kernel/loop.js.map +1 -1
  243. package/dist/kernel/reducer.d.ts.map +1 -1
  244. package/dist/kernel/reducer.js +17 -1
  245. package/dist/kernel/reducer.js.map +1 -1
  246. package/dist/kernel/state.d.ts +17 -4
  247. package/dist/kernel/state.d.ts.map +1 -1
  248. package/dist/kernel/state.js +2 -0
  249. package/dist/kernel/state.js.map +1 -1
  250. package/dist/memory/memory-tools.d.ts.map +1 -1
  251. package/dist/memory/memory-tools.js +25 -5
  252. package/dist/memory/memory-tools.js.map +1 -1
  253. package/dist/pi/context-budget.d.ts +8 -0
  254. package/dist/pi/context-budget.d.ts.map +1 -0
  255. package/dist/pi/context-budget.js +62 -0
  256. package/dist/pi/context-budget.js.map +1 -0
  257. package/dist/pi/pi-runtime-adapter.d.ts +38 -3
  258. package/dist/pi/pi-runtime-adapter.d.ts.map +1 -1
  259. package/dist/pi/pi-runtime-adapter.js +551 -56
  260. package/dist/pi/pi-runtime-adapter.js.map +1 -1
  261. package/dist/plugins/bundled-skills.d.ts +56 -0
  262. package/dist/plugins/bundled-skills.d.ts.map +1 -0
  263. package/dist/plugins/bundled-skills.js +243 -0
  264. package/dist/plugins/bundled-skills.js.map +1 -0
  265. package/dist/plugins/extension-manager.d.ts +23 -1
  266. package/dist/plugins/extension-manager.d.ts.map +1 -1
  267. package/dist/plugins/extension-manager.js +117 -0
  268. package/dist/plugins/extension-manager.js.map +1 -1
  269. package/dist/plugins/types.d.ts +29 -0
  270. package/dist/plugins/types.d.ts.map +1 -1
  271. package/dist/plugins/workspace-skills.d.ts +36 -0
  272. package/dist/plugins/workspace-skills.d.ts.map +1 -0
  273. package/dist/plugins/workspace-skills.js +309 -0
  274. package/dist/plugins/workspace-skills.js.map +1 -0
  275. package/dist/policy/policy.d.ts.map +1 -1
  276. package/dist/policy/policy.js +26 -4
  277. package/dist/policy/policy.js.map +1 -1
  278. package/dist/protocol/client.d.ts +1 -0
  279. package/dist/protocol/client.d.ts.map +1 -1
  280. package/dist/protocol/client.js +49 -24
  281. package/dist/protocol/client.js.map +1 -1
  282. package/dist/protocol/limits.d.ts +69 -0
  283. package/dist/protocol/limits.d.ts.map +1 -0
  284. package/dist/protocol/limits.js +166 -0
  285. package/dist/protocol/limits.js.map +1 -0
  286. package/dist/protocol/schema.d.ts +6 -0
  287. package/dist/protocol/schema.d.ts.map +1 -1
  288. package/dist/protocol/schema.js.map +1 -1
  289. package/dist/protocol/server-frame-codec.d.ts +18 -0
  290. package/dist/protocol/server-frame-codec.d.ts.map +1 -0
  291. package/dist/protocol/server-frame-codec.js +211 -0
  292. package/dist/protocol/server-frame-codec.js.map +1 -0
  293. package/dist/service/legacy-daemon.d.ts +6 -0
  294. package/dist/service/legacy-daemon.d.ts.map +1 -0
  295. package/dist/service/legacy-daemon.js +42 -0
  296. package/dist/service/legacy-daemon.js.map +1 -0
  297. package/dist/service/service-launch.d.ts +10 -0
  298. package/dist/service/service-launch.d.ts.map +1 -0
  299. package/dist/service/service-launch.js +56 -0
  300. package/dist/service/service-launch.js.map +1 -0
  301. package/dist/service/system-service.d.ts +57 -0
  302. package/dist/service/system-service.d.ts.map +1 -0
  303. package/dist/service/system-service.js +263 -0
  304. package/dist/service/system-service.js.map +1 -0
  305. package/dist/sessions/session-catalog.d.ts +45 -0
  306. package/dist/sessions/session-catalog.d.ts.map +1 -0
  307. package/dist/sessions/session-catalog.js +270 -0
  308. package/dist/sessions/session-catalog.js.map +1 -0
  309. package/dist/sessions/session-file.d.ts +12 -0
  310. package/dist/sessions/session-file.d.ts.map +1 -0
  311. package/dist/sessions/session-file.js +47 -0
  312. package/dist/sessions/session-file.js.map +1 -0
  313. package/dist/settings/compaction-settings.d.ts +10 -0
  314. package/dist/settings/compaction-settings.d.ts.map +1 -0
  315. package/dist/settings/compaction-settings.js +8 -0
  316. package/dist/settings/compaction-settings.js.map +1 -0
  317. package/dist/settings/indexing-settings-service.d.ts +16 -0
  318. package/dist/settings/indexing-settings-service.d.ts.map +1 -0
  319. package/dist/settings/indexing-settings-service.js +57 -0
  320. package/dist/settings/indexing-settings-service.js.map +1 -0
  321. package/dist/settings/indexing-settings.d.ts +30 -0
  322. package/dist/settings/indexing-settings.d.ts.map +1 -0
  323. package/dist/settings/indexing-settings.js +58 -0
  324. package/dist/settings/indexing-settings.js.map +1 -0
  325. package/dist/settings/llm-settings-service.d.ts +6 -0
  326. package/dist/settings/llm-settings-service.d.ts.map +1 -1
  327. package/dist/settings/llm-settings-service.js +137 -12
  328. package/dist/settings/llm-settings-service.js.map +1 -1
  329. package/dist/settings/llm-settings.d.ts +68 -2
  330. package/dist/settings/llm-settings.d.ts.map +1 -1
  331. package/dist/settings/llm-settings.js +119 -17
  332. package/dist/settings/llm-settings.js.map +1 -1
  333. package/dist/settings/model-api.d.ts +10 -0
  334. package/dist/settings/model-api.d.ts.map +1 -0
  335. package/dist/settings/model-api.js +10 -0
  336. package/dist/settings/model-api.js.map +1 -0
  337. package/dist/storage/home.d.ts +1 -0
  338. package/dist/storage/home.d.ts.map +1 -1
  339. package/dist/storage/home.js +37 -2
  340. package/dist/storage/home.js.map +1 -1
  341. package/dist/tools/core-tools.d.ts +2 -0
  342. package/dist/tools/core-tools.d.ts.map +1 -1
  343. package/dist/tools/core-tools.js +91 -1
  344. package/dist/tools/core-tools.js.map +1 -1
  345. package/dist/ui/shared/activity.d.ts +27 -0
  346. package/dist/ui/shared/activity.d.ts.map +1 -0
  347. package/dist/ui/shared/activity.js +384 -0
  348. package/dist/ui/shared/activity.js.map +1 -0
  349. package/dist/ui/shared/full-message.d.ts +22 -0
  350. package/dist/ui/shared/full-message.d.ts.map +1 -0
  351. package/dist/ui/shared/full-message.js +40 -0
  352. package/dist/ui/shared/full-message.js.map +1 -0
  353. package/dist/ui/shared/local-links.d.ts +7 -0
  354. package/dist/ui/shared/local-links.d.ts.map +1 -0
  355. package/dist/ui/shared/local-links.js +24 -0
  356. package/dist/ui/shared/local-links.js.map +1 -0
  357. package/dist/ui/shared/markdown-stream.d.ts +25 -0
  358. package/dist/ui/shared/markdown-stream.d.ts.map +1 -0
  359. package/dist/ui/shared/markdown-stream.js +35 -0
  360. package/dist/ui/shared/markdown-stream.js.map +1 -0
  361. package/dist/ui/shared/markdown.d.ts +70 -0
  362. package/dist/ui/shared/markdown.d.ts.map +1 -0
  363. package/dist/ui/shared/markdown.js +434 -0
  364. package/dist/ui/shared/markdown.js.map +1 -0
  365. package/dist/ui/shared/presentation.d.ts +10 -2
  366. package/dist/ui/shared/presentation.d.ts.map +1 -1
  367. package/dist/ui/shared/presentation.js +83 -22
  368. package/dist/ui/shared/presentation.js.map +1 -1
  369. package/dist/ui/shared/protocol-state.d.ts +19 -0
  370. package/dist/ui/shared/protocol-state.d.ts.map +1 -1
  371. package/dist/ui/shared/protocol-state.js +188 -26
  372. package/dist/ui/shared/protocol-state.js.map +1 -1
  373. package/dist/ui/shared/virtual-list.d.ts +34 -0
  374. package/dist/ui/shared/virtual-list.d.ts.map +1 -0
  375. package/dist/ui/shared/virtual-list.js +143 -0
  376. package/dist/ui/shared/virtual-list.js.map +1 -0
  377. package/dist/ui/tui/commands.d.ts +1 -1
  378. package/dist/ui/tui/commands.d.ts.map +1 -1
  379. package/dist/ui/tui/commands.js +9 -3
  380. package/dist/ui/tui/commands.js.map +1 -1
  381. package/dist/ui/tui/store.d.ts +26 -0
  382. package/dist/ui/tui/store.d.ts.map +1 -1
  383. package/dist/ui/tui/store.js +67 -3
  384. package/dist/ui/tui/store.js.map +1 -1
  385. package/dist/ui/tui/tui-app.d.ts.map +1 -1
  386. package/dist/ui/tui/tui-app.js +316 -38
  387. package/dist/ui/tui/tui-app.js.map +1 -1
  388. package/dist/ui/tui/view.d.ts +10 -0
  389. package/dist/ui/tui/view.d.ts.map +1 -1
  390. package/dist/ui/tui/view.js +253 -49
  391. package/dist/ui/tui/view.js.map +1 -1
  392. package/dist/version.d.ts +1 -1
  393. package/dist/version.d.ts.map +1 -1
  394. package/dist/version.js +1 -1
  395. package/dist/version.js.map +1 -1
  396. package/package.json +13 -2
@@ -1,5 +1,36 @@
1
1
  import { randomUUID } from "node:crypto";
2
+ import { previewArticleEdits } from "../articles/edits.js";
3
+ import { MAX_ARTICLE_CHARS } from "../articles/store.js";
2
4
  import { CommandError } from "../protocol/errors.js";
5
+ import { MAX_PUBLISHED_STRING_CHARS } from "../protocol/limits.js";
6
+ /**
7
+ * Runtime events whose broadcast carries the transcript tail. A finished item
8
+ * already carries its own text, so clients append it themselves; the tail rides
9
+ * along only where the transcript needs re-establishing wholesale — the end of a
10
+ * run, a replaced session, a compaction that rewrote the history.
11
+ */
12
+ const TRANSCRIPT_BEARING_EVENTS = new Set([
13
+ "run.settled",
14
+ "session.replaced",
15
+ "runtime.error",
16
+ "compaction.completed",
17
+ ]);
18
+ /**
19
+ * How much of a transcript rides along with a broadcast session view. Enough to
20
+ * render a conversation on arrival, bounded so the payload never grows with the
21
+ * length of the session; older history comes from `session.transcript`.
22
+ */
23
+ const SESSION_VIEW_TAIL_MESSAGES = 200;
24
+ /** Largest transcript page one `session.transcript` command may return. */
25
+ const MAX_TRANSCRIPT_PAGE = 500;
26
+ /**
27
+ * Largest slice of one message `session.message` may return. It matches the
28
+ * daemon's per-string publishing ceiling, so a slice is never cut a second time
29
+ * on its way out — a client asking for a long message pages through it instead.
30
+ */
31
+ const MAX_MESSAGE_SLICE = MAX_PUBLISHED_STRING_CHARS;
32
+ /** Saved versions kept for updates still being streamed. One call is normal; a few cover parallel calls. */
33
+ const DRAFT_BASE_CACHE = 4;
3
34
  export class AgentRunBudgetError extends Error {
4
35
  constructor(message) {
5
36
  super(message);
@@ -11,15 +42,22 @@ export class AgentApplication {
11
42
  #kernel;
12
43
  #approvals;
13
44
  #notifications;
45
+ #sessionCatalog;
14
46
  #sessionTemplate;
15
47
  #modelConfigured;
48
+ #builtinTools;
16
49
  #onShutdownRequested;
17
50
  #clock;
18
51
  #idFactory;
52
+ #onError;
19
53
  #sessions = new Set();
54
+ #rootSessions = new Map();
20
55
  #unsubscribers = new Map();
21
56
  #sessionTails = new Map();
22
57
  #automation;
58
+ #articles;
59
+ /** The saved version each streaming `update` revises, by tool call. */
60
+ #draftBases = new Map();
23
61
  #agents;
24
62
  #mcp;
25
63
  #resources;
@@ -28,11 +66,19 @@ export class AgentApplication {
28
66
  this.#kernel = options.kernel;
29
67
  this.#approvals = options.approvals;
30
68
  this.#notifications = options.notifications;
69
+ this.#sessionCatalog = options.sessionCatalog;
31
70
  this.#sessionTemplate = options.sessionTemplate;
32
71
  this.#modelConfigured = options.modelConfigured;
72
+ this.#builtinTools = options.builtinTools.map((tool) => ({
73
+ name: tool.name,
74
+ ...(tool.label === undefined ? {} : { label: tool.label }),
75
+ ...(tool.description === undefined ? {} : { description: tool.description }),
76
+ }));
33
77
  this.#onShutdownRequested = options.onShutdownRequested;
78
+ this.#articles = options.articles;
34
79
  this.#clock = options.clock ?? (() => new Date());
35
80
  this.#idFactory = options.idFactory ?? randomUUID;
81
+ this.#onError = options.onError ?? (() => undefined);
36
82
  }
37
83
  async handleCommand(command) {
38
84
  switch (command.type) {
@@ -42,24 +88,90 @@ export class AgentApplication {
42
88
  case "session.snapshot": {
43
89
  exactPayload(command.payload, [], command.type);
44
90
  const sessionId = requiredSessionId(command);
45
- return this.#sessions.has(sessionId) ? this.#runtime.snapshot(sessionId) : null;
91
+ return this.#sessions.has(sessionId) ? this.#view(sessionId) : null;
92
+ }
93
+ case "session.transcript": {
94
+ // Broadcasts carry only a bounded tail, so scrolling back through a long
95
+ // conversation asks for the range it needs instead of making every
96
+ // client pay for the whole history on every update.
97
+ const payload = exactPayload(command.payload, ["from", "limit"], command.type);
98
+ const sessionId = requiredSessionId(command);
99
+ if (!this.#sessions.has(sessionId))
100
+ return null;
101
+ const limit = optionalBoundedIndex(payload["limit"], "payload.limit", MAX_TRANSCRIPT_PAGE) ?? MAX_TRANSCRIPT_PAGE;
102
+ const from = optionalBoundedIndex(payload["from"], "payload.from");
103
+ const page = this.#runtime.transcript(sessionId, { limit, ...(from === undefined ? {} : { from }) });
104
+ return { sessionId, ...page };
105
+ }
106
+ case "session.message": {
107
+ // A message longer than the publishing ceiling reaches clients cut, with
108
+ // a marker saying so. This returns the original in slices, so the reader
109
+ // can ask for the whole of one message without every broadcast paying
110
+ // for the possibility.
111
+ const payload = exactPayload(command.payload, ["id", "index", "offset", "limit"], command.type);
112
+ const sessionId = requiredSessionId(command);
113
+ if (!this.#sessions.has(sessionId))
114
+ return null;
115
+ const id = optionalIdentifier(payload["id"], "payload.id");
116
+ const index = optionalBoundedIndex(payload["index"], "payload.index");
117
+ if (id === undefined && index === undefined) {
118
+ throw new CommandError("invalid_payload", "payload.id or payload.index is required");
119
+ }
120
+ const limit = optionalBoundedIndex(payload["limit"], "payload.limit", MAX_MESSAGE_SLICE) ?? MAX_MESSAGE_SLICE;
121
+ const offset = optionalBoundedIndex(payload["offset"], "payload.offset");
122
+ const slice = this.#runtime.transcriptMessage(sessionId, {
123
+ ...(id === undefined ? {} : { id }),
124
+ ...(index === undefined ? {} : { index }),
125
+ ...(offset === undefined ? {} : { offset }),
126
+ limit,
127
+ });
128
+ return { sessionId, ...slice };
129
+ }
130
+ case "session.create": {
131
+ const payload = exactPayload(command.payload, ["title"], command.type);
132
+ const title = optionalSessionTitle(payload["title"], "payload.title");
133
+ const sessionId = generatedRootSessionId(this.#idFactory());
134
+ return this.#serializeSession(sessionId, async () => {
135
+ await this.#createRootSession(sessionId, title);
136
+ return this.#view(sessionId);
137
+ });
138
+ }
139
+ case "session.delete": {
140
+ exactPayload(command.payload, [], command.type);
141
+ const sessionId = requiredSessionId(command);
142
+ return this.#serializeSession(sessionId, async () => this.#deleteRootSession(sessionId));
46
143
  }
47
144
  case "session.prompt": {
48
145
  const payload = exactPayload(command.payload, ["text", "mode"], command.type);
49
146
  const text = nonEmptyString(payload["text"], "payload.text");
50
- const mode = optionalEnum(payload["mode"], ["prompt", "steer", "follow_up"], "payload.mode") ?? "prompt";
147
+ const mode = optionalEnum(payload["mode"], ["auto", "prompt", "steer", "follow_up"], "payload.mode") ?? "auto";
51
148
  const sessionId = requiredSessionId(command);
52
149
  return this.#serializeSession(sessionId, async () => {
53
- await this.#ensureSession(sessionId);
54
- const accepted = mode === "steer"
150
+ await this.#ensureRootSession(sessionId);
151
+ const record = await this.#sessionCatalog.touch(sessionId, { prompt: text });
152
+ if (record)
153
+ this.#rootSessions.set(sessionId, rootMetadata(record));
154
+ const automaticMode = mode === "auto"
155
+ ? this.#runtime.snapshot(sessionId).status === "running" ? "follow_up" : "prompt"
156
+ : mode;
157
+ const accepted = automaticMode === "steer"
55
158
  ? await this.#runtime.steer(sessionId, text)
56
- : mode === "follow_up"
159
+ : automaticMode === "follow_up"
57
160
  ? await this.#runtime.followUp(sessionId, text)
58
161
  : await this.#runtime.prompt(sessionId, text);
59
162
  setImmediate(() => void this.#agents?.sessionAvailable(sessionId));
60
163
  return accepted;
61
164
  });
62
165
  }
166
+ case "session.queue.send-now": {
167
+ const payload = exactPayload(command.payload, ["index"], command.type);
168
+ const index = nonNegativeIntegerValue(payload["index"], "payload.index", 10_000);
169
+ const sessionId = requiredSessionId(command);
170
+ return this.#serializeSession(sessionId, async () => {
171
+ this.#assertRootSession(sessionId);
172
+ return this.#runtime.promoteFollowUp(sessionId, index);
173
+ });
174
+ }
63
175
  case "session.abort": {
64
176
  const payload = exactPayload(command.payload, ["reason"], command.type);
65
177
  const reason = optionalString(payload["reason"], "payload.reason") ?? "user requested abort";
@@ -138,6 +250,11 @@ export class AgentApplication {
138
250
  case "mcp.servers":
139
251
  exactPayload(command.payload, [], command.type);
140
252
  return this.#requireMcp().listServers();
253
+ case "tools.list":
254
+ exactPayload(command.payload, [], command.type);
255
+ // Fixed tools, not MCP ones: these are what this build ships with, and
256
+ // they exist whether or not any server has been discovered.
257
+ return this.#builtinTools.map((tool) => ({ ...tool }));
141
258
  case "mcp.tools":
142
259
  exactPayload(command.payload, [], command.type);
143
260
  return this.#requireMcp().listTools();
@@ -155,6 +272,11 @@ export class AgentApplication {
155
272
  case "skills.list":
156
273
  exactPayload(command.payload, [], command.type);
157
274
  return this.#requireResources().list().filter((entry) => entry.record.kind === "skill");
275
+ case "skills.catalog":
276
+ exactPayload(command.payload, [], command.type);
277
+ // Every skill a new session is offered, whatever its source, and the
278
+ // ones that were found but not offered with the reason.
279
+ return this.#requireResources().skillCatalog();
158
280
  case "plugins.list":
159
281
  exactPayload(command.payload, [], command.type);
160
282
  return this.#requireResources().list().filter((entry) => entry.record.kind === "host-plugin");
@@ -186,10 +308,101 @@ export class AgentApplication {
186
308
  exactPayload(command.payload, [], command.type);
187
309
  this.#onShutdownRequested?.();
188
310
  return { draining: true };
311
+ case "article.list": {
312
+ exactPayload(command.payload, [], command.type);
313
+ const sessionId = requiredSessionId(command);
314
+ return { articles: await this.#requireArticles().list(sessionId) };
315
+ }
316
+ case "article.get": {
317
+ const payload = exactPayload(command.payload, ["id", "version", "offset", "limit"], command.type);
318
+ const sessionId = requiredSessionId(command);
319
+ const version = optionalBoundedIndex(payload["version"], "payload.version", 100);
320
+ const offset = optionalBoundedIndex(payload["offset"], "payload.offset");
321
+ const limit = optionalBoundedIndex(payload["limit"], "payload.limit", MAX_MESSAGE_SLICE) ?? MAX_MESSAGE_SLICE;
322
+ const document = await this.#requireArticles().read({
323
+ sessionId,
324
+ id: nonEmptyString(payload["id"], "payload.id"),
325
+ ...(version === undefined || version < 1 ? {} : { version }),
326
+ ...(offset === undefined ? {} : { offset }),
327
+ limit,
328
+ });
329
+ return document ?? null;
330
+ }
331
+ case "article.preview": {
332
+ const payload = exactPayload(command.payload, ["toolCallId", "offset", "limit"], command.type);
333
+ const sessionId = requiredSessionId(command);
334
+ const draft = this.#runtime.articlePreview?.(nonEmptyString(payload["toolCallId"], "payload.toolCallId"));
335
+ if (!draft || draft.sessionId !== sessionId)
336
+ return null;
337
+ const offset = optionalBoundedIndex(payload["offset"], "payload.offset") ?? 0;
338
+ const limit = optionalBoundedIndex(payload["limit"], "payload.limit", MAX_MESSAGE_SLICE) ?? MAX_MESSAGE_SLICE;
339
+ const body = await this.#draftBody(draft);
340
+ if (!body)
341
+ return null;
342
+ const start = Math.min(Math.max(0, offset), body.text.length);
343
+ return {
344
+ toolCallId: draft.toolCallId,
345
+ ...(draft.title === undefined ? {} : { title: draft.title }),
346
+ ...(draft.articleId === undefined ? {} : { articleId: draft.articleId }),
347
+ whole: body.whole,
348
+ ...(body.baseVersion === undefined ? {} : { baseVersion: body.baseVersion }),
349
+ ...(body.whole ? { edits: body.edits } : {}),
350
+ offset: start,
351
+ length: body.text.length,
352
+ text: body.text.slice(start, start + limit),
353
+ };
354
+ }
189
355
  default:
190
356
  throw new CommandError("unknown_command", `Unknown command type: ${command.type}`);
191
357
  }
192
358
  }
359
+ /**
360
+ * What the panel shows for a draft. A full draft is the start of the next
361
+ * version and goes out as written. Edits are applied to the version they
362
+ * revise, so what goes out is that whole document as it will read (`whole`).
363
+ * An update also names the version it starts from, so a panel showing
364
+ * something else can put that version up first and draw the draft over it.
365
+ */
366
+ async #draftBody(draft) {
367
+ const base = draft.action === "update" && draft.articleId !== undefined
368
+ ? await this.#draftBase(draft.toolCallId, draft.sessionId, draft.articleId)
369
+ : undefined;
370
+ const edits = draft.edits ?? [];
371
+ if (draft.action === "create" || (draft.content !== undefined && edits.length === 0)) {
372
+ return { text: draft.content ?? "", whole: false, edits: 0, ...(base ? { baseVersion: base.version } : {}) };
373
+ }
374
+ if (!base)
375
+ return undefined;
376
+ const preview = previewArticleEdits(draft.content ?? base.text, edits, draft.editOpen === true);
377
+ return { text: preview.text, whole: true, baseVersion: base.version, edits: preview.applied };
378
+ }
379
+ /**
380
+ * The saved version an update revises. Tools run only after the model stops
381
+ * streaming, so it cannot change under a call still being written and one
382
+ * read serves every preview of that call.
383
+ */
384
+ async #draftBase(toolCallId, sessionId, id) {
385
+ const cached = this.#draftBases.get(toolCallId);
386
+ if (cached?.id === id)
387
+ return cached;
388
+ const document = await this.#requireArticles().read({ sessionId, id, limit: MAX_ARTICLE_CHARS });
389
+ if (!document)
390
+ return undefined;
391
+ const base = { id, version: document.version, text: document.text };
392
+ this.#draftBases.set(toolCallId, base);
393
+ while (this.#draftBases.size > DRAFT_BASE_CACHE) {
394
+ const oldest = this.#draftBases.keys().next().value;
395
+ if (oldest === undefined)
396
+ break;
397
+ this.#draftBases.delete(oldest);
398
+ }
399
+ return base;
400
+ }
401
+ #requireArticles() {
402
+ if (!this.#articles)
403
+ throw new CommandError("unavailable", "Article store is not configured");
404
+ return this.#articles;
405
+ }
193
406
  setAutomation(automation) {
194
407
  if (this.#automation)
195
408
  throw new Error("Automation control is already configured");
@@ -199,6 +412,23 @@ export class AgentApplication {
199
412
  if (this.#agents)
200
413
  throw new Error("Agent control is already configured");
201
414
  this.#agents = agents;
415
+ for (const sessionId of this.#rootSessions.keys()) {
416
+ agents.registerRoot({ id: sessionId, agentPath: [sessionId] });
417
+ }
418
+ }
419
+ async restoreRootSessions() {
420
+ const failures = [];
421
+ for (const record of this.#sessionCatalog.list()) {
422
+ try {
423
+ await this.#serializeSession(record.id, async () => this.#resumeRootSession(record));
424
+ }
425
+ catch (error) {
426
+ const failure = toError(error);
427
+ failures.push(failure);
428
+ this.#onError(failure);
429
+ }
430
+ }
431
+ return failures;
202
432
  }
203
433
  setMcp(mcp) {
204
434
  if (this.#mcp)
@@ -275,7 +505,7 @@ export class AgentApplication {
275
505
  });
276
506
  if (finalViolation)
277
507
  throw new AgentRunBudgetError(finalViolation);
278
- return snapshot;
508
+ return this.#view(sessionId);
279
509
  }
280
510
  finally {
281
511
  signal.removeEventListener("abort", abort);
@@ -327,7 +557,7 @@ export class AgentApplication {
327
557
  }
328
558
  snapshotAgentSession(sessionId) {
329
559
  this.#assertSession(sessionId);
330
- return this.#runtime.snapshot(sessionId);
560
+ return this.#view(sessionId);
331
561
  }
332
562
  subscribeAgentSession(sessionId, sink) {
333
563
  this.#assertSession(sessionId);
@@ -339,33 +569,179 @@ export class AgentApplication {
339
569
  async dispose() {
340
570
  await Promise.allSettled([...this.#sessionTails.values()]);
341
571
  await Promise.all([...this.#sessions].map(async (sessionId) => this.#disposeSession(sessionId)));
572
+ await this.#sessionCatalog.idle();
342
573
  }
343
574
  async #ensureSession(sessionId, cwd, authorization) {
344
575
  if (this.#sessions.has(sessionId))
345
576
  return;
346
- if (!this.#modelConfigured) {
347
- throw new CommandError("model_not_configured", "No model is configured. Set config.model.provider, config.model.model, and credentials before prompting.");
348
- }
577
+ this.#assertModelConfigured();
349
578
  await this.#runtime.create({
350
579
  sessionId,
351
580
  ...this.#sessionTemplate,
352
581
  ...(cwd === undefined ? {} : { cwd }),
353
582
  ...(authorization === undefined ? {} : { authorization }),
354
583
  });
584
+ await this.#attachSession(sessionId);
585
+ }
586
+ async #ensureRootSession(sessionId) {
587
+ if (this.#sessions.has(sessionId)) {
588
+ this.#assertRootSession(sessionId);
589
+ return;
590
+ }
591
+ const record = this.#sessionCatalog.get(sessionId);
592
+ if (record) {
593
+ await this.#resumeRootSession(record);
594
+ return;
595
+ }
596
+ if (sessionId !== "main") {
597
+ throw new CommandError("session_not_found", `Root session does not exist: ${sessionId}`);
598
+ }
599
+ await this.#createRootSession(sessionId);
600
+ }
601
+ async #createRootSession(sessionId, title) {
602
+ if (this.#sessions.has(sessionId) || this.#sessionCatalog.get(sessionId)) {
603
+ throw new CommandError("session_exists", `Root session already exists: ${sessionId}`);
604
+ }
605
+ this.#assertModelConfigured();
606
+ const handle = await this.#runtime.create({ sessionId, ...this.#sessionTemplate });
607
+ let rootRegistered = false;
608
+ try {
609
+ const now = this.#clock().toISOString();
610
+ let metadata = {
611
+ title: title ?? (sessionId === "main" ? "Main session" : "New session"),
612
+ createdAt: now,
613
+ updatedAt: now,
614
+ };
615
+ if (handle.sessionFile) {
616
+ metadata = rootMetadata(await this.#sessionCatalog.add({
617
+ id: sessionId,
618
+ sessionFile: handle.sessionFile,
619
+ cwd: this.#sessionTemplate.cwd,
620
+ ...(title === undefined ? {} : { title }),
621
+ }));
622
+ }
623
+ this.#rootSessions.set(sessionId, metadata);
624
+ this.#agents?.registerRoot({ id: sessionId, agentPath: [sessionId] });
625
+ rootRegistered = Boolean(this.#agents);
626
+ await this.#attachSession(sessionId);
627
+ }
628
+ catch (error) {
629
+ this.#rootSessions.delete(sessionId);
630
+ if (rootRegistered)
631
+ this.#agents?.unregisterRoot(sessionId);
632
+ await this.#runtime.dispose(sessionId).catch(() => undefined);
633
+ throw error;
634
+ }
635
+ }
636
+ /**
637
+ * Removes a root session for good: any run is stopped, the runtime session is
638
+ * disposed, the catalog entry is dropped and the transcript is deleted.
639
+ *
640
+ * A record that exists only in the catalog is deleted the same way, so a
641
+ * client can clear history the daemon never restored — and so a session whose
642
+ * restore failed can still be cleaned up. Child agents belonging to the
643
+ * session are cancelled first, because their transcripts would otherwise keep
644
+ * writing into a session that no longer exists.
645
+ */
646
+ async #deleteRootSession(sessionId) {
647
+ const attached = this.#sessions.has(sessionId);
648
+ if (attached)
649
+ this.#assertRootSession(sessionId);
650
+ else if (!this.#sessionCatalog.get(sessionId)) {
651
+ throw new CommandError("session_not_found", `Root session does not exist: ${sessionId}`);
652
+ }
653
+ if (attached) {
654
+ const reason = `root session ${sessionId} deleted`;
655
+ await this.#approvals.cancelSession(sessionId);
656
+ await this.#agents?.cancelChildren(this.#agents.parentIdForSession(sessionId), reason).catch(() => undefined);
657
+ // A running session has to stop before its runtime can be torn down; the
658
+ // abort is best-effort because an already-idle session rejects it.
659
+ await this.#runtime.abort(sessionId, reason).catch(() => undefined);
660
+ await this.#disposeSession(sessionId);
661
+ this.#agents?.unregisterRoot(sessionId);
662
+ }
663
+ const removed = await this.#sessionCatalog.remove(sessionId);
664
+ return {
665
+ deleted: true,
666
+ sessionId,
667
+ ...(removed?.record.title === undefined ? {} : { title: removed.record.title }),
668
+ transcriptRemoved: removed?.transcriptRemoved ?? false,
669
+ };
670
+ }
671
+ async #resumeRootSession(record) {
672
+ if (this.#sessions.has(record.id))
673
+ return;
674
+ this.#assertModelConfigured();
675
+ await this.#runtime.resume({
676
+ sessionId: record.id,
677
+ ...this.#sessionTemplate,
678
+ cwd: record.cwd,
679
+ sessionFile: record.sessionFile,
680
+ });
681
+ let rootRegistered = false;
682
+ try {
683
+ this.#rootSessions.set(record.id, rootMetadata(record));
684
+ this.#agents?.registerRoot({ id: record.id, agentPath: [record.id] });
685
+ rootRegistered = Boolean(this.#agents);
686
+ await this.#attachSession(record.id);
687
+ }
688
+ catch (error) {
689
+ this.#rootSessions.delete(record.id);
690
+ if (rootRegistered)
691
+ this.#agents?.unregisterRoot(record.id);
692
+ await this.#runtime.dispose(record.id).catch(() => undefined);
693
+ throw error;
694
+ }
695
+ }
696
+ async #attachSession(sessionId) {
355
697
  this.#sessions.add(sessionId);
356
698
  this.#unsubscribers.set(sessionId, this.#runtime.subscribe(sessionId, (event) => this.#onRuntimeEvent(event)));
357
- await this.#publishSnapshot(this.#runtime.snapshot(sessionId));
699
+ await this.#publishSnapshot(this.#view(sessionId));
358
700
  }
359
701
  #onRuntimeEvent(event) {
360
- const snapshot = this.#runtime.snapshot(event.sessionId);
361
- void this.#kernel.dispatch({
702
+ // Transient events arrive per streamed token. Rebuilding and cloning the
703
+ // whole session for each one makes streaming cost grow with the length of
704
+ // the conversation, which stalls the daemon's event loop on long runs until
705
+ // clients are dropped for falling behind. They carry no snapshot anyway.
706
+ const snapshot = event.transient ? undefined : this.#view(event.sessionId);
707
+ void this.#dispatch({
362
708
  id: event.id,
363
709
  ts: event.ts,
364
710
  type: "runtime.received",
365
711
  event,
366
- snapshot,
712
+ ...(snapshot === undefined ? {} : { snapshot, publishTranscript: TRANSCRIPT_BEARING_EVENTS.has(event.type) }),
713
+ });
714
+ if (snapshot && this.#rootSessions.has(event.sessionId) && (event.type === "run.settled" || event.type === "session.replaced")) {
715
+ void this.#sessionCatalog.touch(event.sessionId, {
716
+ ...(snapshot.sessionFile === undefined ? {} : { sessionFile: snapshot.sessionFile }),
717
+ }).then((record) => {
718
+ if (record)
719
+ this.#rootSessions.set(event.sessionId, rootMetadata(record));
720
+ }).catch((error) => this.#onError(toError(error)));
721
+ }
722
+ }
723
+ /**
724
+ * A rejected dispatch must never become an unhandled rejection: that ends the
725
+ * daemon process, and repeated restarts trip the service start limit, which
726
+ * leaves clients unable to reconnect at all.
727
+ */
728
+ #dispatch(event) {
729
+ return this.#kernel.dispatch(event).catch((error) => {
730
+ this.#onError(toError(error));
731
+ return undefined;
367
732
  });
368
733
  }
734
+ #metadata(sessionId) {
735
+ const snapshot = this.#runtime.snapshot(sessionId);
736
+ const rootSession = this.#rootSessions.get(sessionId);
737
+ return rootSession ? { ...snapshot, rootSession: structuredClone(rootSession) } : snapshot;
738
+ }
739
+ /** The broadcast shape: session metadata plus a bounded transcript tail. */
740
+ #view(sessionId) {
741
+ const metadata = this.#metadata(sessionId);
742
+ const page = this.#runtime.transcript(sessionId, { limit: SESSION_VIEW_TAIL_MESSAGES });
743
+ return { ...metadata, messageOffset: page.from, messages: page.messages };
744
+ }
369
745
  async #publishSnapshot(snapshot) {
370
746
  await this.#kernel.dispatch({
371
747
  id: this.#idFactory(),
@@ -380,6 +756,7 @@ export class AgentApplication {
380
756
  this.#unsubscribers.get(sessionId)?.();
381
757
  this.#unsubscribers.delete(sessionId);
382
758
  this.#sessions.delete(sessionId);
759
+ this.#rootSessions.delete(sessionId);
383
760
  let failure;
384
761
  try {
385
762
  await this.#runtime.dispose(sessionId);
@@ -400,6 +777,17 @@ export class AgentApplication {
400
777
  if (!this.#sessions.has(sessionId))
401
778
  throw new CommandError("session_not_found", `Session does not exist: ${sessionId}`);
402
779
  }
780
+ #assertRootSession(sessionId) {
781
+ this.#assertSession(sessionId);
782
+ if (!this.#rootSessions.has(sessionId)) {
783
+ throw new CommandError("invalid_session", `Session is not an interactive root session: ${sessionId}`);
784
+ }
785
+ }
786
+ #assertModelConfigured() {
787
+ if (!this.#modelConfigured) {
788
+ throw new CommandError("model_not_configured", "No model is configured. Set config.model.provider, config.model.model, and credentials before prompting.");
789
+ }
790
+ }
403
791
  #requireAutomation() {
404
792
  if (!this.#automation)
405
793
  throw new CommandError("automation_unavailable", "Scheduler is not initialized");
@@ -459,6 +847,48 @@ function optionalString(value, path) {
459
847
  return undefined;
460
848
  return nonEmptyString(value, path);
461
849
  }
850
+ function optionalSessionTitle(value, path) {
851
+ if (value === undefined)
852
+ return undefined;
853
+ const title = nonEmptyString(value, path).replace(/\s+/g, " ").trim();
854
+ if ([...title].length > 120 || /[\u0000-\u001f]/.test(title)) {
855
+ throw new CommandError("invalid_command", `${path} must contain at most 120 characters without controls`);
856
+ }
857
+ return title;
858
+ }
859
+ function generatedRootSessionId(value) {
860
+ const id = `session-${value}`;
861
+ if (id.length > 128 || !/^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(id)) {
862
+ throw new Error("Generated root session id is invalid");
863
+ }
864
+ return id;
865
+ }
866
+ function optionalIdentifier(value, path) {
867
+ if (value === undefined)
868
+ return undefined;
869
+ if (typeof value !== "string" || !value.trim() || value.length > 200) {
870
+ throw new CommandError("invalid_payload", `${path} must be a bounded non-empty string`);
871
+ }
872
+ return value;
873
+ }
874
+ function optionalBoundedIndex(value, path, maximum = Number.MAX_SAFE_INTEGER) {
875
+ if (value === undefined)
876
+ return undefined;
877
+ if (!Number.isSafeInteger(value) || Number(value) < 0 || Number(value) > maximum) {
878
+ throw new CommandError("invalid_payload", `${path} must be an integer from 0 to ${maximum}`);
879
+ }
880
+ return Number(value);
881
+ }
882
+ function rootMetadata(record) {
883
+ return {
884
+ title: record.title,
885
+ createdAt: record.createdAt,
886
+ updatedAt: record.updatedAt,
887
+ };
888
+ }
889
+ function toError(error) {
890
+ return error instanceof Error ? error : new Error(String(error));
891
+ }
462
892
  function optionalEnum(value, allowed, path) {
463
893
  if (value === undefined)
464
894
  return undefined;