@maudecode/talaria-web 1.17.2 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_release.json +8 -8
- package/dist/api/auth-raw.d.ts +1 -1
- package/dist/api/auth-raw.js +4 -4
- package/dist/api/auth-raw.js.map +1 -1
- package/dist/api/automation-raw.d.ts +6 -0
- package/dist/api/automation-raw.js +53 -6
- package/dist/api/automation-raw.js.map +1 -1
- package/dist/api/automation-router.d.ts +26 -1
- package/dist/api/automation-router.js +16 -2
- package/dist/api/automation-router.js.map +1 -1
- package/dist/api/chat-router.d.ts +649 -15
- package/dist/api/chat-router.js +52 -50
- package/dist/api/chat-router.js.map +1 -1
- package/dist/api/git-router.d.ts +35 -1
- package/dist/api/git-router.js +18 -19
- package/dist/api/git-router.js.map +1 -1
- package/dist/api/raw-routes.d.ts +8 -0
- package/dist/api/raw-routes.js +247 -36
- package/dist/api/raw-routes.js.map +1 -1
- package/dist/api/router.d.ts +10 -0
- package/dist/api/router.js +10 -8
- package/dist/api/router.js.map +1 -1
- package/dist/api/sessions-router.d.ts +1492 -115
- package/dist/api/sessions-router.js +134 -8
- package/dist/api/sessions-router.js.map +1 -1
- package/dist/api/settings-router.d.ts +31 -0
- package/dist/api/settings-router.js +8 -3
- package/dist/api/settings-router.js.map +1 -1
- package/dist/api/sse-routes.d.ts +5 -0
- package/dist/api/sse-routes.js +50 -3
- package/dist/api/sse-routes.js.map +1 -1
- package/dist/api/tools-raw.js +6 -23
- package/dist/api/tools-raw.js.map +1 -1
- package/dist/api/tools-router.d.ts +356 -15
- package/dist/api/tools-router.js +148 -19
- package/dist/api/tools-router.js.map +1 -1
- package/dist/app.js +35 -15
- package/dist/app.js.map +1 -1
- package/dist/auth/gate.js +5 -2
- package/dist/auth/gate.js.map +1 -1
- package/dist/auth/oidc.d.ts +5 -2
- package/dist/auth/oidc.js +8 -7
- package/dist/auth/oidc.js.map +1 -1
- package/dist/bin/talaria-web.js +1 -1
- package/dist/bin/talaria-web.js.map +1 -1
- package/dist/cli/dotenv.js +1 -0
- package/dist/cli/dotenv.js.map +1 -1
- package/dist/config/agent-config.js +3 -2
- package/dist/config/agent-config.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.js +5 -1
- package/dist/config.js.map +1 -1
- package/dist/http/context.d.ts +17 -3
- package/dist/http/context.js +31 -4
- package/dist/http/context.js.map +1 -1
- package/dist/http/csp.d.ts +2 -1
- package/dist/http/csp.js +3 -2
- package/dist/http/csp.js.map +1 -1
- package/dist/profiles/profiles.d.ts +10 -0
- package/dist/profiles/profiles.js +40 -2
- package/dist/profiles/profiles.js.map +1 -1
- package/dist/providers/catalog.d.ts +6 -2
- package/dist/providers/catalog.js +30 -11
- package/dist/providers/catalog.js.map +1 -1
- package/dist/raster-image.d.ts +7 -0
- package/dist/raster-image.js +267 -0
- package/dist/raster-image.js.map +1 -0
- package/dist/redact.js +327 -40
- package/dist/redact.js.map +1 -1
- package/dist/runtime.js +89 -23
- package/dist/runtime.js.map +1 -1
- package/dist/server.js +5 -0
- package/dist/server.js.map +1 -1
- package/dist/sessions/background-tasks.d.ts +2 -9
- package/dist/sessions/background-tasks.js +2 -12
- package/dist/sessions/background-tasks.js.map +1 -1
- package/dist/sessions/cli-sessions.d.ts +15 -4
- package/dist/sessions/cli-sessions.js +120 -29
- package/dist/sessions/cli-sessions.js.map +1 -1
- package/dist/sessions/completions.d.ts +48 -8
- package/dist/sessions/completions.js +222 -26
- package/dist/sessions/completions.js.map +1 -1
- package/dist/sessions/export.d.ts +5 -0
- package/dist/sessions/export.js +30 -0
- package/dist/sessions/export.js.map +1 -1
- package/dist/sessions/gateway-watcher.d.ts +7 -3
- package/dist/sessions/gateway-watcher.js +31 -19
- package/dist/sessions/gateway-watcher.js.map +1 -1
- package/dist/sessions/handoff.d.ts +32 -0
- package/dist/sessions/handoff.js +114 -0
- package/dist/sessions/handoff.js.map +1 -0
- package/dist/sessions/list.d.ts +7 -1
- package/dist/sessions/list.js +13 -5
- package/dist/sessions/list.js.map +1 -1
- package/dist/sessions/merge.d.ts +2 -4
- package/dist/sessions/merge.js +8 -15
- package/dist/sessions/merge.js.map +1 -1
- package/dist/sessions/recovery.d.ts +22 -0
- package/dist/sessions/recovery.js +506 -0
- package/dist/sessions/recovery.js.map +1 -0
- package/dist/sessions/relay.d.ts +21 -2
- package/dist/sessions/relay.js +156 -11
- package/dist/sessions/relay.js.map +1 -1
- package/dist/sessions/service.d.ts +108 -6
- package/dist/sessions/service.js +403 -29
- package/dist/sessions/service.js.map +1 -1
- package/dist/sessions/session.d.ts +6 -0
- package/dist/sessions/session.js +40 -2
- package/dist/sessions/session.js.map +1 -1
- package/dist/sessions/state-db-worker.d.ts +1 -0
- package/dist/sessions/state-db-worker.js +33 -0
- package/dist/sessions/state-db-worker.js.map +1 -0
- package/dist/sessions/state-db.d.ts +61 -0
- package/dist/sessions/state-db.js +291 -0
- package/dist/sessions/state-db.js.map +1 -1
- package/dist/sessions/store.d.ts +7 -0
- package/dist/sessions/store.js +31 -22
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/titles.d.ts +2 -0
- package/dist/sessions/titles.js +4 -0
- package/dist/sessions/titles.js.map +1 -1
- package/dist/sessions/tool-display.d.ts +3 -3
- package/dist/sessions/tool-display.js +11 -37
- package/dist/sessions/tool-display.js.map +1 -1
- package/dist/sessions/turn-context.d.ts +1 -1
- package/dist/sessions/turn-context.js +11 -7
- package/dist/sessions/turn-context.js.map +1 -1
- package/dist/sessions/turn.d.ts +37 -2
- package/dist/sessions/turn.js +182 -49
- package/dist/sessions/turn.js.map +1 -1
- package/dist/sessions/wakeup-pause.d.ts +59 -0
- package/dist/sessions/wakeup-pause.js +181 -0
- package/dist/sessions/wakeup-pause.js.map +1 -0
- package/dist/settings.js +5 -7
- package/dist/settings.js.map +1 -1
- package/dist/sidecar/fake.d.ts +2 -2
- package/dist/sidecar/fake.js.map +1 -1
- package/dist/startup.d.ts +13 -5
- package/dist/startup.js +48 -0
- package/dist/startup.js.map +1 -1
- package/dist/test/ts-worker-hooks.d.ts +1 -0
- package/dist/test/ts-worker-hooks.js +24 -0
- package/dist/test/ts-worker-hooks.js.map +1 -0
- package/dist/text/diff.d.ts +9 -14
- package/dist/text/diff.js +74 -108
- package/dist/text/diff.js.map +1 -1
- package/dist/tools/crons.js +45 -8
- package/dist/tools/crons.js.map +1 -1
- package/dist/tools/dashboard-plugins.d.ts +53 -0
- package/dist/tools/dashboard-plugins.js +188 -0
- package/dist/tools/dashboard-plugins.js.map +1 -0
- package/dist/tools/extensions.d.ts +13 -1
- package/dist/tools/extensions.js +132 -60
- package/dist/tools/extensions.js.map +1 -1
- package/dist/tools/health.d.ts +32 -0
- package/dist/tools/health.js +111 -19
- package/dist/tools/health.js.map +1 -1
- package/dist/tools/mcp.d.ts +13 -0
- package/dist/tools/mcp.js +88 -0
- package/dist/tools/mcp.js.map +1 -1
- package/dist/tools/project-os.d.ts +11 -0
- package/dist/tools/project-os.js +267 -0
- package/dist/tools/project-os.js.map +1 -0
- package/dist/tools/skills.d.ts +5 -1
- package/dist/tools/skills.js +5 -3
- package/dist/tools/skills.js.map +1 -1
- package/dist/tools/updates.d.ts +27 -9
- package/dist/tools/updates.js +163 -51
- package/dist/tools/updates.js.map +1 -1
- package/dist/tools/wiki.d.ts +68 -0
- package/dist/tools/wiki.js +413 -0
- package/dist/tools/wiki.js.map +1 -0
- package/dist/workspace/escape.d.ts +61 -0
- package/dist/workspace/escape.js +149 -0
- package/dist/workspace/escape.js.map +1 -0
- package/dist/workspace/extract.d.ts +22 -0
- package/dist/workspace/extract.js +308 -0
- package/dist/workspace/extract.js.map +1 -0
- package/dist/workspace/fs.d.ts +12 -0
- package/dist/workspace/fs.js +92 -23
- package/dist/workspace/fs.js.map +1 -1
- package/dist/workspace/git.d.ts +18 -0
- package/dist/workspace/git.js +38 -25
- package/dist/workspace/git.js.map +1 -1
- package/dist/workspace/media.d.ts +4 -4
- package/dist/workspace/media.js +16 -45
- package/dist/workspace/media.js.map +1 -1
- package/dist/workspace/rollback.d.ts +34 -20
- package/dist/workspace/rollback.js +171 -265
- package/dist/workspace/rollback.js.map +1 -1
- package/dist/workspace/unzip.js +4 -1
- package/dist/workspace/unzip.js.map +1 -1
- package/dist/workspace/upload.d.ts +7 -4
- package/dist/workspace/upload.js +5 -2
- package/dist/workspace/upload.js.map +1 -1
- package/dist/workspace/worktrees.d.ts +2 -1
- package/dist/workspace/worktrees.js +14 -15
- package/dist/workspace/worktrees.js.map +1 -1
- package/package.json +4 -2
- package/sidecar/agent_dependency.json +4 -4
- package/sidecar/scripts/replay_sidecar.py +24 -0
- package/sidecar/talaria_sidecar/__init__.py +1 -1
- package/sidecar/talaria_sidecar/home.py +13 -2
- package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
- package/sidecar/talaria_sidecar/methods/aux.py +39 -5
- package/sidecar/talaria_sidecar/methods/chat.py +9 -5
- package/sidecar/talaria_sidecar/methods/checkpoints.py +89 -0
- package/sidecar/talaria_sidecar/methods/commands.py +81 -0
- package/sidecar/talaria_sidecar/methods/cron.py +29 -2
- package/sidecar/talaria_sidecar/methods/gateway.py +32 -15
- package/sidecar/talaria_sidecar/methods/kanban.py +28 -7
- package/sidecar/talaria_sidecar/methods/mcp.py +1 -1
- package/sidecar/talaria_sidecar/methods/process.py +58 -4
- package/sidecar/talaria_sidecar/methods/profiles.py +77 -7
- package/sidecar/talaria_sidecar/methods/providers.py +27 -8
- package/sidecar/talaria_sidecar/methods/state_db.py +39 -5
- package/sidecar/talaria_sidecar/runtime.py +17 -1
- package/static/dist/FILES.txt +127 -126
- package/static/dist/assets/AppShell-g3bkgnvt.js +1 -0
- package/static/dist/assets/ChatPage-oo7bdan6.js +25 -0
- package/static/dist/assets/Dialog-g355qxhp.js +1 -0
- package/static/dist/assets/ExtensionRoute-i2y9fvjm.js +1 -0
- package/static/dist/assets/HubRoute-lmkrbw47.js +2 -0
- package/static/dist/assets/{Markdown-mdwlvj3w.js → Markdown-j4kut4j2.js} +114 -114
- package/static/dist/assets/{RightPanel-ftvrjlcp.js → RightPanel-ohlsdzfl.js} +1 -1
- package/static/dist/assets/Select-kddq75qc.js +1 -0
- package/static/dist/assets/SessionListPanel-lu3oy0hw.js +1 -0
- package/static/dist/assets/{SettingsLayout-jonjefmj.js → SettingsLayout-o9bv4mgb.js} +1 -1
- package/static/dist/assets/{States-nmejrwrm.js → States-eq6fe29m.js} +1 -1
- package/static/dist/assets/Toaster-pc247u9f.js +1 -0
- package/static/dist/assets/{_app-gdidzd94.js → _app-f8n1i0m1.js} +1 -1
- package/static/dist/assets/{_app.ext._extensionId-opyh9y0v.js → _app.ext._extensionId-gfbzpoou.js} +1 -1
- package/static/dist/assets/_app.index-bzzv80hk.js +1 -0
- package/static/dist/assets/_app.insights-dzixbryi.js +1 -0
- package/static/dist/assets/_app.kanban-c6hep2us.js +1 -0
- package/static/dist/assets/_app.logs-i69qds98.js +1 -0
- package/static/dist/assets/_app.memory-kl4vf0gd.js +1 -0
- package/static/dist/assets/_app.profiles-b8zinmyp.js +1 -0
- package/static/dist/assets/{_app.session._sessionId-bfd4h3id.js → _app.session._sessionId-fn1bhde6.js} +1 -1
- package/static/dist/assets/_app.settings-cg8plwmf.js +1 -0
- package/static/dist/assets/_app.settings._section-bkwbi9zn.js +3 -0
- package/static/dist/assets/_app.skills-cs47sbvs.js +1 -0
- package/static/dist/assets/_app.tasks-dwvk64tc.js +1 -0
- package/static/dist/assets/_app.todos-c8xjm8p3.js +1 -0
- package/static/dist/assets/_app.workspaces-ehvd1gds.js +1 -0
- package/static/dist/assets/{abnfDiagram-VCTEODGH-b2x1lqot.js → abnfDiagram-VCTEODGH-c8kdixke.js} +1 -1
- package/static/dist/assets/architecture-7GRP2DOG-6crvnylg.js +1 -0
- package/static/dist/assets/{architectureDiagram-5GKGNRK7-h38o5l7b.js → architectureDiagram-5GKGNRK7-cku06x1p.js} +1 -1
- package/static/dist/assets/{back-bemaer09.js → back-h7aun7ew.js} +1 -1
- package/static/dist/assets/{blockDiagram-I7D4REHJ-bqquxv2u.js → blockDiagram-I7D4REHJ-jzthp1wa.js} +1 -1
- package/static/dist/assets/{c4Diagram-7LVT6UL2-f3fb8w6z.js → c4Diagram-7LVT6UL2-mwr67mt8.js} +1 -1
- package/static/dist/assets/{cancel-drd0g6d9.js → cancel-fhi0vcnu.js} +1 -1
- package/static/dist/assets/channel-b2zo73z1.js +1 -0
- package/static/dist/assets/{chunk-4HAMMTFA-bzfyyzp3.js → chunk-4HAMMTFA-gpq1y228.js} +1 -1
- package/static/dist/assets/{chunk-75Z2AOVW-ly99rfx5.js → chunk-75Z2AOVW-ktje58c4.js} +1 -1
- package/static/dist/assets/{chunk-DU6HZSFF-bg6ecfqf.js → chunk-DU6HZSFF-ofajdixh.js} +3 -3
- package/static/dist/assets/{chunk-F27PBJKO-e8k7mmqw.js → chunk-F27PBJKO-cmev85ti.js} +1 -1
- package/static/dist/assets/{chunk-GMAD6QVW-cmalnoaj.js → chunk-GMAD6QVW-htjgt22y.js} +1 -1
- package/static/dist/assets/{chunk-GVQU2GXP-gmif5o01.js → chunk-GVQU2GXP-qndvoxl7.js} +1 -1
- package/static/dist/assets/{chunk-IMKFNOWR-f1nwwh6u.js → chunk-IMKFNOWR-e5bkd5q6.js} +1 -1
- package/static/dist/assets/{chunk-L3NEJ4N5-oktfh5v7.js → chunk-L3NEJ4N5-mj1155r6.js} +1 -1
- package/static/dist/assets/{chunk-OSK3NFVY-b1sdx96z.js → chunk-OSK3NFVY-e7gkikcw.js} +1 -1
- package/static/dist/assets/{chunk-P2QGCYS3-fhu4nzxt.js → chunk-P2QGCYS3-f7t54xfm.js} +1 -1
- package/static/dist/assets/{chunk-POPQ4Y6H-em31rnef.js → chunk-POPQ4Y6H-i927v9oa.js} +1 -1
- package/static/dist/assets/{chunk-PWAF6VOD-bxhhyftw.js → chunk-PWAF6VOD-m84lmnyt.js} +1 -1
- package/static/dist/assets/{chunk-SHT3W25Y-fmrvwcav.js → chunk-SHT3W25Y-mi0tlcu9.js} +1 -1
- package/static/dist/assets/{chunk-SVP7TREG-lymukl2j.js → chunk-SVP7TREG-dnau702j.js} +1 -1
- package/static/dist/assets/{chunk-TICWLB2K-lmza6k23.js → chunk-TICWLB2K-edem1gkg.js} +1 -1
- package/static/dist/assets/classDiagram-ZZMXUADV-oe99st8x.js +1 -0
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-oe99st8x.js +1 -0
- package/static/dist/assets/{copied-ima3pysb.js → copied-hcplv46p.js} +1 -1
- package/static/dist/assets/{copy-fw5b1pc6.js → copy-magtofyp.js} +1 -1
- package/static/dist/assets/{cynefin-OW5HDTMX-czlhbyhs.js → cynefin-OW5HDTMX-siqlulkl.js} +1 -1
- package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-gq7mvswb.js → cynefinDiagram-5FMLGOSQ-i648tah9.js} +1 -1
- package/static/dist/assets/{dagre-GXQ25YYZ-cqdbf7dr.js → dagre-GXQ25YYZ-mwn6uc17.js} +1 -1
- package/static/dist/assets/{diagram-S7CK7UJ4-o6arxa58.js → diagram-S7CK7UJ4-l8abxaes.js} +1 -1
- package/static/dist/assets/{diagram-UQ7AKVKN-nhnd68tz.js → diagram-UQ7AKVKN-mekq7zgq.js} +1 -1
- package/static/dist/assets/{diagram-VSXAHHWV-dk2sclzn.js → diagram-VSXAHHWV-fnky6lvc.js} +1 -1
- package/static/dist/assets/{diagram-VX7I27RA-n3k5nyx3.js → diagram-VX7I27RA-xe6r5qfw.js} +1 -1
- package/static/dist/assets/{diagram-Z3DM3KII-m6q9cwdh.js → diagram-Z3DM3KII-nq07dg7k.js} +1 -1
- package/static/dist/assets/{ebnfDiagram-PWID7BFC-lsa5641p.js → ebnfDiagram-PWID7BFC-c62ipm3g.js} +1 -1
- package/static/dist/assets/endpoints-klbxod4t.js +64 -0
- package/static/dist/assets/{erDiagram-RLTQ6QDP-fo2ckbe6.js → erDiagram-RLTQ6QDP-h39kmnzy.js} +1 -1
- package/static/dist/assets/eventmodeling-NTZA5JFV-obfede4u.js +1 -0
- package/static/dist/assets/flowDiagram-HODETNUW-kdpsp1je.js +1 -0
- package/static/dist/assets/{ganttDiagram-EL5Y4UJY-nzqz6x9g.js → ganttDiagram-EL5Y4UJY-ngbsyp37.js} +1 -1
- package/static/dist/assets/{gitGraph-4MIJSDKK-gukvcxe6.js → gitGraph-4MIJSDKK-fr2i7caa.js} +1 -1
- package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-lqhdp8f0.js → gitGraphDiagram-WWUBYQGX-eravdti4.js} +1 -1
- package/static/dist/assets/{highlighted-body-KPVGNVTW-igw6cp39.js → highlighted-body-KPVGNVTW-jwpop9p8.js} +1 -1
- package/static/dist/assets/host-i828xuo7.js +1 -0
- package/static/dist/assets/index-eh8zilnv.css +2 -0
- package/static/dist/assets/{index-cxq58xb1.js → index-r648f4o1.js} +5 -5
- package/static/dist/assets/{info-A6RAGUB7-gvu7a93l.js → info-A6RAGUB7-h11x0dxo.js} +1 -1
- package/static/dist/assets/{infoDiagram-27XIBGKW-m8t8g6w1.js → infoDiagram-27XIBGKW-dfh47dw9.js} +1 -1
- package/static/dist/assets/{ishikawaDiagram-5VMMS53U-l07vkbgj.js → ishikawaDiagram-5VMMS53U-i6ypq351.js} +1 -1
- package/static/dist/assets/{journeyDiagram-3NMN7TZE-hybkgj8l.js → journeyDiagram-3NMN7TZE-lduf1z95.js} +1 -1
- package/static/dist/assets/{kanban-definition-UXKFOSKX-by13uvtn.js → kanban-definition-UXKFOSKX-e18bu4pg.js} +1 -1
- package/static/dist/assets/{lazyRouteComponent-af0lvgjx.js → lazyRouteComponent-e1y4t2i9.js} +1 -1
- package/static/dist/assets/{line-osxq6fq7.js → line-meizqsvn.js} +1 -1
- package/static/dist/assets/{link-jyxicdhg.js → link-oot2yn73.js} +1 -1
- package/static/dist/assets/login-gu28l9ou.js +1 -0
- package/static/dist/assets/mermaid-HWGCJPDP-mtwrj8fn.js +1 -0
- package/static/dist/assets/{mermaid-parser.core-i91oxmou.js → mermaid-parser.core-ntne45i9.js} +3 -3
- package/static/dist/assets/{mindmap-definition-YA3MSWOX-m82mx8ww.js → mindmap-definition-YA3MSWOX-27b26eok.js} +1 -1
- package/static/dist/assets/onboarding-lkphzu1s.js +1 -0
- package/static/dist/assets/{onboarding_model_label-dp7xffoh.js → onboarding_model_label-ftpjdwqk.js} +1 -1
- package/static/dist/assets/{packet-AYTQ26CC-nbtgge2o.js → packet-AYTQ26CC-l6jdka8p.js} +1 -1
- package/static/dist/assets/{pegDiagram-XKGWAZYB-f1zxzga1.js → pegDiagram-XKGWAZYB-g1er3bxj.js} +1 -1
- package/static/dist/assets/{pie-WAS4IAKB-of8vuhxs.js → pie-WAS4IAKB-i0ylcrt9.js} +1 -1
- package/static/dist/assets/{pieDiagram-E7YTZNPT-o7y8ftio.js → pieDiagram-E7YTZNPT-fbz1e55z.js} +1 -1
- package/static/dist/assets/{quadrantDiagram-AXDQQJYC-iqiwia1t.js → quadrantDiagram-AXDQQJYC-hhue7d8o.js} +1 -1
- package/static/dist/assets/{radar-RG4KPBEZ-delnsy2v.js → radar-RG4KPBEZ-f9b70aa4.js} +1 -1
- package/static/dist/assets/{railroad-74A4TZTK-nspnyq9u.js → railroad-74A4TZTK-lgsuoeda.js} +1 -1
- package/static/dist/assets/railroad-abnf-HS5TGJTU-nf9d6pdx.js +1 -0
- package/static/dist/assets/railroad-ebnf-LZEXJU2U-f7ummkt5.js +1 -0
- package/static/dist/assets/railroad-peg-WCYAUIDC-oczq5tqs.js +1 -0
- package/static/dist/assets/{railroadDiagram-O6MQD6OU-z41tqx5z.js → railroadDiagram-O6MQD6OU-eqpy9fnz.js} +1 -1
- package/static/dist/assets/{refresh-b4uams1i.js → refresh-abtu93ke.js} +1 -1
- package/static/dist/assets/{refresh-cw-nt1dtsx4.js → refresh-cw-hlwb02qv.js} +1 -1
- package/static/dist/assets/{requirementDiagram-BXWQKSXE-bdvif194.js → requirementDiagram-BXWQKSXE-ogf6puhi.js} +1 -1
- package/static/dist/assets/{sankeyDiagram-P5KCCOFB-h73e0rol.js → sankeyDiagram-P5KCCOFB-fqb8xcjf.js} +1 -1
- package/static/dist/assets/{saved-kg52csy7.js → saved-girnabkf.js} +1 -1
- package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-iv95mb1z.js → sequenceDiagram-WJ2MYXX4-dlcz6qjz.js} +1 -1
- package/static/dist/assets/{settings_section_extensions_title-ej0iapxe.js → settings_section_extensions_title-h14cwk6f.js} +1 -1
- package/static/dist/assets/share._token-kl0r7ido.js +2 -0
- package/static/dist/assets/{sliders-horizontal-fj18it47.js → sliders-horizontal-n1zm34qq.js} +1 -1
- package/static/dist/assets/sse-iwku0sjo.js +1 -0
- package/static/dist/assets/{stateDiagram-D77RDMKH-k417ixmf.js → stateDiagram-D77RDMKH-fyoznpcs.js} +1 -1
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-ixhx8r46.js +1 -0
- package/static/dist/assets/{swimlanes-42K2YHIH-esola70w.js → swimlanes-42K2YHIH-oefldhfy.js} +1 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-h9ersk2e.js +8 -0
- package/static/dist/assets/{timeline-definition-24CTP7MA-getrz2y8.js → timeline-definition-24CTP7MA-b1vl2zil.js} +1 -1
- package/static/dist/assets/todoStore-jjr5yr78.js +1 -0
- package/static/dist/assets/{treeView-Q6P3EWNA-hc7ap1q7.js → treeView-Q6P3EWNA-f7hcci5o.js} +1 -1
- package/static/dist/assets/{treemap-WGGIJYW6-b7k1syy5.js → treemap-WGGIJYW6-nl85071a.js} +1 -1
- package/static/dist/assets/{tts-lxdxnmjd.js → units-kgxzxfhi.js} +1 -1
- package/static/dist/assets/{untitled-ovpfthg4.js → untitled-cikfjntn.js} +1 -1
- package/static/dist/assets/{useForm-bcblv1rx.js → useForm-kqoqg0k9.js} +1 -1
- package/static/dist/assets/useLocale-o59tbiea.js +1 -0
- package/static/dist/assets/{useParams-lhg566ew.js → useParams-b4dq6n4q.js} +1 -1
- package/static/dist/assets/{useSearch-lbrh6ejn.js → useSearch-ipfmv3he.js} +1 -1
- package/static/dist/assets/{vennDiagram-4TSXK5OY-n6pcy5qb.js → vennDiagram-4TSXK5OY-h5hzbk0n.js} +1 -1
- package/static/dist/assets/{wardley-WFR3VGLG-lpa18vz1.js → wardley-WFR3VGLG-jftvpm3e.js} +1 -1
- package/static/dist/assets/{wardleyDiagram-VM6X3IG4-kpclwwg7.js → wardleyDiagram-VM6X3IG4-hedh85ge.js} +1 -1
- package/static/dist/assets/{xychartDiagram-S5SC5T6Z-paa2n8y0.js → xychartDiagram-S5SC5T6Z-e49523ab.js} +1 -1
- package/static/dist/index.html +1 -1
- package/static/dist/sw.js +1 -1
- package/static/dist/assets/AppShell-fb2td9zi.js +0 -1
- package/static/dist/assets/ChatPage-cj96kiy9.js +0 -25
- package/static/dist/assets/Dialog-kxcg7ard.js +0 -1
- package/static/dist/assets/ExtensionRoute-jf7h0754.js +0 -1
- package/static/dist/assets/HubRoute-gmanpd5y.js +0 -2
- package/static/dist/assets/Select-bilaypej.js +0 -1
- package/static/dist/assets/SessionListPanel-og8n36qs.js +0 -1
- package/static/dist/assets/Toaster-nnl8zkt9.js +0 -1
- package/static/dist/assets/_app.index-hbarzbjx.js +0 -1
- package/static/dist/assets/_app.insights-luywlarz.js +0 -1
- package/static/dist/assets/_app.kanban-kincnbr8.js +0 -1
- package/static/dist/assets/_app.logs-oi2gr3gs.js +0 -1
- package/static/dist/assets/_app.memory-b6jfivt6.js +0 -1
- package/static/dist/assets/_app.profiles-lpvixmqp.js +0 -1
- package/static/dist/assets/_app.settings-vowx3iq0.js +0 -1
- package/static/dist/assets/_app.settings._section-cnntg1tu.js +0 -3
- package/static/dist/assets/_app.skills-fb57lpau.js +0 -1
- package/static/dist/assets/_app.tasks-eszpx1q7.js +0 -2
- package/static/dist/assets/_app.todos-nr05ac6w.js +0 -1
- package/static/dist/assets/_app.workspaces-d2luomu6.js +0 -1
- package/static/dist/assets/architecture-7GRP2DOG-hd0o7bde.js +0 -1
- package/static/dist/assets/channel-cpdgf867.js +0 -1
- package/static/dist/assets/classDiagram-ZZMXUADV-ko09sguk.js +0 -1
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-ko09sguk.js +0 -1
- package/static/dist/assets/endpoints-d33siokx.js +0 -64
- package/static/dist/assets/eventmodeling-NTZA5JFV-emctlo64.js +0 -1
- package/static/dist/assets/flowDiagram-HODETNUW-bfi3moqa.js +0 -1
- package/static/dist/assets/index-hgtx08n5.css +0 -2
- package/static/dist/assets/login-ec21t39x.js +0 -1
- package/static/dist/assets/mermaid-HWGCJPDP-gfl8z3ep.js +0 -1
- package/static/dist/assets/onboarding-btnltvyz.js +0 -1
- package/static/dist/assets/railroad-abnf-HS5TGJTU-nst1qxyn.js +0 -1
- package/static/dist/assets/railroad-ebnf-LZEXJU2U-ci37x8qy.js +0 -1
- package/static/dist/assets/railroad-peg-WCYAUIDC-aimkk07z.js +0 -1
- package/static/dist/assets/share._token-f97e94r0.js +0 -2
- package/static/dist/assets/sse-fwpqtzbg.js +0 -1
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-hwn4apct.js +0 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-d1h97oyg.js +0 -8
- package/static/dist/assets/todoStore-eginf9h4.js +0 -1
- package/static/dist/assets/useLocale-fi5jb8ma.js +0 -1
|
@@ -244,6 +244,77 @@ def execute_plugin_command(command: str) -> str | None:
|
|
|
244
244
|
return f"Plugin command error: {type(exc).__name__}"
|
|
245
245
|
|
|
246
246
|
|
|
247
|
+
def _profile_bundles(skill_bundles) -> dict[str, dict[str, Any]]:
|
|
248
|
+
"""The scoped profile's bundles keyed ``/slug``, read from disk the way ``scan_bundles`` reads them.
|
|
249
|
+
|
|
250
|
+
``scan_bundles``/``get_skill_bundles`` publish one profile's bundles in a process-wide cache keyed only by mtime,
|
|
251
|
+
which cron jobs in other profiles read concurrently, so the sidecar never fills it."""
|
|
252
|
+
out: dict[str, dict[str, Any]] = {}
|
|
253
|
+
for path in skill_bundles._iter_bundle_files():
|
|
254
|
+
info = skill_bundles._load_bundle_file(path)
|
|
255
|
+
if info:
|
|
256
|
+
out.setdefault(f"/{info['slug']}", info)
|
|
257
|
+
return out
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def list_command_bundles() -> list[dict[str, Any]]:
|
|
261
|
+
"""The profile's skill bundles as slash-command rows; [] when the bundle runtime is missing or fails."""
|
|
262
|
+
try:
|
|
263
|
+
import agent.skill_bundles as skill_bundles
|
|
264
|
+
except ImportError:
|
|
265
|
+
return []
|
|
266
|
+
try:
|
|
267
|
+
bundles = sorted(_profile_bundles(skill_bundles).values(), key=lambda b: b["slug"])
|
|
268
|
+
except Exception: # noqa: BLE001
|
|
269
|
+
log.warning("Failed to list skill bundles", exc_info=True)
|
|
270
|
+
return []
|
|
271
|
+
return [{"name": str(b["slug"]).strip().lower(), "description": str(b.get("description") or "").strip() or "Skill bundle",
|
|
272
|
+
"skill_count": len(b.get("skills") or []), "source": "bundle"} for b in bundles if str(b.get("slug") or "").strip()]
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _bundle_invocation(info: dict[str, Any], instruction: str) -> tuple[str, list[str], list[str]] | None:
|
|
276
|
+
"""``build_bundle_invocation_message`` for a bundle read by ``_profile_bundles`` (that one looks it up in the shared cache),
|
|
277
|
+
skipping the skills disabled for Web chat (``skills.platform_disabled.webui``)."""
|
|
278
|
+
from agent.skill_commands import _disabled_skill_names, _load_skill_blocks, _load_skill_payload, _scaffold_header
|
|
279
|
+
|
|
280
|
+
name = info["name"]
|
|
281
|
+
loaded, missing, disabled, blocks = _load_skill_blocks(
|
|
282
|
+
[(skill or "").strip() for skill in info["skills"]], _load_skill_payload, lambda _skill: f'[Loaded as part of the "{name}" skill bundle.]',
|
|
283
|
+
None, disabled_names=_disabled_skill_names("webui"))
|
|
284
|
+
if not blocks:
|
|
285
|
+
return None
|
|
286
|
+
header = _scaffold_header(f'"{name}" skill bundle', loaded, lead_lines=[f"Bundle: {name}"], missing=missing, disabled=disabled,
|
|
287
|
+
extra_instruction=info.get("instruction") or "", user_instruction=instruction)
|
|
288
|
+
return "\n\n".join([header, *blocks]), loaded, missing
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def resolve_bundle_command(command: str) -> dict[str, Any]:
|
|
292
|
+
"""Expand ``/<bundle> [instruction]`` into the user message that loads the bundle's skills."""
|
|
293
|
+
name, instruction = parse_slash_command(command)
|
|
294
|
+
try:
|
|
295
|
+
import agent.skill_bundles as skill_bundles
|
|
296
|
+
from agent.skill_commands import resolve_slash_key
|
|
297
|
+
except ImportError as exc:
|
|
298
|
+
raise RpcError("Skill bundle runtime unavailable", condition="bundle_unavailable") from exc
|
|
299
|
+
try:
|
|
300
|
+
bundles = _profile_bundles(skill_bundles)
|
|
301
|
+
key = resolve_slash_key(name, bundles)
|
|
302
|
+
result = _bundle_invocation(bundles[key], instruction) if key else None
|
|
303
|
+
except ValueError as exc:
|
|
304
|
+
raise InvalidParams(str(exc)) from exc
|
|
305
|
+
except Exception as exc: # noqa: BLE001
|
|
306
|
+
log.warning("Failed to resolve skill bundle command", exc_info=True)
|
|
307
|
+
raise RpcError("Skill bundle command unavailable", condition="bundle_unavailable") from exc
|
|
308
|
+
if key is None:
|
|
309
|
+
raise RpcError("Bundle command not found", condition="bundle_not_found")
|
|
310
|
+
message, loaded, missing = result or ("", [], [])
|
|
311
|
+
message = str(message or "").strip()
|
|
312
|
+
if not message:
|
|
313
|
+
raise RpcError("Bundle command returned no invocation text", condition="bundle_unavailable")
|
|
314
|
+
return {"name": key.lstrip("/"), "source": "bundle", "message": message, "loaded_skills": [str(s) for s in loaded or []],
|
|
315
|
+
"missing_skills": [str(s) for s in missing or []]}
|
|
316
|
+
|
|
317
|
+
|
|
247
318
|
def resolve_moa_config(preset: str | None) -> dict:
|
|
248
319
|
try:
|
|
249
320
|
from hermes_cli.moa_config import moa_usage, normalize_moa_config
|
|
@@ -352,6 +423,16 @@ def register(registry) -> None:
|
|
|
352
423
|
raise RpcError(f"unknown command {parse_slash_command(command)[0]!r}", condition="command_not_found")
|
|
353
424
|
return {"output": output, "source": source}
|
|
354
425
|
|
|
426
|
+
@registry.method("commands.bundles")
|
|
427
|
+
def bundles(ctx: CallContext, params: dict) -> dict:
|
|
428
|
+
with scoped_home(profile_home_param(params)):
|
|
429
|
+
return {"bundles": list_command_bundles()}
|
|
430
|
+
|
|
431
|
+
@registry.method("commands.bundle_resolve")
|
|
432
|
+
def bundle_resolve(ctx: CallContext, params: dict) -> dict:
|
|
433
|
+
with scoped_home(profile_home_param(params)):
|
|
434
|
+
return resolve_bundle_command(str(params.get("command") or ""))
|
|
435
|
+
|
|
355
436
|
@registry.method("commands.moa_preset")
|
|
356
437
|
def moa(ctx: CallContext, params: dict) -> dict:
|
|
357
438
|
with scoped_home(profile_home_param(params)):
|
|
@@ -33,6 +33,35 @@ _RUNNING_LOCK = threading.Lock()
|
|
|
33
33
|
_SNAPSHOT_LOCK = threading.Lock()
|
|
34
34
|
|
|
35
35
|
|
|
36
|
+
def _main_model_pin() -> tuple[str | None, str | None]:
|
|
37
|
+
"""``(provider, model)`` the scoped profile's main agent runs on: the Agent's own ``cron.jobs._main_model_pin``.
|
|
38
|
+
|
|
39
|
+
Agent 0.21.3 predates that helper, so it is rebuilt from the readers 0.21.4 composes: the home's effective
|
|
40
|
+
``config.yaml`` model and the provider the runtime resolves for it. ``(None, None)`` when no model is configured.
|
|
41
|
+
"""
|
|
42
|
+
from cron import jobs
|
|
43
|
+
|
|
44
|
+
if hasattr(jobs, "_main_model_pin"):
|
|
45
|
+
return jobs._main_model_pin()
|
|
46
|
+
from hermes_cli.config_effective import load_user_config_effective
|
|
47
|
+
from hermes_constants import get_hermes_home
|
|
48
|
+
|
|
49
|
+
cfg_path = get_hermes_home() / "config.yaml"
|
|
50
|
+
cfg = load_user_config_effective(cfg_path) if cfg_path.exists() else {}
|
|
51
|
+
model_cfg = cfg.get("model") or {}
|
|
52
|
+
model = model_cfg.get("default") or model_cfg.get("model") if isinstance(model_cfg, dict) else model_cfg
|
|
53
|
+
model = model.strip() or None if isinstance(model, str) else None
|
|
54
|
+
if not model:
|
|
55
|
+
return None, None
|
|
56
|
+
provider = None
|
|
57
|
+
with contextlib.suppress(Exception):
|
|
58
|
+
from hermes_cli.runtime_provider import resolve_runtime_provider
|
|
59
|
+
|
|
60
|
+
resolved = resolve_runtime_provider(requested=None).get("provider")
|
|
61
|
+
provider = resolved.strip().lower() or None if isinstance(resolved, str) else None
|
|
62
|
+
return provider, model
|
|
63
|
+
|
|
64
|
+
|
|
36
65
|
class CronUnavailable(RpcError):
|
|
37
66
|
def __init__(self, exc: Exception):
|
|
38
67
|
super().__init__(f"cron unavailable: {exc}", condition="cron_unavailable")
|
|
@@ -396,8 +425,6 @@ def register(registry) -> None:
|
|
|
396
425
|
# to the store home; owner_profile only preserves management from the creating Web profile.
|
|
397
426
|
if post.get("profile") and execution_home and not (model and provider) and not body.get("no_agent"):
|
|
398
427
|
try:
|
|
399
|
-
from cron.jobs import _main_model_pin
|
|
400
|
-
|
|
401
428
|
with _SNAPSHOT_LOCK, scoped_home(Path(execution_home)):
|
|
402
429
|
profile_provider, profile_model = _main_model_pin()
|
|
403
430
|
if not profile_model:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""``gateway.*``: Hermes gateway lifecycle through the ``hermes`` CLI
|
|
1
|
+
"""``gateway.*``: Hermes gateway lifecycle (start, stop, restart) through the ``hermes`` CLI."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -16,7 +16,10 @@ from ..home import profile_home_param
|
|
|
16
16
|
from ..rpc import CallContext
|
|
17
17
|
|
|
18
18
|
log = logging.getLogger("talaria_sidecar.gateway")
|
|
19
|
-
|
|
19
|
+
# One lifecycle action at a time across every action: overlapping `hermes gateway` subprocesses race each other.
|
|
20
|
+
_ACTION_LOCK = threading.Lock()
|
|
21
|
+
ACTIONS = ("start", "stop", "restart")
|
|
22
|
+
_DONE = {"start": "started", "stop": "stopped", "restart": "restarted"}
|
|
20
23
|
|
|
21
24
|
|
|
22
25
|
def _hermes_command() -> str:
|
|
@@ -39,9 +42,11 @@ def _drain(stream, sink: list[str]) -> None:
|
|
|
39
42
|
pass
|
|
40
43
|
|
|
41
44
|
|
|
42
|
-
def
|
|
43
|
-
if not
|
|
44
|
-
|
|
45
|
+
def control(action: str, home: Path, cli_profile: str | None, ctx: CallContext, *, quick_timeout: float, background_wait: float) -> dict:
|
|
46
|
+
if action not in ACTIONS:
|
|
47
|
+
raise InvalidParams("unsupported gateway action")
|
|
48
|
+
if not _ACTION_LOCK.acquire(blocking=False):
|
|
49
|
+
return {"status": "busy", "message": "Another gateway action is already in progress. Please wait a moment and try again."}
|
|
45
50
|
try:
|
|
46
51
|
env = dict(os.environ)
|
|
47
52
|
env["HERMES_HOME"] = str(home)
|
|
@@ -49,9 +54,12 @@ def restart(home: Path, cli_profile: str | None, ctx: CallContext, *, quick_time
|
|
|
49
54
|
cmd = [_hermes_command()]
|
|
50
55
|
if cli_profile:
|
|
51
56
|
cmd.extend(["--profile", cli_profile])
|
|
52
|
-
cmd.extend(["gateway",
|
|
53
|
-
log.info("
|
|
54
|
-
|
|
57
|
+
cmd.extend(["gateway", action])
|
|
58
|
+
log.info("Running gateway %s via %s (HERMES_HOME=%s)", action, " ".join(cmd), home)
|
|
59
|
+
try:
|
|
60
|
+
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, env=env)
|
|
61
|
+
except FileNotFoundError:
|
|
62
|
+
return {"status": "failed", "message": "Hermes CLI not found"}
|
|
55
63
|
# Drain both pipes from the start: a chatty restart must never block on a full pipe and be mistaken for a hang.
|
|
56
64
|
out: list[str] = []
|
|
57
65
|
err: list[str] = []
|
|
@@ -67,8 +75,8 @@ def restart(home: Path, cli_profile: str | None, ctx: CallContext, *, quick_time
|
|
|
67
75
|
def outcome() -> dict:
|
|
68
76
|
stdout, stderr = collected()
|
|
69
77
|
if proc.returncode == 0:
|
|
70
|
-
return {"status": "completed", "message": "Gateway service
|
|
71
|
-
return {"status": "failed", "message": f"
|
|
78
|
+
return {"status": "completed", "message": f"Gateway service {_DONE[action]} successfully", "detail": stdout or stderr}
|
|
79
|
+
return {"status": "failed", "message": f"{action.capitalize()} failed: {stderr or stdout}", "detail": stdout or stderr, "returncode": proc.returncode}
|
|
72
80
|
|
|
73
81
|
ctx.emit("progress", {"phase": "started"})
|
|
74
82
|
deadline = time.monotonic() + quick_timeout
|
|
@@ -90,17 +98,26 @@ def restart(home: Path, cli_profile: str | None, ctx: CallContext, *, quick_time
|
|
|
90
98
|
proc.kill()
|
|
91
99
|
proc.wait(timeout=5)
|
|
92
100
|
collected()
|
|
93
|
-
return {"status": "failed", "message": f"Gateway
|
|
101
|
+
return {"status": "failed", "message": f"Gateway {action} process timed out after {background_wait:.0f}s and was terminated", "timed_out": True}
|
|
94
102
|
return outcome()
|
|
95
103
|
finally:
|
|
96
|
-
|
|
104
|
+
_ACTION_LOCK.release()
|
|
97
105
|
|
|
98
106
|
|
|
99
107
|
def register(registry) -> None:
|
|
100
|
-
|
|
101
|
-
def restart_(ctx: CallContext, params: dict) -> dict:
|
|
108
|
+
def run(ctx: CallContext, params: dict, action: str) -> dict:
|
|
102
109
|
home = profile_home_param(params)
|
|
103
110
|
cli_profile = params.get("cli_profile")
|
|
104
111
|
if cli_profile is not None and not isinstance(cli_profile, str):
|
|
105
112
|
raise InvalidParams("cli_profile must be a string")
|
|
106
|
-
|
|
113
|
+
# A restart drains the old gateway first, so it gets longer than start or stop.
|
|
114
|
+
default_wait = 240.0 if action == "restart" else 60.0
|
|
115
|
+
return control(action, home, cli_profile or None, ctx, quick_timeout=float(params.get("quick_timeout_seconds") or 2.0), background_wait=float(params.get("background_wait_seconds") or default_wait))
|
|
116
|
+
|
|
117
|
+
@registry.method("gateway.control", requires_agent=False)
|
|
118
|
+
def control_(ctx: CallContext, params: dict) -> dict:
|
|
119
|
+
return run(ctx, params, str(params.get("action") or ""))
|
|
120
|
+
|
|
121
|
+
@registry.method("gateway.restart", requires_agent=False)
|
|
122
|
+
def restart_(ctx: CallContext, params: dict) -> dict:
|
|
123
|
+
return run(ctx, params, "restart")
|
|
@@ -96,10 +96,28 @@ def _row_claim_live(row) -> bool:
|
|
|
96
96
|
return False
|
|
97
97
|
columns = row.keys()
|
|
98
98
|
claim = {k: row[k] if k in columns else None for k in ("status", "claim_lock", "worker_pid", "worker_started_at")}
|
|
99
|
+
claimed = bool(claim["claim_lock"] and claim["worker_pid"])
|
|
99
100
|
try:
|
|
100
|
-
|
|
101
|
-
except
|
|
102
|
-
return
|
|
101
|
+
helper = getattr(_kb(), "_claim_is_live", None)
|
|
102
|
+
except RpcError:
|
|
103
|
+
return claimed
|
|
104
|
+
if helper is not None:
|
|
105
|
+
try:
|
|
106
|
+
return bool(helper(claim))
|
|
107
|
+
except Exception: # noqa: BLE001 - the Agent could not decide: unknown liveness counts as live
|
|
108
|
+
return claimed
|
|
109
|
+
if not claimed:
|
|
110
|
+
return False
|
|
111
|
+
try:
|
|
112
|
+
# Agent 0.21.3 has no _claim_is_live, but its dispatcher's own worker PID check. Like its crash sweep, probe
|
|
113
|
+
# only a claim this host issued: another host's PID means nothing here, so a remote claim stays live.
|
|
114
|
+
from hermes_cli.kanban_db_dispatch import _pid_alive
|
|
115
|
+
|
|
116
|
+
if not str(claim["claim_lock"]).startswith(_kb()._host_prefix()):
|
|
117
|
+
return True
|
|
118
|
+
return bool(_pid_alive(int(claim["worker_pid"])))
|
|
119
|
+
except Exception: # noqa: BLE001
|
|
120
|
+
return True
|
|
103
121
|
|
|
104
122
|
|
|
105
123
|
def _claim_live(conn, task_id) -> bool:
|
|
@@ -655,11 +673,14 @@ def bulk_payload(params: dict) -> dict:
|
|
|
655
673
|
|
|
656
674
|
|
|
657
675
|
def dispatch_payload(params: dict) -> dict:
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
676
|
+
_kb()
|
|
677
|
+
try:
|
|
678
|
+
# Agent 0.21.6 dropped the kanban_db re-export; every supported release defines it here.
|
|
679
|
+
from hermes_cli.kanban_db_dispatch import dispatch_once
|
|
680
|
+
except Exception as exc: # noqa: BLE001
|
|
681
|
+
raise InvalidParams("dispatcher is unavailable") from exc
|
|
661
682
|
with _conn(board=_board(params)) as conn:
|
|
662
|
-
result =
|
|
683
|
+
result = dispatch_once(conn, dry_run=_bool(params, "dry_run"), max_spawn=_int(params, "max", 8, minimum=1, maximum=100))
|
|
663
684
|
if isinstance(result, dict):
|
|
664
685
|
return result
|
|
665
686
|
try:
|
|
@@ -8,6 +8,7 @@ import json
|
|
|
8
8
|
import logging
|
|
9
9
|
import queue
|
|
10
10
|
import sqlite3
|
|
11
|
+
import threading
|
|
11
12
|
import time
|
|
12
13
|
from contextlib import closing
|
|
13
14
|
from pathlib import Path
|
|
@@ -42,12 +43,65 @@ def _event_row(evt: dict) -> dict:
|
|
|
42
43
|
if not row.get("origin_ui_session_id"):
|
|
43
44
|
row["origin_ui_session_id"] = str(getattr(proc, "origin_ui_session_id", "") or "") or str(getattr(proc, "spawn_session_id", "") or "")
|
|
44
45
|
try:
|
|
45
|
-
|
|
46
|
+
# Only the exit is consumed by the agent's wait or poll; a watch notice from the same process still wakes (TAL-724).
|
|
47
|
+
row["consumed"] = bool(registry is not None and process_id and row.get("type") in (None, "completion") and registry.is_completion_consumed(process_id))
|
|
46
48
|
except Exception: # noqa: BLE001
|
|
47
49
|
row["consumed"] = False
|
|
48
50
|
return row
|
|
49
51
|
|
|
50
52
|
|
|
53
|
+
# Recovered sessions, held so their exit is seen even when an Agent call other than drain prunes them first.
|
|
54
|
+
_RECOVERED: dict[str, object] = {}
|
|
55
|
+
_RECOVERED_LOCK = threading.Lock()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _reports_recovered_exits(registry) -> bool:
|
|
59
|
+
"""Agent 0.21.6 (hermes-agent 7550800d) closes or prunes a recovered session whose PID is gone without queuing a
|
|
60
|
+
completion, since it never collected an exit status. Talaria still wakes the session with an unknown exit code."""
|
|
61
|
+
return callable(getattr(registry, "_detached_host_fate", None)) and callable(getattr(registry, "_publish_finished", None))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def track_recovered(registry) -> None:
|
|
65
|
+
if not _reports_recovered_exits(registry):
|
|
66
|
+
return
|
|
67
|
+
try:
|
|
68
|
+
with registry._lock:
|
|
69
|
+
detached = [s for s in registry._running.values() if getattr(s, "detached", False)]
|
|
70
|
+
except Exception: # noqa: BLE001 - recovery itself succeeded; only the exit report is lost
|
|
71
|
+
log.warning("Failed to track recovered processes", exc_info=True)
|
|
72
|
+
return
|
|
73
|
+
with _RECOVERED_LOCK:
|
|
74
|
+
_RECOVERED.update((s.id, s) for s in detached)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _publish_recovered_exits(registry) -> None:
|
|
78
|
+
with _RECOVERED_LOCK:
|
|
79
|
+
exited = [s for s in _RECOVERED.values() if s.exited]
|
|
80
|
+
for session in exited:
|
|
81
|
+
del _RECOVERED[session.id]
|
|
82
|
+
for session in exited:
|
|
83
|
+
# A kill (or any path that collected a status) already queued its own completion through _move_to_finished.
|
|
84
|
+
if session.exit_code is not None or getattr(session, "_finish_claimed", False):
|
|
85
|
+
continue
|
|
86
|
+
try:
|
|
87
|
+
# Best effort, as in the Agent: a durable result must not block the live completion.
|
|
88
|
+
from tools.process_registry_results import save_completed_result
|
|
89
|
+
|
|
90
|
+
save_completed_result(session)
|
|
91
|
+
except Exception: # noqa: BLE001
|
|
92
|
+
log.debug("Failed to retain the result of recovered process %s", session.id, exc_info=True)
|
|
93
|
+
try:
|
|
94
|
+
with registry._lock:
|
|
95
|
+
registry._running.pop(session.id, None)
|
|
96
|
+
session.exited_at = session.exited_at or time.time()
|
|
97
|
+
registry._finished[session.id] = session
|
|
98
|
+
registry._publish_finished(session, True)
|
|
99
|
+
except Exception: # noqa: BLE001 - retried on the next drain
|
|
100
|
+
log.warning("Failed to report the exit of recovered process %s", session.id, exc_info=True)
|
|
101
|
+
with _RECOVERED_LOCK:
|
|
102
|
+
_RECOVERED.setdefault(session.id, session)
|
|
103
|
+
|
|
104
|
+
|
|
51
105
|
def drain(max_events: int) -> list[dict]:
|
|
52
106
|
"""Pop every queued completion event; the server requeues what it does not own."""
|
|
53
107
|
registry = _registry()
|
|
@@ -60,6 +114,7 @@ def drain(max_events: int) -> list[dict]:
|
|
|
60
114
|
detached = [s for s in registry._running.values() if getattr(s, "detached", False)]
|
|
61
115
|
for session in detached:
|
|
62
116
|
registry._refresh_detached_session(session)
|
|
117
|
+
_publish_recovered_exits(registry)
|
|
63
118
|
except Exception: # noqa: BLE001
|
|
64
119
|
log.debug("Failed to reconcile recovered processes", exc_info=True)
|
|
65
120
|
out = []
|
|
@@ -415,10 +470,9 @@ def recover(base_home: Path) -> int:
|
|
|
415
470
|
"""TAL-533: re-adopt every profile's checkpointed processes when a sidecar starts, so a process in a profile nobody
|
|
416
471
|
uses yet is watched (and its exit reported) from the start. Entering a home's scope recovers it once; a failed
|
|
417
472
|
recovery fails the call, so the server retries."""
|
|
418
|
-
from
|
|
473
|
+
from .profiles import named_profile_homes
|
|
419
474
|
|
|
420
|
-
|
|
421
|
-
named = sorted(p for p in profiles_root.iterdir() if p.is_dir() and _PROFILE_ID_RE.match(p.name)) if profiles_root.is_dir() else []
|
|
475
|
+
named = [home for _, home in named_profile_homes(base_home)]
|
|
422
476
|
failed = []
|
|
423
477
|
for home in [base_home, *named]:
|
|
424
478
|
try:
|
|
@@ -24,6 +24,9 @@ log = logging.getLogger("talaria_sidecar.profiles")
|
|
|
24
24
|
_PROFILE_ID_RE = re.compile(r"[a-z0-9][a-z0-9_-]{0,63}")
|
|
25
25
|
_PROFILE_DIRS = ("skills", "memories", "sessions", "logs", "cron")
|
|
26
26
|
_CLONE_CONFIG_FILES = ("config.yaml", ".env", "SOUL.md")
|
|
27
|
+
_AVATAR_EXTS = ("png", "jpg", "webp")
|
|
28
|
+
# The Agent's ``INTERNAL_LISTING_SOURCES``: worker sessions are never a profile's conversation.
|
|
29
|
+
_INTERNAL_SOURCES = ("kanban", "tool", "oneshot")
|
|
27
30
|
_TERMINAL_ENV = {
|
|
28
31
|
"backend": "TERMINAL_ENV", "env_type": "TERMINAL_ENV", "cwd": "TERMINAL_CWD", "timeout": "TERMINAL_TIMEOUT", "lifetime_seconds": "TERMINAL_LIFETIME_SECONDS",
|
|
29
32
|
"modal_mode": "TERMINAL_MODAL_MODE", "docker_image": "TERMINAL_DOCKER_IMAGE", "docker_forward_env": "TERMINAL_DOCKER_FORWARD_ENV", "docker_env": "TERMINAL_DOCKER_ENV",
|
|
@@ -96,6 +99,58 @@ def _visible(profile_dir: Path) -> bool:
|
|
|
96
99
|
return not (isinstance(data, dict) and data.get("visible") is False)
|
|
97
100
|
|
|
98
101
|
|
|
102
|
+
def _bounded(value, limit: int) -> str:
|
|
103
|
+
"""One printable line of at most ``limit`` characters; anything but a string reads as empty."""
|
|
104
|
+
if not isinstance(value, str):
|
|
105
|
+
return ""
|
|
106
|
+
text = " ".join("".join(c if c.isprintable() else " " for c in value).split())
|
|
107
|
+
return text if len(text) <= limit else text[: limit - 1].rstrip() + "\u2026"
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def bot_identity(home: Path) -> dict:
|
|
111
|
+
"""The presentation-only Bot Mode identity from ``profile.yaml`` and ``assets/``; never the raw ``ui_meta``."""
|
|
112
|
+
meta = _yaml_load(home / "profile.yaml") or {}
|
|
113
|
+
ui_meta = meta.get("ui_meta") if isinstance(meta.get("ui_meta"), dict) else {}
|
|
114
|
+
bots = ui_meta.get("hermes-bots") if isinstance(ui_meta.get("hermes-bots"), dict) else {}
|
|
115
|
+
return {
|
|
116
|
+
# Bot Mode's roster name (``ui_meta['hermes-bots'].title``) wins over the profile's own display name.
|
|
117
|
+
"display_name": _bounded(bots.get("title"), 80) or _bounded(meta.get("display_name"), 80),
|
|
118
|
+
"description": _bounded(meta.get("description"), 280), "has_avatar": any((home / "assets" / f"avatar.{ext}").is_file() for ext in _AVATAR_EXTS),
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def canonical_bot_chat(home: Path) -> dict | None:
|
|
123
|
+
"""The profile's canonical Bot Chat: its session titled exactly ``Bot Chat`` (Bot Mode's registry) and live
|
|
124
|
+
compression tip, read through a read-only handle. Missing, unreadable, archived, worker-owned, or an Agent
|
|
125
|
+
without Bot Mode answers None; the gateway's resurrection of accidental archives is a write and stays there."""
|
|
126
|
+
db_path = home / "state.db"
|
|
127
|
+
if not db_path.is_file():
|
|
128
|
+
return None
|
|
129
|
+
try:
|
|
130
|
+
from hermes_state import SessionDB
|
|
131
|
+
|
|
132
|
+
title = getattr(SessionDB, "CANONICAL_BOT_CHAT_TITLE", None)
|
|
133
|
+
if not title:
|
|
134
|
+
return None
|
|
135
|
+
db = SessionDB(db_path=db_path, read_only=True)
|
|
136
|
+
except Exception: # noqa: BLE001
|
|
137
|
+
return None
|
|
138
|
+
try:
|
|
139
|
+
row = db.get_session_by_title(title)
|
|
140
|
+
if not row or row.get("title") != title or row.get("archived") or str(row.get("source") or "").strip().lower() in _INTERNAL_SOURCES:
|
|
141
|
+
return None
|
|
142
|
+
session_id = str(row["id"])
|
|
143
|
+
return {"session_id": session_id, "tip_session_id": str(db.get_compression_tip(session_id) or session_id)}
|
|
144
|
+
except Exception: # noqa: BLE001
|
|
145
|
+
log.debug("canonical Bot Chat lookup failed for %s", db_path, exc_info=True)
|
|
146
|
+
return None
|
|
147
|
+
finally:
|
|
148
|
+
try:
|
|
149
|
+
db.close()
|
|
150
|
+
except Exception: # noqa: BLE001
|
|
151
|
+
pass
|
|
152
|
+
|
|
153
|
+
|
|
99
154
|
def _row(home: Path, name: str, is_default: bool) -> dict:
|
|
100
155
|
from hermes_cli.profiles import _check_gateway_running, _read_config_model
|
|
101
156
|
|
|
@@ -111,21 +166,36 @@ def _row(home: Path, name: str, is_default: bool) -> dict:
|
|
|
111
166
|
return {
|
|
112
167
|
"name": name, "path": str(home), "is_default": is_default, "gateway_running": gateway_running, "model": model, "provider": provider,
|
|
113
168
|
"has_env": (home / ".env").exists(), "visible": _visible(home), "skill_count": enabled, "enabled_skills": enabled, "total_skills": total,
|
|
169
|
+
**bot_identity(home), "canonical_session": canonical_bot_chat(home),
|
|
114
170
|
}
|
|
115
171
|
|
|
116
172
|
|
|
117
|
-
def
|
|
118
|
-
"""
|
|
173
|
+
def named_profile_homes(base_home: Path) -> list[tuple[str, Path]]:
|
|
174
|
+
"""(name, home) for the ``profiles/*`` directories named like profiles, skipping any that resolve outside the resolved
|
|
175
|
+
profiles root (a symlink leading elsewhere is never read as a profile; TAL-579). A contained symlink answers the
|
|
176
|
+
target it was checked against, so retargeting the link afterwards cannot redirect the caller."""
|
|
119
177
|
from hermes_cli.profiles import _PROFILE_ID_RE as UPSTREAM_ID_RE
|
|
120
178
|
|
|
179
|
+
profiles_root = base_home / "profiles"
|
|
180
|
+
if not profiles_root.is_dir():
|
|
181
|
+
return []
|
|
182
|
+
root = profiles_root.resolve()
|
|
183
|
+
homes = []
|
|
184
|
+
for entry in sorted(profiles_root.iterdir()):
|
|
185
|
+
if not (entry.is_dir() and UPSTREAM_ID_RE.match(entry.name)):
|
|
186
|
+
continue
|
|
187
|
+
real = entry.resolve()
|
|
188
|
+
if real.is_relative_to(root):
|
|
189
|
+
homes.append((entry.name, real if entry.is_symlink() else entry))
|
|
190
|
+
return homes
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def list_profiles(base_home: Path) -> list[dict]:
|
|
194
|
+
"""Profile rows for the base Hermes home: the root profile plus ``profiles/*``."""
|
|
121
195
|
rows = []
|
|
122
196
|
if base_home.is_dir():
|
|
123
197
|
rows.append(_row(base_home, "default", True))
|
|
124
|
-
|
|
125
|
-
if profiles_root.is_dir():
|
|
126
|
-
for entry in sorted(profiles_root.iterdir()):
|
|
127
|
-
if entry.is_dir() and UPSTREAM_ID_RE.match(entry.name):
|
|
128
|
-
rows.append(_row(entry, entry.name, False))
|
|
198
|
+
rows.extend(_row(home, name, False) for name, home in named_profile_homes(base_home))
|
|
129
199
|
return rows
|
|
130
200
|
|
|
131
201
|
|
|
@@ -97,8 +97,10 @@ def _user_plugin_profiles() -> dict[int, tuple[Any, Path]]:
|
|
|
97
97
|
def _load_plugin(root: Path) -> None:
|
|
98
98
|
"""Load the scoped profile's installed plugin that the process-wide discovery (run under the launch profile) skipped.
|
|
99
99
|
|
|
100
|
-
Uses the Agent's own loader, as ``hermes plugins dev`` does, once per directory.
|
|
101
|
-
that is already registered, so one profile's plugin cannot
|
|
100
|
+
Uses the Agent's own loader, as ``hermes plugins dev`` does, once per directory. On an Agent with one process-wide
|
|
101
|
+
provider registry, a plugin never displaces a provider that is already registered, so one profile's plugin cannot
|
|
102
|
+
change what another profile's provider id runs. An Agent with per-home provider layers registers it in this home's
|
|
103
|
+
layer instead, so the same id is that profile's own provider.
|
|
102
104
|
"""
|
|
103
105
|
import providers
|
|
104
106
|
|
|
@@ -121,12 +123,12 @@ def _load_plugin(root: Path) -> None:
|
|
|
121
123
|
def installed_plugin_profiles() -> list[tuple[str, Any]]:
|
|
122
124
|
"""``(manifest name, profile | None)`` for each enabled model-provider plugin installed in the scoped home.
|
|
123
125
|
|
|
124
|
-
Bundled providers are built-ins, not plugins.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
name. ponytail: ownership is read from the plugin module's globals, so a plugin that registers an unbound
|
|
129
|
-
profile also reads as not loaded; track registrations in the Agent if one ever does.
|
|
126
|
+
Bundled providers are built-ins, not plugins. A profile belongs to this home only when the plugin module that
|
|
127
|
+
registered it lives in one of this home's plugin directories; a plugin the launch discovery skipped is loaded here.
|
|
128
|
+
``None`` means the plugin could not be loaded: it failed to import, another plugin took its provider id (only on an
|
|
129
|
+
Agent with one process-wide provider registry rather than per-home layers), or another profile's plugin has the same
|
|
130
|
+
directory name. ponytail: ownership is read from the plugin module's globals, so a plugin that registers an unbound
|
|
131
|
+
inline profile also reads as not loaded; track registrations in the Agent if one ever does.
|
|
130
132
|
"""
|
|
131
133
|
try:
|
|
132
134
|
from hermes_cli.plugins_discovery import _get_disabled_plugins, _get_enabled_plugins, collect_directory_manifests, gate_manifest
|
|
@@ -168,8 +170,25 @@ def _other_profile_plugin(provider_id: str) -> bool:
|
|
|
168
170
|
return not any(p is profile for _name, p in installed_plugin_profiles())
|
|
169
171
|
|
|
170
172
|
|
|
173
|
+
def _agent_knows_provider(name: str) -> bool:
|
|
174
|
+
"""Whether the Agent's auth layer can resolve this provider id at all.
|
|
175
|
+
|
|
176
|
+
Agent 0.21.3 resolves providers from a ``PROVIDER_REGISTRY`` snapshot of its import-time plugin discovery, so a
|
|
177
|
+
named profile's plugin loaded here on demand is unknown to it and cannot run. Registering it there would expose it
|
|
178
|
+
to every profile, so it reads as unavailable instead. Newer Agents look providers up live (``_registry_lookup``).
|
|
179
|
+
"""
|
|
180
|
+
try:
|
|
181
|
+
import hermes_cli.auth as auth
|
|
182
|
+
except Exception: # noqa: BLE001 - auth_status reports an unavailable auth layer
|
|
183
|
+
return True
|
|
184
|
+
registry = getattr(auth, "PROVIDER_REGISTRY", None)
|
|
185
|
+
return hasattr(auth, "_registry_lookup") or not isinstance(registry, dict) or name.strip().lower() in registry
|
|
186
|
+
|
|
187
|
+
|
|
171
188
|
def _setup_state(profile: Any) -> str:
|
|
172
189
|
"""The Agent's own setup verdict for a plugin provider, without spawning its CLI or reading its credentials."""
|
|
190
|
+
if not _agent_knows_provider(str(profile.name)):
|
|
191
|
+
return "unavailable"
|
|
173
192
|
try:
|
|
174
193
|
status = auth_status(profile.name)
|
|
175
194
|
except RpcError:
|
|
@@ -95,7 +95,8 @@ def sync_session_usage(home: Path, session_id: str, *, input_tokens=0, output_to
|
|
|
95
95
|
_close(db)
|
|
96
96
|
|
|
97
97
|
|
|
98
|
-
def sync_session_title(home: Path, session_id: str, title: str) -> bool:
|
|
98
|
+
def sync_session_title(home: Path, session_id: str, title: str, *, manual: bool = False) -> bool:
|
|
99
|
+
"""A manual (user-requested) title lands with ``user`` provenance; an auto title is ``llm`` and never replaces a user name."""
|
|
99
100
|
if not title:
|
|
100
101
|
return False
|
|
101
102
|
db = _session_db(home)
|
|
@@ -104,15 +105,38 @@ def sync_session_title(home: Path, session_id: str, title: str) -> bool:
|
|
|
104
105
|
try:
|
|
105
106
|
db.ensure_session(session_id=session_id, source="webui")
|
|
106
107
|
source = getattr(db, "TITLE_SOURCE_LLM", "llm")
|
|
108
|
+
|
|
109
|
+
def write(value: str) -> None:
|
|
110
|
+
if manual:
|
|
111
|
+
db.set_session_title(session_id, value)
|
|
112
|
+
else:
|
|
113
|
+
db.set_auto_title(session_id, value, source=source)
|
|
114
|
+
|
|
107
115
|
try:
|
|
108
|
-
|
|
116
|
+
write(title)
|
|
109
117
|
except ValueError:
|
|
110
118
|
alt = db.get_next_title_in_lineage(title)
|
|
111
119
|
if alt and alt != title:
|
|
112
|
-
|
|
120
|
+
write(alt)
|
|
121
|
+
return True
|
|
122
|
+
except Exception: # noqa: BLE001
|
|
123
|
+
log.debug("Failed to sync session title", exc_info=True)
|
|
124
|
+
return False
|
|
125
|
+
finally:
|
|
126
|
+
_close(db)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def append_message(home: Path, session_id: str, *, role: str, content: str, tool_name=None, timestamp=None) -> bool:
|
|
130
|
+
"""TAL-258 (predecessor ``_persist_handoff_summary_to_state_db``): one row through ``SessionDB.append_message``,
|
|
131
|
+
which also bumps the session's ``message_count``. False without a state.db or when the write fails."""
|
|
132
|
+
db = _session_db(home)
|
|
133
|
+
if not db:
|
|
134
|
+
return False
|
|
135
|
+
try:
|
|
136
|
+
db.append_message(session_id, role, content=content, tool_name=tool_name or None, timestamp=timestamp)
|
|
113
137
|
return True
|
|
114
138
|
except Exception: # noqa: BLE001
|
|
115
|
-
log.
|
|
139
|
+
log.warning("Failed to append a %s row to state.db for %s", role, session_id, exc_info=True)
|
|
116
140
|
return False
|
|
117
141
|
finally:
|
|
118
142
|
_close(db)
|
|
@@ -408,7 +432,17 @@ def register(registry) -> None:
|
|
|
408
432
|
|
|
409
433
|
@registry.method("state_db.sync_title")
|
|
410
434
|
def title(ctx: CallContext, params: dict) -> dict:
|
|
411
|
-
return {"ok": sync_session_title(profile_home_param(params), _session_id(params), str(params.get("title") or ""))}
|
|
435
|
+
return {"ok": sync_session_title(profile_home_param(params), _session_id(params), str(params.get("title") or ""), manual=params.get("manual") is True)}
|
|
436
|
+
|
|
437
|
+
@registry.method("state_db.append_message")
|
|
438
|
+
def append(ctx: CallContext, params: dict) -> dict:
|
|
439
|
+
role = str(params.get("role") or "").strip()
|
|
440
|
+
if not role:
|
|
441
|
+
raise InvalidParams("role is required")
|
|
442
|
+
content = params.get("content")
|
|
443
|
+
if not isinstance(content, str):
|
|
444
|
+
raise InvalidParams("content must be a string")
|
|
445
|
+
return {"ok": append_message(profile_home_param(params), _session_id(params), role=role, content=content, tool_name=params.get("tool_name"), timestamp=params.get("timestamp"))}
|
|
412
446
|
|
|
413
447
|
@registry.method("state_db.delete_cli_session")
|
|
414
448
|
def delete(ctx: CallContext, params: dict) -> dict:
|