@maudecode/talaria-web 1.14.0 → 1.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_release.json +4 -4
- package/dist/api/automation-router.d.ts +123 -7
- package/dist/api/automation-router.js +5 -0
- package/dist/api/automation-router.js.map +1 -1
- package/dist/api/chat-router.d.ts +728 -55
- package/dist/api/chat-router.js +122 -126
- package/dist/api/chat-router.js.map +1 -1
- package/dist/api/git-router.js +44 -32
- package/dist/api/git-router.js.map +1 -1
- package/dist/api/raw-routes.js +5 -58
- package/dist/api/raw-routes.js.map +1 -1
- package/dist/api/router.d.ts +6 -3
- package/dist/api/router.js +10 -8
- package/dist/api/router.js.map +1 -1
- package/dist/api/sessions-router.d.ts +2583 -297
- package/dist/api/sessions-router.js +92 -47
- package/dist/api/sessions-router.js.map +1 -1
- package/dist/api/settings-router.d.ts +188 -17
- package/dist/api/settings-router.js +75 -26
- 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 +13 -1
- package/dist/api/tools-router.js +10 -8
- package/dist/api/tools-router.js.map +1 -1
- package/dist/app.d.ts +3620 -343
- package/dist/auth/gate.js +8 -4
- package/dist/auth/gate.js.map +1 -1
- package/dist/auth/oidc.d.ts +6 -2
- package/dist/auth/oidc.js +16 -11
- package/dist/auth/oidc.js.map +1 -1
- package/dist/auth/store.d.ts +14 -2
- package/dist/auth/store.js +26 -1
- package/dist/auth/store.js.map +1 -1
- package/dist/cli/dotenv.d.ts +1 -1
- package/dist/cli/dotenv.js +36 -3
- package/dist/cli/dotenv.js.map +1 -1
- package/dist/config/agent-config.js +20 -15
- package/dist/config/agent-config.js.map +1 -1
- package/dist/http/addresses.d.ts +6 -0
- package/dist/http/addresses.js +26 -0
- package/dist/http/addresses.js.map +1 -1
- package/dist/http/context.d.ts +7 -5
- package/dist/http/context.js.map +1 -1
- package/dist/links.d.ts +26 -0
- package/dist/links.js +60 -0
- package/dist/links.js.map +1 -0
- package/dist/onboarding.d.ts +1 -1
- package/dist/onboarding.js +28 -8
- package/dist/onboarding.js.map +1 -1
- package/dist/profiles/profiles.js +1 -1
- package/dist/profiles/profiles.js.map +1 -1
- package/dist/providers/catalog.d.ts +85 -1
- package/dist/providers/catalog.js +280 -43
- package/dist/providers/catalog.js.map +1 -1
- package/dist/providers/tables.d.ts +3 -1
- package/dist/providers/tables.js +9 -4
- package/dist/providers/tables.js.map +1 -1
- package/dist/redact.js +493 -13
- package/dist/redact.js.map +1 -1
- package/dist/runtime.js +104 -42
- package/dist/runtime.js.map +1 -1
- package/dist/sessions/anchor.d.ts +22 -3
- package/dist/sessions/anchor.js +87 -42
- package/dist/sessions/anchor.js.map +1 -1
- package/dist/sessions/background-tasks.d.ts +140 -0
- package/dist/sessions/background-tasks.js +352 -0
- package/dist/sessions/background-tasks.js.map +1 -0
- package/dist/sessions/background-updates.d.ts +56 -0
- package/dist/sessions/background-updates.js +152 -0
- package/dist/sessions/background-updates.js.map +1 -0
- package/dist/sessions/cli-sessions.d.ts +6 -0
- package/dist/sessions/cli-sessions.js +20 -7
- package/dist/sessions/cli-sessions.js.map +1 -1
- package/dist/sessions/completions.d.ts +17 -1
- package/dist/sessions/completions.js +84 -13
- package/dist/sessions/completions.js.map +1 -1
- package/dist/sessions/compress.d.ts +33 -0
- package/dist/sessions/compress.js +66 -0
- package/dist/sessions/compress.js.map +1 -0
- package/dist/sessions/list.d.ts +22 -4
- package/dist/sessions/list.js +113 -33
- package/dist/sessions/list.js.map +1 -1
- package/dist/sessions/merge.d.ts +81 -7
- package/dist/sessions/merge.js +391 -26
- package/dist/sessions/merge.js.map +1 -1
- package/dist/sessions/pending.d.ts +5 -0
- package/dist/sessions/pending.js +30 -10
- package/dist/sessions/pending.js.map +1 -1
- package/dist/sessions/relay.d.ts +7 -1
- package/dist/sessions/relay.js +17 -4
- package/dist/sessions/relay.js.map +1 -1
- package/dist/sessions/service.d.ts +118 -6
- package/dist/sessions/service.js +564 -65
- package/dist/sessions/service.js.map +1 -1
- package/dist/sessions/session.d.ts +22 -1
- package/dist/sessions/session.js +29 -2
- package/dist/sessions/session.js.map +1 -1
- package/dist/sessions/shares.js +11 -2
- package/dist/sessions/shares.js.map +1 -1
- package/dist/sessions/source-kind.d.ts +22 -0
- package/dist/sessions/source-kind.js +54 -0
- package/dist/sessions/source-kind.js.map +1 -0
- package/dist/sessions/state-db.d.ts +10 -1
- package/dist/sessions/state-db.js +48 -17
- package/dist/sessions/state-db.js.map +1 -1
- package/dist/sessions/store.d.ts +2 -0
- package/dist/sessions/store.js +24 -5
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/streams.d.ts +3 -0
- package/dist/sessions/streams.js +0 -5
- package/dist/sessions/streams.js.map +1 -1
- package/dist/sessions/titles.d.ts +4 -0
- package/dist/sessions/titles.js +16 -0
- package/dist/sessions/titles.js.map +1 -1
- package/dist/sessions/turn-context.d.ts +1 -0
- package/dist/sessions/turn-context.js +3 -0
- package/dist/sessions/turn-context.js.map +1 -1
- package/dist/sessions/turn.d.ts +85 -7
- package/dist/sessions/turn.js +518 -170
- package/dist/sessions/turn.js.map +1 -1
- package/dist/settings.d.ts +2 -0
- package/dist/settings.js +13 -0
- package/dist/settings.js.map +1 -1
- package/dist/sidecar/fake.js +2 -0
- package/dist/sidecar/fake.js.map +1 -1
- package/dist/tools/commands.d.ts +15 -0
- package/dist/tools/commands.js +52 -0
- package/dist/tools/commands.js.map +1 -0
- package/dist/tools/crons.d.ts +38 -4
- package/dist/tools/crons.js +323 -36
- package/dist/tools/crons.js.map +1 -1
- package/dist/tools/mcp.js +5 -1
- package/dist/tools/mcp.js.map +1 -1
- package/dist/tools/update-notifications.d.ts +1 -1
- package/dist/tools/update-notifications.js +1 -1
- package/dist/tools/update-notifications.js.map +1 -1
- package/dist/tools/updates.d.ts +8 -3
- package/dist/tools/updates.js +133 -34
- package/dist/tools/updates.js.map +1 -1
- package/dist/workspace/git.d.ts +57 -10
- package/dist/workspace/git.js +302 -195
- package/dist/workspace/git.js.map +1 -1
- package/dist/workspace/media-refs.d.ts +13 -0
- package/dist/workspace/media-refs.js +165 -0
- package/dist/workspace/media-refs.js.map +1 -0
- package/dist/workspace/media.d.ts +19 -0
- package/dist/workspace/media.js +61 -2
- package/dist/workspace/media.js.map +1 -1
- package/dist/workspace/workspaces.d.ts +7 -1
- package/dist/workspace/workspaces.js +30 -21
- package/dist/workspace/workspaces.js.map +1 -1
- package/dist/workspace/worktrees.d.ts +2 -4
- package/dist/workspace/worktrees.js +28 -33
- package/dist/workspace/worktrees.js.map +1 -1
- package/package.json +4 -2
- package/sidecar/scripts/replay_sidecar.py +3 -1
- package/sidecar/talaria_sidecar/__init__.py +1 -1
- package/sidecar/talaria_sidecar/home.py +59 -2
- package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
- package/sidecar/talaria_sidecar/methods/aux.py +19 -3
- package/sidecar/talaria_sidecar/methods/chat.py +360 -27
- package/sidecar/talaria_sidecar/methods/commands.py +46 -31
- package/sidecar/talaria_sidecar/methods/config.py +14 -10
- package/sidecar/talaria_sidecar/methods/cron.py +22 -14
- package/sidecar/talaria_sidecar/methods/oauth.py +439 -0
- package/sidecar/talaria_sidecar/methods/process.py +318 -0
- package/sidecar/talaria_sidecar/methods/profiles.py +5 -4
- package/sidecar/talaria_sidecar/methods/providers.py +161 -1
- package/sidecar/talaria_sidecar/methods/runtime.py +2 -0
- package/sidecar/talaria_sidecar/methods/text.py +1 -20
- package/sidecar/talaria_sidecar/methods/usage.py +7 -1
- package/static/dist/FILES.txt +124 -119
- package/static/dist/assets/AppShell-ef3bhu32.js +1 -0
- package/static/dist/assets/Button-nx37f4u1.js +1 -0
- package/static/dist/assets/ChatPage-cdcpykcg.js +25 -0
- package/static/dist/assets/Dialog-mec42hvb.js +1 -0
- package/static/dist/assets/HubRoute-ecb65zpr.js +2 -0
- package/static/dist/assets/{Markdown-emii55o0.js → Markdown-jqrx5es0.js} +125 -125
- package/static/dist/assets/RightPanel-d1lgvk4s.js +1 -0
- package/static/dist/assets/Select-h4ivl9qf.js +1 -0
- package/static/dist/assets/SessionListPanel-ewib046b.js +1 -0
- package/static/dist/assets/SettingsLayout-lx654cup.js +1 -0
- package/static/dist/assets/States-ehsb6lzo.js +1 -0
- package/static/dist/assets/Toaster-mch1pa3t.js +1 -0
- package/static/dist/assets/_app-m8d961ef.js +1 -0
- package/static/dist/assets/_app.ext._extensionId-mp33hbzi.js +1 -0
- package/static/dist/assets/_app.index-dk6zp7e0.js +1 -0
- package/static/dist/assets/_app.insights-jt2fk4i1.js +1 -0
- package/static/dist/assets/_app.kanban-if4pubeu.js +1 -0
- package/static/dist/assets/_app.logs-mfaw9m6g.js +1 -0
- package/static/dist/assets/_app.memory-h3eaarb6.js +1 -0
- package/static/dist/assets/_app.profiles-ienktovs.js +1 -0
- package/static/dist/assets/{_app.session._sessionId-m6nx9g8t.js → _app.session._sessionId-hh27wwu2.js} +1 -1
- package/static/dist/assets/_app.settings-dpoe0bzf.js +1 -0
- package/static/dist/assets/_app.settings._section-l468ynag.js +3 -0
- package/static/dist/assets/_app.skills-kcbncc8i.js +1 -0
- package/static/dist/assets/_app.tasks-occdrqhw.js +2 -0
- package/static/dist/assets/_app.todos-nnke2rcc.js +1 -0
- package/static/dist/assets/_app.workspaces-cdlatvwj.js +1 -0
- package/static/dist/assets/{abnfDiagram-VCTEODGH-kejgxpil.js → abnfDiagram-VCTEODGH-oity9y4o.js} +1 -1
- package/static/dist/assets/architecture-7GRP2DOG-hj11333z.js +1 -0
- package/static/dist/assets/architectureDiagram-5GKGNRK7-ceaepnyr.js +36 -0
- package/static/dist/assets/back-eu64w89w.js +1 -0
- package/static/dist/assets/{blockDiagram-I7D4REHJ-l1466a7j.js → blockDiagram-I7D4REHJ-n79st4pj.js} +1 -1
- package/static/dist/assets/{c4Diagram-7LVT6UL2-gg3aq3sr.js → c4Diagram-7LVT6UL2-mi73akzk.js} +1 -1
- package/static/dist/assets/cancel-mzvkdjg8.js +1 -0
- package/static/dist/assets/channel-mbvmf2vv.js +1 -0
- package/static/dist/assets/{chunk-4HAMMTFA-oalgz5l8.js → chunk-4HAMMTFA-laat0g1q.js} +1 -1
- package/static/dist/assets/{chunk-75Z2AOVW-babmzhdg.js → chunk-75Z2AOVW-0psy1tr2.js} +1 -1
- package/static/dist/assets/{chunk-DU6HZSFF-lagu350c.js → chunk-DU6HZSFF-lw8uox8x.js} +3 -3
- package/static/dist/assets/{chunk-F27PBJKO-gjh2tqif.js → chunk-F27PBJKO-erassib3.js} +1 -1
- package/static/dist/assets/{chunk-GMAD6QVW-ipdgja5c.js → chunk-GMAD6QVW-fz7rx619.js} +1 -1
- package/static/dist/assets/{chunk-GVQU2GXP-j8wkn1at.js → chunk-GVQU2GXP-gdb0epb2.js} +1 -1
- package/static/dist/assets/{chunk-IMKFNOWR-k00laqyk.js → chunk-IMKFNOWR-ea8t26qw.js} +1 -1
- package/static/dist/assets/{chunk-L3NEJ4N5-fju6xqj4.js → chunk-L3NEJ4N5-mb1gu9kz.js} +1 -1
- package/static/dist/assets/{chunk-OSK3NFVY-bi6widqt.js → chunk-OSK3NFVY-eovmccvy.js} +1 -1
- package/static/dist/assets/{chunk-P2QGCYS3-czr4tebv.js → chunk-P2QGCYS3-bo55zjo5.js} +1 -1
- package/static/dist/assets/{chunk-POPQ4Y6H-ktdhn67w.js → chunk-POPQ4Y6H-ja7ohe4h.js} +1 -1
- package/static/dist/assets/{chunk-PWAF6VOD-ii4dsu0d.js → chunk-PWAF6VOD-e30g9nus.js} +1 -1
- package/static/dist/assets/{chunk-SHT3W25Y-fpwk3xxp.js → chunk-SHT3W25Y-gbb798f7.js} +3 -3
- package/static/dist/assets/{chunk-SVP7TREG-k0jrvhei.js → chunk-SVP7TREG-h2iw8urm.js} +1 -1
- package/static/dist/assets/{chunk-TICWLB2K-nkqv9ot0.js → chunk-TICWLB2K-fd2cottz.js} +1 -1
- package/static/dist/assets/classDiagram-ZZMXUADV-o8r05k0n.js +1 -0
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-o8r05k0n.js +1 -0
- package/static/dist/assets/{copied-gpchexqu.js → copied-h65fc3cx.js} +1 -1
- package/static/dist/assets/copy-e6977uwq.js +1 -0
- package/static/dist/assets/{cynefin-OW5HDTMX-lqxt8rs3.js → cynefin-OW5HDTMX-lbltyl5j.js} +1 -1
- package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-j9eqovf5.js → cynefinDiagram-5FMLGOSQ-lxhsqq4a.js} +1 -1
- package/static/dist/assets/{dagre-GXQ25YYZ-m9agobjm.js → dagre-GXQ25YYZ-gn4nnaln.js} +4 -4
- package/static/dist/assets/{diagram-S7CK7UJ4-itodc50c.js → diagram-S7CK7UJ4-pa3ij6q7.js} +3 -3
- package/static/dist/assets/{diagram-UQ7AKVKN-n1h2t4ah.js → diagram-UQ7AKVKN-zbipyz57.js} +1 -1
- package/static/dist/assets/{diagram-VSXAHHWV-lvtvm9bj.js → diagram-VSXAHHWV-d1kbpzgf.js} +1 -1
- package/static/dist/assets/{diagram-VX7I27RA-cgas58sy.js → diagram-VX7I27RA-i3ty14xl.js} +1 -1
- package/static/dist/assets/{diagram-Z3DM3KII-d0aeps1q.js → diagram-Z3DM3KII-m8xefqa7.js} +1 -1
- package/static/dist/assets/{ebnfDiagram-PWID7BFC-md345fyq.js → ebnfDiagram-PWID7BFC-nz7wj05l.js} +1 -1
- package/static/dist/assets/endpoints-k7yf25m0.js +64 -0
- package/static/dist/assets/{erDiagram-RLTQ6QDP-i4rua63b.js → erDiagram-RLTQ6QDP-fa41ib83.js} +1 -1
- package/static/dist/assets/eventmodeling-NTZA5JFV-kpbq09bx.js +1 -0
- package/static/dist/assets/flowDiagram-HODETNUW-lcz0kx14.js +1 -0
- package/static/dist/assets/{ganttDiagram-EL5Y4UJY-geqx7zgl.js → ganttDiagram-EL5Y4UJY-n7zq01qu.js} +1 -1
- package/static/dist/assets/{gitGraph-4MIJSDKK-leoq0z8t.js → gitGraph-4MIJSDKK-c89emb07.js} +1 -1
- package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-llrlxfha.js → gitGraphDiagram-WWUBYQGX-cz7cgr6w.js} +1 -1
- package/static/dist/assets/{highlighted-body-KPVGNVTW-kq2cnw6q.js → highlighted-body-KPVGNVTW-d9bapmni.js} +1 -1
- package/static/dist/assets/{index-f9bzglkn.js → index-h00usru2.js} +3 -3
- package/static/dist/assets/index-n73ijkv2.css +2 -0
- package/static/dist/assets/{info-A6RAGUB7-lkly4xj6.js → info-A6RAGUB7-ndddhhhh.js} +1 -1
- package/static/dist/assets/infoDiagram-27XIBGKW-ccxigcpf.js +2 -0
- package/static/dist/assets/{ishikawaDiagram-5VMMS53U-hq6jf374.js → ishikawaDiagram-5VMMS53U-le0z9vtc.js} +1 -1
- package/static/dist/assets/{journeyDiagram-3NMN7TZE-is03ntb0.js → journeyDiagram-3NMN7TZE-ivsmb26o.js} +1 -1
- package/static/dist/assets/{kanban-definition-UXKFOSKX-jmerizqb.js → kanban-definition-UXKFOSKX-h6n2awt0.js} +1 -1
- package/static/dist/assets/lazyRouteComponent-m1rtyk4t.js +1 -0
- package/static/dist/assets/{line-laaa8h99.js → line-dmzlfe0u.js} +1 -1
- package/static/dist/assets/link-budeg5t9.js +1 -0
- package/static/dist/assets/login-kjhasvp8.js +1 -0
- package/static/dist/assets/mermaid-HWGCJPDP-jb5d211j.js +1 -0
- package/static/dist/assets/{mermaid-parser.core-ix4junux.js → mermaid-parser.core-gwyd5bnq.js} +3 -3
- package/static/dist/assets/{mindmap-definition-YA3MSWOX-o0e0adl1.js → mindmap-definition-YA3MSWOX-l68gg5h7.js} +1 -1
- package/static/dist/assets/onboarding-cdfrk0pj.js +1 -0
- package/static/dist/assets/{packet-AYTQ26CC-kl8pwsyg.js → packet-AYTQ26CC-gpv06065.js} +1 -1
- package/static/dist/assets/{pegDiagram-XKGWAZYB-bbqn9jo4.js → pegDiagram-XKGWAZYB-e52vs23u.js} +1 -1
- package/static/dist/assets/{pie-WAS4IAKB-cpyo8bkr.js → pie-WAS4IAKB-l6m7nz5s.js} +1 -1
- package/static/dist/assets/{pieDiagram-E7YTZNPT-pelug34f.js → pieDiagram-E7YTZNPT-b4is36bq.js} +3 -3
- package/static/dist/assets/{quadrantDiagram-AXDQQJYC-jds4jmr5.js → quadrantDiagram-AXDQQJYC-m8vt2788.js} +1 -1
- package/static/dist/assets/{radar-RG4KPBEZ-e02oxnvd.js → radar-RG4KPBEZ-nxbrhiov.js} +1 -1
- package/static/dist/assets/{railroad-74A4TZTK-kz3delwo.js → railroad-74A4TZTK-222mu9hi.js} +1 -1
- package/static/dist/assets/railroad-abnf-HS5TGJTU-kdvgl6x8.js +1 -0
- package/static/dist/assets/railroad-ebnf-LZEXJU2U-itiqslby.js +1 -0
- package/static/dist/assets/railroad-peg-WCYAUIDC-go7kov78.js +1 -0
- package/static/dist/assets/{railroadDiagram-O6MQD6OU-jgroj2ew.js → railroadDiagram-O6MQD6OU-o8lldfzv.js} +1 -1
- package/static/dist/assets/{refresh-cw-k7yxjdn3.js → refresh-cw-kbcje1uy.js} +1 -1
- package/static/dist/assets/{requirementDiagram-BXWQKSXE-m4az5e0j.js → requirementDiagram-BXWQKSXE-ovyq38kv.js} +1 -1
- package/static/dist/assets/{sankeyDiagram-P5KCCOFB-vlryqiv3.js → sankeyDiagram-P5KCCOFB-ou7lznor.js} +1 -1
- package/static/dist/assets/{saved-brdfjjnw.js → saved-bazmcup9.js} +1 -1
- package/static/dist/assets/search-jen74z4u.js +1 -0
- package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-fr8hrpql.js → sequenceDiagram-WJ2MYXX4-ovckdzf2.js} +1 -1
- package/static/dist/assets/{settings_section_extensions_title-l0zodj0b.js → settings_section_extensions_title-ertwwhlx.js} +1 -1
- package/static/dist/assets/share._token-dan9hgvj.js +2 -0
- package/static/dist/assets/{sliders-horizontal-gcgscv8r.js → sliders-horizontal-f9cb3lh3.js} +1 -1
- package/static/dist/assets/{stateDiagram-D77RDMKH-bg548iyr.js → stateDiagram-D77RDMKH-fcrexkkv.js} +1 -1
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-lcfraua2.js +1 -0
- package/static/dist/assets/{swimlanes-42K2YHIH-hk5z3efh.js → swimlanes-42K2YHIH-f7usu8fw.js} +1 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-ogmanni0.js +8 -0
- package/static/dist/assets/{timeline-definition-24CTP7MA-olvknr62.js → timeline-definition-24CTP7MA-gp1hvz9f.js} +1 -1
- package/static/dist/assets/todoStore-eqtid4r4.js +1 -0
- package/static/dist/assets/{treeView-Q6P3EWNA-ovvjsxtf.js → treeView-Q6P3EWNA-gb9gf11u.js} +1 -1
- package/static/dist/assets/{treemap-WGGIJYW6-ggkipj59.js → treemap-WGGIJYW6-nuvfnvvi.js} +1 -1
- package/static/dist/assets/{untitled-c8ydw0bu.js → untitled-htelhc68.js} +1 -1
- package/static/dist/assets/useForm-h2t0u14e.js +1 -0
- package/static/dist/assets/useLocale-hcsb84pj.js +1 -0
- package/static/dist/assets/useNavigate-nwy70ztj.js +1 -0
- package/static/dist/assets/{useParams-katwvzil.js → useParams-dqd64jv7.js} +1 -1
- package/static/dist/assets/{useSearch-c70sgau1.js → useSearch-j30pvsy7.js} +1 -1
- package/static/dist/assets/{vennDiagram-4TSXK5OY-hzld7zt7.js → vennDiagram-4TSXK5OY-fzchbzaj.js} +1 -1
- package/static/dist/assets/{wardley-WFR3VGLG-hxkeoms8.js → wardley-WFR3VGLG-m47mhfbh.js} +1 -1
- package/static/dist/assets/{wardleyDiagram-VM6X3IG4-ljh90mb1.js → wardleyDiagram-VM6X3IG4-odg6ct1z.js} +2 -2
- package/static/dist/assets/{xychartDiagram-S5SC5T6Z-mnuaes03.js → xychartDiagram-S5SC5T6Z-mrhms5um.js} +1 -1
- package/static/dist/index.html +1 -1
- package/static/dist/sw.js +1 -1
- package/static/dist/assets/AppShell-haqi9qzu.js +0 -1
- package/static/dist/assets/ChatPage-hjyu2g64.js +0 -22
- package/static/dist/assets/HubRoute-gkd8o1vg.js +0 -2
- package/static/dist/assets/RightPanel-o73y98dn.js +0 -1
- package/static/dist/assets/Select-jld3yeni.js +0 -1
- package/static/dist/assets/SessionListPanel-d3qx4ric.js +0 -1
- package/static/dist/assets/SettingsLayout-lnn2wo2s.js +0 -1
- package/static/dist/assets/States-e5hv6gdh.js +0 -1
- package/static/dist/assets/Toaster-c4jq1i26.js +0 -1
- package/static/dist/assets/_app-bnip2pjg.js +0 -1
- package/static/dist/assets/_app.ext._extensionId-egptzrfy.js +0 -1
- package/static/dist/assets/_app.index-vdz13ul5.js +0 -1
- package/static/dist/assets/_app.insights-lgjshsba.js +0 -1
- package/static/dist/assets/_app.kanban-c18cjrpz.js +0 -1
- package/static/dist/assets/_app.logs-ezhojgeg.js +0 -1
- package/static/dist/assets/_app.memory-i00paisv.js +0 -1
- package/static/dist/assets/_app.profiles-kd02bwjx.js +0 -1
- package/static/dist/assets/_app.settings-giaudkuk.js +0 -1
- package/static/dist/assets/_app.settings._section-k4qtlexk.js +0 -1
- package/static/dist/assets/_app.skills-bby2uvl8.js +0 -1
- package/static/dist/assets/_app.tasks-1kz9bosm.js +0 -2
- package/static/dist/assets/_app.todos-izjdbkm2.js +0 -1
- package/static/dist/assets/_app.workspaces-lfh2kw7s.js +0 -1
- package/static/dist/assets/architecture-7GRP2DOG-b4u9z1w3.js +0 -1
- package/static/dist/assets/architectureDiagram-5GKGNRK7-hqnf84fc.js +0 -36
- package/static/dist/assets/channel-o77ioktg.js +0 -1
- package/static/dist/assets/classDiagram-ZZMXUADV-f5rvod26.js +0 -1
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-f5rvod26.js +0 -1
- package/static/dist/assets/endpoints-e58saih3.js +0 -9
- package/static/dist/assets/eventmodeling-NTZA5JFV-bn3fjsaq.js +0 -1
- package/static/dist/assets/flowDiagram-HODETNUW-li70d120.js +0 -1
- package/static/dist/assets/index-gkh7kum4.css +0 -2
- package/static/dist/assets/infoDiagram-27XIBGKW-oo7kdyte.js +0 -2
- package/static/dist/assets/lazyRouteComponent-b1h2ykmb.js +0 -1
- package/static/dist/assets/link-d8f0788e.js +0 -1
- package/static/dist/assets/login-llkohrcg.js +0 -1
- package/static/dist/assets/mermaid-HWGCJPDP-id0ffk2r.js +0 -1
- package/static/dist/assets/onboarding-lx0d6b38.js +0 -1
- package/static/dist/assets/railroad-abnf-HS5TGJTU-l13ped2z.js +0 -1
- package/static/dist/assets/railroad-ebnf-LZEXJU2U-c7rg3g4g.js +0 -1
- package/static/dist/assets/railroad-peg-WCYAUIDC-od0b8niq.js +0 -1
- package/static/dist/assets/search-gt2ueyvg.js +0 -1
- package/static/dist/assets/share._token-oyjv046r.js +0 -2
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-j5v40qih.js +0 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-grzefo9c.js +0 -8
- package/static/dist/assets/tabBuild-idliyw9f.js +0 -56
- package/static/dist/assets/todoStore-j4c2xq0b.js +0 -1
- package/static/dist/assets/useForm-kd759sjk.js +0 -1
- package/static/dist/assets/useLocale-hds4xmja.js +0 -1
|
@@ -11,6 +11,7 @@ through ``approval.respond`` / ``clarify.respond``; ``rpc.cancel`` or
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
13
|
import contextlib
|
|
14
|
+
import contextvars
|
|
14
15
|
import hashlib
|
|
15
16
|
import inspect
|
|
16
17
|
import json
|
|
@@ -257,13 +258,86 @@ def _clarify_timeout(params: dict) -> int:
|
|
|
257
258
|
return 3600
|
|
258
259
|
|
|
259
260
|
|
|
261
|
+
def _steer_slot_lock(agent):
|
|
262
|
+
"""The Agent's own pending-steer lock (a plain ``Lock``), so a slot read or rewrite is atomic against its drains."""
|
|
263
|
+
lock = agent.__dict__.get("_pending_steer_lock") if hasattr(agent, "__dict__") else None
|
|
264
|
+
return contextlib.nullcontext() if lock is None else lock
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _steer_slot(agent) -> str:
|
|
268
|
+
"""The Agent's not-yet-applied steer text; call under ``_steer_slot_lock``."""
|
|
269
|
+
return str(getattr(agent, "_pending_steer", "") or "")
|
|
270
|
+
|
|
271
|
+
|
|
260
272
|
def _agent_pending_steer_text(agent) -> str:
|
|
261
273
|
"""Predecessor ``_agent_pending_steer_text``: the Agent's not-yet-applied steer text."""
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
274
|
+
with _steer_slot_lock(agent):
|
|
275
|
+
return _steer_slot(agent)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def _slot_without(slot: str, pending: list[str], index: int) -> str | None:
|
|
279
|
+
"""``slot`` without the steer ``pending[index]`` while the Agent still holds it, else None (it already took it).
|
|
280
|
+
|
|
281
|
+
The Agent keeps every not-yet-applied steer in one newline-joined slot, so the slot is the tail of ``pending``
|
|
282
|
+
(the server's steers, oldest first), possibly behind text another surface queued: the server's
|
|
283
|
+
``pendingSteerSuffixStart`` rule. The longest tail that still holds the steer wins."""
|
|
284
|
+
for start in range(index + 1):
|
|
285
|
+
tail = "\n".join(pending[start:])
|
|
286
|
+
if slot == tail or slot.endswith("\n" + tail):
|
|
287
|
+
head = slot[: len(slot) - len(tail)].rstrip("\n")
|
|
288
|
+
return "\n".join(part for part in [head, *pending[start:index], *pending[index + 1:]] if part)
|
|
289
|
+
return None
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def withdraw_steer(agent, pending: list[str], index: int) -> bool:
|
|
293
|
+
"""TAL-424: take a not-yet-applied steer back out of the Agent's slot, keeping the others in order. False when the
|
|
294
|
+
Agent already took it; the slot is then left as it was, so no text is ever lost."""
|
|
295
|
+
with _steer_slot_lock(agent):
|
|
296
|
+
rest = _slot_without(_steer_slot(agent), pending, index)
|
|
297
|
+
if rest is None:
|
|
298
|
+
return False
|
|
299
|
+
agent._pending_steer = rest or None
|
|
300
|
+
return True
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def steer_now(agent, pending: list[str], index: int) -> dict:
|
|
304
|
+
"""TAL-424: deliver a pending steer now with the Agent's ``redirect``. During a model request that request restarts
|
|
305
|
+
with it (``delivery: redirect``); during tools it goes back on the slot and the tools yield (``delivery: steer``).
|
|
306
|
+
With no live request the steer stays pending: back in its place, or last when the slot changed meanwhile."""
|
|
307
|
+
text = pending[index]
|
|
308
|
+
with _steer_slot_lock(agent):
|
|
309
|
+
before = _steer_slot(agent)
|
|
310
|
+
rest = _slot_without(before, pending, index)
|
|
311
|
+
if rest is None:
|
|
312
|
+
return {"redirected": False, "withdrawn": False}
|
|
313
|
+
agent._pending_steer = rest or None
|
|
314
|
+
redirect = getattr(agent, "redirect", None)
|
|
315
|
+
try:
|
|
316
|
+
redirected = bool(redirect(text)) if callable(redirect) else False
|
|
317
|
+
except Exception as exc: # noqa: BLE001
|
|
318
|
+
log.debug("redirect failed: %s", exc)
|
|
319
|
+
redirected = False
|
|
320
|
+
if not redirected:
|
|
321
|
+
with _steer_slot_lock(agent):
|
|
322
|
+
if _steer_slot(agent) == rest:
|
|
323
|
+
agent._pending_steer = before or None
|
|
324
|
+
return {"redirected": False, "withdrawn": True, "requeued": "kept"}
|
|
325
|
+
agent.steer(text)
|
|
326
|
+
return {"redirected": False, "withdrawn": True, "requeued": "last"}
|
|
327
|
+
with _steer_slot_lock(agent):
|
|
328
|
+
slot = _steer_slot(agent)
|
|
329
|
+
delivery = "steer" if slot == text or slot.endswith("\n" + text) else "redirect"
|
|
330
|
+
return {"redirected": True, "withdrawn": True, "delivery": delivery}
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def _steer_queue_params(params: dict) -> tuple[list[str], int]:
|
|
334
|
+
pending = params.get("pending")
|
|
335
|
+
index = params.get("index")
|
|
336
|
+
if not isinstance(pending, list) or not all(isinstance(t, str) and t.strip() for t in pending):
|
|
337
|
+
raise InvalidParams("pending must be a list of steer texts")
|
|
338
|
+
if not isinstance(index, int) or isinstance(index, bool) or not 0 <= index < len(pending):
|
|
339
|
+
raise InvalidParams("index must point into pending")
|
|
340
|
+
return [t.strip() for t in pending], index
|
|
267
341
|
|
|
268
342
|
|
|
269
343
|
def _cancel_checkpoint(run: _Run) -> list | None:
|
|
@@ -299,7 +373,7 @@ def _resolve_runtime(provider: str | None, model: str) -> dict:
|
|
|
299
373
|
def _turn_identity(session_id: str, workspace: str):
|
|
300
374
|
"""Bind this turn's identity to the calling context the way the predecessor's ``_set_turn_session_identity`` did:
|
|
301
375
|
the approval session key (so ``register_gateway_notify`` cards reach this turn and dangerous commands are gated
|
|
302
|
-
instead of auto-approved), the gateway session vars (platform ``webui``, chat/ui session ids), and the session cwd
|
|
376
|
+
instead of auto-approved), the gateway session vars (platform ``webui``, chat/ui session ids, profile), and the session cwd
|
|
303
377
|
(so terminals and AGENTS.md discovery run in the selected workspace, not the sidecar's launch directory). Every
|
|
304
378
|
binding is a context variable, so concurrent turns cannot overwrite each other. Missing Agent surfaces are logged,
|
|
305
379
|
not fatal."""
|
|
@@ -315,10 +389,14 @@ def _turn_identity(session_id: str, workspace: str):
|
|
|
315
389
|
log.debug("per-turn approval session-key bind failed", exc_info=True)
|
|
316
390
|
try:
|
|
317
391
|
from gateway import session_context as sc
|
|
392
|
+
from hermes_cli.profiles import get_active_profile_name
|
|
393
|
+
from hermes_constants import get_hermes_home, profile_name_for_home
|
|
318
394
|
|
|
395
|
+
# The persistent Docker sandbox is keyed by the profile name; unbound, every profile reuses "default".
|
|
396
|
+
profile = profile_name_for_home(get_hermes_home()) or get_active_profile_name()
|
|
319
397
|
pairs = [
|
|
320
398
|
(sc._SESSION_KEY, session_id), (sc._SESSION_UI_SESSION_ID, session_id), (sc._SESSION_PLATFORM, "webui"),
|
|
321
|
-
(sc._SESSION_CHAT_ID, session_id), (sc._SESSION_ID, session_id),
|
|
399
|
+
(sc._SESSION_CHAT_ID, session_id), (sc._SESSION_ID, session_id), (sc._SESSION_PROFILE, profile),
|
|
322
400
|
]
|
|
323
401
|
for var, value in pairs:
|
|
324
402
|
tok = var.set(value)
|
|
@@ -342,6 +420,63 @@ def _turn_identity(session_id: str, workspace: str):
|
|
|
342
420
|
log.debug("per-turn identity reset failed", exc_info=True)
|
|
343
421
|
|
|
344
422
|
|
|
423
|
+
#: Older configs named the CLI composite ``hermes``; the Agent registers it split in two (predecessor
|
|
424
|
+
#: ``_LEGACY_CLI_TOOLSET_ALIASES``).
|
|
425
|
+
_LEGACY_TOOLSET_ALIASES = {"hermes": ("hermes-cli", "hermes-api-server")}
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def _turn_toolsets(toolsets) -> list[str]:
|
|
429
|
+
"""The session's override as sent, else the profile's configured toolsets. ``None`` would hand the Agent every
|
|
430
|
+
registered toolset."""
|
|
431
|
+
return toolsets if isinstance(toolsets, list) else _profile_toolsets()
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def _profile_toolsets() -> list[str]:
|
|
435
|
+
"""The profile's ``platform_toolsets.cli`` through the Agent's own resolver (default-off toolsets stay off), like
|
|
436
|
+
``hermes chat``. Runs under the call's ``scoped_home``."""
|
|
437
|
+
from hermes_cli.config import load_config
|
|
438
|
+
from hermes_cli.tools_config import _get_platform_tools
|
|
439
|
+
|
|
440
|
+
resolved = sorted(_get_platform_tools(load_config() or {}, "cli"))
|
|
441
|
+
return list(dict.fromkeys(name for raw in resolved for name in _LEGACY_TOOLSET_ALIASES.get(raw, (raw,))))
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def _discover_mcp_tools(run: _Run) -> None:
|
|
445
|
+
"""Connect the profile's MCP servers before the turn's agent snapshots its tools, like the predecessor's turn start.
|
|
446
|
+
The Agent keys connections by profile scope and skips live ones, so later turns are cheap. Runs under the call's
|
|
447
|
+
``scoped_home``; a failure leaves the turn without MCP tools rather than failing it. A Stop does not wait for a slow
|
|
448
|
+
server: the turn moves on to its cancelled end while the connect finishes in the background."""
|
|
449
|
+
from .commands import retire_unscoped_launch_mcp_servers
|
|
450
|
+
|
|
451
|
+
done = threading.Event()
|
|
452
|
+
|
|
453
|
+
def discover() -> None:
|
|
454
|
+
try:
|
|
455
|
+
from tools.mcp_tool_discovery import discover_mcp_tools
|
|
456
|
+
|
|
457
|
+
retire_unscoped_launch_mcp_servers()
|
|
458
|
+
discover_mcp_tools()
|
|
459
|
+
except Exception: # noqa: BLE001
|
|
460
|
+
log.warning("MCP discovery failed", exc_info=True)
|
|
461
|
+
finally:
|
|
462
|
+
done.set()
|
|
463
|
+
|
|
464
|
+
# The copied context carries the turn's home override and secret scope into the worker.
|
|
465
|
+
threading.Thread(target=contextvars.copy_context().run, args=(discover,), daemon=True, name=f"chat-mcp-{run.stream_id[:8]}").start()
|
|
466
|
+
while not done.wait(0.25):
|
|
467
|
+
if run.cancel.is_set() or run.ctx.cancelled:
|
|
468
|
+
return
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def _profile_fallback_chain() -> list[dict] | None:
|
|
472
|
+
"""The profile's fallback routes (``fallback_providers`` first, then legacy ``fallback_model``, deduplicated)
|
|
473
|
+
through the Agent's own resolver, like the CLI and gateway. Runs under the call's ``scoped_home``."""
|
|
474
|
+
from hermes_cli.config import load_config
|
|
475
|
+
from hermes_cli.fallback_config import get_fallback_chain
|
|
476
|
+
|
|
477
|
+
return get_fallback_chain(load_config() or {}) or None
|
|
478
|
+
|
|
479
|
+
|
|
345
480
|
def _agent_signature(model: str, provider, runtime: dict, toolsets, home: str, kwargs: dict) -> str:
|
|
346
481
|
"""Cache identity of an ``AIAgent``: everything its constructor bound from the resolved runtime, so a rotated key,
|
|
347
482
|
a different API mode, ACP command, or credential pool never reuses an agent built for the old bundle. The key
|
|
@@ -357,6 +492,8 @@ def _agent_signature(model: str, provider, runtime: dict, toolsets, home: str, k
|
|
|
357
492
|
"max_iterations": kwargs.get("max_iterations"), "max_tokens": kwargs.get("max_tokens"),
|
|
358
493
|
# Bound at construction too: a reasoning-effort change from the composer must build a fresh agent.
|
|
359
494
|
"reasoning_config": kwargs.get("reasoning_config"),
|
|
495
|
+
# Entries may carry their own ``api_key``.
|
|
496
|
+
"fallback_model": hashlib.sha256(json.dumps(kwargs.get("fallback_model"), sort_keys=True, default=str).encode("utf-8")).hexdigest()[:16],
|
|
360
497
|
}
|
|
361
498
|
return json.dumps(bundle, sort_keys=True, default=str)
|
|
362
499
|
|
|
@@ -399,6 +536,34 @@ def _supported(cls, name: str) -> bool:
|
|
|
399
536
|
return name in parameters or any(p.kind is inspect.Parameter.VAR_KEYWORD for p in parameters.values())
|
|
400
537
|
|
|
401
538
|
|
|
539
|
+
def _approval_notifier(session_id: str, emit):
|
|
540
|
+
"""The Agent's gateway notify callback for one turn: emits each parked approval, then ``approval_resolved``
|
|
541
|
+
(id plus the Agent's reason) once its wait ends by any path, an ``approvals.timeout`` expiry included (TAL-514)."""
|
|
542
|
+
try:
|
|
543
|
+
from tools.approval import register_gateway_settle
|
|
544
|
+
except Exception: # noqa: BLE001
|
|
545
|
+
register_gateway_settle = None
|
|
546
|
+
|
|
547
|
+
def notify(data):
|
|
548
|
+
payload = dict(data or {})
|
|
549
|
+
payload.setdefault("session_id", session_id)
|
|
550
|
+
payload.setdefault("approval_id", str(payload.get("request_id") or uuid.uuid4().hex))
|
|
551
|
+
emit("approval", payload)
|
|
552
|
+
request_id = str(payload.get("request_id") or "")
|
|
553
|
+
if register_gateway_settle is None or not request_id:
|
|
554
|
+
return
|
|
555
|
+
approval_id = payload["approval_id"]
|
|
556
|
+
|
|
557
|
+
def settle(reason):
|
|
558
|
+
emit("approval_resolved", {"approval_id": approval_id, "session_id": session_id, "reason": str(reason or "")})
|
|
559
|
+
|
|
560
|
+
# False: the wait already ended between the emit and this registration.
|
|
561
|
+
if not register_gateway_settle(session_id, request_id, settle):
|
|
562
|
+
settle("resolved")
|
|
563
|
+
|
|
564
|
+
return notify
|
|
565
|
+
|
|
566
|
+
|
|
402
567
|
def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn, one function
|
|
403
568
|
session_id = str(params.get("session_id") or "").strip()
|
|
404
569
|
stream_id = str(params.get("stream_id") or "").strip()
|
|
@@ -414,7 +579,7 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
|
|
|
414
579
|
history = []
|
|
415
580
|
if not isinstance(history, list):
|
|
416
581
|
raise InvalidParams("conversation_history must be a list")
|
|
417
|
-
toolsets = params.get("enabled_toolsets")
|
|
582
|
+
toolsets = _turn_toolsets(params.get("enabled_toolsets"))
|
|
418
583
|
system_message = params.get("system_message")
|
|
419
584
|
run = _Run(stream_id, session_id, ctx)
|
|
420
585
|
with _RUNS_LOCK:
|
|
@@ -437,6 +602,7 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
|
|
|
437
602
|
raw_emit("steer_pending", {"text": pending})
|
|
438
603
|
raw_emit(event, data)
|
|
439
604
|
try:
|
|
605
|
+
_discover_mcp_tools(run)
|
|
440
606
|
runtime = _resolve_runtime(provider, model)
|
|
441
607
|
resolved_model = model or str(runtime.get("model") or "")
|
|
442
608
|
resolved_provider = provider or runtime.get("provider")
|
|
@@ -553,15 +719,7 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
|
|
|
553
719
|
from tools.approval import register_gateway_notify, unregister_gateway_notify
|
|
554
720
|
except Exception: # noqa: BLE001
|
|
555
721
|
register_gateway_notify = unregister_gateway_notify = None
|
|
556
|
-
if register_gateway_notify is not None
|
|
557
|
-
def _approval_cb(data):
|
|
558
|
-
payload = dict(data or {})
|
|
559
|
-
payload.setdefault("session_id", session_id)
|
|
560
|
-
payload.setdefault("approval_id", str(payload.get("request_id") or uuid.uuid4().hex))
|
|
561
|
-
emit("approval", payload)
|
|
562
|
-
approval_cb = _approval_cb
|
|
563
|
-
else:
|
|
564
|
-
approval_cb = None
|
|
722
|
+
approval_cb = _approval_notifier(session_id, emit) if register_gateway_notify is not None else None
|
|
565
723
|
|
|
566
724
|
kwargs: dict = dict(
|
|
567
725
|
model=resolved_model,
|
|
@@ -570,7 +728,7 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
|
|
|
570
728
|
api_key=runtime.get("api_key"),
|
|
571
729
|
platform="webui",
|
|
572
730
|
quiet_mode=True,
|
|
573
|
-
enabled_toolsets=toolsets
|
|
731
|
+
enabled_toolsets=toolsets,
|
|
574
732
|
session_id=session_id,
|
|
575
733
|
stream_delta_callback=on_token,
|
|
576
734
|
reasoning_callback=on_reasoning,
|
|
@@ -598,6 +756,9 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
|
|
|
598
756
|
reasoning_config = params.get("reasoning_config")
|
|
599
757
|
if isinstance(reasoning_config, dict) and _supported(AIAgent, "reasoning_config"):
|
|
600
758
|
kwargs["reasoning_config"] = reasoning_config
|
|
759
|
+
fallback_chain = _profile_fallback_chain()
|
|
760
|
+
if fallback_chain and _supported(AIAgent, "fallback_model"):
|
|
761
|
+
kwargs["fallback_model"] = fallback_chain
|
|
601
762
|
signature = _agent_signature(resolved_model, resolved_provider, runtime, toolsets, str(params.get("profile_home")), kwargs)
|
|
602
763
|
agent = None
|
|
603
764
|
if not session_busy:
|
|
@@ -699,14 +860,22 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
|
|
|
699
860
|
error = str(last_error)
|
|
700
861
|
if not error and isinstance(result.get("error"), str) and result.get("error"):
|
|
701
862
|
error = str(result["error"])
|
|
702
|
-
|
|
863
|
+
# The Agent ends a failed turn with ``failed``/``partial`` (never ``status``), often after text streamed and with
|
|
864
|
+
# the turn's messages; a failed turn is an error whatever it carries.
|
|
865
|
+
failed = bool(result.get("failed"))
|
|
866
|
+
partial = bool(result.get("partial"))
|
|
867
|
+
if not error and failed and result.get("final_response"):
|
|
868
|
+
error = str(result["final_response"])
|
|
869
|
+
status = "cancelled" if cancelled else ("error" if failed or (error and not result.get("messages")) else "completed")
|
|
703
870
|
pending_steer = str(result.get("pending_steer") or "") if isinstance(result, dict) else ""
|
|
704
871
|
return {
|
|
705
872
|
"status": status,
|
|
706
873
|
"messages": [m for m in (result.get("messages") or []) if isinstance(m, dict)],
|
|
707
874
|
"final_response": str(result.get("final_response") or ""),
|
|
708
875
|
"error": error,
|
|
709
|
-
"
|
|
876
|
+
"failed": failed,
|
|
877
|
+
"partial": partial,
|
|
878
|
+
"compression_exhausted": bool(result.get("compression_exhausted")),
|
|
710
879
|
"tool_limit_reached": bool(result.get("tool_limit_reached") or result.get("max_iterations_reached")),
|
|
711
880
|
"usage": _usage(agent),
|
|
712
881
|
"context": _context_length(agent),
|
|
@@ -725,6 +894,131 @@ def start(ctx: CallContext, params: dict) -> dict: # noqa: PLR0915 - one turn,
|
|
|
725
894
|
_RUNS_BY_SESSION.pop(session_id, None)
|
|
726
895
|
|
|
727
896
|
|
|
897
|
+
def _checkpoint_required() -> bool:
|
|
898
|
+
"""``compression.checkpoint_required`` needs the memory provider loaded to write the pre-compression checkpoint."""
|
|
899
|
+
try:
|
|
900
|
+
from hermes_cli.config import load_config
|
|
901
|
+
|
|
902
|
+
return ((load_config() or {}).get("compression") or {}).get("checkpoint_required") is True
|
|
903
|
+
except Exception: # noqa: BLE001
|
|
904
|
+
return False
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
#: Compressed results awaiting the server's write: ``commit_token -> (agent, profile home, expiry timer)``.
|
|
908
|
+
_PENDING_COMPRESSIONS: dict[str, tuple[Any, Any, threading.Timer]] = {}
|
|
909
|
+
_PENDING_COMPRESSIONS_LOCK = threading.Lock()
|
|
910
|
+
#: A server that never answers (restart, crash) gets its compression discarded after this long.
|
|
911
|
+
_PENDING_COMPRESSION_TTL = 600.0
|
|
912
|
+
|
|
913
|
+
|
|
914
|
+
def _release_compression(agent, committed: bool) -> None:
|
|
915
|
+
"""Emit (committed) or discard the Agent's deferred context-engine notification, then close the throwaway agent."""
|
|
916
|
+
from agent.conversation_compression import finalize_context_engine_compression_notification
|
|
917
|
+
|
|
918
|
+
try:
|
|
919
|
+
finalize_context_engine_compression_notification(agent, committed=committed)
|
|
920
|
+
finally:
|
|
921
|
+
with contextlib.suppress(Exception):
|
|
922
|
+
agent._end_session_on_close = False
|
|
923
|
+
close = getattr(agent, "close", None)
|
|
924
|
+
if callable(close):
|
|
925
|
+
try:
|
|
926
|
+
close()
|
|
927
|
+
except Exception: # noqa: BLE001
|
|
928
|
+
log.debug("compression agent close failed", exc_info=True)
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
def finalize_compression(token: str, committed: bool) -> bool:
|
|
932
|
+
"""``chat.compress_finalize``: the server reports whether it installed the result; unknown tokens are a no-op."""
|
|
933
|
+
with _PENDING_COMPRESSIONS_LOCK:
|
|
934
|
+
entry = _PENDING_COMPRESSIONS.pop(token, None)
|
|
935
|
+
if entry is None:
|
|
936
|
+
return False
|
|
937
|
+
agent, home, expiry = entry
|
|
938
|
+
expiry.cancel() # a no-op when the timer itself is finalizing
|
|
939
|
+
with scoped_home(home):
|
|
940
|
+
_release_compression(agent, committed=committed)
|
|
941
|
+
return True
|
|
942
|
+
|
|
943
|
+
|
|
944
|
+
def compress(ctx: CallContext, params: dict) -> dict:
|
|
945
|
+
"""Manual ``/compress`` of ``conversation_history`` through the Agent's shared core (``compress_now``) on a throwaway
|
|
946
|
+
agent, like the gateway's ``_run_manual_compression``. The server owns the transcript: it re-checks the session and
|
|
947
|
+
installs ``messages`` itself, so nothing here touches history or the cached turn agent. The agent has no session
|
|
948
|
+
store, so the Agent neither rotates nor writes state.db; its one deferred effect, the context-engine notification,
|
|
949
|
+
waits for ``chat.compress_finalize`` with the returned ``commit_token`` (two-phase, like the gateway's commit)."""
|
|
950
|
+
home = profile_home_param(params)
|
|
951
|
+
session_id = str(params.get("session_id") or "").strip()
|
|
952
|
+
if not session_id:
|
|
953
|
+
raise InvalidParams("session_id is required")
|
|
954
|
+
history = params.get("conversation_history")
|
|
955
|
+
if not isinstance(history, list):
|
|
956
|
+
raise InvalidParams("conversation_history must be a list")
|
|
957
|
+
model = str(params.get("model") or "").strip()
|
|
958
|
+
provider = str(params.get("model_provider") or "").strip() or None
|
|
959
|
+
focus_topic = str(params.get("focus_topic") or "").strip()[:500] or None
|
|
960
|
+
toolsets = _turn_toolsets(params.get("enabled_toolsets"))
|
|
961
|
+
runtime = _resolve_runtime(provider, model)
|
|
962
|
+
if not runtime.get("api_key"):
|
|
963
|
+
raise RpcError("No provider configured -- cannot compress.", condition="credential_missing")
|
|
964
|
+
from agent.conversation_compression_manual import CompressRequest, compress_now
|
|
965
|
+
|
|
966
|
+
AIAgent = _agent_class()
|
|
967
|
+
kwargs: dict = dict(
|
|
968
|
+
model=model or str(runtime.get("model") or ""),
|
|
969
|
+
provider=provider or runtime.get("provider"),
|
|
970
|
+
base_url=runtime.get("base_url"),
|
|
971
|
+
api_key=runtime.get("api_key"),
|
|
972
|
+
platform="webui",
|
|
973
|
+
quiet_mode=True,
|
|
974
|
+
enabled_toolsets=toolsets,
|
|
975
|
+
session_id=session_id,
|
|
976
|
+
)
|
|
977
|
+
for name, value in (
|
|
978
|
+
("api_mode", runtime.get("api_mode")),
|
|
979
|
+
("acp_command", runtime.get("acp_command")),
|
|
980
|
+
("acp_args", runtime.get("acp_args")),
|
|
981
|
+
("credential_pool", runtime.get("credential_pool")),
|
|
982
|
+
("gateway_session_key", session_id),
|
|
983
|
+
("skip_memory", not _checkpoint_required()),
|
|
984
|
+
):
|
|
985
|
+
if _supported(AIAgent, name) and value is not None:
|
|
986
|
+
kwargs[name] = value
|
|
987
|
+
agent = AIAgent(**kwargs)
|
|
988
|
+
held = False
|
|
989
|
+
try:
|
|
990
|
+
result = compress_now(agent, history, CompressRequest(focus_topic=focus_topic), task_id=session_id)
|
|
991
|
+
message = None
|
|
992
|
+
if result.status != "compressed":
|
|
993
|
+
from agent.conversation_compression_manual import render_compress_result
|
|
994
|
+
|
|
995
|
+
message = "\n".join(render_compress_result(result)) or None
|
|
996
|
+
payload = {
|
|
997
|
+
"status": result.status,
|
|
998
|
+
"messages": json.loads(json.dumps([m for m in result.after_messages if isinstance(m, dict)], default=str)),
|
|
999
|
+
"before_tokens": int(result.before_tokens or 0),
|
|
1000
|
+
"after_tokens": int(result.after_tokens or 0),
|
|
1001
|
+
"summary": json.loads(json.dumps(result.summary, default=str)) if isinstance(result.summary, dict) else None,
|
|
1002
|
+
"message": message,
|
|
1003
|
+
"agent_session_id": str(getattr(agent, "session_id", None) or session_id),
|
|
1004
|
+
"commit_token": None,
|
|
1005
|
+
}
|
|
1006
|
+
if result.status == "compressed":
|
|
1007
|
+
token = uuid.uuid4().hex
|
|
1008
|
+
# A server that never answers (restart, failed RPC) gets the compression discarded on its own.
|
|
1009
|
+
expiry = threading.Timer(_PENDING_COMPRESSION_TTL, finalize_compression, args=(token, False))
|
|
1010
|
+
expiry.daemon = True
|
|
1011
|
+
with _PENDING_COMPRESSIONS_LOCK:
|
|
1012
|
+
_PENDING_COMPRESSIONS[token] = (agent, home, expiry)
|
|
1013
|
+
expiry.start()
|
|
1014
|
+
held = True
|
|
1015
|
+
payload["commit_token"] = token
|
|
1016
|
+
return payload
|
|
1017
|
+
finally:
|
|
1018
|
+
if not held:
|
|
1019
|
+
_release_compression(agent, committed=False)
|
|
1020
|
+
|
|
1021
|
+
|
|
728
1022
|
def _run_for(params: dict) -> _Run | None:
|
|
729
1023
|
stream_id = str(params.get("stream_id") or "").strip()
|
|
730
1024
|
session_id = str(params.get("session_id") or "").strip()
|
|
@@ -748,6 +1042,18 @@ def register(registry) -> None:
|
|
|
748
1042
|
with _turn_identity(session_id, workspace):
|
|
749
1043
|
return start(ctx, params)
|
|
750
1044
|
|
|
1045
|
+
@registry.method("chat.compress")
|
|
1046
|
+
def compress_(ctx: CallContext, params: dict) -> dict:
|
|
1047
|
+
with scoped_home(profile_home_param(params)):
|
|
1048
|
+
return compress(ctx, params)
|
|
1049
|
+
|
|
1050
|
+
@registry.method("chat.compress_finalize", requires_agent=False)
|
|
1051
|
+
def compress_finalize_(ctx: CallContext, params: dict) -> dict:
|
|
1052
|
+
token = str(params.get("commit_token") or "").strip()
|
|
1053
|
+
if not token:
|
|
1054
|
+
raise InvalidParams("commit_token is required")
|
|
1055
|
+
return {"finalized": finalize_compression(token, params.get("committed") is True)}
|
|
1056
|
+
|
|
751
1057
|
@registry.method("chat.interrupt", requires_agent=False)
|
|
752
1058
|
def interrupt_(ctx: CallContext, params: dict) -> dict:
|
|
753
1059
|
run = _run_for(params)
|
|
@@ -786,18 +1092,43 @@ def register(registry) -> None:
|
|
|
786
1092
|
except Exception as exc: # noqa: BLE001
|
|
787
1093
|
log.debug("steer failed: %s", exc)
|
|
788
1094
|
return {"accepted": False, "fallback": "steer_error"}
|
|
789
|
-
|
|
1095
|
+
# `can_redirect`: whether this Agent can deliver a pending steer now (TAL-424 "Send now").
|
|
1096
|
+
return {"accepted": accepted, "fallback": None if accepted else "not_running", "can_redirect": callable(getattr(run.agent, "redirect", None))}
|
|
1097
|
+
|
|
1098
|
+
@registry.method("chat.steer_withdraw", requires_agent=False)
|
|
1099
|
+
def steer_withdraw_(ctx: CallContext, params: dict) -> dict:
|
|
1100
|
+
pending, index = _steer_queue_params(params)
|
|
1101
|
+
run = _run_for(params)
|
|
1102
|
+
if run is None or run.agent is None:
|
|
1103
|
+
return {"withdrawn": False}
|
|
1104
|
+
return {"withdrawn": withdraw_steer(run.agent, pending, index)}
|
|
1105
|
+
|
|
1106
|
+
@registry.method("chat.steer_now", requires_agent=False)
|
|
1107
|
+
def steer_now_(ctx: CallContext, params: dict) -> dict:
|
|
1108
|
+
pending, index = _steer_queue_params(params)
|
|
1109
|
+
run = _run_for(params)
|
|
1110
|
+
if run is None or run.agent is None:
|
|
1111
|
+
return {"redirected": False, "withdrawn": False}
|
|
1112
|
+
return steer_now(run.agent, pending, index)
|
|
790
1113
|
|
|
791
1114
|
@registry.method("chat.evict_agent", requires_agent=False)
|
|
792
1115
|
def evict_(ctx: CallContext, params: dict) -> dict:
|
|
1116
|
+
"""Drop a session's cached agent. Only delete/clear (``clear_session``) end its approval grants, parked
|
|
1117
|
+
approvals and kernels; a model switch, truncate or compress leaves a running turn's agent in place."""
|
|
793
1118
|
session_id = str(params.get("session_id") or "").strip()
|
|
1119
|
+
end_session = params.get("clear_session") is True
|
|
794
1120
|
with _AGENT_CACHE_LOCK:
|
|
1121
|
+
with _RUNS_LOCK:
|
|
1122
|
+
run = _RUNS.get(_RUNS_BY_SESSION.get(session_id) or "")
|
|
1123
|
+
if not end_session and run is not None and not run.finished.is_set():
|
|
1124
|
+
return {"evicted": False}
|
|
795
1125
|
evicted = _AGENT_CACHE.pop(session_id, None) is not None
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
1126
|
+
if end_session:
|
|
1127
|
+
try:
|
|
1128
|
+
from tools.approval import clear_session
|
|
1129
|
+
clear_session(session_id)
|
|
1130
|
+
except Exception: # noqa: BLE001
|
|
1131
|
+
pass
|
|
801
1132
|
return {"evicted": evicted}
|
|
802
1133
|
|
|
803
1134
|
@registry.method("chat.commit_memory", requires_agent=False)
|
|
@@ -874,7 +1205,9 @@ def register(registry) -> None:
|
|
|
874
1205
|
except Exception as exc: # noqa: BLE001
|
|
875
1206
|
raise RpcError(f"approval module unavailable: {exc}", condition="agent_unavailable") from exc
|
|
876
1207
|
(enable_session_yolo if enabled else disable_session_yolo)(session_id)
|
|
877
|
-
released
|
|
1208
|
+
# Parked approvals are released `once`: a `session` or `always` grant would keep their patterns approved after
|
|
1209
|
+
# YOLO turns off, whatever choice the card that enabled it carried.
|
|
1210
|
+
released = resolve_gateway_approval(session_id, "once", resolve_all=True) if enabled else 0
|
|
878
1211
|
return {"yolo_enabled": bool(is_session_yolo_enabled(session_id)), "released": int(released or 0)}
|
|
879
1212
|
|
|
880
1213
|
@registry.method("clarify.respond", requires_agent=False)
|
|
@@ -7,8 +7,9 @@ import threading
|
|
|
7
7
|
from typing import Any
|
|
8
8
|
|
|
9
9
|
from ..errors import InvalidParams, RpcError
|
|
10
|
-
from ..home import profile_home_param, scoped_home
|
|
10
|
+
from ..home import _is_named_profile, profile_home_param, scoped_home
|
|
11
11
|
from ..rpc import CallContext
|
|
12
|
+
from .providers import plugin_providers
|
|
12
13
|
|
|
13
14
|
log = logging.getLogger("talaria_sidecar.commands")
|
|
14
15
|
|
|
@@ -60,19 +61,54 @@ def list_commands() -> list[dict[str, Any]]:
|
|
|
60
61
|
return out
|
|
61
62
|
|
|
62
63
|
|
|
64
|
+
def retire_unscoped_launch_mcp_servers() -> set[str]:
|
|
65
|
+
"""Close the launch profile's connections opened before multiplexing started; returns their server names.
|
|
66
|
+
|
|
67
|
+
Until the first named-profile call, the launch profile's connections and tools are unscoped (process-wide). Once
|
|
68
|
+
multiplexing is active, a scoped shutdown never selects them and scoped discovery opens duplicates beside them, so
|
|
69
|
+
the launch profile's own calls close them first and its scope reconnects them from the current config."""
|
|
70
|
+
from agent.secret_scope import is_multiplex_active
|
|
71
|
+
from hermes_constants import get_hermes_home
|
|
72
|
+
from tools.mcp_tool import _lock, _servers
|
|
73
|
+
from tools.mcp_tool_lifecycle import shutdown_mcp_servers
|
|
74
|
+
from tools.mcp_tool_scope import _key_name, _key_scope
|
|
75
|
+
|
|
76
|
+
if not is_multiplex_active() or _is_named_profile(get_hermes_home()):
|
|
77
|
+
return set()
|
|
78
|
+
with _lock:
|
|
79
|
+
names = {_key_name(key) for key in _servers if _key_scope(key) is None}
|
|
80
|
+
if names:
|
|
81
|
+
shutdown_mcp_servers(scope=None, names=names)
|
|
82
|
+
return names
|
|
83
|
+
|
|
84
|
+
|
|
63
85
|
def _reload_mcp() -> str:
|
|
64
86
|
with _RELOAD_MCP_LOCK:
|
|
65
87
|
try:
|
|
66
|
-
from
|
|
88
|
+
from agent.secret_scope import is_multiplex_active
|
|
89
|
+
from tools.mcp_tool import _lock, _mcp_tool_server_names, _server_visible_in_scope, _servers
|
|
90
|
+
from tools.mcp_tool_discovery import discover_mcp_tools
|
|
91
|
+
from tools.mcp_tool_lifecycle import shutdown_mcp_servers
|
|
92
|
+
from tools.mcp_tool_scope import _key_name
|
|
93
|
+
from tools.registry import registry
|
|
67
94
|
except Exception as exc: # noqa: BLE001
|
|
68
95
|
raise RpcError("MCP runtime unavailable", condition="mcp_unavailable") from exc
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
96
|
+
# Like the gateway's ``_execute_mcp_reload``: under multiplexing only this profile's connections go down and
|
|
97
|
+
# come back. The bare shutdown is the process-wide wildcard, which would drop every other profile's servers.
|
|
98
|
+
scope = registry.current_scope_key() if is_multiplex_active() else None
|
|
99
|
+
|
|
100
|
+
def server_names() -> set[str]:
|
|
74
101
|
with _lock:
|
|
75
|
-
|
|
102
|
+
return {_key_name(key) for key in _servers if _server_visible_in_scope(key, scope)}
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
old = server_names() | retire_unscoped_launch_mcp_servers()
|
|
106
|
+
shutdown_mcp_servers(scope=scope)
|
|
107
|
+
tools = discover_mcp_tools() or []
|
|
108
|
+
connected = server_names()
|
|
109
|
+
if scope is not None:
|
|
110
|
+
with _lock:
|
|
111
|
+
tools = [name for name in tools if _mcp_tool_server_names.get(name) in connected]
|
|
76
112
|
except Exception as exc: # noqa: BLE001
|
|
77
113
|
log.warning("Failed to reload MCP servers", exc_info=True)
|
|
78
114
|
raise RpcError("Failed to reload MCP servers", condition="mcp_reload_failed") from exc
|
|
@@ -84,7 +120,7 @@ def _reload_mcp() -> str:
|
|
|
84
120
|
lines.append(f"Added: {', '.join(sorted(added))}")
|
|
85
121
|
if removed:
|
|
86
122
|
lines.append(f"Removed: {', '.join(sorted(removed))}")
|
|
87
|
-
lines.append(f"{len(tools
|
|
123
|
+
lines.append(f"{len(tools)} tool(s) available across {len(connected)} server(s)" if connected else "No MCP servers connected")
|
|
88
124
|
if not reconnected and not added and not removed:
|
|
89
125
|
lines.append("Tooling state was already current")
|
|
90
126
|
return "\n".join(lines)
|
|
@@ -230,27 +266,6 @@ def resolve_moa_config(preset: str | None) -> dict:
|
|
|
230
266
|
return resolved
|
|
231
267
|
|
|
232
268
|
|
|
233
|
-
def plugin_provider_profiles() -> list[dict]:
|
|
234
|
-
"""Model-provider plugins registered with the Agent's provider registry."""
|
|
235
|
-
try:
|
|
236
|
-
from providers import list_providers
|
|
237
|
-
except Exception: # noqa: BLE001
|
|
238
|
-
return []
|
|
239
|
-
out = []
|
|
240
|
-
try:
|
|
241
|
-
for profile in list_providers():
|
|
242
|
-
name = str(getattr(profile, "name", "") or "").strip().lower()
|
|
243
|
-
if not name:
|
|
244
|
-
continue
|
|
245
|
-
env_vars = [str(v) for v in (getattr(profile, "env_vars", ()) or ())]
|
|
246
|
-
api_key_env = next((v for v in env_vars if not v.upper().endswith(("_BASE_URL", "_URL", "_FOLDER_ID"))), None)
|
|
247
|
-
out.append({"name": name, "display_name": str(getattr(profile, "display_name", "") or name).strip(), "env_vars": env_vars, "api_key_env": api_key_env})
|
|
248
|
-
except Exception: # noqa: BLE001
|
|
249
|
-
log.debug("Failed to enumerate model-provider plugins", exc_info=True)
|
|
250
|
-
return []
|
|
251
|
-
return out
|
|
252
|
-
|
|
253
|
-
|
|
254
269
|
PLUGIN_VISIBILITY_HOOKS = ("pre_tool_call", "post_tool_call", "pre_llm_call", "post_llm_call")
|
|
255
270
|
|
|
256
271
|
|
|
@@ -332,7 +347,7 @@ def register(registry) -> None:
|
|
|
332
347
|
@registry.method("plugins.providers")
|
|
333
348
|
def providers(ctx: CallContext, params: dict) -> dict:
|
|
334
349
|
with scoped_home(profile_home_param(params)):
|
|
335
|
-
return {"providers":
|
|
350
|
+
return {"providers": plugin_providers()}
|
|
336
351
|
|
|
337
352
|
@registry.method("plugins.list")
|
|
338
353
|
def list_(ctx: CallContext, params: dict) -> dict:
|