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
|
@@ -50,6 +50,7 @@ const explore_smash_1 = require("./explore-smash");
|
|
|
50
50
|
const ai_invocations_1 = require("./ai-invocations");
|
|
51
51
|
const context_scope_1 = require("./context-scope");
|
|
52
52
|
const result_event_1 = require("./result-event");
|
|
53
|
+
const runtime_1 = require("./providers/runtime");
|
|
53
54
|
const crypto_1 = require("crypto");
|
|
54
55
|
const spec_models_1 = require("./spec-models");
|
|
55
56
|
const provider_selection_1 = require("./provider-selection");
|
|
@@ -64,6 +65,48 @@ const workspace_resolution_1 = require("./workspace-resolution");
|
|
|
64
65
|
const multer_1 = __importDefault(require("multer"));
|
|
65
66
|
const attachment_manager_1 = require("./attachment-manager");
|
|
66
67
|
const project_router_helpers_1 = require("./project-router-helpers");
|
|
68
|
+
/**
|
|
69
|
+
* A NULL conversation provider is the intentional single-provider encoding:
|
|
70
|
+
* it means "use this project's primary provider", never "use Claude".
|
|
71
|
+
*/
|
|
72
|
+
function resolveConversationProvider(context, conversationId) {
|
|
73
|
+
const conversation = (0, db_1.getConversation)(context.db, conversationId);
|
|
74
|
+
if (!conversation)
|
|
75
|
+
return null;
|
|
76
|
+
return conversation.provider ?? context.project.provider ?? 'claude';
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Resolve a ticket-bound structured action without silently changing engines.
|
|
80
|
+
* An origin conversation owns the action provider because its session/model
|
|
81
|
+
* state is provider-specific. Explicit requests may confirm that provider but
|
|
82
|
+
* cannot replace it; missing/stale origins fail closed.
|
|
83
|
+
*/
|
|
84
|
+
function resolveTicketBoundProvider(context, ticket, requested) {
|
|
85
|
+
const requestedCheck = (0, provider_selection_1.validateRequestedProvider)(context.project, requested);
|
|
86
|
+
if (!requestedCheck.ok) {
|
|
87
|
+
return { ok: false, status: 400, error: requestedCheck.error };
|
|
88
|
+
}
|
|
89
|
+
if (!ticket.origin_conversation_id) {
|
|
90
|
+
return { ok: true, provider: requestedCheck.provider };
|
|
91
|
+
}
|
|
92
|
+
const originProvider = resolveConversationProvider(context, ticket.origin_conversation_id);
|
|
93
|
+
if (!originProvider) {
|
|
94
|
+
return { ok: false, status: 409, error: 'ticket_origin_conversation_not_found' };
|
|
95
|
+
}
|
|
96
|
+
const originCheck = (0, provider_selection_1.validateRequestedProvider)(context.project, originProvider);
|
|
97
|
+
if (!originCheck.ok) {
|
|
98
|
+
return { ok: false, status: 409, error: originCheck.error };
|
|
99
|
+
}
|
|
100
|
+
const hasExplicitRequest = requested !== undefined && requested !== null && requested !== '';
|
|
101
|
+
if (hasExplicitRequest && requestedCheck.provider !== originCheck.provider) {
|
|
102
|
+
return {
|
|
103
|
+
ok: false,
|
|
104
|
+
status: 400,
|
|
105
|
+
error: `provider '${requestedCheck.provider}' does not match ticket origin provider '${originCheck.provider}'`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return { ok: true, provider: originCheck.provider };
|
|
109
|
+
}
|
|
67
110
|
/**
|
|
68
111
|
* Add Spec on a Jira-backed project: promote the freshly-created LOCAL ticket to
|
|
69
112
|
* a Jira issue (best-effort, server-side). The per-spec `createLocal` escape
|
|
@@ -182,10 +225,12 @@ function registerTicketsRoutes(deps) {
|
|
|
182
225
|
let hasAttachments = false;
|
|
183
226
|
let baseUserPrompt = `Generate a spec for the following idea:\n\n${idea.trim()}`;
|
|
184
227
|
let imageFlags = [];
|
|
228
|
+
let imagePaths = [];
|
|
185
229
|
if (attachmentIds.length > 0 && pendingSpecId) {
|
|
186
230
|
try {
|
|
187
231
|
const extracted = await attachment_manager_1.attachmentManager.getClaudeArgs(project.slug, pendingSpecId, attachmentIds);
|
|
188
232
|
imageFlags = extracted.imageFlags;
|
|
233
|
+
imagePaths = extracted.imagePaths;
|
|
189
234
|
if (extracted.textBlocks.length > 0) {
|
|
190
235
|
hasAttachments = true;
|
|
191
236
|
baseUserPrompt = `${baseUserPrompt}\n\n## Attached Resources\n\n${extracted.textBlocks.join('\n\n')}`;
|
|
@@ -203,9 +248,9 @@ function registerTicketsRoutes(deps) {
|
|
|
203
248
|
// Awareness controls; Quick still keeps Contract Refine as a top-level
|
|
204
249
|
// field for the refine scheduler.
|
|
205
250
|
const rawScope = req.body?.contextScope;
|
|
206
|
-
// Contract Layer is
|
|
207
|
-
//
|
|
208
|
-
const quickContractRefine = provider !==
|
|
251
|
+
// Contract Layer is available only when the selected adapter advertises a
|
|
252
|
+
// safe structured-action implementation.
|
|
253
|
+
const quickContractRefine = (0, providers_1.getAdapter)(provider).capabilities.structuredActions !== true
|
|
209
254
|
? false
|
|
210
255
|
: typeof req.body?.contractRefine === 'boolean'
|
|
211
256
|
? req.body.contractRefine
|
|
@@ -224,6 +269,18 @@ function registerTicketsRoutes(deps) {
|
|
|
224
269
|
contractRefine: quickContractRefine,
|
|
225
270
|
userMcp: typeof rawScope?.userMcp === 'boolean' ? rawScope.userMcp : false,
|
|
226
271
|
};
|
|
272
|
+
const adapter = (0, providers_1.getAdapter)(provider);
|
|
273
|
+
const quickToolPolicy = quickScope.full
|
|
274
|
+
? ((0, runtime_1.supportsToolPolicy)(adapter, 'read-only') ? 'read-only' : null)
|
|
275
|
+
: (0, runtime_1.pureOutputToolPolicy)(adapter);
|
|
276
|
+
if (!quickToolPolicy) {
|
|
277
|
+
res.status(409).json({
|
|
278
|
+
error: 'provider_tool_policy_unsupported',
|
|
279
|
+
provider,
|
|
280
|
+
requiredPolicy: quickScope.full ? 'read-only' : 'pure-output',
|
|
281
|
+
});
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
227
284
|
// Persist Quick mode Contract Refine choice (per-project last value).
|
|
228
285
|
(0, db_1.setQuickContractRefineLast)(ctx(req).db, quickContractRefine);
|
|
229
286
|
// Relocate-artifacts: tickets read from the workspace when relocated.
|
|
@@ -278,24 +335,26 @@ function registerTicketsRoutes(deps) {
|
|
|
278
335
|
// same way. For codex the system prompt folds into the user prompt
|
|
279
336
|
// (no --system-prompt flag) and the extra Claude-only flags are ignored
|
|
280
337
|
// by the codex adapter (it doesn't read extraArgs that don't apply).
|
|
281
|
-
const adapter = (0, providers_1.getAdapter)(provider);
|
|
282
338
|
const toolFlags = provider === 'claude' ? (0, context_scope_1.toolFlagsForScope)(quickScope) : { args: [] };
|
|
283
339
|
// Full scope grants Read/Grep/Glob. The model spends turns exploring the
|
|
284
340
|
// repo before it writes the spec; 6 was too tight (a few tool calls on a
|
|
285
341
|
// sparse/empty repo hit error_max_turns → exit 1 → opaque failure). 15
|
|
286
342
|
// leaves comfortable headroom while --max-turns still bounds runaway loops.
|
|
287
343
|
const claudeMaxTurns = quickScope.full ? 15 : (hasAttachments ? 3 : 1);
|
|
288
|
-
const
|
|
344
|
+
const specGenOptions = {
|
|
289
345
|
prompt: userPrompt,
|
|
290
346
|
systemPrompt,
|
|
291
347
|
model: resolvedModel,
|
|
348
|
+
toolPolicy: quickToolPolicy,
|
|
292
349
|
maxTurns: provider === 'claude' ? claudeMaxTurns : undefined,
|
|
293
350
|
extraArgs: provider === 'claude' ? [...toolFlags.args, ...imageFlags] : undefined,
|
|
294
351
|
// "My approved MCPs" (scope.userMcp) loads the developer's user-scope,
|
|
295
352
|
// plugin, and connector MCP servers (claude-only). Quick already spawns
|
|
296
353
|
// from project.path so project `.mcp.json` is discovered without a flag.
|
|
297
|
-
loadUserEnv:
|
|
298
|
-
|
|
354
|
+
loadUserEnv: adapter.capabilities.userMcp === true && quickScope.userMcp,
|
|
355
|
+
imagePaths: adapter.capabilities.supportsImageInput ? imagePaths : undefined,
|
|
356
|
+
};
|
|
357
|
+
const args = adapter.buildArgs('spec-gen', specGenOptions);
|
|
299
358
|
const binary = adapter.binary;
|
|
300
359
|
// Relocate-artifacts gate: spawn from the workspace + SPECRAILS_REPO_DIR when
|
|
301
360
|
// relocated, else cwd = project.path + process.env (byte-identical legacy).
|
|
@@ -310,7 +369,7 @@ function registerTicketsRoutes(deps) {
|
|
|
310
369
|
// POSIX argv path unchanged.
|
|
311
370
|
console.log(`[project-router] spec-gen spawn: ${binary} (cwd=${specGenExec.cwd}, requestId=${requestId})`);
|
|
312
371
|
const child = (0, cli_prompt_1.spawnAiCli)(binary, args, {
|
|
313
|
-
env: specGenEnv,
|
|
372
|
+
env: (0, runtime_1.buildProviderEnv)(adapter, specGenOptions, specGenEnv),
|
|
314
373
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
315
374
|
cwd: specGenExec.cwd,
|
|
316
375
|
});
|
|
@@ -386,80 +445,23 @@ function registerTicketsRoutes(deps) {
|
|
|
386
445
|
stdoutReader.on('line', (line) => {
|
|
387
446
|
if (!admission.isCurrent())
|
|
388
447
|
return;
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
adapterEvents.push(adapterEv);
|
|
448
|
+
const eventsForLine = (0, runtime_1.parseStreamEvents)(adapter, line);
|
|
449
|
+
adapterEvents.push(...eventsForLine);
|
|
392
450
|
let parsed = null;
|
|
393
451
|
try {
|
|
394
452
|
parsed = JSON.parse(line);
|
|
395
453
|
}
|
|
396
454
|
catch { /* skip */ }
|
|
397
|
-
if (
|
|
398
|
-
return;
|
|
399
|
-
if (provider === 'codex') {
|
|
400
|
-
// Codex `exec --json` emits one event per line. Capture the final
|
|
401
|
-
// `turn.completed` for usage extraction, and accumulate ONLY the
|
|
402
|
-
// assistant_message text — never the command_execution items or
|
|
403
|
-
// wrapper events, otherwise the raw JSONL ends up in the ticket
|
|
404
|
-
// description.
|
|
405
|
-
if (parsed.type === 'turn.completed') {
|
|
406
|
-
lastResultEvent = parsed;
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
if (parsed.type !== 'item.completed')
|
|
410
|
-
return;
|
|
411
|
-
const item = parsed.item;
|
|
412
|
-
if (!item || item.type !== 'agent_message')
|
|
413
|
-
return;
|
|
414
|
-
const newText = (item.text ?? '').trim();
|
|
415
|
-
if (!newText)
|
|
416
|
-
return;
|
|
417
|
-
// Each agent_message is a complete chunk — separate with a blank
|
|
418
|
-
// line so the parser regexes match cleanly across chunks.
|
|
419
|
-
buffer += (buffer.endsWith('\n') || buffer.length === 0 ? '' : '\n') + newText + '\n';
|
|
420
|
-
const msg = {
|
|
421
|
-
type: 'spec_gen_stream', projectId, requestId,
|
|
422
|
-
delta: newText + '\n', timestamp: new Date().toISOString(),
|
|
423
|
-
};
|
|
424
|
-
broadcast(msg);
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
if (provider === 'claude') {
|
|
428
|
-
// Claude path.
|
|
429
|
-
if (parsed.type === 'result') {
|
|
430
|
-
lastResultEvent = parsed;
|
|
431
|
-
}
|
|
432
|
-
if (parsed.type === 'assistant') {
|
|
433
|
-
const msg = parsed.message;
|
|
434
|
-
const texts = (msg?.content ?? [])
|
|
435
|
-
.filter((c) => c.type === 'text')
|
|
436
|
-
.map((c) => c.text ?? '');
|
|
437
|
-
const newText = texts.join('');
|
|
438
|
-
if (newText) {
|
|
439
|
-
buffer += newText;
|
|
440
|
-
const wsMsg = {
|
|
441
|
-
type: 'spec_gen_stream', projectId, requestId,
|
|
442
|
-
delta: newText, timestamp: new Date().toISOString(),
|
|
443
|
-
};
|
|
444
|
-
broadcast(wsMsg);
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
449
|
-
// Adapter-driven path (gemini + future providers): the adapter already
|
|
450
|
-
// parsed this line into a structured AdapterEvent above (`adapterEv`).
|
|
451
|
-
// Accumulate assistant text deltas into the buffer and capture the result
|
|
452
|
-
// event for usage extraction — mirrors the ai-edit endpoint's else branch.
|
|
453
|
-
// Without this, gemini (which emits `type:'message'`, not `type:'assistant'`)
|
|
454
|
-
// would never accumulate text → empty buffer → "Empty response from AI".
|
|
455
|
-
if (adapterEv?.kind === 'result') {
|
|
455
|
+
if (parsed && eventsForLine.some((event) => event.kind === 'result')) {
|
|
456
456
|
lastResultEvent = parsed;
|
|
457
457
|
}
|
|
458
|
-
|
|
459
|
-
|
|
458
|
+
for (const event of eventsForLine) {
|
|
459
|
+
if (event.kind !== 'text-delta' || !event.text)
|
|
460
|
+
continue;
|
|
461
|
+
buffer += event.text;
|
|
460
462
|
const wsMsg = {
|
|
461
463
|
type: 'spec_gen_stream', projectId, requestId,
|
|
462
|
-
delta:
|
|
464
|
+
delta: event.text, timestamp: new Date().toISOString(),
|
|
463
465
|
};
|
|
464
466
|
broadcast(wsMsg);
|
|
465
467
|
}
|
|
@@ -569,14 +571,8 @@ function registerTicketsRoutes(deps) {
|
|
|
569
571
|
// Add Spec → Jira: promote the new ticket to a Jira issue when the
|
|
570
572
|
// project is Jira-backed (unless the user opted to keep it local).
|
|
571
573
|
void maybePromoteSpecToJira(ctx(req), created.id, req.body?.createLocal === true, broadcast);
|
|
572
|
-
// Quick mode Contract Refine
|
|
573
|
-
|
|
574
|
-
// Quick refine path asynchronously. Claude-only today — codex
|
|
575
|
-
// contract refine isn't wired (the spawn hardcodes the `claude`
|
|
576
|
-
// binary). Skip silently on codex projects so the ticket lands
|
|
577
|
-
// without the misleading "Contract layer skipped — model_error"
|
|
578
|
-
// toast that the refine kill-switch would otherwise emit.
|
|
579
|
-
if (quickContractRefine && created && provider === 'claude') {
|
|
574
|
+
// Quick mode Contract Refine runs on the same selected provider.
|
|
575
|
+
if (quickContractRefine && created) {
|
|
580
576
|
const refineTicketId = created.id;
|
|
581
577
|
const refineTitle = created.title;
|
|
582
578
|
const refineDescription = created.description;
|
|
@@ -590,16 +586,13 @@ function registerTicketsRoutes(deps) {
|
|
|
590
586
|
projectSlug: project.slug,
|
|
591
587
|
projectPath: project.path,
|
|
592
588
|
projectName: project.name,
|
|
589
|
+
providerId: provider,
|
|
593
590
|
broadcast: broadcast,
|
|
594
591
|
}, refineTicketId, refineTitle, refineDescription, refineModel).catch((err) => {
|
|
595
592
|
console.error('[project-router] runContractRefineForQuick error:', err);
|
|
596
593
|
});
|
|
597
594
|
});
|
|
598
595
|
}
|
|
599
|
-
else if (quickContractRefine && created && provider === 'codex') {
|
|
600
|
-
console.log(`[project-router] quick contract refine skipped for codex project (ticket #${created.id}); ` +
|
|
601
|
-
`feature is claude-only today`);
|
|
602
|
-
}
|
|
603
596
|
}
|
|
604
597
|
catch (err) {
|
|
605
598
|
console.error('[project-router] generate-spec ticket creation error:', err);
|
|
@@ -1022,17 +1015,18 @@ function registerTicketsRoutes(deps) {
|
|
|
1022
1015
|
// Add Spec (Explore) → Jira: promote the committed ticket to a Jira issue
|
|
1023
1016
|
// when the project is Jira-backed (unless the user opted to keep it local).
|
|
1024
1017
|
void maybePromoteSpecToJira(ctx(req), created.id, body.createLocal === true, broadcast);
|
|
1025
|
-
// Fire Contract Refine post-commit (fire-and-forget). Toggle
|
|
1026
|
-
// are checked inside runContractRefine.
|
|
1027
|
-
// contract refine isn't wired (the spawn hardcodes the `claude`
|
|
1028
|
-
// binary). Skip silently on codex projects.
|
|
1018
|
+
// Fire Contract Refine post-commit (fire-and-forget). Toggle, kill-switch,
|
|
1019
|
+
// and provider capability are checked inside runContractRefine.
|
|
1029
1020
|
// Gate on the CONVERSATION's own engine, not the project's PRIMARY
|
|
1030
1021
|
// provider: a multi-provider project with a non-claude primary can still
|
|
1031
1022
|
// run an Explore on the claude engine + opt into Contract Refine.
|
|
1032
1023
|
const convoProvider = conversationId
|
|
1033
|
-
? (
|
|
1024
|
+
? resolveConversationProvider(ctx(req), conversationId)
|
|
1034
1025
|
: null;
|
|
1035
|
-
if (conversationId &&
|
|
1026
|
+
if (conversationId &&
|
|
1027
|
+
created &&
|
|
1028
|
+
convoProvider &&
|
|
1029
|
+
(0, providers_1.getAdapter)(convoProvider).capabilities.structuredActions === true) {
|
|
1036
1030
|
const createdTicketId = created.id;
|
|
1037
1031
|
const convoId = conversationId;
|
|
1038
1032
|
console.log(`[project-router] from-draft hook: scheduling refine ticket=${createdTicketId} conv=${convoId}`);
|
|
@@ -1043,13 +1037,16 @@ function registerTicketsRoutes(deps) {
|
|
|
1043
1037
|
projectSlug: project.slug,
|
|
1044
1038
|
projectPath: project.path,
|
|
1045
1039
|
projectName: project.name,
|
|
1040
|
+
// A NULL conversation.provider means project primary. Thread the
|
|
1041
|
+
// already-resolved value so the runner never falls back to Claude.
|
|
1042
|
+
providerId: convoProvider,
|
|
1046
1043
|
broadcast: broadcast,
|
|
1047
1044
|
}, convoId, createdTicketId).catch((err) => {
|
|
1048
1045
|
console.error('[project-router] runContractRefine error:', err);
|
|
1049
1046
|
});
|
|
1050
1047
|
});
|
|
1051
1048
|
}
|
|
1052
|
-
else if (conversationId && created && convoProvider
|
|
1049
|
+
else if (conversationId && created && convoProvider) {
|
|
1053
1050
|
console.log(`[project-router] from-draft contract refine skipped for ${convoProvider} conversation (ticket #${created.id})`);
|
|
1054
1051
|
}
|
|
1055
1052
|
else if (!conversationId && draftTicketId === null && created && body.contractRefine === true) {
|
|
@@ -1065,7 +1062,8 @@ function registerTicketsRoutes(deps) {
|
|
|
1065
1062
|
const projectProviders = Array.isArray(project.providers) && project.providers.length > 0
|
|
1066
1063
|
? project.providers
|
|
1067
1064
|
: [project.provider].filter((p) => typeof p === 'string');
|
|
1068
|
-
|
|
1065
|
+
const refineProvider = projectProviders.find((id) => (0, providers_1.getAdapter)(id).capabilities.structuredActions === true);
|
|
1066
|
+
if (refineProvider) {
|
|
1069
1067
|
const refineTicketId = created.id;
|
|
1070
1068
|
const refineTitle = created.title;
|
|
1071
1069
|
const refineDescription = created.description;
|
|
@@ -1077,6 +1075,7 @@ function registerTicketsRoutes(deps) {
|
|
|
1077
1075
|
projectSlug: project.slug,
|
|
1078
1076
|
projectPath: project.path,
|
|
1079
1077
|
projectName: project.name,
|
|
1078
|
+
providerId: refineProvider,
|
|
1080
1079
|
broadcast: broadcast,
|
|
1081
1080
|
}, refineTicketId, refineTitle, refineDescription, null).catch((err) => {
|
|
1082
1081
|
console.error('[project-router] from-draft runContractRefineForQuick error:', err);
|
|
@@ -1084,7 +1083,7 @@ function registerTicketsRoutes(deps) {
|
|
|
1084
1083
|
});
|
|
1085
1084
|
}
|
|
1086
1085
|
else {
|
|
1087
|
-
console.log(`[project-router] from-draft contract refine skipped (ticket #${created.id}):
|
|
1086
|
+
console.log(`[project-router] from-draft contract refine skipped (ticket #${created.id}): no capable provider installed`);
|
|
1088
1087
|
}
|
|
1089
1088
|
}
|
|
1090
1089
|
}
|
|
@@ -1205,10 +1204,6 @@ function registerTicketsRoutes(deps) {
|
|
|
1205
1204
|
res.status(409).json({ error: 'feature_disabled_by_env' });
|
|
1206
1205
|
return;
|
|
1207
1206
|
}
|
|
1208
|
-
if (project.provider === 'codex') {
|
|
1209
|
-
res.status(409).json({ error: 'contract_refine_unsupported_for_codex' });
|
|
1210
|
-
return;
|
|
1211
|
-
}
|
|
1212
1207
|
// Validate the ticket exists.
|
|
1213
1208
|
try {
|
|
1214
1209
|
const filePath = ticketPath(req);
|
|
@@ -1218,6 +1213,16 @@ function registerTicketsRoutes(deps) {
|
|
|
1218
1213
|
res.status(404).json({ error: 'ticket not found' });
|
|
1219
1214
|
return;
|
|
1220
1215
|
}
|
|
1216
|
+
const refineProviderCheck = resolveTicketBoundProvider({ project, db }, ticket, req.body?.provider ?? req.body?.aiEngine);
|
|
1217
|
+
if (!refineProviderCheck.ok) {
|
|
1218
|
+
res.status(refineProviderCheck.status).json({ error: refineProviderCheck.error });
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
const refineProvider = refineProviderCheck.provider;
|
|
1222
|
+
if ((0, providers_1.getAdapter)(refineProvider).capabilities.structuredActions !== true) {
|
|
1223
|
+
res.status(409).json({ error: `contract_refine_unsupported_for_${refineProvider}` });
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1221
1226
|
if (!ticket.origin_conversation_id) {
|
|
1222
1227
|
// No Explore origin (agent-authored commit_draft / Quick spec that
|
|
1223
1228
|
// skipped the refine): fall back to the Quick-style refine, seeded
|
|
@@ -1234,6 +1239,7 @@ function registerTicketsRoutes(deps) {
|
|
|
1234
1239
|
projectSlug: project.slug,
|
|
1235
1240
|
projectPath: project.path,
|
|
1236
1241
|
projectName: project.name,
|
|
1242
|
+
providerId: refineProvider,
|
|
1237
1243
|
broadcast: broadcast,
|
|
1238
1244
|
}, ticketId, seedTitle, seedBody, null).catch((err) => {
|
|
1239
1245
|
console.error('[project-router] retry runContractRefineForQuick error:', err);
|
|
@@ -1250,6 +1256,7 @@ function registerTicketsRoutes(deps) {
|
|
|
1250
1256
|
projectSlug: project.slug,
|
|
1251
1257
|
projectPath: project.path,
|
|
1252
1258
|
projectName: project.name,
|
|
1259
|
+
providerId: refineProvider,
|
|
1253
1260
|
broadcast: broadcast,
|
|
1254
1261
|
ignoreConversationScope: true,
|
|
1255
1262
|
}, convoId, ticketId).catch((err) => {
|
|
@@ -1284,9 +1291,27 @@ function registerTicketsRoutes(deps) {
|
|
|
1284
1291
|
res.status(statusCode).json({ error: 'ineligible', reason: gate.reason });
|
|
1285
1292
|
return;
|
|
1286
1293
|
}
|
|
1294
|
+
const ticket = store.tickets[String(ticketId)];
|
|
1287
1295
|
const rawMode = typeof req.body?.mode === 'string' ? req.body.mode : 'simple';
|
|
1288
1296
|
const mode = rawMode === 'full' ? 'full' : 'simple';
|
|
1297
|
+
const providerCheck = resolveTicketBoundProvider({ project, db }, ticket, req.body?.provider ?? req.body?.aiEngine);
|
|
1298
|
+
if (!providerCheck.ok) {
|
|
1299
|
+
res.status(providerCheck.status).json({ error: providerCheck.error });
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
const smashProvider = providerCheck.provider;
|
|
1303
|
+
if ((0, providers_1.getAdapter)(smashProvider).capabilities.structuredActions !== true) {
|
|
1304
|
+
res.status(409).json({ error: `smash_unsupported_for_${smashProvider}` });
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1289
1307
|
const model = typeof req.body?.model === 'string' && req.body.model.length > 0 ? req.body.model : null;
|
|
1308
|
+
if (model && !(0, spec_models_1.isValidModelForProvider)(model, smashProvider)) {
|
|
1309
|
+
res.status(400).json({
|
|
1310
|
+
error: `model is not valid for provider "${smashProvider}"`,
|
|
1311
|
+
allowed: (0, spec_models_1.getModelsForProvider)(smashProvider),
|
|
1312
|
+
});
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1290
1315
|
res.status(202).json({ scheduled: true, mode });
|
|
1291
1316
|
process.nextTick(() => {
|
|
1292
1317
|
void (0, smash_runner_1.runSmash)({
|
|
@@ -1295,6 +1320,7 @@ function registerTicketsRoutes(deps) {
|
|
|
1295
1320
|
projectSlug: project.slug,
|
|
1296
1321
|
projectPath: project.path,
|
|
1297
1322
|
projectName: project.name,
|
|
1323
|
+
providerId: smashProvider,
|
|
1298
1324
|
// Relocate-artifacts: pass the gated tickets-store path so SMASH
|
|
1299
1325
|
// reads/writes the same store the rails load (workspace when
|
|
1300
1326
|
// relocated), not a stale repo copy.
|
|
@@ -1364,7 +1390,27 @@ function registerTicketsRoutes(deps) {
|
|
|
1364
1390
|
}
|
|
1365
1391
|
const { project, broadcast, ticketWatcher } = ctx(req);
|
|
1366
1392
|
try {
|
|
1393
|
+
// This destructive helper exists solely as the first half of Re-SMASH.
|
|
1394
|
+
// Enforce the same provider capability before touching the ticket store;
|
|
1395
|
+
// otherwise a Kimi Re-SMASH could delete every existing child and only
|
|
1396
|
+
// then have POST /smash reject the unsupported structured action.
|
|
1367
1397
|
const filePath = ticketPath(req);
|
|
1398
|
+
const store = (0, ticket_store_1.readStore)(filePath);
|
|
1399
|
+
const ticket = store.tickets[String(ticketId)];
|
|
1400
|
+
if (!ticket) {
|
|
1401
|
+
res.status(404).json({ error: 'ticket not found' });
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
const providerCheck = resolveTicketBoundProvider(ctx(req), ticket, req.query.provider ?? req.body?.provider ?? req.body?.aiEngine);
|
|
1405
|
+
if (!providerCheck.ok) {
|
|
1406
|
+
res.status(providerCheck.status).json({ error: providerCheck.error });
|
|
1407
|
+
return;
|
|
1408
|
+
}
|
|
1409
|
+
const smashProvider = providerCheck.provider;
|
|
1410
|
+
if ((0, providers_1.getAdapter)(smashProvider).capabilities.structuredActions !== true) {
|
|
1411
|
+
res.status(409).json({ error: `smash_unsupported_for_${smashProvider}` });
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1368
1414
|
const result = (0, smash_runner_1.applyDeleteEpicChildren)(filePath, ticketId);
|
|
1369
1415
|
// Pass the real post-write revision (not 0) so the chokidar echo is
|
|
1370
1416
|
// suppressed; a hardcoded 0 never matches the on-disk revision and
|
|
@@ -1590,15 +1636,33 @@ function registerTicketsRoutes(deps) {
|
|
|
1590
1636
|
const priorProposal = typeof priorProposalRaw === 'string' && priorProposalRaw.length > 0 ? priorProposalRaw : null;
|
|
1591
1637
|
const isRefinement = priorProposal !== null;
|
|
1592
1638
|
const { project, broadcast } = ctx(req);
|
|
1593
|
-
const
|
|
1639
|
+
const providerCheck = (0, provider_selection_1.validateRequestedProvider)(project, req.body?.provider ?? req.body?.aiEngine);
|
|
1640
|
+
if (!providerCheck.ok) {
|
|
1641
|
+
res.status(400).json({ error: providerCheck.error });
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1644
|
+
const provider = providerCheck.provider;
|
|
1594
1645
|
const adapter = (0, providers_1.getAdapter)(provider);
|
|
1646
|
+
if (!(0, runtime_1.supportsToolPolicy)(adapter, 'read-only')) {
|
|
1647
|
+
res.status(409).json({
|
|
1648
|
+
error: 'provider_tool_policy_unsupported',
|
|
1649
|
+
provider,
|
|
1650
|
+
requiredPolicy: 'read-only',
|
|
1651
|
+
});
|
|
1652
|
+
return;
|
|
1653
|
+
}
|
|
1595
1654
|
const requestId = (0, ids_1.newId)();
|
|
1596
1655
|
const projectId = project.id;
|
|
1597
1656
|
const admission = (0, process_admission_1.captureProcessAdmission)(projectId);
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1657
|
+
const requestedAiEditModel = typeof req.body?.model === 'string' ? req.body.model : adapter.defaultModel();
|
|
1658
|
+
if (!(0, spec_models_1.isValidModelForProvider)(requestedAiEditModel, provider)) {
|
|
1659
|
+
res.status(400).json({
|
|
1660
|
+
error: `model is not valid for provider "${provider}"`,
|
|
1661
|
+
allowed: (0, spec_models_1.getModelsForProvider)(provider),
|
|
1662
|
+
});
|
|
1663
|
+
return;
|
|
1664
|
+
}
|
|
1665
|
+
const aiEditModel = requestedAiEditModel;
|
|
1602
1666
|
// Build the focused pre-prompt
|
|
1603
1667
|
const baseRules = `- Output format MUST be exactly:\n` +
|
|
1604
1668
|
` TITLE: <one-line spec title>\n` +
|
|
@@ -1635,10 +1699,12 @@ function registerTicketsRoutes(deps) {
|
|
|
1635
1699
|
`## User Instructions\n\n${instructions.trim()}\n\n` +
|
|
1636
1700
|
`Output the modified TITLE line followed by the modified description now.`;
|
|
1637
1701
|
let imageFlags = [];
|
|
1702
|
+
let aiEditImagePaths = [];
|
|
1638
1703
|
if (attachmentIds.length > 0) {
|
|
1639
1704
|
try {
|
|
1640
1705
|
const extracted = await attachment_manager_1.attachmentManager.getClaudeArgs(project.slug, ticketId, attachmentIds);
|
|
1641
1706
|
imageFlags = extracted.imageFlags;
|
|
1707
|
+
aiEditImagePaths = extracted.imagePaths;
|
|
1642
1708
|
if (extracted.textBlocks.length > 0) {
|
|
1643
1709
|
systemPrompt = `${systemPrompt}\n\n${attachment_manager_1.USER_ATTACHMENT_SYSTEM_NOTE}`;
|
|
1644
1710
|
userPrompt = `${userPrompt}\n\n## Attached Files\n\n${extracted.textBlocks.join('\n\n')}`;
|
|
@@ -1652,43 +1718,21 @@ function registerTicketsRoutes(deps) {
|
|
|
1652
1718
|
res.status(409).json({ error: 'Project is being removed' });
|
|
1653
1719
|
return;
|
|
1654
1720
|
}
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
// AI Edit may ground prose in the repo, but it never needs shell,
|
|
1667
|
-
// network, MCP, or filesystem mutation authority.
|
|
1668
|
-
'--tools', 'Read,Grep,Glob',
|
|
1669
|
-
'--output-format', 'stream-json',
|
|
1670
|
-
'--verbose',
|
|
1671
|
-
'--max-turns', '4',
|
|
1672
|
-
...imageFlags,
|
|
1673
|
-
'--system-prompt', systemPrompt,
|
|
1674
|
-
'-p', userPrompt,
|
|
1675
|
-
];
|
|
1676
|
-
}
|
|
1677
|
-
else {
|
|
1678
|
-
// Adapter-driven path (gemini + any future provider): binary, argv, and
|
|
1679
|
-
// stream parsing all come from the registered adapter — no per-provider
|
|
1680
|
-
// hardcoding. The claude/codex shapes above are kept byte-identical.
|
|
1681
|
-
binary = adapter.binary;
|
|
1682
|
-
args = adapter.buildArgs('agent-refine', {
|
|
1683
|
-
prompt: userPrompt,
|
|
1684
|
-
systemPrompt,
|
|
1685
|
-
model: adapter.defaultModel(),
|
|
1686
|
-
});
|
|
1687
|
-
}
|
|
1721
|
+
const binary = adapter.binary;
|
|
1722
|
+
const aiEditOptions = {
|
|
1723
|
+
prompt: userPrompt,
|
|
1724
|
+
systemPrompt,
|
|
1725
|
+
model: aiEditModel,
|
|
1726
|
+
toolPolicy: 'read-only',
|
|
1727
|
+
maxTurns: 4,
|
|
1728
|
+
extraArgs: provider === 'claude' ? imageFlags : undefined,
|
|
1729
|
+
imagePaths: adapter.capabilities.supportsImageInput ? aiEditImagePaths : undefined,
|
|
1730
|
+
};
|
|
1731
|
+
const args = adapter.buildArgs('agent-refine', aiEditOptions);
|
|
1688
1732
|
// spawnAiCli reroutes multi-line argv values through stdin on Windows.
|
|
1689
1733
|
console.log(`[project-router] ai-edit spawn: ${binary} (cwd=${project.path}, requestId=${requestId})`);
|
|
1690
1734
|
const child = (0, cli_prompt_1.spawnAiCli)(binary, args, {
|
|
1691
|
-
env:
|
|
1735
|
+
env: (0, runtime_1.buildProviderEnv)(adapter, aiEditOptions),
|
|
1692
1736
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
1693
1737
|
cwd: project.path,
|
|
1694
1738
|
});
|
|
@@ -1730,56 +1774,18 @@ function registerTicketsRoutes(deps) {
|
|
|
1730
1774
|
const aiEditStartedAt = new Date().toISOString();
|
|
1731
1775
|
const stdoutReader = (0, readline_1.createInterface)({ input: child.stdout, crlfDelay: Infinity });
|
|
1732
1776
|
stdoutReader.on('line', (line) => {
|
|
1733
|
-
const
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
buffer += line + '\n';
|
|
1777
|
+
const eventsForLine = (0, runtime_1.parseStreamEvents)(adapter, line);
|
|
1778
|
+
adapterEvents.push(...eventsForLine);
|
|
1779
|
+
for (const event of eventsForLine) {
|
|
1780
|
+
if (event.kind === 'text-delta' && event.text) {
|
|
1781
|
+
buffer += event.text;
|
|
1739
1782
|
const msg = {
|
|
1740
1783
|
type: 'ticket_ai_edit_stream', projectId, ticketId: Number(ticketId),
|
|
1741
|
-
requestId, delta:
|
|
1784
|
+
requestId, delta: event.text, timestamp: new Date().toISOString(),
|
|
1742
1785
|
};
|
|
1743
1786
|
broadcast(msg);
|
|
1744
1787
|
}
|
|
1745
1788
|
}
|
|
1746
|
-
else if (provider === 'claude') {
|
|
1747
|
-
let parsed = null;
|
|
1748
|
-
try {
|
|
1749
|
-
parsed = JSON.parse(line);
|
|
1750
|
-
}
|
|
1751
|
-
catch { /* skip */ }
|
|
1752
|
-
if (!parsed)
|
|
1753
|
-
return;
|
|
1754
|
-
if (parsed.type === 'assistant') {
|
|
1755
|
-
const msg = parsed.message;
|
|
1756
|
-
const texts = (msg?.content ?? [])
|
|
1757
|
-
.filter((c) => c.type === 'text')
|
|
1758
|
-
.map((c) => c.text ?? '');
|
|
1759
|
-
const newText = texts.join('');
|
|
1760
|
-
if (newText) {
|
|
1761
|
-
buffer += newText;
|
|
1762
|
-
const wsMsg = {
|
|
1763
|
-
type: 'ticket_ai_edit_stream', projectId, ticketId: Number(ticketId),
|
|
1764
|
-
requestId, delta: newText, timestamp: new Date().toISOString(),
|
|
1765
|
-
};
|
|
1766
|
-
broadcast(wsMsg);
|
|
1767
|
-
}
|
|
1768
|
-
}
|
|
1769
|
-
}
|
|
1770
|
-
else {
|
|
1771
|
-
// Adapter-driven parse (gemini + future providers): uniform AdapterEvent
|
|
1772
|
-
// stream — accumulate assistant text deltas (reuse the event parsed above).
|
|
1773
|
-
const ev = aev;
|
|
1774
|
-
if (ev?.kind === 'text-delta' && ev.text) {
|
|
1775
|
-
buffer += ev.text;
|
|
1776
|
-
const wsMsg = {
|
|
1777
|
-
type: 'ticket_ai_edit_stream', projectId, ticketId: Number(ticketId),
|
|
1778
|
-
requestId, delta: ev.text, timestamp: new Date().toISOString(),
|
|
1779
|
-
};
|
|
1780
|
-
broadcast(wsMsg);
|
|
1781
|
-
}
|
|
1782
|
-
}
|
|
1783
1789
|
});
|
|
1784
1790
|
child.on('close', (code) => {
|
|
1785
1791
|
_aiEditProcesses.delete(requestId);
|
|
@@ -134,6 +134,7 @@ function createProjectRouter(registry) {
|
|
|
134
134
|
fileSummaryManager: projectCtx.fileSummaryManager,
|
|
135
135
|
getTicketSpec: projectCtx.getTicketSpec,
|
|
136
136
|
fileStoryManager,
|
|
137
|
+
aiTransformProvider: storyAdapter.id,
|
|
137
138
|
// Relocate-artifacts: summary JSON OUTPUTS live in the workspace when
|
|
138
139
|
// relocated (source tree still read from project.path). Resolved per-call.
|
|
139
140
|
resolveSummaryRoot: () => {
|