@maudecode/talaria-web 1.14.0 → 1.15.0

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 (253) hide show
  1. package/_release.json +4 -4
  2. package/dist/api/automation-router.d.ts +102 -0
  3. package/dist/api/automation-router.js +2 -0
  4. package/dist/api/automation-router.js.map +1 -1
  5. package/dist/api/chat-router.d.ts +589 -52
  6. package/dist/api/chat-router.js +111 -121
  7. package/dist/api/chat-router.js.map +1 -1
  8. package/dist/api/router.d.ts +2 -1
  9. package/dist/api/router.js +3 -1
  10. package/dist/api/router.js.map +1 -1
  11. package/dist/api/sessions-router.d.ts +1913 -299
  12. package/dist/api/sessions-router.js +35 -24
  13. package/dist/api/sessions-router.js.map +1 -1
  14. package/dist/api/settings-router.d.ts +35 -8
  15. package/dist/api/settings-router.js +14 -10
  16. package/dist/api/settings-router.js.map +1 -1
  17. package/dist/api/sse-routes.js +1 -1
  18. package/dist/api/sse-routes.js.map +1 -1
  19. package/dist/app.d.ts +2715 -418
  20. package/dist/config/agent-config.js +20 -15
  21. package/dist/config/agent-config.js.map +1 -1
  22. package/dist/http/context.d.ts +4 -2
  23. package/dist/http/context.js.map +1 -1
  24. package/dist/onboarding.d.ts +1 -1
  25. package/dist/onboarding.js +2 -1
  26. package/dist/onboarding.js.map +1 -1
  27. package/dist/providers/catalog.d.ts +11 -0
  28. package/dist/providers/catalog.js +32 -1
  29. package/dist/providers/catalog.js.map +1 -1
  30. package/dist/runtime.js +54 -16
  31. package/dist/runtime.js.map +1 -1
  32. package/dist/sessions/background-tasks.d.ts +137 -0
  33. package/dist/sessions/background-tasks.js +349 -0
  34. package/dist/sessions/background-tasks.js.map +1 -0
  35. package/dist/sessions/background-updates.d.ts +56 -0
  36. package/dist/sessions/background-updates.js +152 -0
  37. package/dist/sessions/background-updates.js.map +1 -0
  38. package/dist/sessions/cli-sessions.d.ts +6 -0
  39. package/dist/sessions/cli-sessions.js +20 -7
  40. package/dist/sessions/cli-sessions.js.map +1 -1
  41. package/dist/sessions/completions.d.ts +17 -1
  42. package/dist/sessions/completions.js +83 -12
  43. package/dist/sessions/completions.js.map +1 -1
  44. package/dist/sessions/compress.d.ts +33 -0
  45. package/dist/sessions/compress.js +66 -0
  46. package/dist/sessions/compress.js.map +1 -0
  47. package/dist/sessions/list.d.ts +14 -0
  48. package/dist/sessions/list.js +89 -16
  49. package/dist/sessions/list.js.map +1 -1
  50. package/dist/sessions/merge.d.ts +12 -1
  51. package/dist/sessions/merge.js +64 -7
  52. package/dist/sessions/merge.js.map +1 -1
  53. package/dist/sessions/relay.d.ts +7 -1
  54. package/dist/sessions/relay.js +17 -4
  55. package/dist/sessions/relay.js.map +1 -1
  56. package/dist/sessions/service.d.ts +61 -3
  57. package/dist/sessions/service.js +243 -15
  58. package/dist/sessions/service.js.map +1 -1
  59. package/dist/sessions/session.d.ts +16 -0
  60. package/dist/sessions/session.js +23 -2
  61. package/dist/sessions/session.js.map +1 -1
  62. package/dist/sessions/source-kind.d.ts +22 -0
  63. package/dist/sessions/source-kind.js +54 -0
  64. package/dist/sessions/source-kind.js.map +1 -0
  65. package/dist/sessions/state-db.d.ts +10 -1
  66. package/dist/sessions/state-db.js +48 -17
  67. package/dist/sessions/state-db.js.map +1 -1
  68. package/dist/sessions/store.d.ts +2 -0
  69. package/dist/sessions/store.js +23 -4
  70. package/dist/sessions/store.js.map +1 -1
  71. package/dist/sessions/streams.d.ts +3 -0
  72. package/dist/sessions/streams.js +0 -5
  73. package/dist/sessions/streams.js.map +1 -1
  74. package/dist/sessions/titles.d.ts +4 -0
  75. package/dist/sessions/titles.js +16 -0
  76. package/dist/sessions/titles.js.map +1 -1
  77. package/dist/sessions/turn-context.d.ts +1 -0
  78. package/dist/sessions/turn-context.js +3 -0
  79. package/dist/sessions/turn-context.js.map +1 -1
  80. package/dist/sessions/turn.d.ts +66 -6
  81. package/dist/sessions/turn.js +304 -61
  82. package/dist/sessions/turn.js.map +1 -1
  83. package/dist/settings.d.ts +2 -0
  84. package/dist/settings.js +4 -0
  85. package/dist/settings.js.map +1 -1
  86. package/dist/tools/crons.d.ts +34 -3
  87. package/dist/tools/crons.js +271 -31
  88. package/dist/tools/crons.js.map +1 -1
  89. package/package.json +2 -2
  90. package/sidecar/talaria_sidecar/__init__.py +1 -1
  91. package/sidecar/talaria_sidecar/methods/chat.py +233 -6
  92. package/sidecar/talaria_sidecar/methods/cron.py +22 -14
  93. package/sidecar/talaria_sidecar/methods/process.py +318 -0
  94. package/static/dist/FILES.txt +120 -119
  95. package/static/dist/assets/AppShell-orat1s43.js +1 -0
  96. package/static/dist/assets/ChatPage-ojhka3hy.js +22 -0
  97. package/static/dist/assets/HubRoute-gxgw1t33.js +2 -0
  98. package/static/dist/assets/{Markdown-emii55o0.js → Markdown-dy8t4dy7.js} +7 -7
  99. package/static/dist/assets/{RightPanel-o73y98dn.js → RightPanel-c66ani1s.js} +1 -1
  100. package/static/dist/assets/Select-fswf66up.js +1 -0
  101. package/static/dist/assets/SessionListPanel-goh5mtt8.js +1 -0
  102. package/static/dist/assets/{SettingsLayout-lnn2wo2s.js → SettingsLayout-n5d4wq4m.js} +1 -1
  103. package/static/dist/assets/{States-e5hv6gdh.js → States-kxyu50at.js} +1 -1
  104. package/static/dist/assets/{Toaster-c4jq1i26.js → Toaster-6zmbcqgg.js} +1 -1
  105. package/static/dist/assets/_app-fowyruiu.js +1 -0
  106. package/static/dist/assets/_app.ext._extensionId-nvjyrwhy.js +1 -0
  107. package/static/dist/assets/_app.index-c9zv4y7w.js +1 -0
  108. package/static/dist/assets/_app.insights-jf12nofo.js +1 -0
  109. package/static/dist/assets/_app.kanban-lri5a4yu.js +1 -0
  110. package/static/dist/assets/_app.logs-md1p0zqo.js +1 -0
  111. package/static/dist/assets/_app.memory-bzvf2e28.js +1 -0
  112. package/static/dist/assets/_app.profiles-jpg7zmbn.js +1 -0
  113. package/static/dist/assets/{_app.session._sessionId-m6nx9g8t.js → _app.session._sessionId-gyoh5ylx.js} +1 -1
  114. package/static/dist/assets/_app.settings-mpuwlx4j.js +1 -0
  115. package/static/dist/assets/_app.settings._section-knz8rwoj.js +1 -0
  116. package/static/dist/assets/_app.skills-fwu1dkir.js +1 -0
  117. package/static/dist/assets/_app.tasks-h9gphtl3.js +2 -0
  118. package/static/dist/assets/_app.todos-hd1dnvrh.js +1 -0
  119. package/static/dist/assets/_app.workspaces-l8toyn5f.js +1 -0
  120. package/static/dist/assets/{abnfDiagram-VCTEODGH-kejgxpil.js → abnfDiagram-VCTEODGH-cjqletra.js} +1 -1
  121. package/static/dist/assets/architecture-7GRP2DOG-cavnusaw.js +1 -0
  122. package/static/dist/assets/{architectureDiagram-5GKGNRK7-hqnf84fc.js → architectureDiagram-5GKGNRK7-k5a7v36n.js} +1 -1
  123. package/static/dist/assets/back-e4m6nsnm.js +1 -0
  124. package/static/dist/assets/{blockDiagram-I7D4REHJ-l1466a7j.js → blockDiagram-I7D4REHJ-b195z021.js} +1 -1
  125. package/static/dist/assets/{c4Diagram-7LVT6UL2-gg3aq3sr.js → c4Diagram-7LVT6UL2-olu212k1.js} +1 -1
  126. package/static/dist/assets/channel-gozy3dp3.js +1 -0
  127. package/static/dist/assets/{chunk-4HAMMTFA-oalgz5l8.js → chunk-4HAMMTFA-b8o1nufs.js} +1 -1
  128. package/static/dist/assets/{chunk-75Z2AOVW-babmzhdg.js → chunk-75Z2AOVW-l1seddto.js} +1 -1
  129. package/static/dist/assets/{chunk-DU6HZSFF-lagu350c.js → chunk-DU6HZSFF-lo5zg15v.js} +3 -3
  130. package/static/dist/assets/{chunk-F27PBJKO-gjh2tqif.js → chunk-F27PBJKO-dsdzrp16.js} +1 -1
  131. package/static/dist/assets/{chunk-GMAD6QVW-ipdgja5c.js → chunk-GMAD6QVW-f3fxqq8b.js} +1 -1
  132. package/static/dist/assets/{chunk-GVQU2GXP-j8wkn1at.js → chunk-GVQU2GXP-h8egxtf6.js} +1 -1
  133. package/static/dist/assets/{chunk-IMKFNOWR-k00laqyk.js → chunk-IMKFNOWR-iolwd8s0.js} +1 -1
  134. package/static/dist/assets/{chunk-L3NEJ4N5-fju6xqj4.js → chunk-L3NEJ4N5-b88bbtwi.js} +1 -1
  135. package/static/dist/assets/{chunk-OSK3NFVY-bi6widqt.js → chunk-OSK3NFVY-e3e7nr6s.js} +1 -1
  136. package/static/dist/assets/{chunk-P2QGCYS3-czr4tebv.js → chunk-P2QGCYS3-mdc46rt1.js} +1 -1
  137. package/static/dist/assets/{chunk-POPQ4Y6H-ktdhn67w.js → chunk-POPQ4Y6H-kqlllrrm.js} +1 -1
  138. package/static/dist/assets/{chunk-PWAF6VOD-ii4dsu0d.js → chunk-PWAF6VOD-he585k2x.js} +1 -1
  139. package/static/dist/assets/{chunk-SHT3W25Y-fpwk3xxp.js → chunk-SHT3W25Y-gh94y9a4.js} +1 -1
  140. package/static/dist/assets/{chunk-SVP7TREG-k0jrvhei.js → chunk-SVP7TREG-euxp9y7e.js} +1 -1
  141. package/static/dist/assets/{chunk-TICWLB2K-nkqv9ot0.js → chunk-TICWLB2K-c8cxes1q.js} +1 -1
  142. package/static/dist/assets/classDiagram-ZZMXUADV-n0vs2f2q.js +1 -0
  143. package/static/dist/assets/classDiagram-v2-VYDZK3BY-n0vs2f2q.js +1 -0
  144. package/static/dist/assets/{copied-gpchexqu.js → copied-kq0u3ht7.js} +1 -1
  145. package/static/dist/assets/{cynefin-OW5HDTMX-lqxt8rs3.js → cynefin-OW5HDTMX-lxd44ux4.js} +1 -1
  146. package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-j9eqovf5.js → cynefinDiagram-5FMLGOSQ-njpiwat8.js} +1 -1
  147. package/static/dist/assets/{dagre-GXQ25YYZ-m9agobjm.js → dagre-GXQ25YYZ-nvgy6yju.js} +1 -1
  148. package/static/dist/assets/{diagram-S7CK7UJ4-itodc50c.js → diagram-S7CK7UJ4-g98nyv72.js} +1 -1
  149. package/static/dist/assets/{diagram-UQ7AKVKN-n1h2t4ah.js → diagram-UQ7AKVKN-ovgm6lvv.js} +1 -1
  150. package/static/dist/assets/{diagram-VSXAHHWV-lvtvm9bj.js → diagram-VSXAHHWV-pb2bn2e4.js} +1 -1
  151. package/static/dist/assets/{diagram-VX7I27RA-cgas58sy.js → diagram-VX7I27RA-6j4mvfqo.js} +1 -1
  152. package/static/dist/assets/{diagram-Z3DM3KII-d0aeps1q.js → diagram-Z3DM3KII-dqmb982r.js} +1 -1
  153. package/static/dist/assets/{ebnfDiagram-PWID7BFC-md345fyq.js → ebnfDiagram-PWID7BFC-ozbpzb6d.js} +1 -1
  154. package/static/dist/assets/{endpoints-e58saih3.js → endpoints-k8agnro8.js} +8 -8
  155. package/static/dist/assets/{erDiagram-RLTQ6QDP-i4rua63b.js → erDiagram-RLTQ6QDP-hl7m8vua.js} +1 -1
  156. package/static/dist/assets/eventmodeling-NTZA5JFV-j1y2ekec.js +1 -0
  157. package/static/dist/assets/flowDiagram-HODETNUW-g2cvcbm5.js +1 -0
  158. package/static/dist/assets/{ganttDiagram-EL5Y4UJY-geqx7zgl.js → ganttDiagram-EL5Y4UJY-ompixqmh.js} +1 -1
  159. package/static/dist/assets/{gitGraph-4MIJSDKK-leoq0z8t.js → gitGraph-4MIJSDKK-b6vxwp9q.js} +1 -1
  160. package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-llrlxfha.js → gitGraphDiagram-WWUBYQGX-k98f57on.js} +1 -1
  161. package/static/dist/assets/{highlighted-body-KPVGNVTW-kq2cnw6q.js → highlighted-body-KPVGNVTW-cd3ff6ge.js} +1 -1
  162. package/static/dist/assets/index-nfy0ezs6.css +2 -0
  163. package/static/dist/assets/{index-f9bzglkn.js → index-nqed3oum.js} +3 -3
  164. package/static/dist/assets/{info-A6RAGUB7-lkly4xj6.js → info-A6RAGUB7-hjcwjami.js} +1 -1
  165. package/static/dist/assets/{infoDiagram-27XIBGKW-oo7kdyte.js → infoDiagram-27XIBGKW-fm06rj36.js} +1 -1
  166. package/static/dist/assets/{ishikawaDiagram-5VMMS53U-hq6jf374.js → ishikawaDiagram-5VMMS53U-bxak4mwu.js} +1 -1
  167. package/static/dist/assets/{journeyDiagram-3NMN7TZE-is03ntb0.js → journeyDiagram-3NMN7TZE-khmihclr.js} +1 -1
  168. package/static/dist/assets/{kanban-definition-UXKFOSKX-jmerizqb.js → kanban-definition-UXKFOSKX-h1r32wdx.js} +1 -1
  169. package/static/dist/assets/{lazyRouteComponent-b1h2ykmb.js → lazyRouteComponent-b2eg0fws.js} +1 -1
  170. package/static/dist/assets/{line-laaa8h99.js → line-friyjof3.js} +1 -1
  171. package/static/dist/assets/{link-d8f0788e.js → link-m90ahx3n.js} +1 -1
  172. package/static/dist/assets/login-bw7m2fzo.js +1 -0
  173. package/static/dist/assets/mermaid-HWGCJPDP-njwcl62n.js +1 -0
  174. package/static/dist/assets/{mermaid-parser.core-ix4junux.js → mermaid-parser.core-f1gvpwrt.js} +3 -3
  175. package/static/dist/assets/{mindmap-definition-YA3MSWOX-o0e0adl1.js → mindmap-definition-YA3MSWOX-mt2oxwue.js} +1 -1
  176. package/static/dist/assets/{onboarding-lx0d6b38.js → onboarding-ret37j1e.js} +1 -1
  177. package/static/dist/assets/{packet-AYTQ26CC-kl8pwsyg.js → packet-AYTQ26CC-jik4ixlf.js} +1 -1
  178. package/static/dist/assets/{pegDiagram-XKGWAZYB-bbqn9jo4.js → pegDiagram-XKGWAZYB-erxlv6cc.js} +1 -1
  179. package/static/dist/assets/{pie-WAS4IAKB-cpyo8bkr.js → pie-WAS4IAKB-mnkoldfl.js} +1 -1
  180. package/static/dist/assets/{pieDiagram-E7YTZNPT-pelug34f.js → pieDiagram-E7YTZNPT-i7lwrada.js} +1 -1
  181. package/static/dist/assets/{quadrantDiagram-AXDQQJYC-jds4jmr5.js → quadrantDiagram-AXDQQJYC-fr8ejdoy.js} +1 -1
  182. package/static/dist/assets/{radar-RG4KPBEZ-e02oxnvd.js → radar-RG4KPBEZ-otarjte2.js} +1 -1
  183. package/static/dist/assets/{railroad-74A4TZTK-kz3delwo.js → railroad-74A4TZTK-lw362oto.js} +1 -1
  184. package/static/dist/assets/railroad-abnf-HS5TGJTU-m741owar.js +1 -0
  185. package/static/dist/assets/railroad-ebnf-LZEXJU2U-noa4fbia.js +1 -0
  186. package/static/dist/assets/railroad-peg-WCYAUIDC-o4bpw4nb.js +1 -0
  187. package/static/dist/assets/{railroadDiagram-O6MQD6OU-jgroj2ew.js → railroadDiagram-O6MQD6OU-esyn75pw.js} +1 -1
  188. package/static/dist/assets/{refresh-cw-k7yxjdn3.js → refresh-cw-k66keho6.js} +1 -1
  189. package/static/dist/assets/{requirementDiagram-BXWQKSXE-m4az5e0j.js → requirementDiagram-BXWQKSXE-cyhi5u29.js} +1 -1
  190. package/static/dist/assets/{sankeyDiagram-P5KCCOFB-vlryqiv3.js → sankeyDiagram-P5KCCOFB-jljpbo0h.js} +1 -1
  191. package/static/dist/assets/{saved-brdfjjnw.js → saved-do49si3v.js} +1 -1
  192. package/static/dist/assets/{search-gt2ueyvg.js → search-j5ki6a9o.js} +1 -1
  193. package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-fr8hrpql.js → sequenceDiagram-WJ2MYXX4-fz0dhlu7.js} +1 -1
  194. package/static/dist/assets/{settings_section_extensions_title-l0zodj0b.js → settings_section_extensions_title-b7lojn0o.js} +1 -1
  195. package/static/dist/assets/share._token-kg8w647b.js +2 -0
  196. package/static/dist/assets/{sliders-horizontal-gcgscv8r.js → sliders-horizontal-c4wya4dy.js} +1 -1
  197. package/static/dist/assets/{stateDiagram-D77RDMKH-bg548iyr.js → stateDiagram-D77RDMKH-kdnithdx.js} +1 -1
  198. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-k2wg2645.js +1 -0
  199. package/static/dist/assets/{swimlanes-42K2YHIH-hk5z3efh.js → swimlanes-42K2YHIH-esd6gmz3.js} +1 -1
  200. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-n5kvjtgr.js +8 -0
  201. package/static/dist/assets/tabBuild-bjmyvmn5.js +56 -0
  202. package/static/dist/assets/{timeline-definition-24CTP7MA-olvknr62.js → timeline-definition-24CTP7MA-g0qygccv.js} +1 -1
  203. package/static/dist/assets/todoStore-kjk5l0sy.js +1 -0
  204. package/static/dist/assets/{treeView-Q6P3EWNA-ovvjsxtf.js → treeView-Q6P3EWNA-ndchzkuu.js} +1 -1
  205. package/static/dist/assets/{treemap-WGGIJYW6-ggkipj59.js → treemap-WGGIJYW6-hwjxj0zz.js} +1 -1
  206. package/static/dist/assets/{untitled-c8ydw0bu.js → untitled-mbr6tuhu.js} +1 -1
  207. package/static/dist/assets/{useForm-kd759sjk.js → useForm-ocxu460f.js} +1 -1
  208. package/static/dist/assets/useLocale-fzy5jhg6.js +1 -0
  209. package/static/dist/assets/{useParams-katwvzil.js → useParams-lo9u33d1.js} +1 -1
  210. package/static/dist/assets/{useSearch-c70sgau1.js → useSearch-lq1mr090.js} +1 -1
  211. package/static/dist/assets/{vennDiagram-4TSXK5OY-hzld7zt7.js → vennDiagram-4TSXK5OY-gubovd1r.js} +1 -1
  212. package/static/dist/assets/{wardley-WFR3VGLG-hxkeoms8.js → wardley-WFR3VGLG-ir3om65j.js} +1 -1
  213. package/static/dist/assets/{wardleyDiagram-VM6X3IG4-ljh90mb1.js → wardleyDiagram-VM6X3IG4-ce0or1ie.js} +1 -1
  214. package/static/dist/assets/{xychartDiagram-S5SC5T6Z-mnuaes03.js → xychartDiagram-S5SC5T6Z-dexylcjg.js} +1 -1
  215. package/static/dist/index.html +1 -1
  216. package/static/dist/sw.js +1 -1
  217. package/static/dist/assets/AppShell-haqi9qzu.js +0 -1
  218. package/static/dist/assets/ChatPage-hjyu2g64.js +0 -22
  219. package/static/dist/assets/HubRoute-gkd8o1vg.js +0 -2
  220. package/static/dist/assets/Select-jld3yeni.js +0 -1
  221. package/static/dist/assets/SessionListPanel-d3qx4ric.js +0 -1
  222. package/static/dist/assets/_app-bnip2pjg.js +0 -1
  223. package/static/dist/assets/_app.ext._extensionId-egptzrfy.js +0 -1
  224. package/static/dist/assets/_app.index-vdz13ul5.js +0 -1
  225. package/static/dist/assets/_app.insights-lgjshsba.js +0 -1
  226. package/static/dist/assets/_app.kanban-c18cjrpz.js +0 -1
  227. package/static/dist/assets/_app.logs-ezhojgeg.js +0 -1
  228. package/static/dist/assets/_app.memory-i00paisv.js +0 -1
  229. package/static/dist/assets/_app.profiles-kd02bwjx.js +0 -1
  230. package/static/dist/assets/_app.settings-giaudkuk.js +0 -1
  231. package/static/dist/assets/_app.settings._section-k4qtlexk.js +0 -1
  232. package/static/dist/assets/_app.skills-bby2uvl8.js +0 -1
  233. package/static/dist/assets/_app.tasks-1kz9bosm.js +0 -2
  234. package/static/dist/assets/_app.todos-izjdbkm2.js +0 -1
  235. package/static/dist/assets/_app.workspaces-lfh2kw7s.js +0 -1
  236. package/static/dist/assets/architecture-7GRP2DOG-b4u9z1w3.js +0 -1
  237. package/static/dist/assets/channel-o77ioktg.js +0 -1
  238. package/static/dist/assets/classDiagram-ZZMXUADV-f5rvod26.js +0 -1
  239. package/static/dist/assets/classDiagram-v2-VYDZK3BY-f5rvod26.js +0 -1
  240. package/static/dist/assets/eventmodeling-NTZA5JFV-bn3fjsaq.js +0 -1
  241. package/static/dist/assets/flowDiagram-HODETNUW-li70d120.js +0 -1
  242. package/static/dist/assets/index-gkh7kum4.css +0 -2
  243. package/static/dist/assets/login-llkohrcg.js +0 -1
  244. package/static/dist/assets/mermaid-HWGCJPDP-id0ffk2r.js +0 -1
  245. package/static/dist/assets/railroad-abnf-HS5TGJTU-l13ped2z.js +0 -1
  246. package/static/dist/assets/railroad-ebnf-LZEXJU2U-c7rg3g4g.js +0 -1
  247. package/static/dist/assets/railroad-peg-WCYAUIDC-od0b8niq.js +0 -1
  248. package/static/dist/assets/share._token-oyjv046r.js +0 -2
  249. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-j5v40qih.js +0 -1
  250. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-grzefo9c.js +0 -8
  251. package/static/dist/assets/tabBuild-idliyw9f.js +0 -56
  252. package/static/dist/assets/todoStore-j4c2xq0b.js +0 -1
  253. package/static/dist/assets/useLocale-hds4xmja.js +0 -1
