specrails-desktop 2.21.0 → 2.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/client/dist/assets/{ActivityFeedPage-BPkf89Xk.js → ActivityFeedPage-roDyKGuv.js} +1 -1
- package/client/dist/assets/AgentBrowserCapture-BsXnWa4j.js +1 -0
- package/client/dist/assets/{AgentModeCodePane-DhVkOSdv.js → AgentModeCodePane-o97EJvbJ.js} +2 -2
- package/client/dist/assets/AgentModeJobsPane-CLtpPxPM.js +1 -0
- package/client/dist/assets/AgentsPage-eNSo_ZcC.js +86 -0
- package/client/dist/assets/AnalyticsPage-cAJ60_Ex.js +1 -0
- package/client/dist/assets/{BarChart-9_p1-qtv.js → BarChart-NLtK7xAY.js} +1 -1
- package/client/dist/assets/CodePage-B5rum8ZB.js +2 -0
- package/client/dist/assets/DesktopAnalyticsPage-DSuvG6Tq.js +1 -0
- package/client/dist/assets/DocsDialog-Bo38G3Yc.js +11 -0
- package/client/dist/assets/DocsPage-BNkiBBrC.js +11 -0
- package/client/dist/assets/{ExportDropdown-Clt8DyCs.js → ExportDropdown-DCV8PN4-.js} +1 -1
- package/client/dist/assets/IntegrationsPage-BMJOzpPz.js +3 -0
- package/client/dist/assets/InteractiveJobComposer-aLduNs5R.js +19 -0
- package/client/dist/assets/JobDetailModal-kpkUWJIS.js +1 -0
- package/client/dist/assets/JobDetailPage-CFeqlrPU.js +1 -0
- package/client/dist/assets/JobsPage-fuwhXy2I.js +1 -0
- package/client/dist/assets/LoopBuilderPage-DP66322C.js +7 -0
- package/client/dist/assets/LoopPreviewModal-CpmBhE31.js +1 -0
- package/client/dist/assets/LoopsPage-CvQ18VUx.js +1 -0
- package/client/dist/assets/ProjectSettingsDialog-Deq4pwvq.js +1 -0
- package/client/dist/assets/TemplatePreviewModal-DziOj2Ct.js +1 -0
- package/client/dist/assets/agent-Csh4kP2S.js +1 -0
- package/client/dist/assets/agent-Cz_jgQX32.js +1 -0
- package/client/dist/assets/agent-D3vcIvaz2.js +1 -0
- package/client/dist/assets/agent-Dnbt0V4e.js +1 -0
- package/client/dist/assets/agent-HolR7QrZ.js +1 -0
- package/client/dist/assets/agent-Ka0rRWJy.js +1 -0
- package/client/dist/assets/agent-Te3yysMd.js +1 -0
- package/client/dist/assets/agent-vM4_iLDk2.js +1 -0
- package/client/dist/assets/{upload-DPRq2dC-.js → brain-kFJuEDNH.js} +1 -1
- package/client/dist/assets/{browser-DEEO-OSi.js → browser-BPkhILSJ.js} +1 -1
- package/client/dist/assets/{browser-BTivfZKE2.js → browser-BijVYG0Y2.js} +1 -1
- package/client/dist/assets/{browser-bpO3onlS.js → browser-BqLqJVVT.js} +1 -1
- package/client/dist/assets/{browser-DlSXtfu5.js → browser-Cc0iDqUi.js} +1 -1
- package/client/dist/assets/browser-CdS6O1Qb.js +1 -0
- package/client/dist/assets/{browser-BKP6hqQC2.js → browser-Cvg-QHvd2.js} +1 -1
- package/client/dist/assets/{browser-INCUyduh.js → browser-FRv8P02E.js} +1 -1
- package/client/dist/assets/{browser-DlKLfT612.js → browser-rWKj-RR_2.js} +1 -1
- package/client/dist/assets/code-BRcDlze52.js +1 -0
- package/client/dist/assets/code-BdMy3Ffq.js +1 -0
- package/client/dist/assets/code-CeQ0FZpC.js +1 -0
- package/client/dist/assets/code-CqCcRThR.js +1 -0
- package/client/dist/assets/code-DgeHz9CB.js +1 -0
- package/client/dist/assets/code-DzC1rMk12.js +1 -0
- package/client/dist/assets/code-UPVvmAKM2.js +1 -0
- package/client/dist/assets/code-thSEptMx.js +1 -0
- package/client/dist/assets/{common-QjVnq9XP2.js → common-BM3LFLCE2.js} +1 -1
- package/client/dist/assets/{common-CeQUwuHt2.js → common-Bwafujnc2.js} +1 -1
- package/client/dist/assets/{common-Bb64s3Bm.js → common-C-vU-j-I.js} +1 -1
- package/client/dist/assets/{common-KbQuTSGg.js → common-C5HKSbbz.js} +1 -1
- package/client/dist/assets/{common-BPQQkgJZ.js → common-CYaT2yHp.js} +1 -1
- package/client/dist/assets/{common-DPxotRK9.js → common-DhWTkocg.js} +1 -1
- package/client/dist/assets/{common-k5MTOHpn.js → common-DiPTT7V_2.js} +1 -1
- package/client/dist/assets/{common-C8ZD5upH2.js → common-DmqBPwZG.js} +1 -1
- package/client/dist/assets/{cssMode-DsArsSCT.js → cssMode-DBHbkBvI.js} +1 -1
- package/client/dist/assets/dashboard-BaStj_a_.js +1 -0
- package/client/dist/assets/dashboard-Bi6ugu5Q.js +1 -0
- package/client/dist/assets/dashboard-BmqTn3L02.js +1 -0
- package/client/dist/assets/dashboard-CvT8lOCT.js +1 -0
- package/client/dist/assets/dashboard-D7Pmeavq2.js +1 -0
- package/client/dist/assets/dashboard-DAnWdAac2.js +1 -0
- package/client/dist/assets/dashboard-JnYy2WLg.js +1 -0
- package/client/dist/assets/dashboard-L47YsFS_.js +1 -0
- package/client/dist/assets/{dist-js-4mmVovCB.js → dist-js-BZSiJDbG.js} +1 -1
- package/client/dist/assets/{dist-js-wMFtEMAw.js → dist-js-DKpmIJlQ.js} +1 -1
- package/client/dist/assets/{editor.main-G3Sl9C67.js → editor.main-C9J2ikJK.js} +2 -2
- package/client/dist/assets/{freemarker2-pBzQNgFi.js → freemarker2-J5iIIIK-.js} +1 -1
- package/client/dist/assets/{handlebars-CtBTCnBI.js → handlebars-DSw1l_tw.js} +1 -1
- package/client/dist/assets/{html-Dx3DYBzY.js → html-h2-wE1Lj.js} +1 -1
- package/client/dist/assets/{htmlMode-In7frFOo.js → htmlMode-Cc_ij7lW.js} +1 -1
- package/client/dist/assets/index-DM6heiGa.js +155 -0
- package/client/dist/assets/index-DPX6_xIC.css +2 -0
- package/client/dist/assets/{javascript-BKcn-YkN.js → javascript-C1q3xv6N.js} +1 -1
- package/client/dist/assets/jira-B3613-gj.js +1 -0
- package/client/dist/assets/jira-BHAKtc4B.js +1 -0
- package/client/dist/assets/jira-Be369RIR2.js +1 -0
- package/client/dist/assets/jira-CPpgA-5z2.js +1 -0
- package/client/dist/assets/jira-D5efgwp_.js +1 -0
- package/client/dist/assets/jira-DS7ownDy.js +1 -0
- package/client/dist/assets/jira-bDs7Im2C.js +1 -0
- package/client/dist/assets/jira-uNH1gETL2.js +1 -0
- package/client/dist/assets/jobs-1GV2-4zd2.js +1 -0
- package/client/dist/assets/jobs-26F9gGcE.js +1 -0
- package/client/dist/assets/jobs-BgQzB0Sx2.js +1 -0
- package/client/dist/assets/jobs-BqWui9_f.js +1 -0
- package/client/dist/assets/jobs-CE8qeIYj.js +1 -0
- package/client/dist/assets/jobs-D5sZzgLs.js +1 -0
- package/client/dist/assets/jobs-DzeuASRf.js +1 -0
- package/client/dist/assets/jobs-Wh7dn28q2.js +1 -0
- package/client/dist/assets/{jsonMode-jNREoQ3C.js → jsonMode-CeaCdEhr.js} +1 -1
- package/client/dist/assets/{lib-Cz65p1sn.js → lib-B2nCKzmA.js} +1 -1
- package/client/dist/assets/{liquid-CtRkGqBu.js → liquid-eFsVK0V-.js} +1 -1
- package/client/dist/assets/loops-BvEenWBy.js +1 -0
- package/client/dist/assets/loops-CBiWd9Hf.js +1 -0
- package/client/dist/assets/loops-DNGiYKll2.js +1 -0
- package/client/dist/assets/{loops-Y3FQsUCh.js → loops-DjOtVXRo.js} +1 -1
- package/client/dist/assets/loops-HTHf-xu32.js +1 -0
- package/client/dist/assets/loops-YYV2bwjd.js +1 -0
- package/client/dist/assets/loops-hXgncEpF2.js +1 -0
- package/client/dist/assets/loops-kYbydDyN.js +1 -0
- package/client/dist/assets/{lspLanguageFeatures-Cl3gvD_W.js → lspLanguageFeatures-DT3-bbhn.js} +1 -1
- package/client/dist/assets/{mdx-B_nJldHz.js → mdx-Fuwnh1cX.js} +1 -1
- package/client/dist/assets/{monaco.contribution-PUeaZk8T.js → monaco.contribution-Desq7oq4.js} +2 -2
- package/client/dist/assets/{python-DS1Ce-A6.js → python-BgoEs6Cj.js} +1 -1
- package/client/dist/assets/{razor-L41Qg9OK.js → razor-CM1sPMMq.js} +1 -1
- package/client/dist/assets/setup-B590KsTX.js +1 -0
- package/client/dist/assets/setup-BkyGoby6.js +1 -0
- package/client/dist/assets/setup-CEj6uY3-2.js +1 -0
- package/client/dist/assets/setup-CbIerFGf2.js +1 -0
- package/client/dist/assets/setup-CmU6bTCV2.js +1 -0
- package/client/dist/assets/setup-D674kWQH.js +1 -0
- package/client/dist/assets/setup-VNqXyB6Y.js +1 -0
- package/client/dist/assets/setup-iY8vH8pB.js +1 -0
- package/client/dist/assets/specs-C9Sd7BhH2.js +1 -0
- package/client/dist/assets/specs-CBDvEYKv.js +1 -0
- package/client/dist/assets/specs-CIPr9reA2.js +1 -0
- package/client/dist/assets/specs-CWtKcVDC.js +1 -0
- package/client/dist/assets/specs-CmEZY57v.js +1 -0
- package/client/dist/assets/specs-D2clrt2c2.js +1 -0
- package/client/dist/assets/specs-DTplt2XM.js +1 -0
- package/client/dist/assets/specs-PozUNhqb.js +1 -0
- package/client/dist/assets/{tsMode-BC1g186U.js → tsMode-BmT8WogI.js} +1 -1
- package/client/dist/assets/{typescript-BGlRGvJW.js → typescript-CQrQPsoW.js} +1 -1
- package/client/dist/assets/upload-CLAzW1mS.js +1 -0
- package/client/dist/assets/{value-DJMRojPA.js → value-DSwzyyWR.js} +1 -1
- package/client/dist/assets/{workers-CXcXtO3b.js → workers-U0UXyLUf.js} +1 -1
- package/client/dist/assets/{xml-JdZZaIhJ.js → xml-WRxJs8_I.js} +1 -1
- package/client/dist/assets/{yaml-Cm82BVBt.js → yaml-CjpSTjJU.js} +1 -1
- package/client/dist/index.html +13 -13
- package/docs/customizing.md +2 -2
- package/docs/gemini.md +15 -4
- package/docs/guide/de/insights/3-code-explorer.md +6 -0
- package/docs/guide/de/integrations/3-jira-integration.md +1 -1
- package/docs/guide/de/integrations/6-agent-chat.md +5 -0
- package/docs/guide/de/pipeline/1-rails-and-jobs.md +6 -6
- package/docs/guide/de/pipeline/2-the-job-detail-view.md +27 -2
- package/docs/guide/de/pipeline/3-batch-implement-and-multi-feature.md +5 -4
- package/docs/guide/de/pipeline/5-the-loop-builder.md +1 -1
- package/docs/guide/de/specs/1-specs-and-the-backlog.md +3 -2
- package/docs/guide/en/insights/3-code-explorer.md +6 -0
- package/docs/guide/en/integrations/3-jira-integration.md +1 -1
- package/docs/guide/en/integrations/6-agent-chat.md +5 -0
- package/docs/guide/en/pipeline/1-rails-and-jobs.md +6 -6
- package/docs/guide/en/pipeline/2-the-job-detail-view.md +27 -2
- package/docs/guide/en/pipeline/3-batch-implement-and-multi-feature.md +5 -4
- package/docs/guide/en/pipeline/5-the-loop-builder.md +1 -1
- package/docs/guide/en/specs/1-specs-and-the-backlog.md +3 -2
- package/docs/guide/es/insights/3-code-explorer.md +6 -0
- package/docs/guide/es/integrations/3-jira-integration.md +1 -1
- package/docs/guide/es/integrations/6-agent-chat.md +5 -0
- package/docs/guide/es/pipeline/1-rails-and-jobs.md +6 -6
- package/docs/guide/es/pipeline/2-the-job-detail-view.md +27 -2
- package/docs/guide/es/pipeline/3-batch-implement-and-multi-feature.md +5 -4
- package/docs/guide/es/pipeline/5-the-loop-builder.md +1 -1
- package/docs/guide/es/specs/1-specs-and-the-backlog.md +3 -2
- package/docs/guide/fr/insights/3-code-explorer.md +6 -0
- package/docs/guide/fr/integrations/3-jira-integration.md +1 -1
- package/docs/guide/fr/integrations/6-agent-chat.md +5 -0
- package/docs/guide/fr/pipeline/1-rails-and-jobs.md +6 -6
- package/docs/guide/fr/pipeline/2-the-job-detail-view.md +27 -2
- package/docs/guide/fr/pipeline/3-batch-implement-and-multi-feature.md +5 -4
- package/docs/guide/fr/pipeline/5-the-loop-builder.md +1 -1
- package/docs/guide/fr/specs/1-specs-and-the-backlog.md +3 -2
- package/docs/guide/it/insights/3-code-explorer.md +6 -0
- package/docs/guide/it/integrations/3-jira-integration.md +1 -1
- package/docs/guide/it/integrations/6-agent-chat.md +5 -0
- package/docs/guide/it/pipeline/1-rails-and-jobs.md +6 -6
- package/docs/guide/it/pipeline/2-the-job-detail-view.md +27 -2
- package/docs/guide/it/pipeline/3-batch-implement-and-multi-feature.md +5 -4
- package/docs/guide/it/pipeline/5-the-loop-builder.md +1 -1
- package/docs/guide/it/specs/1-specs-and-the-backlog.md +3 -2
- package/docs/guide/ja/insights/3-code-explorer.md +6 -0
- package/docs/guide/ja/integrations/3-jira-integration.md +1 -1
- package/docs/guide/ja/integrations/6-agent-chat.md +5 -0
- package/docs/guide/ja/pipeline/1-rails-and-jobs.md +6 -6
- package/docs/guide/ja/pipeline/2-the-job-detail-view.md +27 -2
- package/docs/guide/ja/pipeline/3-batch-implement-and-multi-feature.md +5 -4
- package/docs/guide/ja/pipeline/5-the-loop-builder.md +1 -1
- package/docs/guide/ja/specs/1-specs-and-the-backlog.md +3 -2
- package/docs/guide/pt/insights/3-code-explorer.md +6 -0
- package/docs/guide/pt/integrations/3-jira-integration.md +1 -1
- package/docs/guide/pt/integrations/6-agent-chat.md +5 -0
- package/docs/guide/pt/pipeline/1-rails-and-jobs.md +6 -6
- package/docs/guide/pt/pipeline/2-the-job-detail-view.md +27 -2
- package/docs/guide/pt/pipeline/3-batch-implement-and-multi-feature.md +5 -4
- package/docs/guide/pt/pipeline/5-the-loop-builder.md +1 -1
- package/docs/guide/pt/specs/1-specs-and-the-backlog.md +3 -2
- package/docs/guide/zh/insights/3-code-explorer.md +6 -0
- package/docs/guide/zh/integrations/3-jira-integration.md +1 -1
- package/docs/guide/zh/integrations/6-agent-chat.md +5 -0
- package/docs/guide/zh/pipeline/1-rails-and-jobs.md +6 -6
- package/docs/guide/zh/pipeline/2-the-job-detail-view.md +27 -2
- package/docs/guide/zh/pipeline/3-batch-implement-and-multi-feature.md +5 -4
- package/docs/guide/zh/pipeline/5-the-loop-builder.md +1 -1
- package/docs/guide/zh/specs/1-specs-and-the-backlog.md +3 -2
- package/docs/internals/README.md +5 -0
- package/docs/internals/adding-a-provider.md +15 -3
- package/docs/internals/api-reference.md +6 -5
- package/docs/internals/browser-capture-performance.md +93 -0
- package/docs/internals/code-explorer-story.md +135 -0
- package/docs/internals/gemini-mcp-registration.md +53 -0
- package/docs/internals/interactive-jobs.md +212 -0
- package/docs/internals/loop-step-log-explorer.md +146 -0
- package/docs/internals/safe-pr-review-flow.md +367 -0
- package/docs/mcp.md +1 -1
- package/docs/running-pipelines.md +5 -3
- package/package.json +1 -1
- package/server/dist/agent-chat-manager.js +211 -0
- package/server/dist/agent-chat-registry.js +20 -0
- package/server/dist/agent-chat-router.js +22 -0
- package/server/dist/agent-mcp-config.js +45 -4
- package/server/dist/agent-operator-prompt.js +149 -56
- package/server/dist/agent-store.js +25 -0
- package/server/dist/agent-tier.js +6 -1
- package/server/dist/browser-capture-manager.js +184 -34
- package/server/dist/browser-playwright.js +98 -13
- package/server/dist/code-explorer-router.js +92 -0
- package/server/dist/db.js +92 -0
- package/server/dist/file-provenance.js +54 -1
- package/server/dist/file-story-manager.js +170 -0
- package/server/dist/file-story.js +148 -0
- package/server/dist/file-summary-generator.js +5 -4
- package/server/dist/index.js +5 -0
- package/server/dist/interactive-job-session.js +226 -18
- package/server/dist/jira/jira-adf.js +9 -0
- package/server/dist/jira/jira-materializer.js +27 -4
- package/server/dist/jira/jira-status-resolver.js +12 -4
- package/server/dist/jira/jira-sync-manager.js +130 -2
- package/server/dist/jira-router.js +1 -1
- package/server/dist/loop-executors.js +91 -28
- package/server/dist/loop-run-manager.js +354 -28
- package/server/dist/mcp/guide.js +18 -7
- package/server/dist/mcp/tools/jira.js +1 -1
- package/server/dist/mcp/tools/jobs.js +14 -5
- package/server/dist/mcp/tools/rails.js +137 -12
- package/server/dist/mcp/tools/specs.js +35 -9
- package/server/dist/mcp/tools/types.js +49 -0
- package/server/dist/mobile/mobile-ws.js +4 -0
- package/server/dist/path-resolver.js +46 -0
- package/server/dist/pr-body.js +240 -0
- package/server/dist/pr-naming.js +210 -0
- package/server/dist/project-registry.js +77 -17
- package/server/dist/project-router-jobs.js +53 -13
- package/server/dist/project-router-terminals.js +16 -0
- package/server/dist/project-router-tickets.js +57 -3
- package/server/dist/project-router.js +52 -13
- package/server/dist/queue-manager.js +261 -84
- package/server/dist/rail-isolated-launch.js +315 -66
- package/server/dist/rail-isolation.js +18 -5
- package/server/dist/rail-pr-decision.js +519 -0
- package/server/dist/rail-pr-delivery.js +21 -18
- package/server/dist/rail-pr-store.js +175 -0
- package/server/dist/rail-worktrees-store.js +18 -0
- package/server/dist/rails-router.js +224 -37
- package/server/dist/rails-store.js +78 -1
- package/server/dist/setup-prerequisites.js +105 -0
- package/server/dist/smash-runner.js +3 -1
- package/server/dist/ticket-store.js +7 -3
- package/server/dist/workspace-resolution.js +27 -0
- package/server/dist/worktree-manager.js +42 -7
- package/server/dist/worktree-overlay.js +261 -0
- package/client/dist/assets/AgentBrowserCapture-DkA8FLp8.js +0 -1
- package/client/dist/assets/AgentModeJobsPane-TLYCqNAw.js +0 -1
- package/client/dist/assets/AgentsPage-BKsPH-l1.js +0 -86
- package/client/dist/assets/AnalyticsPage-Da9pDQrq.js +0 -1
- package/client/dist/assets/CodePage-DlwvWKCo.js +0 -2
- package/client/dist/assets/DesktopAnalyticsPage-DpVVhXKF.js +0 -1
- package/client/dist/assets/DocsDialog-BSdgftgR.js +0 -11
- package/client/dist/assets/DocsPage-9SGL4_zz.js +0 -11
- package/client/dist/assets/IntegrationsPage-BW5mofSA.js +0 -3
- package/client/dist/assets/JobDetailPage-CqiJ9vwR.js +0 -1
- package/client/dist/assets/JobsPage-CVinOe6p.js +0 -1
- package/client/dist/assets/LogViewer-Cf6s1TDs.js +0 -16
- package/client/dist/assets/LoopBuilderPage-CqgUH5Vw.js +0 -7
- package/client/dist/assets/LoopsPage-C6h2gjPP.js +0 -1
- package/client/dist/assets/ProjectSettingsDialog-BU-JR7wI.js +0 -1
- package/client/dist/assets/agent-B7GdHP3Z.js +0 -1
- package/client/dist/assets/agent-BHmcgoJf.js +0 -1
- package/client/dist/assets/agent-BdY_fii4.js +0 -1
- package/client/dist/assets/agent-C32FOi9v.js +0 -1
- package/client/dist/assets/agent-CiFQxoaZ2.js +0 -1
- package/client/dist/assets/agent-CtAag7V6.js +0 -1
- package/client/dist/assets/agent-DE4Mp1fY2.js +0 -1
- package/client/dist/assets/agent-DK8qovyG2.js +0 -1
- package/client/dist/assets/browser-ZB1afzjZ.js +0 -1
- package/client/dist/assets/code-BFxE1Yt62.js +0 -1
- package/client/dist/assets/code-BefYOm5Q.js +0 -1
- package/client/dist/assets/code-BkXpJt99.js +0 -1
- package/client/dist/assets/code-C6Sq7_t8.js +0 -1
- package/client/dist/assets/code-CGj3w-2N.js +0 -1
- package/client/dist/assets/code-Dfsxc9o82.js +0 -1
- package/client/dist/assets/code-Y5urks5s2.js +0 -1
- package/client/dist/assets/code-wtcs0ivb.js +0 -1
- package/client/dist/assets/dashboard-B-AiVq5X.js +0 -1
- package/client/dist/assets/dashboard-BpYYrPl8.js +0 -1
- package/client/dist/assets/dashboard-Bunv-uon2.js +0 -1
- package/client/dist/assets/dashboard-CYciEbov.js +0 -1
- package/client/dist/assets/dashboard-CvFg-_JQ.js +0 -1
- package/client/dist/assets/dashboard-DAyPERCJ.js +0 -1
- package/client/dist/assets/dashboard-DJ-6T44N2.js +0 -1
- package/client/dist/assets/dashboard-DnBGqZkZ2.js +0 -1
- package/client/dist/assets/index-BHPbzyg6.css +0 -2
- package/client/dist/assets/index-D4R3rVF5.js +0 -152
- package/client/dist/assets/jira-BcZzX9122.js +0 -1
- package/client/dist/assets/jira-CTYs7LA12.js +0 -1
- package/client/dist/assets/jira-D2nN39e92.js +0 -1
- package/client/dist/assets/jira-DJxmIkyr.js +0 -1
- package/client/dist/assets/jira-DXMoAKWD.js +0 -1
- package/client/dist/assets/jira-IhC1ROIc.js +0 -1
- package/client/dist/assets/jira-Iw575fN8.js +0 -1
- package/client/dist/assets/jira-kbWZY7jH.js +0 -1
- package/client/dist/assets/jobs-BqF79kHz2.js +0 -1
- package/client/dist/assets/jobs-C1XM5urS.js +0 -1
- package/client/dist/assets/jobs-C5UjG0262.js +0 -1
- package/client/dist/assets/jobs-DBfaHTxC.js +0 -1
- package/client/dist/assets/jobs-DM4BDPdg.js +0 -1
- package/client/dist/assets/jobs-Da5OkZmf2.js +0 -1
- package/client/dist/assets/jobs-JJdnuQ03.js +0 -1
- package/client/dist/assets/jobs-SI34ML8I.js +0 -1
- package/client/dist/assets/loops-BR4FmOv12.js +0 -1
- package/client/dist/assets/loops-BgX_uudn.js +0 -1
- package/client/dist/assets/loops-Bpl2j2LP.js +0 -1
- package/client/dist/assets/loops-CQuuMlyZ.js +0 -1
- package/client/dist/assets/loops-DW8hSMdS2.js +0 -1
- package/client/dist/assets/loops-Q2muu8hW2.js +0 -1
- package/client/dist/assets/loops-q7CNDvfK.js +0 -1
- package/client/dist/assets/setup-CNMdCHHt2.js +0 -1
- package/client/dist/assets/setup-Cag4XrbL2.js +0 -1
- package/client/dist/assets/setup-D9IHGoWU.js +0 -1
- package/client/dist/assets/setup-Dg6Ba3JT.js +0 -1
- package/client/dist/assets/setup-DyK36RIW.js +0 -1
- package/client/dist/assets/setup-G4ZvDKxX.js +0 -1
- package/client/dist/assets/setup-aTlGxuwi2.js +0 -1
- package/client/dist/assets/setup-kWsoAeHQ.js +0 -1
- package/client/dist/assets/specs-BFSUPa122.js +0 -1
- package/client/dist/assets/specs-BMzip1WY2.js +0 -1
- package/client/dist/assets/specs-BYeAbIEy.js +0 -1
- package/client/dist/assets/specs-C-mtLcXw.js +0 -1
- package/client/dist/assets/specs-CAzpvADd2.js +0 -1
- package/client/dist/assets/specs-CyRIw_IR.js +0 -1
- package/client/dist/assets/specs-DMm7gB_l.js +0 -1
- package/client/dist/assets/specs-DuottM0Z.js +0 -1
- /package/client/dist/assets/{abap-CpHUwCVK.js → abap-D0E4FfVW.js} +0 -0
- /package/client/dist/assets/{activity-DemHQS7l.js → activity--iN4Ad2R2.js} +0 -0
- /package/client/dist/assets/{activity-B_71BWg3.js → activity-6hM_ZSg9.js} +0 -0
- /package/client/dist/assets/{activity-DHv2V5Oi.js → activity-BZNfOdiS.js} +0 -0
- /package/client/dist/assets/{activity-D0rfjQzT2.js → activity-BuVo5sPB.js} +0 -0
- /package/client/dist/assets/{activity-BpTTj_PH.js → activity-CIOLxGvh.js} +0 -0
- /package/client/dist/assets/{activity-D2e62vbV2.js → activity-D2r2h3SE2.js} +0 -0
- /package/client/dist/assets/{activity-Ch-wbr0C2.js → activity-DtxrAUMo2.js} +0 -0
- /package/client/dist/assets/{addon-image-Ba-TY3wr.js → addon-image-CwoZj17F.js} +0 -0
- /package/client/dist/assets/{addon-ligatures-T4ARLiuu.js → addon-ligatures-CWDrED80.js} +0 -0
- /package/client/dist/assets/{addon-webgl-Cd73n8eh.js → addon-webgl-DMbGeI_u.js} +0 -0
- /package/client/dist/assets/{addspec-xwRX_Pnf.js → addspec-BMtLZLW62.js} +0 -0
- /package/client/dist/assets/{addspec-l_zU5gla.js → addspec-CP0HRuxY.js} +0 -0
- /package/client/dist/assets/{addspec-Dio-H5z0.js → addspec-CRuvWn8d.js} +0 -0
- /package/client/dist/assets/{addspec-D6L4ZteP.js → addspec-D-JLDsUV.js} +0 -0
- /package/client/dist/assets/{addspec-CrEm6Fzr2.js → addspec-DfhDbg5c.js} +0 -0
- /package/client/dist/assets/{addspec-B6Ns8KU02.js → addspec-bsNDdqRm2.js} +0 -0
- /package/client/dist/assets/{addspec-qGyH7MHy2.js → addspec-mgPOt5q02.js} +0 -0
- /package/client/dist/assets/{agents-OzxCLVW1.js → agents-78bTZGZh.js} +0 -0
- /package/client/dist/assets/{agents-hv3sqmPm2.js → agents-BLhWCmDS2.js} +0 -0
- /package/client/dist/assets/{agents-CHgCudUV.js → agents-BUdEi3-N.js} +0 -0
- /package/client/dist/assets/{agents-Dumx4JiM2.js → agents-BpoUaoW92.js} +0 -0
- /package/client/dist/assets/{agents-CnaPeSyw.js → agents-CWo-KyAB.js} +0 -0
- /package/client/dist/assets/{agents-Xh-zYHUx2.js → agents-DDtCJ5M02.js} +0 -0
- /package/client/dist/assets/{agents-DuhLEaQ_.js → agents-bvyRLczL.js} +0 -0
- /package/client/dist/assets/{agentstudio-CCthUdzR.js → agentstudio-BMQxc_bz.js} +0 -0
- /package/client/dist/assets/{agentstudio-CcH8Ckql2.js → agentstudio-COZriimI2.js} +0 -0
- /package/client/dist/assets/{agentstudio-Bo91kfUd2.js → agentstudio-CWgXG5W52.js} +0 -0
- /package/client/dist/assets/{agentstudio-VJOa_NVO.js → agentstudio-C_Jq8Tl4.js} +0 -0
- /package/client/dist/assets/{agentstudio-BIxikDf_.js → agentstudio-Dtq3dGYk.js} +0 -0
- /package/client/dist/assets/{agentstudio-Bz8cP8_c.js → agentstudio-jKMTdT70.js} +0 -0
- /package/client/dist/assets/{agentstudio-DARgYg5Z2.js → agentstudio-sI27TpBu2.js} +0 -0
- /package/client/dist/assets/{aiedit-5ZXN4cep2.js → aiedit-6CHdb_iv2.js} +0 -0
- /package/client/dist/assets/{aiedit-B7gAJZVC.js → aiedit-B03pD_Na.js} +0 -0
- /package/client/dist/assets/{aiedit-D92V-kUW.js → aiedit-BtRqRxML.js} +0 -0
- /package/client/dist/assets/{aiedit-D9ualLVJ.js → aiedit-eMN4eQPF.js} +0 -0
- /package/client/dist/assets/{aiedit-h48tJU0V2.js → aiedit-hknZlly82.js} +0 -0
- /package/client/dist/assets/{aiedit-xkA_mR2a2.js → aiedit-igmw-ZoZ2.js} +0 -0
- /package/client/dist/assets/{aiedit-7B2dvCv-.js → aiedit-pc5JDUq1.js} +0 -0
- /package/client/dist/assets/{analytics-slx4X1mu2.js → analytics-BWMUZ2Pq2.js} +0 -0
- /package/client/dist/assets/{analytics-uRNP6yAJ.js → analytics-Bj-SmKHa.js} +0 -0
- /package/client/dist/assets/{analytics-WF1I49ln.js → analytics-C5dpiS0o.js} +0 -0
- /package/client/dist/assets/{analytics-CBvaxOId.js → analytics-CCprAqk3.js} +0 -0
- /package/client/dist/assets/{analytics-BakymH7Z2.js → analytics-CR1TJqUi2.js} +0 -0
- /package/client/dist/assets/{analytics-B7bPj6u_2.js → analytics-DfNdXR0y2.js} +0 -0
- /package/client/dist/assets/{analytics-DZ74aQ54.js → analytics-rmHuJv0V.js} +0 -0
- /package/client/dist/assets/{apex-DlD7oJX6.js → apex-D_X_8J4d.js} +0 -0
- /package/client/dist/assets/{atom-one-dark-BrRN8wBy.js → atom-one-dark-BMDPFwRZ.js} +0 -0
- /package/client/dist/assets/{attachments-C11tOKZ_.js → attachments-BgBNSqQd.js} +0 -0
- /package/client/dist/assets/{attachments-Bnbyj2PN.js → attachments-BjyMmrwE.js} +0 -0
- /package/client/dist/assets/{attachments-CIeLa8l12.js → attachments-C9GTTJsx2.js} +0 -0
- /package/client/dist/assets/{attachments-DslHfa-L.js → attachments-DCZOL1RU.js} +0 -0
- /package/client/dist/assets/{attachments-C-gyrCOb.js → attachments-DL-bPy9E.js} +0 -0
- /package/client/dist/assets/{attachments-Cxrs0pyL2.js → attachments-DPCmkFZu2.js} +0 -0
- /package/client/dist/assets/{attachments-TTPsc0a52.js → attachments-rNn1ekRy2.js} +0 -0
- /package/client/dist/assets/{azcli-CmXFAbc-.js → azcli-PZLnZ3jH.js} +0 -0
- /package/client/dist/assets/{bat-DdNJq4hH.js → bat-KlBOxpO4.js} +0 -0
- /package/client/dist/assets/{bicep-Bppz7WOA.js → bicep-CgG9oF3V.js} +0 -0
- /package/client/dist/assets/{cameligo-0MRMdSmg.js → cameligo-6R45j6Ph.js} +0 -0
- /package/client/dist/assets/{chat-BvHrebKC2.js → chat-BgL3zv6x2.js} +0 -0
- /package/client/dist/assets/{chat-CZyiLJvQ.js → chat-Bh3NdgJs.js} +0 -0
- /package/client/dist/assets/{chat-DKYP7NnC.js → chat-Bom6bqK3.js} +0 -0
- /package/client/dist/assets/{chat-5mdg_YIc2.js → chat-DdwZjT8K2.js} +0 -0
- /package/client/dist/assets/{chat-wkTIy9rX.js → chat-Dnwg-NpQ.js} +0 -0
- /package/client/dist/assets/{chat-BpYxnkr4.js → chat-DsbwJhus.js} +0 -0
- /package/client/dist/assets/{chat-BGax2nV62.js → chat-vylxqR0P2.js} +0 -0
- /package/client/dist/assets/{clojure-B5dKj863.js → clojure-CLAAMO7J.js} +0 -0
- /package/client/dist/assets/{clsx-1dnHRn8W.js → clsx-CNmW0Nip.js} +0 -0
- /package/client/dist/assets/{coffee-DSbWjzk7.js → coffee-AruSygo-.js} +0 -0
- /package/client/dist/assets/{commands-uz_uKT242.js → commands-C4Y3C-PF2.js} +0 -0
- /package/client/dist/assets/{commands-Bb7MnPqn2.js → commands-CSDs9q_2.js} +0 -0
- /package/client/dist/assets/{commands-C_7zZwJf.js → commands-DkU0qliq.js} +0 -0
- /package/client/dist/assets/{commands-D_6B8lfU.js → commands-JWMfGDmr.js} +0 -0
- /package/client/dist/assets/{commands-DBc5llLi2.js → commands-RHOchN4n2.js} +0 -0
- /package/client/dist/assets/{commands-CQkdthjF.js → commands-u-4eEHCN.js} +0 -0
- /package/client/dist/assets/{commands-DhZNhSbl.js → commands-xHDDGfcP2.js} +0 -0
- /package/client/dist/assets/{cpp-SBF8SAh9.js → cpp-DEE5r4Pw.js} +0 -0
- /package/client/dist/assets/{csharp-Lzuq_1t-.js → csharp-BVXJUvbK.js} +0 -0
- /package/client/dist/assets/{csp-BEzxZqi1.js → csp-cXOF0JoV.js} +0 -0
- /package/client/dist/assets/{css-C3D3PKln.js → css-C_zQuAoy.js} +0 -0
- /package/client/dist/assets/{cypher-CQ3dEPmv.js → cypher-t95m_Q-O.js} +0 -0
- /package/client/dist/assets/{dart-DCPmxolI.js → dart-B4r1Tg-e.js} +0 -0
- /package/client/dist/assets/{dockerfile-B_2_aaTO.js → dockerfile-6mt7zHQw.js} +0 -0
- /package/client/dist/assets/{ecl-rADUeXsV.js → ecl-DEY_Fz_Q.js} +0 -0
- /package/client/dist/assets/{editor.api2-T7u8kQWG.js → editor.api2-C3B0tp2x.js} +0 -0
- /package/client/dist/assets/{elixir-DOd-TuJO.js → elixir-R6-q1c5t.js} +0 -0
- /package/client/dist/assets/{explore-BHEhNekP2.js → explore-B7fblXBb2.js} +0 -0
- /package/client/dist/assets/{explore-DAsroA482.js → explore-BHtEpoNN2.js} +0 -0
- /package/client/dist/assets/{explore-UwHHj2ux.js → explore-C0k8HrkN.js} +0 -0
- /package/client/dist/assets/{explore-DaXiW9-6.js → explore-CNBzrEPS.js} +0 -0
- /package/client/dist/assets/{explore-CWlAXx0f2.js → explore-D_mWBYCS2.js} +0 -0
- /package/client/dist/assets/{explore-DG1CtYqT.js → explore-Dahc1lOx.js} +0 -0
- /package/client/dist/assets/{explore-DMSGAnhK.js → explore-UvW1jEMa.js} +0 -0
- /package/client/dist/assets/{flow9-Bf7zg_j0.js → flow9-CNQvHBOt.js} +0 -0
- /package/client/dist/assets/{format-command-BHymDa4r.js → format-command-CuWuRvka.js} +0 -0
- /package/client/dist/assets/{fsharp-BqROZjbA.js → fsharp-vPZkFXwv.js} +0 -0
- /package/client/dist/assets/{go-oQTP5uLe.js → go-KUzBMYJ_.js} +0 -0
- /package/client/dist/assets/{graphql-CSHfTU3q.js → graphql-C3W0rCsD.js} +0 -0
- /package/client/dist/assets/{hcl-CeG-RoFJ.js → hcl-BOOMFSNX.js} +0 -0
- /package/client/dist/assets/{ini-B2Dv4ohc.js → ini-CFat1WJr.js} +0 -0
- /package/client/dist/assets/{integrations-CAF21FbQ.js → integrations-4KZnYECp.js} +0 -0
- /package/client/dist/assets/{integrations-CHCgning2.js → integrations-7W266oqF2.js} +0 -0
- /package/client/dist/assets/{integrations-B-JVE1_D.js → integrations-BNuauK3o.js} +0 -0
- /package/client/dist/assets/{integrations-BAujIDoE.js → integrations-CHy8tnpV.js} +0 -0
- /package/client/dist/assets/{integrations-DS5FFMes.js → integrations-Cl_wD5AI.js} +0 -0
- /package/client/dist/assets/{integrations-axZN4--n2.js → integrations-Cy8nZ_dv2.js} +0 -0
- /package/client/dist/assets/{integrations-CtsOpq2D2.js → integrations-zMqY4VU12.js} +0 -0
- /package/client/dist/assets/{java-aUDDeufb.js → java-Dee-CNAM.js} +0 -0
- /package/client/dist/assets/{julia-C5LbruO5.js → julia-CGNlLYuU.js} +0 -0
- /package/client/dist/assets/{kotlin-C6mOuSKk.js → kotlin-CY7lhknJ.js} +0 -0
- /package/client/dist/assets/{less-D5qC3caW.js → less-D0KN7hfJ.js} +0 -0
- /package/client/dist/assets/{lexon-Dr2La-fj.js → lexon-CuRJufPs.js} +0 -0
- /package/client/dist/assets/{lua-Zy6u5z6Z.js → lua-DHvH62Zf.js} +0 -0
- /package/client/dist/assets/{m3-C7XHeDz_.js → m3-tlthQ8Fo.js} +0 -0
- /package/client/dist/assets/{markdown-PiF7r-2h.js → markdown-Dbx1LgUZ.js} +0 -0
- /package/client/dist/assets/{mcp-B_nWlxZa2.js → mcp-CjVKXAhD2.js} +0 -0
- /package/client/dist/assets/{mcp-B8omp_Cd.js → mcp-Dcl-XxkR.js} +0 -0
- /package/client/dist/assets/{mcp-DTCCrYHz2.js → mcp-DtbxuH3V2.js} +0 -0
- /package/client/dist/assets/{mcp-DytBbASZ.js → mcp-H-gnQUdN.js} +0 -0
- /package/client/dist/assets/{mcp-D_oPS6Zc.js → mcp-N1nzp8oL.js} +0 -0
- /package/client/dist/assets/{mcp-Cb2jtHP72.js → mcp-XJNRfxVp2.js} +0 -0
- /package/client/dist/assets/{mcp-DPqm2C8-.js → mcp-sRmWE8WJ.js} +0 -0
- /package/client/dist/assets/{mips-nn07GiyE.js → mips-D_O3IUz_.js} +0 -0
- /package/client/dist/assets/{msdax-DaKFVMST.js → msdax-C_ZmCnRt.js} +0 -0
- /package/client/dist/assets/{mysql-C6d6xrbU.js → mysql-C-dCEYSX.js} +0 -0
- /package/client/dist/assets/{nav-Cibp2KWF.js → nav-40HzpIFb.js} +0 -0
- /package/client/dist/assets/{nav-DtTnF9xd.js → nav-8yPhBn00.js} +0 -0
- /package/client/dist/assets/{nav-Danf5sRf2.js → nav-B6PkGfTk2.js} +0 -0
- /package/client/dist/assets/{nav-Bqhyfbcj2.js → nav-BHqMLrS72.js} +0 -0
- /package/client/dist/assets/{nav-CQb9y1km.js → nav-CGy4yx3C.js} +0 -0
- /package/client/dist/assets/{nav-BAsYcqdw2.js → nav-CXEDcRdv2.js} +0 -0
- /package/client/dist/assets/{nav-BRrNlZkr.js → nav-EI_OhEAo.js} +0 -0
- /package/client/dist/assets/{objective-c-B6KMtI-k.js → objective-c-DZY9Aqxd.js} +0 -0
- /package/client/dist/assets/{pascal-DYr8MPE7.js → pascal-BVHLXoor.js} +0 -0
- /package/client/dist/assets/{pascaligo-B3pd-jJ_.js → pascaligo-XdG_6u3k.js} +0 -0
- /package/client/dist/assets/{perl-z2xUzoaL.js → perl-b9QV0BcD.js} +0 -0
- /package/client/dist/assets/{pgsql-DUvT-09-.js → pgsql-Du5FDOgT.js} +0 -0
- /package/client/dist/assets/{php-CGyOmJfy.js → php-WJD0z2Z_.js} +0 -0
- /package/client/dist/assets/{pla-CMqgvyD0.js → pla-CbciTf2w.js} +0 -0
- /package/client/dist/assets/{postiats-Dx5LUn4Z.js → postiats-C1QxmvVq.js} +0 -0
- /package/client/dist/assets/{powerquery-DazYxvJ6.js → powerquery-BEYDhPLZ.js} +0 -0
- /package/client/dist/assets/{powershell-B3nwlDka.js → powershell-CfaYw4XL.js} +0 -0
- /package/client/dist/assets/{protobuf-BGuu2u2s.js → protobuf-pwZEDOYa.js} +0 -0
- /package/client/dist/assets/{pug-Dbte1MXA.js → pug-5ftqSPVe.js} +0 -0
- /package/client/dist/assets/{qsharp-DJLwBOvG.js → qsharp-uI6qZMq4.js} +0 -0
- /package/client/dist/assets/{r-DmGBRSHj.js → r-BK54P1EG.js} +0 -0
- /package/client/dist/assets/{redis-VvUYn0a4.js → redis-DI0yd7kN.js} +0 -0
- /package/client/dist/assets/{redshift-6GdDrxg3.js → redshift-Dov26eyI.js} +0 -0
- /package/client/dist/assets/{restructuredtext-CfAB8aRU.js → restructuredtext-QqJ_1dC-.js} +0 -0
- /package/client/dist/assets/{ruby-CIYK6MXY.js → ruby-5WjveeVb.js} +0 -0
- /package/client/dist/assets/{rust-CTHZfjj9.js → rust-BzVSfqFH.js} +0 -0
- /package/client/dist/assets/{sb-CkKTdkz_.js → sb-DMIUfT-3.js} +0 -0
- /package/client/dist/assets/{scala-Bx-Wr9TM.js → scala-CSKA7lqt.js} +0 -0
- /package/client/dist/assets/{scheme-Dcfi6f83.js → scheme-CojOP2Qc.js} +0 -0
- /package/client/dist/assets/{scss-ByL-7PFQ.js → scss-DoBZGJG6.js} +0 -0
- /package/client/dist/assets/{settings-D3rfdI1i.js → settings-BOHOmHkb.js} +0 -0
- /package/client/dist/assets/{settings-DoUJb93K2.js → settings-BlEA_7h02.js} +0 -0
- /package/client/dist/assets/{settings-CgjmTpR8.js → settings-BsJMsWGc.js} +0 -0
- /package/client/dist/assets/{settings-BbxrZlv7.js → settings-D6qY9_UI.js} +0 -0
- /package/client/dist/assets/{settings-BmA06FIJ.js → settings-DZJeXoRV.js} +0 -0
- /package/client/dist/assets/{settings-C2_QJA8f2.js → settings-TNga5gIF2.js} +0 -0
- /package/client/dist/assets/{settings-BJ6fj67y2.js → settings-lFoaYT-s2.js} +0 -0
- /package/client/dist/assets/{shell-Csnb9Yqd.js → shell-YOaE4udl.js} +0 -0
- /package/client/dist/assets/{solidity-Dbw5-0QP.js → solidity-l3jSyVLu.js} +0 -0
- /package/client/dist/assets/{sophia-ZQ3saZQx.js → sophia-BhSGTaQJ.js} +0 -0
- /package/client/dist/assets/{sparql-BEtOel8u.js → sparql-qhk6QD9w.js} +0 -0
- /package/client/dist/assets/{sql-DGPDbzB7.js → sql-DoO3y21-.js} +0 -0
- /package/client/dist/assets/{st-GeW2MOgh.js → st-ChZAY8oG.js} +0 -0
- /package/client/dist/assets/{swift-BVYTN8jq.js → swift-D290Tr7_.js} +0 -0
- /package/client/dist/assets/{systemverilog-Deg2a8Lr.js → systemverilog-DsxzgJ-o.js} +0 -0
- /package/client/dist/assets/{tcl-6DnLmROF.js → tcl-BKDUHAAR.js} +0 -0
- /package/client/dist/assets/{terminal-CDiOAmBd2.js → terminal-BRm03iFA2.js} +0 -0
- /package/client/dist/assets/{terminal-CJN-mQMc.js → terminal-De2Brd23.js} +0 -0
- /package/client/dist/assets/{terminal-CYB4TiAd.js → terminal-DxQWCqSD2.js} +0 -0
- /package/client/dist/assets/{terminal-DsaDTq5N2.js → terminal-WSAvHXoU.js} +0 -0
- /package/client/dist/assets/{terminal-DTQ_dPqi2.js → terminal-Z7KsDM_n.js} +0 -0
- /package/client/dist/assets/{terminal-58ReNaya.js → terminal-cqfd3EDV.js} +0 -0
- /package/client/dist/assets/{terminal-DawP8xNL.js → terminal-xGbCZJHg.js} +0 -0
- /package/client/dist/assets/{tickets-DoRTUYRT.js → tickets-B7zT4IRf.js} +0 -0
- /package/client/dist/assets/{tickets-DBfFePwk2.js → tickets-BoXom7l8.js} +0 -0
- /package/client/dist/assets/{tickets-DvohTziT2.js → tickets-C3b8cHhd2.js} +0 -0
- /package/client/dist/assets/{tickets-Da15iaRw.js → tickets-CFXnJsYF.js} +0 -0
- /package/client/dist/assets/{tickets-CujsZvkf.js → tickets-CWYFvx1W2.js} +0 -0
- /package/client/dist/assets/{tickets-Df6pee-O2.js → tickets-DTHo9c3C.js} +0 -0
- /package/client/dist/assets/{tickets-CNvIilmP.js → tickets-SuX_HkK4.js} +0 -0
- /package/client/dist/assets/{twig-XbbXW9Yz.js → twig-UpZQaM8G.js} +0 -0
- /package/client/dist/assets/{typespec-CRlV8GhI.js → typespec-DzaKvN8i.js} +0 -0
- /package/client/dist/assets/{vb-BHE2izPH.js → vb-CZpbb7tk.js} +0 -0
- /package/client/dist/assets/{wgsl-Bf5vkfmY.js → wgsl-CNt3OIvl.js} +0 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ACTIVE_PR_DECISIONS = exports.TERMINAL_PR_DECISIONS = void 0;
|
|
4
|
+
exports.isTerminalPrDecision = isTerminalPrDecision;
|
|
5
|
+
exports.createPrDelivery = createPrDelivery;
|
|
6
|
+
exports.getPrDelivery = getPrDelivery;
|
|
7
|
+
exports.getActivePrDeliveryByRail = getActivePrDeliveryByRail;
|
|
8
|
+
exports.listActivePrDeliveries = listActivePrDeliveries;
|
|
9
|
+
exports.transitionDecision = transitionDecision;
|
|
10
|
+
exports.toPrDeliverySnapshot = toPrDeliverySnapshot;
|
|
11
|
+
exports.toRailPrStateMessage = toRailPrStateMessage;
|
|
12
|
+
exports.toPrDecisionCardEnvelope = toPrDecisionCardEnvelope;
|
|
13
|
+
const ids_1 = require("./ids");
|
|
14
|
+
/** States after which the delivery is closed (no further decision possible). */
|
|
15
|
+
exports.TERMINAL_PR_DECISIONS = new Set(['merged', 'discarded']);
|
|
16
|
+
/** States in which the delivery still awaits (or is executing) a user decision. */
|
|
17
|
+
exports.ACTIVE_PR_DECISIONS = new Set([
|
|
18
|
+
'building',
|
|
19
|
+
'on_review',
|
|
20
|
+
'pr_draft',
|
|
21
|
+
'pr_ready',
|
|
22
|
+
'pr_failed',
|
|
23
|
+
]);
|
|
24
|
+
function isTerminalPrDecision(s) {
|
|
25
|
+
return exports.TERMINAL_PR_DECISIONS.has(s);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Insert a new delivery row for a rail launch. The row starts at
|
|
29
|
+
* decision='building', pr_state='none' with empty branches/worktree_ids —
|
|
30
|
+
* settle patches those via transitionDecision.
|
|
31
|
+
*/
|
|
32
|
+
function createPrDelivery(db, input) {
|
|
33
|
+
const id = input.id ?? (0, ids_1.newId)();
|
|
34
|
+
db.prepare(`INSERT INTO rail_pr_deliveries (
|
|
35
|
+
id, rail_index, loop_id, rail_key, ticket_ids, base_branch,
|
|
36
|
+
loop_name, origin_surface, origin_conversation_id
|
|
37
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, input.railIndex, input.loopId ?? null, input.railKey, JSON.stringify(input.ticketIds), input.baseBranch, input.loopName, input.originSurface, input.originConversationId ?? null);
|
|
38
|
+
return getPrDelivery(db, id);
|
|
39
|
+
}
|
|
40
|
+
function getPrDelivery(db, id) {
|
|
41
|
+
return db.prepare('SELECT * FROM rail_pr_deliveries WHERE id = ?').get(id);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The newest non-terminal delivery for a rail slot (a slot can be relaunched;
|
|
45
|
+
* older terminal rows are history). Used by the relaunch-collision guard and
|
|
46
|
+
* the GET /rails snapshot.
|
|
47
|
+
*/
|
|
48
|
+
function getActivePrDeliveryByRail(db, railIndex) {
|
|
49
|
+
return db
|
|
50
|
+
.prepare(`SELECT * FROM rail_pr_deliveries
|
|
51
|
+
WHERE rail_index = ? AND decision NOT IN ('merged','discarded')
|
|
52
|
+
ORDER BY created_at DESC, rowid DESC LIMIT 1`)
|
|
53
|
+
.get(railIndex);
|
|
54
|
+
}
|
|
55
|
+
/** Every non-terminal delivery — hydrates the GET /rails prDeliveries snapshot. */
|
|
56
|
+
function listActivePrDeliveries(db) {
|
|
57
|
+
return db
|
|
58
|
+
.prepare(`SELECT * FROM rail_pr_deliveries
|
|
59
|
+
WHERE decision NOT IN ('merged','discarded')
|
|
60
|
+
ORDER BY rail_index, created_at DESC, rowid DESC`)
|
|
61
|
+
.all();
|
|
62
|
+
}
|
|
63
|
+
// Column allow-list — patch keys are interpolated into the SET clause, so gate
|
|
64
|
+
// them to prevent any future caller injecting SQL via an object key (values
|
|
65
|
+
// are always parameterized). Mirrors loop-runs-store's COUNTER_COLUMNS.
|
|
66
|
+
const PATCH_COLUMNS = {
|
|
67
|
+
branch: 'branch',
|
|
68
|
+
prUrl: 'pr_url',
|
|
69
|
+
prNumber: 'pr_number',
|
|
70
|
+
prState: 'pr_state',
|
|
71
|
+
branches: 'branches',
|
|
72
|
+
worktreeIds: 'worktree_ids',
|
|
73
|
+
runIds: 'run_ids',
|
|
74
|
+
};
|
|
75
|
+
function patchValue(key, patch) {
|
|
76
|
+
const raw = patch[key];
|
|
77
|
+
if (key === 'branches' || key === 'worktreeIds' || key === 'runIds')
|
|
78
|
+
return JSON.stringify(raw);
|
|
79
|
+
return raw;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* COMPARE-AND-SET decision transition: one atomic
|
|
83
|
+
* `UPDATE … WHERE id = ? AND decision = ?` (no read-modify-write), so two
|
|
84
|
+
* surfaces racing on the same delivery cannot both win. Returns `false` (row
|
|
85
|
+
* untouched) when `expected` is stale — the caller maps that to a 409.
|
|
86
|
+
* `patch` may set delivery columns alongside the transition; `updated_at` is
|
|
87
|
+
* always bumped.
|
|
88
|
+
*/
|
|
89
|
+
function transitionDecision(db, id, expected, next, patch = {}) {
|
|
90
|
+
const keys = Object.keys(patch).filter((k) => patch[k] !== undefined);
|
|
91
|
+
const patchClause = keys.map((k) => `, ${PATCH_COLUMNS[k]} = ?`).join('');
|
|
92
|
+
const values = keys.map((k) => patchValue(k, patch));
|
|
93
|
+
return (db
|
|
94
|
+
.prepare(`UPDATE rail_pr_deliveries
|
|
95
|
+
SET decision = ?, updated_at = datetime('now')${patchClause}
|
|
96
|
+
WHERE id = ? AND decision = ?`)
|
|
97
|
+
.run(next, ...values, id, expected).changes > 0);
|
|
98
|
+
}
|
|
99
|
+
function parseJsonArray(raw) {
|
|
100
|
+
try {
|
|
101
|
+
const parsed = JSON.parse(raw);
|
|
102
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function toPrDeliverySnapshot(row) {
|
|
109
|
+
return {
|
|
110
|
+
id: row.id,
|
|
111
|
+
railIndex: row.rail_index,
|
|
112
|
+
loopId: row.loop_id,
|
|
113
|
+
railKey: row.rail_key,
|
|
114
|
+
ticketIds: parseJsonArray(row.ticket_ids),
|
|
115
|
+
baseBranch: row.base_branch,
|
|
116
|
+
branch: row.branch,
|
|
117
|
+
prUrl: row.pr_url,
|
|
118
|
+
prNumber: row.pr_number,
|
|
119
|
+
prState: row.pr_state,
|
|
120
|
+
decision: row.decision,
|
|
121
|
+
branches: parseJsonArray(row.branches),
|
|
122
|
+
loopName: row.loop_name,
|
|
123
|
+
worktreeIds: parseJsonArray(row.worktree_ids),
|
|
124
|
+
runIds: parseJsonArray(row.run_ids ?? '[]'),
|
|
125
|
+
originSurface: row.origin_surface,
|
|
126
|
+
originConversationId: row.origin_conversation_id,
|
|
127
|
+
createdAt: row.created_at,
|
|
128
|
+
updatedAt: row.updated_at,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Build the durable `rail.pr_state` broadcast payload from a delivery snapshot
|
|
133
|
+
* — the ONE message shape every mutation of the row re-broadcasts (launch
|
|
134
|
+
* insert, build-settle, decision-endpoint transitions), so both decision
|
|
135
|
+
* surfaces converge on the same state.
|
|
136
|
+
*/
|
|
137
|
+
function toRailPrStateMessage(projectId, snap) {
|
|
138
|
+
return {
|
|
139
|
+
type: 'rail.pr_state',
|
|
140
|
+
projectId,
|
|
141
|
+
railIndex: snap.railIndex,
|
|
142
|
+
prDeliveryId: snap.id,
|
|
143
|
+
railKey: snap.railKey,
|
|
144
|
+
ticketIds: snap.ticketIds,
|
|
145
|
+
baseBranch: snap.baseBranch,
|
|
146
|
+
branch: snap.branch,
|
|
147
|
+
prUrl: snap.prUrl,
|
|
148
|
+
prNumber: snap.prNumber,
|
|
149
|
+
prState: snap.prState,
|
|
150
|
+
decision: snap.decision,
|
|
151
|
+
runIds: snap.runIds,
|
|
152
|
+
originConversationId: snap.originConversationId,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Build the agent-chat PR-decision card envelope from a delivery snapshot —
|
|
157
|
+
* the ONE mapper both card writers (rail-isolated-launch's origin-card sync
|
|
158
|
+
* and rail-pr-decision's finalizeTransition) share, so the persisted card and
|
|
159
|
+
* the `agent_pr_decision` broadcast can never drift between the two sites.
|
|
160
|
+
*/
|
|
161
|
+
function toPrDecisionCardEnvelope(projectId, snap) {
|
|
162
|
+
return {
|
|
163
|
+
kind: 'pr_decision',
|
|
164
|
+
prDeliveryId: snap.id,
|
|
165
|
+
railIndex: snap.railIndex,
|
|
166
|
+
projectId,
|
|
167
|
+
baseBranch: snap.baseBranch,
|
|
168
|
+
ticketIds: snap.ticketIds,
|
|
169
|
+
decision: snap.decision,
|
|
170
|
+
prUrl: snap.prUrl,
|
|
171
|
+
prState: snap.prState,
|
|
172
|
+
branch: snap.branch,
|
|
173
|
+
runIds: snap.runIds,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
@@ -9,6 +9,8 @@ exports.setRailWorktreeRunId = setRailWorktreeRunId;
|
|
|
9
9
|
exports.listRailWorktrees = listRailWorktrees;
|
|
10
10
|
exports.listNonTerminalRailWorktrees = listNonTerminalRailWorktrees;
|
|
11
11
|
exports.deleteRailWorktree = deleteRailWorktree;
|
|
12
|
+
exports.listRailWorktreesForTicket = listRailWorktreesForTicket;
|
|
13
|
+
exports.railWorktreeBranchExistsForTicket = railWorktreeBranchExistsForTicket;
|
|
12
14
|
/** States after which the worktree is done (eligible for cleanup/reconciliation). */
|
|
13
15
|
exports.TERMINAL_MERGE_STATES = new Set(['merged', 'needs-review', 'failed']);
|
|
14
16
|
function isTerminalMergeState(s) {
|
|
@@ -50,3 +52,19 @@ function listNonTerminalRailWorktrees(db) {
|
|
|
50
52
|
function deleteRailWorktree(db, id) {
|
|
51
53
|
return db.prepare('DELETE FROM rail_worktrees WHERE id = ?').run(id).changes > 0;
|
|
52
54
|
}
|
|
55
|
+
/** Every rail allocation ever made for a ticket, newest first — the recovery
|
|
56
|
+
* input for create-pr's pre-flight: when a delivery row records a branch that
|
|
57
|
+
* no longer exists, any OTHER branch a rail allocated for the same ticket that
|
|
58
|
+
* still exists with commits ahead of base is the real carrier of the work. */
|
|
59
|
+
function listRailWorktreesForTicket(db, ticketId) {
|
|
60
|
+
return db
|
|
61
|
+
.prepare('SELECT * FROM rail_worktrees WHERE ticket_id = ? ORDER BY created_at DESC, rowid DESC')
|
|
62
|
+
.all(ticketId);
|
|
63
|
+
}
|
|
64
|
+
/** True when a prior rail allocation for this ticket used exactly this branch —
|
|
65
|
+
* i.e. an existing branch of that name is OURS to resume (partial work from a
|
|
66
|
+
* stopped run), not a foreign collision to suffix away from. */
|
|
67
|
+
function railWorktreeBranchExistsForTicket(db, ticketId, branch) {
|
|
68
|
+
return (db.prepare('SELECT 1 FROM rail_worktrees WHERE ticket_id = ? AND branch = ? LIMIT 1').get(ticketId, branch) !==
|
|
69
|
+
undefined);
|
|
70
|
+
}
|
|
@@ -6,6 +6,8 @@ const rails_store_1 = require("./rails-store");
|
|
|
6
6
|
const queue_manager_1 = require("./queue-manager");
|
|
7
7
|
const provider_selection_1 = require("./provider-selection");
|
|
8
8
|
const feature_flags_1 = require("./feature-flags");
|
|
9
|
+
const file_provenance_1 = require("./file-provenance");
|
|
10
|
+
const file_story_1 = require("./file-story");
|
|
9
11
|
const loops_store_1 = require("./loops-store");
|
|
10
12
|
const loop_runs_store_1 = require("./loop-runs-store");
|
|
11
13
|
const providers_1 = require("./providers");
|
|
@@ -16,7 +18,9 @@ const loop_constants_1 = require("./loop-constants");
|
|
|
16
18
|
const loop_command_catalog_1 = require("./loop-command-catalog");
|
|
17
19
|
const loop_graph_1 = require("./loop-graph");
|
|
18
20
|
const rail_isolation_1 = require("./rail-isolation");
|
|
21
|
+
const rail_pr_store_1 = require("./rail-pr-store");
|
|
19
22
|
const loop_effect_1 = require("./loop-effect");
|
|
23
|
+
const rail_pr_decision_1 = require("./rail-pr-decision");
|
|
20
24
|
const rail_isolated_launch_1 = require("./rail-isolated-launch");
|
|
21
25
|
const worktree_manager_1 = require("./worktree-manager");
|
|
22
26
|
const pr_publisher_1 = require("./pr-publisher");
|
|
@@ -87,17 +91,103 @@ function createRailsRouter() {
|
|
|
87
91
|
const counts = (0, loop_runs_store_1.getRunEventCounts)(c.db, run.id);
|
|
88
92
|
activeLoopRuns[run.rail_index] = { loopRunId: run.id, loopId: run.loop_id, loopName: run.loop_name, provider: run.provider, model: run.model, iteration: run.iteration_count, startedAt: run.started_at, steps: counts.steps, lines: counts.lines };
|
|
89
93
|
}
|
|
90
|
-
|
|
94
|
+
// Pending ask-first PR decisions (safe-pr-review-flow): the newest ACTIVE
|
|
95
|
+
// (non-terminal) delivery per rail slot, so a refreshed client re-renders
|
|
96
|
+
// the decision surface without waiting for a broadcast. The store lists
|
|
97
|
+
// newest-first within each rail — keep the first per index.
|
|
98
|
+
const prDeliveries = {};
|
|
99
|
+
for (const row of (0, rail_pr_store_1.listActivePrDeliveries)(c.db)) {
|
|
100
|
+
if (!(row.rail_index in prDeliveries))
|
|
101
|
+
prDeliveries[row.rail_index] = (0, rail_pr_store_1.toPrDeliverySnapshot)(row);
|
|
102
|
+
}
|
|
103
|
+
res.json({ rails, activeJobs, activeLoopRuns, prDeliveries });
|
|
91
104
|
}
|
|
92
105
|
catch (err) {
|
|
93
106
|
console.error('[rails-router] get rails error:', err);
|
|
94
107
|
res.status(500).json({ error: 'Failed to fetch rails' });
|
|
95
108
|
}
|
|
96
109
|
});
|
|
110
|
+
// POST /rails — create a new rail slot (dynamic rails). Body: { name? }.
|
|
111
|
+
// Allocates the lowest free index; capped at MAX_RAILS per project so the
|
|
112
|
+
// railIndex-keyed maps (metrics, PR deliveries, worktree progress) stay
|
|
113
|
+
// bounded. Broadcast rides the existing rail.updated shape (changed:'name')
|
|
114
|
+
// so every client — including the mobile companion — adopts the new slot
|
|
115
|
+
// without a new wire message.
|
|
116
|
+
router.post('/', (req, res) => {
|
|
117
|
+
const c = ctx(req);
|
|
118
|
+
const body = req.body ?? {};
|
|
119
|
+
const name = body.name;
|
|
120
|
+
if (name !== undefined && name !== null && typeof name !== 'string') {
|
|
121
|
+
res.status(400).json({ error: 'name must be a string or null' });
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (typeof name === 'string' && name.length > 60) {
|
|
125
|
+
res.status(400).json({ error: 'name must be 60 characters or fewer' });
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
if ((0, rails_store_1.railCount)(c.db) >= rails_store_1.MAX_RAILS) {
|
|
130
|
+
res.status(400).json({ error: 'rail_limit_reached', maxRails: rails_store_1.MAX_RAILS });
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const rail = (0, rails_store_1.createRail)(c.db, typeof name === 'string' ? name : null);
|
|
134
|
+
broadcastRailUpdated(c, rail.railIndex, 'name');
|
|
135
|
+
res.status(201).json({ rail });
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
console.error('[rails-router] create rail error:', err);
|
|
139
|
+
res.status(500).json({ error: 'Failed to create rail' });
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
// DELETE /rails/:railIndex — remove a rail slot. Guarded: the rail must
|
|
143
|
+
// exist, hold no tickets, have no active job/loop run, no undecided PR
|
|
144
|
+
// delivery, and must not be the last remaining rail. Indices are identity —
|
|
145
|
+
// deleting a middle rail leaves a gap (never re-numbered).
|
|
146
|
+
router.delete('/:railIndex', (req, res) => {
|
|
147
|
+
const railIndex = parseInt(req.params.railIndex, 10);
|
|
148
|
+
if (isNaN(railIndex) || railIndex < 0 || railIndex >= rails_store_1.MAX_RAILS) {
|
|
149
|
+
res.status(400).json({ error: 'Invalid rail index' });
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const c = ctx(req);
|
|
153
|
+
try {
|
|
154
|
+
if (!(0, rails_store_1.railExists)(c.db, railIndex)) {
|
|
155
|
+
res.status(404).json({ error: 'Rail not found' });
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if ((0, rails_store_1.railCount)(c.db) <= 1) {
|
|
159
|
+
res.status(400).json({ error: 'cannot_delete_last_rail' });
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const hasActiveJob = Array.from(c.railJobs.values()).some((m) => m.railIndex === railIndex);
|
|
163
|
+
const hasActiveRun = Array.from(c.railLoopRuns.values()).some((m) => m.railIndex === railIndex);
|
|
164
|
+
if (hasActiveJob || hasActiveRun) {
|
|
165
|
+
res.status(409).json({ error: 'rail_active' });
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if ((0, rails_store_1.getRail)(c.db, railIndex).ticketIds.length > 0) {
|
|
169
|
+
res.status(409).json({ error: 'rail_not_empty' });
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const pending = (0, rail_pr_store_1.getActivePrDeliveryByRail)(c.db, railIndex);
|
|
173
|
+
if (pending) {
|
|
174
|
+
res.status(409).json({ error: 'pr_decision_pending', prDeliveryId: pending.id });
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
(0, rails_store_1.deleteRail)(c.db, railIndex);
|
|
178
|
+
const msg = { type: 'rail.removed', projectId: c.project.id, railIndex };
|
|
179
|
+
c.broadcast(msg);
|
|
180
|
+
res.json({ ok: true, railIndex });
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
console.error('[rails-router] delete rail error:', err);
|
|
184
|
+
res.status(500).json({ error: 'Failed to delete rail' });
|
|
185
|
+
}
|
|
186
|
+
});
|
|
97
187
|
// PUT /rails/:railIndex/tickets — set ticket assignments for a rail
|
|
98
188
|
router.put('/:railIndex/tickets', (req, res) => {
|
|
99
189
|
const railIndex = parseInt(req.params.railIndex, 10);
|
|
100
|
-
if (isNaN(railIndex) || railIndex < 0) {
|
|
190
|
+
if (isNaN(railIndex) || railIndex < 0 || railIndex >= rails_store_1.MAX_RAILS) {
|
|
101
191
|
res.status(400).json({ error: 'Invalid rail index' });
|
|
102
192
|
return;
|
|
103
193
|
}
|
|
@@ -132,7 +222,7 @@ function createRailsRouter() {
|
|
|
132
222
|
// Body: { profileName: string | null } (null = force legacy mode for this rail)
|
|
133
223
|
router.put('/:railIndex/profile', (req, res) => {
|
|
134
224
|
const railIndex = parseInt(req.params.railIndex, 10);
|
|
135
|
-
if (isNaN(railIndex) || railIndex < 0) {
|
|
225
|
+
if (isNaN(railIndex) || railIndex < 0 || railIndex >= rails_store_1.MAX_RAILS) {
|
|
136
226
|
res.status(400).json({ error: 'Invalid rail index' });
|
|
137
227
|
return;
|
|
138
228
|
}
|
|
@@ -161,7 +251,7 @@ function createRailsRouter() {
|
|
|
161
251
|
// Body: { aiEngine: string | null } (null = use the project's primary provider)
|
|
162
252
|
router.put('/:railIndex/engine', (req, res) => {
|
|
163
253
|
const railIndex = parseInt(req.params.railIndex, 10);
|
|
164
|
-
if (isNaN(railIndex) || railIndex < 0) {
|
|
254
|
+
if (isNaN(railIndex) || railIndex < 0 || railIndex >= rails_store_1.MAX_RAILS) {
|
|
165
255
|
res.status(400).json({ error: 'Invalid rail index' });
|
|
166
256
|
return;
|
|
167
257
|
}
|
|
@@ -194,7 +284,7 @@ function createRailsRouter() {
|
|
|
194
284
|
// Body: { name: string | null } (empty/null clears it back to the default label)
|
|
195
285
|
router.put('/:railIndex/name', (req, res) => {
|
|
196
286
|
const railIndex = parseInt(req.params.railIndex, 10);
|
|
197
|
-
if (isNaN(railIndex) || railIndex < 0) {
|
|
287
|
+
if (isNaN(railIndex) || railIndex < 0 || railIndex >= rails_store_1.MAX_RAILS) {
|
|
198
288
|
res.status(400).json({ error: 'Invalid rail index' });
|
|
199
289
|
return;
|
|
200
290
|
}
|
|
@@ -227,16 +317,29 @@ function createRailsRouter() {
|
|
|
227
317
|
// POST /rails/:railIndex/launch — launch job(s) for a rail
|
|
228
318
|
router.post('/:railIndex/launch', async (req, res) => {
|
|
229
319
|
const railIndex = parseInt(req.params.railIndex, 10);
|
|
230
|
-
if (isNaN(railIndex) || railIndex < 0) {
|
|
320
|
+
if (isNaN(railIndex) || railIndex < 0 || railIndex >= rails_store_1.MAX_RAILS) {
|
|
231
321
|
res.status(400).json({ error: 'Invalid rail index' });
|
|
232
322
|
return;
|
|
233
323
|
}
|
|
234
324
|
let { mode = 'implement' } = req.body ?? {};
|
|
235
|
-
const { profileName, aiEngine, model, loopId, reasoning_effort } = req.body ?? {};
|
|
325
|
+
const { profileName, aiEngine, model, loopId: rawLoopId, reasoning_effort, originConversationId, originSurface } = req.body ?? {};
|
|
326
|
+
let loopId = rawLoopId;
|
|
327
|
+
// Origin link (safe-pr-review-flow): an agent-chat/MCP launch tags itself so
|
|
328
|
+
// the PR decision can later be posted back into the launching conversation.
|
|
329
|
+
// Both fields are optional; malformed values are rejected outright.
|
|
330
|
+
if (originConversationId !== undefined && originConversationId !== null) {
|
|
331
|
+
if (typeof originConversationId !== 'string' || !/^[A-Za-z0-9-]{1,64}$/.test(originConversationId)) {
|
|
332
|
+
res.status(400).json({ error: 'originConversationId must be 1-64 chars of [A-Za-z0-9-]' });
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (originSurface !== undefined && originSurface !== 'dashboard' && originSurface !== 'agent-chat') {
|
|
337
|
+
res.status(400).json({ error: "originSurface must be 'dashboard' or 'agent-chat'" });
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
236
340
|
// rails-as-loops: a FACTORY loop id (`factory:implement` etc.) maps to its
|
|
237
|
-
// legacy mode
|
|
238
|
-
//
|
|
239
|
-
// (LoopRunManager). Legacy callers that still send a bare `mode` are unchanged.
|
|
341
|
+
// legacy mode for validation/back-compat; the loop branch below still runs
|
|
342
|
+
// it through the LoopRunManager. A CUSTOM loop id keeps mode='loop'.
|
|
240
343
|
if (typeof loopId === 'string' && (0, loop_factory_1.isFactoryLoopId)(loopId)) {
|
|
241
344
|
const fmode = (0, loop_factory_1.factoryLoopMode)(loopId);
|
|
242
345
|
if (!fmode) {
|
|
@@ -249,6 +352,15 @@ function createRailsRouter() {
|
|
|
249
352
|
res.status(400).json({ error: 'mode must be "implement", "batch-implement", "ultracode" or "loop"' });
|
|
250
353
|
return;
|
|
251
354
|
}
|
|
355
|
+
// A bare legacy mode (MCP tools, mobile, direct REST — no loopId) must run
|
|
356
|
+
// through the SAME factory loop the dashboard sends, so worktree isolation
|
|
357
|
+
// and the ask-first PR flow apply identically regardless of the launch door.
|
|
358
|
+
// Without this, an agent-launched implement lands in the bare QueueManager
|
|
359
|
+
// branch: shared cwd, SPECRAILS_GIT_AUTO=false, no delivery row — stranded
|
|
360
|
+
// uncommitted work. Loops off ⇒ legacy QueueManager path, unchanged.
|
|
361
|
+
if ((0, feature_flags_1.isLoopsEnabled)() && (typeof loopId !== 'string' || !loopId) && mode !== 'loop') {
|
|
362
|
+
loopId = (0, loop_factory_1.factoryLoopForMode)(mode)?.id;
|
|
363
|
+
}
|
|
252
364
|
if (mode === 'loop' && !(0, feature_flags_1.isLoopsEnabled)()) {
|
|
253
365
|
res.status(403).json({ error: 'Loops are disabled on this server' });
|
|
254
366
|
return;
|
|
@@ -261,8 +373,9 @@ function createRailsRouter() {
|
|
|
261
373
|
return;
|
|
262
374
|
}
|
|
263
375
|
}
|
|
264
|
-
// Interactive in-job chat is ON by default for
|
|
265
|
-
//
|
|
376
|
+
// Interactive in-job chat is ON by default for EVERY claude job — the
|
|
377
|
+
// spawn-time gate in QueueManager (kill-switch + persistent-stdin
|
|
378
|
+
// capability) decides, so the launch no longer passes an explicit flag. A
|
|
266
379
|
// legacy `interactive` body param is accepted and ignored (wire compat).
|
|
267
380
|
const c = ctx(req);
|
|
268
381
|
const rail = (0, rails_store_1.getRail)(c.db, railIndex);
|
|
@@ -270,6 +383,24 @@ function createRailsRouter() {
|
|
|
270
383
|
res.status(400).json({ error: 'Rail has no tickets assigned' });
|
|
271
384
|
return;
|
|
272
385
|
}
|
|
386
|
+
// Concurrent-launch safety (parallel rails): a ticket already worked by an
|
|
387
|
+
// ACTIVE run — this rail relaunched mid-run, or the same ticket sitting on
|
|
388
|
+
// two rails — must not spawn a second concurrent writer. The per-ticket
|
|
389
|
+
// worktree path is keyed by ticketId, so a duplicate launch would silently
|
|
390
|
+
// reuse (and corrupt) the in-flight run's checkout. 409 so Launch-all and
|
|
391
|
+
// agent-driven fan-outs surface a clean per-rail skip instead of a clash.
|
|
392
|
+
const inFlightTickets = new Set();
|
|
393
|
+
for (const meta of c.railJobs.values())
|
|
394
|
+
for (const id of meta.ticketIds)
|
|
395
|
+
inFlightTickets.add(id);
|
|
396
|
+
for (const meta of c.railLoopRuns.values())
|
|
397
|
+
for (const id of meta.ticketIds)
|
|
398
|
+
inFlightTickets.add(id);
|
|
399
|
+
const inFlightOverlap = rail.ticketIds.filter((id) => inFlightTickets.has(id));
|
|
400
|
+
if (inFlightOverlap.length > 0) {
|
|
401
|
+
res.status(409).json({ error: 'tickets_in_flight', ticketIds: inFlightOverlap });
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
273
404
|
// AI engine precedence: explicit body param > stored rail engine > primary.
|
|
274
405
|
// `undefined`/empty in both means "run on the project's primary provider".
|
|
275
406
|
const requestedEngine = aiEngine === undefined ? (rail.aiEngine ?? undefined) : aiEngine;
|
|
@@ -386,6 +517,17 @@ function createRailsRouter() {
|
|
|
386
517
|
// so it is not isolated; anything that can write is.
|
|
387
518
|
const loopReadOnly = (0, loop_effect_1.classifyLoopEffect)(loopGraph) === 'read-only';
|
|
388
519
|
if ((0, rail_isolation_1.isolationApplies)({ loopsEnabled: (0, feature_flags_1.isLoopsEnabled)(), scope, ticketCount: rail.ticketIds.length, readOnly: loopReadOnly })) {
|
|
520
|
+
// Relaunch collision (ask-first PR delivery): a still-undecided
|
|
521
|
+
// delivery on this slot references the slot's ticket branches, and a
|
|
522
|
+
// relaunch would append new commits to them (createWorktree resumes
|
|
523
|
+
// existing branches). Block until the user resolves or discards it.
|
|
524
|
+
if ((0, rail_isolation_1.isRailPrDeliveryEnabled)()) {
|
|
525
|
+
const pending = (0, rail_pr_store_1.getActivePrDeliveryByRail)(c.db, railIndex);
|
|
526
|
+
if (pending) {
|
|
527
|
+
res.status(409).json({ error: 'pr_decision_pending', prDeliveryId: pending.id });
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
389
531
|
// Worktree isolation needs a git repo WITH at least one commit (an
|
|
390
532
|
// unborn HEAD can't be branched). Fall back (with a message) otherwise.
|
|
391
533
|
const status = await (0, worktree_manager_1.repoIsolationStatus)(worktree_manager_1.defaultGitRunner, c.project.path);
|
|
@@ -397,7 +539,9 @@ function createRailsRouter() {
|
|
|
397
539
|
try {
|
|
398
540
|
const ids = await (0, rail_isolated_launch_1.launchIsolatedRail)({
|
|
399
541
|
ctx: c, railIndex, ticketIds: [...rail.ticketIds], loopId, loopName, loopGraph,
|
|
400
|
-
provider: loopProvider, model: loopModel, effort,
|
|
542
|
+
provider: loopProvider, model: loopModel, effort, scope,
|
|
543
|
+
originSurface: originSurface ?? 'dashboard',
|
|
544
|
+
originConversationId: originConversationId ?? null,
|
|
401
545
|
});
|
|
402
546
|
res.status(202).json({ loopRunIds: ids, railIndex, mode, isolated: true });
|
|
403
547
|
return;
|
|
@@ -415,6 +559,36 @@ function createRailsRouter() {
|
|
|
415
559
|
const loopRunIds = [];
|
|
416
560
|
const launchLoopRun = (runId, ticketIds, spec) => {
|
|
417
561
|
c.railLoopRuns.set(runId, { railIndex, ticketIds });
|
|
562
|
+
// Code-Explorer provenance for shared-cwd loop runs (isolated runs
|
|
563
|
+
// record inside rail-isolated-launch): snapshot the REPO before the
|
|
564
|
+
// run, diff + record at settle. Loop runs settle outside QueueManager,
|
|
565
|
+
// so without this they leave no file_provenance at all. Best-effort;
|
|
566
|
+
// diffs the repo (never the workspace) exactly like QueueManager.
|
|
567
|
+
const provenanceRepoDir = loopExec.relocated && loopExec.repoDir ? loopExec.repoDir : loopExec.cwd;
|
|
568
|
+
let provenanceSnapshot = null;
|
|
569
|
+
if ((0, feature_flags_1.isCodeExplorerEnabled)()) {
|
|
570
|
+
try {
|
|
571
|
+
provenanceSnapshot = (0, file_provenance_1.snapshotWorkingTree)(provenanceRepoDir);
|
|
572
|
+
}
|
|
573
|
+
catch (err) {
|
|
574
|
+
console.warn(`[rails-router] provenance snapshot failed: ${err.message}`);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
let provenanceRecorded = false;
|
|
578
|
+
const recordRunProvenance = () => {
|
|
579
|
+
if (provenanceRecorded)
|
|
580
|
+
return;
|
|
581
|
+
provenanceRecorded = true;
|
|
582
|
+
(0, file_story_1.recordLoopRunProvenance)({
|
|
583
|
+
db: c.db,
|
|
584
|
+
projectId: c.project.id,
|
|
585
|
+
runId,
|
|
586
|
+
ticketId: ticketIds[0] ?? null,
|
|
587
|
+
repoDir: provenanceRepoDir,
|
|
588
|
+
snapshot: provenanceSnapshot,
|
|
589
|
+
broadcast: (msg) => c.broadcast(msg),
|
|
590
|
+
});
|
|
591
|
+
};
|
|
418
592
|
c.loopRunManager
|
|
419
593
|
.run({
|
|
420
594
|
runId,
|
|
@@ -432,9 +606,13 @@ function createRailsRouter() {
|
|
|
432
606
|
model: loopModel,
|
|
433
607
|
effort,
|
|
434
608
|
})
|
|
435
|
-
.then((r) =>
|
|
609
|
+
.then((r) => {
|
|
610
|
+
recordRunProvenance();
|
|
611
|
+
c.onLoopRunFinished(r.runId, r.outcome);
|
|
612
|
+
})
|
|
436
613
|
.catch((err) => {
|
|
437
614
|
console.error('[rails-router] loop run failed:', err);
|
|
615
|
+
recordRunProvenance();
|
|
438
616
|
c.onLoopRunFinished(runId, 'failed');
|
|
439
617
|
});
|
|
440
618
|
loopRunIds.push(runId);
|
|
@@ -481,7 +659,9 @@ function createRailsRouter() {
|
|
|
481
659
|
profileName: null,
|
|
482
660
|
provider: 'claude',
|
|
483
661
|
...(ultracodeModel ? { model: ultracodeModel } : {}),
|
|
484
|
-
|
|
662
|
+
// No explicit `interactive` — QueueManager's spawn-time default
|
|
663
|
+
// (kill-switch + persistent-stdin capability) covers it, and the
|
|
664
|
+
// decision survives a restart that way.
|
|
485
665
|
});
|
|
486
666
|
jobIds.push(job.id);
|
|
487
667
|
c.railJobs.set(job.id, { railIndex, mode, ticketIds: [ticketId] });
|
|
@@ -544,7 +724,7 @@ function createRailsRouter() {
|
|
|
544
724
|
// POST /rails/:railIndex/stop — kill the running job for a rail
|
|
545
725
|
router.post('/:railIndex/stop', (req, res) => {
|
|
546
726
|
const railIndex = parseInt(req.params.railIndex, 10);
|
|
547
|
-
if (isNaN(railIndex) || railIndex < 0) {
|
|
727
|
+
if (isNaN(railIndex) || railIndex < 0 || railIndex >= rails_store_1.MAX_RAILS) {
|
|
548
728
|
res.status(400).json({ error: 'Invalid rail index' });
|
|
549
729
|
return;
|
|
550
730
|
}
|
|
@@ -607,35 +787,42 @@ function createRailsRouter() {
|
|
|
607
787
|
}
|
|
608
788
|
res.json({ ok: true, jobIds: targetJobIds, loopRunIds: targetLoopRunIds, canceled: canceledCount });
|
|
609
789
|
});
|
|
610
|
-
// POST /rails/pr-
|
|
611
|
-
//
|
|
612
|
-
//
|
|
613
|
-
//
|
|
614
|
-
|
|
790
|
+
// POST /rails/pr-decision — the ONE decision action (safe-pr-review-flow) both
|
|
791
|
+
// surfaces (dashboard rail row + agent-chat card) call on a rail_pr_deliveries
|
|
792
|
+
// row: create-pr | publish | discard | poll-merge, compare-and-set-guarded by
|
|
793
|
+
// expectedDecision (a raced concurrent answer loses with 409 stale_decision).
|
|
794
|
+
// Replaces the stateless v1 POST /rails/pr-review passthrough. The route only
|
|
795
|
+
// validates and delegates — the action logic lives in rail-pr-decision.ts.
|
|
796
|
+
// specrails never merges — the engineer owns the merge.
|
|
797
|
+
router.post('/pr-decision', async (req, res) => {
|
|
615
798
|
const c = ctx(req);
|
|
616
|
-
const
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
res.status(400).json({ error: 'prUrl must be a valid pull-request URL' });
|
|
799
|
+
const { prDeliveryId, action, expectedDecision } = req.body ?? {};
|
|
800
|
+
if (typeof prDeliveryId !== 'string' || !prDeliveryId) {
|
|
801
|
+
res.status(400).json({ error: 'prDeliveryId is required' });
|
|
620
802
|
return;
|
|
621
803
|
}
|
|
622
|
-
if (
|
|
623
|
-
res.status(400).json({ error: "action must be '
|
|
804
|
+
if (!(0, rail_pr_decision_1.isPrDecisionAction)(action)) {
|
|
805
|
+
res.status(400).json({ error: "action must be 'create-pr', 'publish', 'discard' or 'poll-merge'" });
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
if (typeof expectedDecision !== 'string' || !expectedDecision) {
|
|
809
|
+
res.status(400).json({ error: 'expectedDecision is required' });
|
|
624
810
|
return;
|
|
625
811
|
}
|
|
626
|
-
const args = action === 'ready'
|
|
627
|
-
? ['pr', 'ready', prUrl]
|
|
628
|
-
: ['pr', 'close', prUrl, '--delete-branch'];
|
|
629
812
|
try {
|
|
630
|
-
const
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
813
|
+
const result = await (0, rail_pr_decision_1.executePrDecision)({
|
|
814
|
+
db: c.db,
|
|
815
|
+
project: { id: c.project.id, slug: c.project.slug, path: c.project.path },
|
|
816
|
+
git: worktree_manager_1.defaultGitRunner,
|
|
817
|
+
exec: pr_publisher_1.defaultExec,
|
|
818
|
+
broadcast: c.broadcast,
|
|
819
|
+
jiraSyncManager: c.jiraSyncManager,
|
|
820
|
+
}, { prDeliveryId, action, expectedDecision });
|
|
821
|
+
res.status(result.status).json(result.body);
|
|
636
822
|
}
|
|
637
823
|
catch (err) {
|
|
638
|
-
|
|
824
|
+
console.error('[rails-router] pr-decision error:', err);
|
|
825
|
+
res.status(500).json({ error: 'pr-decision failed', detail: err.message });
|
|
639
826
|
}
|
|
640
827
|
});
|
|
641
828
|
return router;
|