@maudecode/talaria-web 1.15.0 → 1.16.1

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 (312) hide show
  1. package/_release.json +4 -4
  2. package/dist/api/automation-router.d.ts +21 -7
  3. package/dist/api/automation-router.js +3 -0
  4. package/dist/api/automation-router.js.map +1 -1
  5. package/dist/api/chat-router.d.ts +142 -6
  6. package/dist/api/chat-router.js +11 -5
  7. package/dist/api/chat-router.js.map +1 -1
  8. package/dist/api/git-router.js +44 -32
  9. package/dist/api/git-router.js.map +1 -1
  10. package/dist/api/raw-routes.js +5 -58
  11. package/dist/api/raw-routes.js.map +1 -1
  12. package/dist/api/router.d.ts +4 -2
  13. package/dist/api/router.js +8 -8
  14. package/dist/api/router.js.map +1 -1
  15. package/dist/api/sessions-router.d.ts +714 -42
  16. package/dist/api/sessions-router.js +57 -23
  17. package/dist/api/sessions-router.js.map +1 -1
  18. package/dist/api/settings-router.d.ts +153 -9
  19. package/dist/api/settings-router.js +61 -16
  20. package/dist/api/settings-router.js.map +1 -1
  21. package/dist/api/sse-routes.js +1 -1
  22. package/dist/api/sse-routes.js.map +1 -1
  23. package/dist/api/tools-router.d.ts +13 -1
  24. package/dist/api/tools-router.js +10 -8
  25. package/dist/api/tools-router.js.map +1 -1
  26. package/dist/app.d.ts +1047 -67
  27. package/dist/auth/gate.js +8 -4
  28. package/dist/auth/gate.js.map +1 -1
  29. package/dist/auth/oidc.d.ts +6 -2
  30. package/dist/auth/oidc.js +16 -11
  31. package/dist/auth/oidc.js.map +1 -1
  32. package/dist/auth/store.d.ts +14 -2
  33. package/dist/auth/store.js +26 -1
  34. package/dist/auth/store.js.map +1 -1
  35. package/dist/cli/dotenv.d.ts +1 -1
  36. package/dist/cli/dotenv.js +36 -3
  37. package/dist/cli/dotenv.js.map +1 -1
  38. package/dist/http/addresses.d.ts +6 -0
  39. package/dist/http/addresses.js +26 -0
  40. package/dist/http/addresses.js.map +1 -1
  41. package/dist/http/context.d.ts +3 -3
  42. package/dist/links.d.ts +26 -0
  43. package/dist/links.js +60 -0
  44. package/dist/links.js.map +1 -0
  45. package/dist/onboarding.js +26 -7
  46. package/dist/onboarding.js.map +1 -1
  47. package/dist/profiles/profiles.js +1 -1
  48. package/dist/profiles/profiles.js.map +1 -1
  49. package/dist/providers/catalog.d.ts +74 -1
  50. package/dist/providers/catalog.js +248 -42
  51. package/dist/providers/catalog.js.map +1 -1
  52. package/dist/providers/tables.d.ts +3 -1
  53. package/dist/providers/tables.js +9 -4
  54. package/dist/providers/tables.js.map +1 -1
  55. package/dist/redact.js +493 -13
  56. package/dist/redact.js.map +1 -1
  57. package/dist/runtime.js +52 -28
  58. package/dist/runtime.js.map +1 -1
  59. package/dist/sessions/anchor.d.ts +22 -3
  60. package/dist/sessions/anchor.js +87 -42
  61. package/dist/sessions/anchor.js.map +1 -1
  62. package/dist/sessions/background-tasks.d.ts +4 -1
  63. package/dist/sessions/background-tasks.js +7 -4
  64. package/dist/sessions/background-tasks.js.map +1 -1
  65. package/dist/sessions/completions.js +1 -1
  66. package/dist/sessions/completions.js.map +1 -1
  67. package/dist/sessions/list.d.ts +8 -4
  68. package/dist/sessions/list.js +24 -17
  69. package/dist/sessions/list.js.map +1 -1
  70. package/dist/sessions/merge.d.ts +70 -7
  71. package/dist/sessions/merge.js +331 -23
  72. package/dist/sessions/merge.js.map +1 -1
  73. package/dist/sessions/pending.d.ts +5 -0
  74. package/dist/sessions/pending.js +30 -10
  75. package/dist/sessions/pending.js.map +1 -1
  76. package/dist/sessions/service.d.ts +57 -3
  77. package/dist/sessions/service.js +327 -56
  78. package/dist/sessions/service.js.map +1 -1
  79. package/dist/sessions/session.d.ts +6 -1
  80. package/dist/sessions/session.js +6 -0
  81. package/dist/sessions/session.js.map +1 -1
  82. package/dist/sessions/shares.js +11 -2
  83. package/dist/sessions/shares.js.map +1 -1
  84. package/dist/sessions/store.js +1 -1
  85. package/dist/sessions/store.js.map +1 -1
  86. package/dist/sessions/turn.d.ts +19 -1
  87. package/dist/sessions/turn.js +218 -113
  88. package/dist/sessions/turn.js.map +1 -1
  89. package/dist/settings.js +9 -0
  90. package/dist/settings.js.map +1 -1
  91. package/dist/sidecar/fake.js +2 -0
  92. package/dist/sidecar/fake.js.map +1 -1
  93. package/dist/tools/commands.d.ts +15 -0
  94. package/dist/tools/commands.js +52 -0
  95. package/dist/tools/commands.js.map +1 -0
  96. package/dist/tools/crons.d.ts +4 -1
  97. package/dist/tools/crons.js +58 -11
  98. package/dist/tools/crons.js.map +1 -1
  99. package/dist/tools/mcp.js +5 -1
  100. package/dist/tools/mcp.js.map +1 -1
  101. package/dist/tools/update-notifications.d.ts +1 -1
  102. package/dist/tools/update-notifications.js +1 -1
  103. package/dist/tools/update-notifications.js.map +1 -1
  104. package/dist/tools/updates.d.ts +8 -3
  105. package/dist/tools/updates.js +133 -34
  106. package/dist/tools/updates.js.map +1 -1
  107. package/dist/workspace/git.d.ts +57 -10
  108. package/dist/workspace/git.js +302 -195
  109. package/dist/workspace/git.js.map +1 -1
  110. package/dist/workspace/media-refs.d.ts +13 -0
  111. package/dist/workspace/media-refs.js +165 -0
  112. package/dist/workspace/media-refs.js.map +1 -0
  113. package/dist/workspace/media.d.ts +19 -0
  114. package/dist/workspace/media.js +61 -2
  115. package/dist/workspace/media.js.map +1 -1
  116. package/dist/workspace/workspaces.d.ts +7 -1
  117. package/dist/workspace/workspaces.js +30 -21
  118. package/dist/workspace/workspaces.js.map +1 -1
  119. package/dist/workspace/worktrees.d.ts +2 -4
  120. package/dist/workspace/worktrees.js +28 -33
  121. package/dist/workspace/worktrees.js.map +1 -1
  122. package/package.json +4 -2
  123. package/sidecar/scripts/replay_sidecar.py +3 -1
  124. package/sidecar/talaria_sidecar/__init__.py +1 -1
  125. package/sidecar/talaria_sidecar/home.py +59 -2
  126. package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
  127. package/sidecar/talaria_sidecar/methods/aux.py +19 -3
  128. package/sidecar/talaria_sidecar/methods/chat.py +129 -23
  129. package/sidecar/talaria_sidecar/methods/commands.py +46 -31
  130. package/sidecar/talaria_sidecar/methods/config.py +14 -10
  131. package/sidecar/talaria_sidecar/methods/oauth.py +439 -0
  132. package/sidecar/talaria_sidecar/methods/profiles.py +5 -4
  133. package/sidecar/talaria_sidecar/methods/providers.py +161 -1
  134. package/sidecar/talaria_sidecar/methods/runtime.py +2 -0
  135. package/sidecar/talaria_sidecar/methods/text.py +1 -20
  136. package/sidecar/talaria_sidecar/methods/usage.py +7 -1
  137. package/static/dist/FILES.txt +124 -120
  138. package/static/dist/assets/AppShell-ef3bhu32.js +1 -0
  139. package/static/dist/assets/Button-nx37f4u1.js +1 -0
  140. package/static/dist/assets/ChatPage-cdcpykcg.js +25 -0
  141. package/static/dist/assets/Dialog-mec42hvb.js +1 -0
  142. package/static/dist/assets/HubRoute-ecb65zpr.js +2 -0
  143. package/static/dist/assets/{Markdown-dy8t4dy7.js → Markdown-jqrx5es0.js} +125 -125
  144. package/static/dist/assets/RightPanel-d1lgvk4s.js +1 -0
  145. package/static/dist/assets/Select-h4ivl9qf.js +1 -0
  146. package/static/dist/assets/SessionListPanel-ewib046b.js +1 -0
  147. package/static/dist/assets/SettingsLayout-lx654cup.js +1 -0
  148. package/static/dist/assets/States-ehsb6lzo.js +1 -0
  149. package/static/dist/assets/Toaster-mch1pa3t.js +1 -0
  150. package/static/dist/assets/_app-m8d961ef.js +1 -0
  151. package/static/dist/assets/_app.ext._extensionId-mp33hbzi.js +1 -0
  152. package/static/dist/assets/_app.index-dk6zp7e0.js +1 -0
  153. package/static/dist/assets/_app.insights-jt2fk4i1.js +1 -0
  154. package/static/dist/assets/_app.kanban-if4pubeu.js +1 -0
  155. package/static/dist/assets/_app.logs-mfaw9m6g.js +1 -0
  156. package/static/dist/assets/_app.memory-h3eaarb6.js +1 -0
  157. package/static/dist/assets/_app.profiles-ienktovs.js +1 -0
  158. package/static/dist/assets/{_app.session._sessionId-gyoh5ylx.js → _app.session._sessionId-hh27wwu2.js} +1 -1
  159. package/static/dist/assets/_app.settings-dpoe0bzf.js +1 -0
  160. package/static/dist/assets/_app.settings._section-l468ynag.js +3 -0
  161. package/static/dist/assets/_app.skills-kcbncc8i.js +1 -0
  162. package/static/dist/assets/_app.tasks-occdrqhw.js +2 -0
  163. package/static/dist/assets/_app.todos-nnke2rcc.js +1 -0
  164. package/static/dist/assets/_app.workspaces-cdlatvwj.js +1 -0
  165. package/static/dist/assets/{abnfDiagram-VCTEODGH-cjqletra.js → abnfDiagram-VCTEODGH-oity9y4o.js} +1 -1
  166. package/static/dist/assets/architecture-7GRP2DOG-hj11333z.js +1 -0
  167. package/static/dist/assets/architectureDiagram-5GKGNRK7-ceaepnyr.js +36 -0
  168. package/static/dist/assets/{back-e4m6nsnm.js → back-eu64w89w.js} +1 -1
  169. package/static/dist/assets/{blockDiagram-I7D4REHJ-b195z021.js → blockDiagram-I7D4REHJ-n79st4pj.js} +1 -1
  170. package/static/dist/assets/{c4Diagram-7LVT6UL2-olu212k1.js → c4Diagram-7LVT6UL2-mi73akzk.js} +1 -1
  171. package/static/dist/assets/cancel-mzvkdjg8.js +1 -0
  172. package/static/dist/assets/channel-mbvmf2vv.js +1 -0
  173. package/static/dist/assets/{chunk-4HAMMTFA-b8o1nufs.js → chunk-4HAMMTFA-laat0g1q.js} +1 -1
  174. package/static/dist/assets/{chunk-75Z2AOVW-l1seddto.js → chunk-75Z2AOVW-0psy1tr2.js} +1 -1
  175. package/static/dist/assets/{chunk-DU6HZSFF-lo5zg15v.js → chunk-DU6HZSFF-lw8uox8x.js} +3 -3
  176. package/static/dist/assets/{chunk-F27PBJKO-dsdzrp16.js → chunk-F27PBJKO-erassib3.js} +1 -1
  177. package/static/dist/assets/{chunk-GMAD6QVW-f3fxqq8b.js → chunk-GMAD6QVW-fz7rx619.js} +1 -1
  178. package/static/dist/assets/{chunk-GVQU2GXP-h8egxtf6.js → chunk-GVQU2GXP-gdb0epb2.js} +1 -1
  179. package/static/dist/assets/{chunk-IMKFNOWR-iolwd8s0.js → chunk-IMKFNOWR-ea8t26qw.js} +1 -1
  180. package/static/dist/assets/{chunk-L3NEJ4N5-b88bbtwi.js → chunk-L3NEJ4N5-mb1gu9kz.js} +1 -1
  181. package/static/dist/assets/{chunk-OSK3NFVY-e3e7nr6s.js → chunk-OSK3NFVY-eovmccvy.js} +1 -1
  182. package/static/dist/assets/{chunk-P2QGCYS3-mdc46rt1.js → chunk-P2QGCYS3-bo55zjo5.js} +1 -1
  183. package/static/dist/assets/{chunk-POPQ4Y6H-kqlllrrm.js → chunk-POPQ4Y6H-ja7ohe4h.js} +1 -1
  184. package/static/dist/assets/{chunk-PWAF6VOD-he585k2x.js → chunk-PWAF6VOD-e30g9nus.js} +1 -1
  185. package/static/dist/assets/{chunk-SHT3W25Y-gh94y9a4.js → chunk-SHT3W25Y-gbb798f7.js} +3 -3
  186. package/static/dist/assets/{chunk-SVP7TREG-euxp9y7e.js → chunk-SVP7TREG-h2iw8urm.js} +1 -1
  187. package/static/dist/assets/{chunk-TICWLB2K-c8cxes1q.js → chunk-TICWLB2K-fd2cottz.js} +1 -1
  188. package/static/dist/assets/classDiagram-ZZMXUADV-o8r05k0n.js +1 -0
  189. package/static/dist/assets/classDiagram-v2-VYDZK3BY-o8r05k0n.js +1 -0
  190. package/static/dist/assets/{copied-kq0u3ht7.js → copied-h65fc3cx.js} +1 -1
  191. package/static/dist/assets/copy-e6977uwq.js +1 -0
  192. package/static/dist/assets/{cynefin-OW5HDTMX-lxd44ux4.js → cynefin-OW5HDTMX-lbltyl5j.js} +1 -1
  193. package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-njpiwat8.js → cynefinDiagram-5FMLGOSQ-lxhsqq4a.js} +1 -1
  194. package/static/dist/assets/{dagre-GXQ25YYZ-nvgy6yju.js → dagre-GXQ25YYZ-gn4nnaln.js} +4 -4
  195. package/static/dist/assets/{diagram-S7CK7UJ4-g98nyv72.js → diagram-S7CK7UJ4-pa3ij6q7.js} +3 -3
  196. package/static/dist/assets/{diagram-UQ7AKVKN-ovgm6lvv.js → diagram-UQ7AKVKN-zbipyz57.js} +1 -1
  197. package/static/dist/assets/{diagram-VSXAHHWV-pb2bn2e4.js → diagram-VSXAHHWV-d1kbpzgf.js} +1 -1
  198. package/static/dist/assets/{diagram-VX7I27RA-6j4mvfqo.js → diagram-VX7I27RA-i3ty14xl.js} +1 -1
  199. package/static/dist/assets/{diagram-Z3DM3KII-dqmb982r.js → diagram-Z3DM3KII-m8xefqa7.js} +1 -1
  200. package/static/dist/assets/{ebnfDiagram-PWID7BFC-ozbpzb6d.js → ebnfDiagram-PWID7BFC-nz7wj05l.js} +1 -1
  201. package/static/dist/assets/endpoints-k7yf25m0.js +64 -0
  202. package/static/dist/assets/{erDiagram-RLTQ6QDP-hl7m8vua.js → erDiagram-RLTQ6QDP-fa41ib83.js} +1 -1
  203. package/static/dist/assets/eventmodeling-NTZA5JFV-kpbq09bx.js +1 -0
  204. package/static/dist/assets/flowDiagram-HODETNUW-lcz0kx14.js +1 -0
  205. package/static/dist/assets/{ganttDiagram-EL5Y4UJY-ompixqmh.js → ganttDiagram-EL5Y4UJY-n7zq01qu.js} +1 -1
  206. package/static/dist/assets/{gitGraph-4MIJSDKK-b6vxwp9q.js → gitGraph-4MIJSDKK-c89emb07.js} +1 -1
  207. package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-k98f57on.js → gitGraphDiagram-WWUBYQGX-cz7cgr6w.js} +1 -1
  208. package/static/dist/assets/{highlighted-body-KPVGNVTW-cd3ff6ge.js → highlighted-body-KPVGNVTW-d9bapmni.js} +1 -1
  209. package/static/dist/assets/{index-nqed3oum.js → index-h00usru2.js} +3 -3
  210. package/static/dist/assets/index-n73ijkv2.css +2 -0
  211. package/static/dist/assets/{info-A6RAGUB7-hjcwjami.js → info-A6RAGUB7-ndddhhhh.js} +1 -1
  212. package/static/dist/assets/infoDiagram-27XIBGKW-ccxigcpf.js +2 -0
  213. package/static/dist/assets/{ishikawaDiagram-5VMMS53U-bxak4mwu.js → ishikawaDiagram-5VMMS53U-le0z9vtc.js} +1 -1
  214. package/static/dist/assets/{journeyDiagram-3NMN7TZE-khmihclr.js → journeyDiagram-3NMN7TZE-ivsmb26o.js} +1 -1
  215. package/static/dist/assets/{kanban-definition-UXKFOSKX-h1r32wdx.js → kanban-definition-UXKFOSKX-h6n2awt0.js} +1 -1
  216. package/static/dist/assets/lazyRouteComponent-m1rtyk4t.js +1 -0
  217. package/static/dist/assets/{line-friyjof3.js → line-dmzlfe0u.js} +1 -1
  218. package/static/dist/assets/link-budeg5t9.js +1 -0
  219. package/static/dist/assets/login-kjhasvp8.js +1 -0
  220. package/static/dist/assets/mermaid-HWGCJPDP-jb5d211j.js +1 -0
  221. package/static/dist/assets/{mermaid-parser.core-f1gvpwrt.js → mermaid-parser.core-gwyd5bnq.js} +3 -3
  222. package/static/dist/assets/{mindmap-definition-YA3MSWOX-mt2oxwue.js → mindmap-definition-YA3MSWOX-l68gg5h7.js} +1 -1
  223. package/static/dist/assets/onboarding-cdfrk0pj.js +1 -0
  224. package/static/dist/assets/{packet-AYTQ26CC-jik4ixlf.js → packet-AYTQ26CC-gpv06065.js} +1 -1
  225. package/static/dist/assets/{pegDiagram-XKGWAZYB-erxlv6cc.js → pegDiagram-XKGWAZYB-e52vs23u.js} +1 -1
  226. package/static/dist/assets/{pie-WAS4IAKB-mnkoldfl.js → pie-WAS4IAKB-l6m7nz5s.js} +1 -1
  227. package/static/dist/assets/{pieDiagram-E7YTZNPT-i7lwrada.js → pieDiagram-E7YTZNPT-b4is36bq.js} +3 -3
  228. package/static/dist/assets/{quadrantDiagram-AXDQQJYC-fr8ejdoy.js → quadrantDiagram-AXDQQJYC-m8vt2788.js} +1 -1
  229. package/static/dist/assets/{radar-RG4KPBEZ-otarjte2.js → radar-RG4KPBEZ-nxbrhiov.js} +1 -1
  230. package/static/dist/assets/{railroad-74A4TZTK-lw362oto.js → railroad-74A4TZTK-222mu9hi.js} +1 -1
  231. package/static/dist/assets/railroad-abnf-HS5TGJTU-kdvgl6x8.js +1 -0
  232. package/static/dist/assets/railroad-ebnf-LZEXJU2U-itiqslby.js +1 -0
  233. package/static/dist/assets/railroad-peg-WCYAUIDC-go7kov78.js +1 -0
  234. package/static/dist/assets/{railroadDiagram-O6MQD6OU-esyn75pw.js → railroadDiagram-O6MQD6OU-o8lldfzv.js} +1 -1
  235. package/static/dist/assets/{refresh-cw-k66keho6.js → refresh-cw-kbcje1uy.js} +1 -1
  236. package/static/dist/assets/{requirementDiagram-BXWQKSXE-cyhi5u29.js → requirementDiagram-BXWQKSXE-ovyq38kv.js} +1 -1
  237. package/static/dist/assets/{sankeyDiagram-P5KCCOFB-jljpbo0h.js → sankeyDiagram-P5KCCOFB-ou7lznor.js} +1 -1
  238. package/static/dist/assets/{saved-do49si3v.js → saved-bazmcup9.js} +1 -1
  239. package/static/dist/assets/search-jen74z4u.js +1 -0
  240. package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-fz0dhlu7.js → sequenceDiagram-WJ2MYXX4-ovckdzf2.js} +1 -1
  241. package/static/dist/assets/{settings_section_extensions_title-b7lojn0o.js → settings_section_extensions_title-ertwwhlx.js} +1 -1
  242. package/static/dist/assets/share._token-dan9hgvj.js +2 -0
  243. package/static/dist/assets/{sliders-horizontal-c4wya4dy.js → sliders-horizontal-f9cb3lh3.js} +1 -1
  244. package/static/dist/assets/{stateDiagram-D77RDMKH-kdnithdx.js → stateDiagram-D77RDMKH-fcrexkkv.js} +1 -1
  245. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-lcfraua2.js +1 -0
  246. package/static/dist/assets/{swimlanes-42K2YHIH-esd6gmz3.js → swimlanes-42K2YHIH-f7usu8fw.js} +1 -1
  247. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-ogmanni0.js +8 -0
  248. package/static/dist/assets/{timeline-definition-24CTP7MA-g0qygccv.js → timeline-definition-24CTP7MA-gp1hvz9f.js} +1 -1
  249. package/static/dist/assets/todoStore-eqtid4r4.js +1 -0
  250. package/static/dist/assets/{treeView-Q6P3EWNA-ndchzkuu.js → treeView-Q6P3EWNA-gb9gf11u.js} +1 -1
  251. package/static/dist/assets/{treemap-WGGIJYW6-hwjxj0zz.js → treemap-WGGIJYW6-nuvfnvvi.js} +1 -1
  252. package/static/dist/assets/{untitled-mbr6tuhu.js → untitled-htelhc68.js} +1 -1
  253. package/static/dist/assets/useForm-h2t0u14e.js +1 -0
  254. package/static/dist/assets/useLocale-hcsb84pj.js +1 -0
  255. package/static/dist/assets/useNavigate-nwy70ztj.js +1 -0
  256. package/static/dist/assets/{useParams-lo9u33d1.js → useParams-dqd64jv7.js} +1 -1
  257. package/static/dist/assets/{useSearch-lq1mr090.js → useSearch-j30pvsy7.js} +1 -1
  258. package/static/dist/assets/{vennDiagram-4TSXK5OY-gubovd1r.js → vennDiagram-4TSXK5OY-fzchbzaj.js} +1 -1
  259. package/static/dist/assets/{wardley-WFR3VGLG-ir3om65j.js → wardley-WFR3VGLG-m47mhfbh.js} +1 -1
  260. package/static/dist/assets/{wardleyDiagram-VM6X3IG4-ce0or1ie.js → wardleyDiagram-VM6X3IG4-odg6ct1z.js} +2 -2
  261. package/static/dist/assets/{xychartDiagram-S5SC5T6Z-dexylcjg.js → xychartDiagram-S5SC5T6Z-mrhms5um.js} +1 -1
  262. package/static/dist/index.html +1 -1
  263. package/static/dist/sw.js +1 -1
  264. package/static/dist/assets/AppShell-orat1s43.js +0 -1
  265. package/static/dist/assets/ChatPage-ojhka3hy.js +0 -22
  266. package/static/dist/assets/HubRoute-gxgw1t33.js +0 -2
  267. package/static/dist/assets/RightPanel-c66ani1s.js +0 -1
  268. package/static/dist/assets/Select-fswf66up.js +0 -1
  269. package/static/dist/assets/SessionListPanel-goh5mtt8.js +0 -1
  270. package/static/dist/assets/SettingsLayout-n5d4wq4m.js +0 -1
  271. package/static/dist/assets/States-kxyu50at.js +0 -1
  272. package/static/dist/assets/Toaster-6zmbcqgg.js +0 -1
  273. package/static/dist/assets/_app-fowyruiu.js +0 -1
  274. package/static/dist/assets/_app.ext._extensionId-nvjyrwhy.js +0 -1
  275. package/static/dist/assets/_app.index-c9zv4y7w.js +0 -1
  276. package/static/dist/assets/_app.insights-jf12nofo.js +0 -1
  277. package/static/dist/assets/_app.kanban-lri5a4yu.js +0 -1
  278. package/static/dist/assets/_app.logs-md1p0zqo.js +0 -1
  279. package/static/dist/assets/_app.memory-bzvf2e28.js +0 -1
  280. package/static/dist/assets/_app.profiles-jpg7zmbn.js +0 -1
  281. package/static/dist/assets/_app.settings-mpuwlx4j.js +0 -1
  282. package/static/dist/assets/_app.settings._section-knz8rwoj.js +0 -1
  283. package/static/dist/assets/_app.skills-fwu1dkir.js +0 -1
  284. package/static/dist/assets/_app.tasks-h9gphtl3.js +0 -2
  285. package/static/dist/assets/_app.todos-hd1dnvrh.js +0 -1
  286. package/static/dist/assets/_app.workspaces-l8toyn5f.js +0 -1
  287. package/static/dist/assets/architecture-7GRP2DOG-cavnusaw.js +0 -1
  288. package/static/dist/assets/architectureDiagram-5GKGNRK7-k5a7v36n.js +0 -36
  289. package/static/dist/assets/channel-gozy3dp3.js +0 -1
  290. package/static/dist/assets/classDiagram-ZZMXUADV-n0vs2f2q.js +0 -1
  291. package/static/dist/assets/classDiagram-v2-VYDZK3BY-n0vs2f2q.js +0 -1
  292. package/static/dist/assets/endpoints-k8agnro8.js +0 -9
  293. package/static/dist/assets/eventmodeling-NTZA5JFV-j1y2ekec.js +0 -1
  294. package/static/dist/assets/flowDiagram-HODETNUW-g2cvcbm5.js +0 -1
  295. package/static/dist/assets/index-nfy0ezs6.css +0 -2
  296. package/static/dist/assets/infoDiagram-27XIBGKW-fm06rj36.js +0 -2
  297. package/static/dist/assets/lazyRouteComponent-b2eg0fws.js +0 -1
  298. package/static/dist/assets/link-m90ahx3n.js +0 -1
  299. package/static/dist/assets/login-bw7m2fzo.js +0 -1
  300. package/static/dist/assets/mermaid-HWGCJPDP-njwcl62n.js +0 -1
  301. package/static/dist/assets/onboarding-ret37j1e.js +0 -1
  302. package/static/dist/assets/railroad-abnf-HS5TGJTU-m741owar.js +0 -1
  303. package/static/dist/assets/railroad-ebnf-LZEXJU2U-noa4fbia.js +0 -1
  304. package/static/dist/assets/railroad-peg-WCYAUIDC-o4bpw4nb.js +0 -1
  305. package/static/dist/assets/search-j5ki6a9o.js +0 -1
  306. package/static/dist/assets/share._token-kg8w647b.js +0 -2
  307. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-k2wg2645.js +0 -1
  308. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-n5kvjtgr.js +0 -8
  309. package/static/dist/assets/tabBuild-bjmyvmn5.js +0 -56
  310. package/static/dist/assets/todoStore-kjk5l0sy.js +0 -1
  311. package/static/dist/assets/useForm-ocxu460f.js +0 -1
  312. package/static/dist/assets/useLocale-fzy5jhg6.js +0 -1