@@ -3,12 +3,16 @@
3
3
  * detail payload, list and search, and every mutation with its guards.
4
4
  * Runtime concerns owned by other domains arrive through `SessionServiceDeps`.
5
5
  */
6
+ import type { PendingSteer } from '@maudecode/talaria-web-contracts';
6
7
  import type { RunJournal } from './journal.js';
7
8
  import type { DraftStore } from './drafts.js';
8
9
  import type { SessionEventBus } from './events.js';
9
10
  import { type ListParams, type ListResponse, type Row, type RuntimeOverlay } from './list.js';
10
11
  import { Session, titleFrom, type Message } from './session.js';
11
12
  import { type SessionStore } from './store.js';
13
+ import { type CompressionJob } from './compress.js';
14
+ import { type SidecarLike } from '../sidecar/client.js';
15
+ import { type Receipt } from './background-tasks.js';
12
16
  import type { WorkspaceRegistry } from '../workspace/workspaces.js';
13
17
  import { type ShareStore } from './shares.js';
14
18
  import type { ProjectStore } from '../projects.js';
@@ -17,7 +21,20 @@ export declare class HttpFailure extends Error {
17
21
  readonly extra: Record<string, unknown>;
18
22
  constructor(status: number, message: string, extra?: Record<string, unknown>);
19
23
  }
