specrails-desktop 2.30.2 → 2.31.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 +28 -24
- package/client/dist/assets/{ActivityFeedPage-9DZOvIcY.js → ActivityFeedPage-CAhFRUO4.js} +1 -1
- package/client/dist/assets/{AgentBrowserCapture-DqyHbzk7.js → AgentBrowserCapture-Ct4G5lsE.js} +1 -1
- package/client/dist/assets/AgentModeCodePane-DQt9WIvu.js +2 -0
- package/client/dist/assets/{AgentModeJobsPane-CTIHMUKN.js → AgentModeJobsPane-podaJVl9.js} +1 -1
- package/client/dist/assets/AgentsPage-DTZdbW80.js +87 -0
- package/client/dist/assets/AnalyticsPage-BskgGPm0.js +1 -0
- package/client/dist/assets/CodePage-CvtYo1DU.js +2 -0
- package/client/dist/assets/{DesktopAnalyticsPage-CkXUFj3F.js → DesktopAnalyticsPage-Dnf99E8e.js} +1 -1
- package/client/dist/assets/{DocsDialog-PhdCJNVj.js → DocsDialog-DHi36rZ4.js} +1 -1
- package/client/dist/assets/{DocsPage-BY-wDthx.js → DocsPage-DIdVrQz1.js} +1 -1
- package/client/dist/assets/{InteractiveJobComposer-C9I0A4KR.js → InteractiveJobComposer-BHeB2inv.js} +1 -1
- package/client/dist/assets/{JobDetailModal-I_RoQJV4.js → JobDetailModal-CF1SKUHH.js} +1 -1
- package/client/dist/assets/JobDetailPage-De3rY18h.js +1 -0
- package/client/dist/assets/{JobsPage-DcllM4Ip.js → JobsPage-CfAqqJTx.js} +1 -1
- package/client/dist/assets/{LoopBuilderPage-DGm_iuff.js → LoopBuilderPage-DSBMuTb3.js} +2 -2
- package/client/dist/assets/{LoopPreviewModal-BC-TeFdD.js → LoopPreviewModal-B-c37kA0.js} +1 -1
- package/client/dist/assets/LoopsPage-DjnNXdmm.js +1 -0
- package/client/dist/assets/{MinimizedChatsContext-Cs34FPTz.js → MinimizedChatsContext-DIsnQMVz.js} +1 -1
- package/client/dist/assets/PluginsPage-DA5pQ_6u.js +2 -0
- package/client/dist/assets/{ProjectSettingsDialog-nEs-ENrE.js → ProjectSettingsDialog-DA23PSi6.js} +1 -1
- package/client/dist/assets/{TemplatePreviewModal-D4qW-J9B.js → TemplatePreviewModal-Cp7IQ-g2.js} +1 -1
- package/client/dist/assets/TicketDetailModal-BPD2i_0R.js +14 -0
- package/client/dist/assets/{Trans-CPISVji8.js → Trans-cWtExGkC.js} +1 -1
- package/client/dist/assets/addspec-4WmLJmBG.js +1 -0
- package/client/dist/assets/addspec-BRlk8jON.js +1 -0
- package/client/dist/assets/addspec-C7XRejU4.js +1 -0
- package/client/dist/assets/addspec-CHW9xf_X.js +1 -0
- package/client/dist/assets/addspec-DTmRrWrF.js +1 -0
- package/client/dist/assets/addspec-DhmnXER8.js +1 -0
- package/client/dist/assets/addspec-Di41DpYV.js +1 -0
- package/client/dist/assets/addspec-x3mizDxc.js +1 -0
- package/client/dist/assets/agent-BKsuIgIT.js +1 -0
- package/client/dist/assets/agent-Bs9NilfI.js +1 -0
- package/client/dist/assets/agent-C23h684q.js +1 -0
- package/client/dist/assets/agent-CqEjy3Z2.js +1 -0
- package/client/dist/assets/agent-D5DZTks1.js +1 -0
- package/client/dist/assets/agent-DE8hUYPl.js +1 -0
- package/client/dist/assets/agent-DsTuJGSc.js +1 -0
- package/client/dist/assets/agent-DwZM05-x.js +1 -0
- package/client/dist/assets/agents-45GbINfh.js +1 -0
- package/client/dist/assets/agents-BHp0_FQt.js +1 -0
- package/client/dist/assets/agents-Bvyi3y6R.js +1 -0
- package/client/dist/assets/agents-DMKFStHL.js +1 -0
- package/client/dist/assets/agents-DaYcxAr1.js +1 -0
- package/client/dist/assets/agents-IUOgqT5t.js +1 -0
- package/client/dist/assets/agents-ms7TwG16.js +1 -0
- package/client/dist/assets/agents-rzPnqoBR.js +1 -0
- package/client/dist/assets/agentstudio-8TQqBenW.js +1 -0
- package/client/dist/assets/agentstudio-B8Z_mmEX.js +1 -0
- package/client/dist/assets/agentstudio-BZR9YmLs.js +1 -0
- package/client/dist/assets/agentstudio-Bge9pAWb.js +1 -0
- package/client/dist/assets/agentstudio-Bz169Vzt.js +1 -0
- package/client/dist/assets/agentstudio-D6L2jpNi.js +1 -0
- package/client/dist/assets/agentstudio-K2-Z7CL0.js +1 -0
- package/client/dist/assets/agentstudio-Zmgzthv8.js +1 -0
- package/client/dist/assets/analytics-B8Qtptbl.js +1 -0
- package/client/dist/assets/analytics-BHqUazhP.js +1 -0
- package/client/dist/assets/analytics-BPptRY4f.js +1 -0
- package/client/dist/assets/analytics-BxDGOIwf.js +1 -0
- package/client/dist/assets/analytics-C9RaQcE3.js +1 -0
- package/client/dist/assets/analytics-CJIwqrEl.js +1 -0
- package/client/dist/assets/analytics-D_OZdQ9O.js +1 -0
- package/client/dist/assets/analytics-We6lUMI8.js +1 -0
- package/client/dist/assets/builder-3bygQtZi.js +1 -0
- package/client/dist/assets/builder-BiUG9Z5K.js +1 -0
- package/client/dist/assets/builder-CGRz_-zP.js +1 -0
- package/client/dist/assets/builder-CWYPw7Cw.js +1 -0
- package/client/dist/assets/builder-CX1437hF.js +1 -0
- package/client/dist/assets/{builder-CwA4T7gP.js → builder-Cowk9jtL.js} +1 -1
- package/client/dist/assets/builder-DwDexYRE.js +1 -0
- package/client/dist/assets/builder-skfnZ1uC.js +1 -0
- package/client/dist/assets/code-B0O8-p8k.js +1 -0
- package/client/dist/assets/code-B1VLZDhS.js +1 -0
- package/client/dist/assets/code-B86IMAfz.js +1 -0
- package/client/dist/assets/code-Bpj4Ddml.js +1 -0
- package/client/dist/assets/code-BykBZ5-e.js +1 -0
- package/client/dist/assets/code-CHPkOkDS.js +1 -0
- package/client/dist/assets/code-CkUuW4m9.js +1 -0
- package/client/dist/assets/code-cAqaqMTx.js +1 -0
- package/client/dist/assets/{dashboard-Dy2Um-US.js → dashboard-BVq6kRJe.js} +1 -1
- package/client/dist/assets/{dashboard-CHCEkds4.js → dashboard-BaKPPmLH.js} +1 -1
- package/client/dist/assets/{dashboard-CrlTV1ms.js → dashboard-Bm8e4zYG.js} +1 -1
- package/client/dist/assets/{dashboard-WhzOQw1S.js → dashboard-Bt6IesnM.js} +1 -1
- package/client/dist/assets/{dashboard-Cslu_GCH.js → dashboard-C_NQZle_.js} +1 -1
- package/client/dist/assets/{dashboard-DweT3Y4R.js → dashboard-CyZKoGoI.js} +1 -1
- package/client/dist/assets/{dashboard-DUU8-_Rg.js → dashboard-HIUdUBQ0.js} +1 -1
- package/client/dist/assets/{dashboard-Cqmmbh3l.js → dashboard-pm4Mr229.js} +1 -1
- package/client/dist/assets/{dist-js-jwoXo0YD.js → dist-js-BaP-xLOy.js} +1 -1
- package/client/dist/assets/{dist-js-Dj72W6qo.js → dist-js-CLlgOa5a.js} +1 -1
- package/client/dist/assets/format-command-C0D9IPmV.js +1 -0
- package/client/dist/assets/{formatDistanceToNow-ByKli7Q_.js → formatDistanceToNow-DUyzS4rr.js} +1 -1
- package/client/dist/assets/{getTimezoneOffsetInMilliseconds-qrcBaLW1.js → getTimezoneOffsetInMilliseconds-BCIwmmTj.js} +1 -1
- package/client/dist/assets/{i18n-Bf9Ckd10.js → i18n-D21Pr8Sj.js} +2 -2
- package/client/dist/assets/index-BdSDUFnA.css +2 -0
- package/client/dist/assets/index-Ci4-JKDr.js +77 -0
- package/client/dist/assets/{jobs-CVFD8rar.js → jobs-BAls3bWq.js} +1 -1
- package/client/dist/assets/{jobs-DoSaFzAa.js → jobs-B_TeSQxb.js} +1 -1
- package/client/dist/assets/jobs-Bgq-PZkC.js +1 -0
- package/client/dist/assets/jobs-Bpks6doU.js +1 -0
- package/client/dist/assets/jobs-CN_zv4Gr.js +1 -0
- package/client/dist/assets/jobs-DUlWSYq2.js +1 -0
- package/client/dist/assets/{jobs-B-sDHEay.js → jobs-DhxKgWFq.js} +1 -1
- package/client/dist/assets/{jobs-lk1_Dq1U.js → jobs-SwYS2M-B.js} +1 -1
- package/client/dist/assets/nav-BHDiSIVu.js +1 -0
- package/client/dist/assets/nav-CMiYAq4i.js +1 -0
- package/client/dist/assets/nav-CqS7NrDW.js +1 -0
- package/client/dist/assets/nav-D-0M7xfv.js +1 -0
- package/client/dist/assets/nav-D65O6u9I.js +1 -0
- package/client/dist/assets/nav-D_LKYvBr.js +1 -0
- package/client/dist/assets/nav-FDTJmU79.js +1 -0
- package/client/dist/assets/nav-VaB-C4k5.js +1 -0
- package/client/dist/assets/provider-capabilities-BvoExILy.js +1 -0
- package/client/dist/assets/settings-BveOJoaI.js +1 -0
- package/client/dist/assets/settings-Cj1C6F5k.js +1 -0
- package/client/dist/assets/settings-CuVwOpe0.js +1 -0
- package/client/dist/assets/settings-D5X4DfRc.js +1 -0
- package/client/dist/assets/settings-DXwxe-BW.js +1 -0
- package/client/dist/assets/settings-Di81Ba_V.js +1 -0
- package/client/dist/assets/settings-jbLjCYC_.js +1 -0
- package/client/dist/assets/settings-wIZNPFnn.js +1 -0
- package/client/dist/assets/setup-BBiHEO7h.js +1 -0
- package/client/dist/assets/setup-BlTWhNIQ.js +1 -0
- package/client/dist/assets/setup-Bp2L_oiP.js +1 -0
- package/client/dist/assets/setup-CInYXp1-.js +1 -0
- package/client/dist/assets/{setup-CITtPjed.js → setup-CYH3bP6K.js} +1 -1
- package/client/dist/assets/setup-DwcQ921P.js +1 -0
- package/client/dist/assets/setup-PBLjuDSs.js +1 -0
- package/client/dist/assets/setup-bx6FtoBp.js +1 -0
- package/client/dist/assets/{spending-DMnCXy84.js → spending-BCyUxoOI.js} +1 -1
- package/client/dist/assets/{tickets-B8BuAaj5.js → tickets-BL9r1G0e.js} +1 -1
- package/client/dist/assets/{tickets-DpmdE8tv.js → tickets-B_NyHJ8f.js} +1 -1
- package/client/dist/assets/{tickets-B_yh4qQk.js → tickets-BarF1wuG.js} +1 -1
- package/client/dist/assets/{tickets-pAUJkDTu.js → tickets-CclVhpQA.js} +1 -1
- package/client/dist/assets/{tickets-D_yDquEw.js → tickets-CqqOHbli.js} +1 -1
- package/client/dist/assets/{tickets-CFk2n2Or.js → tickets-DCkFSiqt.js} +1 -1
- package/client/dist/assets/{tickets-DQp7JS2R.js → tickets-Djn3ccfT.js} +1 -1
- package/client/dist/assets/{tickets-Bs37scwY.js → tickets-MiHBPsy_.js} +1 -1
- package/client/dist/assets/{useBrowserCaptureSession-CkBJShUw.js → useBrowserCaptureSession-CkVCDzer.js} +1 -1
- package/client/dist/assets/{useDesktop-B4pSBIDd.js → useDesktop-DCP_MHp9.js} +1 -1
- package/client/dist/index.html +25 -24
- package/docs/README.md +7 -3
- package/docs/cli.md +3 -3
- package/docs/codex.md +51 -64
- package/docs/creating-specs.md +36 -4
- package/docs/customizing.md +5 -5
- package/docs/gemini.md +35 -47
- package/docs/getting-started.md +15 -9
- package/docs/guide/de/agents/2-profiles-and-the-balanced-default.md +5 -1
- package/docs/guide/de/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/de/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/de/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/de/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/de/integrations/1-ai-providers.md +29 -18
- package/docs/guide/de/integrations/2-plugins.md +3 -3
- package/docs/guide/de/integrations/6-agent-chat.md +8 -1
- package/docs/guide/de/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/de/pipeline/2-the-job-detail-view.md +3 -1
- package/docs/guide/de/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/de/pipeline/4-picking-an-engine-per-rail.md +13 -13
- package/docs/guide/de/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/de/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/guide/en/agents/2-profiles-and-the-balanced-default.md +7 -1
- package/docs/guide/en/agents/4-custom-agents-catalog.md +17 -4
- package/docs/guide/en/getting-started/1-what-is-specrails.md +2 -1
- package/docs/guide/en/getting-started/2-installing-and-first-run.md +2 -1
- package/docs/guide/en/getting-started/4-the-dashboard-tour.md +5 -5
- package/docs/guide/en/insights/3-code-explorer.md +5 -0
- package/docs/guide/en/integrations/1-ai-providers.md +63 -22
- package/docs/guide/en/integrations/2-plugins.md +3 -3
- package/docs/guide/en/integrations/6-agent-chat.md +14 -2
- package/docs/guide/en/pipeline/1-rails-and-jobs.md +19 -7
- package/docs/guide/en/pipeline/2-the-job-detail-view.md +3 -1
- package/docs/guide/en/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/en/pipeline/4-picking-an-engine-per-rail.md +25 -11
- package/docs/guide/en/pipeline/5-the-loop-builder.md +7 -2
- package/docs/guide/en/specs/2-add-spec-quick-mode.md +10 -1
- package/docs/guide/es/agents/2-profiles-and-the-balanced-default.md +5 -1
- package/docs/guide/es/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/es/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/es/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/es/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/es/integrations/1-ai-providers.md +30 -18
- package/docs/guide/es/integrations/2-plugins.md +3 -3
- package/docs/guide/es/integrations/6-agent-chat.md +8 -1
- package/docs/guide/es/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/es/pipeline/2-the-job-detail-view.md +3 -1
- package/docs/guide/es/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/es/pipeline/4-picking-an-engine-per-rail.md +15 -13
- package/docs/guide/es/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/es/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/guide/fr/agents/2-profiles-and-the-balanced-default.md +5 -1
- package/docs/guide/fr/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/fr/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/fr/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/fr/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/fr/integrations/1-ai-providers.md +29 -18
- package/docs/guide/fr/integrations/2-plugins.md +3 -3
- package/docs/guide/fr/integrations/6-agent-chat.md +8 -1
- package/docs/guide/fr/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/fr/pipeline/2-the-job-detail-view.md +3 -1
- package/docs/guide/fr/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/fr/pipeline/4-picking-an-engine-per-rail.md +15 -13
- package/docs/guide/fr/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/fr/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/guide/it/agents/2-profiles-and-the-balanced-default.md +5 -1
- package/docs/guide/it/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/it/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/it/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/it/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/it/integrations/1-ai-providers.md +28 -18
- package/docs/guide/it/integrations/2-plugins.md +3 -3
- package/docs/guide/it/integrations/6-agent-chat.md +8 -1
- package/docs/guide/it/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/it/pipeline/2-the-job-detail-view.md +3 -1
- package/docs/guide/it/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/it/pipeline/4-picking-an-engine-per-rail.md +13 -13
- package/docs/guide/it/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/it/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/guide/ja/agents/2-profiles-and-the-balanced-default.md +6 -1
- package/docs/guide/ja/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/ja/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/ja/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/ja/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/ja/integrations/1-ai-providers.md +28 -18
- package/docs/guide/ja/integrations/2-plugins.md +3 -3
- package/docs/guide/ja/integrations/6-agent-chat.md +7 -1
- package/docs/guide/ja/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/ja/pipeline/2-the-job-detail-view.md +3 -1
- package/docs/guide/ja/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/ja/pipeline/4-picking-an-engine-per-rail.md +14 -13
- package/docs/guide/ja/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/ja/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/guide/pt/agents/2-profiles-and-the-balanced-default.md +5 -1
- package/docs/guide/pt/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/pt/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/pt/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/pt/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/pt/integrations/1-ai-providers.md +28 -18
- package/docs/guide/pt/integrations/2-plugins.md +3 -3
- package/docs/guide/pt/integrations/6-agent-chat.md +7 -1
- package/docs/guide/pt/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/pt/pipeline/2-the-job-detail-view.md +2 -1
- package/docs/guide/pt/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/pt/pipeline/4-picking-an-engine-per-rail.md +14 -13
- package/docs/guide/pt/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/pt/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/guide/zh/agents/2-profiles-and-the-balanced-default.md +4 -1
- package/docs/guide/zh/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/zh/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/zh/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/zh/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/zh/integrations/1-ai-providers.md +26 -18
- package/docs/guide/zh/integrations/2-plugins.md +3 -3
- package/docs/guide/zh/integrations/6-agent-chat.md +6 -1
- package/docs/guide/zh/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/zh/pipeline/2-the-job-detail-view.md +2 -1
- package/docs/guide/zh/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/zh/pipeline/4-picking-an-engine-per-rail.md +12 -12
- package/docs/guide/zh/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/zh/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/internals/README.md +4 -1
- package/docs/internals/adding-a-provider.md +32 -28
- package/docs/internals/api-reference.md +37 -13
- package/docs/internals/architecture.md +5 -5
- package/docs/internals/bundled-framework-build-plan.md +3 -3
- package/docs/internals/code-explorer-story.md +4 -0
- package/docs/internals/configuration.md +17 -5
- package/docs/internals/global-artifacts-alignment-contract.md +1 -1
- package/docs/internals/operations-runbook.md +1 -1
- package/docs/internals/profiles.md +37 -45
- package/docs/internals/project-builder.md +16 -0
- package/docs/kimi.md +291 -0
- package/docs/mcp.md +9 -2
- package/docs/platforms/macos.md +17 -7
- package/docs/platforms/windows.md +9 -4
- package/docs/running-pipelines.md +46 -12
- package/docs/terminal.md +1 -1
- package/docs/tracking-cost.md +25 -6
- package/package.json +4 -2
- package/server/dist/agent-chat-manager.js +74 -32
- package/server/dist/agent-chat-router.js +40 -17
- package/server/dist/agent-cwd-manager.js +14 -7
- package/server/dist/agent-generator.js +69 -85
- package/server/dist/agent-mcp-config.js +17 -11
- package/server/dist/agent-operator-prompt.js +29 -20
- package/server/dist/agent-refine-manager.js +58 -38
- package/server/dist/ai-invocations.js +65 -20
- package/server/dist/attachment-manager.js +49 -15
- package/server/dist/blueprint-chat-manager.js +41 -16
- package/server/dist/blueprint-router.js +40 -6
- package/server/dist/chat-manager.js +165 -89
- package/server/dist/code-explorer-router.js +26 -7
- package/server/dist/contract-refine-runner.js +146 -113
- package/server/dist/core-compat.js +57 -2
- package/server/dist/core-package.js +3 -3
- package/server/dist/core-update-manager.js +17 -1
- package/server/dist/db.js +60 -5
- package/server/dist/desktop-analytics.js +44 -6
- package/server/dist/desktop-router.js +127 -11
- package/server/dist/explore-cwd-manager.js +19 -3
- package/server/dist/feature-flags.js +1 -1
- package/server/dist/file-story-manager.js +3 -3
- package/server/dist/file-summary-generator.js +21 -15
- package/server/dist/file-summary-manager.js +6 -6
- package/server/dist/framework-manager.js +19 -2
- package/server/dist/framework-migration.js +27 -12
- package/server/dist/install-config-path.js +1 -1
- package/server/dist/interactive-job-session.js +8 -6
- package/server/dist/loop-command-catalog.js +27 -10
- package/server/dist/loop-executors.js +59 -27
- package/server/dist/loop-factory.js +2 -2
- package/server/dist/loop-run-manager.js +100 -39
- package/server/dist/loops-router.js +1 -1
- package/server/dist/mcp/guide.js +23 -16
- package/server/dist/mcp/tools/agents.js +47 -41
- package/server/dist/mcp/tools/analytics.js +2 -2
- package/server/dist/mcp/tools/loops.js +2 -2
- package/server/dist/mcp/tools/plugins.js +14 -9
- package/server/dist/mcp/tools/rails.js +12 -13
- package/server/dist/mcp/tools/support.js +1 -1
- package/server/dist/mobile/mobile-router.js +5 -1
- package/server/dist/offline-assemble.js +13 -1
- package/server/dist/path-resolver.js +2 -2
- package/server/dist/plugin-manager.js +391 -106
- package/server/dist/plugins/claude-md-mutation.js +5 -4
- package/server/dist/plugins/drift.js +32 -29
- package/server/dist/plugins/rail-integration.js +13 -2
- package/server/dist/plugins/serena/install.js +25 -17
- package/server/dist/plugins/serena/manifest.js +22 -5
- package/server/dist/plugins/serena/verify.js +46 -16
- package/server/dist/plugins-router.js +59 -8
- package/server/dist/profile-manager.js +148 -36
- package/server/dist/profiles-router.js +311 -82
- package/server/dist/project-registry.js +2 -2
- package/server/dist/project-router-chat.js +17 -8
- package/server/dist/project-router-helpers.js +93 -5
- package/server/dist/project-router-jobs.js +76 -12
- package/server/dist/project-router-loop-runs.js +42 -14
- package/server/dist/project-router-settings.js +31 -10
- package/server/dist/project-router-setup.js +4 -5
- package/server/dist/project-router-spending.js +15 -3
- package/server/dist/project-router-tickets.js +187 -181
- package/server/dist/project-router.js +1 -0
- package/server/dist/proposal-manager.js +125 -85
- package/server/dist/providers/claude-adapter.js +14 -0
- package/server/dist/providers/codex-adapter.js +20 -0
- package/server/dist/providers/gemini-adapter.js +14 -0
- package/server/dist/providers/index.js +18 -1
- package/server/dist/providers/kimi-adapter.js +361 -0
- package/server/dist/providers/kimi-skill-prompt.js +332 -0
- package/server/dist/providers/runtime.js +137 -0
- package/server/dist/queue-manager.js +103 -78
- package/server/dist/rail-isolated-launch.js +1 -1
- package/server/dist/rails-router.js +51 -38
- package/server/dist/result-event.js +5 -0
- package/server/dist/schemas/profile.v1.json +3 -4
- package/server/dist/setup-manager.js +222 -101
- package/server/dist/setup-prerequisites.js +7 -1
- package/server/dist/smash-runner.js +104 -57
- package/server/dist/spawn-lifecycle.js +18 -14
- package/server/dist/spec-launcher-manager.js +66 -54
- package/server/dist/spec-models.js +15 -6
- package/server/dist/spending.js +110 -17
- package/server/dist/util/cli-prompt.js +142 -5
- package/server/dist/util/stream-display.js +46 -0
- package/server/dist/workspace-manager.js +100 -37
- package/server/dist/worktree-manager.js +4 -0
- package/server/dist/worktree-overlay.js +12 -3
- package/client/dist/assets/AgentModeCodePane-BAYyFRmY.js +0 -2
- package/client/dist/assets/AgentsPage-DuyJuaFs.js +0 -86
- package/client/dist/assets/AnalyticsPage-BY7gtCx6.js +0 -1
- package/client/dist/assets/CodePage-vyQr40Vl.js +0 -2
- package/client/dist/assets/JobDetailPage-CU4FEnUC.js +0 -1
- package/client/dist/assets/LoopsPage-SqglnXlp.js +0 -1
- package/client/dist/assets/PluginsPage-B89S8o1a.js +0 -2
- package/client/dist/assets/TicketDetailModal-CI47Dq9E.js +0 -14
- package/client/dist/assets/addspec-BXjvkh5q.js +0 -1
- package/client/dist/assets/addspec-C4bwrmpi.js +0 -1
- package/client/dist/assets/addspec-CArHXUDM.js +0 -1
- package/client/dist/assets/addspec-DTKJcXiR.js +0 -1
- package/client/dist/assets/addspec-DbNBbrha.js +0 -1
- package/client/dist/assets/addspec-Rw8uVBC1.js +0 -1
- package/client/dist/assets/addspec-htHGZHOe.js +0 -1
- package/client/dist/assets/addspec-lr_LP6jk.js +0 -1
- package/client/dist/assets/agent-BABjRt0P.js +0 -1
- package/client/dist/assets/agent-BuUpgqVx.js +0 -1
- package/client/dist/assets/agent-CCDZesgU.js +0 -1
- package/client/dist/assets/agent-CVn64zWH.js +0 -1
- package/client/dist/assets/agent-CWeOny0E.js +0 -1
- package/client/dist/assets/agent-DOysmad_.js +0 -1
- package/client/dist/assets/agent-DSsjTBhn.js +0 -1
- package/client/dist/assets/agent-DlZMxfM9.js +0 -1
- package/client/dist/assets/agents-6ZOJXWVk.js +0 -1
- package/client/dist/assets/agents-BZ0XQKjg.js +0 -1
- package/client/dist/assets/agents-BqWklnHR.js +0 -1
- package/client/dist/assets/agents-Bz-thHkB.js +0 -1
- package/client/dist/assets/agents-CJclElx6.js +0 -1
- package/client/dist/assets/agents-D_l9L0VA.js +0 -1
- package/client/dist/assets/agents-DhG7mg6-.js +0 -1
- package/client/dist/assets/agents-u_C0jPB5.js +0 -1
- package/client/dist/assets/agentstudio-BF19hbXj.js +0 -1
- package/client/dist/assets/agentstudio-BIUe1Im2.js +0 -1
- package/client/dist/assets/agentstudio-BLGvxWBx.js +0 -1
- package/client/dist/assets/agentstudio-D9UKiWTj.js +0 -1
- package/client/dist/assets/agentstudio-Drs37nIM.js +0 -1
- package/client/dist/assets/agentstudio-nEnKkcXM.js +0 -1
- package/client/dist/assets/agentstudio-pyFcSBKy.js +0 -1
- package/client/dist/assets/agentstudio-zfTzQh3U.js +0 -1
- package/client/dist/assets/analytics-BC7mwR4s.js +0 -1
- package/client/dist/assets/analytics-BiNg8edP.js +0 -1
- package/client/dist/assets/analytics-C0s_T2GN.js +0 -1
- package/client/dist/assets/analytics-CdsAPAwZ.js +0 -1
- package/client/dist/assets/analytics-Cr1_q-lC.js +0 -1
- package/client/dist/assets/analytics-DZ8Aoq3I.js +0 -1
- package/client/dist/assets/analytics-IEsKZvpW.js +0 -1
- package/client/dist/assets/analytics-SRPf84OT.js +0 -1
- package/client/dist/assets/builder-BH8Fua-g.js +0 -1
- package/client/dist/assets/builder-BoV7DgKd.js +0 -1
- package/client/dist/assets/builder-Cc_Lf79C.js +0 -1
- package/client/dist/assets/builder-CwZ7Bme2.js +0 -1
- package/client/dist/assets/builder-CxW-JFsR.js +0 -1
- package/client/dist/assets/builder-D94lrV7x.js +0 -1
- package/client/dist/assets/builder-cqHpCyGa.js +0 -1
- package/client/dist/assets/code-76gS0IdL.js +0 -1
- package/client/dist/assets/code-BDXEM4ml.js +0 -1
- package/client/dist/assets/code-C1VwxlML.js +0 -1
- package/client/dist/assets/code-CnMZyJpr.js +0 -1
- package/client/dist/assets/code-D20ef9Cf.js +0 -1
- package/client/dist/assets/code-DdOXaWRY.js +0 -1
- package/client/dist/assets/code-Duohuf9l.js +0 -1
- package/client/dist/assets/code-geHmknB9.js +0 -1
- package/client/dist/assets/format-command-Dfa1o2d-.js +0 -1
- package/client/dist/assets/index-Dh9i4CnN.css +0 -2
- package/client/dist/assets/index-F9r8mWP_.js +0 -76
- package/client/dist/assets/jobs-BCtGfgUA.js +0 -1
- package/client/dist/assets/jobs-BHR_EnDI.js +0 -1
- package/client/dist/assets/jobs-BMbZJjJJ.js +0 -1
- package/client/dist/assets/jobs-DzSIR8V6.js +0 -1
- package/client/dist/assets/nav-BRJBxaGt.js +0 -1
- package/client/dist/assets/nav-CAGKv3_F.js +0 -1
- package/client/dist/assets/nav-CNskRJKQ.js +0 -1
- package/client/dist/assets/nav-DY93irBL.js +0 -1
- package/client/dist/assets/nav-DhjvYgqt.js +0 -1
- package/client/dist/assets/nav-Gb-r4wop.js +0 -1
- package/client/dist/assets/nav-TvumAFD2.js +0 -1
- package/client/dist/assets/nav-myFaXRHD.js +0 -1
- package/client/dist/assets/settings-B0ypw9uw.js +0 -1
- package/client/dist/assets/settings-B9ilOSYO.js +0 -1
- package/client/dist/assets/settings-BqlWHRHA.js +0 -1
- package/client/dist/assets/settings-D8dlqt4d.js +0 -1
- package/client/dist/assets/settings-DAZqjX2a.js +0 -1
- package/client/dist/assets/settings-DRh39v1S.js +0 -1
- package/client/dist/assets/settings-Mj1OJbfd.js +0 -1
- package/client/dist/assets/settings-lRReaSYm.js +0 -1
- package/client/dist/assets/setup-3832BJCo.js +0 -1
- package/client/dist/assets/setup-B8J-gECe.js +0 -1
- package/client/dist/assets/setup-BCIOxVsc.js +0 -1
- package/client/dist/assets/setup-BciLj_tU.js +0 -1
- package/client/dist/assets/setup-CTkE3cfh.js +0 -1
- package/client/dist/assets/setup-ajB4rYm-.js +0 -1
- package/client/dist/assets/setup-xFO1wNl0.js +0 -1
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CUSTOM_MODEL_ALIAS_MAX_LENGTH = void 0;
|
|
4
|
+
exports.isSafeCustomModelAlias = isSafeCustomModelAlias;
|
|
5
|
+
exports.isModelAvailableForAdapter = isModelAvailableForAdapter;
|
|
6
|
+
exports.supportsToolPolicy = supportsToolPolicy;
|
|
7
|
+
exports.pureOutputToolPolicy = pureOutputToolPolicy;
|
|
8
|
+
exports.requireToolPolicy = requireToolPolicy;
|
|
9
|
+
exports.reasoningEffortsForModel = reasoningEffortsForModel;
|
|
10
|
+
exports.defaultReasoningEffortForModel = defaultReasoningEffortForModel;
|
|
11
|
+
exports.isReasoningEffortValidForModel = isReasoningEffortValidForModel;
|
|
12
|
+
exports.buildProviderEnv = buildProviderEnv;
|
|
13
|
+
exports.formatProviderCommand = formatProviderCommand;
|
|
14
|
+
exports.buildProviderRepoAccessArgs = buildProviderRepoAccessArgs;
|
|
15
|
+
exports.parseStreamEvents = parseStreamEvents;
|
|
16
|
+
/**
|
|
17
|
+
* Conservative grammar for provider-configured model aliases passed as the
|
|
18
|
+
* value immediately following a CLI model flag (for example Kimi's `-m`).
|
|
19
|
+
*
|
|
20
|
+
* Keep aliases shell-neutral even though Desktop spawns with argv arrays:
|
|
21
|
+
* no leading `-`, whitespace, controls, quoting, or unbounded values. Common
|
|
22
|
+
* provider/model forms such as `kimi-code/k3`, `team/model:v2`, and dotted
|
|
23
|
+
* registry ids remain valid and are preserved byte-for-byte after validation.
|
|
24
|
+
*/
|
|
25
|
+
exports.CUSTOM_MODEL_ALIAS_MAX_LENGTH = 128;
|
|
26
|
+
const SAFE_CUSTOM_MODEL_ALIAS_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._/:-]*$/;
|
|
27
|
+
function isSafeCustomModelAlias(value) {
|
|
28
|
+
return typeof value === 'string'
|
|
29
|
+
&& value.length > 0
|
|
30
|
+
&& value.length <= exports.CUSTOM_MODEL_ALIAS_MAX_LENGTH
|
|
31
|
+
&& SAFE_CUSTOM_MODEL_ALIAS_PATTERN.test(value);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Provider-aware model validation.
|
|
35
|
+
*
|
|
36
|
+
* Catalog entries are always accepted. Off-catalog values are accepted only
|
|
37
|
+
* when the adapter explicitly advertises configured aliases and the value
|
|
38
|
+
* satisfies the safe argv grammar above.
|
|
39
|
+
*/
|
|
40
|
+
function isModelAvailableForAdapter(adapter, value) {
|
|
41
|
+
if (typeof value !== 'string' || value.length === 0)
|
|
42
|
+
return false;
|
|
43
|
+
if (adapter.modelCatalog().some((model) => model.value === value))
|
|
44
|
+
return true;
|
|
45
|
+
return adapter.capabilities.customModelAliases === true
|
|
46
|
+
&& isSafeCustomModelAlias(value);
|
|
47
|
+
}
|
|
48
|
+
/** Whether an adapter natively enforces the requested non-default boundary. */
|
|
49
|
+
function supportsToolPolicy(adapter, policy) {
|
|
50
|
+
return (adapter.capabilities.toolPolicies ?? []).includes(policy);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Safest boundary for a pure-output transform. Prefer a true no-tools mode;
|
|
54
|
+
* read-only is an acceptable fallback when the CLI has no native `none`.
|
|
55
|
+
* `null` means the action must fail closed before a child is spawned.
|
|
56
|
+
*/
|
|
57
|
+
function pureOutputToolPolicy(adapter) {
|
|
58
|
+
if (supportsToolPolicy(adapter, 'none'))
|
|
59
|
+
return 'none';
|
|
60
|
+
if (supportsToolPolicy(adapter, 'read-only'))
|
|
61
|
+
return 'read-only';
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
/** Throw a stable, provider-identifying error for an unsupported boundary. */
|
|
65
|
+
function requireToolPolicy(adapter, policy) {
|
|
66
|
+
if (!supportsToolPolicy(adapter, policy)) {
|
|
67
|
+
throw new Error(`provider_tool_policy_unsupported:${adapter.id}:${policy}`);
|
|
68
|
+
}
|
|
69
|
+
return policy;
|
|
70
|
+
}
|
|
71
|
+
/** Exact effort tiers accepted by a provider for the effective model. */
|
|
72
|
+
function reasoningEffortsForModel(adapter, model) {
|
|
73
|
+
if (adapter.capabilities.supportsReasoningEffort !== true)
|
|
74
|
+
return [];
|
|
75
|
+
return adapter.reasoningEffortsForModel?.(model)
|
|
76
|
+
?? adapter.capabilities.reasoningEfforts
|
|
77
|
+
?? [];
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Safe app default for a model: medium where available, otherwise high where
|
|
81
|
+
* available, then the first tier. Kimi K3's native default is high; preferring
|
|
82
|
+
* it over low keeps an omitted Desktop selection semantically identical to the
|
|
83
|
+
* upstream CLI without changing the ordered selector catalog.
|
|
84
|
+
*/
|
|
85
|
+
function defaultReasoningEffortForModel(adapter, model) {
|
|
86
|
+
const efforts = reasoningEffortsForModel(adapter, model);
|
|
87
|
+
if (efforts.includes('medium'))
|
|
88
|
+
return 'medium';
|
|
89
|
+
if (efforts.includes('high'))
|
|
90
|
+
return 'high';
|
|
91
|
+
return efforts[0];
|
|
92
|
+
}
|
|
93
|
+
/** Narrow an untrusted effort value against the model-specific catalog. */
|
|
94
|
+
function isReasoningEffortValidForModel(adapter, model, value) {
|
|
95
|
+
return typeof value === 'string'
|
|
96
|
+
&& reasoningEffortsForModel(adapter, model).includes(value);
|
|
97
|
+
}
|
|
98
|
+
/** Merge provider runtime overrides without mutating the caller or process.env. */
|
|
99
|
+
function buildProviderEnv(adapter, opts, baseEnv = process.env) {
|
|
100
|
+
const merged = { ...baseEnv };
|
|
101
|
+
for (const [key, value] of Object.entries(adapter.buildEnv?.(opts) ?? {})) {
|
|
102
|
+
// Windows treats environment names case-insensitively. Remove every
|
|
103
|
+
// inherited casing before applying an adapter-owned override/deletion so
|
|
104
|
+
// `kimi_code_experimental_flag` cannot survive a scrub of the canonical
|
|
105
|
+
// `KIMI_CODE_EXPERIMENTAL_FLAG` (and likewise for reasoning effort).
|
|
106
|
+
const foldedKey = key.toLowerCase();
|
|
107
|
+
for (const inheritedKey of Object.keys(merged)) {
|
|
108
|
+
if (inheritedKey.toLowerCase() === foldedKey)
|
|
109
|
+
delete merged[inheritedKey];
|
|
110
|
+
}
|
|
111
|
+
if (value !== undefined)
|
|
112
|
+
merged[key] = value;
|
|
113
|
+
}
|
|
114
|
+
return merged;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Provider-native Core command/prompt, identity when no hook is declared.
|
|
118
|
+
* `cwd` points at the provider artifact root for adapters that must load the
|
|
119
|
+
* headless workflow content before spawn.
|
|
120
|
+
*/
|
|
121
|
+
function formatProviderCommand(adapter, command, cwd, sessionId) {
|
|
122
|
+
return adapter.formatCoreCommand?.(command, cwd, sessionId) ?? command;
|
|
123
|
+
}
|
|
124
|
+
/** Flatten provider-native access flags for additional repository roots. */
|
|
125
|
+
function buildProviderRepoAccessArgs(adapter, paths) {
|
|
126
|
+
return adapter.buildRepoAccessArgs?.(paths) ?? [];
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Normalize an adapter's single-or-batch parser contract into an event list.
|
|
130
|
+
* One JSONL record may contain assistant text and several tool calls (Kimi).
|
|
131
|
+
*/
|
|
132
|
+
function parseStreamEvents(adapter, line) {
|
|
133
|
+
const parsed = adapter.parseStreamLine(line);
|
|
134
|
+
if (parsed == null)
|
|
135
|
+
return [];
|
|
136
|
+
return Array.isArray(parsed) ? parsed : [parsed];
|
|
137
|
+
}
|
|
@@ -24,6 +24,7 @@ const file_provenance_1 = require("./file-provenance");
|
|
|
24
24
|
const result_event_1 = require("./result-event");
|
|
25
25
|
const crypto_1 = require("crypto");
|
|
26
26
|
const providers_1 = require("./providers");
|
|
27
|
+
const runtime_1 = require("./providers/runtime");
|
|
27
28
|
const codex_otel_bridge_1 = require("./codex-otel-bridge");
|
|
28
29
|
const db_1 = require("./db");
|
|
29
30
|
const interactive_job_session_1 = require("./interactive-job-session");
|
|
@@ -2155,7 +2156,7 @@ class QueueManager {
|
|
|
2155
2156
|
// true forces interactive where capable, undefined = default ON. Derived
|
|
2156
2157
|
// HERE (spawn time), not at enqueue, so legacy queued rows with no explicit
|
|
2157
2158
|
// selection still receive the current default after restart.
|
|
2158
|
-
const isFreestyle = adapter.
|
|
2159
|
+
const isFreestyle = adapter.capabilities.freestyle === true && exports.FREESTYLE_COMMAND_RE.test(commandToRun);
|
|
2159
2160
|
const interactiveOverride = this._jobInteractiveSelection.get(jobId);
|
|
2160
2161
|
const spawnInteractive = (0, feature_flags_1.isInteractiveJobsEnabled)() &&
|
|
2161
2162
|
adapter.capabilities.persistentStdin &&
|
|
@@ -2259,36 +2260,26 @@ class QueueManager {
|
|
|
2259
2260
|
// claude slash command — propose-spec, implement, batch-implement,
|
|
2260
2261
|
// explore-spec, retry, …). This is the rail equivalent of the
|
|
2261
2262
|
// user typing `$implement #1 --yes` themselves in `codex`.
|
|
2262
|
-
// Freestyle (Claude
|
|
2263
|
-
// pre-prompt + spec text directly as the
|
|
2264
|
-
//
|
|
2265
|
-
//
|
|
2263
|
+
// Freestyle (capability-gated; currently Claude and Kimi): skip the slash
|
|
2264
|
+
// command entirely and send the pre-prompt + spec text directly as the
|
|
2265
|
+
// prompt. The server route rejects adapters that do not advertise
|
|
2266
|
+
// `freestyle` before this point.
|
|
2266
2267
|
// (`isFreestyle` itself is resolved above, before the systemAppend build.)
|
|
2267
2268
|
const railPrompt = isFreestyle
|
|
2268
2269
|
? this._buildFreestylePrompt(commandToRun)
|
|
2269
|
-
: adapter
|
|
2270
|
-
? commandToRun.replace(/^\/(specrails|sr):([\w-]+)/, '$$$2')
|
|
2271
|
-
: commandToRun;
|
|
2270
|
+
: (0, runtime_1.formatProviderCommand)(adapter, commandToRun, execution.cwd);
|
|
2272
2271
|
// Per-job model override (consumed once) takes precedence — used by the
|
|
2273
2272
|
// freestyle model picker so the user can choose haiku/sonnet/opus per launch.
|
|
2274
2273
|
const modelOverride = this._jobModelSelection.get(jobId);
|
|
2275
|
-
const
|
|
2276
|
-
?
|
|
2277
|
-
:
|
|
2278
|
-
? (0, db_1.getProjectSettings)(this._db).orchestratorModel
|
|
2279
|
-
: (this._resolvedModel ?? adapter.defaultModel());
|
|
2274
|
+
const fallbackRailModel = adapter.id === 'claude' && this._db
|
|
2275
|
+
? (0, db_1.getProjectSettings)(this._db).orchestratorModel
|
|
2276
|
+
: (this._resolvedModel ?? adapter.defaultModel());
|
|
2280
2277
|
// Relocate-artifacts: when relocated, claude is spawned from the workspace
|
|
2281
2278
|
// so add `--add-dir <repoDir>` so its tools can still reach repo files by
|
|
2282
2279
|
// absolute path. (gemini/codex get env-only tweaks at spawn time below.)
|
|
2283
|
-
const railExtraArgs = execution.relocated
|
|
2284
|
-
?
|
|
2285
|
-
:
|
|
2286
|
-
const args = adapter.buildArgs('rail-job', {
|
|
2287
|
-
prompt: railPrompt,
|
|
2288
|
-
systemPrompt: systemAppend || undefined,
|
|
2289
|
-
model: railModel,
|
|
2290
|
-
extraArgs: railExtraArgs,
|
|
2291
|
-
});
|
|
2280
|
+
const railExtraArgs = execution.relocated
|
|
2281
|
+
? (0, runtime_1.buildProviderRepoAccessArgs)(adapter, [execution.repoDir])
|
|
2282
|
+
: [];
|
|
2292
2283
|
// Resolve agent profile (if any) and snapshot per-job before spawn.
|
|
2293
2284
|
// Super mode only (projectId + projectSlug + cwd all present).
|
|
2294
2285
|
// Skipped when the adapter does not honour `SPECRAILS_PROFILE_PATH` AND
|
|
@@ -2299,6 +2290,7 @@ class QueueManager {
|
|
|
2299
2290
|
// tracked in OpenSpec change task §13.
|
|
2300
2291
|
let profileSnapshotPath = null;
|
|
2301
2292
|
let profileName = null;
|
|
2293
|
+
let profileOrchestratorModel = null;
|
|
2302
2294
|
if (adapter.capabilities.profileEnvSupport && this._projectId && this._projectSlug && this._cwd) {
|
|
2303
2295
|
try {
|
|
2304
2296
|
const selection = this._jobProfileSelection.get(jobId); // undefined|null|string
|
|
@@ -2312,6 +2304,7 @@ class QueueManager {
|
|
|
2312
2304
|
if (resolved) {
|
|
2313
2305
|
profileSnapshotPath = snapshotForJob(this._projectSlug, jobId, resolved);
|
|
2314
2306
|
profileName = resolved.name;
|
|
2307
|
+
profileOrchestratorModel = resolved.profile.orchestrator.model;
|
|
2315
2308
|
if (this._db) {
|
|
2316
2309
|
persistJobProfile(this._db, jobId, resolved);
|
|
2317
2310
|
}
|
|
@@ -2324,6 +2317,17 @@ class QueueManager {
|
|
|
2324
2317
|
console.warn(`[queue-manager] profile resolution failed for job ${jobId}: ${err.message}`);
|
|
2325
2318
|
}
|
|
2326
2319
|
}
|
|
2320
|
+
// One authoritative model for both transports:
|
|
2321
|
+
// explicit per-job override > resolved profile orchestrator > project/default.
|
|
2322
|
+
// resolveProfile validates the profile model against this adapter's catalog.
|
|
2323
|
+
const railModel = modelOverride ?? profileOrchestratorModel ?? fallbackRailModel;
|
|
2324
|
+
const railSpawnOptions = {
|
|
2325
|
+
prompt: railPrompt,
|
|
2326
|
+
systemPrompt: systemAppend || undefined,
|
|
2327
|
+
model: railModel,
|
|
2328
|
+
extraArgs: railExtraArgs.length > 0 ? railExtraArgs : undefined,
|
|
2329
|
+
};
|
|
2330
|
+
const args = adapter.buildArgs('rail-job', railSpawnOptions);
|
|
2327
2331
|
// Read pipelineTelemetryEnabled at spawn time (not constructor time) so
|
|
2328
2332
|
// toggling the setting takes effect on the next job without restarting.
|
|
2329
2333
|
// OTEL env injection is gated on `adapter.capabilities.nativeOtelEnv`:
|
|
@@ -2369,10 +2373,9 @@ class QueueManager {
|
|
|
2369
2373
|
// or timed out. Degraded does NOT block spawn — rail proceeds, UI gets
|
|
2370
2374
|
// a `plugin.degraded` event so the user can reinstall.
|
|
2371
2375
|
//
|
|
2372
|
-
//
|
|
2373
|
-
//
|
|
2374
|
-
//
|
|
2375
|
-
// spawns cleanly without errors.
|
|
2376
|
+
// Project-json providers (Claude, Kimi, Gemini) resolve only the plugin
|
|
2377
|
+
// installations scoped to this rail's effective provider. CLI-managed MCP
|
|
2378
|
+
// registries remain outside this snapshot path.
|
|
2376
2379
|
let pluginActive = [];
|
|
2377
2380
|
let pluginDegraded = [];
|
|
2378
2381
|
let pluginSnapshotPath = null;
|
|
@@ -2386,7 +2389,7 @@ class QueueManager {
|
|
|
2386
2389
|
snapshotter = pluginIntegration.snapshotPluginsForJob;
|
|
2387
2390
|
}
|
|
2388
2391
|
// Relocated ⇒ `.mcp.json`/plugin state live in the workspace (execution.cwd).
|
|
2389
|
-
const resolution = await resolver(execution.cwd, this._projectId, jobId);
|
|
2392
|
+
const resolution = await resolver(execution.cwd, this._projectId, jobId, adapter.id, this._adapter.id, this._projectSlug);
|
|
2390
2393
|
// shutdown() may have run while plugin verification was awaiting. Never
|
|
2391
2394
|
// snapshot, broadcast, or spawn for a manager generation that no longer
|
|
2392
2395
|
// owns this active slot.
|
|
@@ -2405,6 +2408,7 @@ class QueueManager {
|
|
|
2405
2408
|
type: 'plugin.degraded',
|
|
2406
2409
|
projectId: this._projectId,
|
|
2407
2410
|
name: d.name,
|
|
2411
|
+
providerId: adapter.id,
|
|
2408
2412
|
reason: d.reason,
|
|
2409
2413
|
jobId,
|
|
2410
2414
|
timestamp: new Date().toISOString(),
|
|
@@ -2525,7 +2529,7 @@ class QueueManager {
|
|
|
2525
2529
|
console.warn(`[queue-manager] provenance snapshot failed: ${err.message}`);
|
|
2526
2530
|
}
|
|
2527
2531
|
}
|
|
2528
|
-
const
|
|
2532
|
+
const interactiveSpawnOptions = {
|
|
2529
2533
|
// chat-stream feeds the prompt over stdin per-turn, so the argv `prompt`
|
|
2530
2534
|
// is unused — pass empty to satisfy the shared SpawnOptions shape.
|
|
2531
2535
|
prompt: '',
|
|
@@ -2538,9 +2542,10 @@ class QueueManager {
|
|
|
2538
2542
|
model: railModel,
|
|
2539
2543
|
extraArgs: !isFreestyle && systemAppend
|
|
2540
2544
|
? ['--append-system-prompt', systemAppend, ...(railExtraArgs ?? [])]
|
|
2541
|
-
: railExtraArgs,
|
|
2542
|
-
}
|
|
2543
|
-
|
|
2545
|
+
: (railExtraArgs.length > 0 ? railExtraArgs : undefined),
|
|
2546
|
+
};
|
|
2547
|
+
const interactiveArgs = adapter.buildArgs('chat-stream', interactiveSpawnOptions);
|
|
2548
|
+
this._startInteractiveJob(jobId, job, adapter, { binary, args: interactiveArgs, cwd: spawnCwd, env: (0, runtime_1.buildProviderEnv)(adapter, interactiveSpawnOptions, spawnEnv) }, railPrompt, interactiveSettleMode);
|
|
2544
2549
|
return;
|
|
2545
2550
|
}
|
|
2546
2551
|
// Code-Explorer pre-spawn snapshot. Captures the working-tree state via
|
|
@@ -2579,7 +2584,7 @@ class QueueManager {
|
|
|
2579
2584
|
this._consumePendingSelections(jobId);
|
|
2580
2585
|
// spawnAiCli reroutes multi-line argv values through stdin on Windows.
|
|
2581
2586
|
const child = (0, cli_prompt_1.spawnAiCli)(binary, args, {
|
|
2582
|
-
env: spawnEnv,
|
|
2587
|
+
env: (0, runtime_1.buildProviderEnv)(adapter, railSpawnOptions, spawnEnv),
|
|
2583
2588
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
2584
2589
|
cwd: spawnCwd,
|
|
2585
2590
|
});
|
|
@@ -2729,13 +2734,13 @@ class QueueManager {
|
|
|
2729
2734
|
// alongside the raw event persistence below, NOT in place of it: the
|
|
2730
2735
|
// raw event log is what feeds the live Job Detail UI and the
|
|
2731
2736
|
// telemetry export ZIP for non-bridge providers.
|
|
2732
|
-
const
|
|
2733
|
-
|
|
2737
|
+
const adapterEventsForLine = (0, runtime_1.parseStreamEvents)(adapter, line);
|
|
2738
|
+
for (const adapterEv of adapterEventsForLine) {
|
|
2734
2739
|
adapterEvents.push(adapterEv);
|
|
2735
2740
|
otelBridge?.consumeEvent(adapterEv);
|
|
2736
2741
|
}
|
|
2737
2742
|
if (parsed) {
|
|
2738
|
-
const eventType = parsed.type ?? 'unknown';
|
|
2743
|
+
const eventType = parsed.type ?? parsed.role ?? 'unknown';
|
|
2739
2744
|
const persisted = persistEvent({
|
|
2740
2745
|
event_type: eventType,
|
|
2741
2746
|
source: 'stdout',
|
|
@@ -2774,8 +2779,9 @@ class QueueManager {
|
|
|
2774
2779
|
// For adapters whose stream is JSONL (claude, codex), a non-parseable
|
|
2775
2780
|
// line is unexpected noise. For future plain-text adapters this is
|
|
2776
2781
|
// their normal output. emitLine surfaces it either way.
|
|
2777
|
-
|
|
2778
|
-
|
|
2782
|
+
const textEvent = adapterEventsForLine.find((event) => event.kind === 'text-delta');
|
|
2783
|
+
if (textEvent?.kind === 'text-delta') {
|
|
2784
|
+
emitLine('stdout', textEvent.text);
|
|
2779
2785
|
}
|
|
2780
2786
|
else {
|
|
2781
2787
|
emitLine('stdout', line);
|
|
@@ -2878,6 +2884,12 @@ class QueueManager {
|
|
|
2878
2884
|
const wasZombie = this._zombieJobs.has(jobId);
|
|
2879
2885
|
const wasCanceling = this._cancelingJobs.has(jobId);
|
|
2880
2886
|
const wasPersistenceFailed = this._persistenceFailedJobs.has(jobId);
|
|
2887
|
+
// A provider may report a semantic turn failure while its CLI still exits
|
|
2888
|
+
// successfully (code 0). AdapterEvent is the provider-neutral contract for
|
|
2889
|
+
// that condition, so do not let the process exit code relabel it completed.
|
|
2890
|
+
// Cancellation/zombie outcomes retain precedence because they describe the
|
|
2891
|
+
// user/manager-owned termination of the whole job.
|
|
2892
|
+
const providerReportedError = adapterEvents.some((event) => event.kind === 'error');
|
|
2881
2893
|
let finalStatus;
|
|
2882
2894
|
if (wasPersistenceFailed) {
|
|
2883
2895
|
finalStatus = 'failed';
|
|
@@ -2888,15 +2900,26 @@ class QueueManager {
|
|
|
2888
2900
|
else if (wasCanceling) {
|
|
2889
2901
|
finalStatus = 'canceled';
|
|
2890
2902
|
}
|
|
2903
|
+
else if (providerReportedError) {
|
|
2904
|
+
finalStatus = 'failed';
|
|
2905
|
+
}
|
|
2891
2906
|
else if (code === 0) {
|
|
2892
2907
|
finalStatus = 'completed';
|
|
2893
2908
|
}
|
|
2894
2909
|
else {
|
|
2895
2910
|
finalStatus = 'failed';
|
|
2896
2911
|
}
|
|
2912
|
+
const finishedAt = new Date().toISOString();
|
|
2913
|
+
const startedAtMs = job.startedAt ? new Date(job.startedAt).getTime() : Number.NaN;
|
|
2914
|
+
const finishedAtMs = new Date(finishedAt).getTime();
|
|
2915
|
+
const wallDurationMs = Number.isFinite(startedAtMs)
|
|
2916
|
+
? Math.max(0, finishedAtMs - startedAtMs)
|
|
2917
|
+
: undefined;
|
|
2897
2918
|
// Adapter-driven finalisation must happen before staging so the immutable
|
|
2898
|
-
// intent owns the same usage that lands on jobs and ai_invocations.
|
|
2899
|
-
|
|
2919
|
+
// intent owns the same usage that lands on jobs and ai_invocations. The
|
|
2920
|
+
// manager wall clock fills providers such as Kimi that emit no duration;
|
|
2921
|
+
// a provider-native duration retains precedence inside the finaliser.
|
|
2922
|
+
const { result: normalised, estimated } = (0, result_event_1.finaliseInvocationResult)(adapter, adapterEvents, { fallbackModel: spawnedModel, durationMs: wallDurationMs });
|
|
2900
2923
|
const tokenData = lastResultEvent || adapterEvents.length > 0
|
|
2901
2924
|
? {
|
|
2902
2925
|
tokens_in: normalised.tokens_in,
|
|
@@ -2912,7 +2935,6 @@ class QueueManager {
|
|
|
2912
2935
|
session_id: normalised.session_id,
|
|
2913
2936
|
}
|
|
2914
2937
|
: {};
|
|
2915
|
-
const finishedAt = new Date().toISOString();
|
|
2916
2938
|
const invStatus = finalStatus === 'completed'
|
|
2917
2939
|
? 'success'
|
|
2918
2940
|
: (finalStatus === 'canceled' || finalStatus === 'zombie_terminated')
|
|
@@ -3765,11 +3787,11 @@ class QueueManager {
|
|
|
3765
3787
|
}
|
|
3766
3788
|
const parse = (row) => {
|
|
3767
3789
|
try {
|
|
3768
|
-
return
|
|
3790
|
+
return (0, runtime_1.parseStreamEvents)(adapter, row.payload);
|
|
3769
3791
|
}
|
|
3770
3792
|
catch (err) {
|
|
3771
3793
|
console.warn(`[queue-manager] ignored malformed durable event for ${jobId}:`, err);
|
|
3772
|
-
return
|
|
3794
|
+
return [];
|
|
3773
3795
|
}
|
|
3774
3796
|
};
|
|
3775
3797
|
const makeUsageAccumulator = () => {
|
|
@@ -3844,18 +3866,19 @@ class QueueManager {
|
|
|
3844
3866
|
ORDER BY seq, id
|
|
3845
3867
|
`).iterate(jobId);
|
|
3846
3868
|
for (const row of events) {
|
|
3847
|
-
const event
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3869
|
+
for (const event of parse(row)) {
|
|
3870
|
+
if (event.kind === 'result') {
|
|
3871
|
+
const finalised = (0, result_event_1.finaliseInvocationResult)(adapter, [event], {
|
|
3872
|
+
fallbackModel: fallbackModel ?? undefined,
|
|
3873
|
+
});
|
|
3874
|
+
lastValidResult = {
|
|
3875
|
+
...finalised,
|
|
3876
|
+
result: sanitizeRecoveredResult(finalised.result),
|
|
3877
|
+
};
|
|
3878
|
+
}
|
|
3879
|
+
else {
|
|
3880
|
+
accumulator.add(event, row.seq);
|
|
3881
|
+
}
|
|
3859
3882
|
}
|
|
3860
3883
|
}
|
|
3861
3884
|
const fallback = accumulator.result();
|
|
@@ -3905,27 +3928,28 @@ class QueueManager {
|
|
|
3905
3928
|
ORDER BY seq, id
|
|
3906
3929
|
`).iterate(jobId);
|
|
3907
3930
|
for (const row of resultRows) {
|
|
3908
|
-
const event
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3931
|
+
for (const event of parse(row)) {
|
|
3932
|
+
if (event.kind !== 'result')
|
|
3933
|
+
continue;
|
|
3934
|
+
const finalised = (0, result_event_1.finaliseInvocationResult)(adapter, [event], { fallbackModel: model });
|
|
3935
|
+
const result = sanitizeRecoveredResult(finalised.result);
|
|
3936
|
+
totals.tokens_in += result.tokens_in ?? 0;
|
|
3937
|
+
totals.tokens_out += result.tokens_out ?? 0;
|
|
3938
|
+
totals.tokens_cache_read += result.tokens_cache_read ?? 0;
|
|
3939
|
+
totals.tokens_cache_create += result.tokens_cache_create ?? 0;
|
|
3940
|
+
const cumulativeCost = result.total_cost_usd ?? baselineCost;
|
|
3941
|
+
totals.total_cost_usd += Math.max(0, cumulativeCost - baselineCost);
|
|
3942
|
+
baselineCost = cumulativeCost;
|
|
3943
|
+
const cumulativeTurns = result.num_turns ?? (baselineTurns + 1);
|
|
3944
|
+
totals.num_turns += Math.max(0, cumulativeTurns - baselineTurns);
|
|
3945
|
+
baselineTurns = cumulativeTurns;
|
|
3946
|
+
totals.duration_ms += result.duration_ms ?? 0;
|
|
3947
|
+
totals.duration_api_ms += result.duration_api_ms ?? 0;
|
|
3948
|
+
model = result.model ?? model;
|
|
3949
|
+
sessionId = result.session_id ?? sessionId;
|
|
3950
|
+
estimated = estimated || finalised.estimated;
|
|
3951
|
+
lastResultSeq = row.seq;
|
|
3952
|
+
}
|
|
3929
3953
|
}
|
|
3930
3954
|
const tail = makeUsageAccumulator();
|
|
3931
3955
|
const tailRows = db.prepare(`
|
|
@@ -3934,9 +3958,10 @@ class QueueManager {
|
|
|
3934
3958
|
ORDER BY seq, id
|
|
3935
3959
|
`).iterate(jobId, lastResultSeq);
|
|
3936
3960
|
for (const row of tailRows) {
|
|
3937
|
-
const event
|
|
3938
|
-
|
|
3939
|
-
|
|
3961
|
+
for (const event of parse(row)) {
|
|
3962
|
+
if (event.kind !== 'result')
|
|
3963
|
+
tail.add(event, row.seq);
|
|
3964
|
+
}
|
|
3940
3965
|
}
|
|
3941
3966
|
const tailUsage = tail.result();
|
|
3942
3967
|
const tailResult = tailUsage.result;
|
|
@@ -1479,7 +1479,7 @@ function recoveryOverlayExcludes(repoDir, worktreePath) {
|
|
|
1479
1479
|
return false;
|
|
1480
1480
|
if (value === worktree_overlay_1.OVERLAY_MANIFEST)
|
|
1481
1481
|
return true;
|
|
1482
|
-
if (!/^\.(?:claude|codex|gemini)\/[^/]+(?:\/[^/]+)*$/.test(value))
|
|
1482
|
+
if (!/^\.(?:claude|codex|gemini|kimi-code)\/[^/]+(?:\/[^/]+)*$/.test(value))
|
|
1483
1483
|
return false;
|
|
1484
1484
|
try {
|
|
1485
1485
|
// Manifest contents are worktree-writable and therefore not authority.
|
|
@@ -32,12 +32,6 @@ const repo_lock_1 = require("./repo-lock");
|
|
|
32
32
|
const agent_chat_registry_1 = require("./agent-chat-registry");
|
|
33
33
|
const process_admission_1 = require("./process-admission");
|
|
34
34
|
const VALID_MODES = new Set(['implement', 'batch-implement', 'freestyle', 'loop']);
|
|
35
|
-
// Models the freestyle picker exposes (Claude aliases). Mirrors the client
|
|
36
|
-
// RailModelSelector options and the project-router orchestrator-model allow-list.
|
|
37
|
-
const VALID_FREESTYLE_MODELS = new Set(['haiku', 'sonnet', 'opus', 'fable']);
|
|
38
|
-
// Reasoning-effort tiers a loop launch may request (mirrors the client selector
|
|
39
|
-
// + the provider adapters' supported values).
|
|
40
|
-
const VALID_REASONING_EFFORTS = new Set(['low', 'medium', 'high']);
|
|
41
35
|
function prDeliveryContinuesTickets(delivery, ticketIds) {
|
|
42
36
|
if (delivery.decision !== 'pr_draft' && delivery.decision !== 'pr_ready')
|
|
43
37
|
return false;
|
|
@@ -446,15 +440,7 @@ function createRailsRouter() {
|
|
|
446
440
|
res.status(400).json({ error: 'target_pr_requires_pr_mode', detail: 'targetPrNumber requires loops and PR delivery to be enabled' });
|
|
447
441
|
return;
|
|
448
442
|
}
|
|
449
|
-
//
|
|
450
|
-
// Ignored for non-freestyle modes (they use the orchestrator model).
|
|
451
|
-
if (mode === 'freestyle' && model !== undefined && model !== null) {
|
|
452
|
-
if (typeof model !== 'string' || !VALID_FREESTYLE_MODELS.has(model)) {
|
|
453
|
-
res.status(400).json({ error: 'model must be one of: haiku, sonnet, opus, fable' });
|
|
454
|
-
return;
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
// Interactive in-job chat is ON by default for EVERY claude job — the
|
|
443
|
+
// Interactive in-job chat is ON by default for providers that expose the
|
|
458
444
|
// spawn-time gate in QueueManager (kill-switch + persistent-stdin
|
|
459
445
|
// capability) decides, so the launch no longer passes an explicit flag. A
|
|
460
446
|
// legacy `interactive` body param is accepted and ignored (wire compat).
|
|
@@ -503,21 +489,33 @@ function createRailsRouter() {
|
|
|
503
489
|
// Only pass a provider override when one was actually requested (keeps
|
|
504
490
|
// single-provider rails on the legacy code path).
|
|
505
491
|
const railProvider = requestedEngine ? engineCheck.provider : undefined;
|
|
506
|
-
// Freestyle bypasses the OpenSpec pipeline and hands the raw spec to
|
|
507
|
-
//
|
|
508
|
-
if (mode === 'freestyle' && engineCheck.provider !==
|
|
509
|
-
res.status(400).json({ error:
|
|
492
|
+
// Freestyle bypasses the OpenSpec pipeline and hands the raw spec to a
|
|
493
|
+
// provider that explicitly advertises autonomous freestyle support.
|
|
494
|
+
if (mode === 'freestyle' && (0, providers_1.getAdapter)(engineCheck.provider).capabilities.freestyle !== true) {
|
|
495
|
+
res.status(400).json({ error: `Freestyle is not supported by provider '${engineCheck.provider}'` });
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
// Freestyle is provider-owned. Validate its optional model against the
|
|
499
|
+
// selected engine instead of a Claude-only alias list.
|
|
500
|
+
if (mode === 'freestyle' &&
|
|
501
|
+
model !== undefined &&
|
|
502
|
+
model !== null &&
|
|
503
|
+
(typeof model !== 'string' || !(0, spec_models_1.isValidModelForProvider)(model, engineCheck.provider))) {
|
|
504
|
+
res.status(400).json({
|
|
505
|
+
error: `model is not valid for provider "${engineCheck.provider}"`,
|
|
506
|
+
allowed: (0, spec_models_1.getModelsForProvider)(engineCheck.provider),
|
|
507
|
+
});
|
|
510
508
|
return;
|
|
511
509
|
}
|
|
512
510
|
// Profile selection precedence: explicit body param > stored rail profile > default resolution.
|
|
513
|
-
// `null` in the body explicitly forces legacy mode.
|
|
514
|
-
//
|
|
511
|
+
// `null` in the body explicitly forces legacy mode. Providers without
|
|
512
|
+
// profile support run in legacy mode.
|
|
515
513
|
let resolvedProfile;
|
|
516
514
|
if (mode === 'freestyle') {
|
|
517
515
|
// Freestyle runs no agent pipeline, so profiles do not apply.
|
|
518
516
|
resolvedProfile = null;
|
|
519
517
|
}
|
|
520
|
-
else if (railProvider && railProvider !==
|
|
518
|
+
else if (railProvider && (0, providers_1.getAdapter)(railProvider).capabilities.profiles !== true) {
|
|
521
519
|
resolvedProfile = null;
|
|
522
520
|
}
|
|
523
521
|
else if (profileName === null) {
|
|
@@ -573,27 +571,44 @@ function createRailsRouter() {
|
|
|
573
571
|
loopGraph = loop.graph;
|
|
574
572
|
loopName = loop.name;
|
|
575
573
|
}
|
|
574
|
+
const loopProvider = railProvider ?? c.project.provider ?? 'claude';
|
|
575
|
+
const loopAdapter = (0, providers_1.getAdapter)(loopProvider);
|
|
576
|
+
if (loopGraph.nodes.some((node) => node.type === 'decider')
|
|
577
|
+
&& !(0, providers_1.supportsToolPolicy)(loopAdapter, 'read-only')) {
|
|
578
|
+
res.status(409).json({
|
|
579
|
+
code: 'provider_tool_policy_unsupported',
|
|
580
|
+
provider: loopProvider,
|
|
581
|
+
requiredPolicy: 'read-only',
|
|
582
|
+
error: `Provider '${loopProvider}' cannot run Loop Deciders because its headless CLI ` +
|
|
583
|
+
'does not enforce a read-only tool policy.',
|
|
584
|
+
});
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
if (typeof model === 'string' && model && !(0, spec_models_1.isValidModelForProvider)(model, loopProvider)) {
|
|
588
|
+
res.status(400).json({ error: `model is not valid for provider "${loopProvider}"`, allowed: (0, spec_models_1.getModelsForProvider)(loopProvider) });
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
const loopModel = typeof model === 'string' && model ? model : loopAdapter.defaultModel();
|
|
576
592
|
let effort;
|
|
577
593
|
if (reasoning_effort !== undefined && reasoning_effort !== null) {
|
|
578
|
-
|
|
579
|
-
|
|
594
|
+
const allowed = (0, providers_1.reasoningEffortsForModel)(loopAdapter, loopModel);
|
|
595
|
+
if (typeof reasoning_effort !== 'string' ||
|
|
596
|
+
!allowed.includes(reasoning_effort)) {
|
|
597
|
+
res.status(400).json({
|
|
598
|
+
error: `reasoning_effort is not valid for provider "${loopProvider}" and model "${loopModel}"`,
|
|
599
|
+
allowed,
|
|
600
|
+
});
|
|
580
601
|
return;
|
|
581
602
|
}
|
|
582
603
|
effort = reasoning_effort;
|
|
583
604
|
}
|
|
584
|
-
|
|
585
|
-
if (typeof model === 'string' && model && !(0, spec_models_1.isValidModelForProvider)(model, loopProvider)) {
|
|
586
|
-
res.status(400).json({ error: `model is not valid for provider "${loopProvider}"`, allowed: (0, spec_models_1.getModelsForProvider)(loopProvider) });
|
|
587
|
-
return;
|
|
588
|
-
}
|
|
589
|
-
const loopModel = typeof model === 'string' && model ? model : (0, providers_1.getAdapter)(loopProvider).defaultModel();
|
|
590
|
-
// The loop's command(s) declare ticket scope + claude-only-ness.
|
|
605
|
+
// The loop's command(s) declare ticket scope + required capabilities.
|
|
591
606
|
const promptsText = loopGraph.nodes
|
|
592
607
|
.filter((n) => n.type === 'ai-step')
|
|
593
608
|
.map((n) => String(n.data?.prompt ?? ''))
|
|
594
609
|
.join('\n');
|
|
595
|
-
if ((0, loop_command_catalog_1.
|
|
596
|
-
res.status(400).json({ error:
|
|
610
|
+
if ((0, loop_command_catalog_1.referencesUnsupportedProviderCommand)(promptsText, loopProvider)) {
|
|
611
|
+
res.status(400).json({ error: `This loop uses a command unsupported by provider '${loopProvider}'` });
|
|
597
612
|
return;
|
|
598
613
|
}
|
|
599
614
|
const scope = (0, loop_command_catalog_1.dominantTicketScope)(promptsText);
|
|
@@ -831,13 +846,11 @@ function createRailsRouter() {
|
|
|
831
846
|
}
|
|
832
847
|
let jobId;
|
|
833
848
|
if (mode === 'freestyle') {
|
|
834
|
-
// Freestyle launches ONE independent
|
|
849
|
+
// Freestyle launches ONE independent provider-owned job per ticket — each gets
|
|
835
850
|
// its own log and runs the spec autonomously (no pipeline). The rail UI
|
|
836
851
|
// tracks the first job as its representative active job; every job is
|
|
837
852
|
// registered so its ticket is marked done on completion.
|
|
838
|
-
|
|
839
|
-
// adapter regardless of the project's primary.
|
|
840
|
-
const freestyleModel = mode === 'freestyle' && typeof model === 'string' && VALID_FREESTYLE_MODELS.has(model)
|
|
853
|
+
const freestyleModel = mode === 'freestyle' && typeof model === 'string'
|
|
841
854
|
? model
|
|
842
855
|
: undefined;
|
|
843
856
|
const jobIds = [];
|
|
@@ -845,7 +858,7 @@ function createRailsRouter() {
|
|
|
845
858
|
const command = `/specrails:freestyle #${ticketId} --yes`;
|
|
846
859
|
const job = c.queueManager.enqueue(command, 'normal', {
|
|
847
860
|
profileName: null,
|
|
848
|
-
provider:
|
|
861
|
+
provider: engineCheck.provider,
|
|
849
862
|
...(freestyleModel ? { model: freestyleModel } : {}),
|
|
850
863
|
// No explicit `interactive` — QueueManager's spawn-time default
|
|
851
864
|
// (kill-switch + persistent-stdin capability) covers it, and the
|