@@ -4,19 +4,22 @@ 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
6
  import { allSessions, buildSessionListPayload, CLI_IDENTITY_FIELDS, isClaimableCliSource, isMessagingSessionRecord, withOwnerLocks, withSessionWireFlags, lineageRootId, mergeCliSidebarMetadata, sessionListResponse, sessionSearchMessageText, sessionSearchPreview } from './list.js';
7
- import { anchorSceneIntOrNull, hydrateAnchorActivityScenes, normalizeAnchorSceneMessageRef, readAnchorSceneRows, storeAnchorScene, withTurnIds } from './anchor.js';
8
- import { isSafeSessionId, lastMessageTimestamp, Session, titleFrom } from './session.js';
7
+ import { anchorSceneIntOrNull, fullToolResult, hydrateAnchorActivityScenes, normalizeAnchorSceneMessageRef, readAnchorSceneRows, storeAnchorScene, withTurnIds } from './anchor.js';
8
+ import { isSafeSessionId, lastMessageTimestamp, Session, stripAttachedFilesMarker, 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
12
  import { anchorMessageKey, anchorSummary, CompressionJobs, visibleMessagesForAnchor } from './compress.js';
13
13
  import { SidecarError } from '../sidecar/client.js';
14
- import { attachmentObjects, isContextCompressionMarker, mergeSessionMessagesAppendOnly, pendingUserRow, sanitizeMessagesForApi, withAttachmentObjects, withBodyExcerpts, withPendingUserTurn, withToolCallOutcomes, withoutRunningTurnOutput } from './merge.js';
14
+ import { attachmentObjects, isContextCompressionMarker, mergeSessionMessagesAppendOnly, pendingUserRow, sanitizeMessagesForApi, stripWorkspacePrefix, withAttachmentObjects, withBodyExcerpts, withDisplayMedia, withMarkerKinds, withSceneRowMedia, withPendingUserTurn, withToolCallOutcomes, withoutRunningTurnOutput } from './merge.js';
15
15
  import { withBackgroundUpdates } from './background-updates.js';
16
16
  import { withBackgroundLinks } from './background-tasks.js';
17
17
  import { messagesForLimitedPayload, messageWindowForDisplay, MAX_MSG_LIMIT, parseMsgLimit, toolCallsForMessageWindow } from './window.js';
18
18
  import { redactText } from '../redact.js';
19
+ import { workspaceDisplayName } from '../workspace/workspaces.js';
19
20
  import { buildShareSnapshot } from './shares.js';
21
+ import { mediaAnchorRoot, mediaRefPath, mediaTarget } from '../workspace/media.js';
22
+ import { projectMediaRefs } from '../workspace/media-refs.js';
20
23
  import { loadGatewaySessionIdentityMap } from './list.js';
21
24
  import { join } from 'node:path';
22
25
  export class HttpFailure extends Error {
@@ -57,6 +60,37 @@ export class SessionService {
57
60
  this.compressionJobs = new CompressionJobs();
58
61
  }
59
62
  get store() { return this.deps.store; }
63
+ /**
64
+ * TAL-186: rewrites a session's media references to their URLs. A local path is rewritten only when `/api/media`
65
+ * serves it for this session, under the same resolution, so every rewritten reference loads.
66
+ */
67
+ mediaProjector(s) {
68
+ const media = this.deps.media;
69
+ const served = new Map();
70
+ const localUrl = (path) => {
71
+ if (!media?.localIo(s.profile))
72
+ return null;
73
+ if (served.has(path))
74
+ return served.get(path) ?? null;
75
+ let url = null;
76
+ try {
77
+ const target = mediaTarget(mediaRefPath(path));
78
+ if (mediaAnchorRoot(target, s, media.access))
79
+ url = `./api/media?${new URLSearchParams({ path: target, session_id: s.session_id }).toString()}`;
80
+ }
81
+ catch { /* an invalid path stays text */ }
82
+ served.set(path, url);
83
+ return url;
84
+ };
85
+ const workspace = s.workspace.trim() || null;
86
+ // A scene's final answer usually repeats its row's text: each distinct text is parsed once.
87
+ const projected = new Map();
88
+ return (text) => {
89
+ if (!projected.has(text))
90
+ projected.set(text, projectMediaRefs(text, { workspace, localUrl }));
91
+ return projected.get(text) ?? null;
92
+ };
93
+ }
60
94
  publish(reason, profile, sessionId) {
61
95
  this.deps.events.publish(reason, { profile: profile ?? null, sessionId: sessionId ?? null });
62
96
  }
@@ -163,7 +197,7 @@ export class SessionService {
163
197
  mergedTranscript(s, local = s.messages, stateRows = this.stateDbRows(s)) {
164
198
  if (!stateRows.length)
165
199
  return local;
166
- return mergeSessionMessagesAppendOnly(local, stateRows, { truncationWatermark: s.truncation_watermark });
200
+ return mergeSessionMessagesAppendOnly(local, stateRows, { truncationWatermark: s.truncation_watermark, compressedWatermark: s.truncation_watermark_compressed });
167
201
  }
168
202
  /**
169
203
  * Python `reconciled_state_db_messages_for_session(prefer_context=True)`: the model history is the owner context
@@ -174,6 +208,15 @@ export class SessionService {
174
208
  const local = s.context_messages.length ? s.context_messages : s.messages.filter((m) => !m._error && !m._partial);
175
209
  return local.some((m) => isContextCompressionMarker(m)) ? local : this.mergedTranscript(s, local, stateRows ?? this.stateDbRows(s));
176
210
  }
211
+ /**
212
+ * TAL-518: the model context a `/btw` clone inherits: the chat's history plus its running turn's prompt, which
213
+ * deferred save keeps out of `messages` and `context_messages` until settlement.
214
+ */
215
+ sideQuestionContext(s) {
216
+ const context = structuredClone(this.modelContext(s));
217
+ const pending = this.pendingTurn(s);
218
+ return pending ? withPendingUserTurn(context, pending) : context;
219
+ }
177
220
  /** Python `_lookup_cli_session_metadata`: the sidebar row for a state.db session in the active profile. */
178
221
  lookupCliMeta(sid) {
179
222
  try {
@@ -279,12 +322,33 @@ export class SessionService {
279
322
  const payload = this.wireRow(s);
280
323
  // Mutation replies replace a client's transcript, so they carry the same server-built scenes as the detail.
281
324
  if (withMessages)
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 }));
325
+ payload.messages = this.backgroundLinked(s, hydrateAnchorActivityScenes(withToolCallOutcomes(withBackgroundUpdates(withMarkerKinds(withTurnIds(s.messages)), s), s.tool_calls, s.active_stream_id), s.anchor_activity_scenes, { activeTurnId: s.active_stream_id, runningScene: !this.journaledActiveTurn(s) }));
283
326
  return redactSessionData(payload, this.deps.redactEnabled());
284
327
  }
285
328
  /** `compact()` with the wire streaming/read-only flags (TAL-312), for replies that return the session row. */
286
329
  wireRow(s) {
287
- return withSessionWireFlags({ ...s.compact({ contextLengthFor: this.deps.contextLengthFor }), read_only: this.isReadOnly(s), assistant_name: this.assistantName(s) }, this.deps.runtime.activeStreamIds);
330
+ return withSessionWireFlags({ ...s.compact({ contextLengthFor: this.deps.contextLengthFor, modelOptionFor: this.deps.modelOptionFor }), read_only: this.isReadOnly(s), assistant_name: this.assistantName(s), workspace_name: this.workspaceNames()(s) }, this.deps.runtime.activeStreamIds);
331
+ }
332
+ /**
333
+ * TAL-303: the label for a session's workspace, from its own profile's registry (a profileless row is the root's).
334
+ * One resolver serves one response, so each profile's registry is read once however many rows it names.
335
+ */
336
+ workspaceNames() {
337
+ const registries = new Map();
338
+ return (row) => {
339
+ const profile = str(row.profile) || 'default';
340
+ let entries = registries.get(profile);
341
+ if (!entries) {
342
+ try {
343
+ entries = this.deps.workspaces.entries(profile);
344
+ }
345
+ catch {
346
+ entries = [];
347
+ }
348
+ registries.set(profile, entries);
349
+ }
350
+ return workspaceDisplayName(str(row.workspace), entries);
351
+ };
288
352
  }
289
353
  /** The agent's display name for a session's profile (`assistant_name`). */
290
354
  assistantName(s) {
@@ -328,7 +392,7 @@ export class SessionService {
328
392
  if (journaled)
329
393
  transcript = withoutRunningTurnOutput(transcript, { ...journaled, localCount: s.messages.length });
330
394
  // Turn ids, tool outcomes and scenes are computed over the full transcript, so every window reports the same values.
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) : [];
395
+ const all = loadMessages ? this.backgroundLinked(s, hydrateAnchorActivityScenes(withToolCallOutcomes(withBackgroundUpdates(withMarkerKinds(withTurnIds(withAttachmentObjects(transcript))), s), s.tool_calls, s.active_stream_id), s.anchor_activity_scenes, { activeTurnId: s.active_stream_id, runningScene: !journaled, clipToolResults: msgLimit !== null })) : [];
332
396
  let truncated = [];
333
397
  let offset = 0;
334
398
  let summaryCount = null;
@@ -344,6 +408,8 @@ export class SessionService {
344
408
  truncated = all.slice(prompt, offset + truncated.length);
345
409
  offset = prompt;
346
410
  }
411
+ // Per-row display fields (TAL-186 media, TAL-456 excerpts cut from that display text) only for the rows sent.
412
+ truncated = withBodyExcerpts(withDisplayMedia(truncated, this.mediaProjector(s)), s.active_stream_id);
347
413
  if (msgLimit !== null)
348
414
  truncated = messagesForLimitedPayload(truncated);
349
415
  }
@@ -362,7 +428,7 @@ export class SessionService {
362
428
  }
363
429
  const activeStreamIds = this.deps.runtime.activeStreamIds;
364
430
  const raw = {
365
- ...s.compact({ includeRuntime: true, activeStreamIds, contextLengthFor: this.deps.contextLengthFor }),
431
+ ...s.compact({ includeRuntime: true, activeStreamIds, contextLengthFor: this.deps.contextLengthFor, modelOptionFor: this.deps.modelOptionFor }),
366
432
  messages: truncated,
367
433
  message_count: mergedCount,
368
434
  tool_calls: toolCalls,
@@ -390,6 +456,7 @@ export class SessionService {
390
456
  raw._load_revision = revisionBefore !== null && revisionBefore === revisionAfter ? hashRevision(revisionBefore) : `unstable-${randomUUID().replace(/-/g, '')}`;
391
457
  raw.read_only = this.isReadOnly(s);
392
458
  raw.assistant_name = this.assistantName(s);
459
+ raw.workspace_name = this.workspaceNames()(s);
393
460
  withSessionWireFlags(raw, activeStreamIds);
394
461
  raw.pending_steers = raw.active_stream_id ? (this.deps.runtime.pendingSteers?.(str(raw.active_stream_id)) ?? []) : [];
395
462
  return redactSessionData(raw, this.deps.redactEnabled());
@@ -437,7 +504,7 @@ export class SessionService {
437
504
  foreignSessionDetail(sid) {
438
505
  const { synth, meta } = this.foreignSession(sid);
439
506
  // The same turn projection as a WebUI session: turn ids, tool outcomes, then each completed turn's scene.
440
- const msgs = hydrateAnchorActivityScenes(withToolCallOutcomes(withTurnIds(synth.messages), [], null), {});
507
+ const msgs = hydrateAnchorActivityScenes(withToolCallOutcomes(withMarkerKinds(withTurnIds(synth.messages)), [], null), {});
441
508
  const lastTs = Number(msgs[msgs.length - 1]?.timestamp ?? 0) || 0;
442
509
  const sess = {
443
510
  session_id: synth.session_id, title: synth.title, workspace: synth.workspace, model: synth.model, message_count: msgs.length,
@@ -445,7 +512,7 @@ export class SessionService {
445
512
  pinned: synth.pinned, archived: synth.archived, project_id: synth.project_id ?? null, profile: synth.profile,
446
513
  is_cli_session: synth.is_cli_session, source_tag: synth.source_tag, raw_source: synth.raw_source, session_source: synth.session_source,
447
514
  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),
515
+ assistant_name: this.assistantName(synth), workspace_name: this.workspaceNames()(synth),
449
516
  };
450
517
  attachTodoState(sess, msgs);
451
518
  const merged = withSessionWireFlags(meta ? mergeCliSidebarMetadata(sess, meta) : sess, this.deps.runtime.activeStreamIds);
@@ -519,7 +586,7 @@ export class SessionService {
519
586
  const gatewayIdentity = loadGatewaySessionIdentityMap(join(this.deps.profileHome(activeProfile), 'sessions', 'sessions.json'));
520
587
  const truncatedSources = cliRead?.truncated;
521
588
  const payload = buildSessionListPayload(this.store, { ...params, ...(cliRows ? { cliRows } : {}), ...(truncatedSources ? { truncatedSources } : {}), gatewayIdentity, stateDbSources: this.stateDbSources, activeProfile, isolatedProfileMode: this.deps.isolatedProfileMode(), profilesMatch: this.deps.profilesMatch });
522
- return sessionListResponse(payload, this.deps.runtime, this.deps.redactEnabled(), this.deps.now());
589
+ return sessionListResponse(payload, this.deps.runtime, this.deps.redactEnabled(), this.deps.now(), this.workspaceNames());
523
590
  }
524
591
  search(q, opts) {
525
592
  const activeProfile = this.deps.activeProfile();
@@ -528,8 +595,10 @@ export class SessionService {
528
595
  sessions = sessions.filter((r) => this.deps.profilesMatch(str(r.profile) || null, activeProfile));
529
596
  sessions = withOwnerLocks(sessions, this.stateDbSources);
530
597
  const redact = this.deps.redactEnabled();
598
+ const workspaceName = this.workspaceNames();
531
599
  const redactRow = (item) => {
532
600
  withSessionWireFlags(item, this.deps.runtime.activeStreamIds);
601
+ item.workspace_name = workspaceName(item);
533
602
  if (typeof item.title === 'string')
534
603
  item.title = redactText(item.title, redact);
535
604
  for (const f of ['display_title', '_state_db_title', 'parent_title'])
@@ -548,27 +617,61 @@ export class SessionService {
548
617
  }
549
618
  if (!opts.content)
550
619
  continue;
551
- let sess;
552
- try {
553
- sess = this.store.get(str(s.session_id), { promote: false, cacheOnMiss: false });
620
+ const hit = this.contentMatch(str(s.session_id), query, opts.depth);
621
+ if (hit)
622
+ results.push(redactRow({ ...s, ...hit }));
623
+ }
624
+ return { sessions: results, query, count: results.length, all_profiles: opts.allProfiles, active_profile: activeProfile };
625
+ }
626
+ /**
627
+ * TAL-308: the sidebar search. The candidates are exactly the `/api/sessions` rows for `params` (visibility, source,
628
+ * archived, projection and order), narrowed to `projectId` (`none`: rows without a project), then matched on the
629
+ * title, then the metadata the row shows (workspace path and name, model, provider, profile, source label), then message content.
630
+ */
631
+ sidebarSearch(q, params, opts) {
632
+ let rows = this.list(params).body.sessions;
633
+ if (opts.projectId === 'none')
634
+ rows = rows.filter((r) => !r.project_id);
635
+ else if (opts.projectId)
636
+ rows = rows.filter((r) => str(r.project_id) === opts.projectId);
637
+ const query = q.toLowerCase().trim();
638
+ const base = { all_profiles: params.allProfiles, active_profile: this.deps.activeProfile(), include_archived: params.includeArchived, sidebar_filtered: true };
639
+ if (!query)
640
+ return { ...base, sessions: rows };
641
+ const results = [];
642
+ for (const row of rows) {
643
+ if (str(row.title).toLowerCase().includes(query))
644
+ results.push({ ...row, match_type: 'title' });
645
+ else if (['workspace', 'workspace_name', 'model', 'model_provider', 'profile', 'source_label'].some((k) => str(row[k]).toLowerCase().includes(query)))
646
+ results.push({ ...row, match_type: 'metadata' });
647
+ else if (opts.content) {
648
+ const hit = this.contentMatch(str(row.session_id), query, opts.depth);
649
+ if (hit)
650
+ results.push({ ...row, ...hit });
554
651
  }
555
- catch {
652
+ }
653
+ return { ...base, sessions: results, query, count: results.length };
654
+ }
655
+ /** The first of a stored session's first `depth` messages (0: all) containing `query`, with its redacted excerpt. */
656
+ contentMatch(sid, query, depth) {
657
+ let sess;
658
+ try {
659
+ sess = this.store.get(sid, { promote: false, cacheOnMiss: false });
660
+ }
661
+ catch {
662
+ return null;
663
+ }
664
+ for (const m of depth ? sess.messages.slice(0, depth) : sess.messages) {
665
+ const c = sessionSearchMessageText(m);
666
+ if (!c.toLowerCase().includes(query))
556
667
  continue;
557
- }
558
- const msgs = opts.depth ? sess.messages.slice(0, opts.depth) : sess.messages;
559
- for (const m of msgs) {
560
- const c = sessionSearchMessageText(m);
561
- if (c.toLowerCase().includes(query)) {
562
- const item = { ...s, match_type: 'content' };
563
- const preview = sessionSearchPreview(c, query);
564
- if (preview)
565
- item.match_preview = redactText(preview, redact);
566
- results.push(redactRow(item));
567
- break;
568
- }
569
- }
668
+ const item = { match_type: 'content' };
669
+ const preview = sessionSearchPreview(c, query);
670
+ if (preview)
671
+ item.match_preview = redactText(preview, this.deps.redactEnabled());
672
+ return item;
570
673
  }
571
- return { sessions: results, query, count: results.length, all_profiles: opts.allProfiles, active_profile: activeProfile };
674
+ return null;
572
675
  }
573
676
  // ── status / usage ───────────────────────────────────────────────────────
574
677
  status(sid) {
@@ -628,7 +731,8 @@ export class SessionService {
628
731
  return toolsets;
629
732
  }
630
733
  create(body, opts = {}) {
631
- const profile = (typeof body.profile === 'string' && body.profile) || null;
734
+ // Python `new_session`: a body without a profile takes the request's active profile (cookie first).
735
+ const profile = (typeof body.profile === 'string' && body.profile) || this.deps.activeProfile();
632
736
  let prevSessionId = typeof body.prev_session_id === 'string' && body.prev_session_id ? body.prev_session_id : null;
633
737
  if (prevSessionId && !this.sessionIdVisible(prevSessionId))
634
738
  prevSessionId = null;
@@ -960,6 +1064,7 @@ export class SessionService {
960
1064
  // #4836: an intentional-shrink boundary, so append-only state.db reconciliation does not replay compressed rows.
961
1065
  live.truncation_watermark = truncationWatermarkFor(compressed);
962
1066
  live.truncation_boundary = live.truncation_watermark;
1067
+ live.truncation_watermark_compressed = true;
963
1068
  live.last_prompt_tokens = result.after_tokens;
964
1069
  live.post_compression_context_tokens_estimate = result.after_tokens;
965
1070
  this.store.save(live);
@@ -1029,7 +1134,7 @@ export class SessionService {
1029
1134
  catch { /* ignore */ }
1030
1135
  }
1031
1136
  });
1032
- this.deps.runtime.evictAgent(sid);
1137
+ this.deps.runtime.evictAgent(sid, true);
1033
1138
  return { ok: true, session: this.wireRow(s) };
1034
1139
  }
1035
1140
  async retry(sid) {
@@ -1040,11 +1145,19 @@ export class SessionService {
1040
1145
  const lastUser = findLastUserIndex(history);
1041
1146
  if (lastUser === null)
1042
1147
  return { error: 'No previous message to retry.' };
1043
- const lastUserText = extractText(history[lastUser]?.content);
1148
+ // What a client resends (TAL-515): the files that reached the model, which only ever got attachments with a path,
1149
+ // and the prompt as typed. The attached-files line is the server's only when such files exist; otherwise the user
1150
+ // typed it. Nothing to resend fails before the exchange is removed.
1151
+ const prompt = history[lastUser];
1152
+ const lastUserText = extractText(prompt.content);
1153
+ const lastUserAttachments = attachmentObjects(Array.isArray(prompt.attachments) ? prompt.attachments : []).filter((a) => isDict(a) && Boolean(str(a.path).trim()));
1154
+ const lastUserPrompt = stripWorkspacePrefix(lastUserAttachments.length ? stripAttachedFilesMarker(lastUserText) : lastUserText, true);
1155
+ if (!lastUserPrompt && !lastUserAttachments.length)
1156
+ return { error: 'The last message has nothing to resend.' };
1044
1157
  const removed = history.length - lastUser;
1045
1158
  shrinkTo(s, lastUser);
1046
1159
  this.store.save(s);
1047
- return { ok: true, last_user_text: lastUserText, removed_count: removed };
1160
+ return { ok: true, last_user_text: lastUserText, last_user_prompt: lastUserPrompt, last_user_attachments: lastUserAttachments, removed_count: removed };
1048
1161
  });
1049
1162
  }
1050
1163
  async undo(sid) {
@@ -1069,12 +1182,14 @@ export class SessionService {
1069
1182
  if (!session)
1070
1183
  throw new HttpFailure(404, 'Session not found');
1071
1184
  const now = this.deps.now();
1185
+ // A truncation cut names deleted state.db rows of the source; the copy's own state.db rows are all genuine (TAL-504).
1186
+ const watermark = session.truncation_watermark_compressed ? { truncation_watermark: session.truncation_watermark, truncation_boundary: session.truncation_boundary, truncation_watermark_compressed: true } : {};
1072
1187
  const copied = new Session({
1073
1188
  title: `${session.title || 'Untitled'} (copy)`, workspace: session.workspace, model: session.model, model_provider: session.model_provider,
1074
1189
  messages: copyJson(session.messages), tool_calls: copyJson(session.tool_calls), pinned: false, archived: false, project_id: session.project_id, profile: session.profile,
1075
1190
  input_tokens: session.input_tokens, output_tokens: session.output_tokens, estimated_cost: session.estimated_cost, cache_read_tokens: session.cache_read_tokens, cache_write_tokens: session.cache_write_tokens,
1076
1191
  personality: session.personality, enabled_toolsets: session.enabled_toolsets, context_length: session.context_length, threshold_tokens: session.threshold_tokens,
1077
- truncation_watermark: session.truncation_watermark, truncation_boundary: session.truncation_boundary, context_messages: copyJson(session.context_messages),
1192
+ ...watermark, context_messages: copyJson(session.context_messages),
1078
1193
  gateway_routing: copyJson(session.gateway_routing), gateway_routing_history: copyJson(session.gateway_routing_history), llm_title_generated: session.llm_title_generated,
1079
1194
  manual_title: session.manual_title, composer_draft: copyJson(session.composer_draft), context_engine: session.context_engine, context_engine_state: copyJson(session.context_engine_state),
1080
1195
  created_at: now, updated_at: now,
@@ -1182,6 +1297,14 @@ export class SessionService {
1182
1297
  await this.store.withLock(sid, () => {
1183
1298
  if (blocking())
1184
1299
  throw new HttpFailure(409, 'Session has an active run; stop it before deleting');
1300
+ // A public share outlives its session file, and revokeShare needs the session, so revoke it first.
1301
+ try {
1302
+ this.deps.shares.revoke(this.store.get(sid, { metadataOnly: true }));
1303
+ }
1304
+ catch (error) {
1305
+ if (!(error instanceof SessionNotFound))
1306
+ throw error;
1307
+ }
1185
1308
  if (!this.store.deleteFiles(sid))
1186
1309
  throw new HttpFailure(500, 'Failed to delete session data');
1187
1310
  }, { timeoutMs: 5000 });
@@ -1191,7 +1314,7 @@ export class SessionService {
1191
1314
  throw new HttpFailure(503, 'Session busy, try again');
1192
1315
  throw error;
1193
1316
  }
1194
- this.deps.runtime.evictAgent(sid);
1317
+ this.deps.runtime.evictAgent(sid, true);
1195
1318
  try {
1196
1319
  rmSync(this.deps.attachmentDir(sid), { recursive: true, force: true });
1197
1320
  }
@@ -1226,6 +1349,13 @@ export class SessionService {
1226
1349
  const shouldDelete = zeroOnly ? s.messages.length === 0 : s.title === 'Untitled' && s.messages.length === 0;
1227
1350
  if (!shouldDelete)
1228
1351
  continue;
1352
+ // A cleared session keeps its share; keep the session (and its revoke route) if the share cannot be revoked.
1353
+ try {
1354
+ this.deps.shares.revoke(s);
1355
+ }
1356
+ catch {
1357
+ continue;
1358
+ }
1229
1359
  this.store.sessions.delete(s.session_id);
1230
1360
  rmSync(this.store.pathFor(s.session_id), { force: true });
1231
1361
  cleaned += 1;
@@ -1426,36 +1556,50 @@ export class SessionService {
1426
1556
  return { ok: true, ...placement };
1427
1557
  });
1428
1558
  }
1429
- readAnchorScene(query) {
1430
- const sid = str(query.session_id).trim();
1431
- const messageRef = normalizeAnchorSceneMessageRef(query.message_ref);
1432
- const messageIndex = anchorSceneIntOrNull(query.message_index);
1433
- if (!sid || (!messageRef && messageIndex === null))
1434
- throw new HttpFailure(400, 'session_id and message_ref or message_index are required');
1435
- let session;
1436
- let transcript;
1559
+ /** The session and the full transcript its detail is built from; a state.db-only session gives its synthesized one. */
1560
+ fullTranscript(sid) {
1437
1561
  try {
1438
- session = this.store.get(sid);
1562
+ let session = this.store.get(sid);
1439
1563
  if (session.loadedMetadataOnly)
1440
1564
  session = this.store.load(sid) ?? session;
1441
1565
  if (!this.visibleToActiveProfile(session.profile))
1442
1566
  throw new HttpFailure(404, 'Session not found');
1443
- transcript = this.mergedTranscript(session);
1567
+ return { session, transcript: this.mergedTranscript(session), stored: true };
1444
1568
  }
1445
1569
  catch (error) {
1446
1570
  if (error instanceof HttpFailure)
1447
1571
  throw error;
1448
- // A state.db-only session pages the same synthesized transcript its detail was built from.
1449
- session = this.foreignSession(sid).synth;
1450
- transcript = session.messages;
1572
+ const session = this.foreignSession(sid).synth;
1573
+ return { session, transcript: session.messages, stored: false };
1451
1574
  }
1575
+ }
1576
+ readAnchorScene(query) {
1577
+ const sid = str(query.session_id).trim();
1578
+ const messageRef = normalizeAnchorSceneMessageRef(query.message_ref);
1579
+ const messageIndex = anchorSceneIntOrNull(query.message_index);
1580
+ if (!sid || (!messageRef && messageIndex === null))
1581
+ throw new HttpFailure(400, 'session_id and message_ref or message_index are required');
1582
+ const { session, transcript, stored } = this.fullTranscript(sid);
1452
1583
  const result = readAnchorSceneRows(session, { messageRef, messageIndex, before: anchorSceneIntOrNull(query.before), limit: anchorSceneIntOrNull(query.limit) }, withToolCallOutcomes(withTurnIds(transcript), session.tool_calls, session.active_stream_id));
1453
1584
  if (!result)
1454
1585
  throw new HttpFailure(404, 'Anchor activity scene not found');
1455
1586
  // Paged rows come from the raw transcript, so they take the same credential redaction as the detail's preview.
1456
1587
  const enabled = this.deps.redactEnabled();
1457
1588
  const redacted = redactValue(result, enabled);
1458
- return { ...redacted, rows: withSceneToolDisplay(result.rows, redacted.rows, enabled) };
1589
+ const rows = withSceneToolDisplay(result.rows, redacted.rows, enabled);
1590
+ // `/api/media` serves stored sessions only, so a state.db-only session's rows stay as written, as its detail does.
1591
+ return { ...redacted, rows: stored ? withSceneRowMedia(rows, this.mediaProjector(session)) : rows };
1592
+ }
1593
+ /** TAL-331: one tool call's whole result, which a limited response clipped (`result_truncated`), redacted like the detail. */
1594
+ readToolResult(query) {
1595
+ const sid = query.session_id.trim();
1596
+ const id = query.tool_call_id.trim();
1597
+ if (!sid || !id)
1598
+ throw new HttpFailure(400, 'session_id and tool_call_id are required');
1599
+ const full = fullToolResult(this.fullTranscript(sid).transcript, id);
1600
+ if (full === null)
1601
+ throw new HttpFailure(404, 'Tool result not found');
1602
+ return { tool_call_id: id, ...redactValue(full, this.deps.redactEnabled()) };
1459
1603
  }
1460
1604
  // ── shares ───────────────────────────────────────────────────────────────
1461
1605
  createShare(sid) {
@@ -1557,17 +1701,142 @@ function stampIntentionalShrink(session, oldCount, newCount) {
1557
1701
  session.intentional_shrink_generation = randomUUID().replace(/-/g, '');
1558
1702
  return true;
1559
1703
  }
1560
- /** Align model context with the display prefix (Python `truncate_context_for_display_keep`, simplified to the row-count contract). */
1704
+ /** Python truthiness for the JSON values a transcript row carries. */
1705
+ function pyTruthy(v) {
1706
+ if (Array.isArray(v))
1707
+ return v.length > 0;
1708
+ if (isDict(v))
1709
+ return Object.keys(v).length > 0;
1710
+ return Boolean(v);
1711
+ }
1712
+ /** JSON with sorted object keys (Python `json.dumps(..., sort_keys=True)`). */
1713
+ function sortedJson(v) {
1714
+ return JSON.stringify(v, (_key, value) => (isDict(value) ? Object.fromEntries(Object.keys(value).sort().map((k) => [k, value[k]])) : value)) ?? '';
1715
+ }
1716
+ /** Equality key for a row `id`/`timestamp`; `null` when the value is absent. Numbers compare by value (`2 == 2.0`). */
1717
+ function identityKey(v) {
1718
+ if (v === null || v === undefined)
1719
+ return null;
1720
+ return typeof v === 'string' ? `s${v}` : typeof v === 'number' ? `n${String(v)}` : `j${sortedJson(v)}`;
1721
+ }
1722
+ function rowSignature(row) {
1723
+ if (!isDict(row))
1724
+ return null;
1725
+ const field = (v) => (!pyTruthy(v) ? '' : typeof v === 'string' ? v : sortedJson(v));
1726
+ return JSON.stringify([field(row.role), field(row.content), field(row.tool_call_id), field(row.tool_use_id), field(row.tool_name || row.name), field(row.tool_calls)]);
1727
+ }
1728
+ /** First index in sorted `positions` whose value is `>= start`. */
1729
+ function lowerBound(positions, start) {
1730
+ let lo = 0;
1731
+ let hi = positions.length;
1732
+ while (lo < hi) {
1733
+ const mid = (lo + hi) >> 1;
1734
+ if (positions[mid] < start)
1735
+ lo = mid + 1;
1736
+ else
1737
+ hi = mid;
1738
+ }
1739
+ return lo;
1740
+ }
1741
+ /**
1742
+ * Map each display row to the context row it represents, scanning forward. An `id` match wins; otherwise the role+content
1743
+ * signature must match, and a shared `timestamp` makes it exact. A signature match where an identity is missing is weak:
1744
+ * one weak candidate counts as a match, a second one before any exact match leaves the row ambiguous.
1745
+ */
1746
+ function alignDisplayToContext(ctx, full) {
1747
+ const index = () => new Map();
1748
+ const push = (map, key, idx) => {
1749
+ const list = map.get(key);
1750
+ if (list)
1751
+ list.push(idx);
1752
+ else
1753
+ map.set(key, [idx]);
1754
+ };
1755
+ const first = (list, start) => list?.[lowerBound(list, start)];
1756
+ const byId = index(), bySigTs = index(), bySigTsNoId = index();
1757
+ const weakAny = index(), weakNoId = index(), weakNoTs = index(), weakNoIdNoTs = index();
1758
+ ctx.forEach((row, idx) => {
1759
+ const sig = rowSignature(row);
1760
+ if (sig === null)
1761
+ return;
1762
+ const id = identityKey(row.id);
1763
+ const ts = identityKey(row.timestamp);
1764
+ push(weakAny, sig, idx);
1765
+ if (id === null)
1766
+ push(weakNoId, sig, idx);
1767
+ if (ts === null)
1768
+ push(weakNoTs, sig, idx);
1769
+ if (id === null && ts === null)
1770
+ push(weakNoIdNoTs, sig, idx);
1771
+ if (id !== null)
1772
+ push(byId, id, idx);
1773
+ if (ts !== null) {
1774
+ push(bySigTs, `${sig}\0${ts}`, idx);
1775
+ if (id === null)
1776
+ push(bySigTsNoId, `${sig}\0${ts}`, idx);
1777
+ }
1778
+ });
1779
+ const matches = [];
1780
+ const ambiguous = [];
1781
+ let next = 0;
1782
+ for (const message of full) {
1783
+ let match = null;
1784
+ let ambiguousAt = null;
1785
+ const sig = rowSignature(message);
1786
+ if (sig !== null) {
1787
+ const id = identityKey(message.id);
1788
+ const ts = identityKey(message.timestamp);
1789
+ const exact = [id !== null ? first(byId.get(id), next) : undefined, ts !== null ? first((id !== null ? bySigTsNoId : bySigTs).get(`${sig}\0${ts}`), next) : undefined]
1790
+ .filter((idx) => idx !== undefined);
1791
+ const exactIdx = exact.length ? Math.min(...exact) : null;
1792
+ const weakList = (id !== null ? (ts !== null ? weakNoIdNoTs : weakNoId) : ts !== null ? weakNoTs : weakAny).get(sig) ?? [];
1793
+ const w = lowerBound(weakList, next);
1794
+ const [firstWeak, secondWeak] = [weakList[w], weakList[w + 1]];
1795
+ if (secondWeak !== undefined && (exactIdx === null || secondWeak < exactIdx))
1796
+ ambiguousAt = firstWeak;
1797
+ else
1798
+ match = exactIdx ?? firstWeak ?? null;
1799
+ }
1800
+ matches.push(match);
1801
+ ambiguous.push(ambiguousAt);
1802
+ if (match !== null)
1803
+ next = match + 1;
1804
+ }
1805
+ return { matches, ambiguous };
1806
+ }
1807
+ /** Align model context with the display prefix `full[:keep]` (Python `truncate_context_for_display_keep`, #5096/#5563). */
1561
1808
  export function truncateContextForDisplayKeep(context, full, keep) {
1562
1809
  if (keep <= 0)
1563
1810
  return [];
1564
- const rows = context ?? [];
1565
- if (!rows.length)
1811
+ const ctx = context ?? [];
1812
+ if (!ctx.length || !full.length)
1566
1813
  return [];
1567
- if (keep >= full.length)
1568
- return [...rows];
1569
- // ponytail: context rows map 1:1 onto display rows for WebUI-authored transcripts; fuzzy alignment stays in Python until compression lands.
1570
- return rows.slice(0, Math.min(rows.length, keep));
1814
+ if (keep < full.length) {
1815
+ const { matches, ambiguous } = alignDisplayToContext(ctx, full);
1816
+ const lastKept = matches[keep - 1];
1817
+ const firstUnkept = matches[keep];
1818
+ const boundary = full[keep - 1];
1819
+ const keptUser = isDict(boundary) && boundary.role === 'user';
1820
+ // Cut at the first unkept display row; a kept user turn ends at its own row so unkept tool rows after it drop.
1821
+ if (firstUnkept !== null)
1822
+ return lastKept !== null && keptUser ? ctx.slice(0, lastKept + 1) : ctx.slice(0, firstUnkept);
1823
+ if (lastKept !== null) {
1824
+ const ambiguousUnkept = ambiguous[keep];
1825
+ return ambiguousUnkept !== null && isDict(boundary) && !keptUser ? ctx.slice(0, ambiguousUnkept) : ctx.slice(0, lastKept + 1);
1826
+ }
1827
+ // Both boundary rows unresolved in a shorter (compressed) context: cut past the last kept display row that resolved,
1828
+ // accepting a weak match. Under-keeping beats slicing at the raw display index.
1829
+ if (ctx.length < full.length) {
1830
+ for (let i = keep - 1; i >= 0; i -= 1) {
1831
+ const resolved = matches[i] ?? ambiguous[i];
1832
+ if (resolved !== null && resolved !== undefined)
1833
+ return ctx.slice(0, resolved + 1);
1834
+ }
1835
+ }
1836
+ }
1837
+ // Unalignable (or nothing cut): keep the compaction prefix a longer context carries, then `keep` rows (#5096).
1838
+ const prefixLen = Math.max(0, ctx.length - full.length);
1839
+ return ctx.slice(0, prefixLen + keep);
1571
1840
  }
1572
1841
  export function truncateSessionAtKeep(session, keep) {
1573
1842
  const full = [...session.messages];
@@ -1578,6 +1847,7 @@ export function truncateSessionAtKeep(session, keep) {
1578
1847
  session.context_messages = truncateContextForDisplayKeep(session.context_messages, full, keep);
1579
1848
  session.truncation_watermark = truncationWatermarkFor(session.messages);
1580
1849
  session.truncation_boundary = session.truncation_watermark;
1850
+ session.truncation_watermark_compressed = false;
1581
1851
  return [oldMsgCount, oldCtxCount];
1582
1852
  }
1583
1853
  function shrinkTo(session, lastUserIdx) {
@@ -1586,6 +1856,7 @@ function shrinkTo(session, lastUserIdx) {
1586
1856
  stampIntentionalShrink(session, history.length, session.messages.length);
1587
1857
  session.truncation_watermark = truncationWatermarkFor(session.messages);
1588
1858
  session.truncation_boundary = session.truncation_watermark;
1859
+ session.truncation_watermark_compressed = false;
1589
1860
  if (session.context_messages.length) {
1590
1861
  const ctxLast = findLastUserIndex(session.context_messages);
1591
1862
  if (ctxLast !== null)