@maudecode/talaria-web 1.16.1 → 1.17.2

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 (328) hide show
  1. package/_release.json +4 -4
  2. package/dist/api/automation-raw.js +6 -3
  3. package/dist/api/automation-raw.js.map +1 -1
  4. package/dist/api/automation-router.d.ts +137 -9
  5. package/dist/api/automation-router.js +9 -6
  6. package/dist/api/automation-router.js.map +1 -1
  7. package/dist/api/chat-router.d.ts +51 -0
  8. package/dist/api/chat-router.js +29 -12
  9. package/dist/api/chat-router.js.map +1 -1
  10. package/dist/api/git-router.d.ts +12 -1
  11. package/dist/api/git-router.js +17 -7
  12. package/dist/api/git-router.js.map +1 -1
  13. package/dist/api/raw-routes.d.ts +6 -0
  14. package/dist/api/raw-routes.js +30 -0
  15. package/dist/api/raw-routes.js.map +1 -1
  16. package/dist/api/sessions-router.d.ts +260 -0
  17. package/dist/api/sessions-router.js +30 -2
  18. package/dist/api/sessions-router.js.map +1 -1
  19. package/dist/api/settings-router.d.ts +101 -2
  20. package/dist/api/settings-router.js +7 -2
  21. package/dist/api/settings-router.js.map +1 -1
  22. package/dist/api/sse-routes.d.ts +11 -3
  23. package/dist/api/sse-routes.js +144 -64
  24. package/dist/api/sse-routes.js.map +1 -1
  25. package/dist/api/tools-raw.js +6 -2
  26. package/dist/api/tools-raw.js.map +1 -1
  27. package/dist/api/tools-router.d.ts +73 -8
  28. package/dist/api/tools-router.js +9 -7
  29. package/dist/api/tools-router.js.map +1 -1
  30. package/dist/app.d.ts +0 -9377
  31. package/dist/app.js +7 -2
  32. package/dist/app.js.map +1 -1
  33. package/dist/auth/gate.js +3 -0
  34. package/dist/auth/gate.js.map +1 -1
  35. package/dist/config/agent-config.d.ts +2 -0
  36. package/dist/config/agent-config.js +9 -0
  37. package/dist/config/agent-config.js.map +1 -1
  38. package/dist/http/context.d.ts +3 -0
  39. package/dist/http/context.js +10 -4
  40. package/dist/http/context.js.map +1 -1
  41. package/dist/onboarding.js +4 -0
  42. package/dist/onboarding.js.map +1 -1
  43. package/dist/providers/catalog.d.ts +96 -7
  44. package/dist/providers/catalog.js +434 -113
  45. package/dist/providers/catalog.js.map +1 -1
  46. package/dist/redact.d.ts +1 -0
  47. package/dist/redact.js +27 -4
  48. package/dist/redact.js.map +1 -1
  49. package/dist/runtime.js +27 -9
  50. package/dist/runtime.js.map +1 -1
  51. package/dist/sessions/anchor.d.ts +2 -0
  52. package/dist/sessions/anchor.js +8 -3
  53. package/dist/sessions/anchor.js.map +1 -1
  54. package/dist/sessions/claude-code.d.ts +19 -0
  55. package/dist/sessions/claude-code.js +250 -0
  56. package/dist/sessions/claude-code.js.map +1 -0
  57. package/dist/sessions/completions.d.ts +13 -1
  58. package/dist/sessions/completions.js +63 -11
  59. package/dist/sessions/completions.js.map +1 -1
  60. package/dist/sessions/compress.d.ts +14 -1
  61. package/dist/sessions/compress.js +52 -11
  62. package/dist/sessions/compress.js.map +1 -1
  63. package/dist/sessions/journal.d.ts +8 -0
  64. package/dist/sessions/journal.js +44 -10
  65. package/dist/sessions/journal.js.map +1 -1
  66. package/dist/sessions/list.d.ts +6 -2
  67. package/dist/sessions/list.js +33 -10
  68. package/dist/sessions/list.js.map +1 -1
  69. package/dist/sessions/merge.d.ts +52 -2
  70. package/dist/sessions/merge.js +213 -10
  71. package/dist/sessions/merge.js.map +1 -1
  72. package/dist/sessions/service.d.ts +44 -2
  73. package/dist/sessions/service.js +251 -57
  74. package/dist/sessions/service.js.map +1 -1
  75. package/dist/sessions/session.d.ts +11 -1
  76. package/dist/sessions/session.js +23 -2
  77. package/dist/sessions/session.js.map +1 -1
  78. package/dist/sessions/state-db.d.ts +26 -4
  79. package/dist/sessions/state-db.js +126 -8
  80. package/dist/sessions/state-db.js.map +1 -1
  81. package/dist/sessions/store.js +5 -0
  82. package/dist/sessions/store.js.map +1 -1
  83. package/dist/sessions/streams.d.ts +10 -0
  84. package/dist/sessions/streams.js +14 -3
  85. package/dist/sessions/streams.js.map +1 -1
  86. package/dist/sessions/todo.d.ts +6 -1
  87. package/dist/sessions/todo.js +11 -4
  88. package/dist/sessions/todo.js.map +1 -1
  89. package/dist/sessions/tool-display.d.ts +12 -1
  90. package/dist/sessions/tool-display.js +76 -1
  91. package/dist/sessions/tool-display.js.map +1 -1
  92. package/dist/sessions/turn.d.ts +30 -9
  93. package/dist/sessions/turn.js +247 -78
  94. package/dist/sessions/turn.js.map +1 -1
  95. package/dist/settings.d.ts +20 -2
  96. package/dist/settings.js +40 -2
  97. package/dist/settings.js.map +1 -1
  98. package/dist/sidecar/fake.d.ts +2 -2
  99. package/dist/sidecar/fake.js +5 -2
  100. package/dist/sidecar/fake.js.map +1 -1
  101. package/dist/spa.js +2 -1
  102. package/dist/spa.js.map +1 -1
  103. package/dist/test/harness.js +2 -0
  104. package/dist/test/harness.js.map +1 -1
  105. package/dist/tools/insights.d.ts +8 -2
  106. package/dist/tools/insights.js +18 -2
  107. package/dist/tools/insights.js.map +1 -1
  108. package/dist/tools/kanban.d.ts +41 -1
  109. package/dist/tools/kanban.js +113 -2
  110. package/dist/tools/kanban.js.map +1 -1
  111. package/dist/tools/mcp.d.ts +2 -1
  112. package/dist/tools/mcp.js +11 -5
  113. package/dist/tools/mcp.js.map +1 -1
  114. package/dist/tools/terminal.d.ts +1 -0
  115. package/dist/tools/terminal.js +1 -1
  116. package/dist/tools/terminal.js.map +1 -1
  117. package/dist/tools/updates.d.ts +7 -0
  118. package/dist/tools/updates.js +74 -7
  119. package/dist/tools/updates.js.map +1 -1
  120. package/dist/workspace/fs.d.ts +7 -3
  121. package/dist/workspace/fs.js +5 -1
  122. package/dist/workspace/fs.js.map +1 -1
  123. package/dist/workspace/media.js +2 -0
  124. package/dist/workspace/media.js.map +1 -1
  125. package/dist/workspace/preview.d.ts +28 -0
  126. package/dist/workspace/preview.js +110 -0
  127. package/dist/workspace/preview.js.map +1 -0
  128. package/dist/workspace/rollback.d.ts +5 -4
  129. package/dist/workspace/rollback.js +12 -9
  130. package/dist/workspace/rollback.js.map +1 -1
  131. package/dist/workspace/upload.d.ts +2 -0
  132. package/dist/workspace/upload.js +4 -1
  133. package/dist/workspace/upload.js.map +1 -1
  134. package/package.json +2 -2
  135. package/sidecar/scripts/replay_sidecar.py +1 -1
  136. package/sidecar/talaria_sidecar/__init__.py +1 -1
  137. package/sidecar/talaria_sidecar/__main__.py +7 -1
  138. package/sidecar/talaria_sidecar/home.py +39 -0
  139. package/sidecar/talaria_sidecar/methods/chat.py +422 -38
  140. package/sidecar/talaria_sidecar/methods/commands.py +15 -2
  141. package/sidecar/talaria_sidecar/methods/cron.py +10 -6
  142. package/sidecar/talaria_sidecar/methods/kanban.py +115 -15
  143. package/sidecar/talaria_sidecar/methods/process.py +60 -1
  144. package/sidecar/talaria_sidecar/methods/profiles.py +7 -1
  145. package/sidecar/talaria_sidecar/methods/runtime.py +3 -0
  146. package/sidecar/talaria_sidecar/methods/text.py +17 -1
  147. package/sidecar/talaria_sidecar/methods/usage.py +257 -4
  148. package/static/dist/FILES.txt +126 -125
  149. package/static/dist/assets/AppShell-fb2td9zi.js +1 -0
  150. package/static/dist/assets/ChatPage-cj96kiy9.js +25 -0
  151. package/static/dist/assets/Dialog-kxcg7ard.js +1 -0
  152. package/static/dist/assets/ExtensionRoute-jf7h0754.js +1 -0
  153. package/static/dist/assets/HubRoute-gmanpd5y.js +2 -0
  154. package/static/dist/assets/{Markdown-jqrx5es0.js → Markdown-mdwlvj3w.js} +29 -29
  155. package/static/dist/assets/RightPanel-ftvrjlcp.js +1 -0
  156. package/static/dist/assets/Select-bilaypej.js +1 -0
  157. package/static/dist/assets/SessionListPanel-og8n36qs.js +1 -0
  158. package/static/dist/assets/SettingsLayout-jonjefmj.js +1 -0
  159. package/static/dist/assets/States-nmejrwrm.js +1 -0
  160. package/static/dist/assets/Toaster-nnl8zkt9.js +1 -0
  161. package/static/dist/assets/_app-gdidzd94.js +1 -0
  162. package/static/dist/assets/_app.ext._extensionId-opyh9y0v.js +1 -0
  163. package/static/dist/assets/_app.index-hbarzbjx.js +1 -0
  164. package/static/dist/assets/_app.insights-luywlarz.js +1 -0
  165. package/static/dist/assets/_app.kanban-kincnbr8.js +1 -0
  166. package/static/dist/assets/_app.logs-oi2gr3gs.js +1 -0
  167. package/static/dist/assets/_app.memory-b6jfivt6.js +1 -0
  168. package/static/dist/assets/_app.profiles-lpvixmqp.js +1 -0
  169. package/static/dist/assets/{_app.session._sessionId-hh27wwu2.js → _app.session._sessionId-bfd4h3id.js} +1 -1
  170. package/static/dist/assets/_app.settings-vowx3iq0.js +1 -0
  171. package/static/dist/assets/_app.settings._section-cnntg1tu.js +3 -0
  172. package/static/dist/assets/_app.skills-fb57lpau.js +1 -0
  173. package/static/dist/assets/_app.tasks-eszpx1q7.js +2 -0
  174. package/static/dist/assets/_app.todos-nr05ac6w.js +1 -0
  175. package/static/dist/assets/_app.workspaces-d2luomu6.js +1 -0
  176. package/static/dist/assets/{abnfDiagram-VCTEODGH-oity9y4o.js → abnfDiagram-VCTEODGH-b2x1lqot.js} +1 -1
  177. package/static/dist/assets/architecture-7GRP2DOG-hd0o7bde.js +1 -0
  178. package/static/dist/assets/{architectureDiagram-5GKGNRK7-ceaepnyr.js → architectureDiagram-5GKGNRK7-h38o5l7b.js} +1 -1
  179. package/static/dist/assets/{back-eu64w89w.js → back-bemaer09.js} +1 -1
  180. package/static/dist/assets/{blockDiagram-I7D4REHJ-n79st4pj.js → blockDiagram-I7D4REHJ-bqquxv2u.js} +1 -1
  181. package/static/dist/assets/{c4Diagram-7LVT6UL2-mi73akzk.js → c4Diagram-7LVT6UL2-f3fb8w6z.js} +1 -1
  182. package/static/dist/assets/{cancel-mzvkdjg8.js → cancel-drd0g6d9.js} +1 -1
  183. package/static/dist/assets/channel-cpdgf867.js +1 -0
  184. package/static/dist/assets/{chunk-4HAMMTFA-laat0g1q.js → chunk-4HAMMTFA-bzfyyzp3.js} +1 -1
  185. package/static/dist/assets/{chunk-75Z2AOVW-0psy1tr2.js → chunk-75Z2AOVW-ly99rfx5.js} +1 -1
  186. package/static/dist/assets/{chunk-DU6HZSFF-lw8uox8x.js → chunk-DU6HZSFF-bg6ecfqf.js} +3 -3
  187. package/static/dist/assets/{chunk-F27PBJKO-erassib3.js → chunk-F27PBJKO-e8k7mmqw.js} +1 -1
  188. package/static/dist/assets/{chunk-GMAD6QVW-fz7rx619.js → chunk-GMAD6QVW-cmalnoaj.js} +1 -1
  189. package/static/dist/assets/{chunk-GVQU2GXP-gdb0epb2.js → chunk-GVQU2GXP-gmif5o01.js} +1 -1
  190. package/static/dist/assets/{chunk-IMKFNOWR-ea8t26qw.js → chunk-IMKFNOWR-f1nwwh6u.js} +1 -1
  191. package/static/dist/assets/{chunk-L3NEJ4N5-mb1gu9kz.js → chunk-L3NEJ4N5-oktfh5v7.js} +1 -1
  192. package/static/dist/assets/{chunk-OSK3NFVY-eovmccvy.js → chunk-OSK3NFVY-b1sdx96z.js} +1 -1
  193. package/static/dist/assets/{chunk-P2QGCYS3-bo55zjo5.js → chunk-P2QGCYS3-fhu4nzxt.js} +1 -1
  194. package/static/dist/assets/{chunk-POPQ4Y6H-ja7ohe4h.js → chunk-POPQ4Y6H-em31rnef.js} +1 -1
  195. package/static/dist/assets/{chunk-PWAF6VOD-e30g9nus.js → chunk-PWAF6VOD-bxhhyftw.js} +1 -1
  196. package/static/dist/assets/{chunk-SHT3W25Y-gbb798f7.js → chunk-SHT3W25Y-fmrvwcav.js} +1 -1
  197. package/static/dist/assets/{chunk-SVP7TREG-h2iw8urm.js → chunk-SVP7TREG-lymukl2j.js} +1 -1
  198. package/static/dist/assets/{chunk-TICWLB2K-fd2cottz.js → chunk-TICWLB2K-lmza6k23.js} +1 -1
  199. package/static/dist/assets/classDiagram-ZZMXUADV-ko09sguk.js +1 -0
  200. package/static/dist/assets/classDiagram-v2-VYDZK3BY-ko09sguk.js +1 -0
  201. package/static/dist/assets/{copied-h65fc3cx.js → copied-ima3pysb.js} +1 -1
  202. package/static/dist/assets/copy-fw5b1pc6.js +1 -0
  203. package/static/dist/assets/{cynefin-OW5HDTMX-lbltyl5j.js → cynefin-OW5HDTMX-czlhbyhs.js} +1 -1
  204. package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-lxhsqq4a.js → cynefinDiagram-5FMLGOSQ-gq7mvswb.js} +1 -1
  205. package/static/dist/assets/{dagre-GXQ25YYZ-gn4nnaln.js → dagre-GXQ25YYZ-cqdbf7dr.js} +1 -1
  206. package/static/dist/assets/{diagram-S7CK7UJ4-pa3ij6q7.js → diagram-S7CK7UJ4-o6arxa58.js} +1 -1
  207. package/static/dist/assets/{diagram-UQ7AKVKN-zbipyz57.js → diagram-UQ7AKVKN-nhnd68tz.js} +1 -1
  208. package/static/dist/assets/{diagram-VSXAHHWV-d1kbpzgf.js → diagram-VSXAHHWV-dk2sclzn.js} +1 -1
  209. package/static/dist/assets/{diagram-VX7I27RA-i3ty14xl.js → diagram-VX7I27RA-n3k5nyx3.js} +1 -1
  210. package/static/dist/assets/{diagram-Z3DM3KII-m8xefqa7.js → diagram-Z3DM3KII-m6q9cwdh.js} +1 -1
  211. package/static/dist/assets/{ebnfDiagram-PWID7BFC-nz7wj05l.js → ebnfDiagram-PWID7BFC-lsa5641p.js} +1 -1
  212. package/static/dist/assets/endpoints-d33siokx.js +64 -0
  213. package/static/dist/assets/{erDiagram-RLTQ6QDP-fa41ib83.js → erDiagram-RLTQ6QDP-fo2ckbe6.js} +1 -1
  214. package/static/dist/assets/eventmodeling-NTZA5JFV-emctlo64.js +1 -0
  215. package/static/dist/assets/flowDiagram-HODETNUW-bfi3moqa.js +1 -0
  216. package/static/dist/assets/{ganttDiagram-EL5Y4UJY-n7zq01qu.js → ganttDiagram-EL5Y4UJY-nzqz6x9g.js} +1 -1
  217. package/static/dist/assets/{gitGraph-4MIJSDKK-c89emb07.js → gitGraph-4MIJSDKK-gukvcxe6.js} +1 -1
  218. package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-cz7cgr6w.js → gitGraphDiagram-WWUBYQGX-lqhdp8f0.js} +1 -1
  219. package/static/dist/assets/{highlighted-body-KPVGNVTW-d9bapmni.js → highlighted-body-KPVGNVTW-igw6cp39.js} +1 -1
  220. package/static/dist/assets/index-cxq58xb1.js +11 -0
  221. package/static/dist/assets/index-hgtx08n5.css +2 -0
  222. package/static/dist/assets/{info-A6RAGUB7-ndddhhhh.js → info-A6RAGUB7-gvu7a93l.js} +1 -1
  223. package/static/dist/assets/{infoDiagram-27XIBGKW-ccxigcpf.js → infoDiagram-27XIBGKW-m8t8g6w1.js} +1 -1
  224. package/static/dist/assets/{ishikawaDiagram-5VMMS53U-le0z9vtc.js → ishikawaDiagram-5VMMS53U-l07vkbgj.js} +1 -1
  225. package/static/dist/assets/{journeyDiagram-3NMN7TZE-ivsmb26o.js → journeyDiagram-3NMN7TZE-hybkgj8l.js} +1 -1
  226. package/static/dist/assets/{kanban-definition-UXKFOSKX-h6n2awt0.js → kanban-definition-UXKFOSKX-by13uvtn.js} +1 -1
  227. package/static/dist/assets/lazyRouteComponent-af0lvgjx.js +1 -0
  228. package/static/dist/assets/{line-dmzlfe0u.js → line-osxq6fq7.js} +1 -1
  229. package/static/dist/assets/link-jyxicdhg.js +1 -0
  230. package/static/dist/assets/{login-kjhasvp8.js → login-ec21t39x.js} +1 -1
  231. package/static/dist/assets/mermaid-HWGCJPDP-gfl8z3ep.js +1 -0
  232. package/static/dist/assets/{mermaid-parser.core-gwyd5bnq.js → mermaid-parser.core-i91oxmou.js} +3 -3
  233. package/static/dist/assets/{mindmap-definition-YA3MSWOX-l68gg5h7.js → mindmap-definition-YA3MSWOX-m82mx8ww.js} +1 -1
  234. package/static/dist/assets/onboarding-btnltvyz.js +1 -0
  235. package/static/dist/assets/onboarding_model_label-dp7xffoh.js +1 -0
  236. package/static/dist/assets/{packet-AYTQ26CC-gpv06065.js → packet-AYTQ26CC-nbtgge2o.js} +1 -1
  237. package/static/dist/assets/{pegDiagram-XKGWAZYB-e52vs23u.js → pegDiagram-XKGWAZYB-f1zxzga1.js} +1 -1
  238. package/static/dist/assets/{pie-WAS4IAKB-l6m7nz5s.js → pie-WAS4IAKB-of8vuhxs.js} +1 -1
  239. package/static/dist/assets/{pieDiagram-E7YTZNPT-b4is36bq.js → pieDiagram-E7YTZNPT-o7y8ftio.js} +1 -1
  240. package/static/dist/assets/{quadrantDiagram-AXDQQJYC-m8vt2788.js → quadrantDiagram-AXDQQJYC-iqiwia1t.js} +1 -1
  241. package/static/dist/assets/{radar-RG4KPBEZ-nxbrhiov.js → radar-RG4KPBEZ-delnsy2v.js} +1 -1
  242. package/static/dist/assets/{railroad-74A4TZTK-222mu9hi.js → railroad-74A4TZTK-nspnyq9u.js} +1 -1
  243. package/static/dist/assets/railroad-abnf-HS5TGJTU-nst1qxyn.js +1 -0
  244. package/static/dist/assets/railroad-ebnf-LZEXJU2U-ci37x8qy.js +1 -0
  245. package/static/dist/assets/railroad-peg-WCYAUIDC-aimkk07z.js +1 -0
  246. package/static/dist/assets/{railroadDiagram-O6MQD6OU-o8lldfzv.js → railroadDiagram-O6MQD6OU-z41tqx5z.js} +1 -1
  247. package/static/dist/assets/refresh-b4uams1i.js +1 -0
  248. package/static/dist/assets/refresh-cw-nt1dtsx4.js +1 -0
  249. package/static/dist/assets/{requirementDiagram-BXWQKSXE-ovyq38kv.js → requirementDiagram-BXWQKSXE-bdvif194.js} +1 -1
  250. package/static/dist/assets/{sankeyDiagram-P5KCCOFB-ou7lznor.js → sankeyDiagram-P5KCCOFB-h73e0rol.js} +1 -1
  251. package/static/dist/assets/{saved-bazmcup9.js → saved-kg52csy7.js} +1 -1
  252. package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-ovckdzf2.js → sequenceDiagram-WJ2MYXX4-iv95mb1z.js} +1 -1
  253. package/static/dist/assets/{settings_section_extensions_title-ertwwhlx.js → settings_section_extensions_title-ej0iapxe.js} +1 -1
  254. package/static/dist/assets/share._token-f97e94r0.js +2 -0
  255. package/static/dist/assets/sliders-horizontal-fj18it47.js +1 -0
  256. package/static/dist/assets/sse-fwpqtzbg.js +1 -0
  257. package/static/dist/assets/{stateDiagram-D77RDMKH-fcrexkkv.js → stateDiagram-D77RDMKH-k417ixmf.js} +1 -1
  258. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-hwn4apct.js +1 -0
  259. package/static/dist/assets/{swimlanes-42K2YHIH-f7usu8fw.js → swimlanes-42K2YHIH-esola70w.js} +1 -1
  260. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-d1h97oyg.js +8 -0
  261. package/static/dist/assets/{timeline-definition-24CTP7MA-gp1hvz9f.js → timeline-definition-24CTP7MA-getrz2y8.js} +1 -1
  262. package/static/dist/assets/todoStore-eginf9h4.js +1 -0
  263. package/static/dist/assets/{treeView-Q6P3EWNA-gb9gf11u.js → treeView-Q6P3EWNA-hc7ap1q7.js} +1 -1
  264. package/static/dist/assets/{treemap-WGGIJYW6-nuvfnvvi.js → treemap-WGGIJYW6-b7k1syy5.js} +1 -1
  265. package/static/dist/assets/tts-lxdxnmjd.js +1 -0
  266. package/static/dist/assets/{untitled-htelhc68.js → untitled-ovpfthg4.js} +1 -1
  267. package/static/dist/assets/{useForm-h2t0u14e.js → useForm-bcblv1rx.js} +1 -1
  268. package/static/dist/assets/useLocale-fi5jb8ma.js +1 -0
  269. package/static/dist/assets/{useParams-dqd64jv7.js → useParams-lhg566ew.js} +1 -1
  270. package/static/dist/assets/{useSearch-j30pvsy7.js → useSearch-lbrh6ejn.js} +1 -1
  271. package/static/dist/assets/{vennDiagram-4TSXK5OY-fzchbzaj.js → vennDiagram-4TSXK5OY-n6pcy5qb.js} +1 -1
  272. package/static/dist/assets/{wardley-WFR3VGLG-m47mhfbh.js → wardley-WFR3VGLG-lpa18vz1.js} +1 -1
  273. package/static/dist/assets/{wardleyDiagram-VM6X3IG4-odg6ct1z.js → wardleyDiagram-VM6X3IG4-kpclwwg7.js} +1 -1
  274. package/static/dist/assets/{xychartDiagram-S5SC5T6Z-mrhms5um.js → xychartDiagram-S5SC5T6Z-paa2n8y0.js} +1 -1
  275. package/static/dist/index.html +1 -1
  276. package/static/dist/sw.js +2 -2
  277. package/static/dist/assets/AppShell-ef3bhu32.js +0 -1
  278. package/static/dist/assets/Button-nx37f4u1.js +0 -1
  279. package/static/dist/assets/ChatPage-cdcpykcg.js +0 -25
  280. package/static/dist/assets/Dialog-mec42hvb.js +0 -1
  281. package/static/dist/assets/HubRoute-ecb65zpr.js +0 -2
  282. package/static/dist/assets/RightPanel-d1lgvk4s.js +0 -1
  283. package/static/dist/assets/Select-h4ivl9qf.js +0 -1
  284. package/static/dist/assets/SessionListPanel-ewib046b.js +0 -1
  285. package/static/dist/assets/SettingsLayout-lx654cup.js +0 -1
  286. package/static/dist/assets/States-ehsb6lzo.js +0 -1
  287. package/static/dist/assets/Toaster-mch1pa3t.js +0 -1
  288. package/static/dist/assets/_app-m8d961ef.js +0 -1
  289. package/static/dist/assets/_app.ext._extensionId-mp33hbzi.js +0 -1
  290. package/static/dist/assets/_app.index-dk6zp7e0.js +0 -1
  291. package/static/dist/assets/_app.insights-jt2fk4i1.js +0 -1
  292. package/static/dist/assets/_app.kanban-if4pubeu.js +0 -1
  293. package/static/dist/assets/_app.logs-mfaw9m6g.js +0 -1
  294. package/static/dist/assets/_app.memory-h3eaarb6.js +0 -1
  295. package/static/dist/assets/_app.profiles-ienktovs.js +0 -1
  296. package/static/dist/assets/_app.settings-dpoe0bzf.js +0 -1
  297. package/static/dist/assets/_app.settings._section-l468ynag.js +0 -3
  298. package/static/dist/assets/_app.skills-kcbncc8i.js +0 -1
  299. package/static/dist/assets/_app.tasks-occdrqhw.js +0 -2
  300. package/static/dist/assets/_app.todos-nnke2rcc.js +0 -1
  301. package/static/dist/assets/_app.workspaces-cdlatvwj.js +0 -1
  302. package/static/dist/assets/architecture-7GRP2DOG-hj11333z.js +0 -1
  303. package/static/dist/assets/channel-mbvmf2vv.js +0 -1
  304. package/static/dist/assets/classDiagram-ZZMXUADV-o8r05k0n.js +0 -1
  305. package/static/dist/assets/classDiagram-v2-VYDZK3BY-o8r05k0n.js +0 -1
  306. package/static/dist/assets/copy-e6977uwq.js +0 -1
  307. package/static/dist/assets/endpoints-k7yf25m0.js +0 -64
  308. package/static/dist/assets/eventmodeling-NTZA5JFV-kpbq09bx.js +0 -1
  309. package/static/dist/assets/flowDiagram-HODETNUW-lcz0kx14.js +0 -1
  310. package/static/dist/assets/index-h00usru2.js +0 -11
  311. package/static/dist/assets/index-n73ijkv2.css +0 -2
  312. package/static/dist/assets/lazyRouteComponent-m1rtyk4t.js +0 -1
  313. package/static/dist/assets/link-budeg5t9.js +0 -1
  314. package/static/dist/assets/matchContext-f8oy3lo7.js +0 -1
  315. package/static/dist/assets/mermaid-HWGCJPDP-jb5d211j.js +0 -1
  316. package/static/dist/assets/onboarding-cdfrk0pj.js +0 -1
  317. package/static/dist/assets/railroad-abnf-HS5TGJTU-kdvgl6x8.js +0 -1
  318. package/static/dist/assets/railroad-ebnf-LZEXJU2U-itiqslby.js +0 -1
  319. package/static/dist/assets/railroad-peg-WCYAUIDC-go7kov78.js +0 -1
  320. package/static/dist/assets/refresh-cw-kbcje1uy.js +0 -1
  321. package/static/dist/assets/search-jen74z4u.js +0 -1
  322. package/static/dist/assets/share._token-dan9hgvj.js +0 -2
  323. package/static/dist/assets/sliders-horizontal-f9cb3lh3.js +0 -1
  324. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-lcfraua2.js +0 -1
  325. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-ogmanni0.js +0 -8
  326. package/static/dist/assets/todoStore-eqtid4r4.js +0 -1
  327. package/static/dist/assets/useLocale-hcsb84pj.js +0 -1
  328. package/static/dist/assets/useNavigate-nwy70ztj.js +0 -1
