specrails-desktop 2.57.0 → 2.58.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/README.md +1 -1
- package/cli/dist/args.js +10 -4
- package/cli/dist/help.js +1 -1
- package/client/dist/assets/{ActivityFeedPage-DdMNmVBk.js → ActivityFeedPage-BQGppIR0.js} +1 -1
- package/client/dist/assets/{AgentBrowserCapture-CeerUf--.js → AgentBrowserCapture-PI-cxyxi.js} +1 -1
- package/client/dist/assets/{AgentModeAnalyticsPane-BjGb-8rh.js → AgentModeAnalyticsPane-mQsOqdXx.js} +2 -2
- package/client/dist/assets/{AgentModeCodePane-8XRkQEo_.js → AgentModeCodePane-BL-vpmlY.js} +2 -2
- package/client/dist/assets/{AgentModeJobsPane-BN3dgJOA.js → AgentModeJobsPane-DR_06oqH.js} +1 -1
- package/client/dist/assets/AgentsPage-BKIKjm_1.js +118 -0
- package/client/dist/assets/{AnalyticsPage-CyfhYiBn.js → AnalyticsPage-069npRVo.js} +1 -1
- package/client/dist/assets/{CodePage-C2Nf-Q5W.js → CodePage-Alitorrv.js} +2 -2
- package/client/dist/assets/DesktopAnalyticsPage-qtWAPM47.js +1 -0
- package/client/dist/assets/{DocsDialog-Br3bczCp.js → DocsDialog-DwL6rgcT.js} +1 -1
- package/client/dist/assets/{DocsPage-BcDr2cBm.js → DocsPage-BA1hiJZ-.js} +1 -1
- package/client/dist/assets/{ExportDropdown-BVGKEgkd.js → ExportDropdown-BcMpvV89.js} +1 -1
- package/client/dist/assets/InteractiveJobComposer-B3H1YHr6.js +19 -0
- package/client/dist/assets/JobDetailModal-XZQ9eAAy.js +1 -0
- package/client/dist/assets/{JobDetailPage-vBbNeRuz.js → JobDetailPage-B-SIaq_q.js} +1 -1
- package/client/dist/assets/{JobsPage-CJCtPhIz.js → JobsPage-iQXAv2mx.js} +1 -1
- package/client/dist/assets/LoopBuilderPage-B_gbFAHn.js +1 -0
- package/client/dist/assets/LoopPreviewModal-je5pSCf-.js +1 -0
- package/client/dist/assets/LoopsPage-BZwcY5cu.js +2 -0
- package/client/dist/assets/{MinimizedChatsContext-D3QzgV0B.js → MinimizedChatsContext-CNx_0bZu.js} +1 -1
- package/client/dist/assets/{PluginsPage-DOpTV75p.js → PluginsPage-DFocnoHX.js} +2 -2
- package/client/dist/assets/{ProjectSettingsDialog-Dkwrn0z1.js → ProjectSettingsDialog-Bh3S6EBB.js} +1 -1
- package/client/dist/assets/{RepositoryDeliveries-Du2UCf1a.js → RepositoryDeliveries-BiwJuS8r.js} +1 -1
- package/client/dist/assets/{RepositoryScopeSelector-hPGZNRBm.js → RepositoryScopeSelector-C2mvWBQ6.js} +1 -1
- package/client/dist/assets/{ReviewPacketPage-DcOq-UTA.js → ReviewPacketPage-CrAf-WDy.js} +2 -2
- package/client/dist/assets/TemplatePreviewModal-C7DordpN.js +1 -0
- package/client/dist/assets/{TicketDetailModalContext-DAK-NoK6.js → TicketDetailModalContext-B-DySpKL.js} +1 -1
- package/client/dist/assets/{Trans-DEbgkuAY.js → Trans-D3AfDylf.js} +1 -1
- package/client/dist/assets/agentRuntime-BeUb3g9g.js +1 -0
- package/client/dist/assets/agentRuntime-Bg9tbmqm.js +1 -0
- package/client/dist/assets/agentRuntime-D-GJVGkj.js +1 -0
- package/client/dist/assets/agentRuntime-D2__CVI_.js +1 -0
- package/client/dist/assets/agentRuntime-D35dGVOP.js +1 -0
- package/client/dist/assets/agentRuntime-DGz_IqKW.js +1 -0
- package/client/dist/assets/agentRuntime-O1liDt6G.js +1 -0
- package/client/dist/assets/agentRuntime-hInH9Fzj.js +1 -0
- package/client/dist/assets/agentstudio-B2CTtQgZ.js +1 -0
- package/client/dist/assets/agentstudio-BWAKeGgB.js +1 -0
- package/client/dist/assets/{agentstudio-DR7doNBV.js → agentstudio-Cja8Sjcp.js} +1 -1
- package/client/dist/assets/agentstudio-DtfPcr99.js +1 -0
- package/client/dist/assets/agentstudio-QdAJIDef.js +1 -0
- package/client/dist/assets/agentstudio-X70BKf6u.js +1 -0
- package/client/dist/assets/agentstudio-fLXpRGIi.js +1 -0
- package/client/dist/assets/agentstudio-qXKqVnWS.js +1 -0
- package/client/dist/assets/{analytics-jzCkOec3.js → analytics-B1DIQd4m.js} +1 -1
- package/client/dist/assets/{analytics-CoEzWPx4.js → analytics-BAIZ9vE_.js} +1 -1
- package/client/dist/assets/{analytics-DjLlE7jS.js → analytics-BTqgmgBd.js} +1 -1
- package/client/dist/assets/analytics-BWynXPEm.js +1 -0
- package/client/dist/assets/{analytics-nZIjjtCc.js → analytics-DGcUsQ44.js} +1 -1
- package/client/dist/assets/{analytics-BNgZ1Nj7.js → analytics-GnSnpJ4W.js} +1 -1
- package/client/dist/assets/{analytics-DFh5lSOl.js → analytics-TWgYaoO0.js} +1 -1
- package/client/dist/assets/{analytics-DZp-DS2U.js → analytics-kJy6Tuko.js} +1 -1
- package/client/dist/assets/builder-C0FyvM83.js +1 -0
- package/client/dist/assets/builder-C1neIYGR.js +1 -0
- package/client/dist/assets/builder-CvnFm0im.js +1 -0
- package/client/dist/assets/builder-CzW5ovyI.js +1 -0
- package/client/dist/assets/builder-D06L7iPy.js +1 -0
- package/client/dist/assets/builder-DGETbtZj.js +1 -0
- package/client/dist/assets/builder-DXEJUlC0.js +1 -0
- package/client/dist/assets/builder-DyQperXU.js +1 -0
- package/client/dist/assets/common-BCK7c9Pv.js +1 -0
- package/client/dist/assets/common-C0BOdeNi.js +1 -0
- package/client/dist/assets/common-C4Yvvhp6.js +1 -0
- package/client/dist/assets/{common-BrlwEnL7.js → common-DmsclwjP.js} +1 -1
- package/client/dist/assets/common-DpLWwgI1.js +1 -0
- package/client/dist/assets/common-OCvl3c8t.js +1 -0
- package/client/dist/assets/common-c0aujEZC.js +1 -0
- package/client/dist/assets/common-od8WThlf.js +1 -0
- package/client/dist/assets/dashboard-B1ndRHuC.js +1 -0
- package/client/dist/assets/dashboard-BX7KXiqA.js +1 -0
- package/client/dist/assets/dashboard-B_BSNxcN.js +1 -0
- package/client/dist/assets/dashboard-CCeFN7-V.js +1 -0
- package/client/dist/assets/dashboard-CRl0WtFJ.js +1 -0
- package/client/dist/assets/dashboard-XGdQ5mjw.js +1 -0
- package/client/dist/assets/dashboard-_oltA6Jd.js +1 -0
- package/client/dist/assets/dashboard-bAuf5KVk.js +1 -0
- package/client/dist/assets/{formatDistanceToNow-Cc2u5FwQ.js → formatDistanceToNow-aBss1Nai.js} +1 -1
- package/client/dist/assets/{getTimezoneOffsetInMilliseconds-BHm2U-w1.js → getTimezoneOffsetInMilliseconds-DOGj1Mfz.js} +1 -1
- package/client/dist/assets/index-BS7KCWkV.css +2 -0
- package/client/dist/assets/{index-Cxb288Rj.js → index-C4Mv6tV6.js} +32 -32
- package/client/dist/assets/{jira-api-DrGxiD9p.js → jira-api-BYbEPlkr.js} +1 -1
- package/client/dist/assets/{jobs-DSp9DoD_.js → jobs-6_-r75yC.js} +1 -1
- package/client/dist/assets/{jobs-C6qmvUAi.js → jobs-B1yqyId5.js} +1 -1
- package/client/dist/assets/{jobs-DoFaEl9g.js → jobs-BFuV7ttH.js} +1 -1
- package/client/dist/assets/{jobs-DTQO08nJ.js → jobs-BKa8LuwA.js} +1 -1
- package/client/dist/assets/{jobs-DrF8Do8s.js → jobs-BKiK-ZWh.js} +1 -1
- package/client/dist/assets/{jobs-DLHdDkHc.js → jobs-DmKV7b7d.js} +1 -1
- package/client/dist/assets/{jobs-C9j2PuwW.js → jobs-KG4Sn12X.js} +1 -1
- package/client/dist/assets/{jobs-Dpw6AEOP.js → jobs-pPv4KWjb.js} +1 -1
- package/client/dist/assets/loop-layout-BF8xMiNv.js +7 -0
- package/client/dist/assets/loops-B4_D-nbg.js +1 -0
- package/client/dist/assets/loops-BJDY9FTh.js +1 -0
- package/client/dist/assets/loops-BO3V8244.js +1 -0
- package/client/dist/assets/loops-C-hZK8pf.js +1 -0
- package/client/dist/assets/loops-CDOWZ0ci.js +1 -0
- package/client/dist/assets/loops-Dv849ckF.js +1 -0
- package/client/dist/assets/loops-Q9LIgGkq.js +1 -0
- package/client/dist/assets/loops-eUnCfLjC.js +1 -0
- package/client/dist/assets/{project-repositories-9Wzs0coQ.js → project-repositories-CSnfzUpg.js} +1 -1
- package/client/dist/assets/settings-3o4Ntzi5.js +1 -0
- package/client/dist/assets/settings-B97jlkwD.js +1 -0
- package/client/dist/assets/settings-BTaT_bsS.js +1 -0
- package/client/dist/assets/settings-CT8w7WiC.js +1 -0
- package/client/dist/assets/settings-Cvr8_tvh.js +1 -0
- package/client/dist/assets/settings-Dn1qgAC8.js +1 -0
- package/client/dist/assets/settings-EShDdg4J.js +1 -0
- package/client/dist/assets/settings-tfLhQZRN.js +1 -0
- package/client/dist/assets/{setup-Dwzt5nO1.js → setup-CBMu3tqH.js} +1 -1
- package/client/dist/assets/{setup-B5FHuxvB.js → setup-CPh-e6J2.js} +1 -1
- package/client/dist/assets/{setup-DpqzaEEA.js → setup-CRKLp_ke.js} +1 -1
- package/client/dist/assets/{setup-CROyqD5N.js → setup-Cy302v4J.js} +1 -1
- package/client/dist/assets/{setup-C9Fy9PKQ.js → setup-D681vTPf.js} +1 -1
- package/client/dist/assets/{setup-BihzXadx.js → setup-DYK0VBkE.js} +1 -1
- package/client/dist/assets/{setup-BtC6Hg4K.js → setup-KAuiIiVs.js} +1 -1
- package/client/dist/assets/{setup-zLEDnG7N.js → setup-OGX-oCfI.js} +1 -1
- package/client/dist/assets/{spending-T5GM7wpn.js → spending-HBSk-6jv.js} +1 -1
- package/client/dist/assets/{useDesktop-QogiN0zR.js → useDesktop-0unHDBJJ.js} +1 -1
- package/client/dist/assets/useRuntimeRuns-BN1rhyEm.js +1 -0
- package/client/dist/assets/{useSharedWebSocket-Dz-HI6De.js → useSharedWebSocket-BZMrX4jr.js} +2 -2
- package/client/dist/index.html +25 -25
- package/docs/agent-live-steering.md +16 -0
- package/docs/cli.md +7 -7
- package/docs/codex.md +2 -2
- package/docs/customizing.md +1 -1
- package/docs/gemini.md +1 -1
- package/docs/getting-started.md +1 -1
- package/docs/guide/de/integrations/7-local-engines.md +1 -1
- package/docs/guide/de/pipeline/1-rails-and-jobs.md +8 -6
- package/docs/guide/de/pipeline/2-the-job-detail-view.md +11 -2
- package/docs/guide/de/pipeline/4-picking-an-engine-per-rail.md +2 -2
- package/docs/guide/de/pipeline/5-the-loop-builder.md +24 -5
- package/docs/guide/en/integrations/7-local-engines.md +1 -1
- package/docs/guide/en/pipeline/1-rails-and-jobs.md +8 -6
- package/docs/guide/en/pipeline/2-the-job-detail-view.md +11 -2
- package/docs/guide/en/pipeline/4-picking-an-engine-per-rail.md +2 -2
- package/docs/guide/en/pipeline/5-the-loop-builder.md +34 -5
- package/docs/guide/en/settings/3-pipeline-telemetry-and-diagnostics.md +1 -1
- package/docs/guide/es/integrations/7-local-engines.md +1 -1
- package/docs/guide/es/pipeline/1-rails-and-jobs.md +8 -6
- package/docs/guide/es/pipeline/2-the-job-detail-view.md +11 -2
- package/docs/guide/es/pipeline/4-picking-an-engine-per-rail.md +2 -2
- package/docs/guide/es/pipeline/5-the-loop-builder.md +30 -5
- package/docs/guide/es/settings/3-pipeline-telemetry-and-diagnostics.md +1 -2
- package/docs/guide/fr/integrations/7-local-engines.md +1 -1
- package/docs/guide/fr/pipeline/1-rails-and-jobs.md +8 -6
- package/docs/guide/fr/pipeline/2-the-job-detail-view.md +11 -2
- package/docs/guide/fr/pipeline/4-picking-an-engine-per-rail.md +2 -2
- package/docs/guide/fr/pipeline/5-the-loop-builder.md +24 -5
- package/docs/guide/it/integrations/7-local-engines.md +1 -1
- package/docs/guide/it/pipeline/1-rails-and-jobs.md +8 -6
- package/docs/guide/it/pipeline/2-the-job-detail-view.md +11 -2
- package/docs/guide/it/pipeline/4-picking-an-engine-per-rail.md +2 -2
- package/docs/guide/it/pipeline/5-the-loop-builder.md +24 -5
- package/docs/guide/ja/integrations/7-local-engines.md +1 -1
- package/docs/guide/ja/pipeline/1-rails-and-jobs.md +8 -6
- package/docs/guide/ja/pipeline/2-the-job-detail-view.md +11 -2
- package/docs/guide/ja/pipeline/4-picking-an-engine-per-rail.md +2 -2
- package/docs/guide/ja/pipeline/5-the-loop-builder.md +24 -5
- package/docs/guide/pt/integrations/7-local-engines.md +1 -1
- package/docs/guide/pt/pipeline/1-rails-and-jobs.md +8 -6
- package/docs/guide/pt/pipeline/2-the-job-detail-view.md +11 -2
- package/docs/guide/pt/pipeline/4-picking-an-engine-per-rail.md +2 -2
- package/docs/guide/pt/pipeline/5-the-loop-builder.md +24 -5
- package/docs/guide/zh/integrations/7-local-engines.md +1 -1
- package/docs/guide/zh/pipeline/1-rails-and-jobs.md +8 -6
- package/docs/guide/zh/pipeline/2-the-job-detail-view.md +11 -2
- package/docs/guide/zh/pipeline/4-picking-an-engine-per-rail.md +2 -2
- package/docs/guide/zh/pipeline/5-the-loop-builder.md +24 -5
- package/docs/internals/api-reference.md +2 -2
- package/docs/internals/ci-performance.md +71 -0
- package/docs/internals/companion-rails-as-loops-contract.md +4 -3
- package/docs/internals/configuration.md +3 -3
- package/docs/internals/core-runtime-updates.md +3 -2
- package/docs/internals/interactive-jobs.md +2 -2
- package/docs/internals/loop-step-log-explorer.md +42 -0
- package/docs/internals/mission-rail-cards.md +1 -1
- package/docs/internals/operations-runbook.md +22 -0
- package/docs/internals/programmatic-agent-runtime.md +130 -7
- package/docs/internals/project-builder.md +35 -16
- package/docs/internals/source-map.md +48 -2
- package/docs/kimi.md +2 -2
- package/docs/local-providers.md +1 -1
- package/docs/running-pipelines.md +26 -18
- package/docs/tracking-cost.md +10 -1
- package/package.json +2 -1
- package/server/dist/core-compat.js +8 -3
- package/server/dist/core-execution.js +49 -3
- package/server/dist/db/migrations.js +101 -0
- package/server/dist/desktop-db.js +28 -0
- package/server/dist/desktop-router.js +2 -0
- package/server/dist/mcp/guide.js +6 -3
- package/server/dist/mcp/tools/jobs.js +14 -1
- package/server/dist/mcp/tools/loops.js +17 -8
- package/server/dist/mcp/tools/rails.js +3 -3
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-bridge.js +313 -9
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-controls-router.js +34 -1
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-controls.js +203 -21
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-effective-config.js +46 -1
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-engines.js +10 -0
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-loader.js +68 -3
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-metrics.js +16 -8
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-package-gc.js +81 -0
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-package-lock.js +37 -0
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-package.js +30 -24
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-retention-host.js +87 -0
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-retention-quarantine.js +231 -0
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-retention-records.js +32 -0
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-retention.js +102 -0
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-settings-router.js +12 -7
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-settings.js +57 -13
- package/server/dist/modules/agents/runtime/agent-catalog.js +32 -0
- package/server/dist/modules/agents/runtime/agent-refine-manager.js +12 -0
- package/server/dist/modules/agents/runtime/agent-role-descriptor.js +64 -0
- package/server/dist/modules/agents/runtime/profiles-router.js +26 -34
- package/server/dist/modules/builder/runtime/milestone-chain.js +121 -73
- package/server/dist/modules/delivery/runtime/definition-fork.js +158 -0
- package/server/dist/modules/delivery/runtime/delivery-evidence.js +56 -10
- package/server/dist/modules/delivery/runtime/isolated-settlement-reconstruction.js +99 -0
- package/server/dist/modules/delivery/runtime/isolated-settlement-store.js +53 -0
- package/server/dist/modules/delivery/runtime/multi-repo-execution-store.js +3 -1
- package/server/dist/modules/delivery/runtime/rail-isolated-launch.js +388 -203
- package/server/dist/modules/delivery/runtime/rail-launch-parser.js +3 -2
- package/server/dist/modules/delivery/runtime/rail-pr-store.js +6 -1
- package/server/dist/modules/delivery/runtime/rails-router.js +66 -10
- package/server/dist/modules/execution/runtime/queue-manager.js +11 -2
- package/server/dist/modules/loops/runtime/builtin-loops.js +70 -0
- package/server/dist/modules/loops/runtime/definition-cancellation.js +31 -0
- package/server/dist/modules/loops/runtime/legacy-launch-telemetry.js +78 -0
- package/server/dist/modules/loops/runtime/loop-command-catalog.js +13 -12
- package/server/dist/modules/loops/runtime/loop-compat.js +219 -0
- package/server/dist/modules/loops/runtime/loop-core-factory.js +42 -0
- package/server/dist/modules/loops/runtime/loop-definition-controls.js +48 -0
- package/server/dist/modules/loops/runtime/loop-definition-events.js +145 -0
- package/server/dist/modules/loops/runtime/loop-definition-recovery.js +106 -0
- package/server/dist/modules/loops/runtime/loop-definition-run.js +36 -0
- package/server/dist/modules/loops/runtime/loop-definition.js +246 -0
- package/server/dist/modules/loops/runtime/loop-effect.js +10 -1
- package/server/dist/modules/loops/runtime/loop-executors.js +63 -3
- package/server/dist/modules/loops/runtime/loop-factory.js +35 -15
- package/server/dist/modules/loops/runtime/loop-graph.js +84 -8
- package/server/dist/modules/loops/runtime/loop-migration.js +36 -0
- package/server/dist/modules/loops/runtime/loop-preview.js +10 -0
- package/server/dist/modules/loops/runtime/loop-run-manager.js +206 -13
- package/server/dist/modules/loops/runtime/loop-runs-store.js +271 -7
- package/server/dist/modules/loops/runtime/loop-templates.js +205 -45
- package/server/dist/modules/loops/runtime/loops-router.js +246 -29
- package/server/dist/modules/loops/runtime/loops-store.js +140 -20
- package/server/dist/modules/missions/runtime/agent-operator-prompt.js +14 -11
- package/server/dist/modules/specs/runtime/spec-addenda.js +28 -0
- package/server/dist/project-registry.js +87 -51
- package/server/dist/project-router-jobs.js +17 -6
- package/server/dist/project-router-loop-runs.js +200 -0
- package/server/dist/project-router-spending.js +17 -0
- package/server/dist/project-router.js +8 -6
- package/server/dist/schemas/agent-runtime.schema.json +93 -1
- package/server/dist/schemas/workflow-definition.schema.json +312 -0
- package/server/dist/vitest-setup.js +22 -0
- package/server/dist/worktree-overlay.js +1 -1
- package/client/dist/assets/AgentsPage-DfHWgP_x.js +0 -87
- package/client/dist/assets/DesktopAnalyticsPage-tc0p4jJW.js +0 -1
- package/client/dist/assets/InteractiveJobComposer-DFPpdg2D.js +0 -19
- package/client/dist/assets/JobDetailModal-BdOv-l-M.js +0 -1
- package/client/dist/assets/LoopBuilderPage-BemvUOxy.js +0 -7
- package/client/dist/assets/LoopPreviewModal-C6hNAeYN.js +0 -1
- package/client/dist/assets/LoopsPage-BZr0quvh.js +0 -1
- package/client/dist/assets/TemplatePreviewModal-Cejcquah.js +0 -1
- package/client/dist/assets/agentRuntime-BUimiX__.js +0 -1
- package/client/dist/assets/agentRuntime-BcN97sAB.js +0 -1
- package/client/dist/assets/agentRuntime-Ckyg-ahT.js +0 -1
- package/client/dist/assets/agentRuntime-DJCwyX4t.js +0 -1
- package/client/dist/assets/agentRuntime-DVzPJOl0.js +0 -1
- package/client/dist/assets/agentRuntime-V9AZTG3c.js +0 -1
- package/client/dist/assets/agentRuntime-bAAjw1vw.js +0 -1
- package/client/dist/assets/agentRuntime-v4qKAYH-.js +0 -1
- package/client/dist/assets/agentstudio-BNUth0uj.js +0 -1
- package/client/dist/assets/agentstudio-BNdB5hkr.js +0 -1
- package/client/dist/assets/agentstudio-C7-bt8FO.js +0 -1
- package/client/dist/assets/agentstudio-C8uNchpV.js +0 -1
- package/client/dist/assets/agentstudio-CKabyMlg.js +0 -1
- package/client/dist/assets/agentstudio-CN2pokQu.js +0 -1
- package/client/dist/assets/agentstudio-y2dVDXeM.js +0 -1
- package/client/dist/assets/analytics-BlypLLR6.js +0 -1
- package/client/dist/assets/builder-765NeYTp.js +0 -1
- package/client/dist/assets/builder-8MG3V6ea.js +0 -1
- package/client/dist/assets/builder-B-1UBs5K.js +0 -1
- package/client/dist/assets/builder-BTiGnPT0.js +0 -1
- package/client/dist/assets/builder-Cx9Rz-7u.js +0 -1
- package/client/dist/assets/builder-DKFMELBl.js +0 -1
- package/client/dist/assets/builder-OcmKrC0h.js +0 -1
- package/client/dist/assets/builder-P9qVBXFZ.js +0 -1
- package/client/dist/assets/common-B5AZNYMH.js +0 -1
- package/client/dist/assets/common-BFZ0c_vN.js +0 -1
- package/client/dist/assets/common-BttQx3z4.js +0 -1
- package/client/dist/assets/common-CM65k_-e.js +0 -1
- package/client/dist/assets/common-D4mvFFlu.js +0 -1
- package/client/dist/assets/common-TZ9Ny96S.js +0 -1
- package/client/dist/assets/common-nFpSF4GC.js +0 -1
- package/client/dist/assets/dashboard-B9uWNGO2.js +0 -1
- package/client/dist/assets/dashboard-BIwmO9wc.js +0 -1
- package/client/dist/assets/dashboard-BV4vOZkR.js +0 -1
- package/client/dist/assets/dashboard-Bf6MPVsM.js +0 -1
- package/client/dist/assets/dashboard-C8klWVvZ.js +0 -1
- package/client/dist/assets/dashboard-D1ORhPF0.js +0 -1
- package/client/dist/assets/dashboard-DSubvs9x.js +0 -1
- package/client/dist/assets/dashboard-px_vlRx_.js +0 -1
- package/client/dist/assets/index-C51x61Bz.css +0 -2
- package/client/dist/assets/loops-B7YpGizO.js +0 -1
- package/client/dist/assets/loops-BomD2J-h.js +0 -1
- package/client/dist/assets/loops-BrQHSQw3.js +0 -1
- package/client/dist/assets/loops-BvBwvTPA.js +0 -1
- package/client/dist/assets/loops-CODk6Slz.js +0 -1
- package/client/dist/assets/loops-DC6k-Ym-.js +0 -1
- package/client/dist/assets/loops-DSEg8-uY.js +0 -1
- package/client/dist/assets/loops-DhCoXD_v.js +0 -1
- package/client/dist/assets/settings-BsehKpty.js +0 -1
- package/client/dist/assets/settings-CQWob3SB.js +0 -1
- package/client/dist/assets/settings-CgCl0hzy.js +0 -1
- package/client/dist/assets/settings-DLRLd22L.js +0 -1
- package/client/dist/assets/settings-Dn5prAFl.js +0 -1
- package/client/dist/assets/settings-HZSu-K4l.js +0 -1
- package/client/dist/assets/settings-p4OVIyRh.js +0 -1
- package/client/dist/assets/settings-rddaldUp.js +0 -1
- package/client/dist/assets/useRuntimeRuns-Dqmz9fS9.js +0 -1
- package/docs/guide/de/pipeline/3-batch-implement-and-multi-feature.md +0 -84
- package/docs/guide/en/pipeline/3-batch-implement-and-multi-feature.md +0 -126
- package/docs/guide/es/pipeline/3-batch-implement-and-multi-feature.md +0 -114
- package/docs/guide/fr/pipeline/3-batch-implement-and-multi-feature.md +0 -84
- package/docs/guide/it/pipeline/3-batch-implement-and-multi-feature.md +0 -84
- package/docs/guide/ja/pipeline/3-batch-implement-and-multi-feature.md +0 -84
- package/docs/guide/pt/pipeline/3-batch-implement-and-multi-feature.md +0 -84
- package/docs/guide/zh/pipeline/3-batch-implement-and-multi-feature.md +0 -84
- package/server/dist/modules/loops/runtime/loop-templates-ported.js +0 -675
- /package/client/dist/assets/{LoopBuilderPage-DtxX27Jz.css → loop-layout-DtxX27Jz.css} +0 -0
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.saveDefinitionRun = saveDefinitionRun;
|
|
7
|
+
exports.readDefinitionRun = readDefinitionRun;
|
|
8
|
+
exports.recordDefinitionCheckpoint = recordDefinitionCheckpoint;
|
|
9
|
+
exports.markDefinitionRestart = markDefinitionRestart;
|
|
10
|
+
exports.definitionRepositoryMounts = definitionRepositoryMounts;
|
|
11
|
+
exports.claimDefinitionExecution = claimDefinitionExecution;
|
|
12
|
+
exports.readDefinitionSuccessor = readDefinitionSuccessor;
|
|
13
|
+
exports.readDefinitionForkTarget = readDefinitionForkTarget;
|
|
14
|
+
exports.releaseDefinitionExecution = releaseDefinitionExecution;
|
|
15
|
+
exports.readDefinitionExecutionClaim = readDefinitionExecutionClaim;
|
|
16
|
+
exports.readDefinitionLineage = readDefinitionLineage;
|
|
17
|
+
exports.linkDefinitionFork = linkDefinitionFork;
|
|
3
18
|
exports.createLoopRun = createLoopRun;
|
|
4
19
|
exports.finishLoopRunAndJob = finishLoopRunAndJob;
|
|
5
20
|
exports.listPendingLoopTerminalRecoveries = listPendingLoopTerminalRecoveries;
|
|
@@ -24,6 +39,218 @@ exports.listActiveLoopRuns = listActiveLoopRuns;
|
|
|
24
39
|
exports.listLoopRuns = listLoopRuns;
|
|
25
40
|
exports.countRunningForLoop = countRunningForLoop;
|
|
26
41
|
exports.reconcileOrphanLoopRuns = reconcileOrphanLoopRuns;
|
|
42
|
+
const agent_runtime_retention_records_1 = require("../../agent-runtime/runtime/agent-runtime-retention-records");
|
|
43
|
+
/**
|
|
44
|
+
* Loop runs store — per-project record (jobs.sqlite `loop_runs`) of each
|
|
45
|
+
* executed loop. The manager creates a row at launch, updates counters as it
|
|
46
|
+
* iterates, and finalises it at the end. Powers loop analytics + the
|
|
47
|
+
* active derived state (a loop is active iff a row here has status='running'
|
|
48
|
+
* or status='paused').
|
|
49
|
+
*/
|
|
50
|
+
const path_1 = __importDefault(require("path"));
|
|
51
|
+
const node_fs_1 = require("node:fs");
|
|
52
|
+
function frozenJson(value) {
|
|
53
|
+
const serialized = JSON.stringify(value, (_key, item) => {
|
|
54
|
+
if (typeof item === 'function' || typeof item === 'symbol' || typeof item === 'bigint' || typeof item === 'number' && !Number.isFinite(item))
|
|
55
|
+
throw new Error('Durable workflow inputs must contain only JSON data');
|
|
56
|
+
return item;
|
|
57
|
+
});
|
|
58
|
+
if (!serialized || Buffer.byteLength(serialized) > 4 * 1024 * 1024)
|
|
59
|
+
throw new Error('Durable workflow inputs exceed 4 MiB');
|
|
60
|
+
const sort = (input) => Array.isArray(input) ? input.map(sort) : input && typeof input === 'object'
|
|
61
|
+
? Object.fromEntries(Object.entries(input).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, item]) => [key, sort(item)])) : input;
|
|
62
|
+
return JSON.stringify(sort(JSON.parse(serialized)));
|
|
63
|
+
}
|
|
64
|
+
/** Freeze launch data before spawn; preparation may add fields, never replace them. */
|
|
65
|
+
function saveDefinitionRun(db, runId, input) {
|
|
66
|
+
return db.transaction(() => {
|
|
67
|
+
const row = getLoopRun(db, runId);
|
|
68
|
+
if (!row || row.engine_version !== null && row.engine_version !== undefined && row.engine_version !== 2)
|
|
69
|
+
throw new Error('Definition run is missing or belongs to another engine');
|
|
70
|
+
const { request, ...fields } = input;
|
|
71
|
+
const requestJson = request ? frozenJson({ ...request, runId }) : row.run_request_json;
|
|
72
|
+
if (!requestJson || row.run_request_json && row.run_request_json !== requestJson)
|
|
73
|
+
throw new Error('Frozen loop request cannot change');
|
|
74
|
+
const normalized = JSON.parse(requestJson);
|
|
75
|
+
if (normalized.projectId !== row.project_id || normalized.loopId !== row.loop_id)
|
|
76
|
+
throw new Error('Frozen loop request belongs to another project or loop');
|
|
77
|
+
const prior = JSON.parse(row.runtime_metadata_json ?? '{"source":"definition"}');
|
|
78
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
79
|
+
if (value !== undefined && key in prior && frozenJson(prior[key]) !== frozenJson(value))
|
|
80
|
+
throw new Error('Frozen Core metadata cannot change: ' + key);
|
|
81
|
+
}
|
|
82
|
+
const metadata = { ...prior, ...JSON.parse(frozenJson(fields)) };
|
|
83
|
+
if (metadata.source !== 'definition' || metadata.definitionHash !== undefined && !/^[a-f0-9]{64}$/.test(metadata.definitionHash))
|
|
84
|
+
throw new Error('Invalid Core definition identity');
|
|
85
|
+
db.prepare('UPDATE loop_runs SET engine_version=2,run_request_json=?,runtime_metadata_json=? WHERE id=?').run(requestJson, frozenJson(metadata), runId);
|
|
86
|
+
return { row: getLoopRun(db, runId), request: normalized, metadata };
|
|
87
|
+
})();
|
|
88
|
+
}
|
|
89
|
+
function readDefinitionRun(db, runId) {
|
|
90
|
+
const row = getLoopRun(db, runId);
|
|
91
|
+
if (!row || row.engine_version !== 2 || !row.run_request_json)
|
|
92
|
+
return undefined;
|
|
93
|
+
const request = JSON.parse(row.run_request_json), metadata = JSON.parse(row.runtime_metadata_json ?? '{"source":"definition"}');
|
|
94
|
+
if (request.runId !== runId || request.projectId !== row.project_id || request.loopId !== row.loop_id || metadata.source !== 'definition')
|
|
95
|
+
throw new Error('Frozen workflow ownership is inconsistent');
|
|
96
|
+
return { row, request, metadata };
|
|
97
|
+
}
|
|
98
|
+
/** The projection cursor is independent of the aggregate accounting totals. */
|
|
99
|
+
function recordDefinitionCheckpoint(db, runId, checkpoint) {
|
|
100
|
+
for (const value of [checkpoint.revision, checkpoint.eventCursor])
|
|
101
|
+
if (value !== undefined && (!Number.isSafeInteger(value) || value < 0))
|
|
102
|
+
throw new Error('Core checkpoint cursors must be nonnegative integers');
|
|
103
|
+
const serialized = frozenJson(checkpoint);
|
|
104
|
+
db.transaction(() => {
|
|
105
|
+
const row = readDefinitionRun(db, runId)?.row;
|
|
106
|
+
if (!row)
|
|
107
|
+
throw new Error('Frozen definition run is missing');
|
|
108
|
+
if (checkpoint.revision !== undefined && checkpoint.revision < (row.core_revision ?? 0) || checkpoint.eventCursor !== undefined && checkpoint.eventCursor < (row.core_event_cursor ?? 0))
|
|
109
|
+
return;
|
|
110
|
+
db.prepare('UPDATE loop_runs SET core_revision=?,core_event_cursor=?,runtime_status_json=? WHERE id=?')
|
|
111
|
+
.run(checkpoint.revision ?? row.core_revision ?? null, checkpoint.eventCursor ?? row.core_event_cursor ?? null, serialized, runId);
|
|
112
|
+
})();
|
|
113
|
+
}
|
|
114
|
+
/** Startup preserves the job and launch ownership; no terminal callback is queued. */
|
|
115
|
+
function markDefinitionRestart(db, runId, checkpoint = {}) {
|
|
116
|
+
db.transaction(() => {
|
|
117
|
+
if (!readDefinitionRun(db, runId))
|
|
118
|
+
throw new Error('Frozen definition run is missing');
|
|
119
|
+
recordDefinitionCheckpoint(db, runId, checkpoint);
|
|
120
|
+
db.prepare("UPDATE loop_runs SET status='paused',final_outcome=NULL,finished_at=NULL,restart_reason='restart' WHERE id=? AND status IN ('running','paused')").run(runId);
|
|
121
|
+
})();
|
|
122
|
+
}
|
|
123
|
+
function normalizeMount(mount) {
|
|
124
|
+
const absolute = path_1.default.resolve(mount);
|
|
125
|
+
const resolved = (0, node_fs_1.existsSync)(absolute) ? (0, node_fs_1.realpathSync)(absolute) : absolute;
|
|
126
|
+
const trimmed = resolved === path_1.default.parse(resolved).root ? resolved : resolved.replace(/[\\/]+$/, '');
|
|
127
|
+
return process.platform === 'win32' ? trimmed.toLowerCase() : trimmed;
|
|
128
|
+
}
|
|
129
|
+
function mountsOverlap(a, b) {
|
|
130
|
+
const contains = (parent, child) => {
|
|
131
|
+
const relative = path_1.default.relative(parent, child);
|
|
132
|
+
return relative === '' || !path_1.default.isAbsolute(relative) && relative !== '..' && !relative.startsWith('..' + path_1.default.sep);
|
|
133
|
+
};
|
|
134
|
+
return contains(a, b) || contains(b, a);
|
|
135
|
+
}
|
|
136
|
+
function parseMounts(raw) {
|
|
137
|
+
const parsed = JSON.parse(raw);
|
|
138
|
+
if (!Array.isArray(parsed) || parsed.some(item => typeof item !== 'string' || !path_1.default.isAbsolute(item)))
|
|
139
|
+
throw new Error('Invalid saved definition execution mounts');
|
|
140
|
+
return parsed;
|
|
141
|
+
}
|
|
142
|
+
/** Direct fork ancestry of a run: itself, its parents up the fork_of chain and every descendant. */
|
|
143
|
+
function definitionLineageIds(db, runId, parentRunId) {
|
|
144
|
+
const lineage = new Set();
|
|
145
|
+
const parents = db.prepare('SELECT fork_of FROM loop_runs WHERE id = ?');
|
|
146
|
+
const children = db.prepare('SELECT id FROM loop_runs WHERE fork_of = ?');
|
|
147
|
+
const queue = [runId, ...(parentRunId ? [parentRunId] : [])];
|
|
148
|
+
while (queue.length) {
|
|
149
|
+
const current = queue.shift();
|
|
150
|
+
if (lineage.has(current))
|
|
151
|
+
continue;
|
|
152
|
+
lineage.add(current);
|
|
153
|
+
const parent = parents.get(current)?.fork_of;
|
|
154
|
+
if (parent && !lineage.has(parent))
|
|
155
|
+
queue.push(parent);
|
|
156
|
+
for (const child of children.all(current))
|
|
157
|
+
if (!lineage.has(child.id))
|
|
158
|
+
queue.push(child.id);
|
|
159
|
+
}
|
|
160
|
+
return lineage;
|
|
161
|
+
}
|
|
162
|
+
/** The checkouts a frozen request may write to. Nested paths share a mount. */
|
|
163
|
+
function definitionRepositoryMounts(request) {
|
|
164
|
+
const mounts = [
|
|
165
|
+
...(request.executionManifest?.repositories.map(repository => repository.worktreePath) ?? []),
|
|
166
|
+
...(request.isolation ? [request.isolation.worktreePath] : []),
|
|
167
|
+
...(request.executionManifest ? [] : [request.repoDir ?? request.cwd]),
|
|
168
|
+
].filter((mount) => typeof mount === 'string' && mount.length > 0);
|
|
169
|
+
return [...new Set(mounts.map(normalizeMount))].sort();
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Synchronous, atomic admission for one Core process over a worktree. A live
|
|
173
|
+
* claim of another run that overlaps any repository mount rejects: a parent
|
|
174
|
+
* and its fork share the checkout, and two owners of the same run are never
|
|
175
|
+
* allowed. Paused rows are not claims; only a resident process holds one, and
|
|
176
|
+
* settlement, cancellation and restart reconciliation release it.
|
|
177
|
+
*/
|
|
178
|
+
function claimDefinitionExecution(db, runId, claim) {
|
|
179
|
+
if (!runId || !claim.owner)
|
|
180
|
+
throw new Error('Execution claims require a run and an owner');
|
|
181
|
+
const mounts = [...new Set(claim.repositoryMounts.map(normalizeMount))].sort();
|
|
182
|
+
return db.transaction(() => {
|
|
183
|
+
const collecting = claim.owner.startsWith('retention:') || claim.owner.startsWith('retention-recovery:');
|
|
184
|
+
if ((0, agent_runtime_retention_records_1.readRuntimeExpiration)(db, runId) && !claim.owner.startsWith('retention-recovery:'))
|
|
185
|
+
return { ok: false, reason: 'expired', conflictingRunId: runId };
|
|
186
|
+
const successor = readDefinitionSuccessor(db, runId);
|
|
187
|
+
if (successor && !(collecting && (0, agent_runtime_retention_records_1.readRuntimeExpiration)(db, successor)))
|
|
188
|
+
return { ok: false, reason: 'lineage_conflict', conflictingRunId: successor };
|
|
189
|
+
const pendingFork = readDefinitionForkTarget(db, runId);
|
|
190
|
+
if (pendingFork && !claim.owner.startsWith('fork:') && !(collecting && (0, agent_runtime_retention_records_1.readRuntimeExpiration)(db, pendingFork)))
|
|
191
|
+
return { ok: false, reason: 'lineage_conflict', conflictingRunId: pendingFork };
|
|
192
|
+
const existing = db.prepare('SELECT run_id, owner, repository_mounts_json FROM definition_execution_claims WHERE run_id = ?').get(runId);
|
|
193
|
+
if (existing)
|
|
194
|
+
return { ok: false, reason: 'active_owner', conflictingRunId: runId };
|
|
195
|
+
if (mounts.length) {
|
|
196
|
+
const lineage = definitionLineageIds(db, runId, claim.parentRunId);
|
|
197
|
+
const live = db.prepare('SELECT run_id, owner, repository_mounts_json FROM definition_execution_claims ORDER BY claimed_at, run_id').all();
|
|
198
|
+
for (const row of live) {
|
|
199
|
+
const overlap = parseMounts(row.repository_mounts_json).some(mount => mounts.some(candidate => mountsOverlap(candidate, mount)));
|
|
200
|
+
if (!overlap)
|
|
201
|
+
continue;
|
|
202
|
+
return { ok: false, reason: lineage.has(row.run_id) ? 'lineage_conflict' : 'active_owner', conflictingRunId: row.run_id };
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const at = new Date().toISOString();
|
|
206
|
+
db.prepare('INSERT INTO definition_execution_claims (run_id, owner, repository_mounts_json, claimed_at, heartbeat_at) VALUES (?, ?, ?, ?, ?)')
|
|
207
|
+
.run(runId, claim.owner, JSON.stringify(mounts), at, at);
|
|
208
|
+
return { ok: true, release: () => releaseDefinitionExecution(db, runId, claim.owner) };
|
|
209
|
+
})();
|
|
210
|
+
}
|
|
211
|
+
/** The historical run remains intact; only its child owns shared checkout effects. */
|
|
212
|
+
function readDefinitionSuccessor(db, runId) {
|
|
213
|
+
const row = db.prepare('SELECT child_run_id FROM definition_fork_operations WHERE source_run_id=? AND adopted=1').get(runId);
|
|
214
|
+
return row?.child_run_id;
|
|
215
|
+
}
|
|
216
|
+
function readDefinitionForkTarget(db, runId) {
|
|
217
|
+
return db.prepare('SELECT child_run_id FROM definition_fork_operations WHERE source_run_id=? ORDER BY adopted DESC,created_at LIMIT 1').get(runId)?.child_run_id;
|
|
218
|
+
}
|
|
219
|
+
/** Idempotent: only the owner that holds the claim can release it. */
|
|
220
|
+
function releaseDefinitionExecution(db, runId, owner) {
|
|
221
|
+
db.prepare('DELETE FROM definition_execution_claims WHERE run_id = ? AND owner = ?').run(runId, owner);
|
|
222
|
+
}
|
|
223
|
+
function readDefinitionExecutionClaim(db, runId) {
|
|
224
|
+
const row = db.prepare('SELECT * FROM definition_execution_claims WHERE run_id = ?').get(runId);
|
|
225
|
+
return row ? { runId: row.run_id, owner: row.owner, repositoryMounts: parseMounts(row.repository_mounts_json), claimedAt: row.claimed_at, heartbeatAt: row.heartbeat_at } : undefined;
|
|
226
|
+
}
|
|
227
|
+
function readDefinitionLineage(db, runId) {
|
|
228
|
+
const row = getLoopRun(db, runId);
|
|
229
|
+
let forkCut = null;
|
|
230
|
+
if (row?.fork_cut_json) {
|
|
231
|
+
try {
|
|
232
|
+
forkCut = JSON.parse(row.fork_cut_json);
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
forkCut = null;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const children = db.prepare('SELECT id FROM loop_runs WHERE fork_of = ? ORDER BY started_at, id').all(runId).map(child => child.id);
|
|
239
|
+
return { runId, forkOf: row?.fork_of ?? null, children, forkCut };
|
|
240
|
+
}
|
|
241
|
+
/** Persist fork ancestry and Core cut provenance on a NEW child row. The source row is never touched. */
|
|
242
|
+
function linkDefinitionFork(db, childRunId, link) {
|
|
243
|
+
db.transaction(() => {
|
|
244
|
+
const child = readDefinitionRun(db, childRunId);
|
|
245
|
+
if (!child)
|
|
246
|
+
throw new Error('Forked definition run is missing');
|
|
247
|
+
if (childRunId === link.forkOf || !getLoopRun(db, link.forkOf))
|
|
248
|
+
throw new Error('Fork source is missing or identical to the child');
|
|
249
|
+
if (child.row.fork_of && child.row.fork_of !== link.forkOf)
|
|
250
|
+
throw new Error('Fork ancestry cannot change');
|
|
251
|
+
db.prepare('UPDATE loop_runs SET fork_of = ?, fork_cut_json = ? WHERE id = ?').run(link.forkOf, frozenJson(link.forkCut), childRunId);
|
|
252
|
+
})();
|
|
253
|
+
}
|
|
27
254
|
function createLoopRun(db, input) {
|
|
28
255
|
db.prepare(`
|
|
29
256
|
INSERT INTO loop_runs (
|
|
@@ -116,6 +343,7 @@ function listPendingLoopTerminalRecoveries(db) {
|
|
|
116
343
|
SELECT run_id, payload, callback_completed, created_at
|
|
117
344
|
FROM loop_terminal_recovery
|
|
118
345
|
WHERE callback_completed = 0
|
|
346
|
+
AND NOT EXISTS (SELECT 1 FROM definition_fork_operations f WHERE f.source_run_id=loop_terminal_recovery.run_id AND f.adopted=1)
|
|
119
347
|
ORDER BY created_at, run_id
|
|
120
348
|
`).all();
|
|
121
349
|
}
|
|
@@ -253,7 +481,7 @@ function pauseLoopRun(db, id, counters) {
|
|
|
253
481
|
return getLoopRun(db, id);
|
|
254
482
|
}
|
|
255
483
|
function resumeLoopRun(db, id) {
|
|
256
|
-
db.prepare(`UPDATE loop_runs SET status = 'running' WHERE id = ? AND status = 'paused'`).run(id);
|
|
484
|
+
db.prepare(`UPDATE loop_runs SET status = 'running', restart_reason = NULL WHERE id = ? AND status = 'paused'`).run(id);
|
|
257
485
|
return getLoopRun(db, id);
|
|
258
486
|
}
|
|
259
487
|
function getLoopRun(db, id) {
|
|
@@ -300,17 +528,19 @@ function getRunEventCounts(db, runId) {
|
|
|
300
528
|
}
|
|
301
529
|
return { steps, lines };
|
|
302
530
|
}
|
|
531
|
+
// Adopted parents stay in history but their child owns execution and edit guards.
|
|
532
|
+
const ownsLoopExecution = 'NOT EXISTS (SELECT 1 FROM definition_fork_operations AS fork WHERE fork.source_run_id = loop_runs.id AND fork.adopted = 1)';
|
|
303
533
|
/** All currently-running loop runs for a project, straight from the DB (NOT the
|
|
304
534
|
* in-memory rail map, which is cleared on every server restart). Authoritative
|
|
305
535
|
* source for seeding the dashboard's live rail metrics after a refresh. */
|
|
306
536
|
function listRunningLoopRuns(db, projectId) {
|
|
307
537
|
return db
|
|
308
|
-
.prepare(
|
|
538
|
+
.prepare(`SELECT * FROM loop_runs WHERE project_id = ? AND status = 'running' AND ${ownsLoopExecution} ORDER BY started_at ASC`)
|
|
309
539
|
.all(projectId);
|
|
310
540
|
}
|
|
311
541
|
function listActiveLoopRuns(db, projectId) {
|
|
312
542
|
return db
|
|
313
|
-
.prepare(
|
|
543
|
+
.prepare(`SELECT * FROM loop_runs WHERE project_id = ? AND status IN ('running','paused') AND ${ownsLoopExecution} ORDER BY started_at ASC`)
|
|
314
544
|
.all(projectId);
|
|
315
545
|
}
|
|
316
546
|
function listLoopRuns(db, projectId, limit = 100) {
|
|
@@ -323,7 +553,7 @@ function listLoopRuns(db, projectId, limit = 100) {
|
|
|
323
553
|
* Running?" guard that blocks edit/delete of a loop while it executes. */
|
|
324
554
|
function countRunningForLoop(db, loopId) {
|
|
325
555
|
const row = db
|
|
326
|
-
.prepare(`SELECT COUNT(*) AS n FROM loop_runs WHERE loop_id = ? AND status IN ('running','paused')`)
|
|
556
|
+
.prepare(`SELECT COUNT(*) AS n FROM loop_runs WHERE loop_id = ? AND status IN ('running','paused') AND ${ownsLoopExecution}`)
|
|
327
557
|
.get(loopId);
|
|
328
558
|
return row.n;
|
|
329
559
|
}
|
|
@@ -334,12 +564,16 @@ function countRunningForLoop(db, loopId) {
|
|
|
334
564
|
* not sweep those rows: LoopRunManager is the sole invocation authority, so
|
|
335
565
|
* treating the accumulated step total as a second surface='job' invocation
|
|
336
566
|
* would double-count spend. A paused row is active too: its resumable context
|
|
337
|
-
* lived in the old process.
|
|
567
|
+
* lived in the old process. Execution claims of every active row are released
|
|
568
|
+
* in the same transaction. Returns the number of loop rows reconciled. */
|
|
338
569
|
function reconcileOrphanLoopRuns(db, finishedAt,
|
|
339
570
|
/** Compatibility only for pre-migration rows whose exact launch ticket set
|
|
340
571
|
* was never persisted. New rows always use ticket_ids_json. */
|
|
341
|
-
legacyTicketIdsByRun) {
|
|
572
|
+
legacyTicketIdsByRun, definitionStates) {
|
|
342
573
|
const reconcile = db.transaction(() => {
|
|
574
|
+
// Admission can crash before allocating a run row, or after its terminal
|
|
575
|
+
// commit but before releasing the reservation. Neither has a live executor.
|
|
576
|
+
db.prepare("DELETE FROM definition_execution_claims WHERE run_id NOT IN (SELECT id FROM loop_runs WHERE status IN ('running','paused'))").run();
|
|
343
577
|
const active = db.prepare(`
|
|
344
578
|
SELECT * FROM loop_runs WHERE status IN ('running', 'paused')
|
|
345
579
|
`).all();
|
|
@@ -366,7 +600,37 @@ legacyTicketIdsByRun) {
|
|
|
366
600
|
VALUES (?, ?, 0)
|
|
367
601
|
ON CONFLICT(run_id) DO NOTHING
|
|
368
602
|
`);
|
|
603
|
+
// No process survived the restart: every execution claim of an active row is
|
|
604
|
+
// stale, whether the row is retained as paused or settled as failed.
|
|
605
|
+
const releaseClaim = db.prepare('DELETE FROM definition_execution_claims WHERE run_id = ?');
|
|
606
|
+
let reconciled = 0;
|
|
369
607
|
for (const run of active) {
|
|
608
|
+
// Adoption already transferred execution/delivery ownership to the child.
|
|
609
|
+
// The parent's paused row is immutable history, not another orphan launch.
|
|
610
|
+
if (readDefinitionSuccessor(db, run.id)) {
|
|
611
|
+
releaseClaim.run(run.id);
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
reconciled += 1;
|
|
615
|
+
const probe = definitionStates?.get(run.id);
|
|
616
|
+
const lease = probe?.lease;
|
|
617
|
+
if (!lease?.active && probe?.coreStatus !== 'unavailable')
|
|
618
|
+
releaseClaim.run(run.id);
|
|
619
|
+
else if (!readDefinitionExecutionClaim(db, run.id)) {
|
|
620
|
+
const request = readDefinitionRun(db, run.id)?.request;
|
|
621
|
+
if (request) {
|
|
622
|
+
const claim = claimDefinitionExecution(db, run.id, { owner: `retained:${run.id}`, repositoryMounts: definitionRepositoryMounts(request) });
|
|
623
|
+
if (!claim.ok)
|
|
624
|
+
throw new Error(`Cannot reserve retained Core worktree: ${claim.conflictingRunId}`);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
if (run.engine_version === 2 && run.run_request_json) {
|
|
628
|
+
const state = definitionStates?.get(run.id);
|
|
629
|
+
if (state !== null && (!state?.status || ['running', 'paused', 'interrupted'].includes(state.status))) {
|
|
630
|
+
markDefinitionRestart(db, run.id, state ?? { status: 'unavailable', reason: 'Original Core status could not be read' });
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
370
634
|
updateJob.run(finishedAt, run.id);
|
|
371
635
|
updateRun.run(finishedAt, run.id);
|
|
372
636
|
const persistedIds = parseTicketIdsJson(run.ticket_ids_json);
|
|
@@ -386,7 +650,7 @@ legacyTicketIdsByRun) {
|
|
|
386
650
|
};
|
|
387
651
|
insertIntent.run(run.id, JSON.stringify(payload));
|
|
388
652
|
}
|
|
389
|
-
return
|
|
653
|
+
return reconciled;
|
|
390
654
|
});
|
|
391
655
|
return reconcile();
|
|
392
656
|
}
|