@maudecode/talaria-web 1.15.0 → 1.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) hide show
  1. package/_release.json +4 -4
  2. package/dist/api/automation-router.d.ts +21 -7
  3. package/dist/api/automation-router.js +3 -0
  4. package/dist/api/automation-router.js.map +1 -1
  5. package/dist/api/chat-router.d.ts +142 -6
  6. package/dist/api/chat-router.js +11 -5
  7. package/dist/api/chat-router.js.map +1 -1
  8. package/dist/api/git-router.js +44 -32
  9. package/dist/api/git-router.js.map +1 -1
  10. package/dist/api/raw-routes.js +5 -58
  11. package/dist/api/raw-routes.js.map +1 -1
  12. package/dist/api/router.d.ts +4 -2
  13. package/dist/api/router.js +8 -8
  14. package/dist/api/router.js.map +1 -1
  15. package/dist/api/sessions-router.d.ts +714 -42
  16. package/dist/api/sessions-router.js +57 -23
  17. package/dist/api/sessions-router.js.map +1 -1
  18. package/dist/api/settings-router.d.ts +153 -9
  19. package/dist/api/settings-router.js +61 -16
  20. package/dist/api/settings-router.js.map +1 -1
  21. package/dist/api/sse-routes.js +1 -1
  22. package/dist/api/sse-routes.js.map +1 -1
  23. package/dist/api/tools-router.d.ts +13 -1
  24. package/dist/api/tools-router.js +10 -8
  25. package/dist/api/tools-router.js.map +1 -1
  26. package/dist/app.d.ts +1047 -67
  27. package/dist/auth/gate.js +8 -4
  28. package/dist/auth/gate.js.map +1 -1
  29. package/dist/auth/oidc.d.ts +6 -2
  30. package/dist/auth/oidc.js +16 -11
  31. package/dist/auth/oidc.js.map +1 -1
  32. package/dist/auth/store.d.ts +14 -2
  33. package/dist/auth/store.js +26 -1
  34. package/dist/auth/store.js.map +1 -1
  35. package/dist/cli/dotenv.d.ts +1 -1
  36. package/dist/cli/dotenv.js +36 -3
  37. package/dist/cli/dotenv.js.map +1 -1
  38. package/dist/http/addresses.d.ts +6 -0
  39. package/dist/http/addresses.js +26 -0
  40. package/dist/http/addresses.js.map +1 -1
  41. package/dist/http/context.d.ts +3 -3
  42. package/dist/links.d.ts +26 -0
  43. package/dist/links.js +60 -0
  44. package/dist/links.js.map +1 -0
  45. package/dist/onboarding.js +26 -7
  46. package/dist/onboarding.js.map +1 -1
  47. package/dist/profiles/profiles.js +1 -1
  48. package/dist/profiles/profiles.js.map +1 -1
  49. package/dist/providers/catalog.d.ts +74 -1
  50. package/dist/providers/catalog.js +248 -42
  51. package/dist/providers/catalog.js.map +1 -1
  52. package/dist/providers/tables.d.ts +3 -1
  53. package/dist/providers/tables.js +9 -4
  54. package/dist/providers/tables.js.map +1 -1
  55. package/dist/redact.js +493 -13
  56. package/dist/redact.js.map +1 -1
  57. package/dist/runtime.js +52 -28
  58. package/dist/runtime.js.map +1 -1
  59. package/dist/sessions/anchor.d.ts +22 -3
  60. package/dist/sessions/anchor.js +87 -42
  61. package/dist/sessions/anchor.js.map +1 -1
  62. package/dist/sessions/background-tasks.d.ts +4 -1
  63. package/dist/sessions/background-tasks.js +7 -4
  64. package/dist/sessions/background-tasks.js.map +1 -1
  65. package/dist/sessions/completions.js +1 -1
  66. package/dist/sessions/completions.js.map +1 -1
  67. package/dist/sessions/list.d.ts +8 -4
  68. package/dist/sessions/list.js +24 -17
  69. package/dist/sessions/list.js.map +1 -1
  70. package/dist/sessions/merge.d.ts +70 -7
  71. package/dist/sessions/merge.js +331 -23
  72. package/dist/sessions/merge.js.map +1 -1
  73. package/dist/sessions/pending.d.ts +5 -0
  74. package/dist/sessions/pending.js +30 -10
  75. package/dist/sessions/pending.js.map +1 -1
  76. package/dist/sessions/service.d.ts +57 -3
  77. package/dist/sessions/service.js +327 -56
  78. package/dist/sessions/service.js.map +1 -1
  79. package/dist/sessions/session.d.ts +6 -1
  80. package/dist/sessions/session.js +6 -0
  81. package/dist/sessions/session.js.map +1 -1
  82. package/dist/sessions/shares.js +11 -2
  83. package/dist/sessions/shares.js.map +1 -1
  84. package/dist/sessions/store.js +1 -1
  85. package/dist/sessions/store.js.map +1 -1
  86. package/dist/sessions/turn.d.ts +19 -1
  87. package/dist/sessions/turn.js +218 -113
  88. package/dist/sessions/turn.js.map +1 -1
  89. package/dist/settings.js +9 -0
  90. package/dist/settings.js.map +1 -1
  91. package/dist/sidecar/fake.js +2 -0
  92. package/dist/sidecar/fake.js.map +1 -1
  93. package/dist/tools/commands.d.ts +15 -0
  94. package/dist/tools/commands.js +52 -0
  95. package/dist/tools/commands.js.map +1 -0
  96. package/dist/tools/crons.d.ts +4 -1
  97. package/dist/tools/crons.js +58 -11
  98. package/dist/tools/crons.js.map +1 -1
  99. package/dist/tools/mcp.js +5 -1
  100. package/dist/tools/mcp.js.map +1 -1
  101. package/dist/tools/update-notifications.d.ts +1 -1
  102. package/dist/tools/update-notifications.js +1 -1
  103. package/dist/tools/update-notifications.js.map +1 -1
  104. package/dist/tools/updates.d.ts +8 -3
  105. package/dist/tools/updates.js +133 -34
  106. package/dist/tools/updates.js.map +1 -1
  107. package/dist/workspace/git.d.ts +57 -10
  108. package/dist/workspace/git.js +302 -195
  109. package/dist/workspace/git.js.map +1 -1
  110. package/dist/workspace/media-refs.d.ts +13 -0
  111. package/dist/workspace/media-refs.js +165 -0
  112. package/dist/workspace/media-refs.js.map +1 -0
  113. package/dist/workspace/media.d.ts +19 -0
  114. package/dist/workspace/media.js +61 -2
  115. package/dist/workspace/media.js.map +1 -1
  116. package/dist/workspace/workspaces.d.ts +7 -1
  117. package/dist/workspace/workspaces.js +30 -21
  118. package/dist/workspace/workspaces.js.map +1 -1
  119. package/dist/workspace/worktrees.d.ts +2 -4
  120. package/dist/workspace/worktrees.js +28 -33
  121. package/dist/workspace/worktrees.js.map +1 -1
  122. package/package.json +4 -2
  123. package/sidecar/scripts/replay_sidecar.py +3 -1
  124. package/sidecar/talaria_sidecar/__init__.py +1 -1
  125. package/sidecar/talaria_sidecar/home.py +59 -2
  126. package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
  127. package/sidecar/talaria_sidecar/methods/aux.py +19 -3
  128. package/sidecar/talaria_sidecar/methods/chat.py +129 -23
  129. package/sidecar/talaria_sidecar/methods/commands.py +46 -31
  130. package/sidecar/talaria_sidecar/methods/config.py +14 -10
  131. package/sidecar/talaria_sidecar/methods/oauth.py +439 -0
  132. package/sidecar/talaria_sidecar/methods/profiles.py +5 -4
  133. package/sidecar/talaria_sidecar/methods/providers.py +161 -1
  134. package/sidecar/talaria_sidecar/methods/runtime.py +2 -0
  135. package/sidecar/talaria_sidecar/methods/text.py +1 -20
  136. package/sidecar/talaria_sidecar/methods/usage.py +7 -1
  137. package/static/dist/FILES.txt +124 -120
  138. package/static/dist/assets/AppShell-ef3bhu32.js +1 -0
  139. package/static/dist/assets/Button-nx37f4u1.js +1 -0
  140. package/static/dist/assets/ChatPage-cdcpykcg.js +25 -0
  141. package/static/dist/assets/Dialog-mec42hvb.js +1 -0
  142. package/static/dist/assets/HubRoute-ecb65zpr.js +2 -0
  143. package/static/dist/assets/{Markdown-dy8t4dy7.js → Markdown-jqrx5es0.js} +125 -125
  144. package/static/dist/assets/RightPanel-d1lgvk4s.js +1 -0
  145. package/static/dist/assets/Select-h4ivl9qf.js +1 -0
  146. package/static/dist/assets/SessionListPanel-ewib046b.js +1 -0
  147. package/static/dist/assets/SettingsLayout-lx654cup.js +1 -0
  148. package/static/dist/assets/States-ehsb6lzo.js +1 -0
  149. package/static/dist/assets/Toaster-mch1pa3t.js +1 -0
  150. package/static/dist/assets/_app-m8d961ef.js +1 -0
  151. package/static/dist/assets/_app.ext._extensionId-mp33hbzi.js +1 -0
  152. package/static/dist/assets/_app.index-dk6zp7e0.js +1 -0
  153. package/static/dist/assets/_app.insights-jt2fk4i1.js +1 -0
  154. package/static/dist/assets/_app.kanban-if4pubeu.js +1 -0
  155. package/static/dist/assets/_app.logs-mfaw9m6g.js +1 -0
  156. package/static/dist/assets/_app.memory-h3eaarb6.js +1 -0
  157. package/static/dist/assets/_app.profiles-ienktovs.js +1 -0
  158. package/static/dist/assets/{_app.session._sessionId-gyoh5ylx.js → _app.session._sessionId-hh27wwu2.js} +1 -1
  159. package/static/dist/assets/_app.settings-dpoe0bzf.js +1 -0
  160. package/static/dist/assets/_app.settings._section-l468ynag.js +3 -0
  161. package/static/dist/assets/_app.skills-kcbncc8i.js +1 -0
  162. package/static/dist/assets/_app.tasks-occdrqhw.js +2 -0
  163. package/static/dist/assets/_app.todos-nnke2rcc.js +1 -0
  164. package/static/dist/assets/_app.workspaces-cdlatvwj.js +1 -0
  165. package/static/dist/assets/{abnfDiagram-VCTEODGH-cjqletra.js → abnfDiagram-VCTEODGH-oity9y4o.js} +1 -1
  166. package/static/dist/assets/architecture-7GRP2DOG-hj11333z.js +1 -0
  167. package/static/dist/assets/architectureDiagram-5GKGNRK7-ceaepnyr.js +36 -0
  168. package/static/dist/assets/{back-e4m6nsnm.js → back-eu64w89w.js} +1 -1
  169. package/static/dist/assets/{blockDiagram-I7D4REHJ-b195z021.js → blockDiagram-I7D4REHJ-n79st4pj.js} +1 -1
  170. package/static/dist/assets/{c4Diagram-7LVT6UL2-olu212k1.js → c4Diagram-7LVT6UL2-mi73akzk.js} +1 -1
  171. package/static/dist/assets/cancel-mzvkdjg8.js +1 -0
  172. package/static/dist/assets/channel-mbvmf2vv.js +1 -0
  173. package/static/dist/assets/{chunk-4HAMMTFA-b8o1nufs.js → chunk-4HAMMTFA-laat0g1q.js} +1 -1
  174. package/static/dist/assets/{chunk-75Z2AOVW-l1seddto.js → chunk-75Z2AOVW-0psy1tr2.js} +1 -1
  175. package/static/dist/assets/{chunk-DU6HZSFF-lo5zg15v.js → chunk-DU6HZSFF-lw8uox8x.js} +3 -3
  176. package/static/dist/assets/{chunk-F27PBJKO-dsdzrp16.js → chunk-F27PBJKO-erassib3.js} +1 -1
  177. package/static/dist/assets/{chunk-GMAD6QVW-f3fxqq8b.js → chunk-GMAD6QVW-fz7rx619.js} +1 -1
  178. package/static/dist/assets/{chunk-GVQU2GXP-h8egxtf6.js → chunk-GVQU2GXP-gdb0epb2.js} +1 -1
  179. package/static/dist/assets/{chunk-IMKFNOWR-iolwd8s0.js → chunk-IMKFNOWR-ea8t26qw.js} +1 -1
  180. package/static/dist/assets/{chunk-L3NEJ4N5-b88bbtwi.js → chunk-L3NEJ4N5-mb1gu9kz.js} +1 -1
  181. package/static/dist/assets/{chunk-OSK3NFVY-e3e7nr6s.js → chunk-OSK3NFVY-eovmccvy.js} +1 -1
  182. package/static/dist/assets/{chunk-P2QGCYS3-mdc46rt1.js → chunk-P2QGCYS3-bo55zjo5.js} +1 -1
  183. package/static/dist/assets/{chunk-POPQ4Y6H-kqlllrrm.js → chunk-POPQ4Y6H-ja7ohe4h.js} +1 -1
  184. package/static/dist/assets/{chunk-PWAF6VOD-he585k2x.js → chunk-PWAF6VOD-e30g9nus.js} +1 -1
  185. package/static/dist/assets/{chunk-SHT3W25Y-gh94y9a4.js → chunk-SHT3W25Y-gbb798f7.js} +3 -3
  186. package/static/dist/assets/{chunk-SVP7TREG-euxp9y7e.js → chunk-SVP7TREG-h2iw8urm.js} +1 -1
  187. package/static/dist/assets/{chunk-TICWLB2K-c8cxes1q.js → chunk-TICWLB2K-fd2cottz.js} +1 -1
  188. package/static/dist/assets/classDiagram-ZZMXUADV-o8r05k0n.js +1 -0
  189. package/static/dist/assets/classDiagram-v2-VYDZK3BY-o8r05k0n.js +1 -0
  190. package/static/dist/assets/{copied-kq0u3ht7.js → copied-h65fc3cx.js} +1 -1
  191. package/static/dist/assets/copy-e6977uwq.js +1 -0
  192. package/static/dist/assets/{cynefin-OW5HDTMX-lxd44ux4.js → cynefin-OW5HDTMX-lbltyl5j.js} +1 -1
  193. package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-njpiwat8.js → cynefinDiagram-5FMLGOSQ-lxhsqq4a.js} +1 -1
  194. package/static/dist/assets/{dagre-GXQ25YYZ-nvgy6yju.js → dagre-GXQ25YYZ-gn4nnaln.js} +4 -4
  195. package/static/dist/assets/{diagram-S7CK7UJ4-g98nyv72.js → diagram-S7CK7UJ4-pa3ij6q7.js} +3 -3
  196. package/static/dist/assets/{diagram-UQ7AKVKN-ovgm6lvv.js → diagram-UQ7AKVKN-zbipyz57.js} +1 -1
  197. package/static/dist/assets/{diagram-VSXAHHWV-pb2bn2e4.js → diagram-VSXAHHWV-d1kbpzgf.js} +1 -1
  198. package/static/dist/assets/{diagram-VX7I27RA-6j4mvfqo.js → diagram-VX7I27RA-i3ty14xl.js} +1 -1
  199. package/static/dist/assets/{diagram-Z3DM3KII-dqmb982r.js → diagram-Z3DM3KII-m8xefqa7.js} +1 -1
  200. package/static/dist/assets/{ebnfDiagram-PWID7BFC-ozbpzb6d.js → ebnfDiagram-PWID7BFC-nz7wj05l.js} +1 -1
  201. package/static/dist/assets/endpoints-k7yf25m0.js +64 -0
  202. package/static/dist/assets/{erDiagram-RLTQ6QDP-hl7m8vua.js → erDiagram-RLTQ6QDP-fa41ib83.js} +1 -1
  203. package/static/dist/assets/eventmodeling-NTZA5JFV-kpbq09bx.js +1 -0
  204. package/static/dist/assets/flowDiagram-HODETNUW-lcz0kx14.js +1 -0
  205. package/static/dist/assets/{ganttDiagram-EL5Y4UJY-ompixqmh.js → ganttDiagram-EL5Y4UJY-n7zq01qu.js} +1 -1
  206. package/static/dist/assets/{gitGraph-4MIJSDKK-b6vxwp9q.js → gitGraph-4MIJSDKK-c89emb07.js} +1 -1
  207. package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-k98f57on.js → gitGraphDiagram-WWUBYQGX-cz7cgr6w.js} +1 -1
  208. package/static/dist/assets/{highlighted-body-KPVGNVTW-cd3ff6ge.js → highlighted-body-KPVGNVTW-d9bapmni.js} +1 -1
  209. package/static/dist/assets/{index-nqed3oum.js → index-h00usru2.js} +3 -3
  210. package/static/dist/assets/index-n73ijkv2.css +2 -0
  211. package/static/dist/assets/{info-A6RAGUB7-hjcwjami.js → info-A6RAGUB7-ndddhhhh.js} +1 -1
  212. package/static/dist/assets/infoDiagram-27XIBGKW-ccxigcpf.js +2 -0
  213. package/static/dist/assets/{ishikawaDiagram-5VMMS53U-bxak4mwu.js → ishikawaDiagram-5VMMS53U-le0z9vtc.js} +1 -1
  214. package/static/dist/assets/{journeyDiagram-3NMN7TZE-khmihclr.js → journeyDiagram-3NMN7TZE-ivsmb26o.js} +1 -1
  215. package/static/dist/assets/{kanban-definition-UXKFOSKX-h1r32wdx.js → kanban-definition-UXKFOSKX-h6n2awt0.js} +1 -1
  216. package/static/dist/assets/lazyRouteComponent-m1rtyk4t.js +1 -0
  217. package/static/dist/assets/{line-friyjof3.js → line-dmzlfe0u.js} +1 -1
  218. package/static/dist/assets/link-budeg5t9.js +1 -0
  219. package/static/dist/assets/login-kjhasvp8.js +1 -0
  220. package/static/dist/assets/mermaid-HWGCJPDP-jb5d211j.js +1 -0
  221. package/static/dist/assets/{mermaid-parser.core-f1gvpwrt.js → mermaid-parser.core-gwyd5bnq.js} +3 -3
  222. package/static/dist/assets/{mindmap-definition-YA3MSWOX-mt2oxwue.js → mindmap-definition-YA3MSWOX-l68gg5h7.js} +1 -1
  223. package/static/dist/assets/onboarding-cdfrk0pj.js +1 -0
  224. package/static/dist/assets/{packet-AYTQ26CC-jik4ixlf.js → packet-AYTQ26CC-gpv06065.js} +1 -1
  225. package/static/dist/assets/{pegDiagram-XKGWAZYB-erxlv6cc.js → pegDiagram-XKGWAZYB-e52vs23u.js} +1 -1
  226. package/static/dist/assets/{pie-WAS4IAKB-mnkoldfl.js → pie-WAS4IAKB-l6m7nz5s.js} +1 -1
  227. package/static/dist/assets/{pieDiagram-E7YTZNPT-i7lwrada.js → pieDiagram-E7YTZNPT-b4is36bq.js} +3 -3
  228. package/static/dist/assets/{quadrantDiagram-AXDQQJYC-fr8ejdoy.js → quadrantDiagram-AXDQQJYC-m8vt2788.js} +1 -1
  229. package/static/dist/assets/{radar-RG4KPBEZ-otarjte2.js → radar-RG4KPBEZ-nxbrhiov.js} +1 -1
  230. package/static/dist/assets/{railroad-74A4TZTK-lw362oto.js → railroad-74A4TZTK-222mu9hi.js} +1 -1
  231. package/static/dist/assets/railroad-abnf-HS5TGJTU-kdvgl6x8.js +1 -0
  232. package/static/dist/assets/railroad-ebnf-LZEXJU2U-itiqslby.js +1 -0
  233. package/static/dist/assets/railroad-peg-WCYAUIDC-go7kov78.js +1 -0
  234. package/static/dist/assets/{railroadDiagram-O6MQD6OU-esyn75pw.js → railroadDiagram-O6MQD6OU-o8lldfzv.js} +1 -1
  235. package/static/dist/assets/{refresh-cw-k66keho6.js → refresh-cw-kbcje1uy.js} +1 -1
  236. package/static/dist/assets/{requirementDiagram-BXWQKSXE-cyhi5u29.js → requirementDiagram-BXWQKSXE-ovyq38kv.js} +1 -1
  237. package/static/dist/assets/{sankeyDiagram-P5KCCOFB-jljpbo0h.js → sankeyDiagram-P5KCCOFB-ou7lznor.js} +1 -1
  238. package/static/dist/assets/{saved-do49si3v.js → saved-bazmcup9.js} +1 -1
  239. package/static/dist/assets/search-jen74z4u.js +1 -0
  240. package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-fz0dhlu7.js → sequenceDiagram-WJ2MYXX4-ovckdzf2.js} +1 -1
  241. package/static/dist/assets/{settings_section_extensions_title-b7lojn0o.js → settings_section_extensions_title-ertwwhlx.js} +1 -1
  242. package/static/dist/assets/share._token-dan9hgvj.js +2 -0
  243. package/static/dist/assets/{sliders-horizontal-c4wya4dy.js → sliders-horizontal-f9cb3lh3.js} +1 -1
  244. package/static/dist/assets/{stateDiagram-D77RDMKH-kdnithdx.js → stateDiagram-D77RDMKH-fcrexkkv.js} +1 -1
  245. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-lcfraua2.js +1 -0
  246. package/static/dist/assets/{swimlanes-42K2YHIH-esd6gmz3.js → swimlanes-42K2YHIH-f7usu8fw.js} +1 -1
  247. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-ogmanni0.js +8 -0
  248. package/static/dist/assets/{timeline-definition-24CTP7MA-g0qygccv.js → timeline-definition-24CTP7MA-gp1hvz9f.js} +1 -1
  249. package/static/dist/assets/todoStore-eqtid4r4.js +1 -0
  250. package/static/dist/assets/{treeView-Q6P3EWNA-ndchzkuu.js → treeView-Q6P3EWNA-gb9gf11u.js} +1 -1
  251. package/static/dist/assets/{treemap-WGGIJYW6-hwjxj0zz.js → treemap-WGGIJYW6-nuvfnvvi.js} +1 -1
  252. package/static/dist/assets/{untitled-mbr6tuhu.js → untitled-htelhc68.js} +1 -1
  253. package/static/dist/assets/useForm-h2t0u14e.js +1 -0
  254. package/static/dist/assets/useLocale-hcsb84pj.js +1 -0
  255. package/static/dist/assets/useNavigate-nwy70ztj.js +1 -0
  256. package/static/dist/assets/{useParams-lo9u33d1.js → useParams-dqd64jv7.js} +1 -1
  257. package/static/dist/assets/{useSearch-lq1mr090.js → useSearch-j30pvsy7.js} +1 -1
  258. package/static/dist/assets/{vennDiagram-4TSXK5OY-gubovd1r.js → vennDiagram-4TSXK5OY-fzchbzaj.js} +1 -1
  259. package/static/dist/assets/{wardley-WFR3VGLG-ir3om65j.js → wardley-WFR3VGLG-m47mhfbh.js} +1 -1
  260. package/static/dist/assets/{wardleyDiagram-VM6X3IG4-ce0or1ie.js → wardleyDiagram-VM6X3IG4-odg6ct1z.js} +2 -2
  261. package/static/dist/assets/{xychartDiagram-S5SC5T6Z-dexylcjg.js → xychartDiagram-S5SC5T6Z-mrhms5um.js} +1 -1
  262. package/static/dist/index.html +1 -1
  263. package/static/dist/sw.js +1 -1
  264. package/static/dist/assets/AppShell-orat1s43.js +0 -1
  265. package/static/dist/assets/ChatPage-ojhka3hy.js +0 -22
  266. package/static/dist/assets/HubRoute-gxgw1t33.js +0 -2
  267. package/static/dist/assets/RightPanel-c66ani1s.js +0 -1
  268. package/static/dist/assets/Select-fswf66up.js +0 -1
  269. package/static/dist/assets/SessionListPanel-goh5mtt8.js +0 -1
  270. package/static/dist/assets/SettingsLayout-n5d4wq4m.js +0 -1
  271. package/static/dist/assets/States-kxyu50at.js +0 -1
  272. package/static/dist/assets/Toaster-6zmbcqgg.js +0 -1
  273. package/static/dist/assets/_app-fowyruiu.js +0 -1
  274. package/static/dist/assets/_app.ext._extensionId-nvjyrwhy.js +0 -1
  275. package/static/dist/assets/_app.index-c9zv4y7w.js +0 -1
  276. package/static/dist/assets/_app.insights-jf12nofo.js +0 -1
  277. package/static/dist/assets/_app.kanban-lri5a4yu.js +0 -1
  278. package/static/dist/assets/_app.logs-md1p0zqo.js +0 -1
  279. package/static/dist/assets/_app.memory-bzvf2e28.js +0 -1
  280. package/static/dist/assets/_app.profiles-jpg7zmbn.js +0 -1
  281. package/static/dist/assets/_app.settings-mpuwlx4j.js +0 -1
  282. package/static/dist/assets/_app.settings._section-knz8rwoj.js +0 -1
  283. package/static/dist/assets/_app.skills-fwu1dkir.js +0 -1
  284. package/static/dist/assets/_app.tasks-h9gphtl3.js +0 -2
  285. package/static/dist/assets/_app.todos-hd1dnvrh.js +0 -1
  286. package/static/dist/assets/_app.workspaces-l8toyn5f.js +0 -1
  287. package/static/dist/assets/architecture-7GRP2DOG-cavnusaw.js +0 -1
  288. package/static/dist/assets/architectureDiagram-5GKGNRK7-k5a7v36n.js +0 -36
  289. package/static/dist/assets/channel-gozy3dp3.js +0 -1
  290. package/static/dist/assets/classDiagram-ZZMXUADV-n0vs2f2q.js +0 -1
  291. package/static/dist/assets/classDiagram-v2-VYDZK3BY-n0vs2f2q.js +0 -1
  292. package/static/dist/assets/endpoints-k8agnro8.js +0 -9
  293. package/static/dist/assets/eventmodeling-NTZA5JFV-j1y2ekec.js +0 -1
  294. package/static/dist/assets/flowDiagram-HODETNUW-g2cvcbm5.js +0 -1
  295. package/static/dist/assets/index-nfy0ezs6.css +0 -2
  296. package/static/dist/assets/infoDiagram-27XIBGKW-fm06rj36.js +0 -2
  297. package/static/dist/assets/lazyRouteComponent-b2eg0fws.js +0 -1
  298. package/static/dist/assets/link-m90ahx3n.js +0 -1
  299. package/static/dist/assets/login-bw7m2fzo.js +0 -1
  300. package/static/dist/assets/mermaid-HWGCJPDP-njwcl62n.js +0 -1
  301. package/static/dist/assets/onboarding-ret37j1e.js +0 -1
  302. package/static/dist/assets/railroad-abnf-HS5TGJTU-m741owar.js +0 -1
  303. package/static/dist/assets/railroad-ebnf-LZEXJU2U-noa4fbia.js +0 -1
  304. package/static/dist/assets/railroad-peg-WCYAUIDC-o4bpw4nb.js +0 -1
  305. package/static/dist/assets/search-j5ki6a9o.js +0 -1
  306. package/static/dist/assets/share._token-kg8w647b.js +0 -2
  307. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-k2wg2645.js +0 -1
  308. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-n5kvjtgr.js +0 -8
  309. package/static/dist/assets/tabBuild-bjmyvmn5.js +0 -56
  310. package/static/dist/assets/todoStore-kjk5l0sy.js +0 -1
  311. package/static/dist/assets/useForm-ocxu460f.js +0 -1
  312. package/static/dist/assets/useLocale-fzy5jhg6.js +0 -1