@@ -12,29 +12,52 @@ import { isWithin, resolvePathLikePython } from '../workspace/paths.js';
12
12
  import { SidecarError } from '../sidecar/client.js';
13
13
  import { HttpFailure, markSessionTitleGenerated } from './service.js';
14
14
  import { StreamRegistry, SessionChannels } from './streams.js';
15
+ import { ChatUsageSchema } from '@maudecode/talaria-web-contracts';
15
16
  import { PendingPrompts, clarifyReply } from './pending.js';
16
17
  import { RunJournal } from './journal.js';
17
- import { CONTEXT_USAGE_FIELDS, Session, titleFrom } from './session.js';
18
- import { buildActiveTurnToken, completedToolIndex, publicToolFrame, redactSessionData, redactString, withToolId } from '../redact.js';
18
+ import { CONTEXT_USAGE_FIELDS, Session, contextUsage, titleFrom } from './session.js';
19
+ import { anchorMessageKey, compressionReference, markerSummary, visibleMessagesForAnchor } from './compress.js';
20
+ import { buildActiveTurnToken, completedToolIndex, publicToolFrame, redactNestedMessageContainers, redactSessionData, redactString, withToolId } from '../redact.js';
19
21
  import { dict } from '../config/agent-config.js';
20
22
  import { ReasoningTitleTracker, reasoningEventPayload } from './reasoning-titles.js';
