@maudecode/talaria-web 0.0.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/_release.json +20 -0
  2. package/dist/api/chat-router.d.ts +54 -1
  3. package/dist/api/sessions-router.d.ts +296 -1
  4. package/dist/api/settings-router.d.ts +8 -0
  5. package/dist/api/settings-router.js +13 -5
  6. package/dist/api/settings-router.js.map +1 -1
  7. package/dist/api/sse-routes.js +1 -1
  8. package/dist/api/sse-routes.js.map +1 -1
  9. package/dist/api/tools-router.d.ts +91 -0
  10. package/dist/api/tools-router.js +4 -4
  11. package/dist/api/tools-router.js.map +1 -1
  12. package/dist/app.d.ts +449 -2
  13. package/dist/bin/talaria-web.js +4 -1
  14. package/dist/bin/talaria-web.js.map +1 -1
  15. package/dist/cli/supervise.js +2 -1
  16. package/dist/cli/supervise.js.map +1 -1
  17. package/dist/runtime.js +8 -2
  18. package/dist/runtime.js.map +1 -1
  19. package/dist/sessions/anchor.d.ts +57 -5
  20. package/dist/sessions/anchor.js +368 -32
  21. package/dist/sessions/anchor.js.map +1 -1
  22. package/dist/sessions/merge.d.ts +6 -0
  23. package/dist/sessions/merge.js +35 -4
  24. package/dist/sessions/merge.js.map +1 -1
  25. package/dist/sessions/service.d.ts +2 -0
  26. package/dist/sessions/service.js +31 -16
  27. package/dist/sessions/service.js.map +1 -1
  28. package/dist/sessions/session.js +1 -1
  29. package/dist/sessions/session.js.map +1 -1
  30. package/dist/sessions/turn.d.ts +14 -0
  31. package/dist/sessions/turn.js +147 -20
  32. package/dist/sessions/turn.js.map +1 -1
  33. package/dist/sessions/window.d.ts +1 -0
  34. package/dist/sessions/window.js +1 -1
  35. package/dist/sessions/window.js.map +1 -1
  36. package/dist/settings.js +5 -0
  37. package/dist/settings.js.map +1 -1
  38. package/dist/sidecar/client.d.ts +1 -1
  39. package/dist/sidecar/client.js +15 -4
  40. package/dist/sidecar/client.js.map +1 -1
  41. package/dist/sidecar/fake.js +3 -2
  42. package/dist/sidecar/fake.js.map +1 -1
  43. package/dist/tools/crons.js +55 -43
  44. package/dist/tools/crons.js.map +1 -1
  45. package/dist/tools/updates.d.ts +51 -13
  46. package/dist/tools/updates.js +362 -114
  47. package/dist/tools/updates.js.map +1 -1
  48. package/package.json +2 -2
  49. package/sidecar/agent_dependency.json +4 -3
  50. package/sidecar/talaria_sidecar/home.py +29 -20
  51. package/sidecar/talaria_sidecar/methods/config.py +5 -3
  52. package/sidecar/talaria_sidecar/runtime.py +3 -9
  53. package/static/dist/FILES.txt +116 -116
  54. package/static/dist/assets/AppShell-hikzkcql.js +1 -0
  55. package/static/dist/assets/ChatPage-ofqn89h9.js +21 -0
  56. package/static/dist/assets/{HubRoute-qs9vzuh0.js → HubRoute-f2zrs9si.js} +1 -1
  57. package/static/dist/assets/{Markdown-bt9bqd6a.js → Markdown-ftyfzds0.js} +7 -7
  58. package/static/dist/assets/RightPanel-febk3dkd.js +1 -0
  59. package/static/dist/assets/Select-lvx8gihl.js +1 -0
  60. package/static/dist/assets/SessionListPanel-o70ljmik.js +1 -0
  61. package/static/dist/assets/SettingsLayout-pdpmhy1o.js +1 -0
  62. package/static/dist/assets/States-i46tz8an.js +1 -0
  63. package/static/dist/assets/{Toaster-jtn59pjm.js → Toaster-fklyeeyh.js} +1 -1
  64. package/static/dist/assets/_app-j3rors25.js +1 -0
  65. package/static/dist/assets/_app.ext._extensionId-c0ey5ec9.js +1 -0
  66. package/static/dist/assets/_app.index-coy9jrto.js +1 -0
  67. package/static/dist/assets/_app.insights-ebd2k0i5.js +1 -0
  68. package/static/dist/assets/_app.kanban-e2p7ypi5.js +1 -0
  69. package/static/dist/assets/_app.logs-ml21mfyz.js +1 -0
  70. package/static/dist/assets/_app.memory-lhu5mm47.js +1 -0
  71. package/static/dist/assets/_app.profiles-egx0fgjo.js +1 -0
  72. package/static/dist/assets/{_app.session._sessionId-kevi4qd2.js → _app.session._sessionId-n1ggcf5e.js} +1 -1
  73. package/static/dist/assets/_app.settings-d0vqtyid.js +1 -0
  74. package/static/dist/assets/_app.settings._section-cnzj6pli.js +1 -0
  75. package/static/dist/assets/_app.skills-kkug8obu.js +1 -0
  76. package/static/dist/assets/{_app.tasks-b99d98fo.js → _app.tasks-nf285d2o.js} +2 -2
  77. package/static/dist/assets/_app.todos-dxalqp1e.js +1 -0
  78. package/static/dist/assets/_app.workspaces-bqtduujs.js +1 -0
  79. package/static/dist/assets/{abnfDiagram-VCTEODGH-hvrr25lb.js → abnfDiagram-VCTEODGH-i4bgzu83.js} +1 -1
  80. package/static/dist/assets/architecture-7GRP2DOG-o8zqevhv.js +1 -0
  81. package/static/dist/assets/{architectureDiagram-5GKGNRK7-b3r0z8sq.js → architectureDiagram-5GKGNRK7-mx21lwqa.js} +1 -1
  82. package/static/dist/assets/{blockDiagram-I7D4REHJ-hd3gw0cj.js → blockDiagram-I7D4REHJ-o1hhchxv.js} +1 -1
  83. package/static/dist/assets/{c4Diagram-7LVT6UL2-jlmbjiji.js → c4Diagram-7LVT6UL2-jygqtvd8.js} +1 -1
  84. package/static/dist/assets/channel-mw8l793e.js +1 -0
  85. package/static/dist/assets/{chunk-4HAMMTFA-oydbpsin.js → chunk-4HAMMTFA-hjcpubgk.js} +1 -1
  86. package/static/dist/assets/{chunk-75Z2AOVW-j633otj1.js → chunk-75Z2AOVW-eibnex6m.js} +1 -1
  87. package/static/dist/assets/{chunk-DU6HZSFF-ods2l027.js → chunk-DU6HZSFF-k1tp3qnp.js} +3 -3
  88. package/static/dist/assets/{chunk-F27PBJKO-ln4n2cwt.js → chunk-F27PBJKO-lvcgn16f.js} +1 -1
  89. package/static/dist/assets/{chunk-GMAD6QVW-fpz133eh.js → chunk-GMAD6QVW-ldlzruv1.js} +1 -1
  90. package/static/dist/assets/{chunk-GVQU2GXP-abs9ixiy.js → chunk-GVQU2GXP-hs1mva9d.js} +1 -1
  91. package/static/dist/assets/{chunk-IMKFNOWR-j9vukdy1.js → chunk-IMKFNOWR-o7eqx3yb.js} +1 -1
  92. package/static/dist/assets/{chunk-L3NEJ4N5-cfyw87r3.js → chunk-L3NEJ4N5-bp1ofl70.js} +1 -1
  93. package/static/dist/assets/{chunk-OSK3NFVY-baja78wr.js → chunk-OSK3NFVY-o8m6mdz5.js} +1 -1
  94. package/static/dist/assets/{chunk-P2QGCYS3-g66xby8h.js → chunk-P2QGCYS3-kx83fti2.js} +1 -1
  95. package/static/dist/assets/{chunk-POPQ4Y6H-osqu864t.js → chunk-POPQ4Y6H-mwl99e9y.js} +1 -1
  96. package/static/dist/assets/{chunk-PWAF6VOD-egejasv1.js → chunk-PWAF6VOD-oriuokxy.js} +1 -1
  97. package/static/dist/assets/{chunk-SHT3W25Y-l014h8zz.js → chunk-SHT3W25Y-ab5cf00v.js} +1 -1
  98. package/static/dist/assets/{chunk-SVP7TREG-n3z10bp3.js → chunk-SVP7TREG-kqdrpr17.js} +1 -1
  99. package/static/dist/assets/{chunk-TICWLB2K-bzozkjc0.js → chunk-TICWLB2K-kfbnjabk.js} +1 -1
  100. package/static/dist/assets/classDiagram-ZZMXUADV-egajr1wp.js +1 -0
  101. package/static/dist/assets/classDiagram-v2-VYDZK3BY-egajr1wp.js +1 -0
  102. package/static/dist/assets/{copied-cjsttec1.js → copied-dp33szhq.js} +1 -1
  103. package/static/dist/assets/{cynefin-OW5HDTMX-elpt7h1j.js → cynefin-OW5HDTMX-mm5sj9s7.js} +1 -1
  104. package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-5mzl5pi2.js → cynefinDiagram-5FMLGOSQ-nfon5q38.js} +1 -1
  105. package/static/dist/assets/{dagre-GXQ25YYZ-ihwlhmfl.js → dagre-GXQ25YYZ-dzdp9ypb.js} +1 -1
  106. package/static/dist/assets/{diagram-S7CK7UJ4-4z90agaz.js → diagram-S7CK7UJ4-ckxzqo01.js} +1 -1
  107. package/static/dist/assets/{diagram-UQ7AKVKN-eifw0lg7.js → diagram-UQ7AKVKN-hw16h1um.js} +1 -1
  108. package/static/dist/assets/{diagram-VSXAHHWV-ksh3sjqv.js → diagram-VSXAHHWV-mu7wu2zg.js} +1 -1
  109. package/static/dist/assets/{diagram-VX7I27RA-ett72f71.js → diagram-VX7I27RA-k9uprwtj.js} +1 -1
  110. package/static/dist/assets/{diagram-Z3DM3KII-l8scp8nu.js → diagram-Z3DM3KII-csdjzrg9.js} +1 -1
  111. package/static/dist/assets/{ebnfDiagram-PWID7BFC-f0pxcmit.js → ebnfDiagram-PWID7BFC-n8mbw2jj.js} +1 -1
  112. package/static/dist/assets/endpoints-f6ngsyu5.js +64 -0
  113. package/static/dist/assets/{erDiagram-RLTQ6QDP-f47m5xh0.js → erDiagram-RLTQ6QDP-fsfe1uj0.js} +1 -1
  114. package/static/dist/assets/eventmodeling-NTZA5JFV-k3zhqcyl.js +1 -0
  115. package/static/dist/assets/flowDiagram-HODETNUW-bdxes51f.js +1 -0
  116. package/static/dist/assets/{ganttDiagram-EL5Y4UJY-bmktjdi0.js → ganttDiagram-EL5Y4UJY-gsba3iq9.js} +1 -1
  117. package/static/dist/assets/{gitGraph-4MIJSDKK-j2ueg258.js → gitGraph-4MIJSDKK-na86q1pu.js} +1 -1
  118. package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-ms6586v9.js → gitGraphDiagram-WWUBYQGX-g72ew3xz.js} +1 -1
  119. package/static/dist/assets/{highlighted-body-KPVGNVTW-ev52d2si.js → highlighted-body-KPVGNVTW-okn18cp1.js} +1 -1
  120. package/static/dist/assets/index-ab0wj9ik.css +2 -0
  121. package/static/dist/assets/index-fd27xooo.js +11 -0
  122. package/static/dist/assets/{info-A6RAGUB7-j717vyt7.js → info-A6RAGUB7-i01jeltk.js} +1 -1
  123. package/static/dist/assets/{infoDiagram-27XIBGKW-dsbpib36.js → infoDiagram-27XIBGKW-cayiv6fa.js} +1 -1
  124. package/static/dist/assets/{ishikawaDiagram-5VMMS53U-n0sz3i2d.js → ishikawaDiagram-5VMMS53U-g4rf14bv.js} +1 -1
  125. package/static/dist/assets/{journeyDiagram-3NMN7TZE-mcxz1mme.js → journeyDiagram-3NMN7TZE-fusb2424.js} +1 -1
  126. package/static/dist/assets/{kanban-definition-UXKFOSKX-n6hq2lyc.js → kanban-definition-UXKFOSKX-g831pwlr.js} +1 -1
  127. package/static/dist/assets/{lazyRouteComponent-faquioun.js → lazyRouteComponent-ol965zwk.js} +1 -1
  128. package/static/dist/assets/{line-h5xq20s4.js → line-md6em6gc.js} +1 -1
  129. package/static/dist/assets/login-kfclsqd2.js +1 -0
  130. package/static/dist/assets/mermaid-HWGCJPDP-ejk0d88t.js +1 -0
  131. package/static/dist/assets/{mermaid-parser.core-n9cuu5ue.js → mermaid-parser.core-mq3uns9m.js} +3 -3
  132. package/static/dist/assets/{mindmap-definition-YA3MSWOX-gta3u7z4.js → mindmap-definition-YA3MSWOX-oxafrned.js} +1 -1
  133. package/static/dist/assets/onboarding-fo6hnkyv.js +1 -0
  134. package/static/dist/assets/{packet-AYTQ26CC-bpzuq5n6.js → packet-AYTQ26CC-ggxyvbym.js} +1 -1
  135. package/static/dist/assets/{pegDiagram-XKGWAZYB-juye13e5.js → pegDiagram-XKGWAZYB-nk7xcqgp.js} +1 -1
  136. package/static/dist/assets/{pie-WAS4IAKB-odhb8buq.js → pie-WAS4IAKB-ito2razh.js} +1 -1
  137. package/static/dist/assets/{pieDiagram-E7YTZNPT-mgb14ksh.js → pieDiagram-E7YTZNPT-g02xexar.js} +1 -1
  138. package/static/dist/assets/{quadrantDiagram-AXDQQJYC-cg36naog.js → quadrantDiagram-AXDQQJYC-d4jn4xq5.js} +1 -1
  139. package/static/dist/assets/{radar-RG4KPBEZ-lgzfpnef.js → radar-RG4KPBEZ-nebvfdzt.js} +1 -1
  140. package/static/dist/assets/{railroad-74A4TZTK-gmglc3ua.js → railroad-74A4TZTK-ny3gebse.js} +1 -1
  141. package/static/dist/assets/railroad-abnf-HS5TGJTU-gtd38edw.js +1 -0
  142. package/static/dist/assets/railroad-ebnf-LZEXJU2U-o238oax2.js +1 -0
  143. package/static/dist/assets/railroad-peg-WCYAUIDC-c4acqp2j.js +1 -0
  144. package/static/dist/assets/{railroadDiagram-O6MQD6OU-cmxwjt73.js → railroadDiagram-O6MQD6OU-m09mmu67.js} +1 -1
  145. package/static/dist/assets/{refresh-cw-mf192q3f.js → refresh-cw-eitd7yva.js} +1 -1
  146. package/static/dist/assets/{requirementDiagram-BXWQKSXE-ir0s2mek.js → requirementDiagram-BXWQKSXE-evggpcbk.js} +1 -1
  147. package/static/dist/assets/{sankeyDiagram-P5KCCOFB-jo7jcdq0.js → sankeyDiagram-P5KCCOFB-bun89039.js} +1 -1
  148. package/static/dist/assets/{saved-g777hp1t.js → saved-culx14ii.js} +1 -1
  149. package/static/dist/assets/{search-i2kfogyx.js → search-nu6kzq55.js} +1 -1
  150. package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-n0pf6xk5.js → sequenceDiagram-WJ2MYXX4-hhs9f09o.js} +1 -1
  151. package/static/dist/assets/{settings_section_extensions_title-hgvh507l.js → settings_section_extensions_title-mj0gcozh.js} +1 -1
  152. package/static/dist/assets/share._token-k4pcnxqu.js +2 -0
  153. package/static/dist/assets/sliders-horizontal-b9ljga1o.js +1 -0
  154. package/static/dist/assets/{stateDiagram-D77RDMKH-olbrwyv9.js → stateDiagram-D77RDMKH-gi7t1tro.js} +1 -1
  155. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-jtadbol7.js +1 -0
  156. package/static/dist/assets/{swimlanes-42K2YHIH-od14zrw6.js → swimlanes-42K2YHIH-ea5urvph.js} +1 -1
  157. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-ki7weepw.js +8 -0
  158. package/static/dist/assets/{timeline-definition-24CTP7MA-l0qokmnb.js → timeline-definition-24CTP7MA-mq64rm3b.js} +1 -1
  159. package/static/dist/assets/{todoStore-la6uyqzj.js → todoStore-azvfuqbv.js} +1 -1
  160. package/static/dist/assets/{trash-bxz1k9cw.js → trash-osyo895g.js} +1 -1
  161. package/static/dist/assets/{treeView-Q6P3EWNA-hma3ip8m.js → treeView-Q6P3EWNA-cimbtxlz.js} +1 -1
  162. package/static/dist/assets/{treemap-WGGIJYW6-bghjng5m.js → treemap-WGGIJYW6-wig5gi1y.js} +1 -1
  163. package/static/dist/assets/{untitled-nfaw0q7m.js → untitled-mbwemvxf.js} +1 -1
  164. package/static/dist/assets/{useForm-k7fj2zsr.js → useForm-li4bljlv.js} +1 -1
  165. package/static/dist/assets/useLocale-kdew6auf.js +1 -0
  166. package/static/dist/assets/{vennDiagram-4TSXK5OY-bo97zf3z.js → vennDiagram-4TSXK5OY-ez63kh7n.js} +1 -1
  167. package/static/dist/assets/{wardley-WFR3VGLG-pbjl6jav.js → wardley-WFR3VGLG-r7rftz31.js} +1 -1
  168. package/static/dist/assets/{wardleyDiagram-VM6X3IG4-bmy9i09l.js → wardleyDiagram-VM6X3IG4-mvqg9eqc.js} +1 -1
  169. package/static/dist/assets/{xychartDiagram-S5SC5T6Z-eercu0mc.js → xychartDiagram-S5SC5T6Z-eqxdfc91.js} +1 -1
  170. package/static/dist/index.html +1 -1
  171. package/static/dist/sw.js +1 -1
  172. package/static/dist/assets/AppShell-gs0grlbs.js +0 -1
  173. package/static/dist/assets/ChatPage-gi1xlz52.js +0 -18
  174. package/static/dist/assets/RightPanel-dn19xu42.js +0 -1
  175. package/static/dist/assets/Select-y7wtf85g.js +0 -1
  176. package/static/dist/assets/SessionListPanel-igwdj9xr.js +0 -1
  177. package/static/dist/assets/SettingsLayout-n6mu0ghg.js +0 -1
  178. package/static/dist/assets/States-oil2issf.js +0 -1
  179. package/static/dist/assets/_app-k4jrkgfa.js +0 -1
  180. package/static/dist/assets/_app.ext._extensionId-mmf1mepm.js +0 -1
  181. package/static/dist/assets/_app.index-mb41nf00.js +0 -1
  182. package/static/dist/assets/_app.insights-mz5cep8m.js +0 -1
  183. package/static/dist/assets/_app.kanban-ocgw506i.js +0 -1
  184. package/static/dist/assets/_app.logs-lstmoju6.js +0 -1
  185. package/static/dist/assets/_app.memory-dishj9ra.js +0 -1
  186. package/static/dist/assets/_app.profiles-dynpvsio.js +0 -1
  187. package/static/dist/assets/_app.settings-c2czm9v4.js +0 -1
  188. package/static/dist/assets/_app.settings._section-exwflld7.js +0 -1
  189. package/static/dist/assets/_app.skills-j5jh2nug.js +0 -1
  190. package/static/dist/assets/_app.todos-ma8urvn3.js +0 -1
  191. package/static/dist/assets/_app.workspaces-l86cxeod.js +0 -1
  192. package/static/dist/assets/architecture-7GRP2DOG-mqft9w2p.js +0 -1
  193. package/static/dist/assets/channel-ix8cxdld.js +0 -1
  194. package/static/dist/assets/classDiagram-ZZMXUADV-c210y3il.js +0 -1
  195. package/static/dist/assets/classDiagram-v2-VYDZK3BY-c210y3il.js +0 -1
  196. package/static/dist/assets/endpoints-ipnv1hd4.js +0 -64
  197. package/static/dist/assets/eventmodeling-NTZA5JFV-d9b5a0ia.js +0 -1
  198. package/static/dist/assets/flowDiagram-HODETNUW-kfxxsyrk.js +0 -1
  199. package/static/dist/assets/index-bz7g7rhx.css +0 -2
  200. package/static/dist/assets/index-hywfjw88.js +0 -11
  201. package/static/dist/assets/login-mvjys1ol.js +0 -1
  202. package/static/dist/assets/mermaid-HWGCJPDP-gmnkgf1n.js +0 -1
  203. package/static/dist/assets/onboarding-l6quy6gz.js +0 -1
  204. package/static/dist/assets/railroad-abnf-HS5TGJTU-5mjl5wpv.js +0 -1
  205. package/static/dist/assets/railroad-ebnf-LZEXJU2U-bfram8v8.js +0 -1
  206. package/static/dist/assets/railroad-peg-WCYAUIDC-nwfczn88.js +0 -1
  207. package/static/dist/assets/share._token-il3y6js2.js +0 -2
  208. package/static/dist/assets/sliders-horizontal-kd99ffvl.js +0 -1
  209. package/static/dist/assets/stateDiagram-v2-MP3YSRHH-dnzlmdpu.js +0 -1
  210. package/static/dist/assets/swimlanesDiagram-VR7AAH4N-ck0eqqdp.js +0 -8
  211. package/static/dist/assets/useLocale-evhacmv2.js +0 -1