@@ -24,17 +24,22 @@ import { withSessionWireFlags } from './list.js';
24
24
  import { hydrateAnchorActivityScenes, turnTerminalState, withTurnIds } from './anchor.js';
25
25
  import { persistentStateChanges, persistentStateSnapshot } from './state-saved.js';
26
26
  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, splitThinkingFromContent, stoppedTurnContext, stripXmlToolCalls, toolOutcome, withAttachmentObjects, withBodyExcerpts, withToolCallOutcomes, workspaceContextPrefix } from './merge.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';
28
28
  import { mayBecomeSilentReply, turnOrigin, withBackgroundUpdates } from './background-updates.js';
29
29
  import { fallbackTitleFromExchange, firstExchangeSnippets, isGenericFallbackTitle, latestExchangeSnippets, looksInvalidGeneratedTitle, looksLikeDefaultCliTitle, sanitizeGeneratedTitle, titleLanguageMismatch, titlePrompts } from './titles.js';
30
+ import { WORKSPACE_BUSY_MESSAGE } from '../workspace/git.js';
30
31
  import { str } from '../util.js';
31
32
  export const CHAT_LOCK_WAIT_SECONDS = 2;
32
33
  const IMAGE_MODE_TIMEOUT_MS = 15_000;
33
34
  const TERMINAL_SSE_VISIBLE_MESSAGE_LIMIT = 80;