21
23
  import { messageWindowForDisplay, messagesForLimitedPayload, toolCallsForMessageWindow } from './window.js';
22
- import { attachTodoState } from './todo.js';
24
+ import { UNSETTLED_TODO_KEY, attachTodoState, parseTodoToolResult } from './todo.js';
23
25
  import { withSessionWireFlags } from './list.js';
24
26
  import { hydrateAnchorActivityScenes, turnTerminalState, withTurnIds } from './anchor.js';
25
27
  import { persistentStateChanges, persistentStateSnapshot } from './state-saved.js';
26
28
  import { maxIterationsFromConfig, maxTokensFromConfig, processWakeupMaxIterations, reasoningConfigFromConfig, webuiEphemeralSystemPrompt, workspaceSystemMessage } from './turn-context.js';
27
- import { agentSteerText, assistantReplyAddedAfterCurrentTurn, buildPartialMessage, escapeWorkspacePrefixPath, checkpointTurnStart, extractToolCallsFromMessages, injectMaxIterationSummaryFallback, isDict, mergeDisplayMessagesAfterAgentResult, messageIdentity, messageText, pendingUserRow, sanitizeMessagesForApi, sessionLacksFinalAssistantAnswer, splitDisplayText, joinReasoning, reasoningFieldsText, stoppedTurnContext, toolOutcome, withAttachmentObjects, withBodyExcerpts, withDisplayMedia, withMarkerKinds, withToolCallOutcomes, workspaceContextPrefix } from './merge.js';
29
+ import { agentSteerText, assistantReplyAddedAfterCurrentTurn, attachedFilesPrompt, buildPartialMessage, dedupeContext, checkpointTurnStart, extractToolCallsFromMessages, hasNativeImages, injectMaxIterationSummaryFallback, isDict, mergeDisplayMessagesAfterAgentResult, messageIdentity, messageText, pendingUserRow, sanitizeMessagesForApi, sessionLacksFinalAssistantAnswer, splitDisplayText, stateDbSeenId, joinReasoning, reasoningFieldsText, stoppedTurnContext, toolOutcome, withAttachmentObjects, withBodyExcerpts, withDisplayMedia, withMarkerKinds, withToolCallOutcomes, workspaceContextPrefix, withoutMaxIterationSummaryRequest, withNativeImagesRestored, withoutNativeImages, withoutToolImages } from './merge.js';
28
30
  import { mayBecomeSilentReply, turnOrigin, withBackgroundUpdates } from './background-updates.js';
