@maudecode/talaria-web 1.20.0 → 1.22.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-router.d.ts +319 -14
- package/dist/api/automation-router.js +21 -1
- package/dist/api/automation-router.js.map +1 -1
- package/dist/api/chat-router.d.ts +615 -69
- package/dist/api/chat-router.js +22 -170
- package/dist/api/chat-router.js.map +1 -1
- package/dist/api/git-router.d.ts +50 -0
- package/dist/api/git-router.js +17 -4
- package/dist/api/git-router.js.map +1 -1
- package/dist/api/groups-router.d.ts +764 -0
- package/dist/api/groups-router.js +79 -0
- package/dist/api/groups-router.js.map +1 -0
- package/dist/api/raw-routes.js +3 -1
- package/dist/api/raw-routes.js.map +1 -1
- package/dist/api/router.d.ts +1 -1
- package/dist/api/sessions-router.d.ts +1675 -167
- package/dist/api/sessions-router.js +67 -53
- package/dist/api/sessions-router.js.map +1 -1
- package/dist/api/settings-router.d.ts +911 -1
- package/dist/api/settings-router.js +53 -2
- package/dist/api/settings-router.js.map +1 -1
- package/dist/api/sse-routes.d.ts +1 -1
- package/dist/api/sse-routes.js +15 -5
- package/dist/api/sse-routes.js.map +1 -1
- package/dist/api/tools-raw.js +27 -136
- package/dist/api/tools-raw.js.map +1 -1
- package/dist/api/tools-router.d.ts +588 -51
- package/dist/api/tools-router.js +98 -16
- package/dist/api/tools-router.js.map +1 -1
- package/dist/app.js +5 -1
- package/dist/app.js.map +1 -1
- package/dist/auth/gate.js +5 -1
- package/dist/auth/gate.js.map +1 -1
- package/dist/config/agent-config.d.ts +6 -1
- package/dist/config/agent-config.js +75 -3
- package/dist/config/agent-config.js.map +1 -1
- package/dist/feedback.d.ts +18 -0
- package/dist/feedback.js +145 -0
- package/dist/feedback.js.map +1 -0
- package/dist/fs/stat.d.ts +2 -0
- package/dist/fs/stat.js +4 -0
- package/dist/fs/stat.js.map +1 -1
- package/dist/http/context.d.ts +9 -0
- package/dist/http/context.js +0 -6
- package/dist/http/context.js.map +1 -1
- package/dist/http/pinned.js +19 -8
- package/dist/http/pinned.js.map +1 -1
- package/dist/providers/catalog.d.ts +9 -1
- package/dist/providers/catalog.js +86 -6
- package/dist/providers/catalog.js.map +1 -1
- package/dist/redact.d.ts +2 -0
- package/dist/redact.js +26 -5
- package/dist/redact.js.map +1 -1
- package/dist/runtime.js +62 -11
- package/dist/runtime.js.map +1 -1
- package/dist/server.js +2 -0
- package/dist/server.js.map +1 -1
- package/dist/sessions/admission.d.ts +21 -0
- package/dist/sessions/admission.js +176 -0
- package/dist/sessions/admission.js.map +1 -0
- package/dist/sessions/anchor.d.ts +5 -2
- package/dist/sessions/anchor.js +14 -13
- package/dist/sessions/anchor.js.map +1 -1
- package/dist/sessions/background-tasks.d.ts +19 -1
- package/dist/sessions/background-tasks.js +36 -6
- package/dist/sessions/background-tasks.js.map +1 -1
- package/dist/sessions/cli-sessions.js +11 -6
- package/dist/sessions/cli-sessions.js.map +1 -1
- package/dist/sessions/compress.d.ts +10 -9
- package/dist/sessions/compress.js +31 -20
- package/dist/sessions/compress.js.map +1 -1
- package/dist/sessions/events.d.ts +3 -0
- package/dist/sessions/events.js +4 -0
- package/dist/sessions/events.js.map +1 -1
- package/dist/sessions/gateway-sessions.d.ts +61 -2
- package/dist/sessions/gateway-sessions.js +223 -35
- package/dist/sessions/gateway-sessions.js.map +1 -1
- package/dist/sessions/gateway-watcher.js +8 -4
- package/dist/sessions/gateway-watcher.js.map +1 -1
- package/dist/sessions/groups.d.ts +60 -0
- package/dist/sessions/groups.js +339 -0
- package/dist/sessions/groups.js.map +1 -0
- package/dist/sessions/list.d.ts +7 -2
- package/dist/sessions/list.js +13 -4
- package/dist/sessions/list.js.map +1 -1
- package/dist/sessions/merge.d.ts +15 -5
- package/dist/sessions/merge.js +49 -15
- package/dist/sessions/merge.js.map +1 -1
- package/dist/sessions/pending.d.ts +1 -1
- package/dist/sessions/pending.js +15 -6
- package/dist/sessions/pending.js.map +1 -1
- package/dist/sessions/prefix-index.d.ts +9 -14
- package/dist/sessions/prefix-index.js +14 -52
- package/dist/sessions/prefix-index.js.map +1 -1
- package/dist/sessions/prompt-queues.d.ts +36 -0
- package/dist/sessions/prompt-queues.js +318 -0
- package/dist/sessions/prompt-queues.js.map +1 -0
- package/dist/sessions/reasoning-display.d.ts +2 -0
- package/dist/sessions/reasoning-display.js +31 -0
- package/dist/sessions/reasoning-display.js.map +1 -0
- package/dist/sessions/relay.d.ts +16 -2
- package/dist/sessions/relay.js +119 -24
- package/dist/sessions/relay.js.map +1 -1
- package/dist/sessions/service.d.ts +33 -3
- package/dist/sessions/service.js +304 -70
- package/dist/sessions/service.js.map +1 -1
- package/dist/sessions/session.d.ts +6 -0
- package/dist/sessions/session.js +14 -0
- package/dist/sessions/session.js.map +1 -1
- package/dist/sessions/shares.d.ts +1 -1
- package/dist/sessions/shares.js +13 -4
- package/dist/sessions/shares.js.map +1 -1
- package/dist/sessions/state-db.d.ts +2 -1
- package/dist/sessions/state-db.js +7 -3
- package/dist/sessions/state-db.js.map +1 -1
- package/dist/sessions/storage/json-documents.js +3 -0
- package/dist/sessions/storage/json-documents.js.map +1 -1
- package/dist/sessions/store.d.ts +2 -0
- package/dist/sessions/store.js +11 -3
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/streams.d.ts +4 -0
- package/dist/sessions/streams.js +12 -0
- package/dist/sessions/streams.js.map +1 -1
- package/dist/sessions/todo.js +10 -1
- package/dist/sessions/todo.js.map +1 -1
- package/dist/sessions/tool-display.d.ts +8 -1
- package/dist/sessions/tool-display.js +39 -4
- package/dist/sessions/tool-display.js.map +1 -1
- package/dist/sessions/turn.d.ts +4 -1
- package/dist/sessions/turn.js +53 -22
- package/dist/sessions/turn.js.map +1 -1
- package/dist/settings.js +2 -1
- package/dist/settings.js.map +1 -1
- package/dist/spa.js +1 -1
- package/dist/spa.js.map +1 -1
- package/dist/test/group-fixture.d.ts +66 -0
- package/dist/test/group-fixture.js +89 -0
- package/dist/test/group-fixture.js.map +1 -0
- package/dist/test/harness.d.ts +3 -0
- package/dist/test/harness.js +3 -0
- package/dist/test/harness.js.map +1 -1
- package/dist/tools/connectors.d.ts +9 -1
- package/dist/tools/connectors.js +48 -0
- package/dist/tools/connectors.js.map +1 -1
- package/dist/tools/kanban.d.ts +10 -1
- package/dist/tools/kanban.js +73 -1
- package/dist/tools/kanban.js.map +1 -1
- package/dist/tools/mcp.d.ts +3 -1
- package/dist/tools/mcp.js +73 -0
- package/dist/tools/mcp.js.map +1 -1
- package/dist/tools/memory.d.ts +28 -0
- package/dist/tools/memory.js +120 -14
- package/dist/tools/memory.js.map +1 -1
- package/dist/tools/skills.d.ts +6 -0
- package/dist/tools/skills.js +17 -0
- package/dist/tools/skills.js.map +1 -1
- package/dist/tools/update-notifications.d.ts +12 -10
- package/dist/tools/update-notifications.js +124 -14
- package/dist/tools/update-notifications.js.map +1 -1
- package/dist/tools/updates.d.ts +38 -12
- package/dist/tools/updates.js +180 -44
- package/dist/tools/updates.js.map +1 -1
- package/dist/workspace/fs.d.ts +2 -0
- package/dist/workspace/fs.js +63 -1
- package/dist/workspace/fs.js.map +1 -1
- package/dist/workspace/managed-buffers.d.ts +58 -0
- package/dist/workspace/managed-buffers.js +76 -0
- package/dist/workspace/managed-buffers.js.map +1 -0
- package/dist/workspace/rollback.js +1 -1
- package/dist/workspace/rollback.js.map +1 -1
- package/dist/workspace/upload.d.ts +1 -1
- package/dist/workspace/upload.js +52 -37
- package/dist/workspace/upload.js.map +1 -1
- package/dist/workspace/workspaces.d.ts +5 -1
- package/dist/workspace/workspaces.js +32 -6
- package/dist/workspace/workspaces.js.map +1 -1
- package/package.json +2 -2
- package/sidecar/agent_dependency.json +4 -4
- package/sidecar/agent_importer.json +8 -0
- package/sidecar/agent_support.json +20 -8
- package/sidecar/scripts/agent_importer_contract.py +19 -0
- package/sidecar/scripts/test.sh +3 -1
- package/sidecar/talaria_sidecar/__init__.py +1 -1
- package/sidecar/talaria_sidecar/agent_mutation.py +87 -9
- package/sidecar/talaria_sidecar/agent_services.py +83 -23
- package/sidecar/talaria_sidecar/home.py +12 -5
- package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
- package/sidecar/talaria_sidecar/methods/agent_imports.py +346 -0
- package/sidecar/talaria_sidecar/methods/chat.py +141 -58
- package/sidecar/talaria_sidecar/methods/commands.py +6 -4
- package/sidecar/talaria_sidecar/methods/config.py +8 -3
- package/sidecar/talaria_sidecar/methods/connectors.py +138 -4
- package/sidecar/talaria_sidecar/methods/dashboard_plugins.py +25 -4
- package/sidecar/talaria_sidecar/methods/delegation.py +126 -0
- package/sidecar/talaria_sidecar/methods/gateway.py +5 -0
- package/sidecar/talaria_sidecar/methods/gateway_sessions.py +142 -23
- package/sidecar/talaria_sidecar/methods/groups.py +120 -0
- package/sidecar/talaria_sidecar/methods/kanban.py +48 -0
- package/sidecar/talaria_sidecar/methods/managed_buffers.py +110 -0
- package/sidecar/talaria_sidecar/methods/mcp.py +55 -0
- package/sidecar/talaria_sidecar/methods/memory.py +27 -3
- package/sidecar/talaria_sidecar/methods/nous_account.py +130 -0
- package/sidecar/talaria_sidecar/methods/oauth.py +25 -4
- package/sidecar/talaria_sidecar/methods/outbound_drafts.py +147 -0
- package/sidecar/talaria_sidecar/methods/plugin_catalog.py +73 -2
- package/sidecar/talaria_sidecar/methods/providers.py +51 -1
- package/sidecar/talaria_sidecar/methods/skill_revisions.py +177 -0
- package/sidecar/talaria_sidecar/methods/skills.py +10 -0
- package/sidecar/talaria_sidecar/methods/tts.py +67 -0
- package/sidecar/talaria_sidecar/rpc.py +11 -2
- package/static/dist/FILES.txt +155 -144
- package/static/dist/THIRD_PARTY_LICENSES.md +1 -1
- package/static/dist/assets/AppShell-hvysbd9x.js +8 -0
- package/static/dist/assets/Button-dlsibkzq.js +1 -0
- package/static/dist/assets/ChatPage-jikwrwfd.js +2 -0
- package/static/dist/assets/Composer-mnke8ss2.js +5 -0
- package/static/dist/assets/ConnectTargets-ejplzo4q.js +1 -0
- package/static/dist/assets/Dialog-rksfyyq5.js +1 -0
- package/static/dist/assets/{ExtensionRoute-zrhjn0wm.js → ExtensionRoute-igk5h0rj.js} +1 -1
- package/static/dist/assets/HubRoute-eooassx4.js +2 -0
- package/static/dist/assets/Markdown-m5pzgvtx.js +2 -0
- package/static/dist/assets/{MarkdownRenderer-fbswsgld.js → MarkdownRenderer-74ysuomr.js} +18 -18
- package/static/dist/assets/OAuthSignIn-i4flsuq2.js +1 -0
- package/static/dist/assets/QueueCard-msz9wd1m.js +1 -0
- package/static/dist/assets/ResourceSwitch-b70zg4xd.js +1 -0
- package/static/dist/assets/RightPanel-f5v61m8j.js +1 -0
- package/static/dist/assets/Select-f3l897pt.js +1 -0
- package/static/dist/assets/SessionListPanel-brnzacw9.js +1 -0
- package/static/dist/assets/SettingsLayout-f4akmrt2.js +1 -0
- package/static/dist/assets/States-msohx3c0.js +1 -0
- package/static/dist/assets/TerminalPanel-bnu5xawo.js +16 -0
- package/static/dist/assets/Toaster-d6col3ch.js +1 -0
- package/static/dist/assets/_app-lrjgufcy.js +1 -0
- package/static/dist/assets/{_app.ext._extensionId-na62087t.js → _app.ext._extensionId-k3xitxr1.js} +1 -1
- package/static/dist/assets/_app.groups-bt4wdwnd.js +1 -0
- package/static/dist/assets/{_app.index-jypgdqoi.js → _app.index-n3otiki0.js} +1 -1
- package/static/dist/assets/{_app.insights-kabejdtm.js → _app.insights-b0mzlh6m.js} +1 -1
- package/static/dist/assets/{_app.kanban-m19kfcmx.js → _app.kanban-d923g7j5.js} +1 -1
- package/static/dist/assets/{_app.logs-iw4m96sz.js → _app.logs-donegue1.js} +1 -1
- package/static/dist/assets/{_app.memory-jq5frwcg.js → _app.memory-hdnled08.js} +1 -1
- package/static/dist/assets/{_app.profiles-kwa3m7rp.js → _app.profiles-fumot3jk.js} +1 -1
- package/static/dist/assets/{_app.session._sessionId-o5lyeipj.js → _app.session._sessionId-crmdl2ux.js} +1 -1
- package/static/dist/assets/{_app.settings-ckl8hdll.js → _app.settings-jui8iesz.js} +1 -1
- package/static/dist/assets/_app.settings._section-do84hsqw.js +3 -0
- package/static/dist/assets/{_app.skills-n016rihd.js → _app.skills-heqcn21m.js} +1 -1
- package/static/dist/assets/_app.tasks-jiudtutt.js +1 -0
- package/static/dist/assets/{_app.todos-btew5v2s.js → _app.todos-bl6kk2zq.js} +1 -1
- package/static/dist/assets/{_app.workspaces-lnfk95ch.js → _app.workspaces-frvh3o7w.js} +1 -1
- package/static/dist/assets/_delete-qnxuo87s.js +1 -0
- package/static/dist/assets/{abnfDiagram-VCTEODGH-dttheegb.js → abnfDiagram-VCTEODGH-dppg3yfx.js} +1 -1
- package/static/dist/assets/{architecture-7GRP2DOG-ic435919.js → architecture-7GRP2DOG-gbajejh3.js} +1 -1
- package/static/dist/assets/{architectureDiagram-5GKGNRK7-8r3glgiq.js → architectureDiagram-5GKGNRK7-bzrxyais.js} +1 -1
- package/static/dist/assets/archive-yhdyz231.js +1 -0
- package/static/dist/assets/autoReadClaim-nlyv8tu9.js +1 -0
- package/static/dist/assets/{blockDiagram-I7D4REHJ-jenyos6c.js → blockDiagram-I7D4REHJ-gmk9koab.js} +1 -1
- package/static/dist/assets/{c4Diagram-7LVT6UL2-nzlx7yc4.js → c4Diagram-7LVT6UL2-bfqiqqcz.js} +1 -1
- package/static/dist/assets/channel-grt3rob4.js +1 -0
- package/static/dist/assets/{chunk-4HAMMTFA-g0cru63b.js → chunk-4HAMMTFA-i9s6h4wv.js} +1 -1
- package/static/dist/assets/{chunk-75Z2AOVW-7rkswkjw.js → chunk-75Z2AOVW-gnt6pnso.js} +1 -1
- package/static/dist/assets/{chunk-DU6HZSFF-fn73jzdo.js → chunk-DU6HZSFF-gq439wot.js} +3 -3
- package/static/dist/assets/{chunk-F27PBJKO-oey8e72m.js → chunk-F27PBJKO-wvx90pbt.js} +1 -1
- package/static/dist/assets/{chunk-GMAD6QVW-l2a32wiy.js → chunk-GMAD6QVW-mc2gu30k.js} +1 -1
- package/static/dist/assets/{chunk-GVQU2GXP-o9dwzh8h.js → chunk-GVQU2GXP-f8fxa2ym.js} +1 -1
- package/static/dist/assets/{chunk-IMKFNOWR-eblwj20b.js → chunk-IMKFNOWR-lnsl31hj.js} +1 -1
- package/static/dist/assets/{chunk-L3NEJ4N5-o2mufspb.js → chunk-L3NEJ4N5-kbfdidwu.js} +1 -1
- package/static/dist/assets/{chunk-OSK3NFVY-ijp9pw29.js → chunk-OSK3NFVY-dvxsduj6.js} +1 -1
- package/static/dist/assets/{chunk-P2QGCYS3-hs9hqwl4.js → chunk-P2QGCYS3-ivo3i1wf.js} +1 -1
- package/static/dist/assets/{chunk-POPQ4Y6H-bp9rsqez.js → chunk-POPQ4Y6H-i0gfu0hx.js} +1 -1
- package/static/dist/assets/{chunk-PWAF6VOD-brkprov6.js → chunk-PWAF6VOD-jb0fdyb0.js} +1 -1
- package/static/dist/assets/{chunk-SHT3W25Y-gmpv0pxz.js → chunk-SHT3W25Y-b7ou2xsp.js} +1 -1
- package/static/dist/assets/{chunk-SVP7TREG-oldm08fu.js → chunk-SVP7TREG-e2v7me5v.js} +1 -1
- package/static/dist/assets/{chunk-TICWLB2K-ooqilfv7.js → chunk-TICWLB2K-e5zlai62.js} +1 -1
- package/static/dist/assets/classDiagram-ZZMXUADV-nnsp49jr.js +1 -0
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-nnsp49jr.js +1 -0
- package/static/dist/assets/completionAudio-c5at041z.js +1 -0
- package/static/dist/assets/{copy-jo006coq.js → copy-jj3oiw13.js} +1 -1
- package/static/dist/assets/{cynefin-OW5HDTMX-d47op7qs.js → cynefin-OW5HDTMX-n5n1ck70.js} +1 -1
- package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-doa7594y.js → cynefinDiagram-5FMLGOSQ-b33npu4f.js} +1 -1
- package/static/dist/assets/{dagre-GXQ25YYZ-os768dp4.js → dagre-GXQ25YYZ-miwvae07.js} +1 -1
- package/static/dist/assets/{diagram-S7CK7UJ4-fyi81nxd.js → diagram-S7CK7UJ4-j8fphsp4.js} +1 -1
- package/static/dist/assets/{diagram-UQ7AKVKN-j3exggaq.js → diagram-UQ7AKVKN-5cixo166.js} +1 -1
- package/static/dist/assets/{diagram-VSXAHHWV-omo1en2l.js → diagram-VSXAHHWV-ii3bf92m.js} +1 -1
- package/static/dist/assets/{diagram-VX7I27RA-oro2cyu0.js → diagram-VX7I27RA-b3cfgx81.js} +1 -1
- package/static/dist/assets/{diagram-Z3DM3KII-byt9vdb7.js → diagram-Z3DM3KII-bey9lqr8.js} +1 -1
- package/static/dist/assets/{dist-etpoyscq.js → dist-cb8um5dh.js} +4 -4
- package/static/dist/assets/{dist-crc46di9.js → dist-kpg5ccm9.js} +1 -1
- package/static/dist/assets/{ebnfDiagram-PWID7BFC-ee3haugh.js → ebnfDiagram-PWID7BFC-2m4wrnqc.js} +1 -1
- package/static/dist/assets/{erDiagram-RLTQ6QDP-d1e93yff.js → erDiagram-RLTQ6QDP-kak5te0n.js} +1 -1
- package/static/dist/assets/{eventmodeling-NTZA5JFV-i1ihb40c.js → eventmodeling-NTZA5JFV-ojcc486t.js} +1 -1
- package/static/dist/assets/flowDiagram-HODETNUW-kxr9sicu.js +1 -0
- package/static/dist/assets/folder-plus-fk6j7tlv.js +1 -0
- package/static/dist/assets/{ganttDiagram-EL5Y4UJY-m7g0jud4.js → ganttDiagram-EL5Y4UJY-k6nfmhrh.js} +1 -1
- package/static/dist/assets/getPseudoElementBounds-mwj3vqni.js +1 -0
- package/static/dist/assets/{gitGraph-4MIJSDKK-hzdj07yy.js → gitGraph-4MIJSDKK-bxq1lbhy.js} +1 -1
- package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-dm9agbna.js → gitGraphDiagram-WWUBYQGX-ehvoankt.js} +1 -1
- package/static/dist/assets/group_discussions-fk5hry4l.js +1 -0
- package/static/dist/assets/{highlighted-body-KPVGNVTW-mmgz2czu.js → highlighted-body-KPVGNVTW-nr4nfdq7.js} +1 -1
- package/static/dist/assets/{host-fr6lni1p.js → host-dhgqiozp.js} +1 -1
- package/static/dist/assets/index-hx7z4wdj.js +13 -0
- package/static/dist/assets/index-o8ebuc5x.css +2 -0
- package/static/dist/assets/{info-A6RAGUB7-lll1dqmm.js → info-A6RAGUB7-dtsj0ybw.js} +1 -1
- package/static/dist/assets/{infoDiagram-27XIBGKW-k7esc639.js → infoDiagram-27XIBGKW-jena90ao.js} +1 -1
- package/static/dist/assets/{ishikawaDiagram-5VMMS53U-lgr6k9wo.js → ishikawaDiagram-5VMMS53U-kia6bn5u.js} +1 -1
- package/static/dist/assets/{journeyDiagram-3NMN7TZE-ndmbxuqk.js → journeyDiagram-3NMN7TZE-ipsa6icb.js} +1 -1
- package/static/dist/assets/{kanban-definition-UXKFOSKX-mjxym0jp.js → kanban-definition-UXKFOSKX-moxx59kp.js} +1 -1
- package/static/dist/assets/katex-ic33v820.js +257 -0
- package/static/dist/assets/katex-qcv7xo0t.css +1 -0
- package/static/dist/assets/{lazyRouteComponent-cwb6l7xh.js → lazyRouteComponent-i74ujrfk.js} +1 -1
- package/static/dist/assets/{line-mtr5k6il.js → line-lgrkg9no.js} +1 -1
- package/static/dist/assets/link-2-fp49mwl2.js +1 -0
- package/static/dist/assets/{link-iadw4ghw.js → link-es18mse1.js} +2 -2
- package/static/dist/assets/{locale-cs-n41hptse.js → locale-cs-brltdkcp.js} +1 -1
- package/static/dist/assets/{locale-de-k7ht47ew.js → locale-de-h6e9oq6v.js} +1 -1
- package/static/dist/assets/{locale-es-ih078lco.js → locale-es-oklmh80f.js} +1 -1
- package/static/dist/assets/{locale-fr-ojabnf41.js → locale-fr-b17ozoi2.js} +1 -1
- package/static/dist/assets/{locale-it-kb96jufy.js → locale-it-whe01eef.js} +1 -1
- package/static/dist/assets/{locale-ja-jpsp3xs1.js → locale-ja-g1s0rtpc.js} +1 -1
- package/static/dist/assets/{locale-pl-dbap4leu.js → locale-pl-d9p3lqcb.js} +1 -1
- package/static/dist/assets/{locale-pt-nci7w72x.js → locale-pt-l75on74f.js} +1 -1
- package/static/dist/assets/{locale-ru-soghe4cs.js → locale-ru-wl3mbsaw.js} +1 -1
- package/static/dist/assets/{locale-tr-ks00mu68.js → locale-tr-cz87itrq.js} +1 -1
- package/static/dist/assets/{locale-vi-ij7twfm6.js → locale-vi-elqj1y4k.js} +1 -1
- package/static/dist/assets/{locale-zh-Hant-feakwr7a.js → locale-zh-Hant-hf8gqm7d.js} +1 -1
- package/static/dist/assets/{locale-zh-ren0vjyv.js → locale-zh-cdckwb0u.js} +1 -1
- package/static/dist/assets/login-o6gfl4gy.js +1 -0
- package/static/dist/assets/mermaid-HWGCJPDP-n2kbzsao.js +1 -0
- package/static/dist/assets/{mermaid-parser.core-k91vdnvi.js → mermaid-parser.core-fflovwf2.js} +3 -3
- package/static/dist/assets/{mindmap-definition-YA3MSWOX-ehztfea4.js → mindmap-definition-YA3MSWOX-ic8l9whi.js} +1 -1
- package/static/dist/assets/onboarding-bxm7gdrr.js +1 -0
- package/static/dist/assets/{packet-AYTQ26CC-ok2xrblz.js → packet-AYTQ26CC-ntci3088.js} +1 -1
- package/static/dist/assets/{pegDiagram-XKGWAZYB-ec3rpj0m.js → pegDiagram-XKGWAZYB-e3btz04w.js} +1 -1
- package/static/dist/assets/{pie-WAS4IAKB-iwkir23u.js → pie-WAS4IAKB-o2dc7l36.js} +1 -1
- package/static/dist/assets/{pieDiagram-E7YTZNPT-fcpqpekh.js → pieDiagram-E7YTZNPT-gro55rrw.js} +1 -1
- package/static/dist/assets/presence-lq6m12ga.js +1 -0
- package/static/dist/assets/prompt_queue_cancelled-cebcuagu.js +1 -0
- package/static/dist/assets/{quadrantDiagram-AXDQQJYC-lqsync43.js → quadrantDiagram-AXDQQJYC-fxulmd7d.js} +1 -1
- package/static/dist/assets/queryKeys-cnvn6pea.js +9 -0
- package/static/dist/assets/{radar-RG4KPBEZ-hje4jbk6.js → radar-RG4KPBEZ-9pcospra.js} +1 -1
- package/static/dist/assets/{railroad-74A4TZTK-gll9n6qi.js → railroad-74A4TZTK-nzdga12j.js} +1 -1
- package/static/dist/assets/{railroad-abnf-HS5TGJTU-o6h6lpn4.js → railroad-abnf-HS5TGJTU-ep3ytel0.js} +1 -1
- package/static/dist/assets/{railroad-ebnf-LZEXJU2U-juaefj6o.js → railroad-ebnf-LZEXJU2U-iw7uc1wy.js} +1 -1
- package/static/dist/assets/{railroad-peg-WCYAUIDC-cgfhvpah.js → railroad-peg-WCYAUIDC-fo3b3gme.js} +1 -1
- package/static/dist/assets/{railroadDiagram-O6MQD6OU-lp3w7awf.js → railroadDiagram-O6MQD6OU-lxpxf8f0.js} +1 -1
- package/static/dist/assets/{refresh-cw-iccm7a4e.js → refresh-cw-d14h79x5.js} +1 -1
- package/static/dist/assets/rename-ccd52yws.js +1 -0
- package/static/dist/assets/{requirementDiagram-BXWQKSXE-ha8eibcf.js → requirementDiagram-BXWQKSXE-lrozucd2.js} +1 -1
- package/static/dist/assets/{sankeyDiagram-P5KCCOFB-oxf31pr9.js → sankeyDiagram-P5KCCOFB-iusxy9mq.js} +1 -1
- package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-jiq8lhyo.js → sequenceDiagram-WJ2MYXX4-o987q8ti.js} +1 -1
- package/static/dist/assets/{share._token-o76ki0ly.js → share._token-bfw7f9m5.js} +1 -1
- package/static/dist/assets/sliders-horizontal-l65hoa3x.js +1 -0
- package/static/dist/assets/{stateDiagram-D77RDMKH-f3fslnon.js → stateDiagram-D77RDMKH-hh23v7y5.js} +1 -1
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-jukr0d8h.js +1 -0
- package/static/dist/assets/{swimlanes-42K2YHIH-k1hmk7jf.js → swimlanes-42K2YHIH-gj2t3rm8.js} +1 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-ik9m9j2m.js +8 -0
- package/static/dist/assets/{timeline-definition-24CTP7MA-n3ncfy9r.js → timeline-definition-24CTP7MA-84s2w2sr.js} +1 -1
- package/static/dist/assets/toast-gqqcvxiy.js +56 -0
- package/static/dist/assets/{treeView-Q6P3EWNA-l3y2pejt.js → treeView-Q6P3EWNA-cmuj50l7.js} +1 -1
- package/static/dist/assets/{treemap-WGGIJYW6-jown9axh.js → treemap-WGGIJYW6-dpjlg0xc.js} +1 -1
- package/static/dist/assets/tts-jw84jlqy.js +1 -0
- package/static/dist/assets/{useForm-o4uu74mx.js → useForm-lxzhiqao.js} +1 -1
- package/static/dist/assets/{useParams-ftsveu84.js → useParams-dt6gkoq7.js} +1 -1
- package/static/dist/assets/{useSearch-crqueqga.js → useSearch-o8t49csk.js} +1 -1
- package/static/dist/assets/useTranscript-gx9umcc2.js +1 -0
- package/static/dist/assets/{vennDiagram-4TSXK5OY-dh59uq1c.js → vennDiagram-4TSXK5OY-g9g5o8vy.js} +1 -1
- package/static/dist/assets/{wardley-WFR3VGLG-e6d74b0j.js → wardley-WFR3VGLG-ij35obrl.js} +1 -1
- package/static/dist/assets/{wardleyDiagram-VM6X3IG4-hnofh78f.js → wardleyDiagram-VM6X3IG4-f9my7q7w.js} +1 -1
- package/static/dist/assets/{xychartDiagram-S5SC5T6Z-bpymjelb.js → xychartDiagram-S5SC5T6Z-olqx1s1p.js} +1 -1
- package/static/dist/index.html +1 -1
- package/static/dist/sw.js +1 -1
- package/static/dist/assets/AppShell-3isgvoro.js +0 -8
- package/static/dist/assets/Button-f4n6dapf.js +0 -1
- package/static/dist/assets/ChatPage-k7b6xfax.js +0 -4
- package/static/dist/assets/ConnectTargets-juqvt5lm.js +0 -1
- package/static/dist/assets/Dialog-hta5utr3.js +0 -1
- package/static/dist/assets/Field-b6ez48oz.js +0 -1
- package/static/dist/assets/HubRoute-iwvso2tz.js +0 -2
- package/static/dist/assets/Markdown-jdeth7zq.js +0 -2
- package/static/dist/assets/OAuthSignIn-bpd8hom3.js +0 -1
- package/static/dist/assets/ResourceSwitch-odi6dy2o.js +0 -1
- package/static/dist/assets/RightPanel-eu6enlo6.js +0 -1
- package/static/dist/assets/Select-kr07928w.js +0 -1
- package/static/dist/assets/SessionListPanel-etb2i54l.js +0 -1
- package/static/dist/assets/SettingsLayout-eu3xxp23.js +0 -1
- package/static/dist/assets/States-icab19yg.js +0 -1
- package/static/dist/assets/TerminalPanel-hngmsr8b.js +0 -16
- package/static/dist/assets/Toaster-nb2uzjw1.js +0 -1
- package/static/dist/assets/_app-ga1moszl.js +0 -1
- package/static/dist/assets/_app.settings._section-dtdjk98a.js +0 -3
- package/static/dist/assets/_app.tasks-n8zuqsqe.js +0 -1
- package/static/dist/assets/archive-il7c6053.js +0 -1
- package/static/dist/assets/cancel-bsnfo36u.js +0 -1
- package/static/dist/assets/channel-dsbtk01y.js +0 -1
- package/static/dist/assets/classDiagram-ZZMXUADV-fvgcv24g.js +0 -1
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-fvgcv24g.js +0 -1
- package/static/dist/assets/flowDiagram-HODETNUW-jahfpiod.js +0 -1
- package/static/dist/assets/folder-plus-ivoglq98.js +0 -1
- package/static/dist/assets/index-73649w7o.js +0 -10
- package/static/dist/assets/index-lhf1fsdf.css +0 -2
- package/static/dist/assets/katex-cbdd1ohw.css +0 -1
- package/static/dist/assets/katex-pbij2oo6.js +0 -257
- package/static/dist/assets/login-lkhtzfcl.js +0 -1
- package/static/dist/assets/mermaid-HWGCJPDP-jx0q5dm2.js +0 -1
- package/static/dist/assets/onboarding-f67z0a2r.js +0 -1
- package/static/dist/assets/queryKeys-ed4n2egc.js +0 -9
- package/static/dist/assets/refresh-i9ya9dvl.js +0 -1
- package/static/dist/assets/sliders-horizontal-ddcs88av.js +0 -1
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-hlt6yphw.js +0 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-hd5t9tnt.js +0 -8
- package/static/dist/assets/toast-liparz2j.js +0 -56
|
@@ -17,8 +17,9 @@ from ..errors import InvalidParams
|
|
|
17
17
|
from ..home import profile_home_param, scoped_home
|
|
18
18
|
|
|
19
19
|
_ACTIONS = {"snapshot", "watch", "context_detail", "submit", "receipt", "cancel", "steer_queued", "replace_queued", "interrupt", "approval", "clarify",
|
|
20
|
-
"clarify_lock", "discard", "mutate", "export"}
|
|
21
|
-
|
|
20
|
+
"clarify_lock", "discard", "mutate", "export", "checkpoint_diff", "terminal_info", "shell_exec", "react"}
|
|
21
|
+
_OWNER_READ_MAX_BYTES = 4 * 1024 * 1024
|
|
22
|
+
_REACTION_EMOJIS = {"👍", "👎", "❤️", "😂", "😮", "🎉"}
|
|
22
23
|
# A restart's after-turn wait keeps the owner reachable only to finish its parked turn (the owner's
|
|
23
24
|
# RESTART_WAIT_WS_METHODS); new work waits for the next owner.
|
|
24
25
|
_RESTART_ACTIONS = {"snapshot", "watch", "receipt", "approval", "clarify", "clarify_lock", "interrupt"}
|
|
@@ -69,14 +70,40 @@ def _control_refusal(home: Path, sid: str) -> str | None:
|
|
|
69
70
|
return None
|
|
70
71
|
|
|
71
72
|
|
|
73
|
+
def _todo_state(value: object) -> dict | None:
|
|
74
|
+
if not isinstance(value, dict) or not isinstance(value.get("todos"), list):
|
|
75
|
+
return None
|
|
76
|
+
fields = {"id": (str, int, float), "text": (str,), "content": (str,), "title": (str,),
|
|
77
|
+
"status": (str,), "done": (bool,), "completed": (bool,)}
|
|
78
|
+
revision = value.get("revision", 0)
|
|
79
|
+
if type(revision) is not int or revision < 0:
|
|
80
|
+
return None
|
|
81
|
+
return {"todos": [{k: v for k, v in item.items() if k in fields and type(v) in fields[k]}
|
|
82
|
+
for item in value["todos"] if isinstance(item, dict)], "revision": revision}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _event(value: dict) -> dict:
|
|
86
|
+
if value.get("type") == "todo.updated":
|
|
87
|
+
return {**value, "payload": _todo_state(value.get("payload")) or {}}
|
|
88
|
+
return value
|
|
89
|
+
|
|
90
|
+
|
|
72
91
|
def _snapshot(value: dict) -> dict:
|
|
73
92
|
# Raw conversation rows contain private principal/admission metadata; the client needs
|
|
74
93
|
# display rows, not those bindings or the inventory's system prompt and credential hint.
|
|
75
94
|
message_keys = {"role", "content", "tool_calls", "tool_call_id", "name", "timestamp", "message_id", "row_id",
|
|
76
95
|
"reasoning", "reasoning_content", "display_kind", "finish_reason"}
|
|
77
96
|
info_keys = {"title", "model", "cwd", "source", "provider", "config_warning"}
|
|
78
|
-
|
|
79
|
-
|
|
97
|
+
rows = []
|
|
98
|
+
for row in value["messages"]:
|
|
99
|
+
metadata = row.get("display_metadata")
|
|
100
|
+
reactions = metadata.get("reactions", []) if isinstance(metadata, dict) else []
|
|
101
|
+
rows.append({**{k: v for k, v in row.items() if k in message_keys}, "reactions": [
|
|
102
|
+
{"author": reaction["author"], "emoji": reaction["emoji"]} for reaction in reactions
|
|
103
|
+
if isinstance(reaction, dict) and reaction.get("author") in ("user", "agent")
|
|
104
|
+
and isinstance(reaction.get("emoji"), str) and reaction["emoji"] in _REACTION_EMOJIS] if isinstance(reactions, list) else []})
|
|
105
|
+
return {**value, "messages": rows, "info": {k: v for k, v in value["info"].items() if k in info_keys},
|
|
106
|
+
**({"todo_state": _todo_state(value["todo_state"])} if "todo_state" in value else {})}
|
|
80
107
|
|
|
81
108
|
|
|
82
109
|
def _refused(state: str, reason: str) -> dict:
|
|
@@ -84,7 +111,7 @@ def _refused(state: str, reason: str) -> dict:
|
|
|
84
111
|
|
|
85
112
|
|
|
86
113
|
@asynccontextmanager
|
|
87
|
-
async def _connect(home: Path, endpoint):
|
|
114
|
+
async def _connect(home: Path, endpoint, *, subject: str | None = None):
|
|
88
115
|
from hermes_constants import get_hermes_home
|
|
89
116
|
from hermes_cli.gateway_client import GatewayClientError, connect_gateway
|
|
90
117
|
|
|
@@ -92,7 +119,7 @@ async def _connect(home: Path, endpoint):
|
|
|
92
119
|
# override is mandatory so its bootstrap ticket cannot fall back to the launch profile.
|
|
93
120
|
if Path(get_hermes_home()).resolve() != home:
|
|
94
121
|
raise GatewayClientError("profile_mismatch")
|
|
95
|
-
async with connect_gateway(endpoint) as client:
|
|
122
|
+
async with connect_gateway(endpoint, **({"subject": subject} if subject is not None else {})) as client:
|
|
96
123
|
websocket = client.websocket
|
|
97
124
|
# GatewayClient queues server requests as events. Drain continuously, including
|
|
98
125
|
# during an RPC, so unsupported sudo/secure inputs never wait for a Web renderer.
|
|
@@ -120,16 +147,20 @@ def _served(home: Path, endpoint, *, allow_verified: bool = False) -> bool:
|
|
|
120
147
|
return False
|
|
121
148
|
if not allow_verified:
|
|
122
149
|
return True
|
|
150
|
+
return not _verified_contract(endpoint, "served-local-policy-v1")
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _verified_contract(endpoint, name: str) -> bool:
|
|
123
154
|
# The private control socket reports the owner's boot revision, not the sidecar's
|
|
124
155
|
# checkout or a version label. The authenticated describe must still match below.
|
|
125
156
|
try:
|
|
126
157
|
record = json.loads((Path(__file__).resolve().parents[2] / "agent_support.json").read_text())
|
|
127
|
-
revisions = record["nativeContracts"][
|
|
128
|
-
return (
|
|
129
|
-
|
|
130
|
-
|
|
158
|
+
revisions = record["nativeContracts"][name]
|
|
159
|
+
return (isinstance(revisions, list)
|
|
160
|
+
and all(isinstance(sha, str) and re.fullmatch(r"[0-9a-f]{40}", sha) for sha in revisions)
|
|
161
|
+
and getattr(endpoint, "code_sha", None) in revisions)
|
|
131
162
|
except (OSError, ValueError, KeyError, TypeError):
|
|
132
|
-
return
|
|
163
|
+
return False
|
|
133
164
|
|
|
134
165
|
|
|
135
166
|
def _owner_refusal(described: dict, endpoint) -> dict | None:
|
|
@@ -145,12 +176,41 @@ def _owner_refusal(described: dict, endpoint) -> dict | None:
|
|
|
145
176
|
return None
|
|
146
177
|
|
|
147
178
|
|
|
179
|
+
def _checkpoint_read(value, *, no_baseline=False):
|
|
180
|
+
comparison = {"provenance": "native_owner", "baseline": "earliest_retained_project_checkpoint",
|
|
181
|
+
"available": False, "reason": None, "truncated": False}
|
|
182
|
+
result = {"diff": "", "stat": "", "comparison": comparison}
|
|
183
|
+
reason = None
|
|
184
|
+
if not isinstance(value, dict) or type(value.get("enabled")) is not bool:
|
|
185
|
+
reason = "invalid_payload"
|
|
186
|
+
elif not value["enabled"]:
|
|
187
|
+
reason = "checkpoints_disabled"
|
|
188
|
+
elif value.get("success") is not True:
|
|
189
|
+
reason = "checkpoint_unavailable"
|
|
190
|
+
elif (not isinstance(value.get("diff"), str) or not isinstance(value.get("stat"), str)
|
|
191
|
+
or type(value.get("diff_lines")) is not int or value["diff_lines"] < 0
|
|
192
|
+
or type(value.get("empty")) is not bool):
|
|
193
|
+
reason = "invalid_payload"
|
|
194
|
+
elif sum(len(json.dumps(value[key], ensure_ascii=False).encode("utf-8", "surrogatepass")) - 2
|
|
195
|
+
for key in ("stat", "diff")) > _OWNER_READ_MAX_BYTES:
|
|
196
|
+
reason = "payload_too_large"
|
|
197
|
+
elif no_baseline:
|
|
198
|
+
reason = "no_baseline"
|
|
199
|
+
else:
|
|
200
|
+
result.update(diff=value["diff"], stat=value["stat"])
|
|
201
|
+
comparison.update(available=True, truncated=value["diff_lines"] > len(value["diff"].splitlines()))
|
|
202
|
+
comparison["reason"] = reason
|
|
203
|
+
return result
|
|
204
|
+
|
|
205
|
+
|
|
148
206
|
async def _run(ctx, params: dict, home: Path, discovery) -> dict:
|
|
149
207
|
from hermes_cli.gateway_client import GatewayRPCError
|
|
150
208
|
|
|
151
209
|
endpoint = discovery.endpoint
|
|
152
210
|
if _served(home, endpoint, allow_verified=True):
|
|
153
211
|
return _refused("incompatible", "served_profile_sessions_unavailable")
|
|
212
|
+
if params["action"] in {"terminal_info", "shell_exec"} and not _verified_contract(endpoint, "captured-command-v1"):
|
|
213
|
+
return _refused("incompatible", "owner_shell_unsupported")
|
|
154
214
|
restarting = discovery.state != "ready"
|
|
155
215
|
if restarting and params["action"] not in _RESTART_ACTIONS:
|
|
156
216
|
return _refused("refused", "gateway_restarting")
|
|
@@ -170,10 +230,32 @@ async def _run(ctx, params: dict, home: Path, discovery) -> dict:
|
|
|
170
230
|
subscription = value["subscription_id"]
|
|
171
231
|
if reason := await asyncio.to_thread(_control_refusal, home, sid):
|
|
172
232
|
raise GatewayRPCError(reason)
|
|
173
|
-
|
|
233
|
+
gauge = None
|
|
234
|
+
if params["action"] in {"snapshot", "watch"}:
|
|
235
|
+
try:
|
|
236
|
+
stats = await client.rpc("session.stats", session_id=sid)
|
|
237
|
+
after = await client.rpc("session.resume", session_id=sid)
|
|
238
|
+
if all(value.get(k) == after.get(k) for k in ("session_id", "authority_epoch", "replay_epoch", "execution_generation")):
|
|
239
|
+
gauge = stats.get("plan_gauge")
|
|
240
|
+
except GatewayRPCError:
|
|
241
|
+
pass # older owners have no stats capability
|
|
242
|
+
return {**_snapshot(value), "plan_gauge": gauge,
|
|
243
|
+
"reactions_supported": _verified_contract(endpoint, "message-reactions-v1")}
|
|
174
244
|
|
|
175
245
|
receipt = lock = None
|
|
176
246
|
try:
|
|
247
|
+
if params["action"] == "checkpoint_diff":
|
|
248
|
+
# The owner resolves the physical target and frozen policy. Never resume or read host files.
|
|
249
|
+
value = await asyncio.wait_for(client.rpc("rollback.session_diff", session_id=sid, _timeout=25), timeout=30)
|
|
250
|
+
read = _checkpoint_read(value)
|
|
251
|
+
if read["comparison"]["available"] and value["empty"] and not value["diff"] and not value["stat"]:
|
|
252
|
+
listed = await asyncio.wait_for(client.rpc("rollback.list", session_id=sid, _timeout=3), timeout=3)
|
|
253
|
+
if (not isinstance(listed, dict) or listed.get("enabled") is not True
|
|
254
|
+
or not isinstance(listed.get("checkpoints"), list)):
|
|
255
|
+
read = _checkpoint_read(None)
|
|
256
|
+
elif not listed["checkpoints"]:
|
|
257
|
+
read = _checkpoint_read(value, no_baseline=True)
|
|
258
|
+
return {"state": "attached", "reason": None, "snapshot": None, "receipt": None, "checkpoint_diff": read}
|
|
177
259
|
if params["action"] == "export":
|
|
178
260
|
# No resume/subscription or mutation: the owner selects its current physical target.
|
|
179
261
|
fmt = "md" if params["format"] == "markdown" else params["format"]
|
|
@@ -183,14 +265,34 @@ async def _run(ctx, params: dict, home: Path, discovery) -> dict:
|
|
|
183
265
|
or not isinstance(value.get("filename"), str) or not 0 < len(value["filename"]) <= 255
|
|
184
266
|
or not isinstance(value.get("content"), str)):
|
|
185
267
|
return _refused("refused", "owner_export_invalid")
|
|
186
|
-
if len(value["content"]) >
|
|
268
|
+
if len(value["content"]) > _OWNER_READ_MAX_BYTES or len(value["content"].encode("utf-8")) > _OWNER_READ_MAX_BYTES:
|
|
187
269
|
return _refused("refused", "export_too_large")
|
|
188
270
|
return {"state": "attached", "reason": None, "snapshot": None, "receipt": None,
|
|
189
271
|
"export": {"format": params["format"], "filename": value["filename"], "content": value["content"]}}
|
|
190
272
|
snap = await snapshot()
|
|
191
273
|
action = params["action"]
|
|
192
274
|
common = {"session_id": sid}
|
|
193
|
-
if action == "
|
|
275
|
+
if action == "terminal_info":
|
|
276
|
+
return {"state": "attached", "reason": None, "snapshot": snap, "receipt": None}
|
|
277
|
+
if action == "shell_exec":
|
|
278
|
+
if (snap.get("session_id") != params["session_id"] or snap.get("authority_epoch") != params["authority_epoch"]
|
|
279
|
+
or snap.get("replay_epoch") != params["replay_epoch"]):
|
|
280
|
+
return _refused("refused", "owner_changed")
|
|
281
|
+
# The native owner alone authorizes the frozen backend/cwd and redacts before bounding.
|
|
282
|
+
# No second observation may discard a received result; no retry after an unknown ACK.
|
|
283
|
+
value = await client.rpc("shell.exec", **common, command=params["command"], _timeout=35)
|
|
284
|
+
if (not isinstance(value, dict) or not isinstance(value.get("stdout"), str)
|
|
285
|
+
or not isinstance(value.get("stderr"), str) or type(value.get("code")) is not int
|
|
286
|
+
or len(value["stdout"]) > 4000 or len(value["stderr"]) > 2000):
|
|
287
|
+
return _refused("refused", "owner_shell_invalid")
|
|
288
|
+
return {"state": "attached", "reason": None, "snapshot": None, "receipt": None, "shell": value}
|
|
289
|
+
if action == "react":
|
|
290
|
+
if not snap["reactions_supported"]:
|
|
291
|
+
return _refused("refused", "owner_reactions_unsupported")
|
|
292
|
+
if not any(row.get("row_id") == params["row_id"] and row.get("role") in {"user", "assistant"} for row in snap["messages"]):
|
|
293
|
+
return _refused("refused", "reaction_row_unavailable")
|
|
294
|
+
await client.rpc("message.react", **common, row_id=params["row_id"], emoji=params["emoji"], author="user")
|
|
295
|
+
elif action == "context_detail":
|
|
194
296
|
from ..context_detail import project, unavailable
|
|
195
297
|
# The authorized native read owns lookup and prompt interpretation. No local Agent is built.
|
|
196
298
|
try:
|
|
@@ -259,21 +361,27 @@ async def _run(ctx, params: dict, home: Path, discovery) -> dict:
|
|
|
259
361
|
if (event.get("type") in {"session.updated", "notification.show", "notification.clear"}
|
|
260
362
|
or event.get("execution_generation") == snap["execution_generation"]
|
|
261
363
|
or event.get("type") == "session.info" and event.get("payload", {}).get("execution_generation") == snap["execution_generation"]):
|
|
262
|
-
ctx.emit("event", event)
|
|
364
|
+
ctx.emit("event", _event(event))
|
|
263
365
|
sequence = replay["last_sequence"]
|
|
264
366
|
ctx.emit("replay_complete", {})
|
|
265
367
|
checked = asyncio.get_running_loop().time()
|
|
266
368
|
while not ctx.cancelled:
|
|
369
|
+
now = asyncio.get_running_loop().time()
|
|
370
|
+
if not restarting and now - checked >= 2:
|
|
371
|
+
checked = now
|
|
372
|
+
if restarting := await asyncio.to_thread(_restart_wait, home):
|
|
373
|
+
result["restarting"] = True
|
|
374
|
+
ctx.emit("restarting", {"restarting": True})
|
|
375
|
+
if not restarting and snap.get("reactions_supported"):
|
|
376
|
+
# The owner emits no reaction event. Read through this viewer's existing subscription,
|
|
377
|
+
# including while tokens keep the event queue busy.
|
|
378
|
+
current = await snapshot()
|
|
379
|
+
if [(r.get("row_id"), r.get("reactions")) for r in current["messages"] if r.get("reactions")] != [(r.get("row_id"), r.get("reactions")) for r in snap["messages"] if r.get("reactions")]:
|
|
380
|
+
ctx.emit("snapshot", {**result, "snapshot": current, "reaction_only": True})
|
|
381
|
+
snap = current
|
|
267
382
|
try:
|
|
268
383
|
frame = await asyncio.wait_for(events.get(), .25)
|
|
269
384
|
except TimeoutError:
|
|
270
|
-
now = asyncio.get_running_loop().time()
|
|
271
|
-
if not restarting and now - checked >= 2:
|
|
272
|
-
checked = now
|
|
273
|
-
# One-way for this owner: its next state is exit, which ends the watch.
|
|
274
|
-
if restarting := await asyncio.to_thread(_restart_wait, home):
|
|
275
|
-
result["restarting"] = True
|
|
276
|
-
ctx.emit("restarting", {"restarting": True})
|
|
277
385
|
continue
|
|
278
386
|
if isinstance(frame, Exception):
|
|
279
387
|
raise frame
|
|
@@ -307,9 +415,11 @@ async def _run(ctx, params: dict, home: Path, discovery) -> dict:
|
|
|
307
415
|
if event.get("seq", 0) <= sequence:
|
|
308
416
|
continue
|
|
309
417
|
sequence = event["seq"]
|
|
310
|
-
ctx.emit("event", event)
|
|
418
|
+
ctx.emit("event", _event(event))
|
|
311
419
|
return result
|
|
312
420
|
except GatewayRPCError as exc:
|
|
421
|
+
if params["action"] == "react" and str(exc) not in {"invalid_params", "not_found", "permission_denied", "profile_mismatch"}:
|
|
422
|
+
return _refused("unavailable", "reaction_outcome_unknown")
|
|
313
423
|
if params["action"] == "export":
|
|
314
424
|
return _refused("refused", "export_too_large" if str(exc) == "export_too_large" else "owner_export_unsupported")
|
|
315
425
|
return {**_refused("incompatible" if str(exc) in _CONTROL_REFUSALS else "refused", str(exc)), "receipt": receipt}
|
|
@@ -330,9 +440,18 @@ def session(ctx, params: dict) -> dict:
|
|
|
330
440
|
if action not in _ACTIONS or not isinstance(sid, str) or not sid.strip() or sid != sid.strip():
|
|
331
441
|
raise InvalidParams("session_id and supported action are required")
|
|
332
442
|
params = {**params, "action": action}
|
|
443
|
+
if action == "react" and (set(params) - {"profile_home", "session_id", "action", "row_id", "emoji"}
|
|
444
|
+
or type(params.get("row_id")) is not int or params["row_id"] <= 0
|
|
445
|
+
or "emoji" not in params or (params["emoji"] is not None and (not isinstance(params["emoji"], str) or params["emoji"] not in _REACTION_EMOJIS))):
|
|
446
|
+
raise InvalidParams("react requires a visible row and an allowed emoji")
|
|
333
447
|
if action == "export" and (params.get("format") not in ("json", "markdown", "html")
|
|
334
448
|
or not isinstance(params.get("redact"), bool)):
|
|
335
449
|
raise InvalidParams("export requires an allowed format and redaction policy")
|
|
450
|
+
if action == "shell_exec" and (not isinstance(params.get("command"), str)
|
|
451
|
+
or not params["command"].strip() or len(params["command"]) > 8192
|
|
452
|
+
or type(params.get("authority_epoch")) is not int or params["authority_epoch"] < 0
|
|
453
|
+
or not isinstance(params.get("replay_epoch"), str) or not params["replay_epoch"]):
|
|
454
|
+
raise InvalidParams("captured command requires command and owner identity")
|
|
336
455
|
required = {
|
|
337
456
|
"submit": ("request_id", "text"), "receipt": ("admission_id",), "cancel": ("admission_id",),
|
|
338
457
|
"steer_queued": ("admission_id",), "replace_queued": ("admission_id", "submission_id", "text"),
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Closed native room operations. Attach only, with the Web principal's own grant."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import asyncio
|
|
5
|
+
import json
|
|
6
|
+
from contextlib import suppress
|
|
7
|
+
|
|
8
|
+
from ..errors import InvalidParams
|
|
9
|
+
from ..home import profile_home_param
|
|
10
|
+
from .gateway_sessions import _connect, _owner_refusal, _with_owner
|
|
11
|
+
|
|
12
|
+
_FIELDS = {
|
|
13
|
+
"capabilities": set(), "list": {"offset", "limit"}, "state": {"room_id", "include_disbanded"},
|
|
14
|
+
"log": {"room_id", "since_seq", "limit"}, "create": {"room_id", "name", "members"},
|
|
15
|
+
"rename": {"room_id", "event_id", "name"}, "disband": {"room_id", "cancel_id"},
|
|
16
|
+
"send": {"room_id", "event_id", "payload"}, "stop": {"room_id", "cancel_id"},
|
|
17
|
+
"retry": {"room_id", "member_id", "task_id", "execution_generation"},
|
|
18
|
+
"discard": {"room_id", "member_id", "task_id", "execution_generation"},
|
|
19
|
+
"approve": {"room_id", "member_id", "task_id", "execution_generation", "request_id", "choice"},
|
|
20
|
+
"attachment.upload": {"room_id", "upload_id", "kind", "name", "mime", "data_base64"},
|
|
21
|
+
"attachment.download": {"room_id", "event_id", "attachment_id"},
|
|
22
|
+
}
|
|
23
|
+
_READS = {"capabilities", "list", "state", "log", "attachment.download"}
|
|
24
|
+
_REFUSALS = {"permission_denied", "profile_mismatch", "invalid_params", "stale_generation", "unknown_execution",
|
|
25
|
+
"unsupported_operation", "session_busy", "runtime_coordination_required"}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _result(state, reason=None, value=None):
|
|
29
|
+
return {"state": state, "reason": reason, "value": value}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
async def _run(home, subject, operation, discovery, expected_room=None):
|
|
33
|
+
from hermes_cli.gateway_client import GatewayRPCError
|
|
34
|
+
action = operation["action"]
|
|
35
|
+
method = "groups." + action
|
|
36
|
+
dispatched = False
|
|
37
|
+
try:
|
|
38
|
+
if discovery.state != "ready":
|
|
39
|
+
return _result("unavailable", "stopped")
|
|
40
|
+
principal = 'auth:v1:' + json.dumps(['talaria', '', subject], separators=(',', ':'))
|
|
41
|
+
async with _connect(home, discovery.endpoint, subject=principal) as (client, _):
|
|
42
|
+
# A native 15 MB attachment expands to 20 MB on the wire. Keep the shared native client,
|
|
43
|
+
# with a bounded receiver large enough for the owner's supported attachment envelope.
|
|
44
|
+
client.websocket.protocol.max_size = 24 * 1024 * 1024
|
|
45
|
+
described = await client.rpc("runtime.describe")
|
|
46
|
+
if _owner_refusal(described, discovery.endpoint):
|
|
47
|
+
return _result("unavailable", "unsupported")
|
|
48
|
+
caps = await client.rpc("groups.capabilities")
|
|
49
|
+
if (not isinstance(caps, dict) or type(caps.get("driver")) is not bool
|
|
50
|
+
or not isinstance(caps.get("methods"), list) or method not in caps["methods"]):
|
|
51
|
+
return _result("unavailable", "unsupported")
|
|
52
|
+
if not caps["driver"]:
|
|
53
|
+
return _result("unavailable", "driver_inactive")
|
|
54
|
+
if action == "capabilities":
|
|
55
|
+
return _result("available", value=caps)
|
|
56
|
+
if expected_room is not None:
|
|
57
|
+
current = await client.rpc("groups.state", room_id=expected_room["room_id"], include_disbanded=True)
|
|
58
|
+
room = current.get("room", {})
|
|
59
|
+
if (room.get("room_id") != expected_room["room_id"]
|
|
60
|
+
or room.get("authority_gateway_id") != expected_room["gateway_id"]
|
|
61
|
+
or room.get("authority_epoch") != expected_room["authority_epoch"]):
|
|
62
|
+
return _result("refused", "refused")
|
|
63
|
+
if action == "send":
|
|
64
|
+
operation = {**operation, "payload": {**operation["payload"], "thread_id": operation["event_id"]}}
|
|
65
|
+
dispatched = True
|
|
66
|
+
value = await client.rpc(method, **{k: v for k, v in operation.items() if k != "action"}, _timeout=25)
|
|
67
|
+
if not isinstance(value, dict) or len(json.dumps(value).encode()) > 24 * 1024 * 1024:
|
|
68
|
+
return _result("unknown" if action not in _READS else "unavailable", "unknown")
|
|
69
|
+
return _result("available", value=value)
|
|
70
|
+
except GatewayRPCError as exc:
|
|
71
|
+
reason = str(exc)
|
|
72
|
+
# Native errors are data, never public diagnostics. Only fixed refusals prove non-admission.
|
|
73
|
+
if reason in _REFUSALS:
|
|
74
|
+
return _result("refused", "refused")
|
|
75
|
+
if "method" in reason.lower() and not dispatched:
|
|
76
|
+
return _result("unavailable", "unsupported")
|
|
77
|
+
return _result("unknown" if dispatched and action not in _READS else "unavailable", "unknown")
|
|
78
|
+
except Exception: # noqa: BLE001 - a transport loss never authorizes automatic replay
|
|
79
|
+
return _result("unknown" if dispatched and action not in _READS else "unavailable", "unknown")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
async def _read(ctx, coroutine):
|
|
83
|
+
task = asyncio.create_task(coroutine)
|
|
84
|
+
try:
|
|
85
|
+
while not task.done():
|
|
86
|
+
await asyncio.wait({task}, timeout=.1)
|
|
87
|
+
ctx.check_cancelled()
|
|
88
|
+
return await task
|
|
89
|
+
finally:
|
|
90
|
+
if not task.done():
|
|
91
|
+
task.cancel()
|
|
92
|
+
with suppress(asyncio.CancelledError):
|
|
93
|
+
await task
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def groups(ctx, params):
|
|
97
|
+
operation, subject = params.get("operation"), params.get("subject")
|
|
98
|
+
if (set(params) - {"profile_home", "subject", "operation", "expected_room"}
|
|
99
|
+
or not {"profile_home", "subject", "operation"} <= set(params)
|
|
100
|
+
or not isinstance(subject, str) or not 0 < len(subject) <= 128
|
|
101
|
+
or not isinstance(operation, dict) or operation.get("action") not in _FIELDS
|
|
102
|
+
or set(operation) - (_FIELDS[operation["action"]] | {"action"})):
|
|
103
|
+
raise InvalidParams("invalid room operation")
|
|
104
|
+
expected = params.get("expected_room")
|
|
105
|
+
if expected is not None and (not isinstance(expected, dict)
|
|
106
|
+
or set(expected) != {"room_id", "gateway_id", "authority_epoch"}
|
|
107
|
+
or expected.get("room_id") != operation.get("room_id")
|
|
108
|
+
or not isinstance(expected.get("gateway_id"), str)
|
|
109
|
+
or type(expected.get("authority_epoch")) is not int):
|
|
110
|
+
raise InvalidParams("invalid room authority fence")
|
|
111
|
+
home = profile_home_param(params).resolve()
|
|
112
|
+
ctx.check_cancelled()
|
|
113
|
+
def run(discovery):
|
|
114
|
+
coroutine = _run(home, subject, operation, discovery, expected)
|
|
115
|
+
return _read(ctx, coroutine) if operation["action"] in _READS else coroutine
|
|
116
|
+
result = _with_owner(home, run)
|
|
117
|
+
if "value" not in result:
|
|
118
|
+
return _result("unavailable", "stopped" if result["state"] == "stopped" else "unsupported"
|
|
119
|
+
if result["state"] == "incompatible" else "unavailable")
|
|
120
|
+
return result
|
|
@@ -10,8 +10,11 @@ from __future__ import annotations
|
|
|
10
10
|
import functools
|
|
11
11
|
import json
|
|
12
12
|
import secrets
|
|
13
|
+
import sqlite3
|
|
13
14
|
import time
|
|
15
|
+
from contextlib import closing
|
|
14
16
|
from dataclasses import asdict, is_dataclass
|
|
17
|
+
from pathlib import Path
|
|
15
18
|
from typing import Any, NamedTuple
|
|
16
19
|
|
|
17
20
|
from ..errors import InvalidParams, RpcError
|
|
@@ -463,6 +466,50 @@ def _bool(params: dict, key: str, default: bool = False) -> bool:
|
|
|
463
466
|
|
|
464
467
|
# ── payload builders ──────────────────────────────────────────────────────
|
|
465
468
|
|
|
469
|
+
def inspection_payload(params: dict) -> dict:
|
|
470
|
+
"""Read only the selected profile's live boards, with shared global row/edge budgets."""
|
|
471
|
+
kb = _kb()
|
|
472
|
+
selected = _board(params)
|
|
473
|
+
boards = sorted((b for b in kb.list_boards(include_archived=False) if not b.get("archived")), key=lambda b: b["slug"])
|
|
474
|
+
if selected is not None:
|
|
475
|
+
boards = [b for b in boards if b["slug"] == selected]
|
|
476
|
+
if not boards:
|
|
477
|
+
raise NotFound("board not found")
|
|
478
|
+
nodes, edges = [], []
|
|
479
|
+
truncated = len(boards) > 32
|
|
480
|
+
for meta in boards[:32]:
|
|
481
|
+
slug = _normalize_existing_slug(meta["slug"])
|
|
482
|
+
if len(nodes) >= 500:
|
|
483
|
+
truncated = True
|
|
484
|
+
break
|
|
485
|
+
# An inspection must never initialize a missing database or run migrations.
|
|
486
|
+
uri = Path(kb.kanban_db_path(slug)).resolve().as_uri() + "?mode=ro"
|
|
487
|
+
with closing(sqlite3.connect(uri, uri=True)) as conn:
|
|
488
|
+
conn.row_factory = sqlite3.Row
|
|
489
|
+
conn.execute("BEGIN")
|
|
490
|
+
where = "" if _bool(params, "include_archived") else "WHERE t.status != 'archived'"
|
|
491
|
+
rows = conn.execute(
|
|
492
|
+
f"SELECT t.*, r.started_at AS current_run_started_at FROM tasks t LEFT JOIN task_runs r ON r.id = t.current_run_id {where} ORDER BY t.id LIMIT ?",
|
|
493
|
+
(501 - len(nodes),),
|
|
494
|
+
).fetchall()
|
|
495
|
+
remaining = 500 - len(nodes)
|
|
496
|
+
truncated = truncated or len(rows) > remaining
|
|
497
|
+
rows = rows[:remaining]
|
|
498
|
+
ids = [row["id"] for row in rows]
|
|
499
|
+
for row in rows:
|
|
500
|
+
task = _task_dict(dict(row), conn)
|
|
501
|
+
nodes.append({"key": f"{slug}/{row['id']}", "board": slug, "board_name": meta.get("name") or slug, "task": task})
|
|
502
|
+
if ids:
|
|
503
|
+
placeholders = ",".join("?" for _ in ids)
|
|
504
|
+
links = conn.execute(
|
|
505
|
+
f"SELECT parent_id, child_id FROM task_links WHERE parent_id IN ({placeholders}) AND child_id IN ({placeholders}) ORDER BY parent_id, child_id LIMIT ?",
|
|
506
|
+
[*ids, *ids, 2001 - len(edges)],
|
|
507
|
+
).fetchall()
|
|
508
|
+
remaining_edges = 2000 - len(edges)
|
|
509
|
+
truncated = truncated or len(links) > remaining_edges
|
|
510
|
+
edges.extend({"source": f"{slug}/{r['parent_id']}", "target": f"{slug}/{r['child_id']}"} for r in links[:remaining_edges])
|
|
511
|
+
return {"nodes": nodes, "edges": edges, "generated_at": time.time(), "truncated": truncated, "read_only": False}
|
|
512
|
+
|
|
466
513
|
def board_payload(params: dict) -> dict:
|
|
467
514
|
board = _board(params)
|
|
468
515
|
kb = _kb()
|
|
@@ -852,6 +899,7 @@ def normalize_board_payload(params: dict) -> dict:
|
|
|
852
899
|
|
|
853
900
|
|
|
854
901
|
METHODS = {
|
|
902
|
+
"kanban.inspection": inspection_payload,
|
|
855
903
|
"kanban.normalize_board": normalize_board_payload,
|
|
856
904
|
"kanban.board": board_payload,
|
|
857
905
|
"kanban.boards": list_boards_payload,
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Fixed devkit REST adapter through the existing admitted Agent plugin host."""
|
|
2
|
+
import base64
|
|
3
|
+
import json
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
from .. import home as homes
|
|
7
|
+
from ..errors import InvalidParams, RpcError
|
|
8
|
+
from . import dashboard_plugins as host
|
|
9
|
+
|
|
10
|
+
MAX_BYTES = 256 * 1024
|
|
11
|
+
_NAME = re.compile(r'[A-Za-z0-9][A-Za-z0-9._-]{0,127}')
|
|
12
|
+
_DIR = re.compile(r'[A-Za-z0-9_-]{1,96}')
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _text_size(value):
|
|
16
|
+
try:
|
|
17
|
+
return len(value.encode('utf-8')) if isinstance(value, str) else -1
|
|
18
|
+
except UnicodeError:
|
|
19
|
+
return -1
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _bad():
|
|
23
|
+
raise RpcError('Managed buffer response is invalid or too large', condition='buffer_invalid')
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _json(ctx, plugin, home, method, path, content=None):
|
|
27
|
+
# Recheck admission for each native call, including the PUT after inventory.
|
|
28
|
+
current = host._named(home, 'devkit', require_panel=False)
|
|
29
|
+
if current is None or current['_dir'] != plugin['_dir'] or current.get('_api_file') != plugin.get('_api_file'):
|
|
30
|
+
raise RpcError('Managed buffer backend changed or was disabled', condition='buffer_unavailable')
|
|
31
|
+
body = b'' if content is None else json.dumps({'content': content}, ensure_ascii=False).encode()
|
|
32
|
+
result = host._request(ctx, plugin, dict(profile_home=str(home), authenticated=True, method=method, path=path, query='',
|
|
33
|
+
headers={'content-type': 'application/json'}, body=base64.b64encode(body).decode()), require_panel=False)
|
|
34
|
+
if result['status'] != 200:
|
|
35
|
+
# A PUT may have committed before an error or malformed acknowledgement. Never replay it here.
|
|
36
|
+
raise RpcError('Managed buffer unavailable' if method == 'GET' else 'Save outcome is unknown; reload before retrying',
|
|
37
|
+
condition='buffer_unavailable' if method == 'GET' else 'buffer_save_unconfirmed')
|
|
38
|
+
try:
|
|
39
|
+
value = json.loads(base64.b64decode(result['body']))
|
|
40
|
+
except (ValueError, UnicodeDecodeError):
|
|
41
|
+
_bad()
|
|
42
|
+
if not isinstance(value, dict):
|
|
43
|
+
_bad()
|
|
44
|
+
return value
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def run(ctx, params):
|
|
48
|
+
action, sid = params.get('action'), params.get('session_id')
|
|
49
|
+
if action not in ('list', 'read', 'save') or not isinstance(sid, str) or not 0 < len(sid) <= 200:
|
|
50
|
+
raise InvalidParams('Invalid managed buffer request')
|
|
51
|
+
name = params.get('name')
|
|
52
|
+
if action != 'list' and (not isinstance(name, str) or not _NAME.fullmatch(name)):
|
|
53
|
+
raise InvalidParams('Invalid managed buffer name')
|
|
54
|
+
content = params.get('content')
|
|
55
|
+
if action == 'save' and (not 0 <= _text_size(content) <= MAX_BYTES):
|
|
56
|
+
raise InvalidParams('Managed buffer content is too large or is not text')
|
|
57
|
+
home = homes.profile_home_param(params)
|
|
58
|
+
with homes.scoped_home(home, load_plugins=False):
|
|
59
|
+
plugin = host._named(home, 'devkit', require_panel=False) if host._supported() else None
|
|
60
|
+
result = {'supported': plugin is not None, 'buffers': [], 'write_semantics': 'unconditional'}
|
|
61
|
+
if plugin is None:
|
|
62
|
+
if action != 'list':
|
|
63
|
+
raise RpcError('Enable and approve the devkit backend in Plugins', condition='buffer_unavailable')
|
|
64
|
+
return result
|
|
65
|
+
rows = _json(ctx, plugin, home, 'GET', '/sessions').get('sessions')
|
|
66
|
+
if not isinstance(rows, list) or len(rows) > 2000 or any(not isinstance(r, dict) for r in rows):
|
|
67
|
+
_bad()
|
|
68
|
+
matches = [r for r in rows if r.get('session_id') == sid]
|
|
69
|
+
if len(matches) > 1:
|
|
70
|
+
_bad()
|
|
71
|
+
row = matches[0] if matches else None
|
|
72
|
+
if row is None:
|
|
73
|
+
if action != 'list':
|
|
74
|
+
raise RpcError('Managed buffer no longer exists', condition='buffer_unavailable')
|
|
75
|
+
return result
|
|
76
|
+
directory, buffers = row.get('dir'), row.get('buffers')
|
|
77
|
+
if not isinstance(directory, str) or not _DIR.fullmatch(directory) or not isinstance(buffers, list) or len(buffers) > 200:
|
|
78
|
+
_bad()
|
|
79
|
+
seen = set()
|
|
80
|
+
for buf in buffers:
|
|
81
|
+
if not isinstance(buf, dict):
|
|
82
|
+
_bad()
|
|
83
|
+
key, size = buf.get('name'), buf.get('bytes')
|
|
84
|
+
if not isinstance(key, str) or not _NAME.fullmatch(key) or key in seen or type(size) is not int or not 0 <= size <= MAX_BYTES:
|
|
85
|
+
_bad()
|
|
86
|
+
seen.add(key)
|
|
87
|
+
result['buffers'].append({'name': key, 'size': size, 'read_only': row.get('exists') is not True})
|
|
88
|
+
if action == 'list':
|
|
89
|
+
return result
|
|
90
|
+
selected = next((b for b in result['buffers'] if b['name'] == name), None)
|
|
91
|
+
if selected is None:
|
|
92
|
+
raise RpcError('Managed buffer no longer exists', condition='buffer_unavailable')
|
|
93
|
+
if action == 'save' and selected['read_only']:
|
|
94
|
+
raise RpcError('Managed session is deleted or cannot be verified', condition='buffer_read_only')
|
|
95
|
+
value = _json(ctx, plugin, home, 'PUT' if action == 'save' else 'GET', f'/sessions/{directory}/buffers/{name}', content if action == 'save' else None)
|
|
96
|
+
if action == 'read':
|
|
97
|
+
text = value.get('content')
|
|
98
|
+
if value.get('session_id') != sid or value.get('name') != name or not 0 <= _text_size(text) <= MAX_BYTES:
|
|
99
|
+
_bad()
|
|
100
|
+
result.update(content=text, size=_text_size(text), read_only=selected['read_only'])
|
|
101
|
+
else:
|
|
102
|
+
# Native PUT acknowledgements may omit session_id, but an explicit identity must match.
|
|
103
|
+
if value.get('session_id', sid) != sid or value.get('name') != name or type(value.get('bytes')) is not int or not 0 <= value['bytes'] <= MAX_BYTES:
|
|
104
|
+
raise RpcError('Save outcome is unknown; reload before retrying', condition='buffer_save_unconfirmed')
|
|
105
|
+
result.update(size=value['bytes'], read_only=False)
|
|
106
|
+
return result
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def register(registry):
|
|
110
|
+
registry.method('plugins.buffers')(run)
|