@maudecode/talaria-web 1.19.0 → 1.20.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/automation-raw.js +61 -6
- package/dist/api/automation-raw.js.map +1 -1
- package/dist/api/automation-router.d.ts +8 -6
- package/dist/api/automation-router.js +7 -1
- package/dist/api/automation-router.js.map +1 -1
- package/dist/api/chat-router.d.ts +258 -33
- package/dist/api/chat-router.js +60 -25
- package/dist/api/chat-router.js.map +1 -1
- package/dist/api/git-router.d.ts +4 -0
- package/dist/api/git-router.js +1 -1
- package/dist/api/git-router.js.map +1 -1
- package/dist/api/raw-routes.js +13 -1
- package/dist/api/raw-routes.js.map +1 -1
- package/dist/api/router.d.ts +2 -2
- package/dist/api/session-visibility.js +2 -2
- package/dist/api/session-visibility.js.map +1 -1
- package/dist/api/sessions-router.d.ts +787 -107
- package/dist/api/sessions-router.js +16 -4
- package/dist/api/sessions-router.js.map +1 -1
- package/dist/api/settings-router.d.ts +34 -13
- package/dist/api/settings-router.js +54 -17
- package/dist/api/settings-router.js.map +1 -1
- package/dist/api/sse-routes.js +41 -19
- package/dist/api/sse-routes.js.map +1 -1
- package/dist/api/tools-router.d.ts +314 -32
- package/dist/api/tools-router.js +135 -38
- package/dist/api/tools-router.js.map +1 -1
- package/dist/app.js +15 -11
- package/dist/app.js.map +1 -1
- package/dist/auth/crypto.d.ts +3 -0
- package/dist/auth/crypto.js +18 -0
- package/dist/auth/crypto.js.map +1 -1
- package/dist/auth/gate.js +5 -1
- package/dist/auth/gate.js.map +1 -1
- package/dist/cli/ctl.js +6 -1
- package/dist/cli/ctl.js.map +1 -1
- package/dist/cli/main.js +23 -18
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/supervise.d.ts +22 -0
- package/dist/cli/supervise.js +80 -2
- package/dist/cli/supervise.js.map +1 -1
- package/dist/config/agent-config.d.ts +3 -0
- package/dist/config/agent-config.js +36 -9
- package/dist/config/agent-config.js.map +1 -1
- package/dist/fs/atomic.d.ts +3 -0
- package/dist/fs/atomic.js +19 -5
- package/dist/fs/atomic.js.map +1 -1
- package/dist/fs/stat.d.ts +6 -0
- package/dist/fs/stat.js +18 -2
- package/dist/fs/stat.js.map +1 -1
- package/dist/http/context.d.ts +14 -0
- package/dist/http/context.js +48 -5
- package/dist/http/context.js.map +1 -1
- package/dist/profiles/profiles.d.ts +2 -0
- package/dist/profiles/profiles.js +1 -0
- package/dist/profiles/profiles.js.map +1 -1
- package/dist/providers/catalog.d.ts +11 -6
- package/dist/providers/catalog.js +76 -24
- package/dist/providers/catalog.js.map +1 -1
- package/dist/redact.js +38 -14
- package/dist/redact.js.map +1 -1
- package/dist/release.js +5 -3
- package/dist/release.js.map +1 -1
- package/dist/runtime.js +85 -21
- package/dist/runtime.js.map +1 -1
- package/dist/server.js +24 -21
- package/dist/server.js.map +1 -1
- package/dist/sessions/anchor.d.ts +2 -1
- package/dist/sessions/anchor.js +5 -1
- package/dist/sessions/anchor.js.map +1 -1
- package/dist/sessions/background-tasks.d.ts +23 -3
- package/dist/sessions/background-tasks.js +74 -4
- package/dist/sessions/background-tasks.js.map +1 -1
- package/dist/sessions/cli-sessions.d.ts +9 -2
- package/dist/sessions/cli-sessions.js +38 -57
- package/dist/sessions/cli-sessions.js.map +1 -1
- package/dist/sessions/completions.d.ts +8 -0
- package/dist/sessions/completions.js +55 -25
- package/dist/sessions/completions.js.map +1 -1
- package/dist/sessions/export.d.ts +2 -0
- package/dist/sessions/export.js +6 -2
- package/dist/sessions/export.js.map +1 -1
- package/dist/sessions/gateway-sessions.d.ts +44 -4
- package/dist/sessions/gateway-sessions.js +626 -90
- package/dist/sessions/gateway-sessions.js.map +1 -1
- package/dist/sessions/gateway-watcher.d.ts +10 -11
- package/dist/sessions/gateway-watcher.js +25 -21
- package/dist/sessions/gateway-watcher.js.map +1 -1
- package/dist/sessions/list.d.ts +9 -2
- package/dist/sessions/list.js +38 -26
- package/dist/sessions/list.js.map +1 -1
- package/dist/sessions/merge.d.ts +11 -5
- package/dist/sessions/merge.js +59 -17
- package/dist/sessions/merge.js.map +1 -1
- package/dist/sessions/prefix-index.d.ts +7 -4
- package/dist/sessions/prefix-index.js +21 -10
- package/dist/sessions/prefix-index.js.map +1 -1
- package/dist/sessions/recovery.d.ts +1 -1
- package/dist/sessions/recovery.js +4 -3
- package/dist/sessions/recovery.js.map +1 -1
- package/dist/sessions/service.d.ts +107 -37
- package/dist/sessions/service.js +490 -156
- package/dist/sessions/service.js.map +1 -1
- package/dist/sessions/session.d.ts +2 -0
- package/dist/sessions/session.js +4 -0
- package/dist/sessions/session.js.map +1 -1
- package/dist/sessions/state-db-worker.js +22 -5
- package/dist/sessions/state-db-worker.js.map +1 -1
- package/dist/sessions/state-db.d.ts +106 -15
- package/dist/sessions/state-db.js +651 -236
- package/dist/sessions/state-db.js.map +1 -1
- package/dist/sessions/state-saved.d.ts +1 -1
- package/dist/sessions/state-saved.js +35 -11
- package/dist/sessions/state-saved.js.map +1 -1
- package/dist/sessions/storage/backend.d.ts +10 -1
- package/dist/sessions/storage/backend.js.map +1 -1
- package/dist/sessions/storage/conformance.js +1 -1
- package/dist/sessions/storage/conformance.js.map +1 -1
- package/dist/sessions/storage/json-documents.d.ts +2 -0
- package/dist/sessions/storage/json-documents.js +70 -26
- package/dist/sessions/storage/json-documents.js.map +1 -1
- package/dist/sessions/storage/json-files.d.ts +13 -2
- package/dist/sessions/storage/json-files.js +126 -6
- package/dist/sessions/storage/json-files.js.map +1 -1
- package/dist/sessions/storage/json-journal.d.ts +6 -7
- package/dist/sessions/storage/json-journal.js +143 -73
- package/dist/sessions/storage/json-journal.js.map +1 -1
- package/dist/sessions/storage/json.d.ts +15 -1
- package/dist/sessions/storage/json.js +125 -33
- package/dist/sessions/storage/json.js.map +1 -1
- package/dist/sessions/store.d.ts +5 -1
- package/dist/sessions/store.js +67 -27
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/streams.d.ts +8 -7
- package/dist/sessions/streams.js +49 -14
- package/dist/sessions/streams.js.map +1 -1
- package/dist/sessions/tool-display.d.ts +3 -1
- package/dist/sessions/tool-display.js +6 -0
- package/dist/sessions/tool-display.js.map +1 -1
- package/dist/sessions/turn-context.js +6 -2
- package/dist/sessions/turn-context.js.map +1 -1
- package/dist/sessions/turn.d.ts +23 -5
- package/dist/sessions/turn.js +168 -78
- package/dist/sessions/turn.js.map +1 -1
- package/dist/sidecar/client.d.ts +24 -1
- package/dist/sidecar/client.js +118 -38
- package/dist/sidecar/client.js.map +1 -1
- package/dist/sidecar/fake.d.ts +6 -2
- package/dist/sidecar/fake.js +15 -2
- package/dist/sidecar/fake.js.map +1 -1
- package/dist/spa.d.ts +9 -4
- package/dist/spa.js +36 -12
- package/dist/spa.js.map +1 -1
- package/dist/startup-timing.d.ts +66 -0
- package/dist/startup-timing.js +129 -0
- package/dist/startup-timing.js.map +1 -0
- package/dist/test/fs-handles.d.ts +11 -0
- package/dist/test/fs-handles.js +31 -0
- package/dist/test/fs-handles.js.map +1 -0
- package/dist/tools/connectors.d.ts +1 -1
- package/dist/tools/connectors.js +2 -2
- package/dist/tools/connectors.js.map +1 -1
- package/dist/tools/crons.d.ts +8 -4
- package/dist/tools/crons.js +25 -18
- package/dist/tools/crons.js.map +1 -1
- package/dist/tools/dashboard-plugins.d.ts +29 -0
- package/dist/tools/dashboard-plugins.js +132 -0
- package/dist/tools/dashboard-plugins.js.map +1 -1
- package/dist/tools/gateway-restart.d.ts +25 -5
- package/dist/tools/gateway-restart.js +103 -23
- package/dist/tools/gateway-restart.js.map +1 -1
- package/dist/tools/health.d.ts +13 -1
- package/dist/tools/health.js +80 -22
- package/dist/tools/health.js.map +1 -1
- package/dist/tools/insights.d.ts +1 -1
- package/dist/tools/insights.js +2 -2
- package/dist/tools/insights.js.map +1 -1
- package/dist/tools/mcp-health.js +74 -24
- package/dist/tools/mcp-health.js.map +1 -1
- package/dist/tools/mcp.js +19 -1
- package/dist/tools/mcp.js.map +1 -1
- package/dist/tools/plugin-oauth.d.ts +14 -0
- package/dist/tools/plugin-oauth.js +53 -0
- package/dist/tools/plugin-oauth.js.map +1 -0
- package/dist/tools/prune-advice.d.ts +50 -0
- package/dist/tools/prune-advice.js +114 -0
- package/dist/tools/prune-advice.js.map +1 -0
- package/dist/tools/skills.d.ts +5 -2
- package/dist/tools/skills.js +9 -6
- package/dist/tools/skills.js.map +1 -1
- package/dist/tools/update-notifications.d.ts +73 -7
- package/dist/tools/update-notifications.js +277 -43
- package/dist/tools/update-notifications.js.map +1 -1
- package/dist/tools/updates.d.ts +8 -1
- package/dist/tools/updates.js +51 -17
- package/dist/tools/updates.js.map +1 -1
- package/dist/workspace/media.js +69 -20
- package/dist/workspace/media.js.map +1 -1
- package/dist/workspace/preview.js +4 -14
- package/dist/workspace/preview.js.map +1 -1
- package/dist/workspace/rollback.d.ts +6 -2
- package/dist/workspace/rollback.js +17 -7
- package/dist/workspace/rollback.js.map +1 -1
- package/package.json +3 -3
- package/sidecar/agent_dependency.json +4 -4
- package/sidecar/agent_support.json +17 -14
- package/sidecar/scripts/suite_runner.py +183 -27
- package/sidecar/scripts/test.sh +5 -7
- package/sidecar/talaria_sidecar/__init__.py +1 -1
- package/sidecar/talaria_sidecar/__main__.py +10 -3
- package/sidecar/talaria_sidecar/agent_mutation.py +159 -23
- package/sidecar/talaria_sidecar/agent_services.py +74 -11
- package/sidecar/talaria_sidecar/context_detail.py +59 -0
- package/sidecar/talaria_sidecar/home.py +97 -20
- package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
- package/sidecar/talaria_sidecar/methods/aux.py +80 -18
- package/sidecar/talaria_sidecar/methods/chat.py +571 -106
- package/sidecar/talaria_sidecar/methods/checkpoints.py +29 -4
- package/sidecar/talaria_sidecar/methods/commands.py +277 -16
- package/sidecar/talaria_sidecar/methods/config.py +73 -3
- package/sidecar/talaria_sidecar/methods/connectors.py +45 -13
- package/sidecar/talaria_sidecar/methods/cron.py +8 -2
- package/sidecar/talaria_sidecar/methods/dashboard_plugins.py +301 -0
- package/sidecar/talaria_sidecar/methods/gateway.py +24 -4
- package/sidecar/talaria_sidecar/methods/gateway_sessions.py +211 -27
- package/sidecar/talaria_sidecar/methods/kanban.py +2 -3
- package/sidecar/talaria_sidecar/methods/oauth.py +139 -11
- package/sidecar/talaria_sidecar/methods/plugin_catalog.py +198 -0
- package/sidecar/talaria_sidecar/methods/plugin_oauth.py +234 -0
- package/sidecar/talaria_sidecar/methods/process.py +70 -6
- package/sidecar/talaria_sidecar/methods/profiles.py +4 -3
- package/sidecar/talaria_sidecar/methods/runtime.py +2 -0
- package/sidecar/talaria_sidecar/methods/skills.py +22 -7
- package/sidecar/talaria_sidecar/methods/state_db.py +128 -42
- package/sidecar/talaria_sidecar/methods/toolsets.py +4 -2
- package/sidecar/talaria_sidecar/methods/worktree.py +121 -2
- package/sidecar/talaria_sidecar/rpc.py +25 -3
- package/sidecar/talaria_sidecar/runtime.py +42 -3
- package/sidecar/talaria_sidecar/speed.py +13 -1
- package/sidecar/talaria_sidecar/startup_timing.py +119 -0
- package/static/dist/FILES.txt +241 -213
- package/static/dist/THIRD_PARTY_LICENSES.md +6 -22
- package/static/dist/assets/AppShell-3isgvoro.js +8 -0
- package/static/dist/assets/Button-f4n6dapf.js +1 -0
- package/static/dist/assets/ChatPage-k7b6xfax.js +4 -0
- package/static/dist/assets/ConnectTargets-juqvt5lm.js +1 -0
- package/static/dist/assets/Dialog-hta5utr3.js +1 -0
- package/static/dist/assets/ExtensionRoute-zrhjn0wm.js +1 -0
- package/static/dist/assets/Field-b6ez48oz.js +1 -0
- package/static/dist/assets/HubRoute-iwvso2tz.js +2 -0
- package/static/dist/assets/LinkCheckContext-ix6zqqkk.js +1 -0
- package/static/dist/assets/Markdown-jdeth7zq.js +2 -0
- package/static/dist/assets/MarkdownRenderer-fbswsgld.js +302 -0
- package/static/dist/assets/MarkdownRenderer-i9o6nr38.css +1 -0
- package/static/dist/assets/OAuthSignIn-bpd8hom3.js +1 -0
- package/static/dist/assets/ResourceSwitch-odi6dy2o.js +1 -0
- package/static/dist/assets/RightPanel-eu6enlo6.js +1 -0
- package/static/dist/assets/Select-kr07928w.js +1 -0
- package/static/dist/assets/SessionListPanel-etb2i54l.js +1 -0
- package/static/dist/assets/SettingsLayout-eu3xxp23.js +1 -0
- package/static/dist/assets/States-icab19yg.js +1 -0
- package/static/dist/assets/TerminalPanel-hngmsr8b.js +16 -0
- package/static/dist/assets/Toaster-nb2uzjw1.js +1 -0
- package/static/dist/assets/_app-ga1moszl.js +1 -0
- package/static/dist/assets/_app.ext._extensionId-na62087t.js +1 -0
- package/static/dist/assets/_app.index-jypgdqoi.js +1 -0
- package/static/dist/assets/_app.insights-kabejdtm.js +1 -0
- package/static/dist/assets/_app.kanban-m19kfcmx.js +1 -0
- package/static/dist/assets/_app.logs-iw4m96sz.js +1 -0
- package/static/dist/assets/_app.memory-jq5frwcg.js +1 -0
- package/static/dist/assets/_app.profiles-kwa3m7rp.js +1 -0
- package/static/dist/assets/_app.session._sessionId-o5lyeipj.js +1 -0
- package/static/dist/assets/_app.settings-ckl8hdll.js +1 -0
- package/static/dist/assets/_app.settings._section-dtdjk98a.js +3 -0
- package/static/dist/assets/_app.skills-n016rihd.js +1 -0
- package/static/dist/assets/_app.tasks-n8zuqsqe.js +1 -0
- package/static/dist/assets/_app.todos-btew5v2s.js +1 -0
- package/static/dist/assets/_app.workspaces-lnfk95ch.js +1 -0
- package/static/dist/assets/abnfDiagram-VCTEODGH-dttheegb.js +1 -0
- package/static/dist/assets/{angular-html-nu6l31w3.js → angular-html-l28bd5fo.js} +1 -1
- package/static/dist/assets/{angular-ts-fbx4f8mm.js → angular-ts-ffzx1vj0.js} +1 -1
- package/static/dist/assets/{apl-bx2xr7dh.js → apl-b7h7mmy4.js} +1 -1
- package/static/dist/assets/{arc-7popfhbk.js → arc-fk1b21lm.js} +1 -1
- package/static/dist/assets/architecture-7GRP2DOG-ic435919.js +1 -0
- package/static/dist/assets/architectureDiagram-5GKGNRK7-8r3glgiq.js +36 -0
- package/static/dist/assets/archive-il7c6053.js +1 -0
- package/static/dist/assets/{astro-g77i97cb.js → astro-k117fy0t.js} +1 -1
- package/static/dist/assets/back-gu3nu279.js +1 -0
- package/static/dist/assets/{blade-boo0u92y.js → blade-npefsmn8.js} +1 -1
- package/static/dist/assets/{blockDiagram-I7D4REHJ-joedghi3.js → blockDiagram-I7D4REHJ-jenyos6c.js} +4 -4
- package/static/dist/assets/{c-cat0w3sc.js → c-2umg6kbn.js} +1 -1
- package/static/dist/assets/{c4Diagram-7LVT6UL2-hyrjrohe.js → c4Diagram-7LVT6UL2-nzlx7yc4.js} +1 -1
- package/static/dist/assets/cancel-bsnfo36u.js +1 -0
- package/static/dist/assets/channel-dsbtk01y.js +1 -0
- package/static/dist/assets/{chunk-4HAMMTFA-koouqbut.js → chunk-4HAMMTFA-g0cru63b.js} +20 -20
- package/static/dist/assets/{chunk-75Z2AOVW-bl4bhdnt.js → chunk-75Z2AOVW-7rkswkjw.js} +1 -1
- package/static/dist/assets/chunk-DU6HZSFF-fn73jzdo.js +128 -0
- package/static/dist/assets/{chunk-F27PBJKO-d8xxi0zi.js → chunk-F27PBJKO-oey8e72m.js} +1 -1
- package/static/dist/assets/{chunk-FOHPRMQF-opuflp73.js → chunk-FOHPRMQF-ipewusgj.js} +8 -8
- package/static/dist/assets/{chunk-GMAD6QVW-jro0hl73.js → chunk-GMAD6QVW-l2a32wiy.js} +6 -6
- package/static/dist/assets/{chunk-GVQU2GXP-djn6gbyu.js → chunk-GVQU2GXP-o9dwzh8h.js} +1 -1
- package/static/dist/assets/{chunk-IMKFNOWR-vw4p5p7o.js → chunk-IMKFNOWR-eblwj20b.js} +5 -5
- package/static/dist/assets/{chunk-L3NEJ4N5-n1hj77q8.js → chunk-L3NEJ4N5-o2mufspb.js} +1 -1
- package/static/dist/assets/chunk-OSK3NFVY-ijp9pw29.js +10 -0
- package/static/dist/assets/{chunk-P2QGCYS3-onf1hz6c.js → chunk-P2QGCYS3-hs9hqwl4.js} +1 -1
- package/static/dist/assets/{chunk-POPQ4Y6H-m8sqn7cs.js → chunk-POPQ4Y6H-bp9rsqez.js} +1 -1
- package/static/dist/assets/{chunk-PWAF6VOD-ek4smc8x.js → chunk-PWAF6VOD-brkprov6.js} +1 -1
- package/static/dist/assets/{chunk-SHT3W25Y-g9gfvefo.js → chunk-SHT3W25Y-gmpv0pxz.js} +13 -13
- package/static/dist/assets/{chunk-SVP7TREG-f9nfv2cu.js → chunk-SVP7TREG-oldm08fu.js} +4 -4
- package/static/dist/assets/{chunk-TICWLB2K-bnk3m1mq.js → chunk-TICWLB2K-ooqilfv7.js} +6 -6
- package/static/dist/assets/{chunk-XXDRQBXY-n40pf9vy.js → chunk-XXDRQBXY-jkmnqu65.js} +1 -1
- package/static/dist/assets/classDiagram-ZZMXUADV-fvgcv24g.js +1 -0
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-fvgcv24g.js +1 -0
- package/static/dist/assets/{cobol-go622aep.js → cobol-ssf9hkh7.js} +1 -1
- package/static/dist/assets/{coffee-jwo1xjhq.js → coffee-lsi9fwae.js} +1 -1
- package/static/dist/assets/copied-bz3gkazh.js +1 -0
- package/static/dist/assets/copy-jo006coq.js +1 -0
- package/static/dist/assets/cose-bilkent-JH36ORCC-layy2r27.js +1 -0
- package/static/dist/assets/{cpp-nre7j0kq.js → cpp-lfrh4y55.js} +1 -1
- package/static/dist/assets/{crystal-lviso2je.js → crystal-efh4pte7.js} +1 -1
- package/static/dist/assets/{css-ywz9j4hb.js → css-i82qdo89.js} +1 -1
- package/static/dist/assets/cynefin-OW5HDTMX-d47op7qs.js +1 -0
- package/static/dist/assets/cynefinDiagram-5FMLGOSQ-doa7594y.js +62 -0
- package/static/dist/assets/cytoscape.esm-fjn2algp.js +321 -0
- package/static/dist/assets/dagre-GXQ25YYZ-os768dp4.js +4 -0
- package/static/dist/assets/dagre-o5jmjz46.js +1 -0
- package/static/dist/assets/{diagram-S7CK7UJ4-m6aeoe7c.js → diagram-S7CK7UJ4-fyi81nxd.js} +4 -4
- package/static/dist/assets/diagram-UQ7AKVKN-j3exggaq.js +41 -0
- package/static/dist/assets/{diagram-VSXAHHWV-hbawi7hn.js → diagram-VSXAHHWV-omo1en2l.js} +1 -1
- package/static/dist/assets/diagram-VX7I27RA-oro2cyu0.js +24 -0
- package/static/dist/assets/diagram-Z3DM3KII-byt9vdb7.js +24 -0
- package/static/dist/assets/dist-crc46di9.js +7 -0
- package/static/dist/assets/dist-etpoyscq.js +44 -0
- package/static/dist/assets/{dist-c4c2m3kv.js → dist-nb670eu6.js} +1 -1
- package/static/dist/assets/ebnfDiagram-PWID7BFC-ee3haugh.js +1 -0
- package/static/dist/assets/{edge-dvv35byi.js → edge-lrob635n.js} +1 -1
- package/static/dist/assets/{elixir-i2f1ki1g.js → elixir-kdpxeq93.js} +1 -1
- package/static/dist/assets/{elm-cpepyjm4.js → elm-c8irj9uh.js} +1 -1
- package/static/dist/assets/erDiagram-RLTQ6QDP-d1e93yff.js +99 -0
- package/static/dist/assets/{erb-idlyfheg.js → erb-flwsv146.js} +1 -1
- package/static/dist/assets/eventmodeling-NTZA5JFV-i1ihb40c.js +1 -0
- package/static/dist/assets/flowDiagram-HODETNUW-jahfpiod.js +1 -0
- package/static/dist/assets/folder-plus-ivoglq98.js +1 -0
- package/static/dist/assets/{ganttDiagram-EL5Y4UJY-ma6jxj5i.js → ganttDiagram-EL5Y4UJY-m7g0jud4.js} +1 -1
- package/static/dist/assets/{git-rebase-d9lpfpbn.js → git-rebase-bli19aoq.js} +1 -1
- package/static/dist/assets/gitGraph-4MIJSDKK-hzdj07yy.js +1 -0
- package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-hzltvsod.js → gitGraphDiagram-WWUBYQGX-dm9agbna.js} +1 -1
- package/static/dist/assets/{glimmer-js-39i0urfb.js → glimmer-js-d4icg38p.js} +1 -1
- package/static/dist/assets/{glimmer-ts-gdtd6n73.js → glimmer-ts-bk75772u.js} +1 -1
- package/static/dist/assets/{glsl-o1djm5pp.js → glsl-lqormp2b.js} +1 -1
- package/static/dist/assets/{graphql-kj6rz55t.js → graphql-hw6s5qzd.js} +1 -1
- package/static/dist/assets/{hack-pe1qqr2i.js → hack-ozrv6l4d.js} +1 -1
- package/static/dist/assets/{haml-cknnbcqs.js → haml-gytfosdt.js} +1 -1
- package/static/dist/assets/{handlebars-hwq64orh.js → handlebars-b6g7jffd.js} +1 -1
- package/static/dist/assets/highlighted-body-KPVGNVTW-mmgz2czu.js +1 -0
- package/static/dist/assets/host-fr6lni1p.js +1 -0
- package/static/dist/assets/{html-derivative-h2i2pyjc.js → html-derivative-f1tqcroo.js} +1 -1
- package/static/dist/assets/{html-ha05xtvt.js → html-on6oj6yj.js} +1 -1
- package/static/dist/assets/{http-cf94zu27.js → http-odhufsl7.js} +1 -1
- package/static/dist/assets/{hurl-lslax568.js → hurl-mu680336.js} +1 -1
- package/static/dist/assets/index-73649w7o.js +10 -0
- package/static/dist/assets/index-lhf1fsdf.css +2 -0
- package/static/dist/assets/info-A6RAGUB7-lll1dqmm.js +1 -0
- package/static/dist/assets/infoDiagram-27XIBGKW-k7esc639.js +2 -0
- package/static/dist/assets/{ishikawaDiagram-5VMMS53U-e7bnwq7h.js → ishikawaDiagram-5VMMS53U-lgr6k9wo.js} +6 -6
- package/static/dist/assets/{java-ndjf2o89.js → java-nxbe8uju.js} +1 -1
- package/static/dist/assets/{javascript-coftlwy3.js → javascript-lz3kxcyz.js} +1 -1
- package/static/dist/assets/{jinja-etei9wy7.js → jinja-l5pk84gt.js} +1 -1
- package/static/dist/assets/{jison-hyekgft1.js → jison-jdutdwyy.js} +1 -1
- package/static/dist/assets/{journeyDiagram-3NMN7TZE-etd1bi6p.js → journeyDiagram-3NMN7TZE-ndmbxuqk.js} +1 -1
- package/static/dist/assets/{json-lok0yaho.js → json-dnge4ssf.js} +1 -1
- package/static/dist/assets/{jsx-b64w0nbx.js → jsx-c3lwhkep.js} +1 -1
- package/static/dist/assets/{jsx-runtime-eu92a5pu.js → jsx-runtime-o40ukmjl.js} +1 -1
- package/static/dist/assets/{julia-jxxzz2yn.js → julia-hwd4rrcb.js} +1 -1
- package/static/dist/assets/{just-mc29iw9t.js → just-nka3oxdx.js} +1 -1
- package/static/dist/assets/{kanban-definition-UXKFOSKX-emb1g9dz.js → kanban-definition-UXKFOSKX-mjxym0jp.js} +28 -28
- package/static/dist/assets/katex-cbdd1ohw.css +1 -0
- package/static/dist/assets/katex-pbij2oo6.js +257 -0
- package/static/dist/assets/{latex-pafa03ft.js → latex-iqmr7e40.js} +1 -1
- package/static/dist/assets/lazyRouteComponent-cwb6l7xh.js +1 -0
- package/static/dist/assets/{line-h0ojzsvs.js → line-mtr5k6il.js} +1 -1
- package/static/dist/assets/linear-jlwrvvy6.js +1 -0
- package/static/dist/assets/link-iadw4ghw.js +2 -0
- package/static/dist/assets/{liquid-hz5c2pov.js → liquid-okmk9tbp.js} +1 -1
- package/static/dist/assets/locale-cs-n41hptse.js +5 -0
- package/static/dist/assets/locale-de-k7ht47ew.js +5 -0
- package/static/dist/assets/locale-es-ih078lco.js +5 -0
- package/static/dist/assets/locale-fr-ojabnf41.js +5 -0
- package/static/dist/assets/locale-it-kb96jufy.js +5 -0
- package/static/dist/assets/locale-ja-jpsp3xs1.js +7 -0
- package/static/dist/assets/locale-ko-cpzuy0n7.js +5 -0
- package/static/dist/assets/locale-pl-dbap4leu.js +5 -0
- package/static/dist/assets/locale-pt-nci7w72x.js +5 -0
- package/static/dist/assets/locale-ru-soghe4cs.js +7 -0
- package/static/dist/assets/locale-tr-ks00mu68.js +5 -0
- package/static/dist/assets/locale-vi-ij7twfm6.js +5 -0
- package/static/dist/assets/locale-zh-Hant-feakwr7a.js +5 -0
- package/static/dist/assets/locale-zh-ren0vjyv.js +7 -0
- package/static/dist/assets/login-lkhtzfcl.js +1 -0
- package/static/dist/assets/{lua-leyf8mlb.js → lua-krx4xj5h.js} +1 -1
- package/static/dist/assets/{marko-cn1fzpdv.js → marko-gbvpfehz.js} +1 -1
- package/static/dist/assets/{mdc-huhy88df.js → mdc-c21zlyi5.js} +1 -1
- package/static/dist/assets/mermaid-HWGCJPDP-jx0q5dm2.js +1 -0
- package/static/dist/assets/mermaid-parser.core-k91vdnvi.js +7 -0
- package/static/dist/assets/micromark-factory-space-fnmnpi6s.js +1 -0
- package/static/dist/assets/{mindmap-definition-YA3MSWOX-o1vathns.js → mindmap-definition-YA3MSWOX-ehztfea4.js} +28 -28
- package/static/dist/assets/{nginx-e7lvhsmn.js → nginx-f3d68ed4.js} +1 -1
- package/static/dist/assets/{nim-dun5d8mk.js → nim-dw0jcy70.js} +1 -1
- package/static/dist/assets/onboarding-f67z0a2r.js +1 -0
- package/static/dist/assets/packet-AYTQ26CC-ok2xrblz.js +1 -0
- package/static/dist/assets/pegDiagram-XKGWAZYB-ec3rpj0m.js +1 -0
- package/static/dist/assets/{perl-bj3oqtet.js → perl-3c0r45c8.js} +1 -1
- package/static/dist/assets/{php-buzp12r0.js → php-rznhidvp.js} +1 -1
- package/static/dist/assets/pie-WAS4IAKB-iwkir23u.js +1 -0
- package/static/dist/assets/pieDiagram-E7YTZNPT-fcpqpekh.js +39 -0
- package/static/dist/assets/{pug-mjad7he9.js → pug-f2le4f3y.js} +1 -1
- package/static/dist/assets/{qml-d1gdp10s.js → qml-e2wbq9cr.js} +1 -1
- package/static/dist/assets/{quadrantDiagram-AXDQQJYC-owqc4uqn.js → quadrantDiagram-AXDQQJYC-lqsync43.js} +1 -1
- package/static/dist/assets/queryKeys-ed4n2egc.js +9 -0
- package/static/dist/assets/{r-o3r9c7ao.js → r-epfzuw6q.js} +1 -1
- package/static/dist/assets/radar-RG4KPBEZ-hje4jbk6.js +1 -0
- package/static/dist/assets/railroad-74A4TZTK-gll9n6qi.js +1 -0
- package/static/dist/assets/railroad-abnf-HS5TGJTU-o6h6lpn4.js +1 -0
- package/static/dist/assets/railroad-ebnf-LZEXJU2U-juaefj6o.js +1 -0
- package/static/dist/assets/railroad-peg-WCYAUIDC-cgfhvpah.js +1 -0
- package/static/dist/assets/railroadDiagram-O6MQD6OU-lp3w7awf.js +1 -0
- package/static/dist/assets/{razor-ilc585yr.js → razor-pea1u1d0.js} +1 -1
- package/static/dist/assets/refresh-cw-iccm7a4e.js +1 -0
- package/static/dist/assets/refresh-i9ya9dvl.js +1 -0
- package/static/dist/assets/{regexp-mpjtcvwm.js → regexp-hzte0a5x.js} +1 -1
- package/static/dist/assets/registry-g7w3353u.js +1 -0
- package/static/dist/assets/requirementDiagram-BXWQKSXE-ha8eibcf.js +84 -0
- package/static/dist/assets/rolldown-runtime-bnnw9q4a.js +1 -0
- package/static/dist/assets/{rst-jrkwjdyi.js → rst-m6tugj5o.js} +1 -1
- package/static/dist/assets/{ruby-n4tjcbpz.js → ruby-l19vq3w1.js} +1 -1
- package/static/dist/assets/runtime-nlxpnegr.js +1 -0
- package/static/dist/assets/{sankeyDiagram-P5KCCOFB-iqc02emv.js → sankeyDiagram-P5KCCOFB-oxf31pr9.js} +1 -1
- package/static/dist/assets/{sas-hjo6y93k.js → sas-ktdxo397.js} +1 -1
- package/static/dist/assets/{scss-ncidjv48.js → scss-kn3oxfwf.js} +1 -1
- package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-i0v116q0.js → sequenceDiagram-WJ2MYXX4-jiq8lhyo.js} +11 -11
- package/static/dist/assets/settings_section_extensions_title-fadc7o65.js +1 -0
- package/static/dist/assets/share._token-o76ki0ly.js +2 -0
- package/static/dist/assets/{shellscript-m8whjpqd.js → shellscript-l100jtrg.js} +1 -1
- package/static/dist/assets/{shellsession-i5bj6syz.js → shellsession-m5qukwow.js} +1 -1
- package/static/dist/assets/sizeCapture-INFHLROL-dk1io13w.js +1 -0
- package/static/dist/assets/sliders-horizontal-ddcs88av.js +1 -0
- package/static/dist/assets/{soy-hhh36ysq.js → soy-mexlscur.js} +1 -1
- package/static/dist/assets/{sql-j21bzcl6.js → sql-o0ho5suh.js} +1 -1
- package/static/dist/assets/src-h89s0hs4.js +1 -0
- package/static/dist/assets/{stata-bbu2onh1.js → stata-be9ck1q3.js} +1 -1
- package/static/dist/assets/stateDiagram-D77RDMKH-f3fslnon.js +1 -0
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-hlt6yphw.js +1 -0
- package/static/dist/assets/{surrealql-l5vczuco.js → surrealql-gk1xkkyy.js} +1 -1
- package/static/dist/assets/{svelte-idn422g6.js → svelte-k6ecjilf.js} +1 -1
- package/static/dist/assets/swimlanes-42K2YHIH-k1hmk7jf.js +1 -0
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-hd5t9tnt.js +8 -0
- package/static/dist/assets/{templ-e22qou3c.js → templ-gf3bftk6.js} +1 -1
- package/static/dist/assets/{tex-deqpc5ee.js → tex-d21fqv1z.js} +1 -1
- package/static/dist/assets/{timeline-definition-24CTP7MA-ol2hnvma.js → timeline-definition-24CTP7MA-n3ncfy9r.js} +8 -8
- package/static/dist/assets/toast-liparz2j.js +56 -0
- package/static/dist/assets/treeView-Q6P3EWNA-l3y2pejt.js +1 -0
- package/static/dist/assets/treemap-WGGIJYW6-jown9axh.js +1 -0
- package/static/dist/assets/{ts-tags-ctnibc27.js → ts-tags-n0g61gr6.js} +1 -1
- package/static/dist/assets/{tsx-i163n32g.js → tsx-cvv7wv6q.js} +1 -1
- package/static/dist/assets/{twig-x0fi7wm8.js → twig-n1f67204.js} +1 -1
- package/static/dist/assets/{typescript-g18c34xz.js → typescript-og7o55cx.js} +1 -1
- package/static/dist/assets/untitled-mj86r4gz.js +1 -0
- package/static/dist/assets/useDebounced-g8r0ggve.js +1 -0
- package/static/dist/assets/useForm-o4uu74mx.js +1 -0
- package/static/dist/assets/{useParams-gjc70qru.js → useParams-ftsveu84.js} +1 -1
- package/static/dist/assets/{useSearch-e6ae539u.js → useSearch-crqueqga.js} +1 -1
- package/static/dist/assets/vennDiagram-4TSXK5OY-dh59uq1c.js +34 -0
- package/static/dist/assets/{vue-mwu5l1bb.js → vue-ej4xpwqw.js} +1 -1
- package/static/dist/assets/{vue-html-nskj3du0.js → vue-html-d48xsjuo.js} +1 -1
- package/static/dist/assets/{vue-vine-cqe77smq.js → vue-vine-pev88ip6.js} +1 -1
- package/static/dist/assets/wardley-WFR3VGLG-e6d74b0j.js +1 -0
- package/static/dist/assets/wardleyDiagram-VM6X3IG4-hnofh78f.js +78 -0
- package/static/dist/assets/with-selector-nu1b5z0v.js +1 -0
- package/static/dist/assets/{xml-gau7gvrv.js → xml-ifxp6gqb.js} +1 -1
- package/static/dist/assets/{xsl-ew09zq8b.js → xsl-fug2cckc.js} +1 -1
- package/static/dist/assets/{xychartDiagram-S5SC5T6Z-aqbgvujg.js → xychartDiagram-S5SC5T6Z-bpymjelb.js} +6 -6
- package/static/dist/assets/{yaml-ofnrxtvf.js → yaml-fjmafnlo.js} +1 -1
- package/static/dist/extension-sdk.js +1 -1
- package/static/dist/index.html +1 -1
- package/static/dist/sw.js +180 -136
- package/static/dist/assets/AppShell-cey33v9s.js +0 -8
- package/static/dist/assets/ChatPage-i8wnugr7.js +0 -18
- package/static/dist/assets/ConnectTargets-hg19553x.js +0 -1
- package/static/dist/assets/ExtensionRoute-cly4apm5.js +0 -1
- package/static/dist/assets/Field-j8uqoigz.js +0 -9
- package/static/dist/assets/HubRoute-e4bgz41j.js +0 -2
- package/static/dist/assets/Markdown-lahuichm.js +0 -598
- package/static/dist/assets/Markdown-lwviulmg.css +0 -1
- package/static/dist/assets/RightPanel-lbo4i0fn.js +0 -1
- package/static/dist/assets/Select-gqatm2e0.js +0 -1
- package/static/dist/assets/SessionListPanel-mfqayfjh.js +0 -1
- package/static/dist/assets/SettingsLayout-b8980u7k.js +0 -1
- package/static/dist/assets/States-e7dkgtxf.js +0 -1
- package/static/dist/assets/Toaster-bfqqy786.js +0 -1
- package/static/dist/assets/_app-n1n6pfx1.js +0 -1
- package/static/dist/assets/_app.ext._extensionId-buxjxav3.js +0 -1
- package/static/dist/assets/_app.index-jt8zwiu2.js +0 -1
- package/static/dist/assets/_app.insights-cv76ml71.js +0 -1
- package/static/dist/assets/_app.kanban-h6ts53j7.js +0 -1
- package/static/dist/assets/_app.logs-dm873z90.js +0 -1
- package/static/dist/assets/_app.memory-glk5y6m8.js +0 -1
- package/static/dist/assets/_app.profiles-ka6zpstq.js +0 -1
- package/static/dist/assets/_app.session._sessionId-g2vzghvc.js +0 -1
- package/static/dist/assets/_app.settings-hgjdhzcd.js +0 -1
- package/static/dist/assets/_app.settings._section-jf2kmhae.js +0 -3
- package/static/dist/assets/_app.skills-cii852yq.js +0 -1
- package/static/dist/assets/_app.tasks-fp9q7atm.js +0 -1
- package/static/dist/assets/_app.todos-dw94qzue.js +0 -1
- package/static/dist/assets/_app.workspaces-inyygzze.js +0 -1
- package/static/dist/assets/abnfDiagram-VCTEODGH-gjayq3ra.js +0 -1
- package/static/dist/assets/architecture-7GRP2DOG-lp56972r.js +0 -1
- package/static/dist/assets/architectureDiagram-5GKGNRK7-nwb3xovn.js +0 -36
- package/static/dist/assets/archive-b1j004vn.js +0 -1
- package/static/dist/assets/back-g74gkozu.js +0 -1
- package/static/dist/assets/channel-es95o4y8.js +0 -1
- package/static/dist/assets/chunk-DU6HZSFF-gpedttsp.js +0 -128
- package/static/dist/assets/chunk-OSK3NFVY-oy0gwn2n.js +0 -10
- package/static/dist/assets/classDiagram-ZZMXUADV-kmihoqxh.js +0 -1
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-kmihoqxh.js +0 -1
- package/static/dist/assets/copied-bli9tbcx.js +0 -1
- package/static/dist/assets/copy-jamtd1pf.js +0 -1
- package/static/dist/assets/cose-bilkent-JH36ORCC-eclcy6ja.js +0 -1
- package/static/dist/assets/cynefin-OW5HDTMX-f8xfdd3f.js +0 -1
- package/static/dist/assets/cynefinDiagram-5FMLGOSQ-n9lb99h9.js +0 -62
- package/static/dist/assets/cytoscape.esm-bqu53u12.js +0 -321
- package/static/dist/assets/dagre-GXQ25YYZ-dgailddi.js +0 -4
- package/static/dist/assets/dagre-lkoknbjn.js +0 -1
- package/static/dist/assets/diagram-UQ7AKVKN-jall3j0x.js +0 -41
- package/static/dist/assets/diagram-VX7I27RA-hendl2tt.js +0 -24
- package/static/dist/assets/diagram-Z3DM3KII-igwc35p3.js +0 -24
- package/static/dist/assets/ebnfDiagram-PWID7BFC-c64knz08.js +0 -1
- package/static/dist/assets/erDiagram-RLTQ6QDP-brdu9lcm.js +0 -99
- package/static/dist/assets/eventmodeling-NTZA5JFV-njk1j8wl.js +0 -1
- package/static/dist/assets/flowDiagram-HODETNUW-nma6f0ga.js +0 -1
- package/static/dist/assets/folder-plus-lvt9kudb.js +0 -1
- package/static/dist/assets/gitGraph-4MIJSDKK-naa9w6xa.js +0 -1
- package/static/dist/assets/highlighted-body-KPVGNVTW-ghoavj8d.js +0 -1
- package/static/dist/assets/host-hzm8n85n.js +0 -1
- package/static/dist/assets/index-drdcaqeq.css +0 -2
- package/static/dist/assets/index-jzm844he.js +0 -11
- package/static/dist/assets/info-A6RAGUB7-mn2ut5ko.js +0 -1
- package/static/dist/assets/infoDiagram-27XIBGKW-h6kuqvsb.js +0 -2
- package/static/dist/assets/lazyRouteComponent-o816ytzl.js +0 -1
- package/static/dist/assets/linear-becwlnhk.js +0 -1
- package/static/dist/assets/link-k01ryaf9.js +0 -1
- package/static/dist/assets/login-bsae49ew.js +0 -1
- package/static/dist/assets/mermaid-HWGCJPDP-ihl34s74.js +0 -1
- package/static/dist/assets/mermaid-parser.core-bnre27u8.js +0 -7
- package/static/dist/assets/not-found-ccnnac39.js +0 -1
- package/static/dist/assets/onboarding-c65tysya.js +0 -1
- package/static/dist/assets/onboarding_model_label-gpn28uix.js +0 -1
- package/static/dist/assets/packet-AYTQ26CC-hatq7m2e.js +0 -1
- package/static/dist/assets/pegDiagram-XKGWAZYB-gwisz8jv.js +0 -1
- package/static/dist/assets/pie-WAS4IAKB-fpftf0mk.js +0 -1
- package/static/dist/assets/pieDiagram-E7YTZNPT-khm1o9a6.js +0 -39
- package/static/dist/assets/radar-RG4KPBEZ-cntfwjpq.js +0 -1
- package/static/dist/assets/railroad-74A4TZTK-a7t5eibp.js +0 -1
- package/static/dist/assets/railroad-abnf-HS5TGJTU-ol8cdktf.js +0 -1
- package/static/dist/assets/railroad-ebnf-LZEXJU2U-i8vzd429.js +0 -1
- package/static/dist/assets/railroad-peg-WCYAUIDC-nw3ulewz.js +0 -1
- package/static/dist/assets/railroadDiagram-O6MQD6OU-etuh0qa0.js +0 -1
- package/static/dist/assets/refresh-cw-mqbkv2a3.js +0 -1
- package/static/dist/assets/refresh-l29ljp76.js +0 -1
- package/static/dist/assets/requirementDiagram-BXWQKSXE-oq2ulk22.js +0 -84
- package/static/dist/assets/rolldown-runtime-b9miacsj.js +0 -1
- package/static/dist/assets/root-mbklpuv4.js +0 -1
- package/static/dist/assets/saved-ndd4t2xb.js +0 -1
- package/static/dist/assets/settings_section_extensions_title-jhfo6o8z.js +0 -1
- package/static/dist/assets/share._token-c1ri0hy1.js +0 -2
- package/static/dist/assets/sizeCapture-INFHLROL-g612l48j.js +0 -1
- package/static/dist/assets/sliders-horizontal-j3jjt9tv.js +0 -1
- package/static/dist/assets/src-hjyib4mu.js +0 -1
- package/static/dist/assets/stateDiagram-D77RDMKH-dnpt1ato.js +0 -1
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-bxjacpk6.js +0 -1
- package/static/dist/assets/swimlanes-42K2YHIH-loqbdazb.js +0 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-id62p9hz.js +0 -8
- package/static/dist/assets/toast-i3h34v6o.js +0 -64
- package/static/dist/assets/treeView-Q6P3EWNA-idrj6iox.js +0 -1
- package/static/dist/assets/treemap-WGGIJYW6-lzr49vv5.js +0 -1
- package/static/dist/assets/untitled-oaoya4qj.js +0 -1
- package/static/dist/assets/useForm-nfaxgmsk.js +0 -1
- package/static/dist/assets/vennDiagram-4TSXK5OY-bvwr9o2z.js +0 -34
- package/static/dist/assets/wardley-WFR3VGLG-31ui8tma.js +0 -1
- package/static/dist/assets/wardleyDiagram-VM6X3IG4-k5wqtx4b.js +0 -78
- /package/static/dist/assets/{ChatPage-gmu5c7lf.css → TerminalPanel-gmu5c7lf.css} +0 -0
|
@@ -12,13 +12,13 @@ from ..home import profile_home_param, scoped_home
|
|
|
12
12
|
from ..rpc import CallContext
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
def checkpoint_kwargs() -> dict:
|
|
15
|
+
def checkpoint_kwargs(cfg: dict | None = None) -> dict:
|
|
16
16
|
"""The profile's ``checkpoints`` config as ``AIAgent`` kwargs, resolved as the Agent's gateway does
|
|
17
17
|
(``gateway/run.py::_checkpoint_agent_kwargs``): the legacy ``checkpoints: true`` form, else the section over
|
|
18
18
|
``DEFAULT_CONFIG``. Only a real ``true`` turns them on, so a quoted ``"false"`` stays off."""
|
|
19
19
|
from hermes_cli.config import DEFAULT_CONFIG, load_config
|
|
20
20
|
|
|
21
|
-
section = (load_config() or {}).get("checkpoints", {})
|
|
21
|
+
section = ((load_config() or {}) if cfg is None else cfg).get("checkpoints", {})
|
|
22
22
|
if isinstance(section, bool):
|
|
23
23
|
section = {"enabled": section}
|
|
24
24
|
elif not isinstance(section, dict):
|
|
@@ -54,6 +54,19 @@ def _agent_result(result: dict) -> dict:
|
|
|
54
54
|
return result
|
|
55
55
|
|
|
56
56
|
|
|
57
|
+
def _paths(result: dict, key: str) -> list[str]:
|
|
58
|
+
return [str(f) for f in result.get(key) or []]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _has_agent_writes(manager, workspace: str) -> bool:
|
|
62
|
+
"""Whether the Agent's agent-write ledger for ``workspace`` has entries: the same file and key its safe restore reads
|
|
63
|
+
(``_safe_restore_plan``), which takes the full-restore branch when it is empty. A write only adds an entry, so a ledger
|
|
64
|
+
seen here is not empty when the restore reads it."""
|
|
65
|
+
from tools.checkpoint_manager import _load_ledger, _normalize_path, _store_path
|
|
66
|
+
|
|
67
|
+
return bool(_load_ledger(_store_path(), manager._ledger_key(str(_normalize_path(workspace)))))
|
|
68
|
+
|
|
69
|
+
|
|
57
70
|
def _owned(manager, workspace: str, checkpoint: str) -> str:
|
|
58
71
|
"""The full hash of ``checkpoint`` among ``workspace``'s own checkpoints. The store is shared by every project and the
|
|
59
72
|
Agent's diff and restore accept any commit in it, so another workspace's hash would check that tree out here."""
|
|
@@ -82,8 +95,20 @@ def register(registry) -> None:
|
|
|
82
95
|
|
|
83
96
|
@registry.method("checkpoints.restore")
|
|
84
97
|
def restore(ctx: CallContext, params: dict) -> dict:
|
|
98
|
+
"""Safe by default, as the Agent's own ``rollback.restore`` (TAL-1260): only files whose content is still what the
|
|
99
|
+
Agent last wrote are reverted, and the hand-edited ones it kept are reported. ``all`` restores every file, and only
|
|
100
|
+
then is ``restored_files`` null. Without an agent-write ledger the Agent would fall back to a full restore, so a
|
|
101
|
+
default restore is refused there instead: it cannot tell the user's edits apart."""
|
|
85
102
|
workspace, checkpoint = _param(params, "workspace"), _param(params, "checkpoint")
|
|
103
|
+
safe = params.get("all") is not True
|
|
86
104
|
with scoped_home(profile_home_param(params)):
|
|
87
105
|
manager = _manager()
|
|
88
|
-
|
|
89
|
-
|
|
106
|
+
owned = _owned(manager, workspace, checkpoint)
|
|
107
|
+
if safe and not _has_agent_writes(manager, workspace):
|
|
108
|
+
raise RpcError("The Agent has no record of the files it wrote here, so your own edits cannot be told apart. "
|
|
109
|
+
"Use Restore everything to restore every file.", condition="checkpoint_failed")
|
|
110
|
+
result = _agent_result(manager.restore(workspace, owned, safe=safe))
|
|
111
|
+
return {"restored_to": str(result.get("restored_to") or ""), "reason": str(result.get("reason") or ""),
|
|
112
|
+
"restored_files": _paths(result, "restored_files") if isinstance(result.get("restored_files"), list) else None,
|
|
113
|
+
"kept_user_edits": _paths(result, "skipped_user_edits"), "skipped_oversize": _paths(result, "skipped_oversize"),
|
|
114
|
+
"failed_deletes": _paths(result, "failed_deletes")}
|
|
@@ -3,11 +3,15 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import contextlib
|
|
6
|
+
import hashlib
|
|
6
7
|
import importlib.util
|
|
8
|
+
import json
|
|
7
9
|
import logging
|
|
8
10
|
import os
|
|
9
11
|
import pathlib
|
|
12
|
+
import secrets
|
|
10
13
|
import threading
|
|
14
|
+
import time
|
|
11
15
|
from typing import Any
|
|
12
16
|
|
|
13
17
|
from ..errors import InvalidParams, RpcError
|
|
@@ -26,13 +30,21 @@ _RUNTIME_COMMANDS = {
|
|
|
26
30
|
"codex-runtime": ("Toggle codex app-server runtime for OpenAI/Codex models", "Configuration", "[auto|codex_app_server]"),
|
|
27
31
|
"credits": ("Show your Nous credits", "Info", ""),
|
|
28
32
|
}
|
|
29
|
-
|
|
33
|
+
# Commands ``commands.exec`` runs through the Agent's registry executor (``hermes_cli.slash_exec``), so the reply is the
|
|
34
|
+
# Agent's own; an Agent whose registry lacks one neither lists nor runs it.
|
|
35
|
+
_REGISTRY_EXECUTED = frozenset({"autoprune"})
|
|
36
|
+
_ALLOWED = frozenset(_RUNTIME_COMMANDS) | _REGISTRY_EXECUTED
|
|
30
37
|
_RELOAD_MCP_LOCK = threading.Lock()
|
|
31
38
|
_RELOAD_SKILLS_LOCK = threading.Lock()
|
|
32
39
|
_CODEX_RUNTIME_LOCK = threading.Lock()
|
|
40
|
+
_MCP_DISCOVERED_HOMES: set[str] = set()
|
|
41
|
+
_MCP_DISCOVERED_LOCK = threading.Lock()
|
|
42
|
+
_PLUGIN_UPDATE_CONSENTS: dict[str, dict] = {}
|
|
43
|
+
_PLUGIN_UPDATE_CONSENT_LOCK = threading.Lock()
|
|
33
44
|
|
|
34
45
|
|
|
35
|
-
def
|
|
46
|
+
def _split_slash_command(command: str) -> tuple[str, str]:
|
|
47
|
+
"""The command name as typed (lowercase, no slash) and its raw arguments."""
|
|
36
48
|
raw = str(command or "").strip()
|
|
37
49
|
if not raw:
|
|
38
50
|
raise InvalidParams("command is required")
|
|
@@ -41,7 +53,12 @@ def parse_slash_command(command: str) -> tuple[str, str]:
|
|
|
41
53
|
base = (parts[0] if parts else "").strip().lower()
|
|
42
54
|
if not base:
|
|
43
55
|
raise InvalidParams("command is required")
|
|
44
|
-
return
|
|
56
|
+
return base, parts[1] if len(parts) > 1 else ""
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def parse_slash_command(command: str) -> tuple[str, str]:
|
|
60
|
+
base, args = _split_slash_command(command)
|
|
61
|
+
return _ALIASES.get(base, base), args
|
|
45
62
|
|
|
46
63
|
|
|
47
64
|
def _expand_plan(args: str, **_turn) -> str:
|
|
@@ -115,7 +132,7 @@ def _prompt_mode(name: str) -> str | None:
|
|
|
115
132
|
return "expand" if _prompt_expander(name) else "unsupported"
|
|
116
133
|
|
|
117
134
|
|
|
118
|
-
def expand_prompt(command: str, workspace: str, enabled_toolsets) -> dict[str, Any]:
|
|
135
|
+
def expand_prompt(command: str, workspace: str, enabled_toolsets, session_id: str = "") -> dict[str, Any]:
|
|
119
136
|
"""``/<prompt built-in> [args]`` as the turn text the Agent's own builder writes. ``name`` is the built-in's (None
|
|
120
137
|
when the command is not one) and ``message`` None when this Agent has no builder for it."""
|
|
121
138
|
try:
|
|
@@ -126,6 +143,8 @@ def expand_prompt(command: str, workspace: str, enabled_toolsets) -> dict[str, A
|
|
|
126
143
|
cmd = resolve_command(name)
|
|
127
144
|
if cmd is None or cmd.name not in _PROMPT_BUILTINS:
|
|
128
145
|
return {"name": None, "message": None}
|
|
146
|
+
# TAL-1279: as the CLI does, before it expands or refuses the built-in.
|
|
147
|
+
_fire_pre_command(command, session_id, cmd.name, workspace)
|
|
129
148
|
expander = _prompt_expander(cmd.name)
|
|
130
149
|
if expander is None:
|
|
131
150
|
return {"name": cmd.name, "message": None}
|
|
@@ -198,10 +217,26 @@ def retire_unscoped_launch_mcp_servers() -> set[str]:
|
|
|
198
217
|
|
|
199
218
|
def discover_profile_mcp_tools() -> list[str]:
|
|
200
219
|
"""Connect this profile's configured servers, retiring pre-multiplex launch connections first."""
|
|
220
|
+
from hermes_constants import get_hermes_home
|
|
201
221
|
from tools.mcp_tool_discovery import discover_mcp_tools
|
|
202
222
|
|
|
203
223
|
retire_unscoped_launch_mcp_servers()
|
|
204
|
-
|
|
224
|
+
tools = discover_mcp_tools()
|
|
225
|
+
with _MCP_DISCOVERED_LOCK:
|
|
226
|
+
_MCP_DISCOVERED_HOMES.add(os.path.realpath(get_hermes_home()))
|
|
227
|
+
return tools
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def profile_mcp_tools_discovered(home) -> bool:
|
|
231
|
+
"""A completed discovery, including an empty registry, establishes this process's profile baseline."""
|
|
232
|
+
with _MCP_DISCOVERED_LOCK:
|
|
233
|
+
return os.path.realpath(home) in _MCP_DISCOVERED_HOMES
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def forget_profile_mcp_tools(home) -> None:
|
|
237
|
+
"""A successfully deleted profile must start cold if its path is recreated."""
|
|
238
|
+
with _MCP_DISCOVERED_LOCK:
|
|
239
|
+
_MCP_DISCOVERED_HOMES.discard(os.path.realpath(home))
|
|
205
240
|
|
|
206
241
|
|
|
207
242
|
def _reload_mcp() -> str:
|
|
@@ -211,7 +246,6 @@ def _reload_mcp() -> str:
|
|
|
211
246
|
from hermes_constants import get_hermes_home
|
|
212
247
|
from tools.mcp_tool import _lock, _mcp_tool_server_names, _server_visible_in_scope, _servers
|
|
213
248
|
from tools.mcp_tool_agent import reprobe_tool_availability
|
|
214
|
-
from tools.mcp_tool_discovery import discover_mcp_tools
|
|
215
249
|
from tools.mcp_tool_lifecycle import shutdown_mcp_servers
|
|
216
250
|
from tools.mcp_tool_scope import _key_name
|
|
217
251
|
from tools.registry import registry
|
|
@@ -233,7 +267,7 @@ def _reload_mcp() -> str:
|
|
|
233
267
|
# As the gateway does: drop the cached tool availability verdicts, then hand the profile's open chats the
|
|
234
268
|
# rediscovered tools.
|
|
235
269
|
reprobe_tool_availability()
|
|
236
|
-
tools =
|
|
270
|
+
tools = discover_profile_mcp_tools() or []
|
|
237
271
|
refresh_profile_agents_mcp_tools(get_hermes_home())
|
|
238
272
|
connected = server_names()
|
|
239
273
|
if scope is not None:
|
|
@@ -327,11 +361,43 @@ def _credits() -> str:
|
|
|
327
361
|
return "\n".join(lines)
|
|
328
362
|
|
|
329
363
|
|
|
330
|
-
def
|
|
364
|
+
def _fire_pre_command(command: str, session_id: str, canonical: str | None = None, workspace: str = "") -> None:
|
|
365
|
+
"""The Agent's observer-only ``pre_command`` hook, which every local surface fires before a recognised command runs.
|
|
366
|
+
A chat's observers run under that chat's session and workspace, as its plugin commands and turns do."""
|
|
367
|
+
from hermes_cli.plugins import fire_pre_command_hook
|
|
368
|
+
|
|
369
|
+
from .chat import _turn_identity
|
|
370
|
+
|
|
371
|
+
typed, arg = _split_slash_command(command)
|
|
372
|
+
with _turn_identity(session_id, workspace) if session_id else contextlib.nullcontext():
|
|
373
|
+
fire_pre_command_hook(surface="webui", command=canonical or _ALIASES.get(typed, typed), alias_used=typed, args_raw=arg,
|
|
374
|
+
session_key=session_id or None, platform="webui")
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def _registry_executor(name: str):
|
|
378
|
+
"""The Agent registry's shared executor for ``name``; None when this Agent has none."""
|
|
379
|
+
try:
|
|
380
|
+
from hermes_cli.commands import resolve_command
|
|
381
|
+
from hermes_cli.slash_exec import resolve_executor
|
|
382
|
+
except ImportError:
|
|
383
|
+
return None
|
|
384
|
+
cmd = resolve_command(name)
|
|
385
|
+
return resolve_executor(cmd) if cmd is not None else None
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def execute_agent_command(command: str, session_id: str = "", workspace: str = "") -> str | None:
|
|
331
389
|
"""Run one of the allowlisted agent-side runtime commands; None when not one."""
|
|
332
390
|
canonical, arg_string = parse_slash_command(command)
|
|
333
391
|
if canonical not in _ALLOWED:
|
|
334
392
|
return None
|
|
393
|
+
executor = _registry_executor(canonical) if canonical in _REGISTRY_EXECUTED else None
|
|
394
|
+
if canonical in _REGISTRY_EXECUTED and executor is None:
|
|
395
|
+
return None
|
|
396
|
+
_fire_pre_command(command, session_id, workspace=workspace)
|
|
397
|
+
if executor is not None:
|
|
398
|
+
from hermes_cli.slash_exec import CommandContext
|
|
399
|
+
|
|
400
|
+
return str(executor(CommandContext(surface="webui", args=arg_string)).text or "(no output)")
|
|
335
401
|
if canonical == "reload-mcp":
|
|
336
402
|
return _reload_mcp()
|
|
337
403
|
if canonical == "reload-skills":
|
|
@@ -341,7 +407,16 @@ def execute_agent_command(command: str) -> str | None:
|
|
|
341
407
|
return _credits()
|
|
342
408
|
|
|
343
409
|
|
|
344
|
-
def
|
|
410
|
+
def _invoke_plugin_handler(handler, arg: str, session_id: str):
|
|
411
|
+
"""``handler(arg)`` plus the session context it declares, as every Agent surface calls it (v0.22.3+)."""
|
|
412
|
+
try:
|
|
413
|
+
from hermes_cli.plugin_command_dispatch import invoke_plugin_command
|
|
414
|
+
except ImportError: # Delete this fallback once v0.22.3 is the oldest supported Agent.
|
|
415
|
+
return handler(arg)
|
|
416
|
+
return invoke_plugin_command(handler, arg, source=None, session_id=session_id, session_key=session_id, platform="webui")
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def execute_plugin_command(command: str, session_id: str = "") -> str | None:
|
|
345
420
|
"""Run a plugin-registered slash command; None when no plugin owns it."""
|
|
346
421
|
base, arg = parse_slash_command(command)
|
|
347
422
|
try:
|
|
@@ -354,8 +429,9 @@ def execute_plugin_command(command: str) -> str | None:
|
|
|
354
429
|
raise RpcError("plugin command lookup failed", condition="plugins_unavailable") from exc
|
|
355
430
|
if not handler:
|
|
356
431
|
return None
|
|
432
|
+
_fire_pre_command(command, session_id)
|
|
357
433
|
try:
|
|
358
|
-
return str(resolve_plugin_command_result(handler
|
|
434
|
+
return str(resolve_plugin_command_result(_invoke_plugin_handler(handler, arg, session_id)) or "(no output)")
|
|
359
435
|
except Exception as exc: # noqa: BLE001 - user-facing text, never a transport error
|
|
360
436
|
log.warning("Plugin command %r execution failed", base, exc_info=True)
|
|
361
437
|
return f"Plugin command error: {type(exc).__name__}"
|
|
@@ -477,6 +553,103 @@ def _clean_text(value, limit: int = 240) -> str:
|
|
|
477
553
|
return text[: limit - 1].rstrip() + "…" if len(text) > limit else text
|
|
478
554
|
|
|
479
555
|
|
|
556
|
+
def _plugin_update_modules():
|
|
557
|
+
try:
|
|
558
|
+
from hermes_cli import plugins_cmd, plugins_transaction
|
|
559
|
+
from hermes_cli.plugins_cadence import cached_git_updates
|
|
560
|
+
except ImportError:
|
|
561
|
+
return None
|
|
562
|
+
return plugins_cmd, plugins_transaction, cached_git_updates
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
def update_plugin(key: str, accept_caution: bool, consent_token: str | None = None) -> dict:
|
|
566
|
+
"""The Agent owns Git staging, scan consent and publication, under the caller's profile scope."""
|
|
567
|
+
if not key or not isinstance(accept_caution, bool) or (consent_token is not None and not isinstance(consent_token, str)):
|
|
568
|
+
raise InvalidParams("plugins.update requires a key and boolean accept_caution")
|
|
569
|
+
modules = _plugin_update_modules()
|
|
570
|
+
if modules is None:
|
|
571
|
+
raise RpcError("This Hermes Agent cannot update plugins from Web", condition="plugin_update_unsupported")
|
|
572
|
+
pc, transaction, _cached = modules
|
|
573
|
+
found = pc._find_plugin_entry(key)
|
|
574
|
+
if found is None or not found[4]:
|
|
575
|
+
raise RpcError(f"plugin {key!r} not found", condition="plugin_not_found")
|
|
576
|
+
from hermes_constants import get_hermes_home
|
|
577
|
+
target = pathlib.Path(str(found[4])).resolve()
|
|
578
|
+
# Only profile-owned installs, never bundled, project or entrypoint plugins.
|
|
579
|
+
if target.parent != (get_hermes_home() / "plugins").resolve():
|
|
580
|
+
raise InvalidParams("Only profile-installed Git plugins can be updated")
|
|
581
|
+
from pm.store import tree_digest
|
|
582
|
+
from tools.plugin_guard import format_scan_report
|
|
583
|
+
|
|
584
|
+
def installed_identity():
|
|
585
|
+
return (str(get_hermes_home().resolve()), str(target.resolve()), found[5], tree_digest(target),
|
|
586
|
+
json.dumps(pc._read_install_metadata().get(target.name, {}), sort_keys=True))
|
|
587
|
+
|
|
588
|
+
identity = installed_identity()
|
|
589
|
+
with _PLUGIN_UPDATE_CONSENT_LOCK:
|
|
590
|
+
now = time.monotonic()
|
|
591
|
+
for token, receipt in list(_PLUGIN_UPDATE_CONSENTS.items()):
|
|
592
|
+
if receipt["expires"] <= now:
|
|
593
|
+
_PLUGIN_UPDATE_CONSENTS.pop(token)
|
|
594
|
+
consent = _PLUGIN_UPDATE_CONSENTS.pop(consent_token, None) if accept_caution and consent_token else None
|
|
595
|
+
if accept_caution and consent_token and (consent is None or consent["identity"] != identity):
|
|
596
|
+
raise RpcError("Plugin update confirmation expired or the installation changed; update again to review its scan",
|
|
597
|
+
condition="plugin_update_consent_changed")
|
|
598
|
+
|
|
599
|
+
revision = ""
|
|
600
|
+
consent_changed = False
|
|
601
|
+
changed_message = "The plugin installation or revision changed; update again to review its scan"
|
|
602
|
+
|
|
603
|
+
# The Agent staging hook exposes the candidate commit before its scan and publication gates.
|
|
604
|
+
def capture_revision(staged):
|
|
605
|
+
nonlocal revision, consent_changed
|
|
606
|
+
if not staged.resolve().is_relative_to(target.parent) or installed_identity() != identity:
|
|
607
|
+
consent_changed = True
|
|
608
|
+
raise pc.PluginOperationError(changed_message)
|
|
609
|
+
revision = pc._git_head_revision(staged, pc._resolve_git_executable())
|
|
610
|
+
if consent and consent["revision"] != revision:
|
|
611
|
+
consent_changed = True
|
|
612
|
+
raise pc.PluginOperationError(changed_message)
|
|
613
|
+
return []
|
|
614
|
+
|
|
615
|
+
def scan_binding(scan):
|
|
616
|
+
payload = json.dumps([format_scan_report(scan), getattr(scan, "scan_provenance", {}),
|
|
617
|
+
[vars(finding) for finding in getattr(scan, "findings", [])]], sort_keys=True)
|
|
618
|
+
return hashlib.sha256(payload.encode()).hexdigest()
|
|
619
|
+
|
|
620
|
+
def accept_scan(scan):
|
|
621
|
+
nonlocal consent_changed
|
|
622
|
+
if installed_identity() != identity:
|
|
623
|
+
consent_changed = True
|
|
624
|
+
return False
|
|
625
|
+
return consent is not None and consent["revision"] == revision and consent["scan"] == scan_binding(scan)
|
|
626
|
+
|
|
627
|
+
try:
|
|
628
|
+
# A blanket force would bypass the callback: only the exact scanned identity can accept caution.
|
|
629
|
+
transaction.update_plugin(target, force=False, scan_decision_cb=accept_scan, carry_user_files=capture_revision)
|
|
630
|
+
except pc.PluginScanBlocked as exc:
|
|
631
|
+
if consent_changed:
|
|
632
|
+
raise RpcError(changed_message, condition="plugin_update_consent_changed") from exc
|
|
633
|
+
scan = exc.scan_result
|
|
634
|
+
verdict = getattr(scan, "verdict", "dangerous")
|
|
635
|
+
result = {"key": found[5], "status": "needs_consent" if verdict == "caution" else "blocked",
|
|
636
|
+
"verdict": verdict, "findings": str(exc)}
|
|
637
|
+
if verdict == "caution":
|
|
638
|
+
token = secrets.token_urlsafe(32)
|
|
639
|
+
with _PLUGIN_UPDATE_CONSENT_LOCK:
|
|
640
|
+
if len(_PLUGIN_UPDATE_CONSENTS) >= 64:
|
|
641
|
+
_PLUGIN_UPDATE_CONSENTS.pop(next(iter(_PLUGIN_UPDATE_CONSENTS)))
|
|
642
|
+
_PLUGIN_UPDATE_CONSENTS[token] = {"identity": identity, "revision": revision, "scan": scan_binding(scan),
|
|
643
|
+
"expires": time.monotonic() + 300}
|
|
644
|
+
result["consent_token"] = token
|
|
645
|
+
return result
|
|
646
|
+
except pc.PluginOperationError as exc:
|
|
647
|
+
raise RpcError(str(exc), condition="plugin_update_consent_changed" if consent_changed else "plugin_update_refused") from exc
|
|
648
|
+
from hermes_cli.plugins import discover_plugins
|
|
649
|
+
discover_plugins(force=True)
|
|
650
|
+
return {"key": found[5], "status": "updated"}
|
|
651
|
+
|
|
652
|
+
|
|
480
653
|
def plugin_visibility(selected_providers: dict) -> dict:
|
|
481
654
|
"""Sanitized plugin/hook rows for Settings (Python ``_plugin_visibility_payload``); no paths or callbacks."""
|
|
482
655
|
try:
|
|
@@ -486,6 +659,12 @@ def plugin_visibility(selected_providers: dict) -> dict:
|
|
|
486
659
|
manager.discover_and_load(force=False)
|
|
487
660
|
except Exception as exc: # noqa: BLE001
|
|
488
661
|
raise RpcError(f"plugin manager unavailable: {exc}", condition="plugins_unavailable") from exc
|
|
662
|
+
modules = _plugin_update_modules()
|
|
663
|
+
updates = None
|
|
664
|
+
if modules:
|
|
665
|
+
from hermes_cli.plugins_provenance import read_sidecar_rows
|
|
666
|
+
from hermes_constants import get_hermes_home
|
|
667
|
+
updates = modules[2](read_sidecar_rows(get_hermes_home() / "plugins"))
|
|
489
668
|
rows = []
|
|
490
669
|
for key, loaded in sorted((getattr(manager, "_plugins", {}) or {}).items(), key=lambda item: str(item[0])):
|
|
491
670
|
manifest = getattr(loaded, "manifest", None)
|
|
@@ -517,6 +696,13 @@ def plugin_visibility(selected_providers: dict) -> dict:
|
|
|
517
696
|
row["is_active_provider"] = bool(selected) and slug == selected
|
|
518
697
|
else:
|
|
519
698
|
row["is_active_provider"] = kind == "model-provider" and enabled
|
|
699
|
+
if updates is not None:
|
|
700
|
+
installed = pathlib.Path(str(getattr(manifest, "path", "") or "")).resolve()
|
|
701
|
+
owned = installed.parent == (get_hermes_home() / "plugins").resolve() and (installed / ".git").is_dir()
|
|
702
|
+
revision = updates.get(installed.name) if owned else None
|
|
703
|
+
row["update_available"] = bool(revision)
|
|
704
|
+
if revision:
|
|
705
|
+
row["latest_revision"] = revision
|
|
520
706
|
rows.append(row)
|
|
521
707
|
return {"plugins": rows, "supported_hooks": list(PLUGIN_VISIBILITY_HOOKS)}
|
|
522
708
|
|
|
@@ -560,6 +746,60 @@ def plugin_settings() -> dict:
|
|
|
560
746
|
return {"supported": True, "plugins": rows}
|
|
561
747
|
|
|
562
748
|
|
|
749
|
+
def _apply_plugin_mcp_settings(plugins_cmd, plugin_dir, key: str) -> str:
|
|
750
|
+
"""Reconcile package-owned launch inputs on the RPC worker, preserving the request's profile scope."""
|
|
751
|
+
try:
|
|
752
|
+
if not plugin_dir or not plugins_cmd._is_portable_plugin_dir(plugin_dir):
|
|
753
|
+
return "applied"
|
|
754
|
+
from hermes_cli.agent_plugins import load_agent_plugin
|
|
755
|
+
from hermes_cli.plugins_manifest import portable_mcp_server_name
|
|
756
|
+
|
|
757
|
+
package = load_agent_plugin(plugin_dir, plugin_dir)
|
|
758
|
+
if any(d.scope == "mcp" or d.scope.startswith("mcp:") for d in package.diagnostics):
|
|
759
|
+
return "unknown"
|
|
760
|
+
names = {portable_mcp_server_name(key, server) for server in package.mcp_servers}
|
|
761
|
+
if not names:
|
|
762
|
+
return "applied"
|
|
763
|
+
from hermes_cli.config import load_config_readonly
|
|
764
|
+
from hermes_cli.plugins import get_plugin_manager
|
|
765
|
+
|
|
766
|
+
manager = get_plugin_manager()
|
|
767
|
+
manager.discover_and_load(force=False)
|
|
768
|
+
owners = getattr(manager, "_portable_mcp_server_plugins", None)
|
|
769
|
+
if owners is None:
|
|
770
|
+
return "restart_required"
|
|
771
|
+
native = (load_config_readonly() or {}).get("mcp_servers") or {}
|
|
772
|
+
names = {name for name in names if owners.get(name) == key and name not in native}
|
|
773
|
+
if not names:
|
|
774
|
+
return "applied"
|
|
775
|
+
from tools import mcp_tool_discovery as discovery
|
|
776
|
+
|
|
777
|
+
reconnect = getattr(discovery, "restart_mcp_servers_with_new_inputs", None)
|
|
778
|
+
if reconnect is None:
|
|
779
|
+
return "restart_required"
|
|
780
|
+
# Also exclude the explicit Reload MCP path. The native helper serializes settings-driven reconnects.
|
|
781
|
+
with _RELOAD_MCP_LOCK:
|
|
782
|
+
live = {row["name"] for row in discovery.get_mcp_status() if row["name"] in names and row.get("connected")}
|
|
783
|
+
restarted = reconnect(names)
|
|
784
|
+
if restarted:
|
|
785
|
+
from hermes_constants import get_hermes_home
|
|
786
|
+
|
|
787
|
+
from .chat import refresh_profile_agents_mcp_tools
|
|
788
|
+
|
|
789
|
+
refresh_profile_agents_mcp_tools(get_hermes_home())
|
|
790
|
+
statuses = {row["name"]: row["status"] for row in discovery.get_mcp_status() if row["name"] in names}
|
|
791
|
+
if any(status == "failed" for status in statuses.values()):
|
|
792
|
+
return "restart_required"
|
|
793
|
+
if any(status == "connecting" for status in statuses.values()) or any(name not in statuses for name in live | set(restarted)):
|
|
794
|
+
return "unknown"
|
|
795
|
+
return "applied"
|
|
796
|
+
except ImportError:
|
|
797
|
+
return "restart_required"
|
|
798
|
+
except Exception: # noqa: BLE001
|
|
799
|
+
# Persistence already succeeded. Neither expose launch inputs in an exception nor retry an uncertain reconnect.
|
|
800
|
+
return "unknown"
|
|
801
|
+
|
|
802
|
+
|
|
563
803
|
def save_plugin_settings(key: str, values: Any, secret_envs: Any = None) -> dict:
|
|
564
804
|
"""Write manifest-declared, non-secret settings through the Agent's locked writer; returns the refreshed fields.
|
|
565
805
|
|
|
@@ -586,7 +826,9 @@ def save_plugin_settings(key: str, values: Any, secret_envs: Any = None) -> dict
|
|
|
586
826
|
raise RpcError(str(exc), condition="plugin_settings_managed") from exc
|
|
587
827
|
except ValueError as exc:
|
|
588
828
|
raise InvalidParams(str(exc)) from exc
|
|
589
|
-
|
|
829
|
+
fields = plugins_settings.plugin_settings_fields(found[5], plugin_dir)
|
|
830
|
+
application = _apply_plugin_mcp_settings(plugins_cmd, plugin_dir, found[5])
|
|
831
|
+
return {"key": found[5], "written": written, "fields": fields, "runtime_application": application}
|
|
590
832
|
|
|
591
833
|
|
|
592
834
|
def register(registry) -> None:
|
|
@@ -598,16 +840,17 @@ def register(registry) -> None:
|
|
|
598
840
|
@registry.method("commands.exec")
|
|
599
841
|
def exec_(ctx: CallContext, params: dict) -> dict:
|
|
600
842
|
command = str(params.get("command") or "")
|
|
843
|
+
session_id = str(params.get("session_id") or "").strip()
|
|
844
|
+
workspace = str(params.get("workspace") or "")
|
|
601
845
|
with scoped_home(profile_home_param(params)):
|
|
602
|
-
output = execute_agent_command(command)
|
|
846
|
+
output = execute_agent_command(command, session_id, workspace)
|
|
603
847
|
source = "agent"
|
|
604
848
|
if output is None:
|
|
605
849
|
from .chat import _turn_identity
|
|
606
850
|
|
|
607
851
|
# TAL-904: a chat's plugin command runs under that chat's session and workspace, as its turns do.
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
output = execute_plugin_command(command)
|
|
852
|
+
with _turn_identity(session_id, workspace) if session_id else contextlib.nullcontext():
|
|
853
|
+
output = execute_plugin_command(command, session_id)
|
|
611
854
|
source = "plugin"
|
|
612
855
|
if output is None:
|
|
613
856
|
raise RpcError(f"unknown command {parse_slash_command(command)[0]!r}", condition="command_not_found")
|
|
@@ -616,7 +859,8 @@ def register(registry) -> None:
|
|
|
616
859
|
@registry.method("commands.expand_prompt")
|
|
617
860
|
def expand_prompt_(ctx: CallContext, params: dict) -> dict:
|
|
618
861
|
with scoped_home(profile_home_param(params)):
|
|
619
|
-
return expand_prompt(str(params.get("command") or ""), str(params.get("workspace") or ""), params.get("enabled_toolsets")
|
|
862
|
+
return expand_prompt(str(params.get("command") or ""), str(params.get("workspace") or ""), params.get("enabled_toolsets"),
|
|
863
|
+
str(params.get("session_id") or "").strip())
|
|
620
864
|
|
|
621
865
|
@registry.method("commands.bundles")
|
|
622
866
|
def bundles(ctx: CallContext, params: dict) -> dict:
|
|
@@ -643,6 +887,23 @@ def register(registry) -> None:
|
|
|
643
887
|
with scoped_home(profile_home_param(params)):
|
|
644
888
|
return plugin_visibility(params.get("selected_providers") if isinstance(params.get("selected_providers"), dict) else {})
|
|
645
889
|
|
|
890
|
+
@registry.method("plugins.catalog")
|
|
891
|
+
def catalog_(ctx: CallContext, params: dict) -> dict:
|
|
892
|
+
from .plugin_catalog import catalog
|
|
893
|
+
with scoped_home(profile_home_param(params)):
|
|
894
|
+
return catalog(params.get('query', ''), params.get('offset', 0), params.get('limit', 20))
|
|
895
|
+
|
|
896
|
+
@registry.method("plugins.install")
|
|
897
|
+
def install_(ctx: CallContext, params: dict) -> dict:
|
|
898
|
+
from .plugin_catalog import install
|
|
899
|
+
with scoped_home(profile_home_param(params)):
|
|
900
|
+
return install(params.get('name'), params.get('consent_token'))
|
|
901
|
+
|
|
902
|
+
@registry.method("plugins.update")
|
|
903
|
+
def update(ctx: CallContext, params: dict) -> dict:
|
|
904
|
+
with scoped_home(profile_home_param(params)):
|
|
905
|
+
return update_plugin(str(params.get("key") or "").strip(), params.get("accept_caution", False), params.get("consent_token"))
|
|
906
|
+
|
|
646
907
|
@registry.method("plugins.isolation")
|
|
647
908
|
def isolation(ctx: CallContext, params: dict) -> dict:
|
|
648
909
|
return {"profiles": isolation_survey(profile_home_param(params, "base_home"))}
|
|
@@ -11,7 +11,9 @@ gateway, CLI and plugins also take, so concurrent writers never drop a key.
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
13
|
import contextlib
|
|
14
|
+
import copy
|
|
14
15
|
import importlib
|
|
16
|
+
import io
|
|
15
17
|
import logging
|
|
16
18
|
import os
|
|
17
19
|
import re
|
|
@@ -73,9 +75,75 @@ def _write_target(path: Path) -> Path:
|
|
|
73
75
|
return target
|
|
74
76
|
|
|
75
77
|
|
|
76
|
-
def
|
|
78
|
+
def _same_config_value(left, right) -> bool:
|
|
79
|
+
"""Compare decoded JSON values without treating booleans as numbers."""
|
|
80
|
+
if type(left) is not type(right):
|
|
81
|
+
return False
|
|
82
|
+
if isinstance(left, dict):
|
|
83
|
+
return left.keys() == right.keys() and all(_same_config_value(left[key], right[key]) for key in left)
|
|
84
|
+
if isinstance(left, list):
|
|
85
|
+
return len(left) == len(right) and all(_same_config_value(a, b) for a, b in zip(left, right))
|
|
86
|
+
return left == right
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _split_config_aliases(text: str, desired: dict) -> str:
|
|
90
|
+
"""Keep the Agent's round-trip document, separating aliases with different requested values."""
|
|
91
|
+
roundtrip = getattr(yaml_parser(), "roundtrip_yaml", None)
|
|
92
|
+
if roundtrip is None:
|
|
93
|
+
return text
|
|
94
|
+
parser = roundtrip()
|
|
95
|
+
document = parser.load(text)
|
|
96
|
+
seen = {}
|
|
97
|
+
changed = False
|
|
98
|
+
|
|
99
|
+
def split(node, value):
|
|
100
|
+
nonlocal changed
|
|
101
|
+
if not (isinstance(node, dict) and isinstance(value, dict) or isinstance(node, list) and isinstance(value, list)):
|
|
102
|
+
return node
|
|
103
|
+
previous = seen.get(id(node))
|
|
104
|
+
if previous is not None:
|
|
105
|
+
if _same_config_value(previous[1], value):
|
|
106
|
+
return node
|
|
107
|
+
node = copy.deepcopy(node)
|
|
108
|
+
# Copied explicit anchor names must not collide with the original subtree.
|
|
109
|
+
pending, cleared = [node], set()
|
|
110
|
+
while pending:
|
|
111
|
+
child = pending.pop()
|
|
112
|
+
if id(child) in cleared:
|
|
113
|
+
continue
|
|
114
|
+
cleared.add(id(child))
|
|
115
|
+
if hasattr(child, "yaml_set_anchor"):
|
|
116
|
+
child.yaml_set_anchor(None)
|
|
117
|
+
if isinstance(child, dict):
|
|
118
|
+
pending.extend(child.values())
|
|
119
|
+
elif isinstance(child, list):
|
|
120
|
+
pending.extend(child)
|
|
121
|
+
changed = True
|
|
122
|
+
seen[id(node)] = (node, value)
|
|
123
|
+
for key in value if isinstance(value, dict) else range(min(len(node), len(value))):
|
|
124
|
+
if not isinstance(node, dict) or key in node:
|
|
125
|
+
node[key] = split(node[key], value[key])
|
|
126
|
+
return node
|
|
127
|
+
|
|
128
|
+
split(document, desired)
|
|
129
|
+
if not changed:
|
|
130
|
+
return text
|
|
131
|
+
output = io.StringIO()
|
|
132
|
+
parser.dump(document, output)
|
|
133
|
+
return output.getvalue()
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def write_config(path: Path, config: dict, *, profile_home: Path | None = None) -> dict:
|
|
77
137
|
if not isinstance(config, dict):
|
|
78
138
|
raise InvalidParams("config must be an object")
|
|
139
|
+
try:
|
|
140
|
+
managed = importlib.import_module("hermes_constants").get_managed_system(profile_home)
|
|
141
|
+
except Exception as exc: # noqa: BLE001
|
|
142
|
+
raise RpcError("Cannot verify config.yaml management policy; repair the Agent installation before saving.",
|
|
143
|
+
condition="config_policy_unavailable") from exc
|
|
144
|
+
if managed is not None:
|
|
145
|
+
raise RpcError("config.yaml is package-managed. Update the declarative configuration instead.",
|
|
146
|
+
condition="config_write_refused")
|
|
79
147
|
target = _write_target(path)
|
|
80
148
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
81
149
|
try:
|
|
@@ -94,7 +162,7 @@ def write_config(path: Path, config: dict) -> dict:
|
|
|
94
162
|
if replace is not None:
|
|
95
163
|
# Stage the original document so the Agent retains comments; check the real link before committing.
|
|
96
164
|
try:
|
|
97
|
-
handle.write(target.read_text(encoding="utf-8"))
|
|
165
|
+
handle.write(_split_config_aliases(target.read_text(encoding="utf-8"), config))
|
|
98
166
|
except FileNotFoundError:
|
|
99
167
|
pass
|
|
100
168
|
else:
|
|
@@ -107,6 +175,8 @@ def write_config(path: Path, config: dict) -> dict:
|
|
|
107
175
|
replace(Path(tmp), config)
|
|
108
176
|
if mode is not None:
|
|
109
177
|
os.chmod(tmp, mode)
|
|
178
|
+
if not _same_config_value(read_config(Path(tmp))["config"], config):
|
|
179
|
+
raise RpcError("Staged config.yaml does not match the requested values", condition="config_invalid")
|
|
110
180
|
# A link retargeted while the temp file was being written would otherwise send the write to the wrong file.
|
|
111
181
|
if _write_target(path) != target:
|
|
112
182
|
raise RpcError(f"{path} was retargeted while config.yaml was being written; retry", condition="config_changed")
|
|
@@ -240,7 +310,7 @@ def register(registry) -> None:
|
|
|
240
310
|
|
|
241
311
|
@registry.method("config.set", requires_agent=False)
|
|
242
312
|
def set_(ctx: CallContext, params: dict) -> dict:
|
|
243
|
-
return write_config(config_path_param(params), params.get("config"))
|
|
313
|
+
return write_config(config_path_param(params), params.get("config"), profile_home=profile_home_param(params))
|
|
244
314
|
|
|
245
315
|
@registry.method("config.env_write")
|
|
246
316
|
def env_write_(ctx: CallContext, params: dict) -> dict:
|