@@ -20,9 +20,10 @@ import { dict } from '../config/agent-config.js';
20
20
  import { ReasoningTitleTracker, reasoningEventPayload } from './reasoning-titles.js';
21
21
  import { messageWindowForDisplay, messagesForLimitedPayload, toolCallsForMessageWindow } from './window.js';
22
22
  import { attachTodoState } from './todo.js';
23
+ import { hydrateAnchorActivityScenes, withTurnIds } from './anchor.js';
23
24
  import { persistentStateChanges, persistentStateSnapshot } from './state-saved.js';
24
25
  import { maxIterationsFromConfig, maxTokensFromConfig, processWakeupMaxIterations, reasoningConfigFromConfig, webuiEphemeralSystemPrompt, workspaceSystemMessage } from './turn-context.js';
25
- import { assistantReplyAddedAfterCurrentTurn, buildPartialMessage, extractToolCallsFromMessages, injectMaxIterationSummaryFallback, isContextCompressionMarker, isDict, mergeDisplayMessagesAfterAgentResult, messageIdentity, messageText, sanitizeMessagesForApi, sessionLacksFinalAssistantAnswer, splitThinkingFromContent, stripXmlToolCalls, workspaceContextPrefix } from './merge.js';
26
+ import { agentSteerText, assistantReplyAddedAfterCurrentTurn, buildPartialMessage, extractToolCallsFromMessages, injectMaxIterationSummaryFallback, isContextCompressionMarker, isDict, mergeDisplayMessagesAfterAgentResult, messageIdentity, messageText, sanitizeMessagesForApi, sessionLacksFinalAssistantAnswer, splitThinkingFromContent, stripXmlToolCalls, workspaceContextPrefix } from './merge.js';
26
27
  import { fallbackTitleFromExchange, firstExchangeSnippets, isGenericFallbackTitle, latestExchangeSnippets, looksInvalidGeneratedTitle, sanitizeGeneratedTitle, titleLanguageMismatch, titlePrompts } from './titles.js';
