@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
|
@@ -4,8 +4,13 @@ routing; the sidecar drains and formats registry events."""
|
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
+
import json
|
|
7
8
|
import logging
|
|
8
9
|
import queue
|
|
10
|
+
import sqlite3
|
|
11
|
+
import time
|
|
12
|
+
from contextlib import closing
|
|
13
|
+
from pathlib import Path
|
|
9
14
|
|
|
10
15
|
from ..errors import InvalidParams
|
|
11
16
|
from ..home import profile_home_param, scoped_home
|
|
@@ -89,6 +94,62 @@ def mark_consumed(process_id: str) -> bool:
|
|
|
89
94
|
return False
|
|
90
95
|
|
|
91
96
|
|
|
97
|
+
def _delivery_api():
|
|
98
|
+
"""The Agent's durable delivery ledger (``tools.async_delegation``), or None on an Agent without it."""
|
|
99
|
+
try:
|
|
100
|
+
from tools import async_delegation
|
|
101
|
+
except Exception: # noqa: BLE001
|
|
102
|
+
return None
|
|
103
|
+
names = ("claim_event_delivery", "complete_event_delivery", "release_event_delivery")
|
|
104
|
+
return async_delegation if all(callable(getattr(async_delegation, n, None)) for n in names) else None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _ledger_event(evt: dict) -> dict:
|
|
108
|
+
return {k: v for k, v in evt.items() if k not in ("process_id", "consumed")}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def claim_delivery(evt: dict, consumer: str) -> str | None:
|
|
112
|
+
"""Claim a durable delegation completion before delivering it (TAL-459).
|
|
113
|
+
|
|
114
|
+
``None``: another consumer holds it, or the ledger already delivered or dropped it, so do not deliver.
|
|
115
|
+
``""``: nothing durable to acknowledge (interim notice, legacy event, or an Agent without the ledger)."""
|
|
116
|
+
api = _delivery_api()
|
|
117
|
+
if api is None:
|
|
118
|
+
return ""
|
|
119
|
+
claim_id = api.claim_event_delivery(_ledger_event(evt), consumer)
|
|
120
|
+
return None if claim_id is None else str(claim_id)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def complete_delivery(evt: dict, claim_id: str) -> bool:
|
|
124
|
+
api = _delivery_api()
|
|
125
|
+
if api is None or not claim_id:
|
|
126
|
+
return False
|
|
127
|
+
api.complete_event_delivery(_ledger_event(evt), claim_id)
|
|
128
|
+
return True
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def release_delivery(evt: dict, claim_id: str) -> bool:
|
|
132
|
+
api = _delivery_api()
|
|
133
|
+
if api is None or not claim_id:
|
|
134
|
+
return False
|
|
135
|
+
api.release_event_delivery(_ledger_event(evt), claim_id)
|
|
136
|
+
return True
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def defer_delivery(evt: dict, claim_id: str) -> bool:
|
|
140
|
+
"""Hand back a claim the target never admitted (its session was busy) without spending a delivery attempt;
|
|
141
|
+
the Agent drops a row for good after its attempt budget, so a busy session must not burn it."""
|
|
142
|
+
api = _delivery_api()
|
|
143
|
+
if api is None or not claim_id:
|
|
144
|
+
return False
|
|
145
|
+
defer = getattr(api, "defer_completion_delivery", None)
|
|
146
|
+
if not callable(defer) or evt.get("type") != "async_delegation" or not evt.get("delegation_id"):
|
|
147
|
+
api.release_event_delivery(_ledger_event(evt), claim_id)
|
|
148
|
+
return True
|
|
149
|
+
defer(str(evt["delegation_id"]), claim_id)
|
|
150
|
+
return True
|
|
151
|
+
|
|
152
|
+
|
|
92
153
|
def format_notification(evt: dict) -> str:
|
|
93
154
|
if evt.get("type") == "async_delegation":
|
|
94
155
|
try:
|
|
@@ -122,6 +183,210 @@ def list_sessions() -> list[dict]:
|
|
|
122
183
|
return out
|
|
123
184
|
|
|
124
185
|
|
|
186
|
+
_LEDGER_COLUMNS = "delegation_id, origin_ui_session_id, state, dispatched_at, completed_at, updated_at, task_json, result_json, event_json IS NOT NULL"
|
|
187
|
+
_LEDGER_LIMIT = 200
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _json(text) -> dict:
|
|
191
|
+
try:
|
|
192
|
+
value = json.loads(text) if text else {}
|
|
193
|
+
except (TypeError, ValueError):
|
|
194
|
+
return {}
|
|
195
|
+
return value if isinstance(value, dict) else {}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _unit_goals(task: dict) -> list[str]:
|
|
199
|
+
goals = task.get("goals") if isinstance(task.get("goals"), list) else None
|
|
200
|
+
indexes = task.get("task_indexes") if isinstance(task.get("task_indexes"), list) else None
|
|
201
|
+
if goals and indexes:
|
|
202
|
+
return [str(goals[i]) for i in indexes if isinstance(i, int) and 0 <= i < len(goals)]
|
|
203
|
+
if goals:
|
|
204
|
+
return [str(g) for g in goals]
|
|
205
|
+
return [str(task.get("goal") or "")]
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _child_statuses(result: dict) -> list[str]:
|
|
209
|
+
"""Per-subagent outcome of a finished unit: a batch carries ``results``, a single task is its own result."""
|
|
210
|
+
if isinstance(result.get("results"), list):
|
|
211
|
+
return [str(r.get("status") or "") for r in result["results"] if isinstance(r, dict)]
|
|
212
|
+
return [str(result.get("status") or "")] if result else []
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _ledger_rows(home: Path, session_ids: list[str]) -> list[dict]:
|
|
216
|
+
db_path = home / "state.db"
|
|
217
|
+
if not db_path.exists() or not session_ids:
|
|
218
|
+
return []
|
|
219
|
+
marks = ",".join("?" * len(session_ids))
|
|
220
|
+
with closing(sqlite3.connect(f"{db_path.resolve().as_uri()}?mode=ro", uri=True, timeout=5)) as conn:
|
|
221
|
+
try:
|
|
222
|
+
rows = conn.execute(f"SELECT {_LEDGER_COLUMNS} FROM async_delegations WHERE origin_ui_session_id IN ({marks}) ORDER BY dispatched_at DESC LIMIT ?", [*session_ids, _LEDGER_LIMIT]).fetchall()
|
|
223
|
+
except sqlite3.OperationalError: # an Agent that never delegated has no ledger table yet
|
|
224
|
+
return []
|
|
225
|
+
out = []
|
|
226
|
+
for delegation_id, origin, state, dispatched_at, completed_at, updated_at, task_json, result_json, has_event in rows:
|
|
227
|
+
task, result = _json(task_json), _json(result_json)
|
|
228
|
+
goals = _unit_goals(task)
|
|
229
|
+
out.append({
|
|
230
|
+
"delegation_id": delegation_id, "origin_ui_session_id": origin, "state": state, "dispatched_at": dispatched_at,
|
|
231
|
+
"completed_at": completed_at, "updated_at": updated_at, "goals": goals,
|
|
232
|
+
"child_statuses": _child_statuses(result), "has_result": bool(has_event),
|
|
233
|
+
})
|
|
234
|
+
return out
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _live_delegations() -> dict[str, dict]:
|
|
238
|
+
try:
|
|
239
|
+
from tools.async_delegation import list_async_delegations
|
|
240
|
+
items = list_async_delegations()
|
|
241
|
+
except Exception: # noqa: BLE001 - an Agent without the live registry reports from the ledger alone
|
|
242
|
+
return {}
|
|
243
|
+
return {str(i.get("delegation_id")): {"status": str(i.get("status") or "")} for i in items if isinstance(i, dict) and i.get("delegation_id")}
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _owned_processes(session_ids: set[str]) -> list[dict]:
|
|
247
|
+
"""Notified or watched processes a WebUI session started (``session_key`` is the WebUI session id)."""
|
|
248
|
+
registry = _registry()
|
|
249
|
+
if registry is None:
|
|
250
|
+
return []
|
|
251
|
+
try:
|
|
252
|
+
with registry._lock:
|
|
253
|
+
sessions = [*registry._running.values(), *registry._finished.values()]
|
|
254
|
+
except Exception: # noqa: BLE001
|
|
255
|
+
return []
|
|
256
|
+
out = []
|
|
257
|
+
for proc in sessions:
|
|
258
|
+
if str(getattr(proc, "session_key", "") or "") not in session_ids:
|
|
259
|
+
continue
|
|
260
|
+
watched = bool(getattr(proc, "watch_patterns", None))
|
|
261
|
+
if not (getattr(proc, "notify_on_complete", False) or watched):
|
|
262
|
+
continue
|
|
263
|
+
exited = bool(getattr(proc, "exited", False))
|
|
264
|
+
out.append({
|
|
265
|
+
"process_id": str(proc.id), "session_key": str(proc.session_key), "command": str(getattr(proc, "command", "") or "")[:200],
|
|
266
|
+
"started_at": float(getattr(proc, "started_at", 0) or 0) or None, "exited": exited,
|
|
267
|
+
"exited_at": float(getattr(proc, "exited_at", 0) or 0) or None, "exit_code": getattr(proc, "exit_code", None),
|
|
268
|
+
"completion_reason": str(getattr(proc, "completion_reason", "") or ""), "watched": watched,
|
|
269
|
+
})
|
|
270
|
+
return out
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
_CHILD_WINDOW_S = 5
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _live_children() -> list[dict]:
|
|
277
|
+
"""Running subagents from the Agent's in-process registry: the only place a child's session id sits next to its
|
|
278
|
+
delegation id. An Agent without the registry reports none."""
|
|
279
|
+
try:
|
|
280
|
+
from tools.delegate_tool_registry import _active_subagents, _active_subagents_lock
|
|
281
|
+
with _active_subagents_lock:
|
|
282
|
+
records = list(_active_subagents.values())
|
|
283
|
+
except Exception: # noqa: BLE001
|
|
284
|
+
return []
|
|
285
|
+
out = []
|
|
286
|
+
for r in records:
|
|
287
|
+
sid = getattr(r.get("agent"), "session_id", None)
|
|
288
|
+
if isinstance(sid, str) and sid:
|
|
289
|
+
out.append({"delegation_id": str(r.get("delegation_id") or ""), "goal": str(r.get("goal") or ""), "owner": str(r.get("owner_agent_session_id") or ""), "session_id": sid})
|
|
290
|
+
return out
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def _ledger_children(home: Path, owners: list[str]) -> list[dict]:
|
|
294
|
+
"""Finished subagents' own sessions in state.db: ``source = subagent`` rows tagged ``_delegate_from`` with their
|
|
295
|
+
parent's session, each with its start time and first user message (the child's goal, sent verbatim)."""
|
|
296
|
+
db_path = home / "state.db"
|
|
297
|
+
if not db_path.exists() or not owners:
|
|
298
|
+
return []
|
|
299
|
+
marks = ",".join("?" * len(owners))
|
|
300
|
+
with closing(sqlite3.connect(f"{db_path.resolve().as_uri()}?mode=ro", uri=True, timeout=5)) as conn:
|
|
301
|
+
try:
|
|
302
|
+
# Only these chats' children: a long-lived profile keeps every subagent session it ever ran.
|
|
303
|
+
rows = conn.execute(
|
|
304
|
+
"SELECT s.id, s.model_config, s.started_at, (SELECT m.content FROM messages m WHERE m.session_id = s.id AND m.role = 'user' ORDER BY m.id LIMIT 1)"
|
|
305
|
+
f" FROM sessions s WHERE s.source = 'subagent' AND json_extract(s.model_config, '$._delegate_from') IN ({marks})", owners).fetchall()
|
|
306
|
+
except sqlite3.OperationalError:
|
|
307
|
+
return []
|
|
308
|
+
out = []
|
|
309
|
+
for sid, model_config, started_at, first in rows:
|
|
310
|
+
owner = _json(model_config).get("_delegate_from")
|
|
311
|
+
goal = _goal_text(first)
|
|
312
|
+
if owner in owners and goal:
|
|
313
|
+
out.append({"owner": owner, "session_id": sid, "started_at": started_at, "goal": goal})
|
|
314
|
+
return out
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
_CONTENT_JSON_PREFIX = "\x00json:"
|
|
318
|
+
_IMAGE_HINTS = "\n\n[Image attached"
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def _goal_text(content) -> str:
|
|
322
|
+
"""The goal a child was sent, from its stored first message: a task with images is stored as multimodal parts
|
|
323
|
+
(native image input; the goal is the text part) or as the goal followed by image hints (text input)."""
|
|
324
|
+
if not isinstance(content, str):
|
|
325
|
+
return ""
|
|
326
|
+
if content.startswith(_CONTENT_JSON_PREFIX):
|
|
327
|
+
try:
|
|
328
|
+
parts = json.loads(content[len(_CONTENT_JSON_PREFIX):])
|
|
329
|
+
except ValueError:
|
|
330
|
+
return ""
|
|
331
|
+
texts = [p.get("text") for p in parts if isinstance(p, dict) and p.get("type") == "text"] if isinstance(parts, list) else []
|
|
332
|
+
return str(texts[0]) if texts and texts[0] else ""
|
|
333
|
+
return content.split(_IMAGE_HINTS, 1)[0]
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def unit_children(unit: dict, owner: str, live: list[dict], finished: list[dict], now: float) -> list[dict]:
|
|
337
|
+
"""The subagent sessions one delegation unit ran, as ``{goal, session_id}`` per task, in task order. A live registry
|
|
338
|
+
record is exact (same owner, the unit's call id, the task's goal) and is used once. Without one, finished sessions
|
|
339
|
+
of that owner whose goal matches, started in the unit's window, count only when there are exactly as many as tasks
|
|
340
|
+
with that goal; anything else stays unlinked rather than guessed."""
|
|
341
|
+
unit_id, goals = unit["delegation_id"], [g for g in unit["goals"] if g]
|
|
342
|
+
pool = [r for r in live if r["owner"] == owner and r["delegation_id"] and (unit_id == r["delegation_id"] or unit_id.startswith(f"{r['delegation_id']}-"))]
|
|
343
|
+
linked: list[str | None] = []
|
|
344
|
+
used: set[str] = set()
|
|
345
|
+
for goal in goals:
|
|
346
|
+
record = next((r for r in pool if r["goal"] == goal and r["session_id"] not in used), None)
|
|
347
|
+
if record:
|
|
348
|
+
used.add(record["session_id"])
|
|
349
|
+
linked.append(record["session_id"] if record else None)
|
|
350
|
+
start = (unit.get("dispatched_at") or 0) - _CHILD_WINDOW_S
|
|
351
|
+
end = (unit.get("completed_at") or now) + _CHILD_WINDOW_S
|
|
352
|
+
for goal in {g for g, sid in zip(goals, linked) if sid is None}:
|
|
353
|
+
slots = [i for i, (g, sid) in enumerate(zip(goals, linked)) if g == goal and sid is None]
|
|
354
|
+
matches = sorted((c for c in finished if c["owner"] == owner and c["goal"] == goal and c["session_id"] not in used and start <= (c["started_at"] or 0) <= end), key=lambda c: c["started_at"] or 0)
|
|
355
|
+
if len(matches) == len(slots):
|
|
356
|
+
for i, c in zip(slots, matches):
|
|
357
|
+
linked[i] = c["session_id"]
|
|
358
|
+
return [{"goal": g, "session_id": sid} for g, sid in zip(goals, linked) if sid]
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def background_list(home: Path, session_ids: list[str]) -> dict:
|
|
362
|
+
"""TAL-372: what the Agent knows about the background work of these WebUI sessions: delegations from the durable
|
|
363
|
+
ledger (with the live registry's status while they run) and notified processes from the process registry.
|
|
364
|
+
TAL-494: each delegation also names the subagent sessions it ran."""
|
|
365
|
+
live = _live_delegations()
|
|
366
|
+
delegations = _ledger_rows(home, session_ids)
|
|
367
|
+
live_children = _live_children()
|
|
368
|
+
finished = _ledger_children(home, session_ids) if delegations else []
|
|
369
|
+
now = time.time()
|
|
370
|
+
for row in delegations:
|
|
371
|
+
row["live_status"] = live.get(row["delegation_id"], {}).get("status")
|
|
372
|
+
row["children"] = unit_children(row, row["origin_ui_session_id"], live_children, finished, now)
|
|
373
|
+
return {"delegations": delegations, "processes": _owned_processes(set(session_ids))}
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def delegation_result(home: Path, session_id: str, delegation_id: str) -> str:
|
|
377
|
+
"""The full result of a finished delegation unit, as the Agent words it for its own notification."""
|
|
378
|
+
db_path = home / "state.db"
|
|
379
|
+
if not db_path.exists():
|
|
380
|
+
return ""
|
|
381
|
+
with closing(sqlite3.connect(f"{db_path.resolve().as_uri()}?mode=ro", uri=True, timeout=5)) as conn:
|
|
382
|
+
try:
|
|
383
|
+
row = conn.execute("SELECT event_json FROM async_delegations WHERE delegation_id = ? AND origin_ui_session_id = ?", (delegation_id, session_id)).fetchone()
|
|
384
|
+
except sqlite3.OperationalError:
|
|
385
|
+
return ""
|
|
386
|
+
event = _json(row[0]) if row else {}
|
|
387
|
+
return format_notification(event) if event else ""
|
|
388
|
+
|
|
389
|
+
|
|
125
390
|
def register(registry_) -> None:
|
|
126
391
|
@registry_.method("process.drain")
|
|
127
392
|
def drain_(ctx: CallContext, params: dict) -> dict:
|
|
@@ -142,6 +407,39 @@ def register(registry_) -> None:
|
|
|
142
407
|
raise InvalidParams("process_id is required")
|
|
143
408
|
return {"ok": mark_consumed(process_id)}
|
|
144
409
|
|
|
410
|
+
def _delivery_params(params: dict) -> dict:
|
|
411
|
+
evt = params.get("event")
|
|
412
|
+
if not isinstance(evt, dict):
|
|
413
|
+
raise InvalidParams("event must be an object")
|
|
414
|
+
return evt
|
|
415
|
+
|
|
416
|
+
@registry_.method("process.claim_delivery")
|
|
417
|
+
def claim_(ctx: CallContext, params: dict) -> dict:
|
|
418
|
+
evt = _delivery_params(params)
|
|
419
|
+
consumer = str(params.get("consumer") or "").strip()
|
|
420
|
+
if not consumer:
|
|
421
|
+
raise InvalidParams("consumer is required")
|
|
422
|
+
with scoped_home(profile_home_param(params)):
|
|
423
|
+
return {"claim_id": claim_delivery(evt, consumer)}
|
|
424
|
+
|
|
425
|
+
@registry_.method("process.complete_delivery")
|
|
426
|
+
def complete_(ctx: CallContext, params: dict) -> dict:
|
|
427
|
+
evt = _delivery_params(params)
|
|
428
|
+
with scoped_home(profile_home_param(params)):
|
|
429
|
+
return {"ok": complete_delivery(evt, str(params.get("claim_id") or ""))}
|
|
430
|
+
|
|
431
|
+
@registry_.method("process.release_delivery")
|
|
432
|
+
def release_(ctx: CallContext, params: dict) -> dict:
|
|
433
|
+
evt = _delivery_params(params)
|
|
434
|
+
with scoped_home(profile_home_param(params)):
|
|
435
|
+
return {"ok": release_delivery(evt, str(params.get("claim_id") or ""))}
|
|
436
|
+
|
|
437
|
+
@registry_.method("process.defer_delivery")
|
|
438
|
+
def defer_(ctx: CallContext, params: dict) -> dict:
|
|
439
|
+
evt = _delivery_params(params)
|
|
440
|
+
with scoped_home(profile_home_param(params)):
|
|
441
|
+
return {"ok": defer_delivery(evt, str(params.get("claim_id") or ""))}
|
|
442
|
+
|
|
145
443
|
@registry_.method("process.format_notification")
|
|
146
444
|
def format_(ctx: CallContext, params: dict) -> dict:
|
|
147
445
|
evt = params.get("event")
|
|
@@ -149,6 +447,26 @@ def register(registry_) -> None:
|
|
|
149
447
|
raise InvalidParams("event must be an object")
|
|
150
448
|
return {"text": format_notification(evt)}
|
|
151
449
|
|
|
450
|
+
def _session_ids(params: dict) -> list[str]:
|
|
451
|
+
ids = params.get("session_ids")
|
|
452
|
+
if not isinstance(ids, list) or not all(isinstance(i, str) and i for i in ids):
|
|
453
|
+
raise InvalidParams("session_ids must be a list of session ids")
|
|
454
|
+
return ids
|
|
455
|
+
|
|
456
|
+
@registry_.method("process.background_list")
|
|
457
|
+
def background_list_(ctx: CallContext, params: dict) -> dict:
|
|
458
|
+
ids = _session_ids(params)
|
|
459
|
+
with scoped_home(profile_home_param(params)) as home:
|
|
460
|
+
return background_list(home, ids)
|
|
461
|
+
|
|
462
|
+
@registry_.method("process.delegation_result")
|
|
463
|
+
def delegation_result_(ctx: CallContext, params: dict) -> dict:
|
|
464
|
+
sid, did = str(params.get("session_id") or ""), str(params.get("delegation_id") or "")
|
|
465
|
+
if not sid or not did:
|
|
466
|
+
raise InvalidParams("session_id and delegation_id are required")
|
|
467
|
+
with scoped_home(profile_home_param(params)) as home:
|
|
468
|
+
return {"text": delegation_result(home, sid, did)}
|
|
469
|
+
|
|
152
470
|
@registry_.method("process.list")
|
|
153
471
|
def list_(ctx: CallContext, params: dict) -> dict:
|
|
154
472
|
with scoped_home(profile_home_param(params)):
|
|
@@ -18,6 +18,7 @@ from pathlib import Path
|
|
|
18
18
|
from ..errors import InvalidParams, RpcError
|
|
19
19
|
from ..home import profile_home_param
|
|
20
20
|
from ..rpc import CallContext
|
|
21
|
+
from .config import yaml_parser
|
|
21
22
|
|
|
22
23
|
log = logging.getLogger("talaria_sidecar.profiles")
|
|
23
24
|
_PROFILE_ID_RE = re.compile(r"[a-z0-9][a-z0-9_-]{0,63}")
|
|
@@ -34,12 +35,12 @@ _TERMINAL_ENV = {
|
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
def _yaml_load(path: Path):
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
except Exception: # noqa: BLE001
|
|
38
|
+
"""A YAML mapping, or None when the file is absent or malformed; a missing parser raises ``yaml_unavailable``."""
|
|
39
|
+
if not path.exists():
|
|
40
40
|
return None
|
|
41
|
+
parser = yaml_parser()
|
|
41
42
|
try:
|
|
42
|
-
data =
|
|
43
|
+
data = parser.safe_load(path.read_text(encoding="utf-8"))
|
|
43
44
|
except Exception: # noqa: BLE001
|
|
44
45
|
return None
|
|
45
46
|
return data if isinstance(data, dict) else None
|
|
@@ -11,6 +11,9 @@ from __future__ import annotations
|
|
|
11
11
|
|
|
12
12
|
import dataclasses
|
|
13
13
|
import logging
|
|
14
|
+
import sys
|
|
15
|
+
import threading
|
|
16
|
+
from pathlib import Path
|
|
14
17
|
from typing import Any
|
|
15
18
|
|
|
16
19
|
from ..errors import InvalidParams, RpcError
|
|
@@ -61,19 +64,176 @@ def auth_status(provider_id: str | None) -> dict:
|
|
|
61
64
|
return _plain(status) if isinstance(status, dict) else {"logged_in": bool(status)}
|
|
62
65
|
|
|
63
66
|
|
|
67
|
+
def _module_dir(module: Any) -> Path | None:
|
|
68
|
+
try:
|
|
69
|
+
return Path(module.__file__).resolve().parent
|
|
70
|
+
except Exception: # noqa: BLE001 - namespace/builtin modules have no file
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# The Agent's module-name prefix for provider plugins it imports from a ``$HERMES_HOME`` (``providers._import_plugin_dir``).
|
|
75
|
+
_USER_PLUGIN_MODULE = "_hermes_user_provider_"
|
|
76
|
+
_LOAD_LOCK = threading.Lock()
|
|
77
|
+
_LOAD_ATTEMPTED: set[Path] = set()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _user_plugin_profiles() -> dict[int, tuple[Any, Path]]:
|
|
81
|
+
"""Each registered profile a ``$HERMES_HOME`` provider plugin module holds, with that module's directory."""
|
|
82
|
+
from providers import list_providers
|
|
83
|
+
from providers.base import ProviderProfile
|
|
84
|
+
|
|
85
|
+
registered = {id(p) for p in list_providers()}
|
|
86
|
+
out: dict[int, tuple[Any, Path]] = {}
|
|
87
|
+
for name, module in list(sys.modules.items()):
|
|
88
|
+
directory = _module_dir(module) if module is not None and name.startswith(_USER_PLUGIN_MODULE) else None
|
|
89
|
+
if directory is None:
|
|
90
|
+
continue
|
|
91
|
+
for value in list(vars(module).values()):
|
|
92
|
+
if isinstance(value, ProviderProfile) and id(value) in registered:
|
|
93
|
+
out.setdefault(id(value), (value, directory))
|
|
94
|
+
return out
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _load_plugin(root: Path) -> None:
|
|
98
|
+
"""Load the scoped profile's installed plugin that the process-wide discovery (run under the launch profile) skipped.
|
|
99
|
+
|
|
100
|
+
Uses the Agent's own loader, as ``hermes plugins dev`` does, once per directory. A plugin never displaces a provider
|
|
101
|
+
that is already registered, so one profile's plugin cannot change what another profile's provider id runs.
|
|
102
|
+
"""
|
|
103
|
+
import providers
|
|
104
|
+
|
|
105
|
+
with _LOAD_LOCK:
|
|
106
|
+
if root in _LOAD_ATTEMPTED:
|
|
107
|
+
return
|
|
108
|
+
_LOAD_ATTEMPTED.add(root)
|
|
109
|
+
before = {p.name: p for p in providers.list_providers()}
|
|
110
|
+
try:
|
|
111
|
+
providers._import_plugin_dir(root, "user")
|
|
112
|
+
except Exception: # noqa: BLE001 - a broken plugin reads as not loaded
|
|
113
|
+
log.debug("loading provider plugin %s failed", root, exc_info=True)
|
|
114
|
+
return
|
|
115
|
+
after = {p.name: p for p in providers.list_providers()}
|
|
116
|
+
for name, previous in before.items():
|
|
117
|
+
if after.get(name) is not previous:
|
|
118
|
+
providers.register_provider(previous)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def installed_plugin_profiles() -> list[tuple[str, Any]]:
|
|
122
|
+
"""``(manifest name, profile | None)`` for each enabled model-provider plugin installed in the scoped home.
|
|
123
|
+
|
|
124
|
+
Bundled providers are built-ins, not plugins. The Agent keeps one provider registry per process, so a profile
|
|
125
|
+
belongs to this home only when the plugin module that registered it lives in one of this home's plugin
|
|
126
|
+
directories; a plugin the launch discovery skipped is loaded here. ``None`` means the plugin could not be loaded:
|
|
127
|
+
it failed to import, its provider id is taken by another plugin, or another profile's plugin has the same directory
|
|
128
|
+
name. ponytail: ownership is read from the plugin module's globals, so a plugin that registers an unbound inline
|
|
129
|
+
profile also reads as not loaded; track registrations in the Agent if one ever does.
|
|
130
|
+
"""
|
|
131
|
+
try:
|
|
132
|
+
from hermes_cli.plugins_discovery import _get_disabled_plugins, _get_enabled_plugins, collect_directory_manifests, gate_manifest
|
|
133
|
+
except Exception as exc: # noqa: BLE001
|
|
134
|
+
raise RpcError(f"plugin providers unavailable: {exc}", condition="plugins_unavailable") from exc
|
|
135
|
+
disabled, enabled = _get_disabled_plugins(), _get_enabled_plugins()
|
|
136
|
+
roots = {
|
|
137
|
+
Path(m.path).resolve(): str(m.name)
|
|
138
|
+
for m in collect_directory_manifests()
|
|
139
|
+
if m.kind == "model-provider" and m.source == "user" and m.path and gate_manifest(m, disabled, enabled).enabled
|
|
140
|
+
}
|
|
141
|
+
if not roots:
|
|
142
|
+
return []
|
|
143
|
+
|
|
144
|
+
def owned() -> dict[Path, list]:
|
|
145
|
+
found: dict[Path, list] = {root: [] for root in roots}
|
|
146
|
+
for profile, directory in _user_plugin_profiles().values():
|
|
147
|
+
root = next((r for r in roots if directory == r or r in directory.parents), None)
|
|
148
|
+
if root is not None:
|
|
149
|
+
found[root].append(profile)
|
|
150
|
+
return found
|
|
151
|
+
|
|
152
|
+
found = owned()
|
|
153
|
+
missing = [root for root, profiles in found.items() if not profiles]
|
|
154
|
+
if missing:
|
|
155
|
+
for root in missing:
|
|
156
|
+
_load_plugin(root)
|
|
157
|
+
found = owned()
|
|
158
|
+
return [(name, p) for root, name in roots.items() for p in (found[root] or [None])]
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _other_profile_plugin(provider_id: str) -> bool:
|
|
162
|
+
"""Whether ``provider_id`` is a plugin provider that another profile installed (the registry is process-wide)."""
|
|
163
|
+
from providers import get_provider_profile
|
|
164
|
+
|
|
165
|
+
profile = get_provider_profile(provider_id)
|
|
166
|
+
if profile is None or id(profile) not in _user_plugin_profiles():
|
|
167
|
+
return False
|
|
168
|
+
return not any(p is profile for _name, p in installed_plugin_profiles())
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _setup_state(profile: Any) -> str:
|
|
172
|
+
"""The Agent's own setup verdict for a plugin provider, without spawning its CLI or reading its credentials."""
|
|
173
|
+
try:
|
|
174
|
+
status = auth_status(profile.name)
|
|
175
|
+
except RpcError:
|
|
176
|
+
return "unavailable"
|
|
177
|
+
if status.get("error"):
|
|
178
|
+
return "unavailable"
|
|
179
|
+
if profile.auth_type == "external_process":
|
|
180
|
+
return "ready" if status.get("configured") else "missing_cli"
|
|
181
|
+
return "ready" if status.get("logged_in") or status.get("configured") else "needs_setup"
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def plugin_providers() -> list[dict]:
|
|
185
|
+
"""Sanitized rows for this profile's enabled model-provider plugins: identity and setup state, no paths or secrets."""
|
|
186
|
+
rows = []
|
|
187
|
+
for manifest_name, profile in installed_plugin_profiles():
|
|
188
|
+
if profile is None:
|
|
189
|
+
rows.append({"name": manifest_name, "display_name": manifest_name, "auth_type": "", "setup": "not_loaded"})
|
|
190
|
+
continue
|
|
191
|
+
name = str(profile.name).strip().lower()
|
|
192
|
+
rows.append({"name": name, "display_name": str(profile.display_name or name).strip(), "auth_type": str(profile.auth_type), "setup": _setup_state(profile)})
|
|
193
|
+
return rows
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _plugin_catalog(provider_id: str) -> list[str]:
|
|
197
|
+
"""Discovery for an installed plugin the Agent's catalog has no fetcher for: the profile's own listing, then its fallback.
|
|
198
|
+
|
|
199
|
+
API-key plugins already get both from ``provider_model_ids`` with their key, so only keyless auth types list here.
|
|
200
|
+
"""
|
|
201
|
+
for _name, profile in installed_plugin_profiles():
|
|
202
|
+
if profile is None or str(profile.name).strip().lower() != provider_id.strip().lower():
|
|
203
|
+
continue
|
|
204
|
+
try:
|
|
205
|
+
live = profile.fetch_models(timeout=8.0) if profile.supports_model_listing and profile.auth_type != "api_key" else None
|
|
206
|
+
except Exception: # noqa: BLE001 - a broken plugin answers with its fallback catalog
|
|
207
|
+
log.debug("fetch_models(%r) failed", provider_id, exc_info=True)
|
|
208
|
+
live = None
|
|
209
|
+
return [str(m) for m in (live or profile.fallback_models or ()) if m]
|
|
210
|
+
return []
|
|
211
|
+
|
|
212
|
+
|
|
64
213
|
def model_ids(provider_id: str, *, force_refresh: bool) -> list[str]:
|
|
65
214
|
try:
|
|
66
215
|
from hermes_cli.models import provider_model_ids
|
|
67
216
|
except Exception as exc: # noqa: BLE001
|
|
68
217
|
raise RpcError(f"model catalog unavailable: {exc}", condition="providers_unavailable") from exc
|
|
218
|
+
try:
|
|
219
|
+
if _other_profile_plugin(provider_id):
|
|
220
|
+
return []
|
|
221
|
+
except Exception: # noqa: BLE001 - no readable provider registry means no plugin to keep apart
|
|
222
|
+
log.debug("plugin ownership check for %r failed", provider_id, exc_info=True)
|
|
69
223
|
try:
|
|
70
224
|
ids = provider_model_ids(provider_id, force_refresh=force_refresh)
|
|
71
225
|
except TypeError:
|
|
72
226
|
ids = provider_model_ids(provider_id)
|
|
73
227
|
except Exception: # noqa: BLE001
|
|
74
228
|
log.debug("provider_model_ids(%r) failed", provider_id, exc_info=True)
|
|
229
|
+
ids = []
|
|
230
|
+
ids = [str(m) for m in (ids or []) if m]
|
|
231
|
+
if ids:
|
|
232
|
+
return ids
|
|
233
|
+
try:
|
|
234
|
+
return _plugin_catalog(provider_id)
|
|
235
|
+
except RpcError:
|
|
75
236
|
return []
|
|
76
|
-
return [str(m) for m in (ids or []) if m]
|
|
77
237
|
|
|
78
238
|
|
|
79
239
|
def resolve_runtime(*, requested: str | None, api_key: str | None, base_url: str | None, target_model: str | None) -> dict:
|
|
@@ -7,6 +7,7 @@ import re
|
|
|
7
7
|
|
|
8
8
|
from .. import SIDECAR_RPC_VERSION
|
|
9
9
|
from ..errors import InvalidParams, RpcError
|
|
10
|
+
from ..home import edit_launch_env
|
|
10
11
|
from ..rpc import CallContext
|
|
11
12
|
|
|
12
13
|
|
|
@@ -53,6 +54,7 @@ def register(registry) -> None:
|
|
|
53
54
|
os.environ.pop(name, None)
|
|
54
55
|
for name, value in to_set.items():
|
|
55
56
|
os.environ[name] = value
|
|
57
|
+
edit_launch_env(to_set, to_unset)
|
|
56
58
|
# Cached agents bound the credentials they were built with; none of them may outlive a credential change.
|
|
57
59
|
from .chat import evict_all_agents
|
|
58
60
|
|
|
@@ -1,28 +1,16 @@
|
|
|
1
1
|
"""``text.*``: pure helpers from the Agent that the server must match exactly
|
|
2
|
-
(
|
|
2
|
+
(image input routing, portal tags)."""
|
|
3
3
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
6
|
import logging
|
|
7
7
|
|
|
8
|
-
from ..errors import InvalidParams
|
|
9
8
|
from ..home import profile_home_param, scoped_home
|
|
10
9
|
from ..rpc import CallContext
|
|
11
10
|
|
|
12
11
|
log = logging.getLogger("talaria_sidecar.text")
|
|
13
12
|
|
|
14
13
|
|
|
15
|
-
def redact(text: str, *, force: bool = True) -> str:
|
|
16
|
-
try:
|
|
17
|
-
from agent.redact import redact_sensitive_text
|
|
18
|
-
except Exception: # noqa: BLE001
|
|
19
|
-
return text
|
|
20
|
-
try:
|
|
21
|
-
return redact_sensitive_text(text, force=force)
|
|
22
|
-
except TypeError:
|
|
23
|
-
return redact_sensitive_text(text)
|
|
24
|
-
|
|
25
|
-
|
|
26
14
|
def image_mode(*, provider: str, model: str, cfg: dict | None, requested_provider: str) -> dict:
|
|
27
15
|
"""``native`` or ``text`` for current-turn image uploads, as the Agent decides it."""
|
|
28
16
|
try:
|
|
@@ -66,13 +54,6 @@ def portal_tags() -> dict:
|
|
|
66
54
|
|
|
67
55
|
|
|
68
56
|
def register(registry) -> None:
|
|
69
|
-
@registry.method("text.redact")
|
|
70
|
-
def redact_(ctx: CallContext, params: dict) -> dict:
|
|
71
|
-
text = params.get("text")
|
|
72
|
-
if not isinstance(text, str):
|
|
73
|
-
raise InvalidParams("text must be a string")
|
|
74
|
-
return {"text": redact(text, force=bool(params.get("force", True)))}
|
|
75
|
-
|
|
76
57
|
@registry.method("text.image_mode")
|
|
77
58
|
def image_mode_(ctx: CallContext, params: dict) -> dict:
|
|
78
59
|
cfg = params.get("cfg") if isinstance(params.get("cfg"), dict) else None
|
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import dataclasses
|
|
6
6
|
import logging
|
|
7
|
+
from datetime import date, datetime, timezone
|
|
7
8
|
from typing import Any
|
|
8
9
|
|
|
9
10
|
from ..errors import InvalidParams, RpcError
|
|
@@ -22,6 +23,10 @@ def _plain(value: Any) -> Any:
|
|
|
22
23
|
return [_plain(v) for v in value]
|
|
23
24
|
if isinstance(value, (str, int, float, bool)) or value is None:
|
|
24
25
|
return value
|
|
26
|
+
if isinstance(value, datetime) and value.tzinfo is not None:
|
|
27
|
+
return value.astimezone(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
28
|
+
if isinstance(value, date):
|
|
29
|
+
return value.isoformat()
|
|
25
30
|
if hasattr(value, "__dict__"):
|
|
26
31
|
return {k: _plain(v) for k, v in vars(value).items() if not k.startswith("_")}
|
|
27
32
|
return str(value)
|
|
@@ -45,7 +50,8 @@ def account(provider: str, *, base_url: str | None, api_key: str | None) -> dict
|
|
|
45
50
|
row.setdefault("provider", provider)
|
|
46
51
|
row["windows"] = [w for w in (row.get("windows") or []) if isinstance(w, dict)]
|
|
47
52
|
row["details"] = list(row.get("details") or [])
|
|
48
|
-
|
|
53
|
+
# The Agent's `available` is a property, which `_plain` (asdict/vars) never sees; read it off the snapshot.
|
|
54
|
+
row["available"] = bool(getattr(snapshot, "available", row.get("available")))
|
|
49
55
|
return row
|
|
50
56
|
|
|
51
57
|
|