@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,14 +12,17 @@ from __future__ import annotations
12
12
 
13
13
  import contextlib
14
14
  import contextvars
15
+ import copy
15
16
  import hashlib
16
17
  import inspect
17
18
  import json
18
19
  import logging
20
+ import os
19
21
  import threading
20
22
  import time
21
23
  import uuid
22
24
  from collections import OrderedDict
25
+ from pathlib import Path
23
26
  from typing import Any
24
27
 
25
28
  from ..errors import InvalidParams, RpcError
@@ -29,6 +32,10 @@ from ..rpc import CallContext
29
32
  log = logging.getLogger("talaria_sidecar.chat")
30
33
 
31
34
  _AGENT_CACHE_MAX = 32
35
+ # Shutdown waits this long for evicted agents' memory providers; the server kills the sidecar 5 s after closing stdin.
36
+ _AGENT_RELEASE_TIMEOUT = 4.0
37
+ # Profile deletion waits this long for the profile's memory flushes, under the server's 120 s call timeout.
38
+ _PROFILE_RELEASE_TIMEOUT = 60.0
32
39
  _TOOL_RESULT_SNIPPET_MAX = 4000
33
40
  # Predecessor ``_TOOL_ARG_CONTENT_KEYS`` (#4928): card content / diff-reconstruction inputs keep the long cap.
34
41
  _TOOL_ARG_CONTENT_KEYS = frozenset({"command", "cmd", "script", "code", "patch", "diff", "old_string", "new_string", "content", "path", "file_path"})
@@ -65,8 +72,16 @@ _RUNS_BY_SESSION: dict[str, str] = {}
65
72
  _APPROVAL_CB_OWNER: dict[str, str] = {}
66
73
  _APPROVAL_CB_LOCK = threading.Lock()
67
74
  _RUNS_LOCK = threading.Lock()
68
- _AGENT_CACHE: "OrderedDict[str, tuple[Any, str]]" = OrderedDict()
75
+ # session id -> (agent, signature, profile home).
76
+ _AGENT_CACHE: "OrderedDict[str, tuple[Any, str, Any]]" = OrderedDict()
69
77
  _AGENT_CACHE_LOCK = threading.Lock()
78
+ # Running release thread -> the profile home it runs under.
79
+ _RELEASES: dict[threading.Thread, Any] = {}
80
+ _RELEASES_LOCK = threading.Lock()
81
+ # Agents inside ``chat.commit_memory`` (guarded by ``_AGENT_CACHE_LOCK``): like a turn, a commit defers their release.
82
+ _COMMITTING: list = []
83
+ # Set (under ``_RUNS_LOCK``) when shutdown drains: no turn is admitted after the drain's snapshot of ``_RUNS``.
84
+ _DRAINING = threading.Event()
70
85
 
71
86
 
72
87
  def _snippet(raw: Any, limit: int = _TOOL_RESULT_SNIPPET_MAX) -> str:
@@ -88,6 +103,16 @@ _RAW_RESULT_MAX_KEYS = 64
88
103
  _RAW_RESULT_OUTCOME_KEYS = ("error", "exit_code", "exitCode", "success")
89
104
 
90
105
 
106
+ def _result_diff(raw: Any) -> str | None:
107
+ """A dict (or JSON-object text) result's string ``diff`` field, whole; the server decides what it shows."""
108
+ try:
109
+ data = raw if isinstance(raw, dict) else json.loads(str(raw or ""))
110
+ except Exception: # noqa: BLE001
111
+ return None
112
+ diff = data.get("diff") if isinstance(data, dict) else None
113
+ return diff if isinstance(diff, str) else None
114
+
115
+
91
116
  def _raw_result(raw: Any, limit: int = _TOOL_RESULT_SNIPPET_MAX) -> Any:
