@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
|
@@ -14,9 +14,13 @@ import importlib
|
|
|
14
14
|
import logging
|
|
15
15
|
import multiprocessing
|
|
16
16
|
import queue
|
|
17
|
+
import os
|
|
17
18
|
import re
|
|
19
|
+
import signal
|
|
18
20
|
import threading
|
|
19
21
|
import time
|
|
22
|
+
from datetime import datetime
|
|
23
|
+
from functools import wraps
|
|
20
24
|
from pathlib import Path
|
|
21
25
|
|
|
22
26
|
from ..errors import InvalidParams, RpcError
|
|
@@ -31,6 +35,18 @@ _PASSTHROUGH_FIELDS = ("script", "no_agent", "context_from", "reasoning_effort",
|
|
|
31
35
|
_RUNNING: dict[str, float] = {}
|
|
32
36
|
_RUNNING_LOCK = threading.Lock()
|
|
33
37
|
_SNAPSHOT_LOCK = threading.Lock()
|
|
38
|
+
# The Agent's fire claim lives FIRE_CLAIM_TTL_SECONDS (300); a run refreshes it well inside that, re-checks a missed
|
|
39
|
+
# refresh once before treating the claim as lost (as the Agent's own runs do), and gives up once no refresh has landed
|
|
40
|
+
# for most of the lease.
|
|
41
|
+
_CLAIM_HEARTBEAT_SECONDS = 60.0
|
|
42
|
+
_CLAIM_MISS_CONFIRM_SECONDS = 1.0
|
|
43
|
+
_CLAIM_HEARTBEAT_GRACE_SECONDS = 240.0
|
|
44
|
+
_CLAIM_LOST = "Another process took over this task's run claim, so this run stopped before saving or delivering anything"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class _ClaimLost(RuntimeError):
|
|
48
|
+
def __init__(self) -> None:
|
|
49
|
+
super().__init__(_CLAIM_LOST)
|
|
34
50
|
|
|
35
51
|
|
|
36
52
|
def _main_model_pin() -> tuple[str | None, str | None]:
|
|
@@ -97,6 +113,25 @@ def _job_ref(params: dict, key: str = "job_id") -> str:
|
|
|
97
113
|
return ref
|
|
98
114
|
|
|
99
115
|
|
|
116
|
+
def _settled_mutation(method):
|
|
117
|
+
"""Fence schedule edits against manual execution, including its retained recovery obligation."""
|
|
118
|
+
@wraps(method)
|
|
119
|
+
def guarded(ctx, params):
|
|
120
|
+
from .cron_execution import ManualExecution, Unverified
|
|
121
|
+
home, jobs = profile_home_param(params), _jobs()
|
|
122
|
+
with _store(home):
|
|
123
|
+
job = getattr(jobs, "resolve_job_ref", jobs.get_job)(_job_ref(params))
|
|
124
|
+
if job is None:
|
|
125
|
+
return method(ctx, params)
|
|
126
|
+
job_id = _job_id({"job_id": job["id"]})
|
|
127
|
+
execution = ManualExecution(home, home, {"id": job_id}, None)
|
|
128
|
+
with execution.acquire():
|
|
129
|
+
if execution.file.exists():
|
|
130
|
+
raise Unverified("Schedule edits and deletion are blocked until the manual execution is reconciled.")
|
|
131
|
+
return method(ctx, dict(params, job_id=job_id))
|
|
132
|
+
return guarded
|
|
133
|
+
|
|
134
|
+
|
|
100
135
|
# ── job payload shaping ───────────────────────────────────────────────────
|
|
101
136
|
|
|
102
137
|
def _monitor_storage(monitor) -> dict:
|
|
@@ -144,6 +179,87 @@ def job_for_api(job: dict) -> dict:
|
|
|
144
179
|
return payload
|
|
145
180
|
|
|
146
181
|
|
|
182
|
+
# ── store health and foreign runs (TAL-810) ─────────────────────────────
|
|
183
|
+
|
|
184
|
+
def _store_health(cron_dir: Path) -> dict | None:
|
|
185
|
+
"""The Agent's cross-process write probe: ``None`` when the store accepts writes or the Agent predates it."""
|
|
186
|
+
try:
|
|
187
|
+
from cron.store_health import probe_report
|
|
188
|
+
except ImportError:
|
|
189
|
+
return None
|
|
190
|
+
return probe_report(cron_dir)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _live_executions(home: Path, job_ids: list[str], *, strict: bool = False) -> dict[str, dict]:
|
|
194
|
+
"""Jobs whose latest attempt is claimed or running under a live owner: the gateway scheduler or another process.
|
|
195
|
+
|
|
196
|
+
The executions ledger lives under ``get_hermes_home()``, not the cron store override, so it is read in the
|
|
197
|
+
profile's scope. A store without a ledger never ran anything, and is not created by this read. An unreadable
|
|
198
|
+
ledger reads idle for a listing; ``strict`` (run admission) refuses instead, since it cannot prove the job idle."""
|
|
199
|
+
try:
|
|
200
|
+
from cron.executions import live_inflight_execution
|
|
201
|
+
except ImportError:
|
|
202
|
+
return {}
|
|
203
|
+
if not job_ids or not (Path(home).expanduser() / "cron" / "executions.db").is_file():
|
|
204
|
+
return {}
|
|
205
|
+
try:
|
|
206
|
+
with scoped_home(home):
|
|
207
|
+
return {job_id: record for job_id in job_ids if job_id and (record := live_inflight_execution(job_id))}
|
|
208
|
+
except Exception as exc: # noqa: BLE001 - an unreadable ledger cannot prove a run; the list still loads
|
|
209
|
+
if strict:
|
|
210
|
+
raise RpcError("Cannot tell whether this task is already running: its executions ledger is unreadable", condition="cron_ledger_unreadable") from exc
|
|
211
|
+
log.warning("Cron executions ledger unreadable in %s", home, exc_info=True)
|
|
212
|
+
return {}
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _claim_fire(store_home: Path, job: dict) -> dict | bool | None:
|
|
216
|
+
"""The Agent's own run-now lease (``claim_job_for_fire(manual=True)``), which a gateway tick must also win.
|
|
217
|
+
|
|
218
|
+
The exact record the claim took (the one to run), ``False`` when another process holds the job's claim, or
|
|
219
|
+
``None`` for a job the Agent does not claim (paused, disabled, or terminal), which no tick fires. ``mark_job_run``
|
|
220
|
+
releases a claim when the run ends; the run heartbeats it meanwhile."""
|
|
221
|
+
jobs = _jobs()
|
|
222
|
+
with _store(store_home):
|
|
223
|
+
if not jobs.is_job_runnable(job):
|
|
224
|
+
return None
|
|
225
|
+
claimed = jobs.claim_job_for_fire(job["id"], manual=True, return_job=True)
|
|
226
|
+
if not isinstance(claimed, dict):
|
|
227
|
+
# The claim only loses to a live owner's claim; any other refusal is a record the Agent never claims.
|
|
228
|
+
current = jobs.get_job(job["id"]) or {}
|
|
229
|
+
return False if isinstance(current.get("fire_claim"), dict) else None
|
|
230
|
+
return {key: value for key, value in claimed.items() if key != "_scheduled_instant"}
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@contextlib.contextmanager
|
|
234
|
+
def _owner_fence(jobs, job_id: str, owner: str | None, *, protect_unclaimed=False):
|
|
235
|
+
"""Hold the Agent fire fence across side effects; canonical unclaimed runs also exclude a successor claim."""
|
|
236
|
+
if owner is None and protect_unclaimed:
|
|
237
|
+
with jobs._fire_job_lock(job_id) as locked:
|
|
238
|
+
current = jobs.get_job(job_id) if locked else None
|
|
239
|
+
yield bool(current) and not current.get("fire_claim")
|
|
240
|
+
else:
|
|
241
|
+
with jobs.fire_claim_fence(job_id, expected_owner=owner) if owner else contextlib.nullcontext(True) as owns:
|
|
242
|
+
yield owns
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _mark_run(jobs, job_id: str, success: bool, error, owner: str | None, delivery_error=None) -> None:
|
|
246
|
+
"""``mark_job_run`` fenced by the run's fire claim: a run that lost its claim records nothing over the new owner's."""
|
|
247
|
+
fence = {"expected_fire_owner": owner} if owner else {}
|
|
248
|
+
try:
|
|
249
|
+
jobs.mark_job_run(job_id, success, error, delivery_error=delivery_error, **fence)
|
|
250
|
+
except TypeError:
|
|
251
|
+
if owner:
|
|
252
|
+
raise
|
|
253
|
+
jobs.mark_job_run(job_id, success, error)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _elapsed(record: dict) -> float:
|
|
257
|
+
try:
|
|
258
|
+
return round(max(0.0, time.time() - datetime.fromisoformat(str(record.get("claimed_at"))).timestamp()), 1)
|
|
259
|
+
except ValueError:
|
|
260
|
+
return 0.0
|
|
261
|
+
|
|
262
|
+
|
|
147
263
|
# ── output files ──────────────────────────────────────────────────────────
|
|
148
264
|
|
|
149
265
|
def _response_marker_index(text: str) -> int:
|
|
@@ -228,12 +344,20 @@ def _snippet(text: str, limit: int = 600) -> str:
|
|
|
228
344
|
|
|
229
345
|
# ── manual runs ───────────────────────────────────────────────────────────
|
|
230
346
|
|
|
231
|
-
def _child_main(job, execution_home, result_queue):
|
|
347
|
+
def _child_main(job, execution_home, result_queue, cancel_event=None, execution_id=None, recovering=False):
|
|
232
348
|
try:
|
|
233
349
|
from cron.scheduler import run_job
|
|
234
350
|
|
|
351
|
+
def execute():
|
|
352
|
+
if recovering:
|
|
353
|
+
from .cron_execution import recover
|
|
354
|
+
return recover(job, execution_id, cancel_event)
|
|
355
|
+
if execution_id is not None:
|
|
356
|
+
return run_job(job, cancel_event=cancel_event, execution_id=execution_id)
|
|
357
|
+
return run_job(job)
|
|
358
|
+
|
|
235
359
|
if execution_home is None:
|
|
236
|
-
result =
|
|
360
|
+
result = execute()
|
|
237
361
|
else:
|
|
238
362
|
from talaria_sidecar.home import scoped_home as _scoped
|
|
239
363
|
|
|
@@ -241,12 +365,16 @@ def _child_main(job, execution_home, result_queue):
|
|
|
241
365
|
|
|
242
366
|
os.environ["HERMES_HOME"] = str(execution_home)
|
|
243
367
|
with _scoped(Path(execution_home)):
|
|
244
|
-
result =
|
|
245
|
-
result_queue.put(("ok", result))
|
|
368
|
+
result = execute()
|
|
369
|
+
result_queue.put(("ok", result, {key: job[key] for key in ("_model_unreachable", "_quota_hold_seconds") if key in job}))
|
|
246
370
|
except BaseException as exc: # noqa: BLE001 - surfaced in the parent
|
|
247
371
|
import traceback
|
|
372
|
+
from .cron_execution import Unverified
|
|
248
373
|
|
|
249
|
-
|
|
374
|
+
if isinstance(exc, Unverified):
|
|
375
|
+
result_queue.put(("unverified", exc.detail))
|
|
376
|
+
else:
|
|
377
|
+
result_queue.put(("error", f"{type(exc).__name__}: {exc}", traceback.format_exc()))
|
|
250
378
|
|
|
251
379
|
|
|
252
380
|
def _result_timeout(job) -> float:
|
|
@@ -263,44 +391,137 @@ def _result_timeout(job) -> float:
|
|
|
263
391
|
return 6 * 60 * 60.0
|
|
264
392
|
|
|
265
393
|
|
|
266
|
-
def
|
|
394
|
+
def _worker(main, *args) -> None:
|
|
395
|
+
"""A run's worker leads its own process group, so stopping the run also reaches every process it started."""
|
|
396
|
+
if hasattr(os, "setsid"):
|
|
397
|
+
os.setsid()
|
|
398
|
+
main(*args)
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def _signal_group(process, sig) -> None:
|
|
402
|
+
with contextlib.suppress(ProcessLookupError, PermissionError):
|
|
403
|
+
os.killpg(process.pid, sig)
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def _stop(process) -> None:
|
|
407
|
+
"""End a run's worker and everything it started, returning only once the worker has exited: SIGTERM to the
|
|
408
|
+
worker's process group, then SIGKILL to whatever is left, so nothing keeps running after the run gives up its
|
|
409
|
+
claim or its running guard. Without process groups (Windows) only the worker itself is stopped."""
|
|
410
|
+
if not hasattr(os, "killpg"):
|
|
411
|
+
process.terminate()
|
|
412
|
+
process.join(timeout=5)
|
|
413
|
+
if process.is_alive():
|
|
414
|
+
process.kill()
|
|
415
|
+
process.join()
|
|
416
|
+
return
|
|
417
|
+
_signal_group(process, signal.SIGTERM)
|
|
418
|
+
process.join(timeout=5)
|
|
419
|
+
_signal_group(process, signal.SIGKILL)
|
|
420
|
+
process.join()
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def _run_in_child(job, execution_home, ctx: CallContext, heartbeat=None, execution=None):
|
|
424
|
+
from .cron_execution import SETTLEMENT_SECONDS, Unverified
|
|
425
|
+
|
|
267
426
|
ctx_mp = multiprocessing.get_context("spawn")
|
|
268
427
|
result_queue = ctx_mp.Queue(maxsize=1)
|
|
269
|
-
|
|
270
|
-
|
|
428
|
+
cancel_event = ctx_mp.Event() if execution is not None else None
|
|
429
|
+
|
|
430
|
+
def start(recovering=False):
|
|
431
|
+
args = (job, str(execution_home) if execution_home else None, result_queue)
|
|
432
|
+
if execution is not None:
|
|
433
|
+
args += (cancel_event, execution.execution_id, recovering)
|
|
434
|
+
child = ctx_mp.Process(target=_worker, args=(_child_main, *args))
|
|
435
|
+
child.start()
|
|
436
|
+
return child
|
|
437
|
+
|
|
438
|
+
process = start(execution.recovering if execution else False)
|
|
271
439
|
deadline = time.monotonic() + _result_timeout(job)
|
|
440
|
+
next_beat = time.monotonic() + _CLAIM_HEARTBEAT_SECONDS
|
|
272
441
|
status, payload = "error", ["cron run subprocess failed before producing a result", ""]
|
|
442
|
+
settling = None
|
|
443
|
+
recovery_started = bool(execution and execution.recovering)
|
|
444
|
+
|
|
445
|
+
def cancel(reason):
|
|
446
|
+
nonlocal settling
|
|
447
|
+
if execution is None:
|
|
448
|
+
return
|
|
449
|
+
execution.cancel(reason)
|
|
450
|
+
cancel_event.set()
|
|
451
|
+
if settling is None:
|
|
452
|
+
settling = time.monotonic() + SETTLEMENT_SECONDS
|
|
453
|
+
|
|
454
|
+
def recover_child(detail):
|
|
455
|
+
nonlocal process, recovery_started, result_queue
|
|
456
|
+
process.join(timeout=5)
|
|
457
|
+
if process.is_alive():
|
|
458
|
+
_stop(process)
|
|
459
|
+
if recovery_started:
|
|
460
|
+
raise Unverified(detail)
|
|
461
|
+
recovery_started = True
|
|
462
|
+
cancel("submitter interrupted")
|
|
463
|
+
# A killed queue writer can leave a partial frame. Recovery gets a fresh pipe.
|
|
464
|
+
result_queue.close()
|
|
465
|
+
result_queue.join_thread()
|
|
466
|
+
result_queue = ctx_mp.Queue(maxsize=1)
|
|
467
|
+
process = start(True)
|
|
468
|
+
|
|
469
|
+
if execution and execution.record["cancel_reason"]:
|
|
470
|
+
cancel(execution.record["cancel_reason"])
|
|
273
471
|
try:
|
|
274
472
|
while True:
|
|
275
473
|
try:
|
|
276
474
|
status, *payload = result_queue.get(timeout=1.0)
|
|
277
|
-
|
|
475
|
+
if execution is not None and status == "unverified":
|
|
476
|
+
raise Unverified(payload[0])
|
|
477
|
+
if execution is None or status == "ok":
|
|
478
|
+
break
|
|
479
|
+
# A transport failure is not a failed task. Recover the original identity once.
|
|
480
|
+
recover_child(payload[0])
|
|
278
481
|
except queue.Empty:
|
|
482
|
+
if heartbeat is not None and time.monotonic() >= next_beat:
|
|
483
|
+
next_beat = time.monotonic() + _CLAIM_HEARTBEAT_SECONDS
|
|
484
|
+
if not heartbeat():
|
|
485
|
+
if execution is None:
|
|
486
|
+
_stop(process)
|
|
487
|
+
raise _ClaimLost()
|
|
488
|
+
cancel("claim_lost")
|
|
489
|
+
heartbeat = None
|
|
279
490
|
if ctx.cancelled:
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
491
|
+
if execution is None:
|
|
492
|
+
_stop(process)
|
|
493
|
+
status, payload = "cancelled", ["cancelled", ""]
|
|
494
|
+
break
|
|
495
|
+
cancel("cancelled")
|
|
284
496
|
if not process.is_alive():
|
|
285
497
|
# The child may have put its result just before exiting; otherwise it died (OOM, signal) without one.
|
|
286
498
|
try:
|
|
287
499
|
status, *payload = result_queue.get_nowait()
|
|
288
500
|
except queue.Empty:
|
|
289
501
|
payload = [f"cron run subprocess exited with code {process.exitcode} without producing a result", ""]
|
|
502
|
+
if execution is not None and status != "ok":
|
|
503
|
+
recover_child(payload[0])
|
|
504
|
+
continue
|
|
290
505
|
break
|
|
291
506
|
if time.monotonic() >= deadline:
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
507
|
+
if execution is None:
|
|
508
|
+
_stop(process)
|
|
509
|
+
payload = [f"cron run subprocess produced no result within {_result_timeout(job):g}s and was terminated", ""]
|
|
510
|
+
break
|
|
511
|
+
cancel("Manual task timed out")
|
|
512
|
+
if settling is not None and time.monotonic() >= settling:
|
|
513
|
+
raise Unverified("The owner has not provided a terminal receipt.")
|
|
296
514
|
process.join(timeout=5)
|
|
297
515
|
if process.is_alive():
|
|
298
|
-
process
|
|
299
|
-
process.join(timeout=5)
|
|
516
|
+
_stop(process)
|
|
300
517
|
finally:
|
|
518
|
+
if process.is_alive():
|
|
519
|
+
_stop(process)
|
|
301
520
|
result_queue.close()
|
|
302
521
|
result_queue.join_thread()
|
|
303
522
|
if status == "ok":
|
|
523
|
+
if execution is not None and len(payload) > 1:
|
|
524
|
+
execution.job.update(payload[1])
|
|
304
525
|
return payload[0]
|
|
305
526
|
if status == "cancelled":
|
|
306
527
|
raise RpcError("cancelled", condition="cancelled")
|
|
@@ -331,7 +552,32 @@ def _delivery_home(scheduler, home: Path):
|
|
|
331
552
|
pass
|
|
332
553
|
|
|
333
554
|
|
|
334
|
-
def
|
|
555
|
+
def _heartbeat(store_home, job_id, claim_owner):
|
|
556
|
+
jobs = _jobs()
|
|
557
|
+
last_beat = time.monotonic()
|
|
558
|
+
|
|
559
|
+
def heartbeat() -> bool:
|
|
560
|
+
"""Refresh only this fire's claim, giving transient store errors less than a lease to recover."""
|
|
561
|
+
nonlocal last_beat
|
|
562
|
+
try:
|
|
563
|
+
with _store(store_home):
|
|
564
|
+
owned = jobs.heartbeat_fire_claim(job_id, expected_owner=claim_owner) is not False
|
|
565
|
+
if not owned:
|
|
566
|
+
time.sleep(_CLAIM_MISS_CONFIRM_SECONDS)
|
|
567
|
+
owned = jobs.heartbeat_fire_claim(job_id, expected_owner=claim_owner) is not False
|
|
568
|
+
except Exception: # noqa: BLE001
|
|
569
|
+
log.warning("Fire claim heartbeat failed for job %s", job_id, exc_info=True)
|
|
570
|
+
return time.monotonic() - last_beat < _CLAIM_HEARTBEAT_GRACE_SECONDS
|
|
571
|
+
if owned:
|
|
572
|
+
last_beat = time.monotonic()
|
|
573
|
+
return owned
|
|
574
|
+
return heartbeat
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
def run_tracked(job: dict, store_home: Path, execution_home: Path | None, ctx: CallContext, claim_owner: str | None = None, *, recovery_only=False) -> dict:
|
|
578
|
+
from . import cron_execution
|
|
579
|
+
if recovery_only or cron_execution.path(store_home, job["id"]).exists() or cron_execution.supported(job):
|
|
580
|
+
return cron_execution.run(job, store_home, execution_home, ctx, claim_owner, recovery_only=recovery_only)
|
|
335
581
|
jobs = _jobs()
|
|
336
582
|
scheduler = importlib.import_module("cron.scheduler")
|
|
337
583
|
silent_marker = getattr(scheduler, "SILENT_MARKER", "[SILENT]")
|
|
@@ -340,8 +586,12 @@ def run_tracked(job: dict, store_home: Path, execution_home: Path | None, ctx: C
|
|
|
340
586
|
execution_home = execution_home or store_home
|
|
341
587
|
outcome: dict = {"job_id": job_id, "status": "completed"}
|
|
342
588
|
try:
|
|
343
|
-
|
|
344
|
-
|
|
589
|
+
heartbeat = _heartbeat(store_home, job_id, claim_owner)
|
|
590
|
+
success, output, final_response, error = _run_in_child(job, execution_home, ctx, heartbeat if claim_owner else None)
|
|
591
|
+
with _store(store_home), _owner_fence(jobs, job_id, claim_owner) as owns, contextlib.ExitStack() as delivery:
|
|
592
|
+
if not owns:
|
|
593
|
+
raise _ClaimLost()
|
|
594
|
+
delivery.enter_context(_delivery_home(scheduler, store_home))
|
|
345
595
|
jobs.save_job_output(job_id, output)
|
|
346
596
|
content = final_response if success else f"⚠️ Cron job '{job.get('name', job_id)}' failed:\n{error}"
|
|
347
597
|
should_deliver = bool(content) and not (success and silent_marker in content.strip().upper())
|
|
@@ -353,15 +603,12 @@ def run_tracked(job: dict, store_home: Path, execution_home: Path | None, ctx: C
|
|
|
353
603
|
delivery_error = str(exc)
|
|
354
604
|
if success and not final_response:
|
|
355
605
|
success, error = False, "Agent completed but produced empty response (model error, timeout, or misconfiguration)"
|
|
356
|
-
|
|
357
|
-
jobs.mark_job_run(job_id, success, error, delivery_error=delivery_error)
|
|
358
|
-
except TypeError:
|
|
359
|
-
jobs.mark_job_run(job_id, success, error)
|
|
606
|
+
_mark_run(jobs, job_id, success, error, claim_owner, delivery_error)
|
|
360
607
|
outcome.update(success=bool(success), error=error, delivery_error=delivery_error)
|
|
361
608
|
except RpcError:
|
|
362
609
|
with _store(store_home):
|
|
363
610
|
try:
|
|
364
|
-
jobs
|
|
611
|
+
_mark_run(jobs, job_id, False, "cancelled", claim_owner)
|
|
365
612
|
except Exception: # noqa: BLE001
|
|
366
613
|
pass
|
|
367
614
|
raise
|
|
@@ -369,7 +616,7 @@ def run_tracked(job: dict, store_home: Path, execution_home: Path | None, ctx: C
|
|
|
369
616
|
log.exception("Manual cron run failed for job %s", job_id)
|
|
370
617
|
with _store(store_home):
|
|
371
618
|
try:
|
|
372
|
-
jobs
|
|
619
|
+
_mark_run(jobs, job_id, False, str(exc), claim_owner)
|
|
373
620
|
except Exception: # noqa: BLE001
|
|
374
621
|
pass
|
|
375
622
|
outcome.update(status="failed", success=False, error=str(exc))
|
|
@@ -382,17 +629,79 @@ def run_tracked(job: dict, store_home: Path, execution_home: Path | None, ctx: C
|
|
|
382
629
|
# ── methods ───────────────────────────────────────────────────────────────
|
|
383
630
|
|
|
384
631
|
def register(registry) -> None:
|
|
632
|
+
@registry.method("cron.blueprints")
|
|
633
|
+
def blueprints(ctx: CallContext, params: dict) -> dict:
|
|
634
|
+
try:
|
|
635
|
+
catalog = importlib.import_module("cron.blueprint_catalog")
|
|
636
|
+
except ModuleNotFoundError as exc:
|
|
637
|
+
if exc.name != "cron.blueprint_catalog":
|
|
638
|
+
raise
|
|
639
|
+
return {"blueprints": []}
|
|
640
|
+
with scoped_home(profile_home_param(params)):
|
|
641
|
+
rows = []
|
|
642
|
+
for blueprint in catalog.list_blueprints():
|
|
643
|
+
entry = catalog.blueprint_catalog_entry(blueprint)
|
|
644
|
+
schema = catalog.blueprint_form_schema(blueprint)
|
|
645
|
+
slots = [{**slot, "default": str(slot["default"]) if slot.get("default") is not None else None,
|
|
646
|
+
"options": [str(value) for value in slot.get("options", [])]} for slot in schema["fields"]]
|
|
647
|
+
rows.append({key: entry[key] for key in ("key", "title", "description", "category", "source", "plugin")})
|
|
648
|
+
rows[-1].update(schedule_label=entry["scheduleHuman"], slots=slots)
|
|
649
|
+
return {"blueprints": rows}
|
|
650
|
+
|
|
651
|
+
@registry.method("cron.blueprint_fill")
|
|
652
|
+
def blueprint_fill(ctx: CallContext, params: dict) -> dict:
|
|
653
|
+
key, values = params.get("key"), params.get("values")
|
|
654
|
+
if not isinstance(key, str) or not key.strip() or not isinstance(values, dict) or not all(isinstance(v, str) for v in values.values()):
|
|
655
|
+
raise InvalidParams("key and string slot values are required")
|
|
656
|
+
try:
|
|
657
|
+
catalog = importlib.import_module("cron.blueprint_catalog")
|
|
658
|
+
except ModuleNotFoundError as exc:
|
|
659
|
+
if exc.name != "cron.blueprint_catalog":
|
|
660
|
+
raise
|
|
661
|
+
raise InvalidParams("Automation blueprints are unavailable") from exc
|
|
662
|
+
with scoped_home(profile_home_param(params)):
|
|
663
|
+
blueprint = catalog.get_blueprint(key)
|
|
664
|
+
if blueprint is None:
|
|
665
|
+
raise InvalidParams("Unknown automation blueprint")
|
|
666
|
+
try:
|
|
667
|
+
draft = catalog.fill_blueprint(blueprint, values)
|
|
668
|
+
except catalog.BlueprintFillError as exc:
|
|
669
|
+
raise InvalidParams(str(exc)) from exc
|
|
670
|
+
draft.setdefault("skills", [])
|
|
671
|
+
return {"draft": draft}
|
|
672
|
+
|
|
385
673
|
@registry.method("cron.list")
|
|
386
674
|
def list_(ctx: CallContext, params: dict) -> dict:
|
|
387
|
-
"""Jobs of one store, shaped for the API; the server merges profiles."""
|
|
675
|
+
"""Jobs of one store, shaped for the API; the server merges profiles. ``probe_store`` adds the store's write health."""
|
|
388
676
|
jobs = _jobs()
|
|
389
|
-
|
|
390
|
-
|
|
677
|
+
home = profile_home_param(params)
|
|
678
|
+
with _store(home):
|
|
679
|
+
from .cron_execution import pending_jobs
|
|
680
|
+
pending = {job["id"]: job for job in pending_jobs(home)}
|
|
681
|
+
stored = jobs.list_jobs(include_disabled=True)
|
|
682
|
+
ids = {job["id"] for job in stored}
|
|
683
|
+
stored.extend(job for jid, job in pending.items() if jid not in ids)
|
|
684
|
+
listed = [job_for_api(job) for job in stored]
|
|
685
|
+
result: dict = {"jobs": listed}
|
|
686
|
+
if params.get("probe_store"):
|
|
687
|
+
result["store_health"] = _store_health(jobs.get_cron_output_dir().parent)
|
|
688
|
+
live = _live_executions(home, [str(job.get("id") or "") for job in listed])
|
|
689
|
+
with _RUNNING_LOCK:
|
|
690
|
+
active = set(_RUNNING)
|
|
691
|
+
for index, job in enumerate(listed):
|
|
692
|
+
jid = str(job.get("id") or "")
|
|
693
|
+
if jid in pending and jid not in live and jid not in active:
|
|
694
|
+
# Web preflight must use the accepted fire's profile, even after external job edits.
|
|
695
|
+
job = listed[index] = job_for_api(pending[jid])
|
|
696
|
+
job["running_elsewhere"] = jid in live
|
|
697
|
+
return result
|
|
391
698
|
|
|
392
699
|
@registry.method("cron.get")
|
|
393
700
|
def get(ctx: CallContext, params: dict) -> dict:
|
|
394
|
-
|
|
395
|
-
|
|
701
|
+
from .cron_execution import pending_job
|
|
702
|
+
home, job_id = profile_home_param(params), _job_id(params)
|
|
703
|
+
with _store(home):
|
|
704
|
+
job = pending_job(home, job_id) or _jobs().get_job(job_id)
|
|
396
705
|
return {"job": job_for_api(job) if job else None}
|
|
397
706
|
|
|
398
707
|
@registry.method("cron.create")
|
|
@@ -444,6 +753,7 @@ def register(registry) -> None:
|
|
|
444
753
|
return {"job": job_for_api(job)}
|
|
445
754
|
|
|
446
755
|
@registry.method("cron.update")
|
|
756
|
+
@_settled_mutation
|
|
447
757
|
def update(ctx: CallContext, params: dict) -> dict:
|
|
448
758
|
jobs = _jobs()
|
|
449
759
|
job_id = _job_ref(params)
|
|
@@ -472,6 +782,7 @@ def register(registry) -> None:
|
|
|
472
782
|
return {"job": job_for_api(job)}
|
|
473
783
|
|
|
474
784
|
@registry.method("cron.delete")
|
|
785
|
+
@_settled_mutation
|
|
475
786
|
def delete(ctx: CallContext, params: dict) -> dict:
|
|
476
787
|
job_id = _job_ref(params)
|
|
477
788
|
with _store(profile_home_param(params)):
|
|
@@ -489,6 +800,7 @@ def register(registry) -> None:
|
|
|
489
800
|
return {"job": dict(job)}
|
|
490
801
|
|
|
491
802
|
@registry.method("cron.resume")
|
|
803
|
+
@_settled_mutation
|
|
492
804
|
def resume(ctx: CallContext, params: dict) -> dict:
|
|
493
805
|
with _store(profile_home_param(params)):
|
|
494
806
|
try:
|
|
@@ -505,17 +817,37 @@ def register(registry) -> None:
|
|
|
505
817
|
store_home = profile_home_param(params)
|
|
506
818
|
execution_home = Path(params["execution_home"]).expanduser() if params.get("execution_home") else None
|
|
507
819
|
job_id = _job_id(params)
|
|
820
|
+
from .cron_execution import path
|
|
821
|
+
if path(store_home, job_id).exists():
|
|
822
|
+
return run_tracked({"id": job_id}, store_home, execution_home, ctx, recovery_only=True)
|
|
508
823
|
with _store(store_home):
|
|
509
824
|
job = _jobs().get_job(job_id)
|
|
510
825
|
if not job:
|
|
511
826
|
raise RpcError("Job not found", condition="not_found")
|
|
827
|
+
foreign = _live_executions(store_home, [job_id], strict=True).get(job_id)
|
|
828
|
+
if foreign is not None:
|
|
829
|
+
return {"job_id": job_id, "status": "already_running", "elapsed": _elapsed(foreign)}
|
|
512
830
|
with _RUNNING_LOCK:
|
|
513
831
|
started = _RUNNING.get(job_id)
|
|
514
832
|
if started is not None:
|
|
515
833
|
return {"job_id": job_id, "status": "already_running", "elapsed": round(time.time() - started, 1)}
|
|
516
834
|
_RUNNING[job_id] = time.time()
|
|
835
|
+
try:
|
|
836
|
+
claimed = _claim_fire(store_home, job)
|
|
837
|
+
except BaseException:
|
|
838
|
+
with _RUNNING_LOCK:
|
|
839
|
+
_RUNNING.pop(job_id, None)
|
|
840
|
+
raise
|
|
841
|
+
if claimed is False:
|
|
842
|
+
with _RUNNING_LOCK:
|
|
843
|
+
_RUNNING.pop(job_id, None)
|
|
844
|
+
return {"job_id": job_id, "status": "already_running", "elapsed": 0.0}
|
|
845
|
+
owner = None
|
|
846
|
+
if claimed:
|
|
847
|
+
job = claimed
|
|
848
|
+
owner = str((claimed.get("fire_claim") or {}).get("by") or "") or None
|
|
517
849
|
ctx.emit("started", {"job_id": job_id})
|
|
518
|
-
return run_tracked(job, store_home, execution_home, ctx)
|
|
850
|
+
return run_tracked(job, store_home, execution_home, ctx, owner)
|
|
519
851
|
|
|
520
852
|
@registry.method("cron.status", requires_agent=False)
|
|
521
853
|
def status(ctx: CallContext, params: dict) -> dict:
|