27
28
  import { str } from '../util.js';
28
29
  export const CHAT_LOCK_WAIT_SECONDS = 2;
@@ -138,6 +139,8 @@ export function explicitTextSignal(cfg) {
138
139
  return !['', 'auto'].includes(provider) || Boolean(str(vision.model).trim()) || Boolean(str(vision.base_url).trim());
139
140
  }
140
141
  export const GATEWAY_APPROVAL_RELAY_UNAVAILABLE = 'Gateway approval could not be relayed because the active run is unavailable. Reopen the session or retry after it reconnects.';
142
+ /** Error classifications that are turn outcomes in their own right (not a generic failure). */
143
+ const CLASSIFIED_OUTCOMES = new Set(['no_response', 'compression_exhausted', 'interrupted', 'cancelled']);
141
144
  export class TurnRunner {
142
145
  deps;
143
146
  writers = new Map();
@@ -146,6 +149,8 @@ export class TurnRunner {
146
149
  yoloChains = new Map();
147
150
  abortControllers = new Map();
148
151
  steers = new Map();
152
+ consumedSteers = new Map();
153
+ lastCompletedTool = new Map();
149
154
  /** Streams whose run completed (`done` emitted) and only await title work; a late cancel is a no-op for these. */
150
155
  settledStreams = new Set();
151
156
  constructor(deps) {
@@ -156,6 +161,8 @@ export class TurnRunner {
156
161
  /** Python `_start_chat_stream_for_session`: persist pending state, register the channel, launch the worker. */
157
162
  start(session, opts) {
158
163
  const s = session;
164
+ if (this.deps.updateInProgress?.())
165
+ return { error: 'Web is updating. Retry after it restarts.', _status: 503 };
159
166
  if (this.deps.profileDeleting?.(s.profile ?? null))
160
167
  return { error: `Profile '${str(s.profile)}' is being deleted.`, _status: 409 };
161
168
  const locked = s.active_stream_id;
@@ -190,7 +197,7 @@ export class TurnRunner {
190
197
  s.title = provisional;
191
198
  }
192
199
  if (this.deps.saveMode() === 'eager')
193
- this.checkpointUserMessage(s, opts.msg, attachments, s.pending_started_at, source);
200
+ this.checkpointUserMessage(s, opts.msg, attachments, s.pending_started_at, source, streamId);
194
201
  this.deps.store.save(s);
195
202
  this.registry.writebackOwners.set(s.session_id, streamId);
196
203
  if (wasHiddenEmpty)
@@ -206,7 +213,7 @@ export class TurnRunner {
206
213
  void this.run(s.session_id, streamId, channel, opts).catch((error) => {
207
214
  this.deps.log(`[webui] ERROR turn ${streamId} crashed\n${error instanceof Error ? (error.stack ?? error.message) : String(error)}`);
208
215
  });
209
- const response = { stream_id: streamId, session_id: s.session_id, pending_started_at: s.pending_started_at, turn_id: null, title: s.title };
216
+ const response = { stream_id: streamId, session_id: s.session_id, pending_started_at: s.pending_started_at, turn_id: streamId, title: s.title };
210
217
  if (opts.normalizedModel && opts.model)
211
218
  response.effective_model = opts.model;
212
219
  if (opts.modelProvider)
@@ -214,11 +221,11 @@ export class TurnRunner {
214
221
  return response;
215
222
  }
216
223
  /** Python `_checkpoint_user_message_for_eager_session_save`. */
217
- checkpointUserMessage(s, msg, attachments, startedAt, source) {
224
+ checkpointUserMessage(s, msg, attachments, startedAt, source, turnId) {
218
225
  const latest = s.messages[s.messages.length - 1];
219
226
  if (latest?.role === 'user' && messageText(latest.content).split(/\s+/).join(' ') === msg.split(/\s+/).join(' '))
220
227
  return;
221
- const user = { role: 'user', content: msg };
228
+ const user = { role: 'user', content: msg, _turn_id: turnId };
222
229
  const token = buildActiveTurnToken(s.active_stream_id, startedAt);
223
230
  if (token)
224
231
  user._active_turn_token = token;
@@ -370,6 +377,7 @@ export class TurnRunner {
370
377
  case 'steer_pending':
371
378
  for (const record of this.takeConsumedSteers(streamId, str(data.text), { keepLeftovers: true }))
372
379
  put('steer_consumed', record);
380
+ this.saveConsumedSteers(sessionId, streamId);
373
381
  return;
374
382
  case 'tool':
375
383
  liveToolCalls.push({ name: data.name, args: data.args ?? {}, tid: str(data.tid), done: false });
@@ -386,6 +394,8 @@ export class TurnRunner {
386
394
  break;
387
395
  }
388
396
  }
397
+ if (str(data.tid))
398
+ this.lastCompletedTool.set(streamId, str(data.tid));
389
399
  put('tool_complete', data);
390
400
  return;
391
401
  // Python: the live chat frame carries the queue head plus depth, not the entry that just arrived.
@@ -462,7 +472,7 @@ export class TurnRunner {
462
472
  // sidecar reports `completed` whenever a failed run still carries messages).
463
473
  // Python's second chance: a turn that emitted no new row still counts when the merged transcript it produced
464
474
  // ends on a final answer (the current user row or trailing tool activity makes it "lacking").
465
- const mergedForCheck = () => mergeDisplayMessagesAfterAgentResult(previousMessages, previousContext, resultMessages, msgText, { source: opts.source ?? 'webui', activeTurnToken, now: deps.now() });
475
+ const mergedForCheck = () => mergeDisplayMessagesAfterAgentResult(previousMessages, previousContext, resultMessages, msgText, { source: opts.source ?? 'webui', activeTurnToken, now: deps.now(), turnId: streamId });
466
476
  const assistantAdded = assistantReplyAddedAfterCurrentTurn(resultMessages, previousContext, msgText) || !sessionLacksFinalAssistantAnswer(mergedForCheck());
467
477
  const lastErr = result.error ?? capturedTerminalError ?? '';
468
478
  // Python `_turn_transcript_lacks_final_assistant_answer`: a partial result with no final answer is a silent failure even if tokens streamed.
@@ -471,16 +481,22 @@ export class TurnRunner {
471
481
  const classification = classifyProviderError(lastErr, { silentFailure: !lastErr });
472
482
  const errStr = lastErr || `${classification.label}.`;
473
483
  const payload = providerErrorPayload(errStr, classification.type, classification.hint, deps.redactEnabled());
484
+ // Settle the steers first so the persisted turn carries every consumed one; the Agent's pending text stays a leftover.
485
+ const steerEvents = this.finalizeSteerEvents(streamId, str(result.pending_steer));
474
486
  this.persistError(s, streamId, classification.label, payload, activeTurnToken);
475
487
  payload.session = redactSessionData(this.terminalSessionPayload(s), deps.redactEnabled());
476
488
  payload.session_id = s.session_id;
477
489
  payload.old_session_id = sessionId;
490
+ for (const [event, data] of steerEvents)
491
+ put(event, data);
478
492
  put('apperror', payload);
479
493
  failed = true;
480
494
  return;
481
495
  }
482
496
  // ── settle the transcript ──
483
- s.messages = mergeDisplayMessagesAfterAgentResult(previousMessages, previousContext, resultMessages, msgText, { source: opts.source ?? 'webui', activeTurnToken, now: deps.now() });
497
+ // The Agent's last pending-steer text settles the remaining steers before the turn is written back.
498
+ const steerEvents = this.finalizeSteerEvents(streamId, result.pending_steer);
499
+ s.messages = mergeDisplayMessagesAfterAgentResult(previousMessages, previousContext, resultMessages, msgText, { source: opts.source ?? 'webui', activeTurnToken, now: deps.now(), turnId: streamId });
484
500
  s.context_messages = dedupeContext(resultMessages);
485
501
  for (const m of s.messages) {
486
502
  if (m.role !== 'assistant')
@@ -565,9 +581,13 @@ export class TurnRunner {
565
581
  m._turnTps = Math.round((usage.completion_tokens / duration) * 10) / 10;
566
582
  if (result.model)
567
583
  m._usedModel = result.model;
584
+ // The live `done` frame says the tool budget ran out; the persisted row says so too, so a reload agrees.
585
+ if (result.tool_limit_reached && m._turn_id === streamId)
586
+ m._terminal_state = 'tool_limit_reached';
568
587
  break;
569
588
  }
570
589
  }
590
+ this.persistConsumedSteers(s, streamId, previousStartedAt(s, activeRun), now);
571
591
  deps.store.save(s);
572
592
  deps.pending.clearApprovals(sessionId);
573
593
  deps.pending.clearClarifies(sessionId);
@@ -610,7 +630,7 @@ export class TurnRunner {
610
630
  donePayload.terminal_reason = 'max_iterations';
611
631
  }
612
632
  put('done', donePayload);
613
- for (const [event, payload] of this.finalizeSteerEvents(streamId, result.pending_steer))
633
+ for (const [event, payload] of steerEvents)
614
634
  put(event, payload);
615
635
  // The turn is over: release admission before the title work so a follow-up message is accepted while the
616
636
  // (up to two) title prompts run; the channel and journal stay open for the `title` events (Python retired the
@@ -661,6 +681,8 @@ export class TurnRunner {
661
681
  current = s;
662
682
  }
663
683
  if (current.active_stream_id === streamId) {
684
+ for (const [event, data] of this.takeSteerEventsBefore(streamId, 'apperror'))
685
+ put(event, data);
664
686
  this.persistError(current, streamId, classification.label, payload, activeTurnToken);
665
687
  payload.session = redactSessionData(this.terminalSessionPayload(current), deps.redactEnabled());
666
688
  }
@@ -756,7 +778,8 @@ export class TurnRunner {
756
778
  */
757
779
  terminalSessionPayload(s) {
758
780
  const payload = s.compact({ includeRuntime: true, activeStreamIds: this.registry.liveIds });
759
- const [window, offset] = messageWindowForDisplay(s.messages, TERMINAL_SSE_VISIBLE_MESSAGE_LIMIT, null);
781
+ const scened = hydrateAnchorActivityScenes(withTurnIds(s.messages), s.anchor_activity_scenes, { activeTurnId: s.active_stream_id, clipToolResults: true });
782
+ const [window, offset] = messageWindowForDisplay(scened, TERMINAL_SSE_VISIBLE_MESSAGE_LIMIT, null);
760
783
  const limited = messagesForLimitedPayload(window);
761
784
  payload.messages = limited;
762
785
  payload.message_count = s.messages.length;
@@ -769,7 +792,7 @@ export class TurnRunner {
769
792
  /** Python `_materialize_pending_user_turn_before_error` + error message append + save. */
770
793
  persistError(s, streamId, label, payload, activeTurnToken) {
771
794
  const startedAt = s.pending_started_at;
772
- this.materializePendingUserTurn(s, activeTurnToken);
795
+ this.materializePendingUserTurn(s, activeTurnToken, streamId);
773
796
  const duration = typeof startedAt === 'number' && startedAt > 0 ? Math.max(0, this.deps.now() - startedAt) : null;
774
797
  s.active_stream_id = null;
775
798
  s.pending_user_message = null;
@@ -778,9 +801,12 @@ export class TurnRunner {
778
801
  s.pending_user_source = null;
779
802
  this.appendPartialSnapshot(s, streamId);
780
803
  const hint = str(payload.hint);
781
- const errorMessage = { role: 'assistant', content: `**${label}:** ${str(payload.message) || label}${hint ? `\n\n*${hint}*` : ''}`, timestamp: Math.trunc(this.deps.now()), _error: true };
804
+ const errorMessage = { role: 'assistant', content: `**${label}:** ${str(payload.message) || label}${hint ? `\n\n*${hint}*` : ''}`, timestamp: Math.trunc(this.deps.now()), _error: true, _turn_id: streamId };
782
805
  if (duration !== null)
783
806
  errorMessage._turnDuration = Math.round(duration * 1000) / 1000;
807
+ // The classified outcome the live frame reported stays on the row, so the settled scene says the same.
808
+ if (CLASSIFIED_OUTCOMES.has(str(payload.type)))
809
+ errorMessage._terminal_state = str(payload.type);
784
810
  if (payload.type === 'compression_exhausted') {
785
811
  // Python `stamp_compression_exhausted_recovery`: durable recovery metadata on the session, the marker, and the frame.
786
812
  const recovery = stampCompressionExhaustedRecovery(s, str(payload.message) || label, str(payload.details));
@@ -795,6 +821,7 @@ export class TurnRunner {
795
821
  else if (payload.type === 'interrupted')
796
822
  errorMessage.provider_details_label = 'Interruption details';
797
823
  s.messages.push(errorMessage);
824
+ this.persistConsumedSteers(s, streamId, startedAt, this.deps.now());
798
825
  try {
799
826
  this.deps.store.save(s);
800
827
  }
@@ -805,7 +832,7 @@ export class TurnRunner {
805
832
  this.deps.pending.clearClarifies(s.session_id);
806
833
  this.deps.events.publish('session_error', { profile: s.profile, sessionId: s.session_id });
807
834
  }
808
- materializePendingUserTurn(s, activeTurnToken) {
835
+ materializePendingUserTurn(s, activeTurnToken, turnId) {
809
836
  const pendingText = str(s.pending_user_message);
810
837
  if (!pendingText)
811
838
  return false;
@@ -816,7 +843,7 @@ export class TurnRunner {
816
843
  for (let i = s.messages.length - 1; i >= 0; i -= 1) {
817
844
  const m = s.messages[i];
818
845
  if (m.role === 'user' && m._active_turn_token === activeTurnToken) {
819
- if (!s.messages.slice(i + 1).some((later) => later.role === 'user'))
846
+ if (!s.messages.slice(i + 1).some((later) => later.role === 'user' && !later._steer))
820
847
  return false;
821
848
  break;
822
849
  }
@@ -826,7 +853,7 @@ export class TurnRunner {
826
853
  // Python `_synthesize_user_message_on_cancel`: a worker that already merged this prompt (same text, not older than the pending start) wins.
827
854
  if (last?.role === 'user' && messageText(last.content).trim() === pendingText.trim() && Math.trunc(Number(last.timestamp)) >= Math.trunc(recoveredTs))
828
855
  return false;
829
- const recovered = { role: 'user', content: pendingText, timestamp: recoveredTs, _recovered: true };
856
+ const recovered = { role: 'user', content: pendingText, timestamp: recoveredTs, _recovered: true, _turn_id: turnId };
830
857
  if (source !== 'webui')
831
858
  recovered._source = source;
832
859
  if (attachments.length)
@@ -840,6 +867,7 @@ export class TurnRunner {
840
867
  const partial = buildPartialMessage((this.registry.partialText.get(streamId) ?? []).join(''), (this.registry.reasoningText.get(streamId) ?? []).join(''), this.registry.liveToolCalls.get(streamId) ?? [], this.deps.now());
841
868
  if (!partial)
842
869
  return;
870
+ partial._turn_id = streamId;
843
871
  const key = messageIdentity(partial);
844
872
  if (key !== null && s.messages.some((m) => m._partial && messageIdentity(m) === key))
845
873
  return;
@@ -882,7 +910,8 @@ export class TurnRunner {
882
910
  }
883
911
  if (current.messages.some((m) => m._error && str(m.content).startsWith('**Task cancelled:**')) && current.active_stream_id === null && !current.pending_user_message)
884
912
  return true;
885
- this.materializePendingUserTurn(current, buildActiveTurnToken(streamId, current.pending_started_at));
913
+ const startedAt = current.pending_started_at;
914
+ this.materializePendingUserTurn(current, buildActiveTurnToken(streamId, current.pending_started_at), streamId);
886
915
  current.active_stream_id = null;
887
916
  current.pending_user_message = null;
888
917
  current.pending_attachments = [];
@@ -890,7 +919,8 @@ export class TurnRunner {
890
919
  current.pending_user_source = null;
891
920
  this.appendPartialSnapshot(current, streamId);
892
921
  const text = message.trim().endsWith('.') ? message.trim() : `${message.trim()}.`;
893
- current.messages.push({ role: 'assistant', content: `**Task cancelled:** ${text}\n\n*${cancelledTurnHint(this.deps.agentName())}*`, _error: true, provider_details: text, provider_details_label: 'Cancellation details', timestamp: Math.trunc(this.deps.now()) });
922
+ current.messages.push({ role: 'assistant', content: `**Task cancelled:** ${text}\n\n*${cancelledTurnHint(this.deps.agentName())}*`, _error: true, provider_details: text, provider_details_label: 'Cancellation details', timestamp: Math.trunc(this.deps.now()), _turn_id: streamId });
923
+ this.persistConsumedSteers(current, streamId, startedAt, this.deps.now());
894
924
  try {
895
925
  this.deps.store.save(current);
896
926
  }
@@ -917,6 +947,8 @@ export class TurnRunner {
917
947
  }
918
948
  this.abortControllers.delete(streamId);
919
949
  this.steers.delete(streamId);
950
+ this.consumedSteers.delete(streamId);
951
+ this.lastCompletedTool.delete(streamId);
920
952
  this.settledStreams.delete(streamId);
921
953
  this.registry.retire(streamId, this.deps.now());
922
954
  this.registry.forgetOwner(streamId);
@@ -1089,6 +1121,8 @@ export class TurnRunner {
1089
1121
  leftover = '';
1090
1122
  }
1091
1123
  }
1124
+ // Settle the steers first so the persisted cancel carries every consumed one.
1125
+ const steerEvents = (this.steers.get(streamId) ?? []).length ? this.finalizeSteerEvents(streamId, leftover) : [];
1092
1126
  if (sessionId) {
1093
1127
  let current = null;
1094
1128
  try {
@@ -1117,9 +1151,8 @@ export class TurnRunner {
1117
1151
  }
1118
1152
  channel.put([event, data, eventId]);
1119
1153
  };
1120
- if ((this.steers.get(streamId) ?? []).length)
1121
- for (const [event, data] of this.finalizeSteerEvents(streamId, leftover))
1122
- emit(event, data);
1154
+ for (const [event, data] of steerEvents)
1155
+ emit(event, data);
1123
1156
  emit('cancel', sessionId ? this.cancelFrame(sessionId) : this.cancelPayload());
1124
1157
  this.registry.streams.delete(streamId);
1125
1158
  this.registry.liveIds.delete(streamId);
@@ -1196,7 +1229,70 @@ export class TurnRunner {
1196
1229
  return null;
1197
1230
  }
1198
1231
  consumedSteerPayload(record) {
1199
- return { ...record, agent_text: record.text, text: record.display_text || record.text, consumed_at: this.deps.now() };
1232
+ const consumedAt = this.deps.now();
1233
+ const afterToolCallId = this.lastCompletedTool.get(record.stream_id) ?? null;
1234
+ const consumed = this.consumedSteers.get(record.stream_id) ?? [];
1235
+ if (!consumed.some((c) => c.steer_id === record.steer_id)) {
1236
+ consumed.push({ steer_id: record.steer_id, text: record.display_text || record.text, agent_text: record.text, submitted_at: record.created_at, consumed_at: consumedAt, after_tool_call_id: afterToolCallId });
1237
+ this.consumedSteers.set(record.stream_id, consumed);
1238
+ }
1239
+ return { ...record, agent_text: record.text, text: record.display_text || record.text, consumed_at: consumedAt, after_tool_call_id: afterToolCallId };
1240
+ }
1241
+ /**
1242
+ * Consumed steers become display-only `_steer` user rows at their causal place in the turn: after the tool results of
1243
+ * the call that had completed when the Agent took them (else right after the turn's prompt), in consumption order,
1244
+ * each with the phase it ended. Returns where the last phase starts. Replaces the turn's earlier placement, so the
1245
+ * mid-turn save and the terminal one agree. Leftover steers are not persisted.
1246
+ */
1247
+ placeConsumedSteers(s, streamId, startedAt) {
1248
+ const steers = this.consumedSteers.get(streamId) ?? [];
1249
+ if (!steers.length)
1250
+ return null;
1251
+ s.messages = s.messages.filter((m) => !(m._steer && m._turn_id === streamId));
1252
+ let boundary = typeof startedAt === 'number' && startedAt > 0 ? startedAt : steers[0].submitted_at;
1253
+ for (const steer of steers) {
1254
+ const row = {
1255
+ role: 'user', content: steer.text, timestamp: steer.consumed_at, _turn_id: streamId,
1256
+ _steer: { steer_id: steer.steer_id, submitted_at: steer.submitted_at, consumed_at: steer.consumed_at, phase_duration: roundDuration(steer.consumed_at - boundary) },
1257
+ };
1258
+ // An Agent that records the steer it delivered does so at its exact place: that row becomes the steer, not a copy.
1259
+ const agentRows = s.messages.flatMap((m, i) => (m._turn_id === streamId && agentSteerText(m) !== null ? [i] : []));
1260
+ const agentRow = agentRows.find((i) => agentSteerText(s.messages[i]) === steer.agent_text.trim()) ?? agentRows[0];
1261
+ if (agentRow === undefined)
1262
+ s.messages.splice(steerInsertIndex(s.messages, streamId, steer.after_tool_call_id), 0, row);
1263
+ else
1264
+ s.messages[agentRow] = row;
1265
+ boundary = steer.consumed_at;
1266
+ }
1267
+ return boundary;
1268
+ }
1269
+ /** A steer is saved as it enters the stream, so a reload mid-turn already has it; the terminal save re-places it. */
1270
+ saveConsumedSteers(sessionId, streamId) {
1271
+ let s;
1272
+ try {
1273
+ s = this.deps.store.get(sessionId);
1274
+ }
1275
+ catch {
1276
+ return;
1277
+ }
1278
+ if (s.active_stream_id !== streamId || this.placeConsumedSteers(s, streamId, s.pending_started_at) === null)
1279
+ return;
1280
+ try {
1281
+ this.deps.store.save(s, { touchUpdatedAt: false });
1282
+ }
1283
+ catch (error) {
1284
+ this.deps.log(`[webui] WARNING: failed to save consumed steer for ${sessionId}: ${error.message}`);
1285
+ }
1286
+ }
1287
+ /** Terminal placement: the turn's rows are final now, and its last reply carries the final phase. */
1288
+ persistConsumedSteers(s, streamId, startedAt, endedAt) {
1289
+ const boundary = this.placeConsumedSteers(s, streamId, startedAt);
1290
+ this.consumedSteers.delete(streamId);
1291
+ if (boundary === null)
1292
+ return;
1293
+ const last = s.messages.findLast((m) => m.role === 'assistant' && m._turn_id === streamId);
1294
+ if (last)
1295
+ last._final_phase_duration = roundDuration(endedAt - boundary);
1200
1296
  }
1201
1297
  /**
1202
1298
  * Python `_take_consumed_webui_steers` (live, `keepLeftovers`) and `_finalize_webui_steers` (terminal): records the
@@ -1354,4 +1450,35 @@ function dedupeContext(messages) {
1354
1450
  return out;
1355
1451
  }
1356
1452
  export { HttpFailure };
1453
+ /** Where a consumed steer goes: past the tool results of the call it followed, else past the turn's prompt. */
1454
+ function roundDuration(seconds) {
1455
+ return Math.round(Math.max(0, seconds) * 1000) / 1000;
1456
+ }
1457
+ function steerInsertIndex(messages, turnId, afterToolCallId) {
1458
+ // OpenAI-style `tool_calls` and Anthropic-style `tool_use` content blocks both name the call a steer followed.
1459
+ const callIds = (m) => [
1460
+ ...(Array.isArray(m.tool_calls) ? m.tool_calls : []).map((tc) => str(tc.id) || str(tc.call_id) || str(tc.tool_call_id)),
1461
+ ...(Array.isArray(m.content) ? m.content : []).flatMap((part) => (isDict(part) && part.type === 'tool_use' ? [str(part.id)] : [])),
1462
+ ];
1463
+ // The steer followed that call's result: right after its row (a `tool` row, or a user row of `tool_result` blocks), even
1464
+ // when the same assistant message made later calls; steers already placed there keep their consumption order.
1465
+ const answers = (m) => (m.role === 'tool' && (str(m.tool_call_id) || str(m.tool_use_id)) === afterToolCallId)
1466
+ || (m.role === 'user' && Array.isArray(m.content) && m.content.some((part) => isDict(part) && part.type === 'tool_result' && str(part.tool_use_id) === afterToolCallId));
1467
+ const result = afterToolCallId ? messages.findIndex((m) => m._turn_id === turnId && answers(m)) : -1;
1468
+ if (result !== -1) {
1469
+ let i = result + 1;
1470
+ while (i < messages.length && messages[i]._turn_id === turnId && messages[i]._steer)
1471
+ i += 1;
1472
+ return i;
1473
+ }
1474
+ let at = afterToolCallId ? messages.findIndex((m) => m._turn_id === turnId && m.role === 'assistant' && callIds(m).includes(afterToolCallId)) : -1;
1475
+ if (at === -1)
1476
+ at = messages.findIndex((m) => m._turn_id === turnId && m.role === 'user' && !m._steer);
1477
+ if (at === -1)
1478
+ return messages.length;
1479
+ let i = at + 1;
1480
+ while (i < messages.length && messages[i]._turn_id === turnId && (messages[i].role === 'tool' || messages[i]._steer))
1481
+ i += 1;
1482
+ return i;
1483
+ }
1357
1484
  //# sourceMappingURL=turn.js.map