@maudecode/talaria-web 1.16.1 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_release.json +8 -8
- package/dist/api/auth-raw.d.ts +1 -1
- package/dist/api/auth-raw.js +4 -4
- package/dist/api/auth-raw.js.map +1 -1
- package/dist/api/automation-raw.d.ts +6 -0
- package/dist/api/automation-raw.js +59 -9
- package/dist/api/automation-raw.js.map +1 -1
- package/dist/api/automation-router.d.ts +163 -10
- package/dist/api/automation-router.js +25 -8
- package/dist/api/automation-router.js.map +1 -1
- package/dist/api/chat-router.d.ts +700 -15
- package/dist/api/chat-router.js +74 -55
- package/dist/api/chat-router.js.map +1 -1
- package/dist/api/git-router.d.ts +47 -2
- package/dist/api/git-router.js +31 -22
- package/dist/api/git-router.js.map +1 -1
- package/dist/api/raw-routes.d.ts +14 -0
- package/dist/api/raw-routes.js +277 -36
- package/dist/api/raw-routes.js.map +1 -1
- package/dist/api/router.d.ts +10 -0
- package/dist/api/router.js +10 -8
- package/dist/api/router.js.map +1 -1
- package/dist/api/sessions-router.d.ts +1710 -73
- package/dist/api/sessions-router.js +164 -10
- package/dist/api/sessions-router.js.map +1 -1
- package/dist/api/settings-router.d.ts +132 -2
- package/dist/api/settings-router.js +15 -5
- package/dist/api/settings-router.js.map +1 -1
- package/dist/api/sse-routes.d.ts +16 -3
- package/dist/api/sse-routes.js +194 -67
- package/dist/api/sse-routes.js.map +1 -1
- package/dist/api/tools-raw.js +12 -25
- package/dist/api/tools-raw.js.map +1 -1
- package/dist/api/tools-router.d.ts +425 -19
- package/dist/api/tools-router.js +157 -26
- package/dist/api/tools-router.js.map +1 -1
- package/dist/app.d.ts +0 -9377
- package/dist/app.js +42 -17
- package/dist/app.js.map +1 -1
- package/dist/auth/gate.js +8 -2
- package/dist/auth/gate.js.map +1 -1
- package/dist/auth/oidc.d.ts +5 -2
- package/dist/auth/oidc.js +8 -7
- package/dist/auth/oidc.js.map +1 -1
- package/dist/bin/talaria-web.js +1 -1
- package/dist/bin/talaria-web.js.map +1 -1
- package/dist/cli/dotenv.js +1 -0
- package/dist/cli/dotenv.js.map +1 -1
- package/dist/config/agent-config.d.ts +2 -0
- package/dist/config/agent-config.js +12 -2
- package/dist/config/agent-config.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.js +5 -1
- package/dist/config.js.map +1 -1
- package/dist/http/context.d.ts +20 -3
- package/dist/http/context.js +41 -8
- package/dist/http/context.js.map +1 -1
- package/dist/http/csp.d.ts +2 -1
- package/dist/http/csp.js +3 -2
- package/dist/http/csp.js.map +1 -1
- package/dist/onboarding.js +4 -0
- package/dist/onboarding.js.map +1 -1
- package/dist/profiles/profiles.d.ts +10 -0
- package/dist/profiles/profiles.js +40 -2
- package/dist/profiles/profiles.js.map +1 -1
- package/dist/providers/catalog.d.ts +101 -8
- package/dist/providers/catalog.js +463 -123
- package/dist/providers/catalog.js.map +1 -1
- package/dist/raster-image.d.ts +7 -0
- package/dist/raster-image.js +267 -0
- package/dist/raster-image.js.map +1 -0
- package/dist/redact.d.ts +1 -0
- package/dist/redact.js +354 -44
- package/dist/redact.js.map +1 -1
- package/dist/runtime.js +111 -27
- package/dist/runtime.js.map +1 -1
- package/dist/server.js +5 -0
- package/dist/server.js.map +1 -1
- package/dist/sessions/anchor.d.ts +2 -0
- package/dist/sessions/anchor.js +8 -3
- package/dist/sessions/anchor.js.map +1 -1
- package/dist/sessions/background-tasks.d.ts +2 -9
- package/dist/sessions/background-tasks.js +2 -12
- package/dist/sessions/background-tasks.js.map +1 -1
- package/dist/sessions/claude-code.d.ts +19 -0
- package/dist/sessions/claude-code.js +250 -0
- package/dist/sessions/claude-code.js.map +1 -0
- package/dist/sessions/cli-sessions.d.ts +15 -4
- package/dist/sessions/cli-sessions.js +120 -29
- package/dist/sessions/cli-sessions.js.map +1 -1
- package/dist/sessions/completions.d.ts +61 -9
- package/dist/sessions/completions.js +282 -34
- package/dist/sessions/completions.js.map +1 -1
- package/dist/sessions/compress.d.ts +14 -1
- package/dist/sessions/compress.js +52 -11
- package/dist/sessions/compress.js.map +1 -1
- package/dist/sessions/export.d.ts +5 -0
- package/dist/sessions/export.js +30 -0
- package/dist/sessions/export.js.map +1 -1
- package/dist/sessions/gateway-watcher.d.ts +7 -3
- package/dist/sessions/gateway-watcher.js +31 -19
- package/dist/sessions/gateway-watcher.js.map +1 -1
- package/dist/sessions/handoff.d.ts +32 -0
- package/dist/sessions/handoff.js +114 -0
- package/dist/sessions/handoff.js.map +1 -0
- package/dist/sessions/journal.d.ts +8 -0
- package/dist/sessions/journal.js +44 -10
- package/dist/sessions/journal.js.map +1 -1
- package/dist/sessions/list.d.ts +13 -3
- package/dist/sessions/list.js +46 -15
- package/dist/sessions/list.js.map +1 -1
- package/dist/sessions/merge.d.ts +53 -5
- package/dist/sessions/merge.js +219 -23
- package/dist/sessions/merge.js.map +1 -1
- package/dist/sessions/recovery.d.ts +22 -0
- package/dist/sessions/recovery.js +506 -0
- package/dist/sessions/recovery.js.map +1 -0
- package/dist/sessions/relay.d.ts +21 -2
- package/dist/sessions/relay.js +156 -11
- package/dist/sessions/relay.js.map +1 -1
- package/dist/sessions/service.d.ts +152 -8
- package/dist/sessions/service.js +640 -72
- package/dist/sessions/service.js.map +1 -1
- package/dist/sessions/session.d.ts +17 -1
- package/dist/sessions/session.js +63 -4
- package/dist/sessions/session.js.map +1 -1
- package/dist/sessions/state-db-worker.d.ts +1 -0
- package/dist/sessions/state-db-worker.js +33 -0
- package/dist/sessions/state-db-worker.js.map +1 -0
- package/dist/sessions/state-db.d.ts +87 -4
- package/dist/sessions/state-db.js +417 -8
- package/dist/sessions/state-db.js.map +1 -1
- package/dist/sessions/store.d.ts +7 -0
- package/dist/sessions/store.js +36 -22
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/streams.d.ts +10 -0
- package/dist/sessions/streams.js +14 -3
- package/dist/sessions/streams.js.map +1 -1
- package/dist/sessions/titles.d.ts +2 -0
- package/dist/sessions/titles.js +4 -0
- package/dist/sessions/titles.js.map +1 -1
- package/dist/sessions/todo.d.ts +6 -1
- package/dist/sessions/todo.js +11 -4
- package/dist/sessions/todo.js.map +1 -1
- package/dist/sessions/tool-display.d.ts +12 -1
- package/dist/sessions/tool-display.js +50 -1
- package/dist/sessions/tool-display.js.map +1 -1
- package/dist/sessions/turn-context.d.ts +1 -1
- package/dist/sessions/turn-context.js +11 -7
- package/dist/sessions/turn-context.js.map +1 -1
- package/dist/sessions/turn.d.ts +67 -11
- package/dist/sessions/turn.js +413 -111
- package/dist/sessions/turn.js.map +1 -1
- package/dist/sessions/wakeup-pause.d.ts +59 -0
- package/dist/sessions/wakeup-pause.js +181 -0
- package/dist/sessions/wakeup-pause.js.map +1 -0
- package/dist/settings.d.ts +20 -2
- package/dist/settings.js +45 -9
- package/dist/settings.js.map +1 -1
- package/dist/sidecar/fake.d.ts +4 -4
- package/dist/sidecar/fake.js +5 -2
- package/dist/sidecar/fake.js.map +1 -1
- package/dist/spa.js +2 -1
- package/dist/spa.js.map +1 -1
- package/dist/startup.d.ts +13 -5
- package/dist/startup.js +48 -0
- package/dist/startup.js.map +1 -1
- package/dist/test/harness.js +2 -0
- package/dist/test/harness.js.map +1 -1
- package/dist/test/ts-worker-hooks.d.ts +1 -0
- package/dist/test/ts-worker-hooks.js +24 -0
- package/dist/test/ts-worker-hooks.js.map +1 -0
- package/dist/text/diff.d.ts +9 -14
- package/dist/text/diff.js +74 -108
- package/dist/text/diff.js.map +1 -1
- package/dist/tools/crons.js +45 -8
- package/dist/tools/crons.js.map +1 -1
- package/dist/tools/dashboard-plugins.d.ts +53 -0
- package/dist/tools/dashboard-plugins.js +188 -0
- package/dist/tools/dashboard-plugins.js.map +1 -0
- package/dist/tools/extensions.d.ts +13 -1
- package/dist/tools/extensions.js +132 -60
- package/dist/tools/extensions.js.map +1 -1
- package/dist/tools/health.d.ts +32 -0
- package/dist/tools/health.js +111 -19
- package/dist/tools/health.js.map +1 -1
- package/dist/tools/insights.d.ts +8 -2
- package/dist/tools/insights.js +18 -2
- package/dist/tools/insights.js.map +1 -1
- package/dist/tools/kanban.d.ts +41 -1
- package/dist/tools/kanban.js +113 -2
- package/dist/tools/kanban.js.map +1 -1
- package/dist/tools/mcp.d.ts +15 -1
- package/dist/tools/mcp.js +99 -5
- package/dist/tools/mcp.js.map +1 -1
- package/dist/tools/project-os.d.ts +11 -0
- package/dist/tools/project-os.js +267 -0
- package/dist/tools/project-os.js.map +1 -0
- package/dist/tools/skills.d.ts +5 -1
- package/dist/tools/skills.js +5 -3
- package/dist/tools/skills.js.map +1 -1
- package/dist/tools/terminal.d.ts +1 -0
- package/dist/tools/terminal.js +1 -1
- package/dist/tools/terminal.js.map +1 -1
- package/dist/tools/updates.d.ts +33 -8
- package/dist/tools/updates.js +236 -57
- package/dist/tools/updates.js.map +1 -1
- package/dist/tools/wiki.d.ts +68 -0
- package/dist/tools/wiki.js +413 -0
- package/dist/tools/wiki.js.map +1 -0
- package/dist/workspace/escape.d.ts +61 -0
- package/dist/workspace/escape.js +149 -0
- package/dist/workspace/escape.js.map +1 -0
- package/dist/workspace/extract.d.ts +22 -0
- package/dist/workspace/extract.js +308 -0
- package/dist/workspace/extract.js.map +1 -0
- package/dist/workspace/fs.d.ts +19 -3
- package/dist/workspace/fs.js +96 -23
- package/dist/workspace/fs.js.map +1 -1
- package/dist/workspace/git.d.ts +18 -0
- package/dist/workspace/git.js +38 -25
- package/dist/workspace/git.js.map +1 -1
- package/dist/workspace/media.d.ts +4 -4
- package/dist/workspace/media.js +18 -45
- package/dist/workspace/media.js.map +1 -1
- package/dist/workspace/preview.d.ts +28 -0
- package/dist/workspace/preview.js +110 -0
- package/dist/workspace/preview.js.map +1 -0
- package/dist/workspace/rollback.d.ts +36 -21
- package/dist/workspace/rollback.js +179 -270
- package/dist/workspace/rollback.js.map +1 -1
- package/dist/workspace/unzip.js +4 -1
- package/dist/workspace/unzip.js.map +1 -1
- package/dist/workspace/upload.d.ts +9 -4
- package/dist/workspace/upload.js +9 -3
- package/dist/workspace/upload.js.map +1 -1
- package/dist/workspace/worktrees.d.ts +2 -1
- package/dist/workspace/worktrees.js +14 -15
- package/dist/workspace/worktrees.js.map +1 -1
- package/package.json +4 -2
- package/sidecar/agent_dependency.json +4 -4
- package/sidecar/scripts/replay_sidecar.py +25 -1
- package/sidecar/talaria_sidecar/__init__.py +1 -1
- package/sidecar/talaria_sidecar/__main__.py +7 -1
- package/sidecar/talaria_sidecar/home.py +52 -2
- package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
- package/sidecar/talaria_sidecar/methods/aux.py +39 -5
- package/sidecar/talaria_sidecar/methods/chat.py +431 -43
- package/sidecar/talaria_sidecar/methods/checkpoints.py +89 -0
- package/sidecar/talaria_sidecar/methods/commands.py +96 -2
- package/sidecar/talaria_sidecar/methods/cron.py +39 -8
- package/sidecar/talaria_sidecar/methods/gateway.py +32 -15
- package/sidecar/talaria_sidecar/methods/kanban.py +140 -19
- package/sidecar/talaria_sidecar/methods/mcp.py +1 -1
- package/sidecar/talaria_sidecar/methods/process.py +115 -2
- package/sidecar/talaria_sidecar/methods/profiles.py +84 -8
- package/sidecar/talaria_sidecar/methods/providers.py +27 -8
- package/sidecar/talaria_sidecar/methods/runtime.py +3 -0
- package/sidecar/talaria_sidecar/methods/state_db.py +39 -5
- package/sidecar/talaria_sidecar/methods/text.py +17 -1
- package/sidecar/talaria_sidecar/methods/usage.py +257 -4
- package/sidecar/talaria_sidecar/runtime.py +17 -1
- package/static/dist/FILES.txt +127 -125
- package/static/dist/assets/AppShell-g3bkgnvt.js +1 -0
- package/static/dist/assets/ChatPage-oo7bdan6.js +25 -0
- package/static/dist/assets/Dialog-g355qxhp.js +1 -0
- package/static/dist/assets/ExtensionRoute-i2y9fvjm.js +1 -0
- package/static/dist/assets/HubRoute-lmkrbw47.js +2 -0
- package/static/dist/assets/{Markdown-jqrx5es0.js → Markdown-j4kut4j2.js} +125 -125
- package/static/dist/assets/RightPanel-ohlsdzfl.js +1 -0
- package/static/dist/assets/Select-kddq75qc.js +1 -0
- package/static/dist/assets/SessionListPanel-lu3oy0hw.js +1 -0
- package/static/dist/assets/SettingsLayout-o9bv4mgb.js +1 -0
- package/static/dist/assets/States-eq6fe29m.js +1 -0
- package/static/dist/assets/Toaster-pc247u9f.js +1 -0
- package/static/dist/assets/_app-f8n1i0m1.js +1 -0
- package/static/dist/assets/_app.ext._extensionId-gfbzpoou.js +1 -0
- package/static/dist/assets/_app.index-bzzv80hk.js +1 -0
- package/static/dist/assets/_app.insights-dzixbryi.js +1 -0
- package/static/dist/assets/_app.kanban-c6hep2us.js +1 -0
- package/static/dist/assets/_app.logs-i69qds98.js +1 -0
- package/static/dist/assets/_app.memory-kl4vf0gd.js +1 -0
- package/static/dist/assets/_app.profiles-b8zinmyp.js +1 -0
- package/static/dist/assets/{_app.session._sessionId-hh27wwu2.js → _app.session._sessionId-fn1bhde6.js} +1 -1
- package/static/dist/assets/_app.settings-cg8plwmf.js +1 -0
- package/static/dist/assets/_app.settings._section-bkwbi9zn.js +3 -0
- package/static/dist/assets/_app.skills-cs47sbvs.js +1 -0
- package/static/dist/assets/_app.tasks-dwvk64tc.js +1 -0
- package/static/dist/assets/_app.todos-c8xjm8p3.js +1 -0
- package/static/dist/assets/_app.workspaces-ehvd1gds.js +1 -0
- package/static/dist/assets/{abnfDiagram-VCTEODGH-oity9y4o.js → abnfDiagram-VCTEODGH-c8kdixke.js} +1 -1
- package/static/dist/assets/architecture-7GRP2DOG-6crvnylg.js +1 -0
- package/static/dist/assets/{architectureDiagram-5GKGNRK7-ceaepnyr.js → architectureDiagram-5GKGNRK7-cku06x1p.js} +1 -1
- package/static/dist/assets/{back-eu64w89w.js → back-h7aun7ew.js} +1 -1
- package/static/dist/assets/{blockDiagram-I7D4REHJ-n79st4pj.js → blockDiagram-I7D4REHJ-jzthp1wa.js} +1 -1
- package/static/dist/assets/{c4Diagram-7LVT6UL2-mi73akzk.js → c4Diagram-7LVT6UL2-mwr67mt8.js} +1 -1
- package/static/dist/assets/{cancel-mzvkdjg8.js → cancel-fhi0vcnu.js} +1 -1
- package/static/dist/assets/channel-b2zo73z1.js +1 -0
- package/static/dist/assets/{chunk-4HAMMTFA-laat0g1q.js → chunk-4HAMMTFA-gpq1y228.js} +1 -1
- package/static/dist/assets/{chunk-75Z2AOVW-0psy1tr2.js → chunk-75Z2AOVW-ktje58c4.js} +1 -1
- package/static/dist/assets/{chunk-DU6HZSFF-lw8uox8x.js → chunk-DU6HZSFF-ofajdixh.js} +3 -3
- package/static/dist/assets/{chunk-F27PBJKO-erassib3.js → chunk-F27PBJKO-cmev85ti.js} +1 -1
- package/static/dist/assets/{chunk-GMAD6QVW-fz7rx619.js → chunk-GMAD6QVW-htjgt22y.js} +1 -1
- package/static/dist/assets/{chunk-GVQU2GXP-gdb0epb2.js → chunk-GVQU2GXP-qndvoxl7.js} +1 -1
- package/static/dist/assets/{chunk-IMKFNOWR-ea8t26qw.js → chunk-IMKFNOWR-e5bkd5q6.js} +1 -1
- package/static/dist/assets/{chunk-L3NEJ4N5-mb1gu9kz.js → chunk-L3NEJ4N5-mj1155r6.js} +1 -1
- package/static/dist/assets/{chunk-OSK3NFVY-eovmccvy.js → chunk-OSK3NFVY-e7gkikcw.js} +1 -1
- package/static/dist/assets/{chunk-P2QGCYS3-bo55zjo5.js → chunk-P2QGCYS3-f7t54xfm.js} +1 -1
- package/static/dist/assets/{chunk-POPQ4Y6H-ja7ohe4h.js → chunk-POPQ4Y6H-i927v9oa.js} +1 -1
- package/static/dist/assets/{chunk-PWAF6VOD-e30g9nus.js → chunk-PWAF6VOD-m84lmnyt.js} +1 -1
- package/static/dist/assets/{chunk-SHT3W25Y-gbb798f7.js → chunk-SHT3W25Y-mi0tlcu9.js} +1 -1
- package/static/dist/assets/{chunk-SVP7TREG-h2iw8urm.js → chunk-SVP7TREG-dnau702j.js} +1 -1
- package/static/dist/assets/{chunk-TICWLB2K-fd2cottz.js → chunk-TICWLB2K-edem1gkg.js} +1 -1
- package/static/dist/assets/classDiagram-ZZMXUADV-oe99st8x.js +1 -0
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-oe99st8x.js +1 -0
- package/static/dist/assets/{copied-h65fc3cx.js → copied-hcplv46p.js} +1 -1
- package/static/dist/assets/copy-magtofyp.js +1 -0
- package/static/dist/assets/{cynefin-OW5HDTMX-lbltyl5j.js → cynefin-OW5HDTMX-siqlulkl.js} +1 -1
- package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-lxhsqq4a.js → cynefinDiagram-5FMLGOSQ-i648tah9.js} +1 -1
- package/static/dist/assets/{dagre-GXQ25YYZ-gn4nnaln.js → dagre-GXQ25YYZ-mwn6uc17.js} +1 -1
- package/static/dist/assets/{diagram-S7CK7UJ4-pa3ij6q7.js → diagram-S7CK7UJ4-l8abxaes.js} +1 -1
- package/static/dist/assets/{diagram-UQ7AKVKN-zbipyz57.js → diagram-UQ7AKVKN-mekq7zgq.js} +1 -1
- package/static/dist/assets/{diagram-VSXAHHWV-d1kbpzgf.js → diagram-VSXAHHWV-fnky6lvc.js} +1 -1
- package/static/dist/assets/{diagram-VX7I27RA-i3ty14xl.js → diagram-VX7I27RA-xe6r5qfw.js} +1 -1
- package/static/dist/assets/{diagram-Z3DM3KII-m8xefqa7.js → diagram-Z3DM3KII-nq07dg7k.js} +1 -1
- package/static/dist/assets/{ebnfDiagram-PWID7BFC-nz7wj05l.js → ebnfDiagram-PWID7BFC-c62ipm3g.js} +1 -1
- package/static/dist/assets/endpoints-klbxod4t.js +64 -0
- package/static/dist/assets/{erDiagram-RLTQ6QDP-fa41ib83.js → erDiagram-RLTQ6QDP-h39kmnzy.js} +1 -1
- package/static/dist/assets/eventmodeling-NTZA5JFV-obfede4u.js +1 -0
- package/static/dist/assets/flowDiagram-HODETNUW-kdpsp1je.js +1 -0
- package/static/dist/assets/{ganttDiagram-EL5Y4UJY-n7zq01qu.js → ganttDiagram-EL5Y4UJY-ngbsyp37.js} +1 -1
- package/static/dist/assets/{gitGraph-4MIJSDKK-c89emb07.js → gitGraph-4MIJSDKK-fr2i7caa.js} +1 -1
- package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-cz7cgr6w.js → gitGraphDiagram-WWUBYQGX-eravdti4.js} +1 -1
- package/static/dist/assets/{highlighted-body-KPVGNVTW-d9bapmni.js → highlighted-body-KPVGNVTW-jwpop9p8.js} +1 -1
- package/static/dist/assets/host-i828xuo7.js +1 -0
- package/static/dist/assets/index-eh8zilnv.css +2 -0
- package/static/dist/assets/index-r648f4o1.js +11 -0
- package/static/dist/assets/{info-A6RAGUB7-ndddhhhh.js → info-A6RAGUB7-h11x0dxo.js} +1 -1
- package/static/dist/assets/{infoDiagram-27XIBGKW-ccxigcpf.js → infoDiagram-27XIBGKW-dfh47dw9.js} +1 -1
- package/static/dist/assets/{ishikawaDiagram-5VMMS53U-le0z9vtc.js → ishikawaDiagram-5VMMS53U-i6ypq351.js} +1 -1
- package/static/dist/assets/{journeyDiagram-3NMN7TZE-ivsmb26o.js → journeyDiagram-3NMN7TZE-lduf1z95.js} +1 -1
- package/static/dist/assets/{kanban-definition-UXKFOSKX-h6n2awt0.js → kanban-definition-UXKFOSKX-e18bu4pg.js} +1 -1
- package/static/dist/assets/lazyRouteComponent-e1y4t2i9.js +1 -0
- package/static/dist/assets/{line-dmzlfe0u.js → line-meizqsvn.js} +1 -1
- package/static/dist/assets/link-oot2yn73.js +1 -0
- package/static/dist/assets/login-gu28l9ou.js +1 -0
- package/static/dist/assets/mermaid-HWGCJPDP-mtwrj8fn.js +1 -0
- package/static/dist/assets/{mermaid-parser.core-gwyd5bnq.js → mermaid-parser.core-ntne45i9.js} +3 -3
- package/static/dist/assets/{mindmap-definition-YA3MSWOX-l68gg5h7.js → mindmap-definition-YA3MSWOX-27b26eok.js} +1 -1
- package/static/dist/assets/onboarding-lkphzu1s.js +1 -0
- package/static/dist/assets/onboarding_model_label-ftpjdwqk.js +1 -0
- package/static/dist/assets/{packet-AYTQ26CC-gpv06065.js → packet-AYTQ26CC-l6jdka8p.js} +1 -1
- package/static/dist/assets/{pegDiagram-XKGWAZYB-e52vs23u.js → pegDiagram-XKGWAZYB-g1er3bxj.js} +1 -1
- package/static/dist/assets/{pie-WAS4IAKB-l6m7nz5s.js → pie-WAS4IAKB-i0ylcrt9.js} +1 -1
- package/static/dist/assets/{pieDiagram-E7YTZNPT-b4is36bq.js → pieDiagram-E7YTZNPT-fbz1e55z.js} +1 -1
- package/static/dist/assets/{quadrantDiagram-AXDQQJYC-m8vt2788.js → quadrantDiagram-AXDQQJYC-hhue7d8o.js} +1 -1
- package/static/dist/assets/{radar-RG4KPBEZ-nxbrhiov.js → radar-RG4KPBEZ-f9b70aa4.js} +1 -1
- package/static/dist/assets/{railroad-74A4TZTK-222mu9hi.js → railroad-74A4TZTK-lgsuoeda.js} +1 -1
- package/static/dist/assets/railroad-abnf-HS5TGJTU-nf9d6pdx.js +1 -0
- package/static/dist/assets/railroad-ebnf-LZEXJU2U-f7ummkt5.js +1 -0
- package/static/dist/assets/railroad-peg-WCYAUIDC-oczq5tqs.js +1 -0
- package/static/dist/assets/{railroadDiagram-O6MQD6OU-o8lldfzv.js → railroadDiagram-O6MQD6OU-eqpy9fnz.js} +1 -1
- package/static/dist/assets/refresh-abtu93ke.js +1 -0
- package/static/dist/assets/refresh-cw-hlwb02qv.js +1 -0
- package/static/dist/assets/{requirementDiagram-BXWQKSXE-ovyq38kv.js → requirementDiagram-BXWQKSXE-ogf6puhi.js} +1 -1
- package/static/dist/assets/{sankeyDiagram-P5KCCOFB-ou7lznor.js → sankeyDiagram-P5KCCOFB-fqb8xcjf.js} +1 -1
- package/static/dist/assets/{saved-bazmcup9.js → saved-girnabkf.js} +1 -1
- package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-ovckdzf2.js → sequenceDiagram-WJ2MYXX4-dlcz6qjz.js} +1 -1
- package/static/dist/assets/{settings_section_extensions_title-ertwwhlx.js → settings_section_extensions_title-h14cwk6f.js} +1 -1
- package/static/dist/assets/share._token-kl0r7ido.js +2 -0
- package/static/dist/assets/sliders-horizontal-n1zm34qq.js +1 -0
- package/static/dist/assets/sse-iwku0sjo.js +1 -0
- package/static/dist/assets/{stateDiagram-D77RDMKH-fcrexkkv.js → stateDiagram-D77RDMKH-fyoznpcs.js} +1 -1
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-ixhx8r46.js +1 -0
- package/static/dist/assets/{swimlanes-42K2YHIH-f7usu8fw.js → swimlanes-42K2YHIH-oefldhfy.js} +1 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-h9ersk2e.js +8 -0
- package/static/dist/assets/{timeline-definition-24CTP7MA-gp1hvz9f.js → timeline-definition-24CTP7MA-b1vl2zil.js} +1 -1
- package/static/dist/assets/todoStore-jjr5yr78.js +1 -0
- package/static/dist/assets/{treeView-Q6P3EWNA-gb9gf11u.js → treeView-Q6P3EWNA-f7hcci5o.js} +1 -1
- package/static/dist/assets/{treemap-WGGIJYW6-nuvfnvvi.js → treemap-WGGIJYW6-nl85071a.js} +1 -1
- package/static/dist/assets/units-kgxzxfhi.js +1 -0
- package/static/dist/assets/{untitled-htelhc68.js → untitled-cikfjntn.js} +1 -1
- package/static/dist/assets/{useForm-h2t0u14e.js → useForm-kqoqg0k9.js} +1 -1
- package/static/dist/assets/useLocale-o59tbiea.js +1 -0
- package/static/dist/assets/{useParams-dqd64jv7.js → useParams-b4dq6n4q.js} +1 -1
- package/static/dist/assets/{useSearch-j30pvsy7.js → useSearch-ipfmv3he.js} +1 -1
- package/static/dist/assets/{vennDiagram-4TSXK5OY-fzchbzaj.js → vennDiagram-4TSXK5OY-h5hzbk0n.js} +1 -1
- package/static/dist/assets/{wardley-WFR3VGLG-m47mhfbh.js → wardley-WFR3VGLG-jftvpm3e.js} +1 -1
- package/static/dist/assets/{wardleyDiagram-VM6X3IG4-odg6ct1z.js → wardleyDiagram-VM6X3IG4-hedh85ge.js} +1 -1
- package/static/dist/assets/{xychartDiagram-S5SC5T6Z-mrhms5um.js → xychartDiagram-S5SC5T6Z-e49523ab.js} +1 -1
- package/static/dist/index.html +1 -1
- package/static/dist/sw.js +2 -2
- package/static/dist/assets/AppShell-ef3bhu32.js +0 -1
- package/static/dist/assets/Button-nx37f4u1.js +0 -1
- package/static/dist/assets/ChatPage-cdcpykcg.js +0 -25
- package/static/dist/assets/Dialog-mec42hvb.js +0 -1
- package/static/dist/assets/HubRoute-ecb65zpr.js +0 -2
- package/static/dist/assets/RightPanel-d1lgvk4s.js +0 -1
- package/static/dist/assets/Select-h4ivl9qf.js +0 -1
- package/static/dist/assets/SessionListPanel-ewib046b.js +0 -1
- package/static/dist/assets/SettingsLayout-lx654cup.js +0 -1
- package/static/dist/assets/States-ehsb6lzo.js +0 -1
- package/static/dist/assets/Toaster-mch1pa3t.js +0 -1
- package/static/dist/assets/_app-m8d961ef.js +0 -1
- package/static/dist/assets/_app.ext._extensionId-mp33hbzi.js +0 -1
- package/static/dist/assets/_app.index-dk6zp7e0.js +0 -1
- package/static/dist/assets/_app.insights-jt2fk4i1.js +0 -1
- package/static/dist/assets/_app.kanban-if4pubeu.js +0 -1
- package/static/dist/assets/_app.logs-mfaw9m6g.js +0 -1
- package/static/dist/assets/_app.memory-h3eaarb6.js +0 -1
- package/static/dist/assets/_app.profiles-ienktovs.js +0 -1
- package/static/dist/assets/_app.settings-dpoe0bzf.js +0 -1
- package/static/dist/assets/_app.settings._section-l468ynag.js +0 -3
- package/static/dist/assets/_app.skills-kcbncc8i.js +0 -1
- package/static/dist/assets/_app.tasks-occdrqhw.js +0 -2
- package/static/dist/assets/_app.todos-nnke2rcc.js +0 -1
- package/static/dist/assets/_app.workspaces-cdlatvwj.js +0 -1
- package/static/dist/assets/architecture-7GRP2DOG-hj11333z.js +0 -1
- package/static/dist/assets/channel-mbvmf2vv.js +0 -1
- package/static/dist/assets/classDiagram-ZZMXUADV-o8r05k0n.js +0 -1
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-o8r05k0n.js +0 -1
- package/static/dist/assets/copy-e6977uwq.js +0 -1
- package/static/dist/assets/endpoints-k7yf25m0.js +0 -64
- package/static/dist/assets/eventmodeling-NTZA5JFV-kpbq09bx.js +0 -1
- package/static/dist/assets/flowDiagram-HODETNUW-lcz0kx14.js +0 -1
- package/static/dist/assets/index-h00usru2.js +0 -11
- package/static/dist/assets/index-n73ijkv2.css +0 -2
- package/static/dist/assets/lazyRouteComponent-m1rtyk4t.js +0 -1
- package/static/dist/assets/link-budeg5t9.js +0 -1
- package/static/dist/assets/login-kjhasvp8.js +0 -1
- package/static/dist/assets/matchContext-f8oy3lo7.js +0 -1
- package/static/dist/assets/mermaid-HWGCJPDP-jb5d211j.js +0 -1
- package/static/dist/assets/onboarding-cdfrk0pj.js +0 -1
- package/static/dist/assets/railroad-abnf-HS5TGJTU-kdvgl6x8.js +0 -1
- package/static/dist/assets/railroad-ebnf-LZEXJU2U-itiqslby.js +0 -1
- package/static/dist/assets/railroad-peg-WCYAUIDC-go7kov78.js +0 -1
- package/static/dist/assets/refresh-cw-kbcje1uy.js +0 -1
- package/static/dist/assets/search-jen74z4u.js +0 -1
- package/static/dist/assets/share._token-dan9hgvj.js +0 -2
- package/static/dist/assets/sliders-horizontal-f9cb3lh3.js +0 -1
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-lcfraua2.js +0 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-ogmanni0.js +0 -8
- package/static/dist/assets/todoStore-eqtid4r4.js +0 -1
- package/static/dist/assets/useLocale-hcsb84pj.js +0 -1
- package/static/dist/assets/useNavigate-nwy70ztj.js +0 -1
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""``checkpoints.*``: a workspace's filesystem checkpoints through the Agent's own ``CheckpointManager`` (TAL-913).
|
|
2
|
+
|
|
3
|
+
The Agent keeps every project's checkpoints as refs in one shared git store under the profile home; listing, diffing and
|
|
4
|
+
restoring go through its manager so the store layout, the nested-repository refusal and the pre-rollback snapshot stay
|
|
5
|
+
the Agent's.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from ..errors import InvalidParams, RpcError
|
|
11
|
+
from ..home import profile_home_param, scoped_home
|
|
12
|
+
from ..rpc import CallContext
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def checkpoint_kwargs() -> dict:
|
|
16
|
+
"""The profile's ``checkpoints`` config as ``AIAgent`` kwargs, resolved as the Agent's gateway does
|
|
17
|
+
(``gateway/run.py::_checkpoint_agent_kwargs``): the legacy ``checkpoints: true`` form, else the section over
|
|
18
|
+
``DEFAULT_CONFIG``. Only a real ``true`` turns them on, so a quoted ``"false"`` stays off."""
|
|
19
|
+
from hermes_cli.config import DEFAULT_CONFIG, load_config
|
|
20
|
+
|
|
21
|
+
section = (load_config() or {}).get("checkpoints", {})
|
|
22
|
+
if isinstance(section, bool):
|
|
23
|
+
section = {"enabled": section}
|
|
24
|
+
elif not isinstance(section, dict):
|
|
25
|
+
section = {}
|
|
26
|
+
defaults = DEFAULT_CONFIG["checkpoints"]
|
|
27
|
+
return {
|
|
28
|
+
"checkpoints_enabled": section.get("enabled", defaults["enabled"]) is True,
|
|
29
|
+
"checkpoint_max_snapshots": section.get("max_snapshots", defaults["max_snapshots"]),
|
|
30
|
+
"checkpoint_max_total_size_mb": section.get("max_total_size_mb", defaults["max_total_size_mb"]),
|
|
31
|
+
"checkpoint_max_file_size_mb": section.get("max_file_size_mb", defaults["max_file_size_mb"]),
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _manager():
|
|
36
|
+
from tools.checkpoint_manager import CheckpointManager
|
|
37
|
+
|
|
38
|
+
kwargs = checkpoint_kwargs()
|
|
39
|
+
return CheckpointManager(enabled=kwargs["checkpoints_enabled"], max_snapshots=kwargs["checkpoint_max_snapshots"],
|
|
40
|
+
max_total_size_mb=kwargs["checkpoint_max_total_size_mb"], max_file_size_mb=kwargs["checkpoint_max_file_size_mb"])
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _param(params: dict, name: str) -> str:
|
|
44
|
+
value = params.get(name)
|
|
45
|
+
if not isinstance(value, str) or not value.strip():
|
|
46
|
+
raise InvalidParams(f"{name} is required")
|
|
47
|
+
return value
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _agent_result(result: dict) -> dict:
|
|
51
|
+
"""The Agent answers a refusal (unknown checkpoint, uncaptured nested repository, failed checkout) as ``success: False``."""
|
|
52
|
+
if not result.get("success"):
|
|
53
|
+
raise RpcError(str(result.get("error") or "checkpoint operation failed"), condition="checkpoint_failed")
|
|
54
|
+
return result
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _owned(manager, workspace: str, checkpoint: str) -> str:
|
|
58
|
+
"""The full hash of ``checkpoint`` among ``workspace``'s own checkpoints. The store is shared by every project and the
|
|
59
|
+
Agent's diff and restore accept any commit in it, so another workspace's hash would check that tree out here."""
|
|
60
|
+
matches = [c["hash"] for c in manager.list_checkpoints(workspace) if c.get("hash") and str(c["hash"]).startswith(checkpoint.lower())]
|
|
61
|
+
if len(matches) != 1:
|
|
62
|
+
raise RpcError(f"Checkpoint '{checkpoint}' is not a checkpoint of this workspace", condition="checkpoint_failed")
|
|
63
|
+
return matches[0]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def register(registry) -> None:
|
|
67
|
+
@registry.method("checkpoints.list")
|
|
68
|
+
def list_(ctx: CallContext, params: dict) -> dict:
|
|
69
|
+
workspace = _param(params, "workspace")
|
|
70
|
+
with scoped_home(profile_home_param(params)):
|
|
71
|
+
from tools.checkpoint_manager import _store_path
|
|
72
|
+
|
|
73
|
+
return {"checkpoints": _manager().list_checkpoints(workspace), "store": str(_store_path())}
|
|
74
|
+
|
|
75
|
+
@registry.method("checkpoints.diff")
|
|
76
|
+
def diff(ctx: CallContext, params: dict) -> dict:
|
|
77
|
+
workspace, checkpoint = _param(params, "workspace"), _param(params, "checkpoint")
|
|
78
|
+
with scoped_home(profile_home_param(params)):
|
|
79
|
+
manager = _manager()
|
|
80
|
+
result = _agent_result(manager.diff(workspace, _owned(manager, workspace, checkpoint)))
|
|
81
|
+
return {"stat": str(result.get("stat") or ""), "diff": str(result.get("diff") or "")}
|
|
82
|
+
|
|
83
|
+
@registry.method("checkpoints.restore")
|
|
84
|
+
def restore(ctx: CallContext, params: dict) -> dict:
|
|
85
|
+
workspace, checkpoint = _param(params, "workspace"), _param(params, "checkpoint")
|
|
86
|
+
with scoped_home(profile_home_param(params)):
|
|
87
|
+
manager = _manager()
|
|
88
|
+
result = _agent_result(manager.restore(workspace, _owned(manager, workspace, checkpoint)))
|
|
89
|
+
return {"restored_to": str(result.get("restored_to") or ""), "reason": str(result.get("reason") or "")}
|
|
@@ -15,7 +15,14 @@ log = logging.getLogger("talaria_sidecar.commands")
|
|
|
15
15
|
|
|
16
16
|
_NEVER_EXPOSE = frozenset({"sethome", "restart", "update", "commands"})
|
|
17
17
|
_ALIASES = {"reload_mcp": "reload-mcp", "reload_skills": "reload-skills", "codex_runtime": "codex-runtime"}
|
|
18
|
-
|
|
18
|
+
# The commands ``commands.exec`` runs, with the catalog row the sidecar lists when the Agent registry lacks one.
|
|
19
|
+
_RUNTIME_COMMANDS = {
|
|
20
|
+
"reload-mcp": ("Reload MCP servers from config", "Tools & Skills", ""),
|
|
21
|
+
"reload-skills": ("Re-scan skills from disk", "Tools & Skills", ""),
|
|
22
|
+
"codex-runtime": ("Toggle codex app-server runtime for OpenAI/Codex models", "Configuration", "[auto|codex_app_server]"),
|
|
23
|
+
"credits": ("Show your Nous credits", "Info", ""),
|
|
24
|
+
}
|
|
25
|
+
_ALLOWED = frozenset(_RUNTIME_COMMANDS)
|
|
19
26
|
_RELOAD_MCP_LOCK = threading.Lock()
|
|
20
27
|
_RELOAD_SKILLS_LOCK = threading.Lock()
|
|
21
28
|
_CODEX_RUNTIME_LOCK = threading.Lock()
|
|
@@ -46,7 +53,13 @@ def list_commands() -> list[dict[str, Any]]:
|
|
|
46
53
|
out.append({
|
|
47
54
|
"name": cmd.name, "description": cmd.description, "category": cmd.category, "aliases": list(cmd.aliases),
|
|
48
55
|
"args_hint": cmd.args_hint, "subcommands": list(cmd.subcommands), "cli_only": bool(cmd.cli_only), "gateway_only": bool(cmd.gateway_only),
|
|
56
|
+
"exec": cmd.name in _ALLOWED,
|
|
49
57
|
})
|
|
58
|
+
listed = {c["name"] for c in out}
|
|
59
|
+
for name, (description, category, args_hint) in _RUNTIME_COMMANDS.items():
|
|
60
|
+
if name not in listed:
|
|
61
|
+
out.append({"name": name, "description": description, "category": category, "aliases": [a for a, n in _ALIASES.items() if n == name],
|
|
62
|
+
"args_hint": args_hint, "subcommands": [], "cli_only": False, "gateway_only": False, "exec": True})
|
|
50
63
|
try:
|
|
51
64
|
from hermes_cli.plugins import get_plugin_commands
|
|
52
65
|
|
|
@@ -55,7 +68,7 @@ def list_commands() -> list[dict[str, Any]]:
|
|
|
55
68
|
if name in existing or name in _NEVER_EXPOSE:
|
|
56
69
|
continue
|
|
57
70
|
out.append({"name": name, "description": str(info.get("description", "Plugin command")), "category": "Plugin", "aliases": [],
|
|
58
|
-
"args_hint": str(info.get("args_hint", "")), "subcommands": [], "cli_only": False, "gateway_only": False})
|
|
71
|
+
"args_hint": str(info.get("args_hint", "")), "subcommands": [], "cli_only": False, "gateway_only": False, "exec": True})
|
|
59
72
|
except Exception: # noqa: BLE001 - plugin registry is optional
|
|
60
73
|
log.debug("plugin command listing failed", exc_info=True)
|
|
61
74
|
return out
|
|
@@ -231,6 +244,77 @@ def execute_plugin_command(command: str) -> str | None:
|
|
|
231
244
|
return f"Plugin command error: {type(exc).__name__}"
|
|
232
245
|
|
|
233
246
|
|
|
247
|
+
def _profile_bundles(skill_bundles) -> dict[str, dict[str, Any]]:
|
|
248
|
+
"""The scoped profile's bundles keyed ``/slug``, read from disk the way ``scan_bundles`` reads them.
|
|
249
|
+
|
|
250
|
+
``scan_bundles``/``get_skill_bundles`` publish one profile's bundles in a process-wide cache keyed only by mtime,
|
|
251
|
+
which cron jobs in other profiles read concurrently, so the sidecar never fills it."""
|
|
252
|
+
out: dict[str, dict[str, Any]] = {}
|
|
253
|
+
for path in skill_bundles._iter_bundle_files():
|
|
254
|
+
info = skill_bundles._load_bundle_file(path)
|
|
255
|
+
if info:
|
|
256
|
+
out.setdefault(f"/{info['slug']}", info)
|
|
257
|
+
return out
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def list_command_bundles() -> list[dict[str, Any]]:
|
|
261
|
+
"""The profile's skill bundles as slash-command rows; [] when the bundle runtime is missing or fails."""
|
|
262
|
+
try:
|
|
263
|
+
import agent.skill_bundles as skill_bundles
|
|
264
|
+
except ImportError:
|
|
265
|
+
return []
|
|
266
|
+
try:
|
|
267
|
+
bundles = sorted(_profile_bundles(skill_bundles).values(), key=lambda b: b["slug"])
|
|
268
|
+
except Exception: # noqa: BLE001
|
|
269
|
+
log.warning("Failed to list skill bundles", exc_info=True)
|
|
270
|
+
return []
|
|
271
|
+
return [{"name": str(b["slug"]).strip().lower(), "description": str(b.get("description") or "").strip() or "Skill bundle",
|
|
272
|
+
"skill_count": len(b.get("skills") or []), "source": "bundle"} for b in bundles if str(b.get("slug") or "").strip()]
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _bundle_invocation(info: dict[str, Any], instruction: str) -> tuple[str, list[str], list[str]] | None:
|
|
276
|
+
"""``build_bundle_invocation_message`` for a bundle read by ``_profile_bundles`` (that one looks it up in the shared cache),
|
|
277
|
+
skipping the skills disabled for Web chat (``skills.platform_disabled.webui``)."""
|
|
278
|
+
from agent.skill_commands import _disabled_skill_names, _load_skill_blocks, _load_skill_payload, _scaffold_header
|
|
279
|
+
|
|
280
|
+
name = info["name"]
|
|
281
|
+
loaded, missing, disabled, blocks = _load_skill_blocks(
|
|
282
|
+
[(skill or "").strip() for skill in info["skills"]], _load_skill_payload, lambda _skill: f'[Loaded as part of the "{name}" skill bundle.]',
|
|
283
|
+
None, disabled_names=_disabled_skill_names("webui"))
|
|
284
|
+
if not blocks:
|
|
285
|
+
return None
|
|
286
|
+
header = _scaffold_header(f'"{name}" skill bundle', loaded, lead_lines=[f"Bundle: {name}"], missing=missing, disabled=disabled,
|
|
287
|
+
extra_instruction=info.get("instruction") or "", user_instruction=instruction)
|
|
288
|
+
return "\n\n".join([header, *blocks]), loaded, missing
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def resolve_bundle_command(command: str) -> dict[str, Any]:
|
|
292
|
+
"""Expand ``/<bundle> [instruction]`` into the user message that loads the bundle's skills."""
|
|
293
|
+
name, instruction = parse_slash_command(command)
|
|
294
|
+
try:
|
|
295
|
+
import agent.skill_bundles as skill_bundles
|
|
296
|
+
from agent.skill_commands import resolve_slash_key
|
|
297
|
+
except ImportError as exc:
|
|
298
|
+
raise RpcError("Skill bundle runtime unavailable", condition="bundle_unavailable") from exc
|
|
299
|
+
try:
|
|
300
|
+
bundles = _profile_bundles(skill_bundles)
|
|
301
|
+
key = resolve_slash_key(name, bundles)
|
|
302
|
+
result = _bundle_invocation(bundles[key], instruction) if key else None
|
|
303
|
+
except ValueError as exc:
|
|
304
|
+
raise InvalidParams(str(exc)) from exc
|
|
305
|
+
except Exception as exc: # noqa: BLE001
|
|
306
|
+
log.warning("Failed to resolve skill bundle command", exc_info=True)
|
|
307
|
+
raise RpcError("Skill bundle command unavailable", condition="bundle_unavailable") from exc
|
|
308
|
+
if key is None:
|
|
309
|
+
raise RpcError("Bundle command not found", condition="bundle_not_found")
|
|
310
|
+
message, loaded, missing = result or ("", [], [])
|
|
311
|
+
message = str(message or "").strip()
|
|
312
|
+
if not message:
|
|
313
|
+
raise RpcError("Bundle command returned no invocation text", condition="bundle_unavailable")
|
|
314
|
+
return {"name": key.lstrip("/"), "source": "bundle", "message": message, "loaded_skills": [str(s) for s in loaded or []],
|
|
315
|
+
"missing_skills": [str(s) for s in missing or []]}
|
|
316
|
+
|
|
317
|
+
|
|
234
318
|
def resolve_moa_config(preset: str | None) -> dict:
|
|
235
319
|
try:
|
|
236
320
|
from hermes_cli.moa_config import moa_usage, normalize_moa_config
|
|
@@ -339,6 +423,16 @@ def register(registry) -> None:
|
|
|
339
423
|
raise RpcError(f"unknown command {parse_slash_command(command)[0]!r}", condition="command_not_found")
|
|
340
424
|
return {"output": output, "source": source}
|
|
341
425
|
|
|
426
|
+
@registry.method("commands.bundles")
|
|
427
|
+
def bundles(ctx: CallContext, params: dict) -> dict:
|
|
428
|
+
with scoped_home(profile_home_param(params)):
|
|
429
|
+
return {"bundles": list_command_bundles()}
|
|
430
|
+
|
|
431
|
+
@registry.method("commands.bundle_resolve")
|
|
432
|
+
def bundle_resolve(ctx: CallContext, params: dict) -> dict:
|
|
433
|
+
with scoped_home(profile_home_param(params)):
|
|
434
|
+
return resolve_bundle_command(str(params.get("command") or ""))
|
|
435
|
+
|
|
342
436
|
@registry.method("commands.moa_preset")
|
|
343
437
|
def moa(ctx: CallContext, params: dict) -> dict:
|
|
344
438
|
with scoped_home(profile_home_param(params)):
|
|
@@ -33,6 +33,35 @@ _RUNNING_LOCK = threading.Lock()
|
|
|
33
33
|
_SNAPSHOT_LOCK = threading.Lock()
|
|
34
34
|
|
|
35
35
|
|
|
36
|
+
def _main_model_pin() -> tuple[str | None, str | None]:
|
|
37
|
+
"""``(provider, model)`` the scoped profile's main agent runs on: the Agent's own ``cron.jobs._main_model_pin``.
|
|
38
|
+
|
|
39
|
+
Agent 0.21.3 predates that helper, so it is rebuilt from the readers 0.21.4 composes: the home's effective
|
|
40
|
+
``config.yaml`` model and the provider the runtime resolves for it. ``(None, None)`` when no model is configured.
|
|
41
|
+
"""
|
|
42
|
+
from cron import jobs
|
|
43
|
+
|
|
44
|
+
if hasattr(jobs, "_main_model_pin"):
|
|
45
|
+
return jobs._main_model_pin()
|
|
46
|
+
from hermes_cli.config_effective import load_user_config_effective
|
|
47
|
+
from hermes_constants import get_hermes_home
|
|
48
|
+
|
|
49
|
+
cfg_path = get_hermes_home() / "config.yaml"
|
|
50
|
+
cfg = load_user_config_effective(cfg_path) if cfg_path.exists() else {}
|
|
51
|
+
model_cfg = cfg.get("model") or {}
|
|
52
|
+
model = model_cfg.get("default") or model_cfg.get("model") if isinstance(model_cfg, dict) else model_cfg
|
|
53
|
+
model = model.strip() or None if isinstance(model, str) else None
|
|
54
|
+
if not model:
|
|
55
|
+
return None, None
|
|
56
|
+
provider = None
|
|
57
|
+
with contextlib.suppress(Exception):
|
|
58
|
+
from hermes_cli.runtime_provider import resolve_runtime_provider
|
|
59
|
+
|
|
60
|
+
resolved = resolve_runtime_provider(requested=None).get("provider")
|
|
61
|
+
provider = resolved.strip().lower() or None if isinstance(resolved, str) else None
|
|
62
|
+
return provider, model
|
|
63
|
+
|
|
64
|
+
|
|
36
65
|
class CronUnavailable(RpcError):
|
|
37
66
|
def __init__(self, exc: Exception):
|
|
38
67
|
super().__init__(f"cron unavailable: {exc}", condition="cron_unavailable")
|
|
@@ -252,14 +281,18 @@ def _run_in_child(job, execution_home, ctx: CallContext):
|
|
|
252
281
|
process.join(timeout=5)
|
|
253
282
|
status, payload = "cancelled", ["cancelled", ""]
|
|
254
283
|
break
|
|
255
|
-
if
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
else:
|
|
284
|
+
if not process.is_alive():
|
|
285
|
+
# The child may have put its result just before exiting; otherwise it died (OOM, signal) without one.
|
|
286
|
+
try:
|
|
287
|
+
status, *payload = result_queue.get_nowait()
|
|
288
|
+
except queue.Empty:
|
|
261
289
|
payload = [f"cron run subprocess exited with code {process.exitcode} without producing a result", ""]
|
|
262
290
|
break
|
|
291
|
+
if time.monotonic() >= deadline:
|
|
292
|
+
process.terminate()
|
|
293
|
+
process.join(timeout=5)
|
|
294
|
+
payload = [f"cron run subprocess produced no result within {_result_timeout(job):g}s and was terminated", ""]
|
|
295
|
+
break
|
|
263
296
|
process.join(timeout=5)
|
|
264
297
|
if process.is_alive():
|
|
265
298
|
process.terminate()
|
|
@@ -392,8 +425,6 @@ def register(registry) -> None:
|
|
|
392
425
|
# to the store home; owner_profile only preserves management from the creating Web profile.
|
|
393
426
|
if post.get("profile") and execution_home and not (model and provider) and not body.get("no_agent"):
|
|
394
427
|
try:
|
|
395
|
-
from cron.jobs import _main_model_pin
|
|
396
|
-
|
|
397
428
|
with _SNAPSHOT_LOCK, scoped_home(Path(execution_home)):
|
|
398
429
|
profile_provider, profile_model = _main_model_pin()
|
|
399
430
|
if not profile_model:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""``gateway.*``: Hermes gateway lifecycle through the ``hermes`` CLI
|
|
1
|
+
"""``gateway.*``: Hermes gateway lifecycle (start, stop, restart) through the ``hermes`` CLI."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -16,7 +16,10 @@ from ..home import profile_home_param
|
|
|
16
16
|
from ..rpc import CallContext
|
|
17
17
|
|
|
18
18
|
log = logging.getLogger("talaria_sidecar.gateway")
|
|
19
|
-
|
|
19
|
+
# One lifecycle action at a time across every action: overlapping `hermes gateway` subprocesses race each other.
|
|
20
|
+
_ACTION_LOCK = threading.Lock()
|
|
21
|
+
ACTIONS = ("start", "stop", "restart")
|
|
22
|
+
_DONE = {"start": "started", "stop": "stopped", "restart": "restarted"}
|
|
20
23
|
|
|
21
24
|
|
|
22
25
|
def _hermes_command() -> str:
|
|
@@ -39,9 +42,11 @@ def _drain(stream, sink: list[str]) -> None:
|
|
|
39
42
|
pass
|
|
40
43
|
|
|
41
44
|
|
|
42
|
-
def
|
|
43
|
-
if not
|
|
44
|
-
|
|
45
|
+
def control(action: str, home: Path, cli_profile: str | None, ctx: CallContext, *, quick_timeout: float, background_wait: float) -> dict:
|
|
46
|
+
if action not in ACTIONS:
|
|
47
|
+
raise InvalidParams("unsupported gateway action")
|
|
48
|
+
if not _ACTION_LOCK.acquire(blocking=False):
|
|
49
|
+
return {"status": "busy", "message": "Another gateway action is already in progress. Please wait a moment and try again."}
|
|
45
50
|
try:
|
|
46
51
|
env = dict(os.environ)
|
|
47
52
|
env["HERMES_HOME"] = str(home)
|
|
@@ -49,9 +54,12 @@ def restart(home: Path, cli_profile: str | None, ctx: CallContext, *, quick_time
|
|
|
49
54
|
cmd = [_hermes_command()]
|
|
50
55
|
if cli_profile:
|
|
51
56
|
cmd.extend(["--profile", cli_profile])
|
|
52
|
-
cmd.extend(["gateway",
|
|
53
|
-
log.info("
|
|
54
|
-
|
|
57
|
+
cmd.extend(["gateway", action])
|
|
58
|
+
log.info("Running gateway %s via %s (HERMES_HOME=%s)", action, " ".join(cmd), home)
|
|
59
|
+
try:
|
|
60
|
+
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, env=env)
|
|
61
|
+
except FileNotFoundError:
|
|
62
|
+
return {"status": "failed", "message": "Hermes CLI not found"}
|
|
55
63
|
# Drain both pipes from the start: a chatty restart must never block on a full pipe and be mistaken for a hang.
|
|
56
64
|
out: list[str] = []
|
|
57
65
|
err: list[str] = []
|
|
@@ -67,8 +75,8 @@ def restart(home: Path, cli_profile: str | None, ctx: CallContext, *, quick_time
|
|
|
67
75
|
def outcome() -> dict:
|
|
68
76
|
stdout, stderr = collected()
|
|
69
77
|
if proc.returncode == 0:
|
|
70
|
-
return {"status": "completed", "message": "Gateway service
|
|
71
|
-
return {"status": "failed", "message": f"
|
|
78
|
+
return {"status": "completed", "message": f"Gateway service {_DONE[action]} successfully", "detail": stdout or stderr}
|
|
79
|
+
return {"status": "failed", "message": f"{action.capitalize()} failed: {stderr or stdout}", "detail": stdout or stderr, "returncode": proc.returncode}
|
|
72
80
|
|
|
73
81
|
ctx.emit("progress", {"phase": "started"})
|
|
74
82
|
deadline = time.monotonic() + quick_timeout
|
|
@@ -90,17 +98,26 @@ def restart(home: Path, cli_profile: str | None, ctx: CallContext, *, quick_time
|
|
|
90
98
|
proc.kill()
|
|
91
99
|
proc.wait(timeout=5)
|
|
92
100
|
collected()
|
|
93
|
-
return {"status": "failed", "message": f"Gateway
|
|
101
|
+
return {"status": "failed", "message": f"Gateway {action} process timed out after {background_wait:.0f}s and was terminated", "timed_out": True}
|
|
94
102
|
return outcome()
|
|
95
103
|
finally:
|
|
96
|
-
|
|
104
|
+
_ACTION_LOCK.release()
|
|
97
105
|
|
|
98
106
|
|
|
99
107
|
def register(registry) -> None:
|
|
100
|
-
|
|
101
|
-
def restart_(ctx: CallContext, params: dict) -> dict:
|
|
108
|
+
def run(ctx: CallContext, params: dict, action: str) -> dict:
|
|
102
109
|
home = profile_home_param(params)
|
|
103
110
|
cli_profile = params.get("cli_profile")
|
|
104
111
|
if cli_profile is not None and not isinstance(cli_profile, str):
|
|
105
112
|
raise InvalidParams("cli_profile must be a string")
|
|
106
|
-
|
|
113
|
+
# A restart drains the old gateway first, so it gets longer than start or stop.
|
|
114
|
+
default_wait = 240.0 if action == "restart" else 60.0
|
|
115
|
+
return control(action, home, cli_profile or None, ctx, quick_timeout=float(params.get("quick_timeout_seconds") or 2.0), background_wait=float(params.get("background_wait_seconds") or default_wait))
|
|
116
|
+
|
|
117
|
+
@registry.method("gateway.control", requires_agent=False)
|
|
118
|
+
def control_(ctx: CallContext, params: dict) -> dict:
|
|
119
|
+
return run(ctx, params, str(params.get("action") or ""))
|
|
120
|
+
|
|
121
|
+
@registry.method("gateway.restart", requires_agent=False)
|
|
122
|
+
def restart_(ctx: CallContext, params: dict) -> dict:
|
|
123
|
+
return run(ctx, params, "restart")
|
|
@@ -8,6 +8,7 @@ server; the sidecar receives typed params.
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
import json
|
|
11
|
+
import secrets
|
|
11
12
|
import time
|
|
12
13
|
from dataclasses import asdict, is_dataclass
|
|
13
14
|
from typing import Any
|
|
@@ -82,10 +83,120 @@ def _obj_dict(value):
|
|
|
82
83
|
return dict(getattr(value, "__dict__", {}))
|
|
83
84
|
|
|
84
85
|
|
|
85
|
-
|
|
86
|
+
LIVE_CLAIM_MESSAGE = "task has a live worker; archive it to stop the worker"
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _row_claim_live(row) -> bool:
|
|
90
|
+
"""The Agent's ``_claim_is_live`` for a ``tasks`` row: a running task's worker process still holds its claim.
|
|
91
|
+
|
|
92
|
+
Releasing that claim (block, a direct status change) leaves the worker running, so the card
|
|
93
|
+
policy withholds those writes and the write paths refuse them (TAL-557). Unknown liveness counts as live.
|
|
94
|
+
"""
|
|
95
|
+
if row is None or row["status"] != "running":
|
|
96
|
+
return False
|
|
97
|
+
columns = row.keys()
|
|
98
|
+
claim = {k: row[k] if k in columns else None for k in ("status", "claim_lock", "worker_pid", "worker_started_at")}
|
|
99
|
+
claimed = bool(claim["claim_lock"] and claim["worker_pid"])
|
|
100
|
+
try:
|
|
101
|
+
helper = getattr(_kb(), "_claim_is_live", None)
|
|
102
|
+
except RpcError:
|
|
103
|
+
return claimed
|
|
104
|
+
if helper is not None:
|
|
105
|
+
try:
|
|
106
|
+
return bool(helper(claim))
|
|
107
|
+
except Exception: # noqa: BLE001 - the Agent could not decide: unknown liveness counts as live
|
|
108
|
+
return claimed
|
|
109
|
+
if not claimed:
|
|
110
|
+
return False
|
|
111
|
+
try:
|
|
112
|
+
# Agent 0.21.3 has no _claim_is_live, but its dispatcher's own worker PID check. Like its crash sweep, probe
|
|
113
|
+
# only a claim this host issued: another host's PID means nothing here, so a remote claim stays live.
|
|
114
|
+
from hermes_cli.kanban_db_dispatch import _pid_alive
|
|
115
|
+
|
|
116
|
+
if not str(claim["claim_lock"]).startswith(_kb()._host_prefix()):
|
|
117
|
+
return True
|
|
118
|
+
return bool(_pid_alive(int(claim["worker_pid"])))
|
|
119
|
+
except Exception: # noqa: BLE001
|
|
120
|
+
return True
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _claim_live(conn, task_id) -> bool:
|
|
124
|
+
return _row_claim_live(conn.execute("SELECT * FROM tasks WHERE id = ?", (task_id,)).fetchone())
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
RUNNING_EXIT_MESSAGE = "task is running; confirm leaving Running"
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _guard_running_exit(row, confirm_running_exit: bool) -> None:
|
|
131
|
+
"""A write that takes a task out of Running needs the client's explicit confirmation, judged on the row the
|
|
132
|
+
write sees: a client's card policy can predate a dispatcher claim (TAL-557)."""
|
|
133
|
+
if row["status"] == "running" and not confirm_running_exit:
|
|
134
|
+
raise Conflict(RUNNING_EXIT_MESSAGE)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
FENCE_PREFIX = "talaria-fence:"
|
|
138
|
+
FENCE_SECONDS = 60
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _clear_stale_fences(conn) -> None:
|
|
142
|
+
"""Release fences whose write never finished (the sidecar died mid-write), so the dispatcher can claim again."""
|
|
143
|
+
now = int(time.time())
|
|
144
|
+
stale = []
|
|
145
|
+
for row in conn.execute("SELECT id, claim_lock FROM tasks WHERE claim_lock LIKE ?", (FENCE_PREFIX + "%",)).fetchall():
|
|
146
|
+
try:
|
|
147
|
+
expires = int(str(row["claim_lock"])[len(FENCE_PREFIX):].split(":", 1)[0])
|
|
148
|
+
except ValueError:
|
|
149
|
+
expires = 0
|
|
150
|
+
if expires < now:
|
|
151
|
+
stale.append((row["id"], row["claim_lock"]))
|
|
152
|
+
if not stale:
|
|
153
|
+
return
|
|
154
|
+
kb = _kb()
|
|
155
|
+
with kb.write_txn(conn):
|
|
156
|
+
for task_id, fence in stale:
|
|
157
|
+
conn.execute("UPDATE tasks SET claim_lock = NULL WHERE id = ? AND claim_lock = ?", (task_id, fence))
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _fenced(conn, task_id: str, write, *, confirm_running_exit: bool, releases_claim: bool = True):
|
|
161
|
+
"""Run an Agent transition (block, complete, archive) with the dispatcher fenced out.
|
|
162
|
+
|
|
163
|
+
The Agent's helpers open their own write transaction, which cannot nest, so the checks and a fence lock share
|
|
164
|
+
one transaction first: ``claim_task`` claims only a ``ready`` task with ``claim_lock IS NULL``, so the card cannot
|
|
165
|
+
become a live run before the helper runs. ``releases_claim`` writes (block, complete) refuse a live worker's claim;
|
|
166
|
+
archive keeps it, because ``archive_task`` terminates that worker. The fence expires, so a write the sidecar never
|
|
167
|
+
finished stops holding the card once ``_clear_stale_fences`` next runs.
|
|
168
|
+
"""
|
|
169
|
+
kb = _kb()
|
|
170
|
+
_clear_stale_fences(conn)
|
|
171
|
+
fence = f"{FENCE_PREFIX}{int(time.time()) + FENCE_SECONDS}:{secrets.token_hex(8)}"
|
|
172
|
+
with kb.write_txn(conn):
|
|
173
|
+
row = conn.execute("SELECT * FROM tasks WHERE id = ?", (task_id,)).fetchone()
|
|
174
|
+
if row is None:
|
|
175
|
+
raise NotFound("task not found")
|
|
176
|
+
_guard_running_exit(row, confirm_running_exit)
|
|
177
|
+
live = _row_claim_live(row)
|
|
178
|
+
if live and releases_claim:
|
|
179
|
+
raise Conflict(LIVE_CLAIM_MESSAGE)
|
|
180
|
+
# Only an unclaimed ready task is claimable; anything else needs no fence.
|
|
181
|
+
fenced = row["status"] == "ready" and row["claim_lock"] is None
|
|
182
|
+
if fenced:
|
|
183
|
+
conn.execute("UPDATE tasks SET claim_lock = ? WHERE id = ?", (fence, task_id))
|
|
184
|
+
try:
|
|
185
|
+
return write()
|
|
186
|
+
finally:
|
|
187
|
+
if fenced:
|
|
188
|
+
with kb.write_txn(conn):
|
|
189
|
+
conn.execute("UPDATE tasks SET claim_lock = NULL WHERE id = ? AND claim_lock = ?", (task_id, fence))
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _task_dict(task, conn):
|
|
86
193
|
data = _obj_dict(task)
|
|
87
194
|
if not data:
|
|
88
195
|
return data
|
|
196
|
+
# Facts the server's card policy needs (TAL-557): the Agent completes a non-review task only with
|
|
197
|
+
# stored or supplied evidence, and refuses to release a live worker's claim.
|
|
198
|
+
data["claim_live"] = data.get("status") == "running" and _claim_live(conn, data.get("id"))
|
|
199
|
+
data["has_completion_evidence"] = bool(str(data.get("result") or "").strip())
|
|
89
200
|
try:
|
|
90
201
|
age = _kb().task_age(task)
|
|
91
202
|
except Exception: # noqa: BLE001
|
|
@@ -131,12 +242,16 @@ def _validate_status(status: str) -> str:
|
|
|
131
242
|
return value
|
|
132
243
|
|
|
133
244
|
|
|
134
|
-
def _set_status_direct(conn, task_id: str, new_status: str) -> bool:
|
|
245
|
+
def _set_status_direct(conn, task_id: str, new_status: str, *, confirm_running_exit: bool = False) -> bool:
|
|
135
246
|
kb = _kb()
|
|
136
247
|
with kb.write_txn(conn):
|
|
137
|
-
prev = conn.execute("SELECT
|
|
248
|
+
prev = conn.execute("SELECT * FROM tasks WHERE id = ?", (task_id,)).fetchone()
|
|
138
249
|
if prev is None:
|
|
139
250
|
return False
|
|
251
|
+
# Checked inside the write transaction, so a claim taken since the client's read is never released.
|
|
252
|
+
_guard_running_exit(prev, confirm_running_exit)
|
|
253
|
+
if _row_claim_live(prev):
|
|
254
|
+
raise Conflict(LIVE_CLAIM_MESSAGE)
|
|
140
255
|
was_running = prev["status"] == "running"
|
|
141
256
|
cur = conn.execute(
|
|
142
257
|
"UPDATE tasks SET status = ?, "
|
|
@@ -202,14 +317,15 @@ def _patch_task(conn, task_id: str, body: dict) -> None:
|
|
|
202
317
|
if "status" not in body or body.get("status") in (None, ""):
|
|
203
318
|
return
|
|
204
319
|
status = _validate_status(body.get("status"))
|
|
320
|
+
confirm = bool(body.get("confirm_running_exit"))
|
|
205
321
|
if status == "done":
|
|
206
|
-
if not kb.complete_task(conn, task_id, result=body.get("result"), summary=body.get("summary")):
|
|
322
|
+
if not _fenced(conn, task_id, lambda: kb.complete_task(conn, task_id, result=body.get("result"), summary=body.get("summary")), confirm_running_exit=confirm):
|
|
207
323
|
raise NotFound("task not found")
|
|
208
324
|
elif status == "blocked":
|
|
209
|
-
if not kb.block_task(conn, task_id, reason=body.get("block_reason") or body.get("reason")):
|
|
325
|
+
if not _fenced(conn, task_id, lambda: kb.block_task(conn, task_id, reason=body.get("block_reason") or body.get("reason")), confirm_running_exit=confirm):
|
|
210
326
|
raise NotFound("task not found")
|
|
211
327
|
elif status == "archived":
|
|
212
|
-
if not kb.archive_task(conn, task_id):
|
|
328
|
+
if not _fenced(conn, task_id, lambda: kb.archive_task(conn, task_id), confirm_running_exit=confirm, releases_claim=False):
|
|
213
329
|
raise NotFound("task not found")
|
|
214
330
|
elif status == "running":
|
|
215
331
|
raise InvalidParams("Cannot set status to 'running' directly; use the dispatcher/claim path")
|
|
@@ -220,10 +336,10 @@ def _patch_task(conn, task_id: str, body: dict) -> None:
|
|
|
220
336
|
if current.status == "blocked":
|
|
221
337
|
if not kb.unblock_task(conn, task_id):
|
|
222
338
|
raise NotFound("task not found")
|
|
223
|
-
elif not _set_status_direct(conn, task_id, "ready"):
|
|
339
|
+
elif not _set_status_direct(conn, task_id, "ready", confirm_running_exit=confirm):
|
|
224
340
|
raise NotFound("task not found")
|
|
225
341
|
elif status in ("triage", "todo"):
|
|
226
|
-
if not _set_status_direct(conn, task_id, status):
|
|
342
|
+
if not _set_status_direct(conn, task_id, status, confirm_running_exit=confirm):
|
|
227
343
|
raise NotFound("task not found")
|
|
228
344
|
|
|
229
345
|
|
|
@@ -320,6 +436,7 @@ def board_payload(params: dict) -> dict:
|
|
|
320
436
|
profile = _str(params, "profile") or "default"
|
|
321
437
|
assignee = profile
|
|
322
438
|
with _conn(board=board) as conn:
|
|
439
|
+
_clear_stale_fences(conn)
|
|
323
440
|
latest = _latest_event_id(conn)
|
|
324
441
|
if since is not None and since >= latest:
|
|
325
442
|
return {"changed": False, "latest_event_id": latest, "read_only": False}
|
|
@@ -327,7 +444,7 @@ def board_payload(params: dict) -> dict:
|
|
|
327
444
|
link_counts, comment_counts = _link_counts(conn, tasks), _comment_counts(conn)
|
|
328
445
|
|
|
329
446
|
def row(task):
|
|
330
|
-
data = _task_dict(task)
|
|
447
|
+
data = _task_dict(task, conn)
|
|
331
448
|
data["link_counts"] = link_counts.get(task.id, {"parents": 0, "children": 0})
|
|
332
449
|
data["comment_count"] = comment_counts.get(task.id, 0)
|
|
333
450
|
return data
|
|
@@ -365,7 +482,7 @@ def create_task_payload(params: dict) -> dict:
|
|
|
365
482
|
)
|
|
366
483
|
if body.get("status"):
|
|
367
484
|
_patch_task(conn, task_id, {"status": body.get("status")})
|
|
368
|
-
return {"task": _task_dict(kb.get_task(conn, task_id)), "read_only": False}
|
|
485
|
+
return {"task": _task_dict(kb.get_task(conn, task_id), conn), "read_only": False}
|
|
369
486
|
|
|
370
487
|
|
|
371
488
|
def patch_task_payload(params: dict) -> dict:
|
|
@@ -375,7 +492,7 @@ def patch_task_payload(params: dict) -> dict:
|
|
|
375
492
|
kb = _kb()
|
|
376
493
|
with _conn(board=_board(params)) as conn:
|
|
377
494
|
_patch_task(conn, task_id, params.get("patch") or {})
|
|
378
|
-
return {"task": _task_dict(kb.get_task(conn, task_id)), "read_only": False}
|
|
495
|
+
return {"task": _task_dict(kb.get_task(conn, task_id), conn), "read_only": False}
|
|
379
496
|
|
|
380
497
|
|
|
381
498
|
def comment_payload(params: dict) -> dict:
|
|
@@ -418,7 +535,7 @@ def task_detail_payload(params: dict) -> dict:
|
|
|
418
535
|
if not task:
|
|
419
536
|
raise NotFound("task not found")
|
|
420
537
|
return {
|
|
421
|
-
"task": _task_dict(task),
|
|
538
|
+
"task": _task_dict(task, conn),
|
|
422
539
|
"comments": [_obj_dict(c) for c in kb.list_comments(conn, task_id)],
|
|
423
540
|
"events": [_obj_dict(e) for e in kb.list_events(conn, task_id)],
|
|
424
541
|
"links": {"parents": kb.parent_ids(conn, task_id), "children": kb.child_ids(conn, task_id)},
|
|
@@ -533,7 +650,8 @@ def bulk_payload(params: dict) -> dict:
|
|
|
533
650
|
results.append(entry)
|
|
534
651
|
continue
|
|
535
652
|
if body.get("archive"):
|
|
536
|
-
|
|
653
|
+
# Bulk carries no running-exit confirmation, so a running row is refused like a card write.
|
|
654
|
+
if not _fenced(conn, task_id, lambda: kb.archive_task(conn, task_id), confirm_running_exit=False, releases_claim=False):
|
|
537
655
|
entry.update(ok=False, error="archive refused")
|
|
538
656
|
elif body.get("status") is not None:
|
|
539
657
|
_patch_task(conn, task_id, {"status": body.get("status")})
|
|
@@ -555,11 +673,14 @@ def bulk_payload(params: dict) -> dict:
|
|
|
555
673
|
|
|
556
674
|
|
|
557
675
|
def dispatch_payload(params: dict) -> dict:
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
676
|
+
_kb()
|
|
677
|
+
try:
|
|
678
|
+
# Agent 0.21.6 dropped the kanban_db re-export; every supported release defines it here.
|
|
679
|
+
from hermes_cli.kanban_db_dispatch import dispatch_once
|
|
680
|
+
except Exception as exc: # noqa: BLE001
|
|
681
|
+
raise InvalidParams("dispatcher is unavailable") from exc
|
|
561
682
|
with _conn(board=_board(params)) as conn:
|
|
562
|
-
result =
|
|
683
|
+
result = dispatch_once(conn, dry_run=_bool(params, "dry_run"), max_spawn=_int(params, "max", 8, minimum=1, maximum=100))
|
|
563
684
|
if isinstance(result, dict):
|
|
564
685
|
return result
|
|
565
686
|
try:
|
|
@@ -578,7 +699,7 @@ def task_action_payload(params: dict) -> dict:
|
|
|
578
699
|
if not kb.get_task(conn, task_id):
|
|
579
700
|
raise NotFound("task not found")
|
|
580
701
|
if action == "block":
|
|
581
|
-
ok = kb.block_task(conn, task_id, reason=params.get("reason"))
|
|
702
|
+
ok = _fenced(conn, task_id, lambda: kb.block_task(conn, task_id, reason=params.get("reason")), confirm_running_exit=bool(params.get("confirm_running_exit")))
|
|
582
703
|
elif action == "unblock":
|
|
583
704
|
if hasattr(kb, "unblock_task"):
|
|
584
705
|
ok = kb.unblock_task(conn, task_id)
|
|
@@ -589,7 +710,7 @@ def task_action_payload(params: dict) -> dict:
|
|
|
589
710
|
raise InvalidParams(f"invalid action: {action}")
|
|
590
711
|
if not ok:
|
|
591
712
|
raise Conflict(f"{action} refused")
|
|
592
|
-
return {"task": _task_dict(kb.get_task(conn, task_id)), "read_only": False}
|
|
713
|
+
return {"task": _task_dict(kb.get_task(conn, task_id), conn), "read_only": False}
|
|
593
714
|
|
|
594
715
|
|
|
595
716
|
def list_boards_payload(params: dict) -> dict:
|