34
35
  /** How long a user's message waits for a stopped background turn to unwind before its own turn is admitted. */
35
36
  const BACKGROUND_UNWIND_WAIT_MS = 30_000;
37
+ 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.' };
38
+ /** `compressionExhausted`: the Agent flagged the turn itself, so its wording does not matter. */
36
39
  export function classifyProviderError(errStr, opts = {}) {
37
40
  const lower = errStr.toLowerCase();
41
+ if (opts.compressionExhausted)
42
+ return COMPRESSION_EXHAUSTED;
38
43
  if (opts.condition === 'credential_missing')
39
44
  return { label: 'Authentication failed', type: 'auth_mismatch', hint: 'The selected model may not be supported by your configured provider or your API key is invalid. Run `hermes model` in your terminal to update credentials, then restart the WebUI.' };
40
45
  const cancelled = ['cancelled by user', 'canceled by user', 'user cancelled', 'user canceled', 'task cancelled', 'task canceled', 'cancellederror'].some((k) => lower.includes(k));
@@ -60,7 +65,7 @@ export function classifyProviderError(errStr, opts = {}) {
60
65
  if (notFound)
61
66
  return { label: 'Model not found', type: 'model_not_found', hint: 'The selected model was not found by the provider. Check the model ID in Settings or run `hermes model` to verify it exists for your provider.' };
62
67
  if (compressionExhausted)
63
- return { 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.' };
68
+ return COMPRESSION_EXHAUSTED;
64
69
  if (opts.silentFailure)
65
70
  return { label: 'No response from provider', type: 'no_response', hint: 'The provider returned no content and no error. This often means a usage/rate limit was hit silently. Check provider status, switch providers via `hermes model`, or try again in a moment.' };
66
71
  return { label: 'Error', type: 'error', hint: '' };
@@ -180,6 +185,8 @@ export class TurnRunner {
180
185
  return { error: 'Web is updating. Retry after it restarts.', _status: 503 };
181
186
  if (this.deps.profileDeleting?.(s.profile ?? null))
182
187
  return { error: `Profile '${str(s.profile)}' is being deleted.`, _status: 409 };
188
+ if (this.deps.workspaceBusy?.(opts.workspace))
189
+ return { error: WORKSPACE_BUSY_MESSAGE, _status: 409 };
183
190
  const locked = s.active_stream_id;
184
191
  if (locked) {
185
192
  if (this.registry.liveIds.has(locked) || this.registry.activeRuns.has(locked))
@@ -424,15 +431,20 @@ export class TurnRunner {
424
431
  return;
425
432
  }
426
433
  case 'tool_complete': {
427
- // TAL-313: the server decides failure from the sidecar's raw result, which never leaves the server.
434
+ // TAL-313: the server decides failure from the sidecar's raw result, which never leaves the server; TAL-315: so
435
+ // are its display sections, which keep the stderr and exit code the flat preview drops.
428
436
  const { raw_result: rawResult, ...complete } = data;
429
- complete.is_error = toolOutcome(rawResult).is_error;
437
+ const outcome = toolOutcome(rawResult);
438
+ complete.is_error = outcome.is_error;
439
+ if (rawResult !== undefined)
440
+ complete.result_view = outcome.result_view;
430
441
  const tc = liveToolCalls[completedToolIndex(liveToolCalls, str(data.tid), data.name)];
431
442
  if (tc) {
432
443
  const startedAt = toolStartedAt.get(tc);
433
444
  if (startedAt !== undefined)
434
445
  complete.duration = Math.round(Math.max(0, deps.now() - startedAt) * 1000) / 1000;
435
- Object.assign(tc, { done: true, snippet: data.preview, is_error: complete.is_error, duration: complete.duration ?? null });
446
+ // 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 } : {}) });
436
448
  }
437
449
  const id = (tc && toolIds.get(tc)) || str(data.tid) || mintToolId();
438
450
  this.lastCompletedTool.set(streamId, id);
@@ -447,6 +459,14 @@ export class TurnRunner {
447
459
  put('approval', { ...(pending ?? data), pending_count });
448
460
  return;
449
461
  }
462
+ // TAL-514: the Agent stopped waiting on an approval (answer, timeout, or interrupt); withdraw its copy.
463
+ case 'approval_resolved': {
464
+ if (!str(data.approval_id))
465
+ return;
466
+ deps.pending.resolveApproval(sessionId, str(data.approval_id));
467
+ put(...this.approvalHeadFrame(sessionId));
468
+ return;
469
+ }
450
470
  case 'clarify': {
451
471
  deps.pending.submitClarify(sessionId, { ...data, session_id: sessionId, kind: 'clarify' });
452
472
  const head = deps.pending.clarifyHeadFrame(sessionId);
@@ -478,25 +498,6 @@ export class TurnRunner {
478
498
  put('cancel', this.cancelFrame(sessionId));
479
499
  return;
480
500
  }
481
- if (opts.ephemeral) {
482
- let answer = '';
483
- for (let i = result.messages.length - 1; i >= 0; i -= 1) {
484
- const m = result.messages[i];
485
- if (m.role === 'assistant') {
486
- answer = str(m.content);
487
- break;
488
- }
489
- }
490
- opts.onDone?.(answer);
491
- // Python `_ephemeral_session_payload`: only role and content leave the server for a btw turn.
492
- 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' });
493
- try {
494
- rmSync(deps.store.pathFor(sessionId), { force: true });
495
- }
496
- catch { /* ignore */ }
497
- deps.store.sessions.delete(sessionId);
498
- return;
499
- }
500
501
  // Writeback only while this stream still owns the session (Python `_stream_writeback_is_current`).
501
502
  let current;
502
503
  try {
@@ -518,12 +519,14 @@ export class TurnRunner {
518
519
  // Python's second chance: a turn that emitted no new row still counts when the merged transcript it produced
519
520
  // ends on a final answer (the current user row or trailing tool activity makes it "lacking").
520
521
  const mergedForCheck = () => mergeDisplayMessagesAfterAgentResult(previousMessages, previousContext, resultMessages, msgText, { source: opts.source ?? 'webui', activeTurnToken, now: deps.now(), turnId: streamId, attachments: opts.attachments ?? [] });
521
- const assistantAdded = assistantReplyAddedAfterCurrentTurn(resultMessages, previousContext, msgText) || !sessionLacksFinalAssistantAnswer(mergedForCheck());
522
+ // TAL-512: a btw answer must be a reply this turn added: everything before it is the parent's replayed history.
523
+ const assistantAdded = assistantReplyAddedAfterCurrentTurn(resultMessages, previousContext, msgText) || (!opts.ephemeral && !sessionLacksFinalAssistantAnswer(mergedForCheck()));
522
524
  const lastErr = result.error ?? capturedTerminalError ?? '';
523
- // Python `_turn_transcript_lacks_final_assistant_answer`: a partial result with no final answer is a silent failure even if tokens streamed.
524
- const stalePartial = result.result_status === 'partial' && !assistantAdded;
525
- if (result.status === 'error' || (!assistantAdded && !tokenSent) || stalePartial) {
526
- const classification = classifyProviderError(lastErr, { silentFailure: !lastErr });
525
+ // Python `_agent_result_terminal_failure`: the Agent's failed, partial, or compression-exhausted result ends the turn
526
+ // even after text streamed; only a partial with no error text and a final answer still completes.
527
+ const agentFailed = result.failed || result.compression_exhausted || (result.partial && (Boolean(lastErr) || !assistantAdded));
528
+ if (result.status === 'error' || agentFailed || (!assistantAdded && (!tokenSent || opts.ephemeral))) {
529
+ const classification = classifyProviderError(lastErr, { silentFailure: !lastErr, compressionExhausted: result.compression_exhausted });
527
530
  const errStr = lastErr || `${classification.label}.`;
528
531
  const payload = providerErrorPayload(errStr, classification.type, classification.hint, deps.redactEnabled());
529
532
  // Settle the steers first so the persisted turn carries every consumed one; the Agent's pending text is sent next.
@@ -532,7 +535,9 @@ export class TurnRunner {
532
535
  const { events: steerEvents, leftovers } = this.finalizeSteers(streamId, str(result.pending_steer), 'followup');
533
536
  followUp = leftovers;
534
537
  this.persistError(s, streamId, classification.label, payload, activeTurnToken);
535
- payload.session = redactSessionData(this.terminalSessionPayload(s), deps.redactEnabled());
538
+ // TAL-512: a btw error carries no session: its journaled frame must not keep a copy of the parent conversation.
539
+ if (!opts.ephemeral)
540
+ payload.session = redactSessionData(this.terminalSessionPayload(s), deps.redactEnabled());
536
541
  payload.session_id = s.session_id;
537
542
  payload.old_session_id = sessionId;
538
543
  for (const [event, data] of steerEvents)
@@ -541,18 +546,32 @@ export class TurnRunner {
541
546
  failed = true;
542
547
  return;
543
548
  }
549
+ // TAL-512: a btw turn passes the failure check above first, so its last assistant row is this turn's reply.
550
+ if (opts.ephemeral) {
551
+ let answer = '';
552
+ for (let i = result.messages.length - 1; i >= 0; i -= 1) {
553
+ const m = result.messages[i];
554
+ if (m.role === 'assistant') {
555
+ answer = str(m.content);
556
+ break;
557
+ }
558
+ }
559
+ opts.onDone?.(answer);
560
+ // 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' });
562
+ try {
563
+ rmSync(deps.store.pathFor(sessionId), { force: true });
564
+ }
565
+ catch { /* ignore */ }
566
+ deps.store.sessions.delete(sessionId);
567
+ return;
568
+ }
544
569
  // ── settle the transcript ──
545
570
  // The Agent's last pending-steer text settles the remaining steers before the turn is written back.
546
571
  await this.steerRewrites.get(streamId);
547
572
  const { events: steerEvents, leftovers } = this.finalizeSteers(streamId, result.pending_steer, 'followup');
548
573
  s.messages = mergeDisplayMessagesAfterAgentResult(previousMessages, previousContext, resultMessages, msgText, { source: opts.source ?? 'webui', activeTurnToken, now: deps.now(), turnId: streamId, attachments: opts.attachments ?? [] });
549
574
  s.context_messages = dedupeContext(resultMessages);
550
- for (const m of s.messages) {
551
- if (m.role !== 'assistant')
552
- continue;
553
- if (typeof m.content === 'string')
554
- m.content = stripXmlToolCalls(m.content);
555
- }
556
575
  if (result.compressed) {
557
576
  s.compression_anchor_visible_idx = Math.max(0, previousMessages.length - 1);
558
577
  put('compressed', { session_id: sessionId, old_session_id: sessionId, new_session_id: sessionId, continuation_session_id: sessionId, message: 'Compression finished' });
@@ -611,12 +630,12 @@ export class TurnRunner {
611
630
  asstIdx += 1;
612
631
  if (turnIdx < prevAssistants)
613
632
  continue;
614
- const existing = str(m.reasoning);
633
+ // The settled row keeps its own fields: only its prose and its inline thinking (into `reasoning`) are rewritten.
615
634
  if (typeof m.content === 'string' && m.content) {
616
- const [content, merged] = splitThinkingFromContent(m.content, existing);
635
+ const [content, inline] = splitDisplayText(m.content);
617
636
  m.content = content;
618
- if (merged)
619
- m.reasoning = merged;
637
+ if (inline)
638
+ m.reasoning = joinReasoning([reasoningFieldsText({ reasoning: m.reasoning }), inline]);
620
639
  }
621
640
  if (turnIdx === prevAssistants && reasoning.trim() && !m.reasoning)
622
641
  m.reasoning = reasoning.trim();
@@ -690,22 +709,14 @@ export class TurnRunner {
690
709
  this.registry.activeRuns.delete(streamId);
691
710
  this.settledStreams.add(streamId);
692
711
  this.startSteerFollowUp(sessionId, leftovers);
693
- await this.backgroundTitle(s, put);
712
+ // Title work and the goal judge run side by side; `stream_end` follows both, so their frames reach the stream.
713
+ const work = await Promise.allSettled([this.backgroundTitle(s, put), this.continueGoal(s, streamId, put)]);
714
+ put('stream_end', { session_id: sessionId });
715
+ for (const r of work)
716
+ if (r.status === 'rejected')
717
+ throw r.reason;
694
718
  // Python: the last non-error assistant reply with content, else "(no answer produced)".
695
- if (opts.onDone) {
696
- let answer = '';
697
- for (let i = s.messages.length - 1; i >= 0; i -= 1) {
698
- const m = s.messages[i];
699
- if (m.role !== 'assistant' || m._error)
700
- continue;
701
- const text = messageText(m.content).trim();
702
- if (text) {
703
- answer = text;
704
- break;
705
- }
706
- }
707
- opts.onDone(answer || '(no answer produced)');
708
- }
719
+ opts.onDone?.(lastAnswer(s.messages) || '(no answer produced)');
709
720
  }
710
721
  catch (error) {
711
722
  if (settledAt.value && !(error instanceof SidecarError)) {
@@ -737,7 +748,8 @@ export class TurnRunner {
737
748
  for (const [event, data] of this.takeSteerEventsBefore(streamId, 'apperror'))
738
749
  put(event, data);
739
750
  this.persistError(current, streamId, classification.label, payload, activeTurnToken);
740
- payload.session = redactSessionData(this.terminalSessionPayload(current), deps.redactEnabled());
751
+ if (!opts.ephemeral)
752
+ payload.session = redactSessionData(this.terminalSessionPayload(current), deps.redactEnabled());
741
753
  }
742
754
  }
743
755
  catch (persistError) {
@@ -835,11 +847,13 @@ export class TurnRunner {
835
847
  * msg_limit=` (renderable rows, limited payload shape, windowed tool calls, todo state) with the full count.
836
848
  */
837
849
  terminalSessionPayload(s) {
838
- const payload = withSessionWireFlags(s.compact({ contextLengthFor: this.deps.service().deps.contextLengthFor }), this.registry.liveIds);
850
+ const payload = withSessionWireFlags(s.compact({ contextLengthFor: this.deps.service().deps.contextLengthFor, modelOptionFor: this.deps.service().deps.modelOptionFor }), this.registry.liveIds);
839
851
  payload.assistant_name = this.deps.service().assistantName(s);
840
- const scened = withBodyExcerpts(this.deps.service().backgroundLinked(s, hydrateAnchorActivityScenes(withToolCallOutcomes(withBackgroundUpdates(withTurnIds(withAttachmentObjects(s.messages)), s), s.tool_calls, s.active_stream_id), s.anchor_activity_scenes, { activeTurnId: s.active_stream_id, clipToolResults: true })), s.active_stream_id);
852
+ payload.workspace_name = this.deps.service().workspaceNames()(s);
853
+ const service = this.deps.service();
854
+ const scened = service.backgroundLinked(s, hydrateAnchorActivityScenes(withToolCallOutcomes(withBackgroundUpdates(withMarkerKinds(withTurnIds(withAttachmentObjects(s.messages))), s), s.tool_calls, s.active_stream_id), s.anchor_activity_scenes, { activeTurnId: s.active_stream_id, clipToolResults: true }));
841
855
  const [window, offset] = messageWindowForDisplay(scened, TERMINAL_SSE_VISIBLE_MESSAGE_LIMIT, null);
842
- const limited = messagesForLimitedPayload(window);
856
+ const limited = messagesForLimitedPayload(withBodyExcerpts(withDisplayMedia(window, service.mediaProjector(s)), s.active_stream_id));
843
857
  payload.messages = limited;
844
858
  payload.message_count = s.messages.length;
845
859
  payload._messages_offset = offset;
@@ -1131,65 +1145,60 @@ export class TurnRunner {
1131
1145
  const status = (status, reason = '', title = '', rawPreview = '') => {
1132
1146
  put('title_status', { session_id: sessionId, status, ...(reason ? { reason } : {}), ...(title ? { title } : {}), ...(rawPreview ? { raw_preview: rawPreview } : {}) });
1133
1147
  };
1134
- try {
1135
- if (!eligible || (s.llm_title_generated && !invalidExisting) || !userText || !assistantText)
1136
- return;
1137
- if (s.manual_title) {
1138
- status('skipped', 'manual_title', placeholder);
1139
- return;
1148
+ if (!eligible || (s.llm_title_generated && !invalidExisting) || !userText || !assistantText)
1149
+ return;
1150
+ if (s.manual_title) {
1151
+ status('skipped', 'manual_title', placeholder);
1152
+ return;
1153
+ }
1154
+ if (!this.deps.titleGenerationEnabled()) {
1155
+ status('skipped', 'title_generation_disabled', placeholder);
1156
+ return;
1157
+ }
1158
+ const generated = await this.llmTitle(s, userText, assistantText);
1159
+ const { status: llmStatus, rawPreview } = generated;
1160
+ let next = generated.title;
1161
+ let source = llmStatus;
1162
+ if (!next) {
1163
+ const fallback = fallbackTitleFromExchange(userText, assistantText);
1164
+ if (fallback && !isGenericFallbackTitle(fallback)) {
1165
+ next = fallback;
1166
+ source = 'fallback';
1140
1167
  }
1141
- if (!this.deps.titleGenerationEnabled()) {
1142
- status('skipped', 'title_generation_disabled', placeholder);
1168
+ }
1169
+ const fallbackReason = source === 'fallback' && llmStatus ? `local_summary:${llmStatus}` : 'local_summary';
1170
+ let current = s;
1171
+ try {
1172
+ current = this.deps.store.get(sessionId);
1173
+ }
1174
+ catch {
1175
+ current = s;
1176
+ }
1177
+ let effective = str(current.title).trim();
1178
+ let wrote = false;
1179
+ if (next) {
1180
+ const stillAuto = effective === placeholder || ['Untitled', 'New Chat', ''].includes(effective) || effective === titleFrom(current.messages, '') || looksInvalidGeneratedTitle(current.title) || defaultTitle(current);
1181
+ if (current.manual_title || !stillAuto) {
1182
+ status('skipped', 'manual_title', effective);
1143
1183
  return;
1144
1184
  }
1145
- const generated = await this.llmTitle(s, userText, assistantText);
1146
- const { status: llmStatus, rawPreview } = generated;
1147
- let next = generated.title;
1148
- let source = llmStatus;
1149
- if (!next) {
1150
- const fallback = fallbackTitleFromExchange(userText, assistantText);
1151
- if (fallback && !isGenericFallbackTitle(fallback)) {
1152
- next = fallback;
1153
- source = 'fallback';
1154
- }
1155
- }
1156
- const fallbackReason = source === 'fallback' && llmStatus ? `local_summary:${llmStatus}` : 'local_summary';
1157
- let current = s;
1158
- try {
1159
- current = this.deps.store.get(sessionId);
1160
- }
1161
- catch {
1162
- current = s;
1163
- }
1164
- let effective = str(current.title).trim();
1165
- let wrote = false;
1166
- if (next) {
1167
- const stillAuto = effective === placeholder || ['Untitled', 'New Chat', ''].includes(effective) || effective === titleFrom(current.messages, '') || looksInvalidGeneratedTitle(current.title) || defaultTitle(current);
1168
- if (current.manual_title || !stillAuto) {
1169
- status('skipped', 'manual_title', effective);
1170
- return;
1171
- }
1172
- if (next !== effective) {
1173
- current.title = next;
1174
- markSessionTitleGenerated(current);
1175
- this.deps.store.save(current, { touchUpdatedAt: false });
1176
- // Python `sync_session_title` after generation: the state.db row follows when `sync_to_insights` is on.
1177
- await this.deps.syncTitle?.(current);
1178
- this.deps.events.publish('title', { profile: current.profile, sessionId });
1179
- effective = next;
1180
- wrote = true;
1181
- }
1182
- }
1183
- if (wrote) {
1184
- status(source, source === 'fallback' ? fallbackReason : llmStatus, effective, rawPreview);
1185
- put('title', { session_id: sessionId, title: effective });
1185
+ if (next !== effective) {
1186
+ current.title = next;
1187
+ markSessionTitleGenerated(current);
1188
+ this.deps.store.save(current, { touchUpdatedAt: false });
1189
+ // Python `sync_session_title` after generation: the state.db row follows when `sync_to_insights` is on.
1190
+ await this.deps.syncTitle?.(current);
1191
+ this.deps.events.publish('title', { profile: current.profile, sessionId });
1192
+ effective = next;
1193
+ wrote = true;
1186
1194
  }
1187
- else
1188
- status('skipped', source || 'unchanged', effective, rawPreview);
1189
1195
  }
1190
- finally {
1191
- put('stream_end', { session_id: sessionId });
1196
+ if (wrote) {
1197
+ status(source, source === 'fallback' ? fallbackReason : llmStatus, effective, rawPreview);
1198
+ put('title', { session_id: sessionId, title: effective });
1192
1199
  }
1200
+ else
1201
+ status('skipped', source || 'unchanged', effective, rawPreview);
1193
1202
  }
1194
1203
  // ── cancel / steer ───────────────────────────────────────────────────────
1195
1204
  /**
@@ -1503,6 +1512,60 @@ export class TurnRunner {
1503
1512
  }
1504
1513
  this.deps.log(`[webui] WARNING: steer follow-up for ${sessionId} not started (${String(started._status)}): ${str(started.error)}`);
1505
1514
  }
1515
+ /**
1516
+ * TAL-396 (Agent gateway `_post_turn_goal_continuation`): after every turn settles, the Agent's GoalManager judges it
1517
+ * while a goal is active and the server starts the continuation turn it asks for. Any turn counts, so a resumed or
1518
+ * persisted goal picks up from the next user message. A user turn that took the session meanwhile runs first and is
1519
+ * judged next. A stopped or failed turn never gets here, so the run ends there.
1520
+ */
1521
+ async continueGoal(s, streamId, put) {
1522
+ const sidecar = this.deps.sidecar();
1523
+ const lastResponse = lastAnswer(s.messages);
1524
+ // The Agent never drives a goal from an empty reply.
1525
+ if (!sidecar || !lastResponse)
1526
+ return;
1527
+ const sessionId = s.session_id;
1528
+ // Only a known goal turn shows progress up front; any other turn shows nothing unless a goal turns out to be active.
1529
+ const goalTurn = this.registry.goalRelated.has(streamId);
1530
+ if (goalTurn)
1531
+ put('goal', { session_id: sessionId, state: 'evaluating', message: 'Evaluating goal progress…', message_key: 'goal_evaluating_progress' });
1532
+ let decision;
1533
+ try {
1534
+ decision = await sidecar.call('goals.evaluate', { session_id: sessionId, profile_home: this.deps.profileHome(s.profile), last_response: lastResponse, user_initiated: true });
1535
+ }
1536
+ catch (error) {
1537
+ this.deps.log(`[webui] WARNING: goal evaluation failed for ${sessionId}: ${error.message}`);
1538
+ if (goalTurn)
1539
+ put('goal', { session_id: sessionId, state: 'idle', decision: 'error', message: '' });
1540
+ return;
1541
+ }
1542
+ if (!goalTurn && decision.verdict === 'inactive')
1543
+ return;
1544
+ const prompt = str(decision.continuation_prompt).trim();
1545
+ const proceed = decision.should_continue && Boolean(prompt);
1546
+ const status = { message: decision.message, message_key: decision.message_key, message_args: decision.message_args ?? [], decision: decision.verdict };
1547
+ put('goal', { session_id: sessionId, state: proceed ? 'continuing' : 'idle', ...status });
1548
+ if (!proceed)
1549
+ return;
1550
+ let current;
1551
+ try {
1552
+ current = this.deps.store.get(sessionId);
1553
+ }
1554
+ catch {
1555
+ return;
1556
+ }
1557
+ const started = this.start(current, { msg: prompt, attachments: [], workspace: current.workspace, model: current.model, modelProvider: current.model_provider, goalRelated: true });
1558
+ if (started._status === 409 && started.active_stream_id) {
1559
+ this.registry.goalRelated.add(started.active_stream_id);
1560
+ return;
1561
+ }
1562
+ if (started._status !== undefined && started._status >= 400) {
1563
+ this.deps.log(`[webui] WARNING: goal continuation for ${sessionId} not started (${String(started._status)}): ${str(started.error)}`);
1564
+ put('goal', { session_id: sessionId, state: 'idle', decision: 'error', message: '' });
1565
+ return;
1566
+ }
1567
+ put('goal_continue', { session_id: sessionId, state: 'continuing', continuation_prompt: prompt, text: prompt, stream_id: str(started.stream_id), ...status });
1568
+ }
1506
1569
  /** The Agent's pending steer text settles which steers it has taken: those become `steer_consumed` and their rows. */
1507
1570
  settleConsumedSteers(sessionId, streamId, pendingText) {
1508
1571
  for (const record of this.takeConsumedSteers(streamId, pendingText, { keepLeftovers: true }))
@@ -1645,6 +1708,7 @@ export class TurnRunner {
1645
1708
  }
1646
1709
  const profileHome = this.deps.profileHome(s?.profile ?? null);
1647
1710
  let resolved = false;
1711
+ let withdrawn = false;
1648
1712
  if (entry) {
1649
1713
  if (!sidecar)
1650
1714
  return relayFailure('not running');
@@ -1656,8 +1720,13 @@ export class TurnRunner {
1656
1720
  this.deps.log(`[webui] approval relay failed for ${sessionId}: ${error.message}`);
1657
1721
  return relayFailure(str(error.message));
1658
1722
  }
1659
- if (resolved && entry)
1723
+ if (resolved)
1660
1724
  this.deps.pending.resolveApproval(sessionId, str(entry.approval_id));
1725
+ else {
1726
+ // TAL-514: a rejected answer can mean the Agent already stopped waiting on this card; drop what it no longer holds.
1727
+ await this.reconcileApprovals(sessionId, sidecar);
1728
+ withdrawn = !this.deps.pending.hasApprovalId(sessionId, str(entry.approval_id));
1729
+ }
1661
1730
  }
1662
1731
  let yoloEnabled;
1663
1732
  if (enableYolo) {
@@ -1680,10 +1749,34 @@ export class TurnRunner {
1680
1749
  yoloEnabled = true;
1681
1750
  this.deps.pending.clearApprovals(sessionId);
1682
1751
  }
1752
+ if (withdrawn)
1753
+ return { ok: true, choice, stale_cleared: true, pending_count: this.deps.pending.approvalPending(sessionId).pending_count, ...(enableYolo ? { yolo_enabled: yoloEnabled } : {}) };
1683
1754
  if (!found && !this.deps.pending.hasPendingApproval(sessionId))
1684
1755
  return { ok: true, choice, stale_cleared: true, ...(enableYolo ? { yolo_enabled: yoloEnabled } : {}) };
1685
1756
  return { ok: resolved || !approvalId, choice, ...(enableYolo && (resolved || !approvalId) ? { yolo_enabled: yoloEnabled } : {}) };
1686
1757
  }
1758
+ /** The live chat frame after approvals were withdrawn: the new queue head, or `approval_cleared` once none is left. */
1759
+ approvalHeadFrame(sessionId) {
1760
+ const { pending, pending_count } = this.deps.pending.approvalPending(sessionId);
1761
+ return pending ? ['approval', { ...pending, pending_count }] : ['approval_cleared', { session_id: sessionId, pending_count: 0 }];
1762
+ }
1763
+ /**
1764
+ * Drop mirrored approvals the Agent no longer holds; a failed read keeps them (the card stays answerable). Only
1765
+ * entries queued before the read are candidates: one the Agent parks meanwhile can be missing from its snapshot.
1766
+ */
1767
+ async reconcileApprovals(sessionId, sidecar) {
1768
+ const before = this.deps.pending.approvalRequestIds(sessionId);
1769
+ let live;
1770
+ try {
1771
+ live = new Set((await sidecar.call('approval.pending', { session_id: sessionId })).pending.map((p) => str(p.request_id)));
1772
+ }
1773
+ catch (error) {
1774
+ this.deps.log(`[webui] approval reconcile failed for ${sessionId}: ${error.message}`);
1775
+ return;
1776
+ }
1777
+ if (this.deps.pending.dropApprovals(sessionId, new Set(before.filter((id) => !live.has(id)))).length)
1778
+ this.emitToSession(sessionId, ...this.approvalHeadFrame(sessionId));
1779
+ }
1687
1780
  withYoloLock(sessionId, fn) {
1688
1781
  const run = (this.yoloChains.get(sessionId) ?? Promise.resolve()).then(fn);
1689
1782
  const settled = run.catch(() => undefined).then(() => { if (this.yoloChains.get(sessionId) === settled)
@@ -1736,6 +1829,18 @@ export class TurnRunner {
1736
1829
  return { ok: true, response };
1737
1830
  }
1738
1831
  }
1832
+ /** The turn's answer: the last non-error assistant reply with content. */
1833
+ function lastAnswer(messages) {
1834
+ for (let i = messages.length - 1; i >= 0; i -= 1) {
1835
+ const m = messages[i];
1836
+ if (m.role !== 'assistant' || m._error)
1837
+ continue;
1838
+ const text = messageText(m.content).trim();
1839
+ if (text)
1840
+ return text;
1841
+ }
1842
+ return '';
1843
+ }
1739
1844
  function previousStartedAt(s, run) {
1740
1845
  return typeof s.pending_started_at === 'number' && s.pending_started_at > 0 ? s.pending_started_at : (run?.started_at ?? Date.now() / 1000);
1741
1846
  }