29
31
  import { fallbackTitleFromExchange, firstExchangeSnippets, isGenericFallbackTitle, latestExchangeSnippets, looksInvalidGeneratedTitle, looksLikeDefaultCliTitle, sanitizeGeneratedTitle, titleLanguageMismatch, titlePrompts } from './titles.js';
30
32
  import { WORKSPACE_BUSY_MESSAGE } from '../workspace/git.js';
31
33
  import { str } from '../util.js';
34
+ import { toolEditDiff } from './tool-display.js';
32
35
  export const CHAT_LOCK_WAIT_SECONDS = 2;
33
36
  const IMAGE_MODE_TIMEOUT_MS = 15_000;
34
37
  const TERMINAL_SSE_VISIBLE_MESSAGE_LIMIT = 80;
38
+ /** TAL-397: streamed text reports the live meter at most this often (seconds); a counter change reports at once. */
39
+ const METERING_INTERVAL_S = 1;
35
40
  /** How long a user's message waits for a stopped background turn to unwind before its own turn is admitted. */
36
41
  const BACKGROUND_UNWIND_WAIT_MS = 30_000;
37
42
  const COMPRESSION_EXHAUSTED = { label: 'Context compression exhausted', type: 'compression_exhausted', hint: 'The conversation context is too large to compress safely. Start a new conversation or retry with a narrower task.' };
43
+ /** The Agent's session counters onto a session's: a zero or missing reading keeps the figure it already has. */
44
+ function applyAgentUsage(target, usage) {
45
+ if (usage.prompt_tokens > 0)
46
+ target.input_tokens = usage.prompt_tokens;
47
+ if (usage.completion_tokens > 0)
48
+ target.output_tokens = usage.completion_tokens;
49
+ if (usage.estimated_cost_usd !== null)
50
+ target.estimated_cost = usage.estimated_cost_usd;
51
+ if (usage.cache_read_tokens > 0)
52
+ target.cache_read_tokens = usage.cache_read_tokens;
53
+ if (usage.cache_write_tokens > 0)
54
+ target.cache_write_tokens = usage.cache_write_tokens;
55
+ }
56
+ /** TAL-397: the `metering` frame's counters, the same figures the session persists. */
57
+ const meteringUsage = (c) => ({
58
+ input_tokens: c.input_tokens || 0, output_tokens: c.output_tokens || 0, estimated_cost: typeof c.estimated_cost === 'number' ? c.estimated_cost : null,
59
+ cache_read_tokens: c.cache_read_tokens || 0, cache_write_tokens: c.cache_write_tokens || 0,
60
+ });
38
61
  /** `compressionExhausted`: the Agent flagged the turn itself, so its wording does not matter. */
