@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.
- package/_release.json +20 -0
- package/dist/api/chat-router.d.ts +54 -1
- package/dist/api/sessions-router.d.ts +296 -1
- package/dist/api/settings-router.d.ts +8 -0
- package/dist/api/settings-router.js +13 -5
- package/dist/api/settings-router.js.map +1 -1
- package/dist/api/sse-routes.js +1 -1
- package/dist/api/sse-routes.js.map +1 -1
- package/dist/api/tools-router.d.ts +91 -0
- package/dist/api/tools-router.js +4 -4
- package/dist/api/tools-router.js.map +1 -1
- package/dist/app.d.ts +449 -2
- package/dist/bin/talaria-web.js +4 -1
- package/dist/bin/talaria-web.js.map +1 -1
- package/dist/cli/supervise.js +2 -1
- package/dist/cli/supervise.js.map +1 -1
- package/dist/runtime.js +8 -2
- package/dist/runtime.js.map +1 -1
- package/dist/sessions/anchor.d.ts +57 -5
- package/dist/sessions/anchor.js +368 -32
- package/dist/sessions/anchor.js.map +1 -1
- package/dist/sessions/merge.d.ts +6 -0
- package/dist/sessions/merge.js +35 -4
- package/dist/sessions/merge.js.map +1 -1
- package/dist/sessions/service.d.ts +2 -0
- package/dist/sessions/service.js +31 -16
- package/dist/sessions/service.js.map +1 -1
- package/dist/sessions/session.js +1 -1
- package/dist/sessions/session.js.map +1 -1
- package/dist/sessions/turn.d.ts +14 -0
- package/dist/sessions/turn.js +147 -20
- package/dist/sessions/turn.js.map +1 -1
- package/dist/sessions/window.d.ts +1 -0
- package/dist/sessions/window.js +1 -1
- package/dist/sessions/window.js.map +1 -1
- package/dist/settings.js +5 -0
- package/dist/settings.js.map +1 -1
- package/dist/sidecar/client.d.ts +1 -1
- package/dist/sidecar/client.js +15 -4
- package/dist/sidecar/client.js.map +1 -1
- package/dist/sidecar/fake.js +3 -2
- package/dist/sidecar/fake.js.map +1 -1
- package/dist/tools/crons.js +55 -43
- package/dist/tools/crons.js.map +1 -1
- package/dist/tools/updates.d.ts +51 -13
- package/dist/tools/updates.js +362 -114
- package/dist/tools/updates.js.map +1 -1
- package/package.json +2 -2
- package/sidecar/agent_dependency.json +4 -3
- package/sidecar/talaria_sidecar/home.py +29 -20
- package/sidecar/talaria_sidecar/methods/config.py +5 -3
- package/sidecar/talaria_sidecar/runtime.py +3 -9
- package/static/dist/FILES.txt +116 -116
- package/static/dist/assets/AppShell-hikzkcql.js +1 -0
- package/static/dist/assets/ChatPage-ofqn89h9.js +21 -0
- package/static/dist/assets/{HubRoute-qs9vzuh0.js → HubRoute-f2zrs9si.js} +1 -1
- package/static/dist/assets/{Markdown-bt9bqd6a.js → Markdown-ftyfzds0.js} +7 -7
- package/static/dist/assets/RightPanel-febk3dkd.js +1 -0
- package/static/dist/assets/Select-lvx8gihl.js +1 -0
- package/static/dist/assets/SessionListPanel-o70ljmik.js +1 -0
- package/static/dist/assets/SettingsLayout-pdpmhy1o.js +1 -0
- package/static/dist/assets/States-i46tz8an.js +1 -0
- package/static/dist/assets/{Toaster-jtn59pjm.js → Toaster-fklyeeyh.js} +1 -1
- package/static/dist/assets/_app-j3rors25.js +1 -0
- package/static/dist/assets/_app.ext._extensionId-c0ey5ec9.js +1 -0
- package/static/dist/assets/_app.index-coy9jrto.js +1 -0
- package/static/dist/assets/_app.insights-ebd2k0i5.js +1 -0
- package/static/dist/assets/_app.kanban-e2p7ypi5.js +1 -0
- package/static/dist/assets/_app.logs-ml21mfyz.js +1 -0
- package/static/dist/assets/_app.memory-lhu5mm47.js +1 -0
- package/static/dist/assets/_app.profiles-egx0fgjo.js +1 -0
- package/static/dist/assets/{_app.session._sessionId-kevi4qd2.js → _app.session._sessionId-n1ggcf5e.js} +1 -1
- package/static/dist/assets/_app.settings-d0vqtyid.js +1 -0
- package/static/dist/assets/_app.settings._section-cnzj6pli.js +1 -0
- package/static/dist/assets/_app.skills-kkug8obu.js +1 -0
- package/static/dist/assets/{_app.tasks-b99d98fo.js → _app.tasks-nf285d2o.js} +2 -2
- package/static/dist/assets/_app.todos-dxalqp1e.js +1 -0
- package/static/dist/assets/_app.workspaces-bqtduujs.js +1 -0
- package/static/dist/assets/{abnfDiagram-VCTEODGH-hvrr25lb.js → abnfDiagram-VCTEODGH-i4bgzu83.js} +1 -1
- package/static/dist/assets/architecture-7GRP2DOG-o8zqevhv.js +1 -0
- package/static/dist/assets/{architectureDiagram-5GKGNRK7-b3r0z8sq.js → architectureDiagram-5GKGNRK7-mx21lwqa.js} +1 -1
- package/static/dist/assets/{blockDiagram-I7D4REHJ-hd3gw0cj.js → blockDiagram-I7D4REHJ-o1hhchxv.js} +1 -1
- package/static/dist/assets/{c4Diagram-7LVT6UL2-jlmbjiji.js → c4Diagram-7LVT6UL2-jygqtvd8.js} +1 -1
- package/static/dist/assets/channel-mw8l793e.js +1 -0
- package/static/dist/assets/{chunk-4HAMMTFA-oydbpsin.js → chunk-4HAMMTFA-hjcpubgk.js} +1 -1
- package/static/dist/assets/{chunk-75Z2AOVW-j633otj1.js → chunk-75Z2AOVW-eibnex6m.js} +1 -1
- package/static/dist/assets/{chunk-DU6HZSFF-ods2l027.js → chunk-DU6HZSFF-k1tp3qnp.js} +3 -3
- package/static/dist/assets/{chunk-F27PBJKO-ln4n2cwt.js → chunk-F27PBJKO-lvcgn16f.js} +1 -1
- package/static/dist/assets/{chunk-GMAD6QVW-fpz133eh.js → chunk-GMAD6QVW-ldlzruv1.js} +1 -1
- package/static/dist/assets/{chunk-GVQU2GXP-abs9ixiy.js → chunk-GVQU2GXP-hs1mva9d.js} +1 -1
- package/static/dist/assets/{chunk-IMKFNOWR-j9vukdy1.js → chunk-IMKFNOWR-o7eqx3yb.js} +1 -1
- package/static/dist/assets/{chunk-L3NEJ4N5-cfyw87r3.js → chunk-L3NEJ4N5-bp1ofl70.js} +1 -1
- package/static/dist/assets/{chunk-OSK3NFVY-baja78wr.js → chunk-OSK3NFVY-o8m6mdz5.js} +1 -1
- package/static/dist/assets/{chunk-P2QGCYS3-g66xby8h.js → chunk-P2QGCYS3-kx83fti2.js} +1 -1
- package/static/dist/assets/{chunk-POPQ4Y6H-osqu864t.js → chunk-POPQ4Y6H-mwl99e9y.js} +1 -1
- package/static/dist/assets/{chunk-PWAF6VOD-egejasv1.js → chunk-PWAF6VOD-oriuokxy.js} +1 -1
- package/static/dist/assets/{chunk-SHT3W25Y-l014h8zz.js → chunk-SHT3W25Y-ab5cf00v.js} +1 -1
- package/static/dist/assets/{chunk-SVP7TREG-n3z10bp3.js → chunk-SVP7TREG-kqdrpr17.js} +1 -1
- package/static/dist/assets/{chunk-TICWLB2K-bzozkjc0.js → chunk-TICWLB2K-kfbnjabk.js} +1 -1
- package/static/dist/assets/classDiagram-ZZMXUADV-egajr1wp.js +1 -0
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-egajr1wp.js +1 -0
- package/static/dist/assets/{copied-cjsttec1.js → copied-dp33szhq.js} +1 -1
- package/static/dist/assets/{cynefin-OW5HDTMX-elpt7h1j.js → cynefin-OW5HDTMX-mm5sj9s7.js} +1 -1
- package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-5mzl5pi2.js → cynefinDiagram-5FMLGOSQ-nfon5q38.js} +1 -1
- package/static/dist/assets/{dagre-GXQ25YYZ-ihwlhmfl.js → dagre-GXQ25YYZ-dzdp9ypb.js} +1 -1
- package/static/dist/assets/{diagram-S7CK7UJ4-4z90agaz.js → diagram-S7CK7UJ4-ckxzqo01.js} +1 -1
- package/static/dist/assets/{diagram-UQ7AKVKN-eifw0lg7.js → diagram-UQ7AKVKN-hw16h1um.js} +1 -1
- package/static/dist/assets/{diagram-VSXAHHWV-ksh3sjqv.js → diagram-VSXAHHWV-mu7wu2zg.js} +1 -1
- package/static/dist/assets/{diagram-VX7I27RA-ett72f71.js → diagram-VX7I27RA-k9uprwtj.js} +1 -1
- package/static/dist/assets/{diagram-Z3DM3KII-l8scp8nu.js → diagram-Z3DM3KII-csdjzrg9.js} +1 -1
- package/static/dist/assets/{ebnfDiagram-PWID7BFC-f0pxcmit.js → ebnfDiagram-PWID7BFC-n8mbw2jj.js} +1 -1
- package/static/dist/assets/endpoints-f6ngsyu5.js +64 -0
- package/static/dist/assets/{erDiagram-RLTQ6QDP-f47m5xh0.js → erDiagram-RLTQ6QDP-fsfe1uj0.js} +1 -1
- package/static/dist/assets/eventmodeling-NTZA5JFV-k3zhqcyl.js +1 -0
- package/static/dist/assets/flowDiagram-HODETNUW-bdxes51f.js +1 -0
- package/static/dist/assets/{ganttDiagram-EL5Y4UJY-bmktjdi0.js → ganttDiagram-EL5Y4UJY-gsba3iq9.js} +1 -1
- package/static/dist/assets/{gitGraph-4MIJSDKK-j2ueg258.js → gitGraph-4MIJSDKK-na86q1pu.js} +1 -1
- package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-ms6586v9.js → gitGraphDiagram-WWUBYQGX-g72ew3xz.js} +1 -1
- package/static/dist/assets/{highlighted-body-KPVGNVTW-ev52d2si.js → highlighted-body-KPVGNVTW-okn18cp1.js} +1 -1
- package/static/dist/assets/index-ab0wj9ik.css +2 -0
- package/static/dist/assets/index-fd27xooo.js +11 -0
- package/static/dist/assets/{info-A6RAGUB7-j717vyt7.js → info-A6RAGUB7-i01jeltk.js} +1 -1
- package/static/dist/assets/{infoDiagram-27XIBGKW-dsbpib36.js → infoDiagram-27XIBGKW-cayiv6fa.js} +1 -1
- package/static/dist/assets/{ishikawaDiagram-5VMMS53U-n0sz3i2d.js → ishikawaDiagram-5VMMS53U-g4rf14bv.js} +1 -1
- package/static/dist/assets/{journeyDiagram-3NMN7TZE-mcxz1mme.js → journeyDiagram-3NMN7TZE-fusb2424.js} +1 -1
- package/static/dist/assets/{kanban-definition-UXKFOSKX-n6hq2lyc.js → kanban-definition-UXKFOSKX-g831pwlr.js} +1 -1
- package/static/dist/assets/{lazyRouteComponent-faquioun.js → lazyRouteComponent-ol965zwk.js} +1 -1
- package/static/dist/assets/{line-h5xq20s4.js → line-md6em6gc.js} +1 -1
- package/static/dist/assets/login-kfclsqd2.js +1 -0
- package/static/dist/assets/mermaid-HWGCJPDP-ejk0d88t.js +1 -0
- package/static/dist/assets/{mermaid-parser.core-n9cuu5ue.js → mermaid-parser.core-mq3uns9m.js} +3 -3
- package/static/dist/assets/{mindmap-definition-YA3MSWOX-gta3u7z4.js → mindmap-definition-YA3MSWOX-oxafrned.js} +1 -1
- package/static/dist/assets/onboarding-fo6hnkyv.js +1 -0
- package/static/dist/assets/{packet-AYTQ26CC-bpzuq5n6.js → packet-AYTQ26CC-ggxyvbym.js} +1 -1
- package/static/dist/assets/{pegDiagram-XKGWAZYB-juye13e5.js → pegDiagram-XKGWAZYB-nk7xcqgp.js} +1 -1
- package/static/dist/assets/{pie-WAS4IAKB-odhb8buq.js → pie-WAS4IAKB-ito2razh.js} +1 -1
- package/static/dist/assets/{pieDiagram-E7YTZNPT-mgb14ksh.js → pieDiagram-E7YTZNPT-g02xexar.js} +1 -1
- package/static/dist/assets/{quadrantDiagram-AXDQQJYC-cg36naog.js → quadrantDiagram-AXDQQJYC-d4jn4xq5.js} +1 -1
- package/static/dist/assets/{radar-RG4KPBEZ-lgzfpnef.js → radar-RG4KPBEZ-nebvfdzt.js} +1 -1
- package/static/dist/assets/{railroad-74A4TZTK-gmglc3ua.js → railroad-74A4TZTK-ny3gebse.js} +1 -1
- package/static/dist/assets/railroad-abnf-HS5TGJTU-gtd38edw.js +1 -0
- package/static/dist/assets/railroad-ebnf-LZEXJU2U-o238oax2.js +1 -0
- package/static/dist/assets/railroad-peg-WCYAUIDC-c4acqp2j.js +1 -0
- package/static/dist/assets/{railroadDiagram-O6MQD6OU-cmxwjt73.js → railroadDiagram-O6MQD6OU-m09mmu67.js} +1 -1
- package/static/dist/assets/{refresh-cw-mf192q3f.js → refresh-cw-eitd7yva.js} +1 -1
- package/static/dist/assets/{requirementDiagram-BXWQKSXE-ir0s2mek.js → requirementDiagram-BXWQKSXE-evggpcbk.js} +1 -1
- package/static/dist/assets/{sankeyDiagram-P5KCCOFB-jo7jcdq0.js → sankeyDiagram-P5KCCOFB-bun89039.js} +1 -1
- package/static/dist/assets/{saved-g777hp1t.js → saved-culx14ii.js} +1 -1
- package/static/dist/assets/{search-i2kfogyx.js → search-nu6kzq55.js} +1 -1
- package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-n0pf6xk5.js → sequenceDiagram-WJ2MYXX4-hhs9f09o.js} +1 -1
- package/static/dist/assets/{settings_section_extensions_title-hgvh507l.js → settings_section_extensions_title-mj0gcozh.js} +1 -1
- package/static/dist/assets/share._token-k4pcnxqu.js +2 -0
- package/static/dist/assets/sliders-horizontal-b9ljga1o.js +1 -0
- package/static/dist/assets/{stateDiagram-D77RDMKH-olbrwyv9.js → stateDiagram-D77RDMKH-gi7t1tro.js} +1 -1
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-jtadbol7.js +1 -0
- package/static/dist/assets/{swimlanes-42K2YHIH-od14zrw6.js → swimlanes-42K2YHIH-ea5urvph.js} +1 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-ki7weepw.js +8 -0
- package/static/dist/assets/{timeline-definition-24CTP7MA-l0qokmnb.js → timeline-definition-24CTP7MA-mq64rm3b.js} +1 -1
- package/static/dist/assets/{todoStore-la6uyqzj.js → todoStore-azvfuqbv.js} +1 -1
- package/static/dist/assets/{trash-bxz1k9cw.js → trash-osyo895g.js} +1 -1
- package/static/dist/assets/{treeView-Q6P3EWNA-hma3ip8m.js → treeView-Q6P3EWNA-cimbtxlz.js} +1 -1
- package/static/dist/assets/{treemap-WGGIJYW6-bghjng5m.js → treemap-WGGIJYW6-wig5gi1y.js} +1 -1
- package/static/dist/assets/{untitled-nfaw0q7m.js → untitled-mbwemvxf.js} +1 -1
- package/static/dist/assets/{useForm-k7fj2zsr.js → useForm-li4bljlv.js} +1 -1
- package/static/dist/assets/useLocale-kdew6auf.js +1 -0
- package/static/dist/assets/{vennDiagram-4TSXK5OY-bo97zf3z.js → vennDiagram-4TSXK5OY-ez63kh7n.js} +1 -1
- package/static/dist/assets/{wardley-WFR3VGLG-pbjl6jav.js → wardley-WFR3VGLG-r7rftz31.js} +1 -1
- package/static/dist/assets/{wardleyDiagram-VM6X3IG4-bmy9i09l.js → wardleyDiagram-VM6X3IG4-mvqg9eqc.js} +1 -1
- package/static/dist/assets/{xychartDiagram-S5SC5T6Z-eercu0mc.js → xychartDiagram-S5SC5T6Z-eqxdfc91.js} +1 -1
- package/static/dist/index.html +1 -1
- package/static/dist/sw.js +1 -1
- package/static/dist/assets/AppShell-gs0grlbs.js +0 -1
- package/static/dist/assets/ChatPage-gi1xlz52.js +0 -18
- package/static/dist/assets/RightPanel-dn19xu42.js +0 -1
- package/static/dist/assets/Select-y7wtf85g.js +0 -1
- package/static/dist/assets/SessionListPanel-igwdj9xr.js +0 -1
- package/static/dist/assets/SettingsLayout-n6mu0ghg.js +0 -1
- package/static/dist/assets/States-oil2issf.js +0 -1
- package/static/dist/assets/_app-k4jrkgfa.js +0 -1
- package/static/dist/assets/_app.ext._extensionId-mmf1mepm.js +0 -1
- package/static/dist/assets/_app.index-mb41nf00.js +0 -1
- package/static/dist/assets/_app.insights-mz5cep8m.js +0 -1
- package/static/dist/assets/_app.kanban-ocgw506i.js +0 -1
- package/static/dist/assets/_app.logs-lstmoju6.js +0 -1
- package/static/dist/assets/_app.memory-dishj9ra.js +0 -1
- package/static/dist/assets/_app.profiles-dynpvsio.js +0 -1
- package/static/dist/assets/_app.settings-c2czm9v4.js +0 -1
- package/static/dist/assets/_app.settings._section-exwflld7.js +0 -1
- package/static/dist/assets/_app.skills-j5jh2nug.js +0 -1
- package/static/dist/assets/_app.todos-ma8urvn3.js +0 -1
- package/static/dist/assets/_app.workspaces-l86cxeod.js +0 -1
- package/static/dist/assets/architecture-7GRP2DOG-mqft9w2p.js +0 -1
- package/static/dist/assets/channel-ix8cxdld.js +0 -1
- package/static/dist/assets/classDiagram-ZZMXUADV-c210y3il.js +0 -1
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-c210y3il.js +0 -1
- package/static/dist/assets/endpoints-ipnv1hd4.js +0 -64
- package/static/dist/assets/eventmodeling-NTZA5JFV-d9b5a0ia.js +0 -1
- package/static/dist/assets/flowDiagram-HODETNUW-kfxxsyrk.js +0 -1
- package/static/dist/assets/index-bz7g7rhx.css +0 -2
- package/static/dist/assets/index-hywfjw88.js +0 -11
- package/static/dist/assets/login-mvjys1ol.js +0 -1
- package/static/dist/assets/mermaid-HWGCJPDP-gmnkgf1n.js +0 -1
- package/static/dist/assets/onboarding-l6quy6gz.js +0 -1
- package/static/dist/assets/railroad-abnf-HS5TGJTU-5mjl5wpv.js +0 -1
- package/static/dist/assets/railroad-ebnf-LZEXJU2U-bfram8v8.js +0 -1
- package/static/dist/assets/railroad-peg-WCYAUIDC-nwfczn88.js +0 -1
- package/static/dist/assets/share._token-il3y6js2.js +0 -2
- package/static/dist/assets/sliders-horizontal-kd99ffvl.js +0 -1
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-dnzlmdpu.js +0 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-ck0eqqdp.js +0 -8
- package/static/dist/assets/useLocale-evhacmv2.js +0 -1
package/dist/sessions/turn.js
CHANGED
|
@@ -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:
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1121
|
-
|
|
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
|
-
|
|
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
|