specrails-desktop 2.20.1 → 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 -2
- package/client/dist/assets/{ActivityFeedPage-3WSqRYVx.js → ActivityFeedPage-roDyKGuv.js} +1 -1
- package/client/dist/assets/AgentBrowserCapture-BsXnWa4j.js +1 -0
- package/client/dist/assets/{AgentModeCodePane-Cnsea6eZ.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-C6If656N.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-CezTK0EX.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-CRtTj4Co.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-BQn4yxaw.js → dist-js-BZSiJDbG.js} +1 -1
- package/client/dist/assets/{dist-js-BAWSqVtr.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-C-g8QbZl.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/{settings-BPq9nCJm.js → settings-BOHOmHkb.js} +1 -1
- package/client/dist/assets/{settings-r8Qq2Gzp2.js → settings-BlEA_7h02.js} +1 -1
- package/client/dist/assets/{settings-C-m3yEZ2.js → settings-BsJMsWGc.js} +1 -1
- package/client/dist/assets/{settings-zZr7NzuB.js → settings-D6qY9_UI.js} +1 -1
- package/client/dist/assets/{settings-VgCsjaYf.js → settings-DZJeXoRV.js} +1 -1
- package/client/dist/assets/settings-Mr7MnJbf.js +1 -0
- package/client/dist/assets/{settings-BWHGtmU12.js → settings-TNga5gIF2.js} +1 -1
- package/client/dist/assets/{settings-DZj7Uqy32.js → settings-lFoaYT-s2.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-D9aLbxw-.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 +14 -14
- 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 +9 -5
- 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 +10 -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 +9 -5
- 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 +9 -5
- 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 +10 -6
- 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 +9 -5
- 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 +9 -5
- 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 +10 -6
- 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 +6 -2
- 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 +102 -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/git-guardrails.js +62 -0
- package/server/dist/index.js +5 -0
- package/server/dist/integration-branch.js +63 -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-effect.js +12 -0
- package/server/dist/loop-executors.js +92 -22
- 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/pr-publisher.js +68 -0
- package/server/dist/project-registry.js +77 -17
- package/server/dist/project-router-jobs.js +53 -13
- package/server/dist/project-router-settings.js +33 -0
- 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 +268 -83
- package/server/dist/rail-isolated-launch.js +324 -34
- package/server/dist/rail-isolation.js +32 -5
- package/server/dist/rail-pr-decision.js +519 -0
- package/server/dist/rail-pr-delivery.js +75 -0
- package/server/dist/rail-pr-store.js +175 -0
- package/server/dist/rail-worktrees-store.js +18 -0
- package/server/dist/rails-router.js +241 -17
- 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-BlGDZahz.js +0 -1
- package/client/dist/assets/AgentModeJobsPane-CyNR-9BO.js +0 -1
- package/client/dist/assets/AgentsPage-dNsEsY_x.js +0 -86
- package/client/dist/assets/AnalyticsPage-BAxmR0HQ.js +0 -1
- package/client/dist/assets/CodePage-Crc4BBQN.js +0 -2
- package/client/dist/assets/DesktopAnalyticsPage-IXSnbCce.js +0 -1
- package/client/dist/assets/DocsDialog-Bc4hx7oW.js +0 -11
- package/client/dist/assets/DocsPage-bEU4t9c6.js +0 -11
- package/client/dist/assets/IntegrationsPage-wq2CezEL.js +0 -3
- package/client/dist/assets/JobDetailPage-BBcPk3j8.js +0 -1
- package/client/dist/assets/JobsPage-DXrSPlwa.js +0 -1
- package/client/dist/assets/LogViewer-BoYbwvbU.js +0 -16
- package/client/dist/assets/LoopBuilderPage-CiUMAuWz.js +0 -7
- package/client/dist/assets/LoopsPage-Xrp6W5XW.js +0 -1
- package/client/dist/assets/ProjectSettingsDialog-BU98L5JD.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-B8032psZ.js +0 -1
- package/client/dist/assets/dashboard-BHGKtxty2.js +0 -1
- package/client/dist/assets/dashboard-CXnoUgN22.js +0 -1
- package/client/dist/assets/dashboard-Ccyxqy7d.js +0 -1
- package/client/dist/assets/dashboard-CoED_41r.js +0 -1
- package/client/dist/assets/dashboard-Dc5ZAgMN.js +0 -1
- package/client/dist/assets/dashboard-JMY8ueD3.js +0 -1
- package/client/dist/assets/dashboard-tkWr1aHD2.js +0 -1
- package/client/dist/assets/index-BHPbzyg6.css +0 -2
- package/client/dist/assets/index-n2PmJGF6.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/settings-CY8udi0e.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/{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,8 +18,12 @@ 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");
|
|
22
|
+
const loop_effect_1 = require("./loop-effect");
|
|
23
|
+
const rail_pr_decision_1 = require("./rail-pr-decision");
|
|
19
24
|
const rail_isolated_launch_1 = require("./rail-isolated-launch");
|
|
20
25
|
const worktree_manager_1 = require("./worktree-manager");
|
|
26
|
+
const pr_publisher_1 = require("./pr-publisher");
|
|
21
27
|
const ids_1 = require("./ids");
|
|
22
28
|
const VALID_MODES = new Set(['implement', 'batch-implement', 'ultracode', 'loop']);
|
|
23
29
|
// Models the ultracode picker exposes (Claude aliases). Mirrors the client
|
|
@@ -85,17 +91,103 @@ function createRailsRouter() {
|
|
|
85
91
|
const counts = (0, loop_runs_store_1.getRunEventCounts)(c.db, run.id);
|
|
86
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 };
|
|
87
93
|
}
|
|
88
|
-
|
|
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 });
|
|
89
104
|
}
|
|
90
105
|
catch (err) {
|
|
91
106
|
console.error('[rails-router] get rails error:', err);
|
|
92
107
|
res.status(500).json({ error: 'Failed to fetch rails' });
|
|
93
108
|
}
|
|
94
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
|
+
});
|
|
95
187
|
// PUT /rails/:railIndex/tickets — set ticket assignments for a rail
|
|
96
188
|
router.put('/:railIndex/tickets', (req, res) => {
|
|
97
189
|
const railIndex = parseInt(req.params.railIndex, 10);
|
|
98
|
-
if (isNaN(railIndex) || railIndex < 0) {
|
|
190
|
+
if (isNaN(railIndex) || railIndex < 0 || railIndex >= rails_store_1.MAX_RAILS) {
|
|
99
191
|
res.status(400).json({ error: 'Invalid rail index' });
|
|
100
192
|
return;
|
|
101
193
|
}
|
|
@@ -130,7 +222,7 @@ function createRailsRouter() {
|
|
|
130
222
|
// Body: { profileName: string | null } (null = force legacy mode for this rail)
|
|
131
223
|
router.put('/:railIndex/profile', (req, res) => {
|
|
132
224
|
const railIndex = parseInt(req.params.railIndex, 10);
|
|
133
|
-
if (isNaN(railIndex) || railIndex < 0) {
|
|
225
|
+
if (isNaN(railIndex) || railIndex < 0 || railIndex >= rails_store_1.MAX_RAILS) {
|
|
134
226
|
res.status(400).json({ error: 'Invalid rail index' });
|
|
135
227
|
return;
|
|
136
228
|
}
|
|
@@ -159,7 +251,7 @@ function createRailsRouter() {
|
|
|
159
251
|
// Body: { aiEngine: string | null } (null = use the project's primary provider)
|
|
160
252
|
router.put('/:railIndex/engine', (req, res) => {
|
|
161
253
|
const railIndex = parseInt(req.params.railIndex, 10);
|
|
162
|
-
if (isNaN(railIndex) || railIndex < 0) {
|
|
254
|
+
if (isNaN(railIndex) || railIndex < 0 || railIndex >= rails_store_1.MAX_RAILS) {
|
|
163
255
|
res.status(400).json({ error: 'Invalid rail index' });
|
|
164
256
|
return;
|
|
165
257
|
}
|
|
@@ -192,7 +284,7 @@ function createRailsRouter() {
|
|
|
192
284
|
// Body: { name: string | null } (empty/null clears it back to the default label)
|
|
193
285
|
router.put('/:railIndex/name', (req, res) => {
|
|
194
286
|
const railIndex = parseInt(req.params.railIndex, 10);
|
|
195
|
-
if (isNaN(railIndex) || railIndex < 0) {
|
|
287
|
+
if (isNaN(railIndex) || railIndex < 0 || railIndex >= rails_store_1.MAX_RAILS) {
|
|
196
288
|
res.status(400).json({ error: 'Invalid rail index' });
|
|
197
289
|
return;
|
|
198
290
|
}
|
|
@@ -225,16 +317,29 @@ function createRailsRouter() {
|
|
|
225
317
|
// POST /rails/:railIndex/launch — launch job(s) for a rail
|
|
226
318
|
router.post('/:railIndex/launch', async (req, res) => {
|
|
227
319
|
const railIndex = parseInt(req.params.railIndex, 10);
|
|
228
|
-
if (isNaN(railIndex) || railIndex < 0) {
|
|
320
|
+
if (isNaN(railIndex) || railIndex < 0 || railIndex >= rails_store_1.MAX_RAILS) {
|
|
229
321
|
res.status(400).json({ error: 'Invalid rail index' });
|
|
230
322
|
return;
|
|
231
323
|
}
|
|
232
324
|
let { mode = 'implement' } = req.body ?? {};
|
|
233
|
-
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
|
+
}
|
|
234
340
|
// rails-as-loops: a FACTORY loop id (`factory:implement` etc.) maps to its
|
|
235
|
-
// legacy mode
|
|
236
|
-
//
|
|
237
|
-
// (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'.
|
|
238
343
|
if (typeof loopId === 'string' && (0, loop_factory_1.isFactoryLoopId)(loopId)) {
|
|
239
344
|
const fmode = (0, loop_factory_1.factoryLoopMode)(loopId);
|
|
240
345
|
if (!fmode) {
|
|
@@ -247,6 +352,15 @@ function createRailsRouter() {
|
|
|
247
352
|
res.status(400).json({ error: 'mode must be "implement", "batch-implement", "ultracode" or "loop"' });
|
|
248
353
|
return;
|
|
249
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
|
+
}
|
|
250
364
|
if (mode === 'loop' && !(0, feature_flags_1.isLoopsEnabled)()) {
|
|
251
365
|
res.status(403).json({ error: 'Loops are disabled on this server' });
|
|
252
366
|
return;
|
|
@@ -259,8 +373,9 @@ function createRailsRouter() {
|
|
|
259
373
|
return;
|
|
260
374
|
}
|
|
261
375
|
}
|
|
262
|
-
// Interactive in-job chat is ON by default for
|
|
263
|
-
//
|
|
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
|
|
264
379
|
// legacy `interactive` body param is accepted and ignored (wire compat).
|
|
265
380
|
const c = ctx(req);
|
|
266
381
|
const rail = (0, rails_store_1.getRail)(c.db, railIndex);
|
|
@@ -268,6 +383,24 @@ function createRailsRouter() {
|
|
|
268
383
|
res.status(400).json({ error: 'Rail has no tickets assigned' });
|
|
269
384
|
return;
|
|
270
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
|
+
}
|
|
271
404
|
// AI engine precedence: explicit body param > stored rail engine > primary.
|
|
272
405
|
// `undefined`/empty in both means "run on the project's primary provider".
|
|
273
406
|
const requestedEngine = aiEngine === undefined ? (rail.aiEngine ?? undefined) : aiEngine;
|
|
@@ -379,7 +512,22 @@ function createRailsRouter() {
|
|
|
379
512
|
// repo, an unborn HEAD, or a worktree-allocation failure all fall through to
|
|
380
513
|
// the shared-cwd path below. See rail-isolation.ts.
|
|
381
514
|
let isolationUnavailable;
|
|
382
|
-
|
|
515
|
+
// Read-only vs mutating is DERIVED from the loop's nodes (see loop-effect),
|
|
516
|
+
// not a user flag — a content-read-only loop (no ai-step/shell) never writes,
|
|
517
|
+
// so it is not isolated; anything that can write is.
|
|
518
|
+
const loopReadOnly = (0, loop_effect_1.classifyLoopEffect)(loopGraph) === 'read-only';
|
|
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
|
+
}
|
|
383
531
|
// Worktree isolation needs a git repo WITH at least one commit (an
|
|
384
532
|
// unborn HEAD can't be branched). Fall back (with a message) otherwise.
|
|
385
533
|
const status = await (0, worktree_manager_1.repoIsolationStatus)(worktree_manager_1.defaultGitRunner, c.project.path);
|
|
@@ -391,7 +539,9 @@ function createRailsRouter() {
|
|
|
391
539
|
try {
|
|
392
540
|
const ids = await (0, rail_isolated_launch_1.launchIsolatedRail)({
|
|
393
541
|
ctx: c, railIndex, ticketIds: [...rail.ticketIds], loopId, loopName, loopGraph,
|
|
394
|
-
provider: loopProvider, model: loopModel, effort,
|
|
542
|
+
provider: loopProvider, model: loopModel, effort, scope,
|
|
543
|
+
originSurface: originSurface ?? 'dashboard',
|
|
544
|
+
originConversationId: originConversationId ?? null,
|
|
395
545
|
});
|
|
396
546
|
res.status(202).json({ loopRunIds: ids, railIndex, mode, isolated: true });
|
|
397
547
|
return;
|
|
@@ -409,6 +559,36 @@ function createRailsRouter() {
|
|
|
409
559
|
const loopRunIds = [];
|
|
410
560
|
const launchLoopRun = (runId, ticketIds, spec) => {
|
|
411
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
|
+
};
|
|
412
592
|
c.loopRunManager
|
|
413
593
|
.run({
|
|
414
594
|
runId,
|
|
@@ -426,9 +606,13 @@ function createRailsRouter() {
|
|
|
426
606
|
model: loopModel,
|
|
427
607
|
effort,
|
|
428
608
|
})
|
|
429
|
-
.then((r) =>
|
|
609
|
+
.then((r) => {
|
|
610
|
+
recordRunProvenance();
|
|
611
|
+
c.onLoopRunFinished(r.runId, r.outcome);
|
|
612
|
+
})
|
|
430
613
|
.catch((err) => {
|
|
431
614
|
console.error('[rails-router] loop run failed:', err);
|
|
615
|
+
recordRunProvenance();
|
|
432
616
|
c.onLoopRunFinished(runId, 'failed');
|
|
433
617
|
});
|
|
434
618
|
loopRunIds.push(runId);
|
|
@@ -475,7 +659,9 @@ function createRailsRouter() {
|
|
|
475
659
|
profileName: null,
|
|
476
660
|
provider: 'claude',
|
|
477
661
|
...(ultracodeModel ? { model: ultracodeModel } : {}),
|
|
478
|
-
|
|
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.
|
|
479
665
|
});
|
|
480
666
|
jobIds.push(job.id);
|
|
481
667
|
c.railJobs.set(job.id, { railIndex, mode, ticketIds: [ticketId] });
|
|
@@ -538,7 +724,7 @@ function createRailsRouter() {
|
|
|
538
724
|
// POST /rails/:railIndex/stop — kill the running job for a rail
|
|
539
725
|
router.post('/:railIndex/stop', (req, res) => {
|
|
540
726
|
const railIndex = parseInt(req.params.railIndex, 10);
|
|
541
|
-
if (isNaN(railIndex) || railIndex < 0) {
|
|
727
|
+
if (isNaN(railIndex) || railIndex < 0 || railIndex >= rails_store_1.MAX_RAILS) {
|
|
542
728
|
res.status(400).json({ error: 'Invalid rail index' });
|
|
543
729
|
return;
|
|
544
730
|
}
|
|
@@ -601,5 +787,43 @@ function createRailsRouter() {
|
|
|
601
787
|
}
|
|
602
788
|
res.json({ ok: true, jobIds: targetJobIds, loopRunIds: targetLoopRunIds, canceled: canceledCount });
|
|
603
789
|
});
|
|
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) => {
|
|
798
|
+
const c = ctx(req);
|
|
799
|
+
const { prDeliveryId, action, expectedDecision } = req.body ?? {};
|
|
800
|
+
if (typeof prDeliveryId !== 'string' || !prDeliveryId) {
|
|
801
|
+
res.status(400).json({ error: 'prDeliveryId is required' });
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
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' });
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
try {
|
|
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);
|
|
822
|
+
}
|
|
823
|
+
catch (err) {
|
|
824
|
+
console.error('[rails-router] pr-decision error:', err);
|
|
825
|
+
res.status(500).json({ error: 'pr-decision failed', detail: err.message });
|
|
826
|
+
}
|
|
827
|
+
});
|
|
604
828
|
return router;
|
|
605
829
|
}
|