@maudecode/talaria-web 1.18.0 → 1.19.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.js +16 -16
- package/dist/api/auth-raw.js.map +1 -1
- package/dist/api/automation-raw.d.ts +2 -2
- package/dist/api/automation-raw.js +32 -32
- package/dist/api/automation-raw.js.map +1 -1
- package/dist/api/automation-router.d.ts +102 -22
- package/dist/api/automation-router.js +15 -13
- package/dist/api/automation-router.js.map +1 -1
- package/dist/api/chat-router.d.ts +1205 -34
- package/dist/api/chat-router.js +158 -78
- package/dist/api/chat-router.js.map +1 -1
- package/dist/api/git-router.d.ts +2 -2
- package/dist/api/git-router.js +29 -29
- package/dist/api/git-router.js.map +1 -1
- package/dist/api/raw-routes.d.ts +3 -3
- package/dist/api/raw-routes.js +63 -61
- package/dist/api/raw-routes.js.map +1 -1
- package/dist/api/router.d.ts +6 -6
- package/dist/api/router.js +22 -22
- package/dist/api/router.js.map +1 -1
- package/dist/api/session-visibility.d.ts +4 -4
- package/dist/api/session-visibility.js +26 -16
- package/dist/api/session-visibility.js.map +1 -1
- package/dist/api/sessions-router.d.ts +1928 -124
- package/dist/api/sessions-router.js +177 -173
- package/dist/api/sessions-router.js.map +1 -1
- package/dist/api/settings-router.d.ts +427 -9
- package/dist/api/settings-router.js +48 -32
- package/dist/api/settings-router.js.map +1 -1
- package/dist/api/sse-routes.d.ts +2 -2
- package/dist/api/sse-routes.js +63 -63
- package/dist/api/sse-routes.js.map +1 -1
- package/dist/api/tools-raw.d.ts +5 -0
- package/dist/api/tools-raw.js +58 -33
- package/dist/api/tools-raw.js.map +1 -1
- package/dist/api/tools-router.d.ts +663 -91
- package/dist/api/tools-router.js +309 -52
- package/dist/api/tools-router.js.map +1 -1
- package/dist/api/transcribe-stream.d.ts +16 -0
- package/dist/api/transcribe-stream.js +172 -0
- package/dist/api/transcribe-stream.js.map +1 -0
- package/dist/app.d.ts +4 -1
- package/dist/app.js +66 -47
- package/dist/app.js.map +1 -1
- package/dist/auth/gate.js +10 -9
- package/dist/auth/gate.js.map +1 -1
- package/dist/auth/passkeys.d.ts +15 -14
- package/dist/auth/passkeys.js +89 -91
- package/dist/auth/passkeys.js.map +1 -1
- package/dist/auth/store.d.ts +17 -7
- package/dist/auth/store.js +93 -52
- package/dist/auth/store.js.map +1 -1
- package/dist/bin/talaria-web.js +12 -81
- package/dist/bin/talaria-web.js.map +1 -1
- package/dist/cli/ctl.js +2 -1
- package/dist/cli/ctl.js.map +1 -1
- package/dist/cli/dotenv.d.ts +5 -0
- package/dist/cli/dotenv.js +14 -2
- package/dist/cli/dotenv.js.map +1 -1
- package/dist/cli/launcher.js +5 -5
- package/dist/cli/launcher.js.map +1 -1
- package/dist/cli/main.d.ts +1 -0
- package/dist/cli/main.js +85 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/supervise.d.ts +2 -0
- package/dist/cli/supervise.js +67 -4
- package/dist/cli/supervise.js.map +1 -1
- package/dist/config/agent-config.d.ts +34 -11
- package/dist/config/agent-config.js +113 -58
- package/dist/config/agent-config.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.js +14 -5
- package/dist/config.js.map +1 -1
- package/dist/fs/atomic.d.ts +1 -0
- package/dist/fs/atomic.js +4 -1
- package/dist/fs/atomic.js.map +1 -1
- package/dist/fs/stat.d.ts +1 -0
- package/dist/fs/stat.js +11 -0
- package/dist/fs/stat.js.map +1 -0
- package/dist/http/context.d.ts +14 -9
- package/dist/http/context.js +29 -20
- package/dist/http/context.js.map +1 -1
- package/dist/onboarding.d.ts +2 -2
- package/dist/onboarding.js +10 -8
- package/dist/onboarding.js.map +1 -1
- package/dist/profiles/profiles.d.ts +10 -4
- package/dist/profiles/profiles.js +77 -30
- package/dist/profiles/profiles.js.map +1 -1
- package/dist/projects.d.ts +15 -6
- package/dist/projects.js +55 -47
- package/dist/projects.js.map +1 -1
- package/dist/providers/catalog.d.ts +78 -12
- package/dist/providers/catalog.js +445 -141
- package/dist/providers/catalog.js.map +1 -1
- package/dist/providers/env-file.d.ts +10 -3
- package/dist/providers/env-file.js +24 -75
- package/dist/providers/env-file.js.map +1 -1
- package/dist/providers/runtime-env.d.ts +4 -2
- package/dist/providers/runtime-env.js +9 -6
- package/dist/providers/runtime-env.js.map +1 -1
- package/dist/providers/tables.d.ts +3 -1
- package/dist/providers/tables.js +4 -2
- package/dist/providers/tables.js.map +1 -1
- package/dist/redact.js +18 -9
- package/dist/redact.js.map +1 -1
- package/dist/release.d.ts +7 -0
- package/dist/release.js +14 -0
- package/dist/release.js.map +1 -1
- package/dist/runtime.d.ts +5 -1
- package/dist/runtime.js +233 -90
- package/dist/runtime.js.map +1 -1
- package/dist/server.js +10 -0
- package/dist/server.js.map +1 -1
- package/dist/sessions/anchor.d.ts +9 -3
- package/dist/sessions/anchor.js +28 -36
- package/dist/sessions/anchor.js.map +1 -1
- package/dist/sessions/background-events.d.ts +2 -0
- package/dist/sessions/background-events.js +10 -0
- package/dist/sessions/background-events.js.map +1 -0
- package/dist/sessions/background-tasks.d.ts +26 -20
- package/dist/sessions/background-tasks.js +88 -86
- package/dist/sessions/background-tasks.js.map +1 -1
- package/dist/sessions/background-updates.d.ts +15 -8
- package/dist/sessions/background-updates.js +101 -25
- package/dist/sessions/background-updates.js.map +1 -1
- package/dist/sessions/cli-sessions.d.ts +15 -5
- package/dist/sessions/cli-sessions.js +62 -43
- package/dist/sessions/cli-sessions.js.map +1 -1
- package/dist/sessions/completions.d.ts +30 -2
- package/dist/sessions/completions.js +294 -53
- package/dist/sessions/completions.js.map +1 -1
- package/dist/sessions/compress.d.ts +8 -0
- package/dist/sessions/compress.js +2 -2
- package/dist/sessions/compress.js.map +1 -1
- package/dist/sessions/drafts.d.ts +14 -10
- package/dist/sessions/drafts.js +38 -43
- package/dist/sessions/drafts.js.map +1 -1
- package/dist/sessions/gateway-sessions.d.ts +46 -0
- package/dist/sessions/gateway-sessions.js +378 -0
- package/dist/sessions/gateway-sessions.js.map +1 -0
- package/dist/sessions/goals.d.ts +31 -0
- package/dist/sessions/goals.js +75 -0
- package/dist/sessions/goals.js.map +1 -0
- package/dist/sessions/journal.d.ts +5 -79
- package/dist/sessions/journal.js +15 -606
- package/dist/sessions/journal.js.map +1 -1
- package/dist/sessions/list.d.ts +27 -18
- package/dist/sessions/list.js +133 -131
- package/dist/sessions/list.js.map +1 -1
- package/dist/sessions/merge.d.ts +68 -10
- package/dist/sessions/merge.js +259 -51
- package/dist/sessions/merge.js.map +1 -1
- package/dist/sessions/notices.d.ts +22 -0
- package/dist/sessions/notices.js +72 -0
- package/dist/sessions/notices.js.map +1 -0
- package/dist/sessions/pending.d.ts +10 -4
- package/dist/sessions/pending.js +83 -7
- package/dist/sessions/pending.js.map +1 -1
- package/dist/sessions/prefix-index.d.ts +82 -0
- package/dist/sessions/prefix-index.js +232 -0
- package/dist/sessions/prefix-index.js.map +1 -0
- package/dist/sessions/recovery.d.ts +4 -7
- package/dist/sessions/recovery.js +97 -281
- package/dist/sessions/recovery.js.map +1 -1
- package/dist/sessions/relay.d.ts +20 -20
- package/dist/sessions/relay.js +205 -127
- package/dist/sessions/relay.js.map +1 -1
- package/dist/sessions/service.d.ts +162 -59
- package/dist/sessions/service.js +1133 -521
- package/dist/sessions/service.js.map +1 -1
- package/dist/sessions/session.d.ts +18 -2
- package/dist/sessions/session.js +29 -5
- package/dist/sessions/session.js.map +1 -1
- package/dist/sessions/shares.d.ts +7 -6
- package/dist/sessions/shares.js +22 -45
- package/dist/sessions/shares.js.map +1 -1
- package/dist/sessions/source-kind.js +1 -0
- package/dist/sessions/source-kind.js.map +1 -1
- package/dist/sessions/state-db-identity.d.ts +18 -0
- package/dist/sessions/state-db-identity.js +67 -0
- package/dist/sessions/state-db-identity.js.map +1 -0
- package/dist/sessions/state-db-worker.js +3 -1
- package/dist/sessions/state-db-worker.js.map +1 -1
- package/dist/sessions/state-db.d.ts +53 -8
- package/dist/sessions/state-db.js +576 -50
- package/dist/sessions/state-db.js.map +1 -1
- package/dist/sessions/storage/backend.d.ts +162 -0
- package/dist/sessions/storage/backend.js +19 -0
- package/dist/sessions/storage/backend.js.map +1 -0
- package/dist/sessions/storage/conformance.d.ts +20 -0
- package/dist/sessions/storage/conformance.js +237 -0
- package/dist/sessions/storage/conformance.js.map +1 -0
- package/dist/sessions/storage/document.d.ts +16 -0
- package/dist/sessions/storage/document.js +44 -0
- package/dist/sessions/storage/document.js.map +1 -0
- package/dist/sessions/storage/json-documents.d.ts +23 -0
- package/dist/sessions/storage/json-documents.js +233 -0
- package/dist/sessions/storage/json-documents.js.map +1 -0
- package/dist/sessions/storage/json-files.d.ts +12 -0
- package/dist/sessions/storage/json-files.js +124 -0
- package/dist/sessions/storage/json-files.js.map +1 -0
- package/dist/sessions/storage/json-installation.d.ts +3 -0
- package/dist/sessions/storage/json-installation.js +31 -0
- package/dist/sessions/storage/json-installation.js.map +1 -0
- package/dist/sessions/storage/json-journal.d.ts +114 -0
- package/dist/sessions/storage/json-journal.js +686 -0
- package/dist/sessions/storage/json-journal.js.map +1 -0
- package/dist/sessions/storage/json-recovery.d.ts +4 -0
- package/dist/sessions/storage/json-recovery.js +195 -0
- package/dist/sessions/storage/json-recovery.js.map +1 -0
- package/dist/sessions/storage/json.d.ts +68 -0
- package/dist/sessions/storage/json.js +258 -0
- package/dist/sessions/storage/json.js.map +1 -0
- package/dist/sessions/storage/row.d.ts +6 -0
- package/dist/sessions/storage/row.js +49 -0
- package/dist/sessions/storage/row.js.map +1 -0
- package/dist/sessions/storage/search-text.d.ts +4 -0
- package/dist/sessions/storage/search-text.js +16 -0
- package/dist/sessions/storage/search-text.js.map +1 -0
- package/dist/sessions/store.d.ts +63 -73
- package/dist/sessions/store.js +305 -611
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/streams.d.ts +4 -0
- package/dist/sessions/streams.js +9 -1
- package/dist/sessions/streams.js.map +1 -1
- package/dist/sessions/titles.js +1 -1
- package/dist/sessions/titles.js.map +1 -1
- package/dist/sessions/todo.d.ts +5 -0
- package/dist/sessions/todo.js +28 -21
- package/dist/sessions/todo.js.map +1 -1
- package/dist/sessions/tool-display.d.ts +5 -4
- package/dist/sessions/tool-display.js +22 -16
- package/dist/sessions/tool-display.js.map +1 -1
- package/dist/sessions/turn-context.d.ts +10 -1
- package/dist/sessions/turn-context.js +7 -4
- package/dist/sessions/turn-context.js.map +1 -1
- package/dist/sessions/turn.d.ts +87 -17
- package/dist/sessions/turn.js +795 -347
- package/dist/sessions/turn.js.map +1 -1
- package/dist/sessions/wakeup-pause.d.ts +8 -13
- package/dist/sessions/wakeup-pause.js +31 -49
- package/dist/sessions/wakeup-pause.js.map +1 -1
- package/dist/sessions/window.js +13 -12
- package/dist/sessions/window.js.map +1 -1
- package/dist/settings.d.ts +19 -7
- package/dist/settings.js +179 -183
- package/dist/settings.js.map +1 -1
- package/dist/sidecar/client.d.ts +17 -3
- package/dist/sidecar/client.js +78 -8
- package/dist/sidecar/client.js.map +1 -1
- package/dist/sidecar/discover.d.ts +2 -1
- package/dist/sidecar/discover.js +93 -10
- package/dist/sidecar/discover.js.map +1 -1
- package/dist/sidecar/env.d.ts +4 -0
- package/dist/sidecar/env.js +3 -0
- package/dist/sidecar/env.js.map +1 -0
- package/dist/sidecar/fake.js +19 -2
- package/dist/sidecar/fake.js.map +1 -1
- package/dist/test/env-file.d.ts +5 -0
- package/dist/test/env-file.js +88 -0
- package/dist/test/env-file.js.map +1 -0
- package/dist/test/harness.d.ts +1 -0
- package/dist/test/harness.js +1 -1
- package/dist/test/harness.js.map +1 -1
- package/dist/test/passkeys.d.ts +22 -0
- package/dist/test/passkeys.js +62 -0
- package/dist/test/passkeys.js.map +1 -0
- package/dist/test/storage.d.ts +15 -0
- package/dist/test/storage.js +26 -0
- package/dist/test/storage.js.map +1 -0
- package/dist/tools/commands.d.ts +6 -1
- package/dist/tools/commands.js +8 -2
- package/dist/tools/commands.js.map +1 -1
- package/dist/tools/connectors.d.ts +33 -0
- package/dist/tools/connectors.js +121 -0
- package/dist/tools/connectors.js.map +1 -0
- package/dist/tools/crons.d.ts +38 -1
- package/dist/tools/crons.js +82 -15
- package/dist/tools/crons.js.map +1 -1
- package/dist/tools/dashboard-plugins.d.ts +5 -5
- package/dist/tools/dashboard-plugins.js +14 -14
- package/dist/tools/dashboard-plugins.js.map +1 -1
- package/dist/tools/extensions.d.ts +5 -4
- package/dist/tools/extensions.js +213 -207
- package/dist/tools/extensions.js.map +1 -1
- package/dist/tools/gateway-restart.d.ts +62 -0
- package/dist/tools/gateway-restart.js +142 -0
- package/dist/tools/gateway-restart.js.map +1 -0
- package/dist/tools/health.d.ts +15 -3
- package/dist/tools/health.js +97 -34
- package/dist/tools/health.js.map +1 -1
- package/dist/tools/hygiene.d.ts +4 -3
- package/dist/tools/hygiene.js +23 -9
- package/dist/tools/hygiene.js.map +1 -1
- package/dist/tools/kanban.d.ts +4 -2
- package/dist/tools/kanban.js +28 -19
- package/dist/tools/kanban.js.map +1 -1
- package/dist/tools/memory-masks.d.ts +5 -0
- package/dist/tools/memory-masks.js +256 -0
- package/dist/tools/memory-masks.js.map +1 -0
- package/dist/tools/memory.d.ts +29 -3
- package/dist/tools/memory.js +69 -27
- package/dist/tools/memory.js.map +1 -1
- package/dist/tools/plugin-settings.d.ts +15 -0
- package/dist/tools/plugin-settings.js +90 -0
- package/dist/tools/plugin-settings.js.map +1 -0
- package/dist/tools/skills.d.ts +9 -4
- package/dist/tools/skills.js +44 -13
- package/dist/tools/skills.js.map +1 -1
- package/dist/tools/update-notifications.d.ts +34 -15
- package/dist/tools/update-notifications.js +141 -57
- package/dist/tools/update-notifications.js.map +1 -1
- package/dist/tools/update-rollback.d.ts +11 -0
- package/dist/tools/update-rollback.js +28 -0
- package/dist/tools/update-rollback.js.map +1 -0
- package/dist/tools/updates.d.ts +93 -33
- package/dist/tools/updates.js +1051 -345
- package/dist/tools/updates.js.map +1 -1
- package/dist/util.d.ts +2 -0
- package/dist/util.js +5 -0
- package/dist/util.js.map +1 -1
- package/dist/workspace/git.d.ts +16 -1
- package/dist/workspace/git.js +291 -11
- package/dist/workspace/git.js.map +1 -1
- package/dist/workspace/media.d.ts +12 -9
- package/dist/workspace/media.js +31 -20
- package/dist/workspace/media.js.map +1 -1
- package/dist/workspace/rollback.d.ts +2 -2
- package/dist/workspace/rollback.js +5 -5
- package/dist/workspace/rollback.js.map +1 -1
- package/dist/workspace/workspaces.d.ts +15 -19
- package/dist/workspace/workspaces.js +55 -127
- package/dist/workspace/workspaces.js.map +1 -1
- package/dist/workspace/worktrees.js +170 -32
- package/dist/workspace/worktrees.js.map +1 -1
- package/package.json +5 -3
- package/sidecar/agent_dependency.json +4 -4
- package/sidecar/agent_support.json +26 -0
- package/sidecar/scripts/record-fixtures.py +12 -3
- package/sidecar/scripts/replay_sidecar.py +8 -0
- package/sidecar/scripts/suite_runner.py +302 -0
- package/sidecar/scripts/test.sh +12 -4
- package/sidecar/talaria_sidecar/__init__.py +6 -1
- package/sidecar/talaria_sidecar/__main__.py +4 -2
- package/sidecar/talaria_sidecar/agent_mutation.py +975 -0
- package/sidecar/talaria_sidecar/agent_services.py +1053 -0
- package/sidecar/talaria_sidecar/dependency_sync.py +85 -0
- package/sidecar/talaria_sidecar/home.py +43 -1
- package/sidecar/talaria_sidecar/methods/__init__.py +2 -2
- package/sidecar/talaria_sidecar/methods/aux.py +139 -31
- package/sidecar/talaria_sidecar/methods/chat.py +847 -134
- package/sidecar/talaria_sidecar/methods/commands.py +213 -4
- package/sidecar/talaria_sidecar/methods/config.py +123 -4
- package/sidecar/talaria_sidecar/methods/connectors.py +343 -0
- package/sidecar/talaria_sidecar/methods/cron.py +366 -34
- package/sidecar/talaria_sidecar/methods/cron_execution.py +284 -0
- package/sidecar/talaria_sidecar/methods/gateway.py +194 -54
- package/sidecar/talaria_sidecar/methods/gateway_sessions.py +234 -0
- package/sidecar/talaria_sidecar/methods/goals.py +81 -41
- package/sidecar/talaria_sidecar/methods/kanban.py +80 -27
- package/sidecar/talaria_sidecar/methods/memory.py +84 -0
- package/sidecar/talaria_sidecar/methods/oauth.py +5 -2
- package/sidecar/talaria_sidecar/methods/process.py +160 -11
- package/sidecar/talaria_sidecar/methods/profiles.py +44 -6
- package/sidecar/talaria_sidecar/methods/providers.py +196 -26
- package/sidecar/talaria_sidecar/methods/runtime.py +69 -1
- package/sidecar/talaria_sidecar/methods/skills.py +67 -4
- package/sidecar/talaria_sidecar/methods/state_db.py +277 -70
- package/sidecar/talaria_sidecar/methods/stt.py +159 -78
- package/sidecar/talaria_sidecar/methods/text.py +14 -1
- package/sidecar/talaria_sidecar/methods/toolsets.py +49 -0
- package/sidecar/talaria_sidecar/methods/usage.py +113 -14
- package/sidecar/talaria_sidecar/methods/worktree.py +26 -19
- package/sidecar/talaria_sidecar/methods/write_approval.py +181 -0
- package/sidecar/talaria_sidecar/rpc.py +45 -4
- package/sidecar/talaria_sidecar/runtime.py +81 -12
- package/sidecar/talaria_sidecar/speed.py +35 -0
- package/static/dist/FILES.txt +126 -127
- package/static/dist/assets/AppShell-cey33v9s.js +8 -0
- package/static/dist/assets/ChatPage-i8wnugr7.js +18 -0
- package/static/dist/assets/ConnectTargets-hg19553x.js +1 -0
- package/static/dist/assets/ExtensionRoute-cly4apm5.js +1 -0
- package/static/dist/assets/Field-j8uqoigz.js +9 -0
- package/static/dist/assets/HubRoute-e4bgz41j.js +2 -0
- package/static/dist/assets/Markdown-lahuichm.js +598 -0
- package/static/dist/assets/RightPanel-lbo4i0fn.js +1 -0
- package/static/dist/assets/Select-gqatm2e0.js +1 -0
- package/static/dist/assets/SessionListPanel-mfqayfjh.js +1 -0
- package/static/dist/assets/SettingsLayout-b8980u7k.js +1 -0
- package/static/dist/assets/States-e7dkgtxf.js +1 -0
- package/static/dist/assets/Toaster-bfqqy786.js +1 -0
- package/static/dist/assets/_app-n1n6pfx1.js +1 -0
- package/static/dist/assets/{_app.ext._extensionId-gfbzpoou.js → _app.ext._extensionId-buxjxav3.js} +1 -1
- package/static/dist/assets/_app.index-jt8zwiu2.js +1 -0
- package/static/dist/assets/_app.insights-cv76ml71.js +1 -0
- package/static/dist/assets/_app.kanban-h6ts53j7.js +1 -0
- package/static/dist/assets/_app.logs-dm873z90.js +1 -0
- package/static/dist/assets/_app.memory-glk5y6m8.js +1 -0
- package/static/dist/assets/_app.profiles-ka6zpstq.js +1 -0
- package/static/dist/assets/{_app.session._sessionId-fn1bhde6.js → _app.session._sessionId-g2vzghvc.js} +1 -1
- package/static/dist/assets/_app.settings-hgjdhzcd.js +1 -0
- package/static/dist/assets/_app.settings._section-jf2kmhae.js +3 -0
- package/static/dist/assets/_app.skills-cii852yq.js +1 -0
- package/static/dist/assets/_app.tasks-fp9q7atm.js +1 -0
- package/static/dist/assets/_app.todos-dw94qzue.js +1 -0
- package/static/dist/assets/_app.workspaces-inyygzze.js +1 -0
- package/static/dist/assets/{abnfDiagram-VCTEODGH-c8kdixke.js → abnfDiagram-VCTEODGH-gjayq3ra.js} +1 -1
- package/static/dist/assets/architecture-7GRP2DOG-lp56972r.js +1 -0
- package/static/dist/assets/{architectureDiagram-5GKGNRK7-cku06x1p.js → architectureDiagram-5GKGNRK7-nwb3xovn.js} +1 -1
- package/static/dist/assets/archive-b1j004vn.js +1 -0
- package/static/dist/assets/back-g74gkozu.js +1 -0
- package/static/dist/assets/{blockDiagram-I7D4REHJ-jzthp1wa.js → blockDiagram-I7D4REHJ-joedghi3.js} +1 -1
- package/static/dist/assets/{c4Diagram-7LVT6UL2-mwr67mt8.js → c4Diagram-7LVT6UL2-hyrjrohe.js} +1 -1
- package/static/dist/assets/channel-es95o4y8.js +1 -0
- package/static/dist/assets/{chunk-4HAMMTFA-gpq1y228.js → chunk-4HAMMTFA-koouqbut.js} +1 -1
- package/static/dist/assets/{chunk-75Z2AOVW-ktje58c4.js → chunk-75Z2AOVW-bl4bhdnt.js} +1 -1
- package/static/dist/assets/{chunk-DU6HZSFF-ofajdixh.js → chunk-DU6HZSFF-gpedttsp.js} +3 -3
- package/static/dist/assets/{chunk-F27PBJKO-cmev85ti.js → chunk-F27PBJKO-d8xxi0zi.js} +1 -1
- package/static/dist/assets/{chunk-GMAD6QVW-htjgt22y.js → chunk-GMAD6QVW-jro0hl73.js} +1 -1
- package/static/dist/assets/{chunk-GVQU2GXP-qndvoxl7.js → chunk-GVQU2GXP-djn6gbyu.js} +1 -1
- package/static/dist/assets/{chunk-IMKFNOWR-e5bkd5q6.js → chunk-IMKFNOWR-vw4p5p7o.js} +1 -1
- package/static/dist/assets/{chunk-L3NEJ4N5-mj1155r6.js → chunk-L3NEJ4N5-n1hj77q8.js} +1 -1
- package/static/dist/assets/{chunk-OSK3NFVY-e7gkikcw.js → chunk-OSK3NFVY-oy0gwn2n.js} +1 -1
- package/static/dist/assets/{chunk-P2QGCYS3-f7t54xfm.js → chunk-P2QGCYS3-onf1hz6c.js} +1 -1
- package/static/dist/assets/{chunk-POPQ4Y6H-i927v9oa.js → chunk-POPQ4Y6H-m8sqn7cs.js} +1 -1
- package/static/dist/assets/{chunk-PWAF6VOD-m84lmnyt.js → chunk-PWAF6VOD-ek4smc8x.js} +1 -1
- package/static/dist/assets/{chunk-SHT3W25Y-mi0tlcu9.js → chunk-SHT3W25Y-g9gfvefo.js} +3 -3
- package/static/dist/assets/{chunk-SVP7TREG-dnau702j.js → chunk-SVP7TREG-f9nfv2cu.js} +1 -1
- package/static/dist/assets/{chunk-TICWLB2K-edem1gkg.js → chunk-TICWLB2K-bnk3m1mq.js} +1 -1
- package/static/dist/assets/classDiagram-ZZMXUADV-kmihoqxh.js +1 -0
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-kmihoqxh.js +1 -0
- package/static/dist/assets/copied-bli9tbcx.js +1 -0
- package/static/dist/assets/{copy-magtofyp.js → copy-jamtd1pf.js} +1 -1
- package/static/dist/assets/{cynefin-OW5HDTMX-siqlulkl.js → cynefin-OW5HDTMX-f8xfdd3f.js} +1 -1
- package/static/dist/assets/{cynefinDiagram-5FMLGOSQ-i648tah9.js → cynefinDiagram-5FMLGOSQ-n9lb99h9.js} +1 -1
- package/static/dist/assets/{dagre-GXQ25YYZ-mwn6uc17.js → dagre-GXQ25YYZ-dgailddi.js} +4 -4
- package/static/dist/assets/{diagram-S7CK7UJ4-l8abxaes.js → diagram-S7CK7UJ4-m6aeoe7c.js} +1 -1
- package/static/dist/assets/{diagram-UQ7AKVKN-mekq7zgq.js → diagram-UQ7AKVKN-jall3j0x.js} +1 -1
- package/static/dist/assets/{diagram-VSXAHHWV-fnky6lvc.js → diagram-VSXAHHWV-hbawi7hn.js} +1 -1
- package/static/dist/assets/{diagram-VX7I27RA-xe6r5qfw.js → diagram-VX7I27RA-hendl2tt.js} +1 -1
- package/static/dist/assets/{diagram-Z3DM3KII-nq07dg7k.js → diagram-Z3DM3KII-igwc35p3.js} +1 -1
- package/static/dist/assets/{ebnfDiagram-PWID7BFC-c62ipm3g.js → ebnfDiagram-PWID7BFC-c64knz08.js} +1 -1
- package/static/dist/assets/{erDiagram-RLTQ6QDP-h39kmnzy.js → erDiagram-RLTQ6QDP-brdu9lcm.js} +1 -1
- package/static/dist/assets/eventmodeling-NTZA5JFV-njk1j8wl.js +1 -0
- package/static/dist/assets/flowDiagram-HODETNUW-nma6f0ga.js +1 -0
- package/static/dist/assets/folder-plus-lvt9kudb.js +1 -0
- package/static/dist/assets/{ganttDiagram-EL5Y4UJY-ngbsyp37.js → ganttDiagram-EL5Y4UJY-ma6jxj5i.js} +1 -1
- package/static/dist/assets/{gitGraph-4MIJSDKK-fr2i7caa.js → gitGraph-4MIJSDKK-naa9w6xa.js} +1 -1
- package/static/dist/assets/{gitGraphDiagram-WWUBYQGX-eravdti4.js → gitGraphDiagram-WWUBYQGX-hzltvsod.js} +1 -1
- package/static/dist/assets/{highlighted-body-KPVGNVTW-jwpop9p8.js → highlighted-body-KPVGNVTW-ghoavj8d.js} +1 -1
- package/static/dist/assets/host-hzm8n85n.js +1 -0
- package/static/dist/assets/index-drdcaqeq.css +2 -0
- package/static/dist/assets/index-jzm844he.js +11 -0
- package/static/dist/assets/{info-A6RAGUB7-h11x0dxo.js → info-A6RAGUB7-mn2ut5ko.js} +1 -1
- package/static/dist/assets/{infoDiagram-27XIBGKW-dfh47dw9.js → infoDiagram-27XIBGKW-h6kuqvsb.js} +1 -1
- package/static/dist/assets/{ishikawaDiagram-5VMMS53U-i6ypq351.js → ishikawaDiagram-5VMMS53U-e7bnwq7h.js} +1 -1
- package/static/dist/assets/{journeyDiagram-3NMN7TZE-lduf1z95.js → journeyDiagram-3NMN7TZE-etd1bi6p.js} +1 -1
- package/static/dist/assets/{kanban-definition-UXKFOSKX-e18bu4pg.js → kanban-definition-UXKFOSKX-emb1g9dz.js} +4 -4
- package/static/dist/assets/lazyRouteComponent-o816ytzl.js +1 -0
- package/static/dist/assets/{line-meizqsvn.js → line-h0ojzsvs.js} +1 -1
- package/static/dist/assets/link-k01ryaf9.js +1 -0
- package/static/dist/assets/login-bsae49ew.js +1 -0
- package/static/dist/assets/mermaid-HWGCJPDP-ihl34s74.js +1 -0
- package/static/dist/assets/{mermaid-parser.core-ntne45i9.js → mermaid-parser.core-bnre27u8.js} +3 -3
- package/static/dist/assets/{mindmap-definition-YA3MSWOX-27b26eok.js → mindmap-definition-YA3MSWOX-o1vathns.js} +1 -1
- package/static/dist/assets/onboarding-c65tysya.js +1 -0
- package/static/dist/assets/onboarding_model_label-gpn28uix.js +1 -0
- package/static/dist/assets/{packet-AYTQ26CC-l6jdka8p.js → packet-AYTQ26CC-hatq7m2e.js} +1 -1
- package/static/dist/assets/pcmTap.worklet-74EKjWAZ.js +1 -0
- package/static/dist/assets/{pegDiagram-XKGWAZYB-g1er3bxj.js → pegDiagram-XKGWAZYB-gwisz8jv.js} +1 -1
- package/static/dist/assets/{pie-WAS4IAKB-i0ylcrt9.js → pie-WAS4IAKB-fpftf0mk.js} +1 -1
- package/static/dist/assets/{pieDiagram-E7YTZNPT-fbz1e55z.js → pieDiagram-E7YTZNPT-khm1o9a6.js} +1 -1
- package/static/dist/assets/{quadrantDiagram-AXDQQJYC-hhue7d8o.js → quadrantDiagram-AXDQQJYC-owqc4uqn.js} +1 -1
- package/static/dist/assets/{radar-RG4KPBEZ-f9b70aa4.js → radar-RG4KPBEZ-cntfwjpq.js} +1 -1
- package/static/dist/assets/{railroad-74A4TZTK-lgsuoeda.js → railroad-74A4TZTK-a7t5eibp.js} +1 -1
- package/static/dist/assets/railroad-abnf-HS5TGJTU-ol8cdktf.js +1 -0
- package/static/dist/assets/railroad-ebnf-LZEXJU2U-i8vzd429.js +1 -0
- package/static/dist/assets/railroad-peg-WCYAUIDC-nw3ulewz.js +1 -0
- package/static/dist/assets/{railroadDiagram-O6MQD6OU-eqpy9fnz.js → railroadDiagram-O6MQD6OU-etuh0qa0.js} +1 -1
- package/static/dist/assets/{refresh-cw-hlwb02qv.js → refresh-cw-mqbkv2a3.js} +1 -1
- package/static/dist/assets/refresh-l29ljp76.js +1 -0
- package/static/dist/assets/{requirementDiagram-BXWQKSXE-ogf6puhi.js → requirementDiagram-BXWQKSXE-oq2ulk22.js} +1 -1
- package/static/dist/assets/{sankeyDiagram-P5KCCOFB-fqb8xcjf.js → sankeyDiagram-P5KCCOFB-iqc02emv.js} +1 -1
- package/static/dist/assets/saved-ndd4t2xb.js +1 -0
- package/static/dist/assets/{sequenceDiagram-WJ2MYXX4-dlcz6qjz.js → sequenceDiagram-WJ2MYXX4-i0v116q0.js} +2 -2
- package/static/dist/assets/settings_section_extensions_title-jhfo6o8z.js +1 -0
- package/static/dist/assets/share._token-c1ri0hy1.js +2 -0
- package/static/dist/assets/sliders-horizontal-j3jjt9tv.js +1 -0
- package/static/dist/assets/{stateDiagram-D77RDMKH-fyoznpcs.js → stateDiagram-D77RDMKH-dnpt1ato.js} +1 -1
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-bxjacpk6.js +1 -0
- package/static/dist/assets/{swimlanes-42K2YHIH-oefldhfy.js → swimlanes-42K2YHIH-loqbdazb.js} +1 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-id62p9hz.js +8 -0
- package/static/dist/assets/{timeline-definition-24CTP7MA-b1vl2zil.js → timeline-definition-24CTP7MA-ol2hnvma.js} +1 -1
- package/static/dist/assets/toast-i3h34v6o.js +64 -0
- package/static/dist/assets/{treeView-Q6P3EWNA-f7hcci5o.js → treeView-Q6P3EWNA-idrj6iox.js} +1 -1
- package/static/dist/assets/{treemap-WGGIJYW6-nl85071a.js → treemap-WGGIJYW6-lzr49vv5.js} +1 -1
- package/static/dist/assets/untitled-oaoya4qj.js +1 -0
- package/static/dist/assets/useForm-nfaxgmsk.js +1 -0
- package/static/dist/assets/{useParams-b4dq6n4q.js → useParams-gjc70qru.js} +1 -1
- package/static/dist/assets/{useSearch-ipfmv3he.js → useSearch-e6ae539u.js} +1 -1
- package/static/dist/assets/{vennDiagram-4TSXK5OY-h5hzbk0n.js → vennDiagram-4TSXK5OY-bvwr9o2z.js} +1 -1
- package/static/dist/assets/{wardley-WFR3VGLG-jftvpm3e.js → wardley-WFR3VGLG-31ui8tma.js} +1 -1
- package/static/dist/assets/{wardleyDiagram-VM6X3IG4-hedh85ge.js → wardleyDiagram-VM6X3IG4-k5wqtx4b.js} +1 -1
- package/static/dist/assets/{xychartDiagram-S5SC5T6Z-e49523ab.js → xychartDiagram-S5SC5T6Z-aqbgvujg.js} +1 -1
- package/static/dist/index.html +1 -1
- package/static/dist/sw.js +1 -1
- package/static/dist/assets/AppShell-g3bkgnvt.js +0 -1
- package/static/dist/assets/ChatPage-oo7bdan6.js +0 -25
- package/static/dist/assets/Dialog-g355qxhp.js +0 -1
- package/static/dist/assets/ExtensionRoute-i2y9fvjm.js +0 -1
- package/static/dist/assets/HubRoute-lmkrbw47.js +0 -2
- package/static/dist/assets/Markdown-j4kut4j2.js +0 -598
- package/static/dist/assets/RightPanel-ohlsdzfl.js +0 -1
- package/static/dist/assets/Select-kddq75qc.js +0 -1
- package/static/dist/assets/SessionListPanel-lu3oy0hw.js +0 -1
- package/static/dist/assets/SettingsLayout-o9bv4mgb.js +0 -1
- package/static/dist/assets/States-eq6fe29m.js +0 -1
- package/static/dist/assets/Toaster-pc247u9f.js +0 -1
- package/static/dist/assets/_app-f8n1i0m1.js +0 -1
- package/static/dist/assets/_app.index-bzzv80hk.js +0 -1
- package/static/dist/assets/_app.insights-dzixbryi.js +0 -1
- package/static/dist/assets/_app.kanban-c6hep2us.js +0 -1
- package/static/dist/assets/_app.logs-i69qds98.js +0 -1
- package/static/dist/assets/_app.memory-kl4vf0gd.js +0 -1
- package/static/dist/assets/_app.profiles-b8zinmyp.js +0 -1
- package/static/dist/assets/_app.settings-cg8plwmf.js +0 -1
- package/static/dist/assets/_app.settings._section-bkwbi9zn.js +0 -3
- package/static/dist/assets/_app.skills-cs47sbvs.js +0 -1
- package/static/dist/assets/_app.tasks-dwvk64tc.js +0 -1
- package/static/dist/assets/_app.todos-c8xjm8p3.js +0 -1
- package/static/dist/assets/_app.workspaces-ehvd1gds.js +0 -1
- package/static/dist/assets/architecture-7GRP2DOG-6crvnylg.js +0 -1
- package/static/dist/assets/back-h7aun7ew.js +0 -1
- package/static/dist/assets/cancel-fhi0vcnu.js +0 -1
- package/static/dist/assets/channel-b2zo73z1.js +0 -1
- package/static/dist/assets/classDiagram-ZZMXUADV-oe99st8x.js +0 -1
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-oe99st8x.js +0 -1
- package/static/dist/assets/copied-hcplv46p.js +0 -1
- package/static/dist/assets/endpoints-klbxod4t.js +0 -64
- package/static/dist/assets/eventmodeling-NTZA5JFV-obfede4u.js +0 -1
- package/static/dist/assets/flowDiagram-HODETNUW-kdpsp1je.js +0 -1
- package/static/dist/assets/host-i828xuo7.js +0 -1
- package/static/dist/assets/index-eh8zilnv.css +0 -2
- package/static/dist/assets/index-r648f4o1.js +0 -11
- package/static/dist/assets/lazyRouteComponent-e1y4t2i9.js +0 -1
- package/static/dist/assets/link-oot2yn73.js +0 -1
- package/static/dist/assets/login-gu28l9ou.js +0 -1
- package/static/dist/assets/mermaid-HWGCJPDP-mtwrj8fn.js +0 -1
- package/static/dist/assets/onboarding-lkphzu1s.js +0 -1
- package/static/dist/assets/onboarding_model_label-ftpjdwqk.js +0 -1
- package/static/dist/assets/railroad-abnf-HS5TGJTU-nf9d6pdx.js +0 -1
- package/static/dist/assets/railroad-ebnf-LZEXJU2U-f7ummkt5.js +0 -1
- package/static/dist/assets/railroad-peg-WCYAUIDC-oczq5tqs.js +0 -1
- package/static/dist/assets/refresh-abtu93ke.js +0 -1
- package/static/dist/assets/saved-girnabkf.js +0 -1
- package/static/dist/assets/settings_section_extensions_title-h14cwk6f.js +0 -1
- package/static/dist/assets/share._token-kl0r7ido.js +0 -2
- package/static/dist/assets/sliders-horizontal-n1zm34qq.js +0 -1
- package/static/dist/assets/sse-iwku0sjo.js +0 -1
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-ixhx8r46.js +0 -1
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-h9ersk2e.js +0 -8
- package/static/dist/assets/todoStore-jjr5yr78.js +0 -1
- package/static/dist/assets/units-kgxzxfhi.js +0 -1
- package/static/dist/assets/untitled-cikfjntn.js +0 -1
- package/static/dist/assets/useForm-kqoqg0k9.js +0 -1
- package/static/dist/assets/useLocale-o59tbiea.js +0 -1
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"""Authenticated observations and fenced controls of an existing native owner.
|
|
2
|
+
|
|
3
|
+
Never ensure/start a gateway, build an Agent, or write its transcript locally.
|
|
4
|
+
Each call owns its connection and subscription; cancellation only detaches it.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
from contextlib import asynccontextmanager, closing, suppress
|
|
10
|
+
import json
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
import sqlite3
|
|
13
|
+
|
|
14
|
+
from ..errors import InvalidParams
|
|
15
|
+
from ..home import profile_home_param, scoped_home
|
|
16
|
+
|
|
17
|
+
_ACTIONS = {"snapshot", "watch", "submit", "receipt", "cancel", "interrupt", "approval", "clarify", "discard", "mutate"}
|
|
18
|
+
_CAPABILITIES = {"durable-admission-v1", "event-replay-v1"}
|
|
19
|
+
_CONTROL_REFUSALS = {"native_clarify_batch_unavailable", "owner_controls_unverified"}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _control_refusal(home: Path, sid: str) -> str | None:
|
|
23
|
+
"""Only canonical PendingControls can be recovered on v0.22.1. Never infer a missing batch.
|
|
24
|
+
|
|
25
|
+
Read the owner's committed admission in a read-only connection; opening SessionDB here
|
|
26
|
+
would run its schema writer alongside the owner. Compression keeps the logical target.
|
|
27
|
+
"""
|
|
28
|
+
try:
|
|
29
|
+
with closing(sqlite3.connect((home / "state.db").as_uri() + "?mode=ro", uri=True, timeout=2)) as db:
|
|
30
|
+
rows = db.execute("""SELECT payload_json FROM session_admissions
|
|
31
|
+
WHERE target_session_id=? AND status IN ('queued', 'started')""", (sid,))
|
|
32
|
+
for (raw,) in rows:
|
|
33
|
+
payload = json.loads(raw)
|
|
34
|
+
policy = payload.get("cli_turn_v1", {})
|
|
35
|
+
if not isinstance(policy, dict):
|
|
36
|
+
return "owner_controls_unverified"
|
|
37
|
+
if "clarify_batch" in policy and not isinstance(policy["clarify_batch"], bool):
|
|
38
|
+
return "owner_controls_unverified"
|
|
39
|
+
if policy.get("clarify_batch") is True:
|
|
40
|
+
return "native_clarify_batch_unavailable"
|
|
41
|
+
except (sqlite3.Error, OSError, ValueError, TypeError, AttributeError):
|
|
42
|
+
return "owner_controls_unverified"
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _snapshot(value: dict) -> dict:
|
|
47
|
+
# Raw conversation rows contain private principal/admission metadata; the client needs
|
|
48
|
+
# display rows, not those bindings or the inventory's system prompt and credential hint.
|
|
49
|
+
message_keys = {"role", "content", "tool_calls", "tool_call_id", "name", "timestamp", "message_id", "row_id",
|
|
50
|
+
"reasoning", "reasoning_content", "display_kind", "finish_reason"}
|
|
51
|
+
info_keys = {"title", "model", "cwd", "source", "provider"}
|
|
52
|
+
return {**value, "messages": [{k: v for k, v in row.items() if k in message_keys} for row in value["messages"]],
|
|
53
|
+
"info": {k: v for k, v in value["info"].items() if k in info_keys}}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _refused(state: str, reason: str) -> dict:
|
|
57
|
+
return {"state": state, "reason": reason, "snapshot": None, "receipt": None}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@asynccontextmanager
|
|
61
|
+
async def _connect(home: Path, endpoint):
|
|
62
|
+
from hermes_constants import get_hermes_home
|
|
63
|
+
from hermes_cli.gateway_client import GatewayClientError, connect_gateway
|
|
64
|
+
|
|
65
|
+
# The supplied endpoint selects the native client's attach-only branch. The home
|
|
66
|
+
# override is mandatory so its bootstrap ticket cannot fall back to the launch profile.
|
|
67
|
+
if Path(get_hermes_home()).resolve() != home:
|
|
68
|
+
raise GatewayClientError("profile_mismatch")
|
|
69
|
+
async with connect_gateway(endpoint) as client:
|
|
70
|
+
websocket = client.websocket
|
|
71
|
+
# GatewayClient queues server requests as events. Drain continuously, including
|
|
72
|
+
# during an RPC, so unsupported sudo/secure inputs never wait for a Web renderer.
|
|
73
|
+
events = asyncio.Queue(maxsize=4096)
|
|
74
|
+
|
|
75
|
+
async def drain():
|
|
76
|
+
while True:
|
|
77
|
+
frame = await client.events.get()
|
|
78
|
+
if isinstance(frame, dict) and frame.get("method") in {"sudo", "secret"} and "id" in frame:
|
|
79
|
+
await websocket.send(json.dumps({"jsonrpc": "2.0", "id": frame["id"], "result": {"value": ""}}))
|
|
80
|
+
else:
|
|
81
|
+
await events.put(frame)
|
|
82
|
+
|
|
83
|
+
reader = asyncio.create_task(drain())
|
|
84
|
+
try:
|
|
85
|
+
yield client, events
|
|
86
|
+
finally:
|
|
87
|
+
reader.cancel()
|
|
88
|
+
with suppress(asyncio.CancelledError):
|
|
89
|
+
await reader
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
async def _run(ctx, params: dict, home: Path, discovery) -> dict:
|
|
93
|
+
from hermes_cli.gateway_client import GatewayRPCError
|
|
94
|
+
|
|
95
|
+
endpoint = discovery.endpoint
|
|
96
|
+
# v1's served LOCAL route can lose its frozen policy when the shared runner pins
|
|
97
|
+
# transport identity. A dedicated owner's profile is isolated; a served profile
|
|
98
|
+
# needs a verified execution contract before Talaria may submit through it.
|
|
99
|
+
if endpoint.control_home and Path(endpoint.control_home).resolve() != home:
|
|
100
|
+
return _refused("incompatible", "served_profile_sessions_unavailable")
|
|
101
|
+
subscription = None
|
|
102
|
+
async with _connect(home, endpoint) as (client, events):
|
|
103
|
+
described = await client.rpc("runtime.describe")
|
|
104
|
+
if (described.get("instance_id") != endpoint.instance_id
|
|
105
|
+
or described.get("profile_id") != endpoint.profile_id
|
|
106
|
+
or described.get("authority_epoch") != endpoint.authority_epoch
|
|
107
|
+
or not _CAPABILITIES.issubset(described.get("capabilities", []))):
|
|
108
|
+
return _refused("incompatible", "owner_capabilities")
|
|
109
|
+
sid = params["session_id"]
|
|
110
|
+
|
|
111
|
+
async def snapshot():
|
|
112
|
+
nonlocal subscription, sid
|
|
113
|
+
value = await client.rpc("session.resume", session_id=sid)
|
|
114
|
+
sid = value["session_id"]
|
|
115
|
+
# Native attach reuses this actor's subscription; overflow retires it before
|
|
116
|
+
# a replacement is issued. A resume does not accumulate live subscriptions.
|
|
117
|
+
subscription = value["subscription_id"]
|
|
118
|
+
if reason := await asyncio.to_thread(_control_refusal, home, sid):
|
|
119
|
+
raise GatewayRPCError(reason)
|
|
120
|
+
return _snapshot(value)
|
|
121
|
+
|
|
122
|
+
receipt = None
|
|
123
|
+
try:
|
|
124
|
+
snap = await snapshot()
|
|
125
|
+
action = params["action"]
|
|
126
|
+
common = {"session_id": sid}
|
|
127
|
+
if action == "submit":
|
|
128
|
+
# The caller retains this identity across lost ACKs. An ambiguous error never
|
|
129
|
+
# causes this adapter to generate a replacement or replay started work.
|
|
130
|
+
receipt = await client.rpc("prompt.submit", **common, submission_id=params["request_id"], text=params["text"])
|
|
131
|
+
elif action == "receipt":
|
|
132
|
+
receipt = await client.rpc("prompt.receipt", **common, admission_id=params["admission_id"])
|
|
133
|
+
elif action in {"cancel", "discard"}:
|
|
134
|
+
method = "prompt.cancel" if action == "cancel" else "prompt.resolve_unknown"
|
|
135
|
+
receipt = await client.rpc(method, **common, admission_id=params["admission_id"],
|
|
136
|
+
**({"execution_generation": params["execution_generation"]} if action == "discard" else {}))
|
|
137
|
+
elif action == "interrupt":
|
|
138
|
+
await client.rpc("session.interrupt", **common, execution_generation=params["execution_generation"])
|
|
139
|
+
elif action in {"approval", "clarify"}:
|
|
140
|
+
await client.rpc(action + ".respond", **common, prompt_id=params["prompt_id"],
|
|
141
|
+
execution_generation=params["execution_generation"],
|
|
142
|
+
**({"choice": params["choice"]} if action == "approval" else {"answer": params["text"]}))
|
|
143
|
+
elif action == "mutate":
|
|
144
|
+
await client.rpc("session.mutate", **common, request_id=params["request_id"],
|
|
145
|
+
expected_revision=params["expected_revision"], expected_generation=params["execution_generation"],
|
|
146
|
+
operation=params["operation"], payload=params["payload"], _timeout=660)
|
|
147
|
+
if action not in {"snapshot", "watch"}:
|
|
148
|
+
snap = await snapshot()
|
|
149
|
+
result = {"state": "attached", "reason": None, "snapshot": snap, "receipt": receipt}
|
|
150
|
+
if action != "watch":
|
|
151
|
+
return result
|
|
152
|
+
ctx.emit("snapshot", result)
|
|
153
|
+
epoch, sequence = snap["replay_epoch"], snap["last_sequence"]
|
|
154
|
+
# Rebuild the active turn's display from retained events, even when its initial
|
|
155
|
+
# tokens predate this viewer. A gap replaces the subscription, never invents replay.
|
|
156
|
+
replay = await client.rpc("session.events.since", **common, replay_epoch=epoch, last_sequence=0)
|
|
157
|
+
if replay.get("snapshot_required"):
|
|
158
|
+
snap = await snapshot()
|
|
159
|
+
ctx.emit("snapshot", {**result, "snapshot": snap, "replay_gap": True})
|
|
160
|
+
epoch, sequence = snap["replay_epoch"], snap["last_sequence"]
|
|
161
|
+
else:
|
|
162
|
+
for event in replay["events"]:
|
|
163
|
+
if (event.get("execution_generation") == snap["execution_generation"]
|
|
164
|
+
or event.get("type") == "session.info" and event.get("payload", {}).get("execution_generation") == snap["execution_generation"]):
|
|
165
|
+
ctx.emit("event", event)
|
|
166
|
+
sequence = replay["last_sequence"]
|
|
167
|
+
while not ctx.cancelled:
|
|
168
|
+
try:
|
|
169
|
+
frame = await asyncio.wait_for(events.get(), .25)
|
|
170
|
+
except TimeoutError:
|
|
171
|
+
continue
|
|
172
|
+
if isinstance(frame, Exception):
|
|
173
|
+
raise frame
|
|
174
|
+
if frame.get("method") == "clarify" and "id" in frame:
|
|
175
|
+
raise GatewayRPCError("native_clarify_batch_unavailable")
|
|
176
|
+
event = frame.get("params", {})
|
|
177
|
+
if event.get("session_id") != sid:
|
|
178
|
+
continue
|
|
179
|
+
if event.get("type") == "session.info":
|
|
180
|
+
if reason := await asyncio.to_thread(_control_refusal, home, sid):
|
|
181
|
+
raise GatewayRPCError(reason)
|
|
182
|
+
if (event.get("type") == "session.replay_gap" or event.get("replay_epoch") != epoch
|
|
183
|
+
or event.get("seq", sequence + 1) > sequence + 1):
|
|
184
|
+
snap = await snapshot()
|
|
185
|
+
ctx.emit("snapshot", {**result, "snapshot": snap, "replay_gap": True})
|
|
186
|
+
epoch, sequence = snap["replay_epoch"], snap["last_sequence"]
|
|
187
|
+
continue
|
|
188
|
+
if event.get("seq", 0) <= sequence:
|
|
189
|
+
continue
|
|
190
|
+
sequence = event["seq"]
|
|
191
|
+
ctx.emit("event", event)
|
|
192
|
+
return result
|
|
193
|
+
except GatewayRPCError as exc:
|
|
194
|
+
return {**_refused("incompatible" if str(exc) in _CONTROL_REFUSALS else "refused", str(exc)), "receipt": receipt}
|
|
195
|
+
except Exception: # noqa: BLE001 - native transport/contract failures never authorize another writer
|
|
196
|
+
# A received receipt remains admission proof even if the following observation
|
|
197
|
+
# disconnects. Losing that ACK would invite a new request identity for admitted work.
|
|
198
|
+
return {**_refused("unavailable", "owner_connection_lost"), "receipt": receipt}
|
|
199
|
+
finally:
|
|
200
|
+
if subscription:
|
|
201
|
+
with suppress(Exception):
|
|
202
|
+
await client.rpc("session.detach", session_id=sid, subscription_id=subscription)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def session(ctx, params: dict) -> dict:
|
|
206
|
+
home = profile_home_param(params).resolve()
|
|
207
|
+
action = params.get("action", "snapshot")
|
|
208
|
+
sid = params.get("session_id")
|
|
209
|
+
if action not in _ACTIONS or not isinstance(sid, str) or not sid.strip() or sid != sid.strip():
|
|
210
|
+
raise InvalidParams("session_id and supported action are required")
|
|
211
|
+
params = {**params, "action": action}
|
|
212
|
+
required = {
|
|
213
|
+
"submit": ("request_id", "text"), "receipt": ("admission_id",), "cancel": ("admission_id",),
|
|
214
|
+
"interrupt": ("execution_generation",), "approval": ("execution_generation", "prompt_id", "choice"),
|
|
215
|
+
"clarify": ("execution_generation", "prompt_id", "text"), "discard": ("execution_generation", "admission_id"),
|
|
216
|
+
"mutate": ("request_id", "expected_revision", "execution_generation", "operation", "payload"),
|
|
217
|
+
}.get(action, ())
|
|
218
|
+
if any(key not in params for key in required):
|
|
219
|
+
raise InvalidParams("missing gateway session action fields")
|
|
220
|
+
try:
|
|
221
|
+
from hermes_constants import set_hermes_home_override, reset_hermes_home_override # noqa: F401
|
|
222
|
+
from hermes_cli.gateway_runtime import discover_gateway_endpoint
|
|
223
|
+
from hermes_cli.gateway_client import connect_gateway # noqa: F401
|
|
224
|
+
except ImportError:
|
|
225
|
+
return _refused("incompatible", "native_sessions_unavailable")
|
|
226
|
+
with scoped_home(home):
|
|
227
|
+
discovery = discover_gateway_endpoint(home)
|
|
228
|
+
if discovery.state != "ready" or discovery.endpoint is None:
|
|
229
|
+
return _refused("stopped" if discovery.state in {"stopped", "absent"} else "unavailable", discovery.reason_code or discovery.state)
|
|
230
|
+
try:
|
|
231
|
+
return asyncio.run(_run(ctx, params, home, discovery))
|
|
232
|
+
except Exception: # noqa: BLE001 - native transport/contract failures never authorize another writer
|
|
233
|
+
# Do not expose peer diagnostics, endpoint paths or bootstrap credentials.
|
|
234
|
+
return _refused("unavailable", "owner_connection_lost")
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""``goals.*``: Hermes persistent session goals (ported from api/goals.py).
|
|
2
2
|
|
|
3
|
-
The server passes the session's ``profile_home
|
|
4
|
-
budget; the sidecar never reads WebUI settings itself.
|
|
3
|
+
The server passes the session's ``profile_home``, its workspace and the configured
|
|
4
|
+
turn budget; the sidecar never reads WebUI settings itself. ``/goal`` parses through
|
|
5
|
+
the Agent's own ``dispatch_goal_command``, like its CLI, gateway and TUI.
|
|
5
6
|
"""
|
|
6
7
|
|
|
7
8
|
from __future__ import annotations
|
|
@@ -14,7 +15,7 @@ from pathlib import Path
|
|
|
14
15
|
from typing import Any
|
|
15
16
|
|
|
16
17
|
from ..errors import InvalidParams, RpcError
|
|
17
|
-
from ..home import profile_home_param, scoped_home
|
|
18
|
+
from ..home import profile_home_param, scoped_home, session_workspace
|
|
18
19
|
from ..rpc import CallContext
|
|
19
20
|
|
|
20
21
|
log = logging.getLogger("talaria_sidecar.goals")
|
|
@@ -29,17 +30,18 @@ def _native():
|
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
class _Manager:
|
|
32
|
-
"""Native GoalManager scoped to one profile home."""
|
|
33
|
+
"""Native GoalManager scoped to one profile home and the session's workspace, where its gates run."""
|
|
33
34
|
|
|
34
|
-
def __init__(self, session_id: str, profile_home: Path, default_max_turns: int):
|
|
35
|
+
def __init__(self, session_id: str, profile_home: Path, default_max_turns: int, workspace: str | None = None):
|
|
35
36
|
_, default, GoalManager, _, _ = _native()
|
|
36
37
|
self.session_id = session_id
|
|
37
38
|
self.profile_home = profile_home
|
|
39
|
+
self.workspace = workspace
|
|
38
40
|
with scoped_home(profile_home):
|
|
39
41
|
self._manager = GoalManager(session_id=session_id, default_max_turns=int(default_max_turns or default or 20))
|
|
40
42
|
|
|
41
43
|
def scoped(self, func, *args, **kwargs):
|
|
42
|
-
with scoped_home(self.profile_home):
|
|
44
|
+
with scoped_home(self.profile_home), session_workspace(self.workspace):
|
|
43
45
|
return func(*args, **kwargs)
|
|
44
46
|
|
|
45
47
|
@property
|
|
@@ -123,13 +125,22 @@ def _decision_payload(decision: dict, state: Any) -> dict:
|
|
|
123
125
|
turns_used, max_turns = _turns_from_message(str(decision.get("message") or ""))
|
|
124
126
|
if status == "done":
|
|
125
127
|
return {**decision, "message_key": "goal_achieved", "message_args": [reason]}
|
|
126
|
-
if status == "paused":
|
|
128
|
+
if status == "paused" and str(getattr(state, "paused_reason", "") or "").startswith("turn budget exhausted"):
|
|
127
129
|
return {**decision, "message_key": "goal_paused_budget_exhausted", "message_args": [turns_used, max_turns]}
|
|
128
130
|
if decision.get("should_continue"):
|
|
129
131
|
return {**decision, "message_key": "goal_continuing", "message_args": [turns_used, max_turns, reason]}
|
|
130
132
|
return decision
|
|
131
133
|
|
|
132
134
|
|
|
135
|
+
def _parked(mgr: _Manager) -> dict | None:
|
|
136
|
+
"""TAL-808: an active goal's live wait barrier, as the Agent's own ``is_waiting`` (which clears a satisfied one) sees it."""
|
|
137
|
+
if not mgr.is_waiting():
|
|
138
|
+
return None
|
|
139
|
+
state = mgr.state
|
|
140
|
+
until = float(getattr(state, "waiting_until", 0.0) or 0.0)
|
|
141
|
+
return {"reason": getattr(state, "waiting_reason", None) or None, "until": until or None}
|
|
142
|
+
|
|
143
|
+
|
|
133
144
|
def _inactive(reason: str, status=None) -> dict:
|
|
134
145
|
return {"status": status, "should_continue": False, "continuation_prompt": None, "verdict": "inactive", "reason": reason, "message": ""}
|
|
135
146
|
|
|
@@ -139,7 +150,41 @@ def _manager_from(params: dict) -> _Manager:
|
|
|
139
150
|
if not session_id:
|
|
140
151
|
raise InvalidParams("session_id is required")
|
|
141
152
|
max_turns = params.get("default_max_turns") or 20
|
|
142
|
-
return _Manager(session_id, profile_home_param(params), int(max_turns))
|
|
153
|
+
return _Manager(session_id, profile_home_param(params), int(max_turns), str(params.get("workspace") or "") or None)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
#: Older Agents run gates in their launch directory, where a gate checks another project and can pass for it.
|
|
157
|
+
_GATES_NEED_WORKSPACE = "Goal gates need Hermes Agent 0.21.6 or later, which runs them in the session workspace."
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _gates_run_in_workspace() -> bool:
|
|
161
|
+
from hermes_cli import goals as agent_goals
|
|
162
|
+
|
|
163
|
+
return hasattr(agent_goals, "_gate_workspace")
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
#: The response ``action`` of each control verb the Agent dispatches; any other command sets the goal.
|
|
167
|
+
_CONTROL_ACTIONS = {
|
|
168
|
+
"": "status", "status": "status", "show": "show", "pause": "pause", "resume": "resume", "continue": "resume",
|
|
169
|
+
"unpause": "resume", "clear": "clear", "stop": "clear", "done": "clear", "unwait": "unwait", "wait": "wait", "gate": "gate",
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _localized(action: str, state: Any, had_goal: bool) -> dict:
|
|
174
|
+
"""``message_key``/``message_args`` for the verbs Web already localizes; other verbs show the Agent's text."""
|
|
175
|
+
status = str(getattr(state, "status", "") or "")
|
|
176
|
+
goal = str(getattr(state, "goal", "") or "")
|
|
177
|
+
if action == "status":
|
|
178
|
+
return {k: v for k, v in _status_payload(state).items() if k != "message"}
|
|
179
|
+
if action == "set":
|
|
180
|
+
return {"message_key": "goal_set", "message_args": [int(getattr(state, "max_turns", 0) or 0), goal]}
|
|
181
|
+
if action == "pause":
|
|
182
|
+
return {"message_key": "goal_paused", "message_args": [goal]} if had_goal and status == "paused" else {"message_key": "goal_no_goal"}
|
|
183
|
+
if action == "resume":
|
|
184
|
+
return {"message_key": "goal_resumed", "message_args": [goal]} if had_goal and status == "active" else {"message_key": "goal_no_goal"}
|
|
185
|
+
if action == "clear":
|
|
186
|
+
return {"message_key": "goal_cleared" if had_goal else "goal_no_goal"}
|
|
187
|
+
return {}
|
|
143
188
|
|
|
144
189
|
|
|
145
190
|
def register(registry) -> None:
|
|
@@ -147,45 +192,35 @@ def register(registry) -> None:
|
|
|
147
192
|
def get(ctx: CallContext, params: dict) -> dict:
|
|
148
193
|
mgr = _manager_from(params)
|
|
149
194
|
state = mgr.state
|
|
150
|
-
|
|
195
|
+
active = bool(state is not None and getattr(state, "status", None) == "active")
|
|
196
|
+
return {"goal": _state_payload(state), "active": active, "parked": _parked(mgr) if active else None, **_status_payload(state)}
|
|
151
197
|
|
|
152
198
|
@registry.method("goals.command")
|
|
153
199
|
def command(ctx: CallContext, params: dict) -> dict:
|
|
154
|
-
"""/goal <args>
|
|
200
|
+
"""/goal <args> through the Agent's ``dispatch_goal_command``, as its TUI runs it."""
|
|
155
201
|
mgr = _manager_from(params)
|
|
202
|
+
try:
|
|
203
|
+
from hermes_cli import goals as agent_goals
|
|
204
|
+
from hermes_cli.goal_command import dispatch_goal_command, is_goal_control
|
|
205
|
+
except Exception as exc: # noqa: BLE001
|
|
206
|
+
raise RpcError(f"Hermes goals unavailable: {exc}", condition="goals_unavailable")
|
|
156
207
|
text = str(params.get("args") or "").strip()
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if
|
|
160
|
-
state = mgr.state
|
|
161
|
-
return _payload(action="status", state=state, **_status_payload(state))
|
|
162
|
-
if lower == "pause":
|
|
163
|
-
state = mgr.pause(reason="user-paused")
|
|
164
|
-
if state is None:
|
|
165
|
-
return _payload(ok=False, action="pause", error="no_goal", message="No goal set.", message_key="goal_no_goal")
|
|
166
|
-
return _payload(action="pause", message=f"⏸ Goal paused: {state.goal}", message_key="goal_paused", message_args=[str(state.goal)], state=state)
|
|
167
|
-
if lower == "resume":
|
|
168
|
-
state = mgr.resume()
|
|
169
|
-
if state is None:
|
|
170
|
-
return _payload(ok=False, action="resume", error="no_goal", message="No goal to resume.", message_key="goal_no_goal")
|
|
171
|
-
return _payload(action="resume", message=f"▶ Goal resumed: {state.goal}\nSend a new message, or type continue, to kick it off.", message_key="goal_resumed", message_args=[str(state.goal)], state=state)
|
|
172
|
-
if lower in ("clear", "stop", "done"):
|
|
173
|
-
had = bool(mgr.has_goal())
|
|
174
|
-
mgr.clear()
|
|
175
|
-
return _payload(action="clear", message="Goal cleared." if had else "No active goal.", message_key="goal_cleared" if had else "goal_no_goal", state=mgr.state)
|
|
176
|
-
if stream_running:
|
|
208
|
+
tokens = text.lower().split()
|
|
209
|
+
action = _CONTROL_ACTIONS.get(tokens[0] if tokens else "", "set") if is_goal_control(text) else "set"
|
|
210
|
+
if action == "set" and params.get("stream_running"):
|
|
177
211
|
return _payload(ok=False, action="set", error="agent_running", message="Agent is running — use /goal status / pause / clear mid-run, or /stop before setting a new goal.")
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
)
|
|
212
|
+
if tokens[:2] == ["gate", "add"] and not _gates_run_in_workspace():
|
|
213
|
+
return _payload(ok=False, action="gate", error="gates_unsupported", state=mgr.state, message=_GATES_NEED_WORKSPACE)
|
|
214
|
+
had_goal = bool(mgr.has_goal())
|
|
215
|
+
# Only goal-setting text reads it: a re-pasted goal kicks off without repeating itself.
|
|
216
|
+
last_user_message = mgr.scoped(agent_goals.last_user_message_from_db, mgr.session_id) if action == "set" else None
|
|
217
|
+
result = mgr.scoped(dispatch_goal_command, mgr._manager, text, authorize_gate=lambda: None, last_user_message=last_user_message)
|
|
218
|
+
state = mgr.state
|
|
219
|
+
if result.error:
|
|
220
|
+
return _payload(ok=False, action=action, error="invalid_goal" if action == "set" else "invalid_command", message=result.output, state=state)
|
|
221
|
+
# A running turn's judge continues a resumed goal itself.
|
|
222
|
+
kickoff = result.prompt if result.prompt and not params.get("stream_running") else None
|
|
223
|
+
return _payload(action=action, message=result.output, state=state, kickoff_prompt=kickoff, **_localized(action, state, had_goal))
|
|
189
224
|
|
|
190
225
|
@registry.method("goals.snapshot")
|
|
191
226
|
def snapshot(ctx: CallContext, params: dict) -> dict:
|
|
@@ -212,6 +247,11 @@ def register(registry) -> None:
|
|
|
212
247
|
try:
|
|
213
248
|
if not mgr.is_active():
|
|
214
249
|
return _inactive("no active goal", getattr(mgr.state, "status", None) if mgr.state is not None else None)
|
|
250
|
+
# Gates set on a newer Agent persist across a channel switch; this one would run them outside the workspace.
|
|
251
|
+
if getattr(mgr.state, "gates", None) and not _gates_run_in_workspace():
|
|
252
|
+
mgr.pause(reason="gates need Hermes Agent 0.21.6 or later")
|
|
253
|
+
return {"status": "paused", "should_continue": False, "continuation_prompt": None, "verdict": "gate_failed", "reason": "gates not run",
|
|
254
|
+
"message": f"⏸ Goal paused — quality gates not run. {_GATES_NEED_WORKSPACE} /goal gate clear, then /goal resume."}
|
|
215
255
|
decision = mgr.evaluate_after_turn(str(params.get("last_response") or ""), user_initiated=bool(params.get("user_initiated", True)))
|
|
216
256
|
except Exception as exc: # noqa: BLE001 - never fail a turn on goal evaluation
|
|
217
257
|
log.debug("goal evaluation failed", exc_info=True)
|
|
@@ -7,17 +7,20 @@ server; the sidecar receives typed params.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import functools
|
|
10
11
|
import json
|
|
11
12
|
import secrets
|
|
12
13
|
import time
|
|
13
14
|
from dataclasses import asdict, is_dataclass
|
|
14
|
-
from typing import Any
|
|
15
|
+
from typing import Any, NamedTuple
|
|
15
16
|
|
|
16
17
|
from ..errors import InvalidParams, RpcError
|
|
17
18
|
from ..home import profile_home_param, scoped_home
|
|
18
19
|
from ..rpc import CallContext
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
# Agent 2026.9.24 predates ``kanban_workflow``; its ``VALID_STATUSES`` set holds these columns, in this board order.
|
|
22
|
+
# Delete with that release's entry in ``agent_support.json``.
|
|
23
|
+
_LEGACY_COLUMNS = ("triage", "todo", "scheduled", "ready", "running", "blocked", "review", "done")
|
|
21
24
|
|
|
22
25
|
|
|
23
26
|
class NotFound(RpcError):
|
|
@@ -38,6 +41,27 @@ def _kb():
|
|
|
38
41
|
return kb
|
|
39
42
|
|
|
40
43
|
|
|
44
|
+
class _Workflow(NamedTuple):
|
|
45
|
+
columns: tuple[tuple[str, str], ...] # (key, label) in board order
|
|
46
|
+
manual: dict | None # src -> [dst]; None on an Agent without a move table
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@functools.cache
|
|
50
|
+
def _workflow() -> _Workflow:
|
|
51
|
+
"""The Agent's board columns and manual move table from ``kanban_workflow.DEFAULT_WORKFLOW`` (Agent 0.21.6+, TAL-796)."""
|
|
52
|
+
try:
|
|
53
|
+
from hermes_cli.kanban_workflow import DEFAULT_WORKFLOW
|
|
54
|
+
except ImportError:
|
|
55
|
+
statuses = getattr(_kb(), "VALID_STATUSES", _LEGACY_COLUMNS)
|
|
56
|
+
return _Workflow(tuple((key, key.title()) for key in _LEGACY_COLUMNS if key in statuses), None)
|
|
57
|
+
workflow = DEFAULT_WORKFLOW.to_dict()
|
|
58
|
+
return _Workflow(tuple((c["key"], c["label"]) for c in workflow["columns"]), workflow["manual"])
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _columns() -> list[str]:
|
|
62
|
+
return [key for key, _ in _workflow().columns]
|
|
63
|
+
|
|
64
|
+
|
|
41
65
|
def _board(params: dict):
|
|
42
66
|
raw = params.get("board")
|
|
43
67
|
if raw is None or (isinstance(raw, str) and raw.strip() == ""):
|
|
@@ -49,9 +73,16 @@ def _board(params: dict):
|
|
|
49
73
|
raise InvalidParams(f"invalid board slug: {raw!r}") from exc
|
|
50
74
|
if not normed:
|
|
51
75
|
return None
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
76
|
+
if normed == getattr(kb, "DEFAULT_BOARD", "default"):
|
|
77
|
+
return normed
|
|
78
|
+
return _live_board(kb, normed, normed)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _live_board(kb, normed: str, shown, *, archived_ok: bool = False) -> str:
|
|
82
|
+
"""``normed`` when it names a live board. Agent 0.21.6 leaves an ``archived`` ``board.json`` tombstone at an
|
|
83
|
+
archived board's slug, so ``board_exists`` alone would let a stale link reach it (TAL-795)."""
|
|
84
|
+
if not kb.board_exists(normed) or (not archived_ok and kb.read_board_metadata(normed).get("archived")):
|
|
85
|
+
raise NotFound(f"board {shown!r} does not exist")
|
|
55
86
|
return normed
|
|
56
87
|
|
|
57
88
|
|
|
@@ -197,6 +228,8 @@ def _task_dict(task, conn):
|
|
|
197
228
|
# stored or supplied evidence, and refuses to release a live worker's claim.
|
|
198
229
|
data["claim_live"] = data.get("status") == "running" and _claim_live(conn, data.get("id"))
|
|
199
230
|
data["has_completion_evidence"] = bool(str(data.get("result") or "").strip())
|
|
231
|
+
manual = _workflow().manual
|
|
232
|
+
data["agent_move_targets"] = None if manual is None else list(manual.get(data.get("status"), []))
|
|
200
233
|
try:
|
|
201
234
|
age = _kb().task_age(task)
|
|
202
235
|
except Exception: # noqa: BLE001
|
|
@@ -237,8 +270,13 @@ def _comment_counts(conn):
|
|
|
237
270
|
|
|
238
271
|
def _validate_status(status: str) -> str:
|
|
239
272
|
value = str(status or "").strip().lower()
|
|
240
|
-
if value not in
|
|
273
|
+
if value not in {*_columns(), "archived"}:
|
|
241
274
|
raise InvalidParams(f"invalid status: {value}")
|
|
275
|
+
if value == "running":
|
|
276
|
+
raise InvalidParams("Cannot set status to 'running' directly; use the dispatcher/claim path")
|
|
277
|
+
if value in ("scheduled", "review"):
|
|
278
|
+
# The Agent reaches these through verbs with extra input (a wake time, a reviewer), which Talaria does not offer.
|
|
279
|
+
raise InvalidParams(f"Cannot set status to {value!r} directly")
|
|
242
280
|
return value
|
|
243
281
|
|
|
244
282
|
|
|
@@ -327,19 +365,21 @@ def _patch_task(conn, task_id: str, body: dict) -> None:
|
|
|
327
365
|
elif status == "archived":
|
|
328
366
|
if not _fenced(conn, task_id, lambda: kb.archive_task(conn, task_id), confirm_running_exit=confirm, releases_claim=False):
|
|
329
367
|
raise NotFound("task not found")
|
|
330
|
-
|
|
331
|
-
raise InvalidParams("Cannot set status to 'running' directly; use the dispatcher/claim path")
|
|
332
|
-
elif status == "ready":
|
|
368
|
+
else:
|
|
333
369
|
current = kb.get_task(conn, task_id)
|
|
334
370
|
if not current:
|
|
335
371
|
raise NotFound("task not found")
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
372
|
+
# Like the Agent's dashboard: leaving review reopens it, and blocked/scheduled -> ready unblocks; both re-gate on
|
|
373
|
+
# parents. Triage is a direct write there too. The reopen lands in ready whatever the target, so todo is refused.
|
|
374
|
+
if current.status == "review" and status == "todo":
|
|
375
|
+
raise Conflict("A task in review reopens to ready")
|
|
376
|
+
if current.status == "review" and status == "ready":
|
|
377
|
+
ok = kb.reopen_review_task(conn, task_id)
|
|
378
|
+
elif status == "ready" and current.status in ("blocked", "scheduled"):
|
|
379
|
+
ok = kb.unblock_task(conn, task_id)
|
|
380
|
+
else:
|
|
381
|
+
ok = _set_status_direct(conn, task_id, status, confirm_running_exit=confirm)
|
|
382
|
+
if not ok:
|
|
343
383
|
raise NotFound("task not found")
|
|
344
384
|
|
|
345
385
|
|
|
@@ -373,15 +413,15 @@ def _board_counts(slug):
|
|
|
373
413
|
pass
|
|
374
414
|
|
|
375
415
|
|
|
376
|
-
def _normalize_existing_slug(slug) -> str:
|
|
416
|
+
def _normalize_existing_slug(slug, *, archived_ok: bool = False) -> str:
|
|
377
417
|
kb = _kb()
|
|
378
418
|
try:
|
|
379
419
|
normed = kb._normalize_board_slug(slug)
|
|
380
420
|
except (ValueError, AttributeError) as exc:
|
|
381
421
|
raise InvalidParams(f"invalid board slug: {slug!r}") from exc
|
|
382
|
-
if not normed
|
|
422
|
+
if not normed:
|
|
383
423
|
raise NotFound(f"board {slug!r} does not exist")
|
|
384
|
-
return normed
|
|
424
|
+
return _live_board(kb, normed, slug, archived_ok=archived_ok)
|
|
385
425
|
|
|
386
426
|
|
|
387
427
|
def _current_board() -> str:
|
|
@@ -449,9 +489,9 @@ def board_payload(params: dict) -> dict:
|
|
|
449
489
|
data["comment_count"] = comment_counts.get(task.id, 0)
|
|
450
490
|
return data
|
|
451
491
|
|
|
452
|
-
columns = [{"name":
|
|
492
|
+
columns = [{"name": key, "label": label, "tasks": [row(t) for t in tasks if t.status == key]} for key, label in _workflow().columns]
|
|
453
493
|
if include_archived:
|
|
454
|
-
columns.append({"name": "archived", "tasks": [row(t) for t in tasks if t.status == "archived"]})
|
|
494
|
+
columns.append({"name": "archived", "label": "Archived", "tasks": [row(t) for t in tasks if t.status == "archived"]})
|
|
455
495
|
return {
|
|
456
496
|
"columns": columns,
|
|
457
497
|
"tenants": sorted({t.tenant for t in tasks if getattr(t, "tenant", None)}),
|
|
@@ -472,6 +512,8 @@ def create_task_payload(params: dict) -> dict:
|
|
|
472
512
|
priority = int(body.get("priority") or 0)
|
|
473
513
|
except (TypeError, ValueError):
|
|
474
514
|
raise InvalidParams("priority must be an integer")
|
|
515
|
+
# Checked before the insert, so a refused status leaves no task behind.
|
|
516
|
+
status = _validate_status(body.get("status")) if body.get("status") else None
|
|
475
517
|
kb = _kb()
|
|
476
518
|
with _conn(board=_board(params)) as conn:
|
|
477
519
|
task_id = kb.create_task(
|
|
@@ -480,8 +522,8 @@ def create_task_payload(params: dict) -> dict:
|
|
|
480
522
|
workspace_kind=body.get("workspace_kind") or "scratch", workspace_path=body.get("workspace_path") or None,
|
|
481
523
|
idempotency_key=body.get("idempotency_key") or None, max_runtime_seconds=body.get("max_runtime_seconds") or None, skills=body.get("skills") or None,
|
|
482
524
|
)
|
|
483
|
-
if
|
|
484
|
-
_patch_task(conn, task_id, {"status":
|
|
525
|
+
if status:
|
|
526
|
+
_patch_task(conn, task_id, {"status": status})
|
|
485
527
|
return {"task": _task_dict(kb.get_task(conn, task_id), conn), "read_only": False}
|
|
486
528
|
|
|
487
529
|
|
|
@@ -534,11 +576,17 @@ def task_detail_payload(params: dict) -> dict:
|
|
|
534
576
|
task = kb.get_task(conn, task_id)
|
|
535
577
|
if not task:
|
|
536
578
|
raise NotFound("task not found")
|
|
579
|
+
data = _task_dict(task, conn)
|
|
580
|
+
# ``started_at`` is the first-ever start; a retried task's run clock counts from its current run (TAL-809).
|
|
581
|
+
data["current_run_started_at"] = kb.current_run_started_ats(conn, [task_id]).get(task_id)
|
|
582
|
+
links = {"parents": kb.parent_ids(conn, task_id), "children": kb.child_ids(conn, task_id)}
|
|
583
|
+
linked = (kb.get_task(conn, link_id) for link_id in dict.fromkeys([*links["parents"], *links["children"]]))
|
|
537
584
|
return {
|
|
538
|
-
"task":
|
|
585
|
+
"task": data,
|
|
539
586
|
"comments": [_obj_dict(c) for c in kb.list_comments(conn, task_id)],
|
|
540
587
|
"events": [_obj_dict(e) for e in kb.list_events(conn, task_id)],
|
|
541
|
-
"links":
|
|
588
|
+
"links": links,
|
|
589
|
+
"link_tasks": [{"id": t.id, "title": t.title, "status": t.status} for t in linked if t],
|
|
542
590
|
"runs": [_obj_dict(r) for r in kb.list_runs(conn, task_id)],
|
|
543
591
|
"read_only": False,
|
|
544
592
|
}
|
|
@@ -583,13 +631,17 @@ def config_payload(params: dict) -> dict:
|
|
|
583
631
|
cfg = {}
|
|
584
632
|
k = ((cfg.get("dashboard") or {}).get("kanban") or {})
|
|
585
633
|
return {
|
|
586
|
-
"columns":
|
|
634
|
+
"columns": _columns(), "assignees": assignees, "default_tenant": k.get("default_tenant") or "",
|
|
587
635
|
"lane_by_profile": bool(k.get("lane_by_profile", True)), "include_archived_by_default": bool(k.get("include_archived_by_default", False)),
|
|
588
636
|
"render_markdown": bool(k.get("render_markdown", True)), "read_only": False,
|
|
589
637
|
}
|
|
590
638
|
|
|
591
639
|
|
|
592
640
|
def stats_payload(params: dict) -> dict:
|
|
641
|
+
return {**_stats(params), "column_order": _columns()}
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
def _stats(params: dict) -> dict:
|
|
593
645
|
kb = _kb()
|
|
594
646
|
with _conn(board=_board(params)) as conn:
|
|
595
647
|
if hasattr(kb, "board_stats"):
|
|
@@ -761,7 +813,8 @@ def create_board_payload(params: dict) -> dict:
|
|
|
761
813
|
def update_board_payload(params: dict) -> dict:
|
|
762
814
|
kb = _kb()
|
|
763
815
|
body = params.get("board_spec") or {}
|
|
764
|
-
|
|
816
|
+
# Metadata edits stay open on an archived board: clearing ``archived`` is how one archived in place comes back.
|
|
817
|
+
normed = _normalize_existing_slug(params.get("slug"), archived_ok=True)
|
|
765
818
|
kwargs = {}
|
|
766
819
|
if "default_workdir" in body:
|
|
767
820
|
kwargs["default_workdir"] = str(body.get("default_workdir") or "")
|