24
+ /**
25
+ * Python `_agent_runtime_barrier_response`: a stale local Agent checkout is refused with a typed 409 before the caller
26
+ * mutates anything. Any other failure of the check is not a verdict and lets the caller proceed.
27
+ */
28
+ export declare function ensureAgentRuntimeCurrent(sidecar: SidecarLike | null): Promise<void>;
20
29
  export interface SessionServiceDeps {
30
+ /** TAL-255: the Agent sidecar for manual compression (`chat.compress`); null while it is down. */
31
+ sidecar?: () => SidecarLike | null;
32
+ /** Detached sidecar work for a profile: deletion waits for the returned release, so the home outlives the work. */
33
+ profileActivity?: (profile: string | null) => () => void;
34
+ /** A profile whose deletion has started takes no new detached work. */
35
+ profileDeleting?: (profile: string | null) => boolean;
36
+ /** TAL-372: the session's background work receipts, for the delegation rows that started them. */
37
+ backgroundReceipts?: (sid: string) => Receipt[];
21
38
  store: SessionStore;
22
39
  drafts: DraftStore;
23
40
  events: SessionEventBus;
@@ -29,6 +46,8 @@ export interface SessionServiceDeps {
29
46
  activeProfile: () => string;
30
47
  isolatedProfileMode: () => boolean;
31
48
  profilesMatch: (a: string | null | undefined, b: string | null | undefined) => boolean;
49
+ /** The agent's display name for a session's profile (`assistant_name`). */
50
+ assistantName: (profile: string | null) => string;
32
51
  redactEnabled: () => boolean;
33
52
  pinnedSessionsLimit: () => number;
34
53
  runtime: RuntimeOverlay & {
@@ -36,12 +55,16 @@ export interface SessionServiceDeps {
36
55
  activeRunStream: (sid: string) => string | null;
37
56
  /** A live run whose journal missed a frame, so a replay cannot restore its whole output. */
38
57
  journalDegraded: (streamId: string) => boolean;
58
+ /** TAL-424: the live stream's pending steers, as clients show them. */
59
+ pendingSteers?: (streamId: string) => PendingSteer[];
39
60
  evictAgent: (sid: string) => void;
40
61
  closeTerminal: (sid: string) => void;
41
62
  /** Python `delete_cli_session`: remove the session's rows from the profile's state.db; resolves false on failure. */
42
63
  deleteCliSession: (profile: string | null, sid: string) => Promise<boolean>;
43
64
  };
44
65
  attachmentDir: (sid: string) => string;
66
+ /** A deleted session can never be viewed, so its finished runs are cleared from the relay. */
67
+ clearRelayCompletions?: (sid: string, profile: string | null) => void;
45
68
  /** Run journals are removed with their session (Python `delete_run_journal`). */
46
69
  journal?: RunJournal;
47
70
  hermesHome: string;
@@ -59,15 +82,21 @@ export interface SessionServiceDeps {
59
82
  set: (sid: string, enabled: boolean) => void;
60
83
  };
61
84
  /** state.db sidebar rows for a profile (Python `get_cli_sessions`); null when the projection is unavailable. */
85
+ /** `truncated`: source kinds whose rows stopped at the per-kind window on this read (TAL-482). */
62
86
  cliSessions: (profile: string, opts: {
63
87
  sourceFilter: string | null;
64
- }) => Row[];
88
+ }) => {
89
+ rows: Row[];
90
+ truncated: ReadonlySet<string>;
91
+ };
65
92
  profileHome: (profile: string) => string;
66
93
  /** Python `commit_session_memory` (fire-and-forget): the cached Agent flushes memory for a session the user left. */
67
94
  commitSessionMemory?: (sid: string) => void;
68
95
  }