39
62
  export function classifyProviderError(errStr, opts = {}) {
40
63
  const lower = errStr.toLowerCase();
@@ -137,7 +160,27 @@ export function isGenericContinuationIntent(text) {
137
160
  const parts = normalized.split(' ');
138
161
  return parts.length > 0 && parts.length <= 2 && parts.every((p) => GENERIC_CONTINUATION_INTENTS.has(p));
139
162
  }
163
+ /**
164
+ * Python `_aux_title_generation_enabled`: `auxiliary.title_generation.enabled` with the Agent's
165
+ * `is_truthy_value(default=True)` semantics. Missing/null enables; a string enables only as 1/true/yes/on.
166
+ */
167
+ export function titleGenerationEnabled(cfg) {
168
+ const value = dict(dict(cfg.auxiliary).title_generation).enabled;
169
+ if (value === undefined || value === null)
170
+ return true;
171
+ if (typeof value === 'string')
172
+ return ['1', 'true', 'yes', 'on'].includes(value.trim().toLowerCase());
173
+ // Python `bool()`: empty containers are false; any other number than zero (NaN included) is true.
174
+ if (Array.isArray(value))
175
+ return value.length > 0;
176
+ if (typeof value === 'object')
177
+ return Object.keys(value).length > 0;
178
+ if (typeof value === 'number')
179
+ return value !== 0;
180
+ return Boolean(value);
181
+ }
140
182
  /** Python `_explicit_text_signal`: `agent.image_input_mode: text` or a configured `auxiliary.vision` backend. */
183
+ const isImageAttachment = (att) => Boolean(str(att.path).trim()) && str(att.mime).trim().startsWith('image/');
141
184
  export function explicitTextSignal(cfg) {
142
185
  const agent = dict(cfg.agent);
143
186
  if (str(agent.image_input_mode ?? 'auto').trim().toLowerCase() === 'text')
@@ -162,6 +205,10 @@ export class TurnRunner {
162
205
  steerRewrites = new Map();
163
206
  deferredPendingSteer = new Map();
164
207
  lastCompletedTool = new Map();
208
+ /** TAL-397: each running turn's live counters, once the Agent reported any; an error or cancel persists them too. */
209
+ liveUsage = new Map();
210
+ /** TAL-397: each running turn's latest todo list, which an error or cancel keeps on the session. */
211
+ liveTodos = new Map();
165
212
  /** Streams whose run completed (`done` emitted) and only await title work; a late cancel is a no-op for these. */
166
213
  settledStreams = new Set();
167
214
  /**
@@ -309,13 +356,16 @@ export class TurnRunner {
309
356
  }
310
357
  const msgText = opts.msg;
311
358
  const previousMessages = structuredClone(s.messages);
312
- const previousContext = structuredClone(deps.service().modelContext(s));
313
- // Python `_sanitize_messages_for_api`: the model never sees display-only rows or a replayed cancelled prompt.
314
- const apiHistory = sanitizeMessagesForApi(previousContext);
359
+ // TAL-493: one state.db read builds the history and marks where this turn's own rows begin.
360
+ const startRead = deps.service().stateDbRead(s);
361
+ const previousContext = structuredClone(deps.service().modelContext(s, startRead.rows));
362
+ // Python `_sanitize_messages_for_agent`: the model never sees display-only rows or a replayed cancelled prompt, and
363
+ // the Agent gets back the exact bytes it sent (TAL-541).
364
+ const apiHistory = sanitizeMessagesForApi(previousContext, { preserveApiContent: true });
315
365
  const workspaceCtx = workspaceContextPrefix(opts.workspace);
316
366
  // Before the first await: a Stop can land at any point after admission.
317
367
  // An eager save already put this turn's prompt in the transcript; the Stop fallback appends it once itself.
318
- this.stopContexts.set(streamId, { previousContext: previousContext.filter((m) => m._turn_id !== streamId), historyLength: apiHistory.length, prompt: workspaceCtx + msgText, msgText, checkpointed: false });
368
+ this.stopContexts.set(streamId, { previousContext: previousContext.filter((m) => m._turn_id !== streamId), historyLength: apiHistory.length, prompt: workspaceCtx + msgText, msgText, checkpointed: false, stateDbStartId: startRead.ok ? stateDbSeenId(startRead.rows) ?? 0 : null });
319
369
  const activeTurnToken = buildActiveTurnToken(streamId, s.pending_started_at);
320
370
  const sidecar = deps.sidecar();
321
371
  const partialText = this.registry.partialText.get(streamId) ?? [];
@@ -329,6 +379,25 @@ export class TurnRunner {
329
379
  const toolStartedAt = new WeakMap();
330
380
  let tokenSent = false;
331
381
  let firstTokenAt = null;
382
+ // TAL-397: the live meter. Counters start from the session's and follow the Agent's; the rate counts text deltas.
383
+ const liveUsage = { input_tokens: s.input_tokens, output_tokens: s.output_tokens, estimated_cost: s.estimated_cost, cache_read_tokens: s.cache_read_tokens, cache_write_tokens: s.cache_write_tokens };
384
+ const meter = { deltas: 0, first: 0, last: 0, sentAt: null };
385
+ const putMetering = (force) => {
386
+ const now = deps.now();
387
+ if (!force && meter.sentAt !== null && now - meter.sentAt < METERING_INTERVAL_S)
388
+ return;
389
+ meter.sentAt = now;
390
+ const tps = meter.deltas && meter.last > meter.first ? Math.round((meter.deltas / (meter.last - meter.first)) * 10) / 10 : null;
391
+ put('metering', { session_id: sessionId, usage: meteringUsage(liveUsage), tps, tps_available: tps !== null, estimated: false });
392
+ };
393
+ const meterDelta = () => {
394
+ const now = deps.now();
395
+ if (!meter.deltas)
396
+ meter.first = now;
397
+ meter.last = now;
398
+ meter.deltas += 1;
399
+ putMetering(false);
400
+ };
332
401
  // TAL-460: a background turn's text is held back while it could still be a silence marker, so one never flashes by.
333
402
  // ponytail: checks the turn's whole text, so a marker after earlier prose streams until the settled row hides it;
334
403
  // check the last text segment instead if that shows up.
@@ -338,7 +407,12 @@ export class TurnRunner {
338
407
  let capturedTerminalError = null;
339
408
  const controller = new AbortController();
340
409
  this.abortControllers.set(streamId, controller);
341
- const userMessage = await this.buildUserMessage(workspaceCtx, msgText, opts.attachments ?? [], opts.workspace, sessionId, s, opts, controller.signal);
410
+ const attachments = opts.attachments ?? [];
411
+ // TAL-545: one image mode decides both this turn's upload and the replayed history, which a text-mode turn sends
412
+ // without native images; only a turn with an image on either side asks for it.
413
+ const imageMode = attachments.some(isImageAttachment) || hasNativeImages(apiHistory) ? await this.imageInputMode(sessionId, s, opts, controller.signal) : null;
414
+ const userMessage = this.buildUserMessage(workspaceCtx, msgText, attachments, opts.workspace, sessionId, imageMode);
415
+ const conversationHistory = imageMode === 'text' ? withoutNativeImages(apiHistory) : apiHistory;
342
416
  // The prompt the Agent actually gets, native image parts included.
343
417
  const stop = this.stopContexts.get(streamId);
344
418
  if (stop)
@@ -382,7 +456,7 @@ export class TurnRunner {
382
456
  put('context_status', { session_id: sessionId, prefill: { status: 'not_configured', source: 'none', label: '', message_count: 0 } });
383
457
  const result = await sidecar.call('chat.start', {
384
458
  profile_home: deps.profileHome(s.profile), session_id: sessionId, stream_id: streamId, workspace: opts.workspace, model: opts.model ?? '', model_provider: opts.modelProvider,
385
- user_message: userMessage, ...turnContext, conversation_history: apiHistory, enabled_toolsets: deps.toolsetsFor(s),
459
+ user_message: userMessage, ...turnContext, conversation_history: conversationHistory, enabled_toolsets: deps.toolsetsFor(s), ...(msgText ? { persist_user_message: msgText } : {}), ...(opts.ephemeral ? { ephemeral: true } : {}),
386
460
  }, {
387
461
  signal: controller.signal,
388
462
  timeoutMs: 0,
@@ -399,10 +473,12 @@ export class TurnRunner {
399
473
  partialText.push(str(data.text));
400
474
  if (holdSilence && mayBecomeSilentReply(partialText.join(''))) {
401
475
  heldText += str(data.text);
476
+ meterDelta();
402
477
  return;
403
478
  }
404
479
  put('token', { text: heldText + str(data.text) });
405
480
  heldText = '';
481
+ meterDelta();
406
482
  return;
407
483
  }
408
484
  case 'reasoning': {
@@ -411,6 +487,16 @@ export class TurnRunner {
411
487
  reasoningText.push(str(data.text));
412
488
  // Python `reasoning_event_payload`: bold or first-line titles ride on the delta they complete.
413
489
  put('reasoning', titles.prepare(reasoningEventPayload(str(data.text), reasoningText.join(''))));
490
+ meterDelta();
491
+ return;
492
+ }
493
+ case 'usage': {
494
+ const usage = ChatUsageSchema.safeParse(data);
495
+ if (usage.success) {
496
+ applyAgentUsage(liveUsage, usage.data);
497
+ this.liveUsage.set(streamId, liveUsage);
498
+ }
499
+ putMetering(true);
414
500
  return;
415
501
  }
416
502
  case 'steer_pending':
@@ -433,23 +519,34 @@ export class TurnRunner {
433
519
  case 'tool_complete': {
434
520
  // TAL-313: the server decides failure from the sidecar's raw result, which never leaves the server; TAL-315: so
435
521
  // are its display sections, which keep the stderr and exit code the flat preview drops.
436
- const { raw_result: rawResult, ...complete } = data;
522
+ const { raw_result: rawResult, todo_result: todoResult, result_diff: resultDiff, ...complete } = data;
437
523
  const outcome = toolOutcome(rawResult);
438
524
  complete.is_error = outcome.is_error;
439
525
  if (rawResult !== undefined)
440
526
  complete.result_view = outcome.result_view;
527
+ // TAL-448: a file edit's change, counted over the whole diff the sidecar forwards; redacted with the frame.
528
+ const editDiff = toolEditDiff(data.name, { diff: resultDiff });
529
+ if (editDiff)
530
+ complete.edit_diff = editDiff;
441
531
  const tc = liveToolCalls[completedToolIndex(liveToolCalls, str(data.tid), data.name)];
442
532
  if (tc) {
443
533
  const startedAt = toolStartedAt.get(tc);
444
534
  if (startedAt !== undefined)
445
535
  complete.duration = Math.round(Math.max(0, deps.now() - startedAt) * 1000) / 1000;
446
536
  // TAL-315: the decided view too, so a failed or cancelled turn's snapshot keeps the sections shown live.
447
- Object.assign(tc, { done: true, snippet: data.preview, is_error: complete.is_error, duration: complete.duration ?? null, ...(complete.result_view ? { result_view: complete.result_view } : {}) });
537
+ Object.assign(tc, { done: true, snippet: data.preview, is_error: complete.is_error, duration: complete.duration ?? null, ...(complete.result_view ? { result_view: complete.result_view } : {}), ...(editDiff ? { edit_diff: editDiff } : {}) });
448
538
  }
449
539
  const id = (tc && toolIds.get(tc)) || str(data.tid) || mintToolId();
450
540
  this.lastCompletedTool.set(streamId, id);
451
541
  const redacted = deps.redactEnabled();
452
542
  put('tool_complete', publicToolFrame(withToolId(complete, id), redacted), { redacted });
543
+ // TAL-397: the todo tool's result is the session's new list; the settled session derives the same one.
544
+ const todos = data.name === 'todo' ? parseTodoToolResult(todoResult) : null;
545
+ if (todos) {
546
+ todos.ts = deps.now();
547
+ this.liveTodos.set(streamId, todos);
548
+ put('todo_state', redactNestedMessageContainers({ ...todos, session_id: sessionId, stream_id: streamId, source: 'live' }, redacted));
549
+ }
453
550
  return;
454
551
  }
455
552
  // Python: the live chat frame carries the queue head plus depth, not the entry that just arrived.
@@ -490,11 +587,18 @@ export class TurnRunner {
490
587
  },
491
588
  });
492
589
  settledAt.value = true;
590
+ // TAL-545: every transcript the Agent hands back settles with the history's images, not the request's projection.
591
+ const withImages = (rows) => withNativeImagesRestored(rows, conversationHistory, apiHistory);
592
+ result.messages = withImages(result.messages);
593
+ if (result.context_messages)
594
+ result.context_messages = withImages(result.context_messages);
493
595
  if (this.registry.cancelled.has(streamId) || result.status === 'cancelled') {
494
596
  // The Stop's pre-interrupt snapshot is the boundary, even when its reply lands after this result (the sidecar
495
597
  // answers each request on its own thread); without one, the Agent's interrupted result is its canonical transcript.
496
598
  const interrupted = await this.interrupts.get(streamId);
497
- this.finalizeCancelled(s, streamId, opts.ephemeral, interrupted?.checkpoint ?? result.messages);
599
+ applyAgentUsage(liveUsage, result.usage);
600
+ this.liveUsage.set(streamId, liveUsage);
601
+ this.finalizeCancelled(s, streamId, opts.ephemeral, interrupted?.checkpoint ? withImages(interrupted.checkpoint) : result.messages);
498
602
  put('cancel', this.cancelFrame(sessionId));
499
603
  return;
500
604
  }
@@ -513,7 +617,10 @@ export class TurnRunner {
513
617
  s = current;
514
618
  // Python `_maybe_inject_max_iteration_summary_fallback`: an exhausted tool budget leaves the closing
515
619
  // explanation in `final_response` only, so it becomes the turn's assistant answer before anything else reads it.
516
- const resultMessages = result.tool_limit_reached ? injectMaxIterationSummaryFallback(result.messages, result.final_response) : result.messages;
620
+ const withFallback = (rows) => (result.tool_limit_reached ? injectMaxIterationSummaryFallback(rows, result.final_response) : rows);
621
+ const withoutRequest = (rows) => (result.tool_limit_reached ? withoutMaxIterationSummaryRequest(rows, result.max_iterations_summary_request, msgText) : rows);
622
+ const agentRows = withFallback(result.messages);
623
+ const resultMessages = withoutRequest(agentRows);
517
624
  // Python `_assistant_reply_added_after_current_turn`: replayed history never counts as this turn's answer (the
518
625
  // sidecar reports `completed` whenever a failed run still carries messages).
519
626
  // Python's second chance: a turn that emitted no new row still counts when the merged transcript it produced
@@ -534,7 +641,7 @@ export class TurnRunner {
534
641
  await this.steerRewrites.get(streamId);
535
642
  const { events: steerEvents, leftovers } = this.finalizeSteers(streamId, str(result.pending_steer), 'followup');
536
643
  followUp = leftovers;
537
- this.persistError(s, streamId, classification.label, payload, activeTurnToken);
644
+ this.persistError(s, streamId, classification.label, payload, activeTurnToken, resultMessages);
538
645
  // TAL-512: a btw error carries no session: its journaled frame must not keep a copy of the parent conversation.
539
646
  if (!opts.ephemeral)
540
647
  payload.session = redactSessionData(this.terminalSessionPayload(s), deps.redactEnabled());
@@ -549,8 +656,8 @@ export class TurnRunner {
549
656
  // TAL-512: a btw turn passes the failure check above first, so its last assistant row is this turn's reply.
550
657
  if (opts.ephemeral) {
551
658
  let answer = '';
552
- for (let i = result.messages.length - 1; i >= 0; i -= 1) {
553
- const m = result.messages[i];
659
+ for (let i = resultMessages.length - 1; i >= 0; i -= 1) {
660
+ const m = resultMessages[i];
554
661
  if (m.role === 'assistant') {
555
662
  answer = str(m.content);
556
663
  break;
@@ -558,7 +665,7 @@ export class TurnRunner {
558
665
  }
559
666
  opts.onDone?.(answer);
560
667
  // Python `_ephemeral_session_payload`: only role and content leave the server for a btw turn.
561
- put('done', { session: { session_id: sessionId, messages: (result.messages).map((m) => ({ role: m.role, content: m.content })) }, usage: { input_tokens: 0, output_tokens: 0 }, ephemeral: true, answer, terminal_state: answer.trim() ? 'completed' : 'no_response' });
668
+ put('done', { session: { session_id: sessionId, messages: resultMessages.map((m) => ({ role: m.role, content: m.content })) }, usage: { input_tokens: 0, output_tokens: 0 }, ephemeral: true, answer, terminal_state: answer.trim() ? 'completed' : 'no_response' });
562
669
  try {
563
670
  rmSync(deps.store.pathFor(sessionId), { force: true });
564
671
  }
@@ -567,14 +674,36 @@ export class TurnRunner {
567
674
  return;
568
675
  }
569
676
  // ── settle the transcript ──
677
+ // TAL-539: after a mid-turn compression the model context is the sidecar's pruned copy, settled like the transcript.
678
+ const pruned = result.compressed ? result.context_messages ?? null : null;
679
+ const context = withoutToolImages(pruned ? withoutRequest(withFallback(pruned)) : resultMessages);
680
+ let estimate = result.compressed ? result.post_compression_context_tokens_estimate ?? null : null;
681
+ // The sidecar estimated its own rows; a tool-limit settlement or stripped screenshots (TAL-544) that changed them move
682
+ // the estimate by the difference.
683
+ if (pruned && estimate !== null && context !== pruned) {
684
+ try {
685
+ const [before, after] = await Promise.all([pruned, context].map((messages) => sidecar.call('models.estimate_tokens', { messages })));
686
+ estimate = Math.max(1, estimate + after.tokens - before.tokens);
687
+ }
688
+ catch {
689
+ estimate = null;
690
+ }
691
+ }
570
692
  // The Agent's last pending-steer text settles the remaining steers before the turn is written back.
571
693
  await this.steerRewrites.get(streamId);
572
694
  const { events: steerEvents, leftovers } = this.finalizeSteers(streamId, result.pending_steer, 'followup');
573
- s.messages = mergeDisplayMessagesAfterAgentResult(previousMessages, previousContext, resultMessages, msgText, { source: opts.source ?? 'webui', activeTurnToken, now: deps.now(), turnId: streamId, attachments: opts.attachments ?? [] });
574
- s.context_messages = dedupeContext(resultMessages);
695
+ s.messages = withoutToolImages(mergeDisplayMessagesAfterAgentResult(previousMessages, previousContext, resultMessages, msgText, { source: opts.source ?? 'webui', activeTurnToken, now: deps.now(), turnId: streamId, attachments: opts.attachments ?? [] }));
696
+ s.context_messages = dedupeContext(context);
575
697
  if (result.compressed) {
576
- s.compression_anchor_visible_idx = Math.max(0, previousMessages.length - 1);
577
- put('compressed', { session_id: sessionId, old_session_id: sessionId, new_session_id: sessionId, continuation_session_id: sessionId, message: 'Compression finished' });
698
+ s.post_compression_context_tokens_estimate = estimate;
699
+ // TAL-540: the anchor is the last visible row before this turn (never its eager checkpoint); the merged transcript
700
+ // holds no compaction marker, so the summary is the model context's newest one.
701
+ const before = visibleMessagesForAnchor(previousMessages.filter((m) => m._turn_id !== streamId));
702
+ const after = visibleMessagesForAnchor(s.messages);
703
+ const anchorIdx = before.length ? before.length - 1 : after.length ? 0 : null;
704
+ s.compression_anchor_visible_idx = anchorIdx;
705
+ s.compression_anchor_message_key = anchorMessageKey(anchorIdx !== null && anchorIdx < after.length ? after[anchorIdx] : after.at(-1));
706
+ s.compression_anchor_summary = markerSummary(s.context_messages);
578
707
  }
579
708
  const now = deps.now();
580
709
  for (const m of s.messages)
@@ -584,22 +713,21 @@ export class TurnRunner {
584
713
  const usage = result.usage;
585
714
  const prevInputTokens = s.input_tokens || 0;
586
715
  const prevCacheReadTokens = s.cache_read_tokens || 0;
587
- if (usage.prompt_tokens > 0)
588
- s.input_tokens = usage.prompt_tokens;
589
- if (usage.completion_tokens > 0)
590
- s.output_tokens = usage.completion_tokens;
591
- if (usage.estimated_cost_usd !== null)
592
- s.estimated_cost = usage.estimated_cost_usd;
593
- if (usage.cache_read_tokens > 0)
594
- s.cache_read_tokens = usage.cache_read_tokens;
595
- if (usage.cache_write_tokens > 0)
596
- s.cache_write_tokens = usage.cache_write_tokens;
716
+ applyAgentUsage(s, usage);
597
717
  if (typeof result.context.context_length === 'number')
598
718
  s.context_length = result.context.context_length;
599
719
  if (typeof result.context.threshold_tokens === 'number')
600
720
  s.threshold_tokens = result.context.threshold_tokens;
601
721
  if (typeof result.context.last_prompt_tokens === 'number')
602
722
  s.last_prompt_tokens = result.context.last_prompt_tokens;
723
+ if (result.compressed) {
724
+ // TAL-540, Python `_live_usage_snapshot`: the counters and the ring's post-compression figures, so clients update before `done`.
725
+ const usage = {
726
+ ...meteringUsage(s), context_length: s.context_length ?? 0, threshold_tokens: s.threshold_tokens ?? 0, last_prompt_tokens: s.last_prompt_tokens ?? 0,
727
+ post_compression_context_tokens_estimate: s.post_compression_context_tokens_estimate, ...contextUsage(s, () => deps.service().deps.contextLengthFor(s.model, s.model_provider)),
728
+ };
729
+ put('compressed', { session_id: sessionId, old_session_id: sessionId, new_session_id: sessionId, continuation_session_id: sessionId, message: 'Compression finished', usage });
730
+ }
603
731
  s.tool_calls = extractToolCallsFromMessages(s.messages, liveToolCalls, s.tool_calls);
604
732
  s.active_stream_id = null;
605
733
  s.pending_user_message = null;
@@ -656,6 +784,9 @@ export class TurnRunner {
656
784
  }
657
785
  }
658
786
  this.persistConsumedSteers(s, streamId, previousStartedAt(s, activeRun), now);
787
+ // TAL-493: the rows this turn's Agent wrote to state.db are now in the transcript, so the merge must not replay them
788
+ // (all of them, the dropped summary request too).
789
+ deps.service().settleStateDb(s, { turnId: streamId, previousContext: this.stopContexts.get(streamId)?.previousContext ?? previousContext, agentRows, startId: this.stopContexts.get(streamId)?.stateDbStartId ?? null });
659
790
  deps.store.save(s);
660
791
  deps.pending.clearApprovals(sessionId);
661
792
  deps.pending.clearClarifies(sessionId);
@@ -700,6 +831,8 @@ export class TurnRunner {
700
831
  };
701
832
  if (result.tool_limit_reached)
702
833
  donePayload.terminal_reason = 'max_iterations';
834
+ // TAL-397: the live meter ends on the persisted counters and turn rate.
835
+ put('metering', { session_id: sessionId, usage: meteringUsage(s), tps: doneUsage.tps ?? null, tps_available: doneUsage.tps !== undefined, estimated: false });
703
836
  put('done', donePayload);
704
837
  for (const [event, payload] of steerEvents)
705
838
  put(event, payload);
@@ -709,6 +842,8 @@ export class TurnRunner {
709
842
  this.registry.activeRuns.delete(streamId);
710
843
  this.settledStreams.add(streamId);
711
844
  this.startSteerFollowUp(sessionId, leftovers);
845
+ // Before the title work, so a generated title's own state.db sync lands after this one's provisional title.
846
+ await deps.syncUsage?.(s, usedModel || null);
712
847
  // Title work and the goal judge run side by side; `stream_end` follows both, so their frames reach the stream.
713
848
  const work = await Promise.allSettled([this.backgroundTitle(s, put), this.continueGoal(s, streamId, put)]);
714
849
  put('stream_end', { session_id: sessionId });
@@ -769,45 +904,49 @@ export class TurnRunner {
769
904
  }
770
905
  }
771
906
  /**
772
- * Python `_build_user_message`: image attachments are embedded as native `image_url` parts only when the Agent's
773
- * resolved image mode for this model is `native` (text mode routes them through the Agent's vision tool path), and
774
- * only after the bytes are read through an anchored descriptor and sniffed as a real image format. Every attached
775
- * file is also named by path after the text (TAL-276), so an attachment-only turn still gives the model a request
776
- * and its transcript row a distinct identity.
907
+ * Python `_resolve_image_input_mode`: the Agent's image mode for this turn's model under its profile config, or null
908
+ * when it is unknown (no sidecar, a cancel, a failed lookup). A canonical "text" is honoured only with an explicit
909
+ * text signal or a model KNOWN to be text-only; an unknown/custom model forwards natively and lets the Agent's retry
910
+ * guard downgrade.
777
911
  */
778
- async buildUserMessage(workspaceCtx, msgText, attachments, workspace, sessionId, s, opts, signal) {
779
- const text = workspaceCtx + msgText;
780
- const named = attachments.map((att) => str(att.path).trim()).filter(Boolean).map(escapeWorkspacePrefixPath);
781
- const withFiles = () => (named.length ? `${text}\n\n[Attached files: ${named.join(', ')}]` : text);
782
- const candidates = attachments.filter((att) => str(att.path).trim() && str(att.mime).trim().startsWith('image/'));
783
- if (!candidates.length)
784
- return withFiles();
912
+ async imageInputMode(sessionId, s, opts, signal) {
785
913
  const sidecar = this.deps.sidecar();
786
- if (!sidecar)
787
- return withFiles();
788
914
  // A cancel that landed before this point is final: never start the lookup or wait on it.
789
- if (signal.aborted)
790
- return text;
915
+ if (!sidecar || signal.aborted)
916
+ return null;
917
+ // The session's provider before the Agent canonicalizes it, so a `custom:<name>` override entry still matches.
918
+ const provider = str(opts.modelProvider ?? s.model_provider);
791
919
  try {
792
920
  // A cancelled turn must not sit behind this lookup: the abort wins the race and `run()` then takes the cancelled path.
793
- const lookup = sidecar.call('text.image_mode', { profile_home: this.deps.profileHome(s.profile), provider: str(opts.modelProvider ?? s.model_provider), model: str(opts.model ?? s.model) }, { signal, timeoutMs: IMAGE_MODE_TIMEOUT_MS });
921
+ const lookup = sidecar.call('text.image_mode', { profile_home: this.deps.profileHome(s.profile), provider, model: str(opts.model ?? s.model), requested_provider: provider }, { signal, timeoutMs: IMAGE_MODE_TIMEOUT_MS });
794
922
  const mode = await Promise.race([lookup, new Promise((_, reject) => { if (signal.aborted) {
795
923
  reject(new Error('turn cancelled'));
796
924
  return;
797
925
  } signal.addEventListener('abort', () => { reject(new Error('turn cancelled')); }, { once: true }); })]);
798
- // Python `_resolve_image_input_mode`: a canonical "text" is honoured only with an explicit text signal or a model
799
- // KNOWN to be text-only; an unknown/custom model forwards natively and lets the Agent's retry guard downgrade.
800
- if (mode.mode !== 'native') {
801
- const cfg = (await this.deps.profileConfig?.(s.profile ?? null)) ?? {};
802
- if (explicitTextSignal(cfg) || mode.supports_vision === false)
803
- return withFiles();
804
- }
926
+ if (mode.mode === 'native')
927
+ return 'native';
928
+ const cfg = (await this.deps.profileConfig?.(s.profile ?? null)) ?? {};
929
+ return explicitTextSignal(cfg) || mode.supports_vision === false ? 'text' : 'native';
805
930
  }
806
931
  catch (error) {
807
932
  if (!signal.aborted)
808
933
  this.deps.log(`[webui] image mode lookup failed for ${sessionId}: ${error.message}`);
809
- return withFiles();
934
+ return null;
810
935
  }
936
+ }
937
+ /**
938
+ * Python `_build_user_message`: image attachments are embedded as native `image_url` parts only when the turn's
939
+ * image mode is `native` (text mode routes them through the Agent's vision tool path), and only after the bytes are
940
+ * read through an anchored descriptor and sniffed as a real image format. Every attached file is also named by path
941
+ * after the text (TAL-276), so an attachment-only turn still gives the model a request and its transcript row a
942
+ * distinct identity.
943
+ */
944
+ buildUserMessage(workspaceCtx, msgText, attachments, workspace, sessionId, imageMode) {
945
+ const text = workspaceCtx + msgText;
946
+ const withFiles = () => attachedFilesPrompt(text, attachments);
947
+ const candidates = attachments.filter(isImageAttachment);
948
+ if (!candidates.length || imageMode !== 'native')
949
+ return withFiles();
811
950
  const parts = [];
812
951
  let images = 0;
813
952
  const roots = [workspace, this.deps.attachmentDir(sessionId)].map((r) => resolvePathLikePython(r));
@@ -858,12 +997,13 @@ export class TurnRunner {
858
997
  payload.message_count = s.messages.length;
859
998
  payload._messages_offset = offset;
860
999
  payload._messages_truncated = offset > 0;
861
- attachTodoState(payload, s.messages);
1000
+ attachTodoState(payload, s.messages, s.extra[UNSETTLED_TODO_KEY]);
862
1001
  payload.tool_calls = toolCallsForMessageWindow(s.tool_calls, offset, limited.length);
1002
+ payload.compression_reference = compressionReference(s, scened);
863
1003
  return payload;
864
1004
  }
865
1005
  /** Python `_materialize_pending_user_turn_before_error` + error message append + save. */
866
- persistError(s, streamId, label, payload, activeTurnToken) {
1006
+ persistError(s, streamId, label, payload, activeTurnToken, agentRows = []) {
867
1007
  const startedAt = s.pending_started_at;
868
1008
  this.materializePendingUserTurn(s, activeTurnToken, streamId);
869
1009
  const duration = typeof startedAt === 'number' && startedAt > 0 ? Math.max(0, this.deps.now() - startedAt) : null;
@@ -893,13 +1033,18 @@ export class TurnRunner {
893
1033
  else if (payload.type === 'interrupted')
894
1034
  errorMessage.provider_details_label = 'Interruption details';
895
1035
  s.messages.push(errorMessage);
1036
+ this.keepLiveState(s, streamId);
896
1037
  this.persistConsumedSteers(s, streamId, startedAt, this.deps.now());
1038
+ const stop = this.stopContexts.get(streamId);
1039
+ this.deps.service().settleStateDb(s, { turnId: streamId, previousContext: stop?.previousContext ?? [], agentRows, startId: stop?.stateDbStartId ?? null });
897
1040
  try {
898
1041
  this.deps.store.save(s);
899
1042
  }
900
1043
  catch (error) {
901
1044
  this.deps.log(`[webui] WARNING: failed to save error turn for ${s.session_id}: ${error.message}`);
902
1045
  }
1046
+ // The kept live counters are the session's usage now, so the insights row follows them (the runtime logs failures).
1047
+ void this.deps.syncUsage?.(s, str(s.model) || null);
903
1048
  this.deps.pending.clearApprovals(s.session_id);
904
1049
  this.deps.pending.clearClarifies(s.session_id);
905
1050
  this.deps.events.publish('session_error', { profile: s.profile, sessionId: s.session_id });
@@ -988,8 +1133,19 @@ export class TurnRunner {
988
1133
  // TAL-460: a background turn stops quietly: its prompt and model context are kept, but no partial and no Stop row,
989
1134
  // and its first settlement is final.
990
1135
  const quiet = this.registry.activeRuns.get(streamId)?.origin === 'background';
991
- if (quiet && current.active_stream_id === null)
1136
+ if (quiet && current.active_stream_id === null) {
1137
+ // TAL-493: rows its worker committed while unwinding are covered by what it reports; others are kept.
1138
+ if (checkpoint) {
1139
+ this.deps.service().settleStateDb(current, { turnId: streamId, previousContext: this.stopContexts.get(streamId)?.previousContext ?? [], agentRows: checkpoint, startId: this.stopContexts.get(streamId)?.stateDbStartId ?? null });
1140
+ try {
1141
+ this.deps.store.save(current);
1142
+ }
1143
+ catch {
1144
+ return false;
1145
+ }
1146
+ }
992
1147
  return true;
1148
+ }
993
1149
  const stop = this.stopContexts.get(streamId);
994
1150
  const canonical = stop !== undefined && checkpoint !== null && checkpointTurnStart(checkpoint, stop.msgText, stop.historyLength) !== null;
995
1151
  const settle = () => {
@@ -1003,9 +1159,13 @@ export class TurnRunner {
1003
1159
  // The worker's canonical result can arrive after cancel() settled without a checkpoint (the interrupt reply failed
1004
1160
  // or timed out): it replaces this stream's context and never adds a second Stop row. A settlement that already had
1005
1161
  // one keeps it, so work the Agent finished after the Stop stays out.
1006
- const late = canonical && !stop.checkpointed && current.messages.some((m) => isCancelMarker(m) && m._turn_id === streamId) ? settle() : null;
1007
- if (late) {
1162
+ const ownStop = current.messages.some((m) => isCancelMarker(m) && m._turn_id === streamId);
1163
+ const late = canonical && !stop.checkpointed && ownStop ? settle() : null;
1164
+ if (late)
1008
1165
  current.context_messages = dedupeContext(late);
1166
+ // TAL-493: the worker's rows committed after the first settlement are covered by what it reports; others are kept.
1167
+ if (late || (ownStop && checkpoint)) {
1168
+ this.deps.service().settleStateDb(current, { turnId: streamId, previousContext: stop?.previousContext ?? [], agentRows: checkpoint, startId: stop?.stateDbStartId ?? null });
1009
1169
  try {
1010
1170
  this.deps.store.save(current);
1011
1171
  }
@@ -1029,13 +1189,16 @@ export class TurnRunner {
1029
1189
  this.appendPartialSnapshot(current, streamId);
1030
1190
  current.messages.push({ role: 'assistant', content: '', _error: true, _terminal_state: 'cancelled', timestamp: Math.trunc(this.deps.now()), _turn_id: streamId });
1031
1191
  }
1192
+ this.keepLiveState(current, streamId);
1032
1193
  this.persistConsumedSteers(current, streamId, startedAt, this.deps.now());
1194
+ this.deps.service().settleStateDb(current, { turnId: streamId, previousContext: stop?.previousContext ?? [], agentRows: checkpoint, startId: stop?.stateDbStartId ?? null });
1033
1195
  try {
1034
1196
  this.deps.store.save(current);
1035
1197
  }
1036
1198
  catch {
1037
1199
  return false;
1038
1200
  }
1201
+ void this.deps.syncUsage?.(current, str(current.model) || null);
1039
1202
  this.deps.pending.clearApprovals(current.session_id);
1040
1203
  this.deps.pending.clearClarifies(current.session_id);
1041
1204
  this.deps.events.publish('session_cancel', { profile: current.profile, sessionId: current.session_id });
@@ -1045,7 +1208,18 @@ export class TurnRunner {
1045
1208
  emitToSession(sessionId, event, data) {
1046
1209
  this.sessionPuts.get(sessionId)?.(event, data);
1047
1210
  }
1211
+ /** TAL-397: a turn that ends without its result keeps what the live stream already showed: counters and todo list. */
1212
+ keepLiveState(s, streamId) {
1213
+ const usage = this.liveUsage.get(streamId);
1214
+ if (usage)
1215
+ Object.assign(s, usage);
1216
+ const todos = this.liveTodos.get(streamId);
1217
+ if (todos)
1218
+ s.extra[UNSETTLED_TODO_KEY] = todos;
1219
+ }
1048
1220
  teardown(sessionId, streamId) {
1221
+ this.liveUsage.delete(streamId);
1222
+ this.liveTodos.delete(streamId);
1049
1223
  const writer = this.writers.get(streamId);
1050
1224
  if (writer) {
1051
1225
  try {
@@ -1113,13 +1287,14 @@ export class TurnRunner {
1113
1287
  }
1114
1288
  return { title: '', status, rawPreview: '' };
1115
1289
  }
1116
- /** Python `generate_session_title_for_session`: on-demand title from the persisted transcript; never touches `llm_title_generated`. */
1290
+ /**
1291
+ * Python `generate_session_title_for_session`: on-demand title from the persisted transcript; never touches
1292
+ * `llm_title_generated`. An explicit regenerate runs even with `auxiliary.title_generation.enabled` off (TAL-531).
1293
+ */
1117
1294
  async generateTitle(s, opts = {}) {
1118
1295
  const [userText, assistantText] = opts.preferLatest ? latestExchangeSnippets(s.messages) : firstExchangeSnippets(s.messages, { scanPastConsecutiveUsers: true });
1119
1296
  if (!userText)
1120
1297
  return { title: null, status: 'empty_user_message', rawPreview: '' };
1121
- if (!this.deps.titleGenerationEnabled())
1122
- return { title: null, status: 'title_generation_disabled', rawPreview: '' };
1123
1298
  let next = '';
1124
1299
  let llmStatus = 'empty_assistant_message';
1125
1300
  let rawPreview = '';
@@ -1151,7 +1326,13 @@ export class TurnRunner {
1151
1326
  status('skipped', 'manual_title', placeholder);
1152
1327
  return;
1153
1328
  }
1154
- if (!this.deps.titleGenerationEnabled()) {
1329
+ // A missing config.yaml reads as `{}` (enabled); an unreadable one cannot confirm the user has not opted out.
1330
+ const cfg = await this.deps.profileConfig?.(s.profile ?? null);
1331
+ if (!cfg) {
1332
+ status('skipped', 'config_unavailable', placeholder);
1333
+ return;
1334
+ }
1335
+ if (!titleGenerationEnabled(cfg)) {
1155
1336
  status('skipped', 'title_generation_disabled', placeholder);
1156
1337
  return;
1157
1338
  }
@@ -1848,18 +2029,6 @@ function previousStartedAt(s, run) {
1848
2029
  function isCancelMarker(m) {
1849
2030
  return m._error === true && (m._terminal_state === 'cancelled' || str(m.content).startsWith('**Task cancelled:**'));
1850
2031
  }
1851
- function dedupeContext(messages) {
1852
- const out = [];
1853
- let lastKey = null;
1854
- for (const m of messages) {
1855
- const key = messageIdentity(m);
1856
- if (key !== null && key === lastKey && m.role === 'assistant')
1857
- continue;
1858
- out.push(m);
1859
- lastKey = key;
1860
- }
1861
- return out;
1862
- }
1863
2032
  export { HttpFailure };
1864
2033
  /** Where a consumed steer goes: past the tool results of the call it followed, else past the turn's prompt. */
1865
2034
  function roundDuration(seconds) {