92
117
  """The tool result as the server's outcome rule reads it, bounded: a dict (or JSON-object text) keeps its first
93
118
  ``_RAW_RESULT_MAX_KEYS`` top-level fields plus its outcome fields, scalars as they are, text and non-empty nested
@@ -232,6 +257,98 @@ def _context_length(agent: Any) -> dict:
232
257
  return out
233
258
 
234
259
 
260
+ # TAL-539: the hard cap on raw tool output kept in a compressed context (predecessor ``_hard_prune_post_compression_*``).
261
+ _TAIL_TOOL_BUDGET_TOKENS = 4096
262
+ _TAIL_TOOL_MIN_SNIPPET_TOKENS = 256
263
+ _ROUGH_TOKEN_CHARS = 4
264
+
265
+
266
+ def _content_text(content: Any) -> str:
267
+ if isinstance(content, list):
268
+ return " ".join(str(p.get("text") or "") for p in content if isinstance(p, dict))
269
+ return str(content or "")
270
+
271
+
272
+ def _rough_tokens(text: str) -> int:
273
+ """The Agent's estimate, which counts CJK and other non-ASCII text as denser; ceil(chars / 4) without it."""
274
+ try:
275
+ from agent.model_metadata import estimate_tokens_rough
276
+
277
+ return int(estimate_tokens_rough(text))
278
+ except Exception: # noqa: BLE001
279
+ return -(-len(text) // _ROUGH_TOKEN_CHARS)
280
+
281
+
282
+ def _capped_tool_result(text: str, tokens: int, keep_tokens: int) -> str:
283
+ """The kept head of an over-budget tool result plus a note on what the model no longer sees."""
284
+ prefix = "[Talaria context budget: omitted "
285
+ suffix = f" (~{tokens} rough tokens) from this tool result; the full output remains in the visible transcript.]"
286
+ if keep_tokens < _TAIL_TOOL_MIN_SNIPPET_TOKENS:
287
+ return f"{prefix}{len(text)} chars{suffix}"
288
+ room = keep_tokens - _rough_tokens(f"{prefix}{len(text)} of {len(text)} chars{suffix}") - 1
289
+ snippet = text[: len(text) * room // tokens]
290
+ # Density varies along the text: shrink until the kept head fits the remaining tokens.
291
+ while snippet and _rough_tokens(snippet) > room:
292
+ snippet = snippet[: len(snippet) * 3 // 4]
293
+ snippet = snippet.rstrip()
294
+ if not snippet:
295
+ return f"{prefix}{len(text)} chars{suffix}"
296
+ return f"{snippet}\n\n{prefix}{len(text) - len(snippet)} of {len(text)} chars{suffix}"
297
+
298
+
299
+ def _cap_tool_results(messages: list, compressor: Any) -> list:
300
+ """Newest tool results spend one shared budget; the first one over it is cut to the remainder, older ones to a note."""
301
+ budget = getattr(compressor, "tail_token_budget", None)
302
+ budget = budget if isinstance(budget, int) and budget > 0 else _TAIL_TOOL_BUDGET_TOKENS
303
+ threshold = getattr(compressor, "threshold_tokens", None)
304
+ if isinstance(threshold, int) and threshold > 0:
305
+ budget = min(budget, max(512, threshold // 2))
306
+ budget = max(512, budget)
307
+ spent = 0
308
+ out = list(messages)
309
+ for i in range(len(out) - 1, -1, -1):
310
+ msg = out[i]
311
+ if msg.get("role") != "tool":
312
+ continue
313
+ text = _content_text(msg.get("content"))
314
+ if not text.strip():
315
+ continue
316
+ tokens = _rough_tokens(text)
317
+ if tokens <= budget - spent:
318
+ spent += tokens
319
+ continue
320
+ out[i] = {**msg, "content": _capped_tool_result(text, tokens, budget - spent)}
321
+ spent = budget
322
+ return out
323
+
324
+
325
+ def _post_compression_context(agent: Any, messages: list, system_message: Any) -> tuple[list, int | None]:
326
+ """TAL-539: tool output produced after a mid-turn compression never went through the compressor's pruning. Run that
327
+ pruning plus a hard cap on the model context, and estimate the next request from it (predecessor
328
+ ``_prune_context_tool_results_after_compression`` / ``_estimate_post_compression_context_tokens``)."""
329
+ compressor = getattr(agent, "context_compressor", None)
330
+ context = messages
331
+ prune = getattr(compressor, "_prune_old_tool_results", None)
332
+ if callable(prune):
333
+ try:
334
+ pruned, count = prune(copy.deepcopy(messages), protect_tail_count=getattr(compressor, "protect_last_n", 20), protect_tail_tokens=getattr(compressor, "tail_token_budget", None))
335
+ if count and isinstance(pruned, list) and len(pruned) == len(messages):
336
+ context = pruned
337
+ except Exception: # noqa: BLE001 - the hard cap below still bounds the context
338
+ log.debug("post-compression tool-result pruning failed", exc_info=True)
339
+ context = _cap_tool_results(context, compressor)
340
+ estimate = None
341
+ try:
342
+ from agent.model_metadata import estimate_request_tokens_rough
343
+
344
+ # The request carries the ephemeral prompt (surface, personality, delivery) after the system message.
345
+ prompts = [p for p in (system_message, getattr(agent, "ephemeral_system_prompt", None)) if isinstance(p, str) and p.strip()]
346
+ estimate = estimate_request_tokens_rough(context, system_prompt="\n\n".join(prompts), tools=getattr(agent, "tools", None) or None)
347
+ except Exception: # noqa: BLE001 - display-only figure
348
+ log.debug("post-compression context estimate failed", exc_info=True)
349
+ return context, estimate if isinstance(estimate, int) and estimate > 0 else None
350
+
351
+
235
352
  def _clarify_timeout(params: dict) -> int:
236
353
  """Predecessor ``_clarify_timeout_seconds``: an explicit request value wins, else the Agent's resolver over config."""
237
354
  explicit = params.get("clarify_timeout_seconds")
@@ -477,6 +594,30 @@ def _profile_fallback_chain() -> list[dict] | None:
477
594
  return get_fallback_chain(load_config() or {}) or None
478
595
 
479
596
 
597
+ def _main_model_request_overrides(model: str, provider) -> dict | None:
598
+ """Predecessor ``_main_model_request_overrides``: the profile's ``model.service_tier`` and ``model.extra_body``
599
+ (written by ``/api/model/set``) apply only to turns on that configured main model. Runs under ``scoped_home``."""
600
+ from hermes_cli.config import load_config
601
+
602
+ model_cfg = (load_config() or {}).get("model")
603
+ if not isinstance(model_cfg, dict):
604
+ return None
605
+ main_model = str(model_cfg.get("default") or model_cfg.get("name") or "").strip()
606
+ main_provider = str(model_cfg.get("provider") or "").strip().lower()
607
+ if not main_model or main_model != model.strip():
608
+ return None
609
+ # An unset or ``auto`` main provider resolves at runtime, so the model id alone identifies the main model.
610
+ if main_provider not in ("", "auto") and main_provider != str(provider or "").strip().lower():
611
+ return None
612
+ overrides: dict = {}
613
+ if str(model_cfg.get("service_tier") or "").strip().lower() == "priority":
614
+ overrides["service_tier"] = "priority"
615
+ extra_body = model_cfg.get("extra_body")
616
+ if isinstance(extra_body, dict) and extra_body:
617
+ overrides["extra_body"] = extra_body
618
+ return overrides or None
619
+
620
+
480
621
  def _agent_signature(model: str, provider, runtime: dict, toolsets, home: str, kwargs: dict) -> str:
481
622
  """Cache identity of an ``AIAgent``: everything its constructor bound from the resolved runtime, so a rotated key,
482
623
  a different API mode, ACP command, or credential pool never reuses an agent built for the old bundle. The key
@@ -492,6 +633,7 @@ def _agent_signature(model: str, provider, runtime: dict, toolsets, home: str, k
492
633
  "max_iterations": kwargs.get("max_iterations"), "max_tokens": kwargs.get("max_tokens"),
493
634
  # Bound at construction too: a reasoning-effort change from the composer must build a fresh agent.
494
635
  "reasoning_config": kwargs.get("reasoning_config"),
636
+ "request_overrides": kwargs.get("request_overrides"),
495
637
  # Entries may carry their own ``api_key``.
496
638
  "fallback_model": hashlib.sha256(json.dumps(kwargs.get("fallback_model"), sort_keys=True, default=str).encode("utf-8")).hexdigest()[:16],
497
639
  }
@@ -504,19 +646,164 @@ def _evict_idle_agents_locked() -> None:
504
646
  return
505
647
  with _RUNS_LOCK:
506
648
  live = {sid for sid, sid_stream in _RUNS_BY_SESSION.items() if (r := _RUNS.get(sid_stream)) is not None and not r.finished.is_set()}
649
+ dropped = []
507
650
  for sid in list(_AGENT_CACHE):
508
651
  if len(_AGENT_CACHE) <= _AGENT_CACHE_MAX:
509
652
  break
510
653
  if sid not in live:
511
- _AGENT_CACHE.pop(sid, None)
654
+ dropped.append(_AGENT_CACHE.pop(sid))
655
+ _release_agents_locked(dropped)
656
+
657
+
658
+ def _release_agent(agent, home) -> threading.Thread:
659
+ """The gateway's soft eviction on a daemon thread under the owning profile: end the memory session with the
660
+ transcript (``on_session_end``, then provider shutdown), then release the LLM clients (the Codex app-server child,
661
+ httpx pools) while session tool state stays for a resumed session."""
662
+
663
+ def call(name: str, *args) -> bool:
664
+ method = getattr(agent, name, None)
665
+ if not callable(method):
666
+ return True
667
+ try:
668
+ method(*args)
669
+ except Exception: # noqa: BLE001 - one failed phase never skips the next
670
+ log.warning("evicted agent %s() failed", name, exc_info=True)
671
+ return False
672
+ return True
673
+
674
+ def release() -> None:
675
+ released = False
676
+ try:
677
+ with scoped_home(home):
678
+ messages = getattr(agent, "_session_messages", None)
679
+ flushed = call("shutdown_memory_provider", messages if isinstance(messages, list) else None)
680
+ released = True
681
+ thread.failed = not (call("release_clients") and flushed)
682
+ except Exception: # noqa: BLE001 - the profile scope could not be entered
683
+ log.warning("releasing an evicted agent under its profile failed", exc_info=True)
684
+ thread.failed = True
685
+ finally:
686
+ if not released:
687
+ # The memory flush needs the profile; the LLM clients and the Codex child close regardless.
688
+ call("release_clients")
689
+ _release_session_db(getattr(agent, "_talaria_session_db", None))
690
+ with _RELEASES_LOCK:
691
+ _RELEASES.pop(thread, None)
692
+
693
+ thread = threading.Thread(target=release, daemon=True, name="chat-agent-release")
694
+ thread.failed = False # type: ignore[attr-defined] - read by profile deletion
695
+ with _RELEASES_LOCK:
696
+ _RELEASES[thread] = home
697
+ thread.start()
698
+ return thread
699
+
700
+
701
+ def _release_agents_locked(entries) -> list[threading.Thread]:
702
+ """Release cache entries that just left the cache (caller holds ``_AGENT_CACHE_LOCK``). An agent a turn still holds
703
+ keeps its memory provider and clients; that turn releases it when it ends."""
704
+ with _RUNS_LOCK:
705
+ held = [run.agent for run in _RUNS.values()] + _COMMITTING
706
+ return [_release_agent(agent, home) for agent, _signature, home in entries if not any(agent is other for other in held)]
707
+
708
+
709
+ def _release_if_dropped_locked(agent, home) -> None:
710
+ """A holder (turn or commit) is done with ``agent``: release it if it left the cache meanwhile and nothing else holds
711
+ it (caller holds ``_AGENT_CACHE_LOCK``)."""
712
+ if agent is not None and not any(entry[0] is agent for entry in _AGENT_CACHE.values()):
713
+ _release_agents_locked([(agent, None, home)])
714
+
715
+
716
+ def _join(threads, deadline: float) -> None:
717
+ for thread in threads:
718
+ thread.join(max(0.0, deadline - time.monotonic()))
512
719
 
513
720
 
514
721
  def evict_all_agents() -> int:
515
722
  """Drop every cached agent (credentials or environment changed underneath them)."""
516
723
  with _AGENT_CACHE_LOCK:
517
- count = len(_AGENT_CACHE)
724
+ entries = list(_AGENT_CACHE.values())
518
725
  _AGENT_CACHE.clear()
519
- return count
726
+ _release_agents_locked(entries)
727
+ return len(entries)
728
+
729
+
730
+ def drain_agents(timeout: float = _AGENT_RELEASE_TIMEOUT) -> None:
731
+ """Shutdown: release every cached agent, stop running turns (like a Stop) so each releases the agent it holds, and
732
+ wait, bounded, for those turns and every release still running."""
733
+ deadline = time.monotonic() + timeout
734
+ evict_all_agents()
735
+ with _RUNS_LOCK:
736
+ _DRAINING.set()
737
+ runs = list(_RUNS.values())
738
+ for run in runs:
739
+ run.cancel.set()
740
+ while time.monotonic() < deadline:
741
+ with _AGENT_CACHE_LOCK, _RUNS_LOCK:
742
+ if not _COMMITTING and not any(_RUNS.get(run.stream_id) is run for run in runs):
743
+ break
744
+ time.sleep(0.05)
745
+ # A turn may have cached its agent after the first eviction.
746
+ evict_all_agents()
747
+ with _RELEASES_LOCK:
748
+ threads = list(_RELEASES)
749
+ _join(threads, deadline)
750
+
751
+
752
+ def release_profile_agents(home) -> None:
753
+ """Profile deletion: release the profile's cached agents and wait for every release running under its home. Fails
754
+ rather than let the home go while a memory flush still runs under it."""
755
+ target = os.path.realpath(home)
756
+ with _AGENT_CACHE_LOCK:
757
+ entries = [_AGENT_CACHE.pop(sid) for sid, entry in list(_AGENT_CACHE.items()) if os.path.realpath(entry[2]) == target]
758
+ _release_agents_locked(entries)
759
+ with _RELEASES_LOCK:
760
+ threads = [thread for thread, owner in _RELEASES.items() if os.path.realpath(owner) == target]
761
+ _join(threads, time.monotonic() + _PROFILE_RELEASE_TIMEOUT)
762
+ if any(thread.is_alive() for thread in threads):
763
+ raise RpcError("The profile is still saving memory from its chats. Retry in a moment.")
764
+ if any(thread.failed for thread in threads):
765
+ raise RpcError("Memory from the profile's chats could not be saved; see the sidecar log. Retry to delete the profile anyway.")
766
+
767
+
768
+ def _acquire_session_db(home):
769
+ """TAL-529: the profile's ``state.db`` from the Agent's shared-handle registry, one reference per agent (released with
770
+ it), so the turn's conversation reaches ``hermes sessions``, CLI resume, and ``session_search``. None when it cannot
771
+ open: the turn runs without it, like the predecessor's ``_build_session_db_for_stream``."""
772
+ try:
773
+ from hermes_state_registry import acquire
774
+
775
+ return acquire(Path(home) / "state.db")
776
+ except Exception: # noqa: BLE001
777
+ log.warning("state.db unavailable under %s; this agent's turns will not reach it", home, exc_info=True)
778
+ return None
779
+
780
+
781
+ def _release_session_db(db) -> None:
782
+ if db is None:
783
+ return
784
+ try:
785
+ from hermes_state_registry import release_or_close
786
+
787
+ release_or_close(db)
788
+ except Exception: # noqa: BLE001
789
+ log.warning("releasing an agent's state.db handle failed", exc_info=True)
790
+
791
+
792
+ def _execution_session_id(db, session_id: str) -> str:
793
+ """Predecessor ``_resolve_agent_execution_session_id``: a new agent for a session the Agent rotated by compression
794
+ (the old agent went with a sidecar restart or a runtime change) runs on the live tip, with the server's history,
795
+ rather than the closed Web id. The tip qualifies as in the Agent's own adoption: a different, still-open row. Any
796
+ doubt keeps the Web id."""
797
+ try:
798
+ tip = db.get_compression_tip(session_id)
799
+ if tip and tip != session_id:
800
+ row = db.get_session(tip)
801
+ if row and row.get("ended_at") is None:
802
+ log.info("agent for %s runs on its live compression tip %s", session_id, tip)
803
+ return str(tip)
804
+ except Exception: # noqa: BLE001
805
+ log.warning("compression tip lookup failed for %s", session_id, exc_info=True)
806
+ return session_id
520
807
 
521
808
 
522
809
  def _agent_class():
@@ -527,6 +814,15 @@ def _agent_class():
527
814
  return AIAgent
528
815
 
529
816
 
817
+ def _max_iterations_summary_request() -> str:
818
+ """The user row the Agent appends to ask for a summary once its tool budget runs out; the server drops it."""
819
+ try:
820
+ from agent.context_compressor import MAX_ITERATIONS_SUMMARY_REQUEST
821
+ except Exception: # noqa: BLE001
822
+ return ""
823
+ return str(MAX_ITERATIONS_SUMMARY_REQUEST)
824
+
825
+
530
826
  def _supported(cls, name: str) -> bool:
531
827
  """Whether ``cls.__init__`` accepts ``name`` (named, or through ``**kwargs``)."""
532
828
  try:
@@ -564,6 +860,36 @@ def _approval_notifier(session_id: str, emit):
564
860
  return notify
565
861
 
566
862
 
863
+ #: The Agent's compression-start lifecycle lines: "Compacting context" (and its heartbeat), pre-API, 413, and too-large
864
+ #: compression. Preflight and idle notices are left out: "Compacting context" follows them once compression proceeds.
865
+ _COMPRESSION_START_MARKERS = ("compacting context", "pre-api compression:", "context too large", "— compressing (", "- compressing (", "compression attempt")
866
+ _COMPRESSION_NOT_STARTED = ("skipping", "defer", "cooldown", "will not start")
867
+
868
+
869
+ def _is_compression_start(lower: str) -> bool:
870
+ """Predecessor ``_is_agent_compression_start_status``: only real start notices, never skips or post-compress chatter."""
871
+ if any(marker in lower for marker in _COMPRESSION_NOT_STARTED):
872
+ return False
873
+ if "compressed" in lower and "compressing" not in lower and "compression attempt" not in lower:
874
+ return False
875
+ return any(marker in lower for marker in _COMPRESSION_START_MARKERS)
876
+
877
+
878
+ def _status_events(kind, text: str) -> list[str]:
879
+ """Events for one Agent status line: the compressing card, a fallback warning, and the turn's terminal error."""
880
+ lower = text.lower()
881
+ events = ["terminal_error"] if "non-retryable error" in lower else []
882
+ if str(kind or "").strip().lower() == "lifecycle":
883
+ if _is_compression_start(lower):
884
+ return events + ["compressing"]
885
+ # The Agent's own compression retries ("Compressed N → M messages, retrying...") are not provider fallbacks.
886
+ if "compress" in lower or "context reduced to" in lower:
887
+ return events
888
+ if "fallback" in lower or "rate limit" in lower or ("retry" in lower and "non-retryable" not in lower):
889
+ events.append("warning")
890
+ return events
891
+
892
+
567
893
  def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn, one function
568
894
  session_id = str(params.get("session_id") or "").strip()
569
895
  stream_id = str(params.get("stream_id") or "").strip()
@@ -581,8 +907,11 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
581
907
  raise InvalidParams("conversation_history must be a list")
582
908
  toolsets = _turn_toolsets(params.get("enabled_toolsets"))
583
909
  system_message = params.get("system_message")
910
+ home = profile_home_param(params)
584
911
  run = _Run(stream_id, session_id, ctx)
585
912
  with _RUNS_LOCK:
913
+ if _DRAINING.is_set():
914
+ raise RpcError("The sidecar is shutting down.", condition="sidecar_unavailable")
586
915
  prior = _RUNS.get(_RUNS_BY_SESSION.get(session_id) or "")
587
916
  # A previous turn for this session that never returned (a tool ignoring its interrupt) still owns the cached
588
917
  # agent; this turn must not share, rebind, or un-interrupt that live instance.
@@ -591,15 +920,21 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
591
920
  _RUNS_BY_SESSION[session_id] = stream_id
592
921
  raw_emit = ctx.emit
593
922
  steer_state = {"last": None}
923
+ usage_state = {"last": None}
594
924
 
595
925
  def emit(event, data=None):
596
926
  """Predecessor ``_webui_steer_events_before``: before content frames, report the Agent's pending steer text
597
- whenever it changed so the server can mark consumed steers live rather than only after ``done``."""
927
+ whenever it changed so the server can mark consumed steers live rather than only after ``done``. TAL-397: the
928
+ Agent's session counters ride the same boundary, so the server meters cost and tokens while the turn runs."""
598
929
  if event in ("token", "reasoning", "interim_assistant", "tool", "tool_complete") and run.agent is not None:
599
930
  pending = _agent_pending_steer_text(run.agent)
600
931
  if pending != steer_state["last"]:
601
932
  steer_state["last"] = pending
602
933
  raw_emit("steer_pending", {"text": pending})
934
+ usage = _usage(run.agent)
935
+ if usage != usage_state["last"]:
936
+ usage_state["last"] = usage
937
+ raw_emit("usage", usage)
603
938
  raw_emit(event, data)
604
939
  try:
605
940
  _discover_mcp_tools(run)
@@ -653,6 +988,13 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
653
988
  call["snippet"] = snippet
654
989
  break
655
990
  payload = {"event_type": "tool.completed", "name": name, "preview": snippet, "args": _args_snapshot(args), "tid": tid, "raw_result": _raw_result(function_result)}
991
+ # TAL-397: ``raw_result`` caps nested values, so the todo tool's list travels whole for the server's ``todo_state``.
992
+ if name == "todo" and function_result is not None:
993
+ payload["todo_result"] = function_result if isinstance(function_result, str) else json.dumps(function_result, default=str)
994
+ # TAL-448: likewise a result's unified ``diff``, so the server's ``edit_diff`` counts cover the whole change.
995
+ diff = _result_diff(function_result)
996
+ if diff is not None:
997
+ payload["result_diff"] = diff
656
998
  cost = _delegation_cost_usd(name, function_result)
657
999
  if cost is not None:
658
1000
  payload["cost_usd"] = cost
@@ -671,15 +1013,13 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
671
1013
 
672
1014
  def on_status(kind, message):
673
1015
  text = str(message or "").strip()
674
- if not text:
675
- return
676
- lower = text.lower()
677
- if "compress" in lower and ("start" in lower or "compressing" in lower):
678
- emit("compressing", {"session_id": session_id, "message": "Compressing context"})
679
- elif "fallback" in lower or "rate limit" in lower or "retry" in lower:
680
- emit("warning", {"type": "fallback", "message": text})
681
- elif "non-retryable error" in lower:
682
- emit("status", {"kind": "terminal_error", "message": text})
1016
+ for event in _status_events(kind, text):
1017
+ if event == "compressing":
1018
+ emit("compressing", {"session_id": session_id, "message": "Compressing context"})
1019
+ elif event == "warning":
1020
+ emit("warning", {"type": "fallback", "message": text})
1021
+ else:
1022
+ emit("status", {"kind": "terminal_error", "message": text})
683
1023
 
684
1024
  def clarify_callback(question, choices, multi_select=False, questions=None):
685
1025
  choices_list = [str(c) for c in (choices or [])]
@@ -759,23 +1099,45 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
759
1099
  fallback_chain = _profile_fallback_chain()
760
1100
  if fallback_chain and _supported(AIAgent, "fallback_model"):
761
1101
  kwargs["fallback_model"] = fallback_chain
1102
+ request_overrides = _main_model_request_overrides(resolved_model, resolved_provider)
1103
+ if request_overrides and _supported(AIAgent, "request_overrides"):
1104
+ kwargs["request_overrides"] = request_overrides
762
1105
  signature = _agent_signature(resolved_model, resolved_provider, runtime, toolsets, str(params.get("profile_home")), kwargs)
763
1106
  agent = None
1107
+
1108
+ def bind(bound) -> None:
1109
+ """Claim the agent for this run under ``_AGENT_CACHE_LOCK``, so an eviction never releases it mid-turn."""
1110
+ run.prior_messages = getattr(bound, "_session_messages", None)
1111
+ run.agent = bound
1112
+
764
1113
  if not session_busy:
765
1114
  with _AGENT_CACHE_LOCK:
766
1115
  cached = _AGENT_CACHE.get(session_id)
767
1116
  if cached and cached[1] == signature:
768
1117
  agent = cached[0]
769
1118
  _AGENT_CACHE.move_to_end(session_id)
1119
+ bind(agent)
770
1120
  if agent is not None:
771
1121
  for name in ("stream_delta_callback", "reasoning_callback", "tool_progress_callback", "clarify_callback", "interim_assistant_callback", "tool_start_callback", "tool_complete_callback", "status_callback"):
772
1122
  if name in kwargs and hasattr(agent, name):
773
1123
  setattr(agent, name, kwargs[name])
774
1124
  else:
775
- agent = AIAgent(**kwargs)
1125
+ # A ``/btw`` side question's throwaway session stays out of state.db.
1126
+ session_db = _acquire_session_db(home) if not params.get("ephemeral") and _supported(AIAgent, "session_db") else None
1127
+ if session_db is not None:
1128
+ kwargs["session_db"] = session_db
1129
+ kwargs["session_id"] = _execution_session_id(session_db, session_id)
1130
+ try:
1131
+ agent = AIAgent(**kwargs)
1132
+ except BaseException:
1133
+ _release_session_db(session_db)
1134
+ raise
1135
+ agent._talaria_session_db = session_db # released with the agent (``_release_agent``)
776
1136
  with _AGENT_CACHE_LOCK:
777
- _AGENT_CACHE[session_id] = (agent, signature)
778
- _AGENT_CACHE.move_to_end(session_id)
1137
+ bind(agent)
1138
+ replaced = _AGENT_CACHE.pop(session_id, None)
1139
+ _AGENT_CACHE[session_id] = (agent, signature, home)
1140
+ _release_agents_locked([replaced] if replaced else [])
779
1141
  _evict_idle_agents_locked()
780
1142
  # Predecessor ``agent.ephemeral_system_prompt``: personality, surface context, progress guidance, and delivery
781
1143
  # hints travel as runtime instructions that are never persisted to history.
@@ -789,8 +1151,7 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
789
1151
  with _APPROVAL_CB_LOCK:
790
1152
  register_gateway_notify(session_id, approval_cb)
791
1153
  _APPROVAL_CB_OWNER[session_id] = stream_id
792
- run.prior_messages = getattr(agent, "_session_messages", None)
793
- run.agent = agent
1154
+ usage_state["last"] = _usage(agent)
794
1155
  compressions_before = int(getattr(getattr(agent, "context_compressor", None), "compression_count", 0) or 0)
795
1156
 
796
1157
  # A cached agent may still carry the interrupt a previous cancel left behind; the Agent keeps a pending
@@ -830,8 +1191,12 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
830
1191
  run_params = set()
831
1192
  if system_message and ("system_message" in run_params or not run_params):
832
1193
  run_kwargs["system_message"] = system_message
833
- if "persist_user_message" in run_params and isinstance(user_message, str):
834
- run_kwargs["persist_user_message"] = user_message
1194
+ # The server's clean user text (no workspace prefix) is what state.db stores; the Agent keeps the sent bytes beside it.
1195
+ persist = params.get("persist_user_message")
1196
+ if not (isinstance(persist, str) and persist.strip()):
1197
+ persist = user_message if isinstance(user_message, str) else None
1198
+ if "persist_user_message" in run_params and persist is not None:
1199
+ run_kwargs["persist_user_message"] = persist
835
1200
  error: str | None = None
836
1201
  result: dict = {}
837
1202
  try:
@@ -868,30 +1233,40 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
868
1233
  error = str(result["final_response"])
869
1234
  status = "cancelled" if cancelled else ("error" if failed or (error and not result.get("messages")) else "completed")
870
1235
  pending_steer = str(result.get("pending_steer") or "") if isinstance(result, dict) else ""
1236
+ messages = [m for m in (result.get("messages") or []) if isinstance(m, dict)]
1237
+ compressed = compressions_after > compressions_before
1238
+ context_messages, context_estimate = _post_compression_context(agent, messages, system_message) if compressed and messages and status == "completed" else (None, None)
871
1239
  return {
872
1240
  "status": status,
873
- "messages": [m for m in (result.get("messages") or []) if isinstance(m, dict)],
1241
+ "messages": messages,
1242
+ "context_messages": context_messages,
1243
+ "post_compression_context_tokens_estimate": context_estimate,
874
1244
  "final_response": str(result.get("final_response") or ""),
875
1245
  "error": error,
876
1246
  "failed": failed,
877
1247
  "partial": partial,
878
1248
  "compression_exhausted": bool(result.get("compression_exhausted")),
879
- "tool_limit_reached": bool(result.get("tool_limit_reached") or result.get("max_iterations_reached")),
1249
+ # The pinned Agent marks an exhausted tool budget only in ``turn_exit_reason`` (``max_iterations_reached(n/n)``).
1250
+ "tool_limit_reached": bool(result.get("tool_limit_reached") or result.get("max_iterations_reached") or str(result.get("turn_exit_reason") or "").startswith("max_iterations_reached")),
1251
+ "max_iterations_summary_request": _max_iterations_summary_request(),
880
1252
  "usage": _usage(agent),
881
1253
  "context": _context_length(agent),
882
1254
  "model": str(getattr(agent, "model", None) or resolved_model),
883
1255
  "provider": str(resolved_provider or ""),
884
- "compressed": compressions_after > compressions_before,
1256
+ "compressed": compressed,
885
1257
  "agent_session_id": str(getattr(agent, "session_id", None) or session_id),
886
1258
  "token_sent": token_sent[0],
887
1259
  "pending_steer": pending_steer,
888
1260
  "live_tool_calls": live_tool_calls,
889
1261
  }
890
1262
  finally:
891
- with _RUNS_LOCK:
892
- _RUNS.pop(stream_id, None)
893
- if _RUNS_BY_SESSION.get(session_id) == stream_id:
894
- _RUNS_BY_SESSION.pop(session_id, None)
1263
+ with _AGENT_CACHE_LOCK:
1264
+ with _RUNS_LOCK:
1265
+ _RUNS.pop(stream_id, None)
1266
+ if _RUNS_BY_SESSION.get(session_id) == stream_id:
1267
+ _RUNS_BY_SESSION.pop(session_id, None)
1268
+ # An agent evicted or replaced while this turn held it is released now that the turn is over.
1269
+ _release_if_dropped_locked(run.agent, home)
895
1270
 
896
1271
 
897
1272
  def _checkpoint_required() -> bool:
@@ -1122,32 +1497,41 @@ def register(registry) -> None:
1122
1497
  run = _RUNS.get(_RUNS_BY_SESSION.get(session_id) or "")
1123
1498
  if not end_session and run is not None and not run.finished.is_set():
1124
1499
  return {"evicted": False}
1125
- evicted = _AGENT_CACHE.pop(session_id, None) is not None
1500
+ entry = _AGENT_CACHE.pop(session_id, None)
1501
+ _release_agents_locked([entry] if entry else [])
1126
1502
  if end_session:
1127
1503
  try:
1128
1504
  from tools.approval import clear_session
1129
1505
  clear_session(session_id)
1130
1506
  except Exception: # noqa: BLE001
1131
1507
  pass
1132
- return {"evicted": evicted}
1508
+ return {"evicted": entry is not None}
1133
1509
 
1134
1510
  @registry.method("chat.commit_memory", requires_agent=False)
1135
1511
  def commit_memory_(ctx: CallContext, params: dict) -> dict:
1136
- """Predecessor ``commit_session_memory``: flush the cached Agent's memory for a session the user just left."""
1512
+ """Predecessor ``commit_session_memory``: flush the cached Agent's memory for a session the user just left.
1513
+ Providers get the session's transcript and run under its profile's ``scoped_home``."""
1137
1514
  session_id = str(params.get("session_id") or "").strip()
1515
+ home = profile_home_param(params)
1138
1516
  with _AGENT_CACHE_LOCK:
1139
1517
  cached = _AGENT_CACHE.get(session_id)
1140
- with _RUNS_LOCK:
1141
- busy = (_RUNS_BY_SESSION.get(session_id) or "") in _RUNS
1142
- agent = cached[0] if cached else None
1143
- commit = getattr(agent, "commit_memory_session", None) if agent is not None else None
1144
- if commit is None or busy:
1145
- return {"committed": False}
1518
+ with _RUNS_LOCK:
1519
+ busy = _DRAINING.is_set() or (_RUNS_BY_SESSION.get(session_id) or "") in _RUNS
1520
+ agent = cached[0] if cached else None
1521
+ commit = getattr(agent, "commit_memory_session", None) if agent is not None else None
1522
+ if commit is None or busy:
1523
+ return {"committed": False}
1524
+ _COMMITTING.append(agent)
1146
1525
  try:
1147
- commit()
1526
+ with scoped_home(home):
1527
+ commit(list(getattr(agent, "_session_messages", None) or []))
1148
1528
  except Exception as exc: # noqa: BLE001
1149
1529
  log.warning("commit_memory_session() failed for session %s: %s", session_id, exc)
1150
1530
  return {"committed": False}
1531
+ finally:
1532
+ with _AGENT_CACHE_LOCK:
1533
+ _COMMITTING.remove(agent)
1534
+ _release_if_dropped_locked(agent, cached[2])
1151
1535
  return {"committed": True}
1152
1536
 
1153
1537
  @registry.method("approval.respond", requires_agent=True)