69
96
  export declare class SessionService {
70
97
  readonly deps: SessionServiceDeps;
98
+ /** TAL-255: one manual compression worker per session, shared by every compress route. */
99
+ private readonly compressionJobs;
71
100
  constructor(deps: SessionServiceDeps);
72
101
  private get store();
73
102
  publish(reason: string, profile?: string | null, sessionId?: string | null): void;
@@ -96,7 +125,13 @@ export declare class SessionService {
96
125
  * rows (a WebUI conversation continued from the CLI shows the CLI turns). This is the coordinate space `GET
97
126
  * /api/session` exposes, so branching and the next model history slice/extend the same list.
98
127
  */
99
- mergedTranscript(s: Session, local?: Message[]): Message[];
128
+ mergedTranscript(s: Session, local?: Message[], stateRows?: Message[]): Message[];
129
+ /**
130
+ * Python `reconciled_state_db_messages_for_session(prefer_context=True)`: the model history is the owner context
131
+ * extended append-only with the Agent's state.db rows (a CLI continuation of this session reaches the model), except
132
+ * for a compressed context whose anchor cannot be verified — that stays context-only.
133
+ */
134
+ modelContext(s: Session, stateRows?: Message[]): Message[];
100
135
  /** Python `_lookup_cli_session_metadata`: the sidebar row for a state.db session in the active profile. */
101
136
  private lookupCliMeta;
102
137
  /** Python `_session_index_marks_was_webui`: an index row that once owned a WebUI sidecar (self-heal 404). */
@@ -112,10 +147,14 @@ export declare class SessionService {
112
147
  };
113
148
  private mutationTarget;
114
149
  private rejectSubagent;
150
+ /** TAL-372: each delegation row of a turn's scene carries the status of the work it started. */
151
+ backgroundLinked(s: Session, messages: unknown[]): unknown[];
115
152
  /** `compact()` plus messages, redacted for the wire (Python `_public_session_projection`). */
116
153
  publicSession(s: Session, withMessages?: boolean): Record<string, unknown>;
117
154
  /** `compact()` with the wire streaming/read-only flags (TAL-312), for replies that return the session row. */
118
155
  wireRow(s: Session): Record<string, unknown>;
156
+ /** The agent's display name for a session's profile (`assistant_name`). */
157
+ assistantName(s: Session): string;
119
158
  /** Python `public_session_projection(s.__dict__)`: every persisted field, redacted (session export). */
120
159
  publicSessionDocument(s: Session): Record<string, unknown>;
121
160
  private loadRevision;
@@ -139,7 +178,7 @@ export declare class SessionService {
139
178
  private foreignSessionDetail;
140
179
  /** Clear persisted streaming flags when no live stream backs them (Python `_clear_stale_stream_state`, no journal recovery). */
141
180
  clearStaleStreamState(session: Session): boolean;
142
- list(params: Omit<ListParams, 'activeProfile' | 'isolatedProfileMode' | 'profilesMatch' | 'cliRows' | 'gatewayIdentity' | 'stateDbSources'>): {
181
+ list(params: Omit<ListParams, 'activeProfile' | 'isolatedProfileMode' | 'profilesMatch' | 'cliRows' | 'gatewayIdentity' | 'stateDbSources' | 'truncatedSources'>): {
143
182
  body: ListResponse;
144
183
  etag: string;
145
184
  };
@@ -173,6 +212,23 @@ export declare class SessionService {
173
212
  /** Python `/api/personality/set` persistence: the name only; the prompt is resolved by the caller. */
174
213
  setPersonality(sid: string, name: string | null): Promise<string | null>;
175
214
  truncate(sid: string, keepRaw: unknown): Promise<Record<string, unknown>>;
215
+ /**
216
+ * Python `_handle_session_compress_start`: validate, then join the session's running job or start a fresh one (a
217
+ * finished job is replaced). The stale-runtime refusal comes before a job exists; a running job is joined without it.
218
+ */
219
+ startCompression(sid: string, focusRaw: unknown): Promise<CompressionJob>;
220
+ /** The session a compression may run on now, and the model history it compresses: the context a turn would send, so a
221
+ * repeat compression builds on the previous summary instead of re-reading the whole display transcript. */
222
+ private compressionTarget;
223
+ /** The session's compression job; a deleted session's finished result is dropped rather than served. */
224
+ compressionJob(sid: string): CompressionJob | undefined;
225
+ /**
226
+ * Python `_handle_session_compress`: compress the sanitized transcript in the sidecar outside the lock, then under the
227
+ * session lock refuse a result the session moved past (stream state or transcript changed) and install it as the
228
+ * model context with the manual anchor and the #4836 boundary that keeps state.db from replaying compressed rows.
229
+ * The guards run again first: a stream may have started while the caller awaited the runtime check.
230
+ */
231
+ private compressSession;
176
232
  clear(sid: string): Promise<Record<string, unknown>>;
177
233
  retry(sid: string): Promise<Record<string, unknown>>;
178
234
  undo(sid: string): Promise<Record<string, unknown>>;
@@ -195,6 +251,8 @@ export declare const AUTO_TITLE_LABELS: Set<string>;
195
251
  /** Python `apply_session_title_rename`. */
196
252
  export declare function applySessionTitleRename(session: Session, rawTitle: unknown): string;
197
253
  export declare function markSessionTitleGenerated(session: Session): void;
254
+ /** Python `_sanitize_error`: absolute paths in an error message never reach the client. */
255
+ export declare function sanitizePaths(error: unknown): string;
198
256
  /** Align model context with the display prefix (Python `truncate_context_for_display_keep`, simplified to the row-count contract). */
199
257
  export declare function truncateContextForDisplayKeep(context: Message[] | null | undefined, full: unknown[], keep: number): Message[];
200
258
  export declare function truncateSessionAtKeep(session: Session, keep: number): [number, number];
@@ -3,13 +3,17 @@ import { randomUUID } from 'node:crypto';
3
3
  import { rmSync } from 'node:fs';
4
4
  import { buildActiveTurnToken, copyJson, redactSessionData, redactValue, stripPublicInternalFields, withSceneToolDisplay } from '../redact.js';
5
5
  import { DraftVersionConflict, normalizeDraftVersion } from './drafts.js';
6
- import { allSessions, buildSessionListPayload, isClaimableCliSource, isMessagingSessionRecord, withOwnerLocks, withSessionWireFlags, lineageRootId, mergeCliSidebarMetadata, sessionListResponse, sessionSearchMessageText, sessionSearchPreview } from './list.js';
6
+ import { allSessions, buildSessionListPayload, CLI_IDENTITY_FIELDS, isClaimableCliSource, isMessagingSessionRecord, withOwnerLocks, withSessionWireFlags, lineageRootId, mergeCliSidebarMetadata, sessionListResponse, sessionSearchMessageText, sessionSearchPreview } from './list.js';
7
7
  import { anchorSceneIntOrNull, hydrateAnchorActivityScenes, normalizeAnchorSceneMessageRef, readAnchorSceneRows, storeAnchorScene, withTurnIds } from './anchor.js';
8
8
  import { isSafeSessionId, lastMessageTimestamp, Session, titleFrom } from './session.js';
9
9
  import { SessionBusy, SessionNotFound, statSignature } from './store.js';
10
10
  import { attachTodoState } from './todo.js';
11
11
  import { stateDbSessionMessages, stateDbSessionRow, stateDbSessionSources } from './state-db.js';
12
- import { attachmentObjects, mergeSessionMessagesAppendOnly, pendingUserRow, withAttachmentObjects, withPendingUserTurn, withToolCallOutcomes, withoutRunningTurnOutput } from './merge.js';
12
+ import { anchorMessageKey, anchorSummary, CompressionJobs, visibleMessagesForAnchor } from './compress.js';
13
+ import { SidecarError } from '../sidecar/client.js';
14
+ import { attachmentObjects, isContextCompressionMarker, mergeSessionMessagesAppendOnly, pendingUserRow, sanitizeMessagesForApi, withAttachmentObjects, withBodyExcerpts, withPendingUserTurn, withToolCallOutcomes, withoutRunningTurnOutput } from './merge.js';
15
+ import { withBackgroundUpdates } from './background-updates.js';
16
+ import { withBackgroundLinks } from './background-tasks.js';
13
17
  import { messagesForLimitedPayload, messageWindowForDisplay, MAX_MSG_LIMIT, parseMsgLimit, toolCallsForMessageWindow } from './window.js';
14
18
  import { redactText } from '../redact.js';
15
19
  import { buildShareSnapshot } from './shares.js';
@@ -25,11 +29,32 @@ export class HttpFailure extends Error {
25
29
  this.name = 'HttpFailure';
26
30
  }
27
31
  }
32
+ /**
33
+ * Python `_agent_runtime_barrier_response`: a stale local Agent checkout is refused with a typed 409 before the caller
34
+ * mutates anything. Any other failure of the check is not a verdict and lets the caller proceed.
35
+ */
36
+ export async function ensureAgentRuntimeCurrent(sidecar) {
37
+ if (!sidecar)
38
+ return;
39
+ try {
40
+ await sidecar.call('runtime.ensure_current', {});
41
+ }
42
+ catch (error) {
43
+ if (error instanceof SidecarError && error.condition === 'agent_runtime_stale')
44
+ throw staleRuntimeFailure(error);
45
+ }
46
+ }
47
+ function staleRuntimeFailure(error) {
48
+ return new HttpFailure(409, error.message, { type: 'agent_runtime_stale', retryable: true, restart_scheduled: false, ...(error.data.agent_update_state !== undefined ? { agent_update_state: error.data.agent_update_state } : {}) });
49
+ }
28
50
  const isDict = (v) => Boolean(v) && typeof v === 'object' && !Array.isArray(v);
29
51
  export class SessionService {
30
52
  deps;
53
+ /** TAL-255: one manual compression worker per session, shared by every compress route. */
54
+ compressionJobs;
31
55
  constructor(deps) {
32
56
  this.deps = deps;
57
+ this.compressionJobs = new CompressionJobs();
33
58
  }
34
59
  get store() { return this.deps.store; }
35
60
  publish(reason, profile, sessionId) {
@@ -135,16 +160,24 @@ export class SessionService {
135
160
  * rows (a WebUI conversation continued from the CLI shows the CLI turns). This is the coordinate space `GET
136
161
  * /api/session` exposes, so branching and the next model history slice/extend the same list.
137
162
  */
138
- mergedTranscript(s, local = s.messages) {
139
- const stateRows = this.stateDbRows(s);
163
+ mergedTranscript(s, local = s.messages, stateRows = this.stateDbRows(s)) {
140
164
  if (!stateRows.length)
141
165
  return local;
142
166
  return mergeSessionMessagesAppendOnly(local, stateRows, { truncationWatermark: s.truncation_watermark });
143
167
  }
168
+ /**
169
+ * Python `reconciled_state_db_messages_for_session(prefer_context=True)`: the model history is the owner context
170
+ * extended append-only with the Agent's state.db rows (a CLI continuation of this session reaches the model), except
171
+ * for a compressed context whose anchor cannot be verified — that stays context-only.
172
+ */
173
+ modelContext(s, stateRows) {
174
+ const local = s.context_messages.length ? s.context_messages : s.messages.filter((m) => !m._error && !m._partial);
175
+ return local.some((m) => isContextCompressionMarker(m)) ? local : this.mergedTranscript(s, local, stateRows ?? this.stateDbRows(s));
176
+ }
144
177
  /** Python `_lookup_cli_session_metadata`: the sidebar row for a state.db session in the active profile. */
145
178
  lookupCliMeta(sid) {
146
179
  try {
147
- return this.deps.cliSessions(this.deps.activeProfile(), { sourceFilter: null }).find((r) => str(r.session_id) === sid) ?? null;
180
+ return this.deps.cliSessions(this.deps.activeProfile(), { sourceFilter: null }).rows.find((r) => str(r.session_id) === sid) ?? null;
148
181
  }
149
182
  catch {
150
183
  return null;
@@ -214,6 +247,10 @@ export class SessionService {
214
247
  messages: msgs, created_at: Number(meta.created_at) || 0, updated_at: Number(meta.updated_at) || 0, profile: str(meta.profile) || null,
215
248
  is_cli_session: claimable ? true : !subagentChild, source_tag: str(meta.source_tag) || null, raw_source: str(meta.raw_source) || null,
216
249
  session_source: str(meta.session_source) || null, source_label: str(meta.source_label) || null, read_only: !claimable,
250
+ // Python `import_cli_session`: the claimed sidecar keeps the row's lineage, background project and channel identity
251
+ // (identity persists through `Session.extra`).
252
+ parent_session_id: str(meta.parent_session_id) || null, project_id: str(meta.project_id) || null,
253
+ ...Object.fromEntries(CLI_IDENTITY_FIELDS.filter((k) => meta[k] != null && meta[k] !== '').map((k) => [k, meta[k]])),
217
254
  }, defaults);
218
255
  return { session, reason: claimable ? 'materialized' : 'not_claimable' };
219
256
  }
@@ -233,17 +270,25 @@ export class SessionService {
233
270
  if (this.isSubagentViewOnly(sid))
234
271
  throw new HttpFailure(400, `Subagent sessions are view-only and cannot be ${verb} from WebUI`);
235
272
  }
273
+ /** TAL-372: each delegation row of a turn's scene carries the status of the work it started. */
274
+ backgroundLinked(s, messages) {
275
+ return withBackgroundLinks(messages, this.deps.backgroundReceipts?.(s.session_id) ?? []);
276
+ }
236
277
  /** `compact()` plus messages, redacted for the wire (Python `_public_session_projection`). */
237
278
  publicSession(s, withMessages = true) {
238
279
  const payload = this.wireRow(s);
239
280
  // Mutation replies replace a client's transcript, so they carry the same server-built scenes as the detail.
240
281
  if (withMessages)
241
- payload.messages = hydrateAnchorActivityScenes(withToolCallOutcomes(withTurnIds(s.messages), s.tool_calls, s.active_stream_id), s.anchor_activity_scenes, { activeTurnId: s.active_stream_id });
282
+ payload.messages = this.backgroundLinked(s, hydrateAnchorActivityScenes(withToolCallOutcomes(withBackgroundUpdates(withTurnIds(s.messages), s), s.tool_calls, s.active_stream_id), s.anchor_activity_scenes, { activeTurnId: s.active_stream_id }));
242
283
  return redactSessionData(payload, this.deps.redactEnabled());
243
284
  }
244
285
  /** `compact()` with the wire streaming/read-only flags (TAL-312), for replies that return the session row. */
245
286
  wireRow(s) {
246
- return withSessionWireFlags({ ...s.compact(), read_only: this.isReadOnly(s) }, this.deps.runtime.activeStreamIds);
287
+ return withSessionWireFlags({ ...s.compact({ contextLengthFor: this.deps.contextLengthFor }), read_only: this.isReadOnly(s), assistant_name: this.assistantName(s) }, this.deps.runtime.activeStreamIds);
288
+ }
289
+ /** The agent's display name for a session's profile (`assistant_name`). */
290
+ assistantName(s) {
291
+ return this.deps.assistantName(s.profile);
247
292
  }
248
293
  /** Python `public_session_projection(s.__dict__)`: every persisted field, redacted (session export). */
249
294
  publicSessionDocument(s) {
@@ -283,7 +328,7 @@ export class SessionService {
283
328
  if (journaled)
284
329
  transcript = withoutRunningTurnOutput(transcript, { ...journaled, localCount: s.messages.length });
285
330
  // Turn ids, tool outcomes and scenes are computed over the full transcript, so every window reports the same values.
286
- const all = loadMessages ? hydrateAnchorActivityScenes(withToolCallOutcomes(withTurnIds(withAttachmentObjects(transcript)), s.tool_calls, s.active_stream_id), s.anchor_activity_scenes, { activeTurnId: s.active_stream_id, clipToolResults: msgLimit !== null }) : [];
331
+ const all = loadMessages ? withBodyExcerpts(this.backgroundLinked(s, hydrateAnchorActivityScenes(withToolCallOutcomes(withBackgroundUpdates(withTurnIds(withAttachmentObjects(transcript)), s), s.tool_calls, s.active_stream_id), s.anchor_activity_scenes, { activeTurnId: s.active_stream_id, clipToolResults: msgLimit !== null })), s.active_stream_id) : [];
287
332
  let truncated = [];
288
333
  let offset = 0;
289
334
  let summaryCount = null;
@@ -317,7 +362,7 @@ export class SessionService {
317
362
  }
318
363
  const activeStreamIds = this.deps.runtime.activeStreamIds;
319
364
  const raw = {
320
- ...s.compact({ includeRuntime: true, activeStreamIds }),
365
+ ...s.compact({ includeRuntime: true, activeStreamIds, contextLengthFor: this.deps.contextLengthFor }),
321
366
  messages: truncated,
322
367
  message_count: mergedCount,
323
368
  tool_calls: toolCalls,
@@ -344,7 +389,9 @@ export class SessionService {
344
389
  const revisionAfter = this.loadRevision(s);
345
390
  raw._load_revision = revisionBefore !== null && revisionBefore === revisionAfter ? hashRevision(revisionBefore) : `unstable-${randomUUID().replace(/-/g, '')}`;
346
391
  raw.read_only = this.isReadOnly(s);
392
+ raw.assistant_name = this.assistantName(s);
347
393
  withSessionWireFlags(raw, activeStreamIds);
394
+ raw.pending_steers = raw.active_stream_id ? (this.deps.runtime.pendingSteers?.(str(raw.active_stream_id)) ?? []) : [];
348
395
  return redactSessionData(raw, this.deps.redactEnabled());
349
396
  }
350
397
  /**
@@ -398,6 +445,7 @@ export class SessionService {
398
445
  pinned: synth.pinned, archived: synth.archived, project_id: synth.project_id ?? null, profile: synth.profile,
399
446
  is_cli_session: synth.is_cli_session, source_tag: synth.source_tag, raw_source: synth.raw_source, session_source: synth.session_source,
400
447
  source_label: synth.source_label, read_only: synth.read_only, can_duplicate: false, messages: msgs, tool_calls: [], transcript_seq: null,
448
+ assistant_name: this.assistantName(synth),
401
449
  };
402
450
  attachTodoState(sess, msgs);
403
451
  const merged = withSessionWireFlags(meta ? mergeCliSidebarMetadata(sess, meta) : sess, this.deps.runtime.activeStreamIds);
@@ -413,7 +461,7 @@ export class SessionService {
413
461
  if (this.deps.runtime.activeRunStream(session.session_id))
414
462
  return false;
415
463
  const pendingAge = session.pending_started_at ? this.deps.now() - session.pending_started_at : null;
416
- if (session.pending_user_message && pendingAge !== null && pendingAge < 30)
464
+ if (session.hasPendingPrompt && pendingAge !== null && pendingAge < 30)
417
465
  return false;
418
466
  let target = session;
419
467
  if (session.loadedMetadataOnly) {
@@ -433,7 +481,7 @@ export class SessionService {
433
481
  // Python `_materialize_pending_user_turn_before_error` (#1361): the prompt that was in flight becomes a durable user
434
482
  // turn and an interruption marker follows it, so a dead stream never silently drops what the user sent.
435
483
  const pendingText = str(target.pending_user_message);
436
- if (pendingText) {
484
+ if (target.hasPendingPrompt) {
437
485
  const turnId = str(target.active_stream_id);
438
486
  const startedAt = typeof target.pending_started_at === 'number' && target.pending_started_at > 0 ? target.pending_started_at : this.deps.now();
439
487
  const attachments = [...target.pending_attachments];
@@ -466,9 +514,11 @@ export class SessionService {
466
514
  const activeProfile = this.deps.activeProfile();
467
515
  const wantState = params.showCliSessions || params.showCronSessions || params.showWebhookSessions || params.showKanbanSessions;
468
516
  // Python reads every profile's state.db under all_profiles; this port projects the active profile only.
469
- const cliRows = wantState ? this.deps.cliSessions(activeProfile, { sourceFilter: params.sourceFilter ?? null }) : undefined;
517
+ const cliRead = wantState ? this.deps.cliSessions(activeProfile, { sourceFilter: params.sourceFilter ?? null }) : undefined;
518
+ const cliRows = cliRead?.rows;
470
519
  const gatewayIdentity = loadGatewaySessionIdentityMap(join(this.deps.profileHome(activeProfile), 'sessions', 'sessions.json'));
471
- const payload = buildSessionListPayload(this.store, { ...params, ...(cliRows ? { cliRows } : {}), gatewayIdentity, stateDbSources: this.stateDbSources, activeProfile, isolatedProfileMode: this.deps.isolatedProfileMode(), profilesMatch: this.deps.profilesMatch });
520
+ const truncatedSources = cliRead?.truncated;
521
+ const payload = buildSessionListPayload(this.store, { ...params, ...(cliRows ? { cliRows } : {}), ...(truncatedSources ? { truncatedSources } : {}), gatewayIdentity, stateDbSources: this.stateDbSources, activeProfile, isolatedProfileMode: this.deps.isolatedProfileMode(), profilesMatch: this.deps.profilesMatch });
472
522
  return sessionListResponse(payload, this.deps.runtime, this.deps.redactEnabled(), this.deps.now());
473
523
  }
474
524
  search(q, opts) {
@@ -535,10 +585,10 @@ export class SessionService {
535
585
  catch {
536
586
  hermesHome = '';
537
587
  }
538
- const { is_streaming: streaming, active_stream_id: live, read_only: readOnly } = this.wireRow(full);
588
+ const { is_streaming: streaming, active_stream_id: live, read_only: readOnly, active_turn_origin: origin } = this.wireRow(full);
539
589
  return {
540
590
  session_id: full.session_id, title: full.title, model: full.model, profile, hermes_home: hermesHome, workspace: full.workspace, personality: full.personality,
541
- message_count: full.messages.length, created_at: full.created_at, updated_at: full.updated_at, agent_running: streaming, is_streaming: streaming, active_stream_id: live, read_only: readOnly,
591
+ message_count: full.messages.length, created_at: full.created_at, updated_at: full.updated_at, agent_running: streaming, is_streaming: streaming, active_stream_id: live, active_turn_origin: origin, read_only: readOnly,
542
592
  input_tokens: inp, output_tokens: out, total_tokens: inp + out, estimated_cost: full.estimated_cost,
543
593
  };
544
594
  }
@@ -774,6 +824,175 @@ export class SessionService {
774
824
  this.deps.runtime.evictAgent(sid);
775
825
  return { ok: true, session: this.publicSession(s) };
776
826
  }
827
+ // ── manual compression (TAL-255) ────────────────────────────────────────
828
+ /**
829
+ * Python `_handle_session_compress_start`: validate, then join the session's running job or start a fresh one (a
830
+ * finished job is replaced). The stale-runtime refusal comes before a job exists; a running job is joined without it.
831
+ */
832
+ async startCompression(sid, focusRaw) {
833
+ const running = () => { const job = this.compressionJobs.get(sid); return job?.status === 'running' ? job : undefined; };
834
+ // A running job is joined first: once it has installed its short context, the guards below would refuse a new one.
835
+ const existing = running();
836
+ if (existing)
837
+ return existing;
838
+ const { s } = this.compressionTarget(sid);
839
+ const focusTopic = str(focusRaw).trim().slice(0, 500) || null;
840
+ await ensureAgentRuntimeCurrent(this.deps.sidecar?.() ?? null);
841
+ // Another start may have admitted a job while the runtime check awaited.
842
+ const admitted = running();
843
+ if (admitted)
844
+ return admitted;
845
+ // The detached job (and its finalize) is the profile's activity until it settles; checked and taken in one step.
846
+ const profile = s.profile ?? null;
847
+ if (this.deps.profileDeleting?.(profile))
848
+ throw new HttpFailure(409, `Profile '${str(profile)}' is being deleted.`);
849
+ const release = this.deps.profileActivity?.(profile);
850
+ const now = this.deps.now();
851
+ const job = { session_id: sid, focus_topic: focusTopic, status: 'running', started_at: now, updated_at: now, done: Promise.resolve() };
852
+ job.done = this.compressSession(sid, focusTopic).finally(() => { release?.(); this.compressionJobs.expireLater(job); }).then((result) => { Object.assign(job, { status: 'done', result, updated_at: this.deps.now() }); }, (error) => {
853
+ const known = error instanceof HttpFailure;
854
+ if (!known)
855
+ this.deps.log(`[webui] Manual compression worker failed for session ${sid}: ${error.message}`);
856
+ Object.assign(job, { status: 'error', error: known ? error.message : `Compression failed: ${sanitizePaths(error)}`, error_status: known ? error.status : 500, error_extra: known ? error.extra : {}, updated_at: this.deps.now() });
857
+ });
858
+ this.compressionJobs.set(job);
859
+ return job;
860
+ }
861
+ /** The session a compression may run on now, and the model history it compresses: the context a turn would send, so a
862
+ * repeat compression builds on the previous summary instead of re-reading the whole display transcript. */
863
+ compressionTarget(sid) {
864
+ this.rejectSubagent(sid, 'compressed');
865
+ const s = this.mutationTarget(sid, 'compressed');
866
+ if (s.active_stream_id)
867
+ throw new HttpFailure(409, 'Session is still streaming; wait for the current turn to finish.');
868
+ const history = sanitizeMessagesForApi(this.modelContext(s));
869
+ if (history.length < 4)
870
+ throw new HttpFailure(400, 'Not enough conversation to compress (need at least 4 messages).');
871
+ return { s, history };
872
+ }
873
+ /** The session's compression job; a deleted session's finished result is dropped rather than served. */
874
+ compressionJob(sid) {
875
+ try {
876
+ this.store.get(sid, { metadataOnly: true });
877
+ }
878
+ catch {
879
+ this.compressionJobs.delete(sid);
880
+ throw new HttpFailure(404, 'Session not found');
881
+ }
882
+ return this.compressionJobs.get(sid);
883
+ }
884
+ /**
885
+ * Python `_handle_session_compress`: compress the sanitized transcript in the sidecar outside the lock, then under the
886
+ * session lock refuse a result the session moved past (stream state or transcript changed) and install it as the
887
+ * model context with the manual anchor and the #4836 boundary that keeps state.db from replaying compressed rows.
888
+ * The guards run again first: a stream may have started while the caller awaited the runtime check.
889
+ */
890
+ async compressSession(sid, focusTopic) {
891
+ const { s, history } = this.compressionTarget(sid);
892
+ // The display transcript and the model context must both be where the compression found them; the key starts from
893
+ // the exact history sent, so a state.db row that lands after this read fails the commit instead of being dropped.
894
+ const transcriptKey = (x, context) => JSON.stringify([sanitizeMessagesForApi(x.messages), context]);
895
+ const historyKey = transcriptKey(s, history);
896
+ const streamState = (x) => JSON.stringify([x.active_stream_id ?? null, x.pending_user_message ?? null, x.pending_attachments ?? null, x.pending_started_at ?? null]);
897
+ const streamBefore = streamState(s);
898
+ const sidecar = this.deps.sidecar?.();
899
+ if (!sidecar)
900
+ throw new HttpFailure(400, 'Compression failed: the Agent sidecar is not running');
901
+ const [model, provider] = this.deps.modelStateFromRequest(s.model, s.model_provider, s.model_provider);
902
+ let result;
903
+ try {
904
+ result = await sidecar.call('chat.compress', {
905
+ profile_home: this.deps.profileHome(s.profile ?? this.deps.activeProfile()), session_id: sid, model: model ?? '', model_provider: provider,
906
+ conversation_history: history, focus_topic: focusTopic, enabled_toolsets: s.enabled_toolsets,
907
+ }, { timeoutMs: 0 });
908
+ }
909
+ catch (error) {
910
+ if (error instanceof SidecarError && error.condition === 'agent_runtime_stale')
911
+ throw staleRuntimeFailure(error);
912
+ if (error instanceof SidecarError && error.condition === 'credential_missing')
913
+ throw new HttpFailure(400, 'No provider configured -- cannot compress.');
914
+ throw new HttpFailure(400, `Compression failed: ${sanitizePaths(error)}`);
915
+ }
916
+ // A held compression lock is a conflict to retry; `nothing_to_do` is the Agent's verdict on this transcript.
917
+ if (result.status !== 'compressed')
918
+ throw new HttpFailure(result.status === 'lock_skipped' ? 409 : 400, result.message ?? 'Nothing to compress yet.');
919
+ const summary = result.summary ?? {};
920
+ let committed = false;
921
+ try {
922
+ const current = await this.store.withLock(sid, () => {
923
+ let live;
924
+ try {
925
+ live = this.store.get(sid);
926
+ }
927
+ catch {
928
+ throw new HttpFailure(404, 'Session not found');
929
+ }
930
+ if (streamState(live) !== streamBefore)
931
+ throw new HttpFailure(409, 'Session stream state changed during compression; please retry.');
932
+ // One state.db read serves the check, the kept display rows, and the boundary.
933
+ const stateRows = this.stateDbRows(live);
934
+ if (transcriptKey(live, sanitizeMessagesForApi(this.modelContext(live, stateRows))) !== historyKey)
935
+ throw new HttpFailure(409, 'Session was modified during compression; please retry.');
936
+ // Rows the sidecar returns without a timestamp take the newest one this compression saw, never the clock: the
937
+ // boundary then hides no state.db row the append-only merge would still show (it already drops rows at or
938
+ // before the newest local row), so a CLI row committed after the read follows the compressed context.
939
+ const seen = [...live.messages, ...live.context_messages, ...stateRows].map((m) => Number(m.timestamp)).filter(Number.isFinite);
940
+ const stamp = seen.length ? Math.max(...seen) : this.deps.now();
941
+ const compressed = copyJson(result.messages);
942
+ for (const m of compressed)
943
+ m.timestamp ??= stamp;
944
+ live.context_messages = compressed;
945
+ live.active_stream_id = null;
946
+ live.pending_user_message = null;
947
+ live.pending_attachments = [];
948
+ live.pending_started_at = null;
949
+ live.pending_user_source = null;
950
+ // The compressed history included any state.db-only continuation; the new boundary would hide those rows from
951
+ // the display, so the transcript keeps them before it is applied (and the anchor counts them).
952
+ const display = this.mergedTranscript(live, live.messages, stateRows);
953
+ if (display.length > live.messages.length)
954
+ live.messages = copyJson(display);
955
+ const visible = visibleMessagesForAnchor(live.messages);
956
+ live.compression_anchor_visible_idx = visible.length ? visible.length - 1 : null;
957
+ live.compression_anchor_message_key = anchorMessageKey(visible.at(-1));
958
+ live.compression_anchor_summary = anchorSummary(summary, compressed);
959
+ live.compression_anchor_mode = 'manual';
960
+ // #4836: an intentional-shrink boundary, so append-only state.db reconciliation does not replay compressed rows.
961
+ live.truncation_watermark = truncationWatermarkFor(compressed);
962
+ live.truncation_boundary = live.truncation_watermark;
963
+ live.last_prompt_tokens = result.after_tokens;
964
+ live.post_compression_context_tokens_estimate = result.after_tokens;
965
+ this.store.save(live);
966
+ // A backup from before the compression would restore the uncompressed context.
967
+ try {
968
+ rmSync(`${this.store.pathFor(sid)}.bak`, { force: true });
969
+ }
970
+ catch { /* ignore */ }
971
+ return live;
972
+ });
973
+ committed = true;
974
+ // The cached turn agent still carries the uncompressed state; the next turn builds a fresh one (gateway parity).
975
+ // Awaited, so a turn sent right after the reply cannot reuse the old agent.
976
+ try {
977
+ await sidecar.call('chat.evict_agent', { session_id: sid });
978
+ }
979
+ catch (error) {
980
+ this.deps.log(`[webui] agent eviction after compression of ${sid} failed: ${error.message}`);
981
+ }
982
+ return { ok: true, session: this.publicSession(current), summary, focus_topic: focusTopic };
983
+ }
984
+ finally {
985
+ // Second phase: the Agent's context-engine notification fires only for a result the session now holds.
986
+ if (result.commit_token) {
987
+ try {
988
+ await sidecar.call('chat.compress_finalize', { commit_token: result.commit_token, committed });
989
+ }
990
+ catch (error) {
991
+ this.deps.log(`[webui] compression finalize for ${sid} failed: ${error.message}`);
992
+ }
993
+ }
994
+ }
995
+ }
777
996
  async clear(sid) {
778
997
  this.rejectSubagent(sid, 'modified');
779
998
  const s = this.mutationTarget(sid, 'modified');
@@ -930,11 +1149,13 @@ export class SessionService {
930
1149
  return {};
931
1150
  } })();
932
1151
  let eventProfile = null;
1152
+ let hadSidecar = true;
933
1153
  try {
934
1154
  eventProfile = this.store.get(sid, { metadataOnly: true }).profile;
935
1155
  }
936
1156
  catch {
937
1157
  eventProfile = null;
1158
+ hadSidecar = false;
938
1159
  }
939
1160
  // Python `_is_messaging_session_id`: decided before the JSON is gone, from WebUI metadata or the Agent's row.
940
1161
  const isMessaging = (() => { try {
@@ -993,6 +1214,9 @@ export class SessionService {
993
1214
  }
994
1215
  }
995
1216
  this.publish('session_delete', eventProfile);
1217
+ // Only a sidecar session is ever published to the relay.
1218
+ if (hadSidecar)
1219
+ this.deps.clearRelayCompletions?.(sid, eventProfile);
996
1220
  return { ok: true, state_db_cleanup_failed: stateDbCleanupFailed, ...retained };
997
1221
  }
998
1222
  cleanup(zeroOnly) {
@@ -1316,6 +1540,10 @@ function findLastUserIndex(history) {
1316
1540
  return i;
1317
1541
  return null;
1318
1542
  }
1543
+ /** Python `_sanitize_error`: absolute paths in an error message never reach the client. */
1544
+ export function sanitizePaths(error) {
1545
+ return str(error?.message ?? error).replace(/(?:(?:\/[a-zA-Z0-9_.-]+)+|(?:[A-Z]:\\[^\s]+))/g, '<path>');
1546
+ }
1319
1547
  function truncationWatermarkFor(messages) {
1320
1548
  if (!messages.length)
1321
1549
  return 0.0;