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
|
@@ -803,7 +803,7 @@ class ProjectRegistry {
|
|
|
803
803
|
// an `ai_invocations` row (surface='setup'). Returns null until the project
|
|
804
804
|
// context exists, in which case nothing is recorded (best-effort).
|
|
805
805
|
(pid) => this._contexts.get(pid)?.db ?? null);
|
|
806
|
-
const proposalManager = new proposal_manager_1.ProposalManager(boundBroadcast, db, project.path, project.id);
|
|
806
|
+
const proposalManager = new proposal_manager_1.ProposalManager(boundBroadcast, db, project.path, project.id, project.provider ?? 'claude', project.slug);
|
|
807
807
|
const agentRefineManager = new agent_refine_manager_1.AgentRefineManager(boundBroadcast, db, project.path, project.id, project.provider ?? 'claude');
|
|
808
808
|
// Retention prune: drop stale/abandoned refine sessions on project load.
|
|
809
809
|
try {
|
|
@@ -812,7 +812,7 @@ class ProjectRegistry {
|
|
|
812
812
|
catch (err) {
|
|
813
813
|
console.error('[project-registry] prune refine sessions failed:', err);
|
|
814
814
|
}
|
|
815
|
-
const specLauncherManager = new spec_launcher_manager_1.SpecLauncherManager(boundBroadcast, project.path, db, project.id);
|
|
815
|
+
const specLauncherManager = new spec_launcher_manager_1.SpecLauncherManager(boundBroadcast, project.path, db, project.id, project.provider ?? 'claude', project.slug);
|
|
816
816
|
// FileSummaryManager — code-explorer. The class is constructed for every
|
|
817
817
|
// project regardless of the feature flag; the router 404s when the flag
|
|
818
818
|
// is off, so no spawn can occur. Budget reader queries `ai_invocations`
|
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.registerChatRoutes = registerChatRoutes;
|
|
4
4
|
const ids_1 = require("./ids");
|
|
5
5
|
const db_1 = require("./db");
|
|
6
|
+
const providers_1 = require("./providers");
|
|
7
|
+
const runtime_1 = require("./providers/runtime");
|
|
6
8
|
const context_scope_1 = require("./context-scope");
|
|
7
9
|
const spec_models_1 = require("./spec-models");
|
|
8
10
|
const provider_selection_1 = require("./provider-selection");
|
|
@@ -46,6 +48,14 @@ function registerChatRoutes(deps) {
|
|
|
46
48
|
}
|
|
47
49
|
const rawKind = req.body?.kind;
|
|
48
50
|
const kind = rawKind === 'explore' ? 'explore' : rawKind === 'milestone' ? 'milestone' : 'sidebar';
|
|
51
|
+
if (kind === 'milestone' && !(0, runtime_1.supportsToolPolicy)((0, providers_1.getAdapter)(provider), 'read-only')) {
|
|
52
|
+
res.status(409).json({
|
|
53
|
+
error: 'provider_tool_policy_unsupported',
|
|
54
|
+
provider,
|
|
55
|
+
requiredPolicy: 'read-only',
|
|
56
|
+
});
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
49
59
|
const id = (0, ids_1.newId)();
|
|
50
60
|
const rawScope = req.body?.contextScope;
|
|
51
61
|
if (rawScope !== undefined && kind !== 'explore') {
|
|
@@ -67,11 +77,9 @@ function registerChatRoutes(deps) {
|
|
|
67
77
|
let scope;
|
|
68
78
|
if (kind === 'explore') {
|
|
69
79
|
const fallback = (0, context_scope_1.getLastContextScope)(db, 'explore');
|
|
70
|
-
// Defence-in-depth:
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
// eligibility) ever sees a mismatched flag.
|
|
74
|
-
const safeRawScope = provider !== 'claude' && rawScope != null
|
|
80
|
+
// Defence-in-depth: retain Contract Refine only for providers that
|
|
81
|
+
// advertise the structured-action contract.
|
|
82
|
+
const safeRawScope = (0, providers_1.getAdapter)(provider).capabilities.structuredActions !== true && rawScope != null
|
|
75
83
|
? { ...rawScope, contractRefine: false }
|
|
76
84
|
: rawScope;
|
|
77
85
|
scope = (0, context_scope_1.normalizeContextScope)(safeRawScope ?? fallback, fallback);
|
|
@@ -86,7 +94,7 @@ function registerChatRoutes(deps) {
|
|
|
86
94
|
res.status(201).json({ conversation });
|
|
87
95
|
});
|
|
88
96
|
router.get('/:projectId/chat/conversations/:id', (req, res) => {
|
|
89
|
-
const { db } = ctx(req);
|
|
97
|
+
const { db, project } = ctx(req);
|
|
90
98
|
const conversation = (0, db_1.getConversation)(db, req.params.id);
|
|
91
99
|
if (!conversation) {
|
|
92
100
|
res.status(404).json({ error: 'Conversation not found' });
|
|
@@ -128,7 +136,7 @@ function registerChatRoutes(deps) {
|
|
|
128
136
|
res.json({ ok: true });
|
|
129
137
|
});
|
|
130
138
|
router.patch('/:projectId/chat/conversations/:id', (req, res) => {
|
|
131
|
-
const { db } = ctx(req);
|
|
139
|
+
const { db, project } = ctx(req);
|
|
132
140
|
const conversation = (0, db_1.getConversation)(db, req.params.id);
|
|
133
141
|
if (!conversation) {
|
|
134
142
|
res.status(404).json({ error: 'Conversation not found' });
|
|
@@ -146,7 +154,8 @@ function registerChatRoutes(deps) {
|
|
|
146
154
|
if (model !== undefined) {
|
|
147
155
|
// Validate against the conversation's own provider so a bad value can't be
|
|
148
156
|
// persisted and break the next turn's spawn. (Mirrors the POST handler.)
|
|
149
|
-
const convProvider = conversation.provider ??
|
|
157
|
+
const convProvider = conversation.provider ??
|
|
158
|
+
(project.provider ?? 'claude');
|
|
150
159
|
if (typeof model !== 'string' || !(0, spec_models_1.isValidModelForProvider)(model, convProvider)) {
|
|
151
160
|
res.status(400).json({
|
|
152
161
|
error: `Invalid model "${String(model)}" for provider "${convProvider}"`,
|
|
@@ -13,6 +13,7 @@ exports.deriveFallbackShortSummary = deriveFallbackShortSummary;
|
|
|
13
13
|
exports.lightlyStructurePrompt = lightlyStructurePrompt;
|
|
14
14
|
exports.formatDescriptionWithCriteria = formatDescriptionWithCriteria;
|
|
15
15
|
exports.resolveDefaultSpecModel = resolveDefaultSpecModel;
|
|
16
|
+
exports.readAgentModelSelection = readAgentModelSelection;
|
|
16
17
|
exports.readAgentModels = readAgentModels;
|
|
17
18
|
exports.applyModelConfig = applyModelConfig;
|
|
18
19
|
exports.serializeInstallConfigYaml = serializeInstallConfigYaml;
|
|
@@ -31,6 +32,11 @@ exports.TERMINAL_PANEL_ENABLED = TERMINAL_PANEL_ENABLED;
|
|
|
31
32
|
*/
|
|
32
33
|
function serializeInstallConfigYaml(config) {
|
|
33
34
|
const c = config;
|
|
35
|
+
const requestedProvider = typeof c.provider === 'string' ? c.provider : '';
|
|
36
|
+
const provider = (0, spec_models_1.getProviderDefault)(requestedProvider)
|
|
37
|
+
? requestedProvider
|
|
38
|
+
: 'claude';
|
|
39
|
+
const defaultModel = (0, spec_models_1.getProviderDefault)(provider) || (0, spec_models_1.getProviderDefault)('claude');
|
|
34
40
|
const overrides = c.models?.overrides ?? {};
|
|
35
41
|
const overridesEntries = Object.entries(overrides);
|
|
36
42
|
const overridesYaml = overridesEntries.length > 0
|
|
@@ -40,14 +46,14 @@ function serializeInstallConfigYaml(config) {
|
|
|
40
46
|
'# specrails install config — generated by Specrails',
|
|
41
47
|
`# Re-run: npx ${core_package_1.CORE_PACKAGE_SPEC} init to regenerate`,
|
|
42
48
|
`version: ${c.version ?? 1}`,
|
|
43
|
-
`provider: ${
|
|
49
|
+
`provider: ${provider}`,
|
|
44
50
|
`tier: ${c.tier ?? 'quick'}`,
|
|
45
51
|
`agents:`,
|
|
46
52
|
` selected: [${(c.agents?.selected ?? []).join(', ')}]`,
|
|
47
53
|
` excluded: [${(c.agents?.excluded ?? []).join(', ')}]`,
|
|
48
54
|
`models:`,
|
|
49
55
|
` preset: ${c.models?.preset ?? 'balanced'}`,
|
|
50
|
-
` defaults: { model: ${c.models?.defaults?.model ??
|
|
56
|
+
` defaults: { model: ${c.models?.defaults?.model ?? defaultModel} }`,
|
|
51
57
|
` overrides:${overridesYaml}`,
|
|
52
58
|
`agent_teams: ${c.agent_teams ?? false}`,
|
|
53
59
|
'',
|
|
@@ -57,6 +63,13 @@ function serializeInstallConfigYaml(config) {
|
|
|
57
63
|
// ─── Agent model helpers ──────────────────────────────────────────────────────
|
|
58
64
|
const VALID_MODEL_ALIASES = ['sonnet', 'fable', 'opus', 'haiku'];
|
|
59
65
|
exports.VALID_MODEL_ALIASES = VALID_MODEL_ALIASES;
|
|
66
|
+
function projectExecutionRoot(project) {
|
|
67
|
+
const exec = (0, workspace_resolution_1.resolveProjectExecution)({ slug: project.slug, path: project.path });
|
|
68
|
+
return exec.relocated && exec.workspaceDir ? exec.workspaceDir : project.path;
|
|
69
|
+
}
|
|
70
|
+
function isKimiAgentModelProject(project) {
|
|
71
|
+
return project.provider === 'kimi';
|
|
72
|
+
}
|
|
60
73
|
/**
|
|
61
74
|
* Relocate-artifacts: where the agent `.claude/agents/*.md` files live. When the
|
|
62
75
|
* project is relocated AND the workspace is populated by core, the agents are
|
|
@@ -64,15 +77,84 @@ exports.VALID_MODEL_ALIASES = VALID_MODEL_ALIASES;
|
|
|
64
77
|
* router uses for profiles). Legacy ⇒ `<project>/.claude/agents` (byte-identical).
|
|
65
78
|
*/
|
|
66
79
|
function resolveAgentsDir(project) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
80
|
+
return path_1.default.join(projectExecutionRoot(project), '.claude', 'agents');
|
|
81
|
+
}
|
|
82
|
+
function resolveKimiSkillsDir(project) {
|
|
83
|
+
return path_1.default.join(projectExecutionRoot(project), '.kimi-code', 'skills');
|
|
84
|
+
}
|
|
85
|
+
function readAgentModelSelection(project) {
|
|
86
|
+
const provider = (project.provider ?? 'claude');
|
|
87
|
+
const fallback = (0, spec_models_1.getProviderDefault)(provider);
|
|
88
|
+
const configPath = (0, install_config_path_1.installConfigPath)(project);
|
|
89
|
+
if (!fs_1.default.existsSync(configPath))
|
|
90
|
+
return { defaultModel: fallback, overrides: {} };
|
|
91
|
+
let configText;
|
|
92
|
+
try {
|
|
93
|
+
configText = fs_1.default.readFileSync(configPath, 'utf-8');
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return { defaultModel: fallback, overrides: {} };
|
|
97
|
+
}
|
|
98
|
+
const defaultsMatch = configText.match(/defaults:\s*\{\s*model:\s*(\S+?)\s*\}/);
|
|
99
|
+
const configuredDefault = defaultsMatch?.[1];
|
|
100
|
+
const defaultModel = configuredDefault && (0, spec_models_1.isValidModelForProvider)(configuredDefault, provider)
|
|
101
|
+
? configuredDefault
|
|
102
|
+
: fallback;
|
|
103
|
+
const overrides = {};
|
|
104
|
+
const overridesBlockMatch = configText.match(/overrides:([\s\S]*?)(?:\n\S|$)/);
|
|
105
|
+
if (overridesBlockMatch) {
|
|
106
|
+
const overrideLines = overridesBlockMatch[1].match(/^ {2,}(\S+):\s*(\S+)/gm) ?? [];
|
|
107
|
+
for (const line of overrideLines) {
|
|
108
|
+
const match = line.match(/^\s+(\S+):\s*(\S+)/);
|
|
109
|
+
if (match
|
|
110
|
+
&& (0, spec_models_1.isValidModelForProvider)(match[2], provider)) {
|
|
111
|
+
overrides[match[1]] = match[2];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return { defaultModel, overrides };
|
|
116
|
+
}
|
|
117
|
+
function readKimiAgentModels(project) {
|
|
118
|
+
const skillsDir = resolveKimiSkillsDir(project);
|
|
119
|
+
if (!fs_1.default.existsSync(skillsDir))
|
|
120
|
+
return [];
|
|
121
|
+
let entries;
|
|
122
|
+
try {
|
|
123
|
+
entries = fs_1.default.readdirSync(skillsDir, { withFileTypes: true });
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
const { defaultModel, overrides } = readAgentModelSelection(project);
|
|
129
|
+
const agents = [];
|
|
130
|
+
for (const entry of entries) {
|
|
131
|
+
// Kimi commands and OpenSpec skills share this directory. Only direct
|
|
132
|
+
// provider-native rail roles participate in per-agent model overrides.
|
|
133
|
+
if (!entry.isDirectory()
|
|
134
|
+
|| (!entry.name.startsWith('sr-') && !entry.name.startsWith('custom-')))
|
|
135
|
+
continue;
|
|
136
|
+
const skillPath = path_1.default.join(skillsDir, entry.name, 'SKILL.md');
|
|
137
|
+
try {
|
|
138
|
+
if (!fs_1.default.statSync(skillPath).isFile())
|
|
139
|
+
continue;
|
|
140
|
+
agents.push({
|
|
141
|
+
name: entry.name,
|
|
142
|
+
model: overrides[entry.name] ?? defaultModel,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
// Skip incomplete or unreadable role directories.
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return agents;
|
|
70
150
|
}
|
|
71
151
|
/**
|
|
72
152
|
* Read installed agents from `.claude/agents/*.md` (top-level only, no subdirs).
|
|
73
153
|
* Extracts the `model:` field from YAML frontmatter.
|
|
74
154
|
*/
|
|
75
155
|
function readAgentModels(project) {
|
|
156
|
+
if (isKimiAgentModelProject(project))
|
|
157
|
+
return readKimiAgentModels(project);
|
|
76
158
|
const agentsDir = resolveAgentsDir(project);
|
|
77
159
|
if (!fs_1.default.existsSync(agentsDir))
|
|
78
160
|
return [];
|
|
@@ -115,6 +197,12 @@ function readAgentModels(project) {
|
|
|
115
197
|
* defaults/overrides. No-op if the config file does not exist.
|
|
116
198
|
*/
|
|
117
199
|
function applyModelConfig(project) {
|
|
200
|
+
// Kimi SKILL.md frontmatter has no per-role `model:` field. Its selected
|
|
201
|
+
// default/overrides live in install-config.yaml and are applied by the
|
|
202
|
+
// runtime when the role is invoked. Never inject Claude agent syntax into a
|
|
203
|
+
// Kimi skill: readAgentModels projects the config over the direct roles.
|
|
204
|
+
if (isKimiAgentModelProject(project))
|
|
205
|
+
return;
|
|
118
206
|
const configPath = (0, install_config_path_1.installConfigPath)(project);
|
|
119
207
|
if (!fs_1.default.existsSync(configPath))
|
|
120
208
|
return;
|
|
@@ -10,6 +10,7 @@ const db_1 = require("./db");
|
|
|
10
10
|
const queue_manager_1 = require("./queue-manager");
|
|
11
11
|
const feature_flags_1 = require("./feature-flags");
|
|
12
12
|
const types_1 = require("./types");
|
|
13
|
+
const providers_1 = require("./providers");
|
|
13
14
|
const hooks_1 = require("./hooks");
|
|
14
15
|
const explore_smash_1 = require("./explore-smash");
|
|
15
16
|
const job_spawn_idempotency_1 = require("./job-spawn-idempotency");
|
|
@@ -39,8 +40,38 @@ function registerJobsRoutes(deps) {
|
|
|
39
40
|
const { router, registry, ctx, ticketPath } = deps;
|
|
40
41
|
// ─── Queue / Spawn routes ────────────────────────────────────────────────────
|
|
41
42
|
router.post('/:projectId/spawn', (req, res) => {
|
|
42
|
-
const { command, priority, dependsOnJobId, pipelineId, profileName, aiEngine } = req.body ?? {};
|
|
43
|
-
if (
|
|
43
|
+
const { command, priority, dependsOnJobId, pipelineId, profileName, aiEngine, model, rerunOfJobId, } = req.body ?? {};
|
|
44
|
+
if (rerunOfJobId !== undefined
|
|
45
|
+
&& (typeof rerunOfJobId !== 'string' || !/^[A-Za-z0-9._:-]{1,128}$/.test(rerunOfJobId))) {
|
|
46
|
+
res.status(400).json({ error: 'rerunOfJobId must be 1-128 URL-safe characters' });
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const c = ctx(req);
|
|
50
|
+
const rerunSource = typeof rerunOfJobId === 'string'
|
|
51
|
+
? (0, db_1.getJob)(c.db, rerunOfJobId)
|
|
52
|
+
: undefined;
|
|
53
|
+
if (typeof rerunOfJobId === 'string' && !rerunSource) {
|
|
54
|
+
res.status(404).json({ error: 'Rerun source job not found' });
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (rerunSource
|
|
58
|
+
&& rerunSource.status !== 'completed'
|
|
59
|
+
&& rerunSource.status !== 'failed') {
|
|
60
|
+
res.status(409).json({ error: 'Only completed or failed jobs can be rerun' });
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// A rerun is server-owned: never trust duplicated command/provider/model/
|
|
64
|
+
// profile metadata from the client. The exact command retains ticket
|
|
65
|
+
// identity, while the persisted job + profile snapshot retain execution
|
|
66
|
+
// identity. Jobs do not persist reasoning effort or launch-surface origin,
|
|
67
|
+
// so this route deliberately does not manufacture either value.
|
|
68
|
+
const effectiveCommand = rerunSource?.command ?? command;
|
|
69
|
+
const effectiveAiEngine = rerunSource?.provider ?? (rerunSource ? undefined : aiEngine);
|
|
70
|
+
const requestedModel = rerunSource?.model ?? (rerunSource ? undefined : model);
|
|
71
|
+
const requestedProfileName = rerunSource
|
|
72
|
+
? (rerunSource.profile_name ?? null)
|
|
73
|
+
: profileName;
|
|
74
|
+
if (!effectiveCommand || typeof effectiveCommand !== 'string' || !effectiveCommand.trim()) {
|
|
44
75
|
res.status(400).json({ error: 'command is required' });
|
|
45
76
|
return;
|
|
46
77
|
}
|
|
@@ -57,8 +88,8 @@ function registerJobsRoutes(deps) {
|
|
|
57
88
|
? dependsOnJobId.trim()
|
|
58
89
|
: undefined;
|
|
59
90
|
// profileName accepts: undefined (default resolution), null (force legacy), string (explicit)
|
|
60
|
-
const normalizedProfileName =
|
|
61
|
-
: typeof
|
|
91
|
+
const normalizedProfileName = requestedProfileName === null ? null
|
|
92
|
+
: typeof requestedProfileName === 'string' && requestedProfileName.trim() ? requestedProfileName.trim()
|
|
62
93
|
: undefined;
|
|
63
94
|
const idempotencyKey = req.get('Idempotency-Key');
|
|
64
95
|
if (idempotencyKey !== undefined && !/^[A-Za-z0-9._:-]{1,128}$/.test(idempotencyKey)) {
|
|
@@ -67,21 +98,46 @@ function registerJobsRoutes(deps) {
|
|
|
67
98
|
}
|
|
68
99
|
// aiEngine: optional per-job provider override; must be installed on the
|
|
69
100
|
// project. Omitting it runs on the project's primary provider.
|
|
70
|
-
const engineCheck = (0, provider_selection_1.validateRequestedProvider)(
|
|
101
|
+
const engineCheck = (0, provider_selection_1.validateRequestedProvider)(c.project, effectiveAiEngine);
|
|
71
102
|
if (!engineCheck.ok) {
|
|
72
103
|
res.status(400).json({ error: engineCheck.error });
|
|
73
104
|
return;
|
|
74
105
|
}
|
|
106
|
+
let normalizedModel;
|
|
107
|
+
if (requestedModel !== undefined && requestedModel !== null) {
|
|
108
|
+
if (typeof requestedModel !== 'string'
|
|
109
|
+
|| !(0, spec_models_1.isValidModelForProvider)(requestedModel, engineCheck.provider)) {
|
|
110
|
+
// Some older non-alias providers persisted the provider-reported full
|
|
111
|
+
// model ID rather than the launch alias. Preserve their historical
|
|
112
|
+
// rerun behaviour (provider default) while failing closed for adapters
|
|
113
|
+
// that advertise exact custom aliases such as Kimi.
|
|
114
|
+
if (rerunSource
|
|
115
|
+
&& (0, providers_1.getAdapter)(engineCheck.provider).capabilities.customModelAliases !== true) {
|
|
116
|
+
normalizedModel = undefined;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
res.status(400).json({
|
|
120
|
+
error: `model is not valid for provider "${engineCheck.provider}"`,
|
|
121
|
+
allowed: (0, spec_models_1.getModelsForProvider)(engineCheck.provider),
|
|
122
|
+
});
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
normalizedModel = requestedModel;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
75
130
|
try {
|
|
76
|
-
const c = ctx(req);
|
|
77
131
|
const normalizedPriority = priority ?? 'normal';
|
|
78
132
|
const spawnFingerprint = idempotencyKey ? (0, job_spawn_idempotency_1.fingerprintJobSpawn)({
|
|
79
|
-
command,
|
|
133
|
+
command: effectiveCommand,
|
|
80
134
|
priority: normalizedPriority,
|
|
81
135
|
dependsOnJobId: normalizedDependsOnJobId ?? null,
|
|
82
136
|
pipelineId: pipelineId || null,
|
|
83
137
|
profileName: normalizedProfileName === undefined ? '__project_default__' : normalizedProfileName,
|
|
84
|
-
provider:
|
|
138
|
+
provider: effectiveAiEngine ? engineCheck.provider : null,
|
|
139
|
+
model: normalizedModel ?? null,
|
|
140
|
+
rerunOfJobId: rerunOfJobId ?? null,
|
|
85
141
|
}) : null;
|
|
86
142
|
if (idempotencyKey && spawnFingerprint) {
|
|
87
143
|
const existingJobId = (0, job_spawn_idempotency_1.findIdempotentJob)(c.db, idempotencyKey, spawnFingerprint);
|
|
@@ -101,14 +157,15 @@ function registerJobsRoutes(deps) {
|
|
|
101
157
|
...(normalizedProfileName !== undefined
|
|
102
158
|
? { profileName: normalizedProfileName }
|
|
103
159
|
: {}),
|
|
104
|
-
provider:
|
|
160
|
+
provider: effectiveAiEngine ? engineCheck.provider : undefined,
|
|
161
|
+
...(normalizedModel ? { model: normalizedModel } : {}),
|
|
105
162
|
};
|
|
106
163
|
const job = idempotencyKey && spawnFingerprint && preparedJobId
|
|
107
|
-
? c.queueManager.enqueue(
|
|
164
|
+
? c.queueManager.enqueue(effectiveCommand, normalizedPriority, enqueueOptions, {
|
|
108
165
|
jobId: preparedJobId,
|
|
109
166
|
commit: (db) => (0, job_spawn_idempotency_1.claimIdempotentJob)(db, idempotencyKey, spawnFingerprint, preparedJobId),
|
|
110
167
|
})
|
|
111
|
-
: c.queueManager.enqueue(
|
|
168
|
+
: c.queueManager.enqueue(effectiveCommand, normalizedPriority, enqueueOptions);
|
|
112
169
|
const position = job.queuePosition ?? 0;
|
|
113
170
|
res.status(202).json({ jobId: job.id, position });
|
|
114
171
|
}
|
|
@@ -280,7 +337,14 @@ function registerJobsRoutes(deps) {
|
|
|
280
337
|
const provider = (0, provider_selection_1.resolveProvider)(project, typeof req.query.provider === 'string' ? req.query.provider : undefined);
|
|
281
338
|
const model = (0, project_router_helpers_1.resolveDefaultSpecModel)({ projectPath: project.path, slug: project.slug, provider });
|
|
282
339
|
const allowed = (0, spec_models_1.getModelsForProvider)(provider);
|
|
283
|
-
|
|
340
|
+
const adapter = (0, providers_1.getAdapter)(provider);
|
|
341
|
+
res.json({
|
|
342
|
+
model,
|
|
343
|
+
provider,
|
|
344
|
+
allowed,
|
|
345
|
+
providers: project.providers,
|
|
346
|
+
customModelAliases: adapter.capabilities.customModelAliases === true,
|
|
347
|
+
});
|
|
284
348
|
});
|
|
285
349
|
// Cancellation is idempotent and intentionally separate from history
|
|
286
350
|
// deletion. A stale UI request that races with natural completion must never
|
|
@@ -11,7 +11,6 @@ const spec_models_1 = require("./spec-models");
|
|
|
11
11
|
const workspace_resolution_1 = require("./workspace-resolution");
|
|
12
12
|
const loop_command_catalog_1 = require("./loop-command-catalog");
|
|
13
13
|
const ids_1 = require("./ids");
|
|
14
|
-
const VALID_REASONING_EFFORTS = new Set(['low', 'medium', 'high']);
|
|
15
14
|
function registerLoopRunRoutes(deps) {
|
|
16
15
|
const { router, ctx } = deps;
|
|
17
16
|
// GET a single loop run's live/terminal state. Backs the companion's running
|
|
@@ -27,7 +26,19 @@ function registerLoopRunRoutes(deps) {
|
|
|
27
26
|
res.status(404).json({ error: 'Loop run not found' });
|
|
28
27
|
return;
|
|
29
28
|
}
|
|
30
|
-
|
|
29
|
+
const usageAvailable = !run.provider
|
|
30
|
+
|| !(0, providers_1.hasAdapter)(run.provider)
|
|
31
|
+
|| (0, providers_1.getAdapter)(run.provider).capabilities.reportsUsage !== false;
|
|
32
|
+
res.json({
|
|
33
|
+
loopRun: usageAvailable
|
|
34
|
+
? { ...run, usage_available: true }
|
|
35
|
+
: {
|
|
36
|
+
...run,
|
|
37
|
+
total_cost_usd: null,
|
|
38
|
+
total_tokens: null,
|
|
39
|
+
usage_available: false,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
31
42
|
});
|
|
32
43
|
router.post('/:projectId/loop-runs', (req, res) => {
|
|
33
44
|
if (!(0, feature_flags_1.isLoopsEnabled)()) {
|
|
@@ -50,27 +61,31 @@ function registerLoopRunRoutes(deps) {
|
|
|
50
61
|
res.status(400).json({ error: 'Loop must be published before it can run' });
|
|
51
62
|
return;
|
|
52
63
|
}
|
|
53
|
-
let effort;
|
|
54
|
-
if (reasoning_effort !== undefined && reasoning_effort !== null) {
|
|
55
|
-
if (typeof reasoning_effort !== 'string' || !VALID_REASONING_EFFORTS.has(reasoning_effort)) {
|
|
56
|
-
res.status(400).json({ error: 'reasoning_effort must be one of: low, medium, high' });
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
effort = reasoning_effort;
|
|
60
|
-
}
|
|
61
64
|
const check = (0, provider_selection_1.validateRequestedProvider)(c.project, aiEngine ?? providerAlias);
|
|
62
65
|
if (!check.ok) {
|
|
63
66
|
res.status(400).json({ error: check.error });
|
|
64
67
|
return;
|
|
65
68
|
}
|
|
66
69
|
const provider = check.provider;
|
|
67
|
-
|
|
70
|
+
const adapter = (0, providers_1.getAdapter)(provider);
|
|
71
|
+
if (loop.graph.nodes.some((node) => node.type === 'decider')
|
|
72
|
+
&& !(0, providers_1.supportsToolPolicy)(adapter, 'read-only')) {
|
|
73
|
+
res.status(409).json({
|
|
74
|
+
code: 'provider_tool_policy_unsupported',
|
|
75
|
+
provider,
|
|
76
|
+
requiredPolicy: 'read-only',
|
|
77
|
+
error: `Provider '${provider}' cannot run Loop Deciders because its headless CLI ` +
|
|
78
|
+
'does not enforce a read-only tool policy.',
|
|
79
|
+
});
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// Provider-capability guard (e.g. a loop that uses {{cmd:freestyle}}).
|
|
68
83
|
const promptsText = loop.graph.nodes
|
|
69
84
|
.filter((n) => n.type === 'ai-step')
|
|
70
85
|
.map((n) => String(n.data?.prompt ?? ''))
|
|
71
86
|
.join('\n');
|
|
72
|
-
if ((0, loop_command_catalog_1.
|
|
73
|
-
res.status(400).json({ error:
|
|
87
|
+
if ((0, loop_command_catalog_1.referencesUnsupportedProviderCommand)(promptsText, provider)) {
|
|
88
|
+
res.status(400).json({ error: `This loop uses a command unsupported by provider '${provider}'` });
|
|
74
89
|
return;
|
|
75
90
|
}
|
|
76
91
|
// Optional explicit model — validated against the chosen provider's catalog
|
|
@@ -84,7 +99,20 @@ function registerLoopRunRoutes(deps) {
|
|
|
84
99
|
model = requestedModel;
|
|
85
100
|
}
|
|
86
101
|
else {
|
|
87
|
-
model =
|
|
102
|
+
model = adapter.defaultModel();
|
|
103
|
+
}
|
|
104
|
+
let effort;
|
|
105
|
+
if (reasoning_effort !== undefined && reasoning_effort !== null) {
|
|
106
|
+
const allowed = (0, providers_1.reasoningEffortsForModel)(adapter, model);
|
|
107
|
+
if (typeof reasoning_effort !== 'string' ||
|
|
108
|
+
!allowed.includes(reasoning_effort)) {
|
|
109
|
+
res.status(400).json({
|
|
110
|
+
error: `reasoning_effort is not valid for provider "${provider}" and model "${model}"`,
|
|
111
|
+
allowed,
|
|
112
|
+
});
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
effort = reasoning_effort;
|
|
88
116
|
}
|
|
89
117
|
const exec = (0, workspace_resolution_1.resolveProjectExecution)({ slug: c.project.slug, path: c.project.path });
|
|
90
118
|
const runId = (0, ids_1.newId)();
|
|
@@ -14,6 +14,7 @@ const integration_branch_1 = require("./integration-branch");
|
|
|
14
14
|
const worktree_manager_1 = require("./worktree-manager");
|
|
15
15
|
const context_budget_1 = require("./context-budget");
|
|
16
16
|
const context_scope_1 = require("./context-scope");
|
|
17
|
+
const spec_models_1 = require("./spec-models");
|
|
17
18
|
const terminal_settings_1 = require("./terminal-settings");
|
|
18
19
|
const terminal_marks_store_1 = require("./terminal-marks-store");
|
|
19
20
|
const project_router_helpers_1 = require("./project-router-helpers");
|
|
@@ -161,10 +162,14 @@ function registerSettingsRoutes(deps) {
|
|
|
161
162
|
router.patch('/:projectId/agent-models', (req, res) => {
|
|
162
163
|
const { project } = ctx(req);
|
|
163
164
|
const { defaultModel, overrides } = req.body ?? {};
|
|
165
|
+
const provider = (project.provider ?? 'claude');
|
|
166
|
+
const allowedModels = (0, spec_models_1.getModelsForProvider)(provider).map((model) => model.value);
|
|
164
167
|
// Validate defaultModel if provided
|
|
165
168
|
if (defaultModel !== undefined) {
|
|
166
|
-
if (
|
|
167
|
-
res.status(400).json({
|
|
169
|
+
if (!(0, spec_models_1.isValidModelForProvider)(defaultModel, provider)) {
|
|
170
|
+
res.status(400).json({
|
|
171
|
+
error: `Invalid model alias for provider "${provider}". Catalog: ${allowedModels.join(', ')}`,
|
|
172
|
+
});
|
|
168
173
|
return;
|
|
169
174
|
}
|
|
170
175
|
}
|
|
@@ -175,8 +180,11 @@ function registerSettingsRoutes(deps) {
|
|
|
175
180
|
return;
|
|
176
181
|
}
|
|
177
182
|
for (const [agentName, modelValue] of Object.entries(overrides)) {
|
|
178
|
-
if (
|
|
179
|
-
res.status(400).json({
|
|
183
|
+
if (!(0, spec_models_1.isValidModelForProvider)(modelValue, provider)) {
|
|
184
|
+
res.status(400).json({
|
|
185
|
+
error: `Invalid model alias for agent "${agentName}" and provider "${provider}". ` +
|
|
186
|
+
`Catalog: ${allowedModels.join(', ')}`,
|
|
187
|
+
});
|
|
180
188
|
return;
|
|
181
189
|
}
|
|
182
190
|
}
|
|
@@ -185,12 +193,17 @@ function registerSettingsRoutes(deps) {
|
|
|
185
193
|
// NEVER `<project>/.specrails` (which would leak into the user's repo).
|
|
186
194
|
const configPath = (0, install_config_path_1.installConfigPath)(project);
|
|
187
195
|
// Read existing config or build default shape
|
|
196
|
+
const providerDefault = (0, spec_models_1.getProviderDefault)(provider) || (0, spec_models_1.getProviderDefault)('claude');
|
|
188
197
|
let existingConfig = {
|
|
189
198
|
version: 1,
|
|
190
|
-
provider
|
|
199
|
+
provider,
|
|
191
200
|
tier: 'quick',
|
|
192
201
|
agents: { selected: [], excluded: [] },
|
|
193
|
-
models: {
|
|
202
|
+
models: {
|
|
203
|
+
preset: 'balanced',
|
|
204
|
+
defaults: { model: providerDefault },
|
|
205
|
+
overrides: {},
|
|
206
|
+
},
|
|
194
207
|
agent_teams: false,
|
|
195
208
|
};
|
|
196
209
|
if (fs_1.default.existsSync(configPath)) {
|
|
@@ -198,9 +211,9 @@ function registerSettingsRoutes(deps) {
|
|
|
198
211
|
const text = fs_1.default.readFileSync(configPath, 'utf-8');
|
|
199
212
|
// Parse fields we care about from the existing config text
|
|
200
213
|
const versionMatch = text.match(/^version:\s*(\d+)/m);
|
|
201
|
-
const providerMatch = text.match(/^provider:\s*(\S+)/m);
|
|
202
214
|
const tierMatch = text.match(/^tier:\s*(\S+)/m);
|
|
203
215
|
const presetMatch = text.match(/preset:\s*(\S+)/);
|
|
216
|
+
const defaultModelMatch = text.match(/defaults:\s*\{\s*model:\s*(\S+?)\s*\}/);
|
|
204
217
|
const agentTeamsMatch = text.match(/^agent_teams:\s*(\S+)/m);
|
|
205
218
|
// Parse selected agents list
|
|
206
219
|
const selectedMatch = text.match(/selected:\s*\[([^\]]*)\]/);
|
|
@@ -219,18 +232,26 @@ function registerSettingsRoutes(deps) {
|
|
|
219
232
|
const overrideLines = block.match(/^ {2,}(\S+):\s*(\S+)/gm) ?? [];
|
|
220
233
|
for (const line of overrideLines) {
|
|
221
234
|
const m = line.match(/^\s+(\S+):\s*(\S+)/);
|
|
222
|
-
if (m)
|
|
235
|
+
if (m && (0, spec_models_1.isValidModelForProvider)(m[2], provider)) {
|
|
223
236
|
existingOverrides[m[1]] = m[2];
|
|
237
|
+
}
|
|
224
238
|
}
|
|
225
239
|
}
|
|
240
|
+
const parsedDefaultModel = defaultModelMatch?.[1];
|
|
226
241
|
existingConfig = {
|
|
227
242
|
version: versionMatch ? parseInt(versionMatch[1], 10) : 1,
|
|
228
|
-
provider
|
|
243
|
+
// The project provider is authoritative. A stale shared config can
|
|
244
|
+
// belong to another provider after a multi-provider setup pass.
|
|
245
|
+
provider,
|
|
229
246
|
tier: tierMatch ? tierMatch[1] : 'quick',
|
|
230
247
|
agents: { selected: parsedSelected, excluded: parsedExcluded },
|
|
231
248
|
models: {
|
|
232
249
|
preset: presetMatch ? presetMatch[1] : 'balanced',
|
|
233
|
-
defaults: {
|
|
250
|
+
defaults: {
|
|
251
|
+
model: parsedDefaultModel && (0, spec_models_1.isValidModelForProvider)(parsedDefaultModel, provider)
|
|
252
|
+
? parsedDefaultModel
|
|
253
|
+
: providerDefault,
|
|
254
|
+
},
|
|
234
255
|
overrides: existingOverrides,
|
|
235
256
|
},
|
|
236
257
|
agent_teams: agentTeamsMatch ? agentTeamsMatch[1] === 'true' : false,
|
|
@@ -12,7 +12,6 @@ const ids_1 = require("./ids");
|
|
|
12
12
|
const db_1 = require("./db");
|
|
13
13
|
const desktop_db_1 = require("./desktop-db");
|
|
14
14
|
const queue_manager_1 = require("./queue-manager");
|
|
15
|
-
const command_resolver_1 = require("./command-resolver");
|
|
16
15
|
const changes_reader_1 = require("./changes-reader");
|
|
17
16
|
const project_router_helpers_1 = require("./project-router-helpers");
|
|
18
17
|
const install_config_path_1 = require("./install-config-path");
|
|
@@ -172,10 +171,10 @@ function registerSetupRoutes(deps) {
|
|
|
172
171
|
res.status(400).json({ error: 'idea is required' });
|
|
173
172
|
return;
|
|
174
173
|
}
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (
|
|
174
|
+
// Provider-aware pre-check. The manager resolves against the lazy
|
|
175
|
+
// execution cwd (including relocated workspaces) and understands Kimi's
|
|
176
|
+
// materialized headless skills rather than probing only Claude markdown.
|
|
177
|
+
if (!ctx(req).proposalManager.canStartExploration()) {
|
|
179
178
|
res.status(400).json({ error: 'This project does not have the /specrails:propose-feature command installed. Run "npx specrails-core@latest" to update.' });
|
|
180
179
|
return;
|
|
181
180
|
}
|
|
@@ -361,12 +361,24 @@ function registerSpendingRoutes(deps) {
|
|
|
361
361
|
// and skewed the day boundary by the tz offset, so the meter disagreed
|
|
362
362
|
// with both the /analytics dashboard and Claude's per-day accounting.
|
|
363
363
|
const { startIso, endIso } = localDayBoundsUtc(parseTzOffsetMinutes(req.query.tzOffsetMinutes));
|
|
364
|
-
const costRow = db.prepare(`SELECT
|
|
364
|
+
const costRow = db.prepare(`SELECT
|
|
365
|
+
COALESCE(SUM(total_cost_usd), 0) as costToday,
|
|
366
|
+
COUNT(total_cost_usd) as pricedRuns,
|
|
367
|
+
SUM(CASE WHEN total_cost_usd IS NULL THEN 1 ELSE 0 END) as unpricedRuns
|
|
368
|
+
FROM ai_invocations WHERE started_at >= ? AND started_at < ?`).get(startIso, endIso);
|
|
365
369
|
const costToday = costRow.costToday;
|
|
366
|
-
const
|
|
370
|
+
const unpricedRuns = costRow.unpricedRuns ?? 0;
|
|
371
|
+
const budgetUtilizationPct = dailyBudgetUsd != null && dailyBudgetUsd > 0 && unpricedRuns === 0
|
|
367
372
|
? (costToday / dailyBudgetUsd) * 100
|
|
368
373
|
: null;
|
|
369
|
-
res.json({
|
|
374
|
+
res.json({
|
|
375
|
+
dailyBudgetUsd,
|
|
376
|
+
jobCostThresholdUsd,
|
|
377
|
+
costToday,
|
|
378
|
+
pricedRuns: costRow.pricedRuns,
|
|
379
|
+
unpricedRuns,
|
|
380
|
+
budgetUtilizationPct,
|
|
381
|
+
});
|
|
370
382
|
}
|
|
371
383
|
catch (err) {
|
|
372
384
|
console.error('[project-router] budget get error:', err);
|