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