specrails-desktop 2.30.3 → 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-CcBOllqm.js → ActivityFeedPage-CAhFRUO4.js} +1 -1
- package/client/dist/assets/{AgentBrowserCapture-BOJkZUj_.js → AgentBrowserCapture-Ct4G5lsE.js} +1 -1
- package/client/dist/assets/AgentModeCodePane-DQt9WIvu.js +2 -0
- package/client/dist/assets/{AgentModeJobsPane-C5tkU2Ed.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-DCmgYV86.js → DesktopAnalyticsPage-Dnf99E8e.js} +1 -1
- package/client/dist/assets/{DocsDialog-BXnOgm7V.js → DocsDialog-DHi36rZ4.js} +1 -1
- package/client/dist/assets/{DocsPage-nFkHh-Dc.js → DocsPage-DIdVrQz1.js} +1 -1
- package/client/dist/assets/{InteractiveJobComposer-qoeZjwVY.js → InteractiveJobComposer-BHeB2inv.js} +1 -1
- package/client/dist/assets/{JobDetailModal-ChRBB9bc.js → JobDetailModal-CF1SKUHH.js} +1 -1
- package/client/dist/assets/JobDetailPage-De3rY18h.js +1 -0
- package/client/dist/assets/{JobsPage-C_UEIXWA.js → JobsPage-CfAqqJTx.js} +1 -1
- package/client/dist/assets/{LoopBuilderPage-BV_wge0g.js → LoopBuilderPage-DSBMuTb3.js} +2 -2
- package/client/dist/assets/{LoopPreviewModal-Qk_Jaxo8.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-DEHcyt_I.js → ProjectSettingsDialog-DA23PSi6.js} +1 -1
- package/client/dist/assets/{TemplatePreviewModal-Bxbtnzob.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-CBhuoTO6.js → dist-js-BaP-xLOy.js} +1 -1
- package/client/dist/assets/{dist-js-eSxaoEw0.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/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-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-BIh67rGi.js +0 -2
- package/client/dist/assets/AgentsPage-CNlE_Uh4.js +0 -86
- package/client/dist/assets/AnalyticsPage-cYcJxWPn.js +0 -1
- package/client/dist/assets/CodePage-D4QYa4t1.js +0 -2
- package/client/dist/assets/JobDetailPage-BuOnlndB.js +0 -1
- package/client/dist/assets/LoopsPage-lLi6ax0p.js +0 -1
- package/client/dist/assets/PluginsPage-DZIFv9kw.js +0 -2
- package/client/dist/assets/TicketDetailModal-CqQ5gx7O.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-CewRMo0W.js +0 -76
- package/client/dist/assets/index-Dh9i4CnN.css +0 -2
- 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
|
@@ -201,10 +201,12 @@ class AgentRefineManager {
|
|
|
201
201
|
}
|
|
202
202
|
fs_1.default.writeFileSync(file, session.draft_body, 'utf8');
|
|
203
203
|
const maxVersion = this._db
|
|
204
|
-
.prepare(`SELECT COALESCE(MAX(version), 0) AS v FROM agent_versions
|
|
205
|
-
|
|
204
|
+
.prepare(`SELECT COALESCE(MAX(version), 0) AS v FROM agent_versions
|
|
205
|
+
WHERE provider = ? AND agent_name = ?`)
|
|
206
|
+
.get(this._adapter.id, session.agent_id).v;
|
|
206
207
|
const nextVersion = maxVersion + 1;
|
|
207
|
-
this._db.prepare(`INSERT INTO agent_versions (agent_name, version, body, created_at)
|
|
208
|
+
this._db.prepare(`INSERT INTO agent_versions (provider, agent_name, version, body, created_at)
|
|
209
|
+
VALUES (?, ?, ?, ?, ?)`).run(this._adapter.id, session.agent_id, nextVersion, session.draft_body, Date.now());
|
|
208
210
|
(0, agent_refine_db_1.updateRefineSession)(this._db, opts.refineId, { status: 'applied', phase: 'done' });
|
|
209
211
|
this._broadcast({
|
|
210
212
|
type: 'agent_refine_applied',
|
|
@@ -230,12 +232,6 @@ class AgentRefineManager {
|
|
|
230
232
|
throw new Error('manager_disposed');
|
|
231
233
|
}
|
|
232
234
|
_agentFile(agentId) {
|
|
233
|
-
// Per-provider on-disk layout:
|
|
234
|
-
// claude → <root>/.claude/agents/<agentId>.md
|
|
235
|
-
// codex → <root>/.codex/skills/<agentId>/SKILL.md
|
|
236
|
-
// Future providers add their own branch via the adapter; the projectDir
|
|
237
|
-
// is already provider-aware.
|
|
238
|
-
//
|
|
239
235
|
// Relocate-artifacts: custom-* agents are materialized by core into the
|
|
240
236
|
// WORKSPACE when the project is relocated (same place the rails load them
|
|
241
237
|
// from). Reading/writing `<project.path>/<providerDir>/agents` would touch a
|
|
@@ -244,15 +240,14 @@ class AgentRefineManager {
|
|
|
244
240
|
// rails consume. Legacy projects are byte-identical (root === project.path).
|
|
245
241
|
const exec = (0, workspace_resolution_1.resolveProjectExecution)({ path: this._projectPath });
|
|
246
242
|
const root = exec.relocated && exec.workspaceDir ? exec.workspaceDir : this._projectPath;
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}
|
|
250
|
-
return path_1.default.join(root, this._adapter.projectDirName, 'agents', `${agentId}.md`);
|
|
243
|
+
return this._adapter.customRolePath?.(root, agentId)
|
|
244
|
+
?? path_1.default.join(root, this._adapter.projectDirName, 'agents', `${agentId}.md`);
|
|
251
245
|
}
|
|
252
246
|
_currentVersion(agentId) {
|
|
253
247
|
const row = this._db
|
|
254
|
-
.prepare(`SELECT COALESCE(MAX(version), 0) AS v FROM agent_versions
|
|
255
|
-
|
|
248
|
+
.prepare(`SELECT COALESCE(MAX(version), 0) AS v FROM agent_versions
|
|
249
|
+
WHERE provider = ? AND agent_name = ?`)
|
|
250
|
+
.get(this._adapter.id, agentId);
|
|
256
251
|
return row.v;
|
|
257
252
|
}
|
|
258
253
|
_appendHistory(refineId, turn) {
|
|
@@ -272,12 +267,22 @@ class AgentRefineManager {
|
|
|
272
267
|
agentId: session.agent_id,
|
|
273
268
|
currentBody: bodyForFirstTurn ?? '',
|
|
274
269
|
userInstruction: instruction,
|
|
270
|
+
providerId: this._adapter.id,
|
|
275
271
|
})
|
|
276
272
|
: instruction;
|
|
277
273
|
const action = !isFirst && session.session_id && this._adapter.capabilities.nativeResume
|
|
278
274
|
? 'chat-resume'
|
|
279
275
|
: 'agent-refine';
|
|
280
276
|
const refineModel = this._adapter.defaultModel();
|
|
277
|
+
const supportedToolPolicies = this._adapter.capabilities.toolPolicies ?? [];
|
|
278
|
+
const toolPolicy = supportedToolPolicies.includes('none')
|
|
279
|
+
? 'none'
|
|
280
|
+
: supportedToolPolicies.includes('read-only')
|
|
281
|
+
? 'read-only'
|
|
282
|
+
: null;
|
|
283
|
+
if (!toolPolicy) {
|
|
284
|
+
throw new Error(`provider_tool_policy_unsupported:${this._adapter.id}`);
|
|
285
|
+
}
|
|
281
286
|
const buildOpts = {
|
|
282
287
|
prompt,
|
|
283
288
|
model: refineModel,
|
|
@@ -285,9 +290,8 @@ class AgentRefineManager {
|
|
|
285
290
|
// The model receives the complete current agent body in its prompt and
|
|
286
291
|
// must only return a replacement body. Repo tools cannot improve this
|
|
287
292
|
// contract and would turn prompt text into filesystem authority.
|
|
288
|
-
toolPolicy
|
|
293
|
+
toolPolicy,
|
|
289
294
|
};
|
|
290
|
-
const args = this._adapter.buildArgs(action, buildOpts);
|
|
291
295
|
let drafted = false;
|
|
292
296
|
this._bodyBuffers.set(refineId, '');
|
|
293
297
|
const turnStartedAt = new Date().toISOString();
|
|
@@ -300,14 +304,13 @@ class AgentRefineManager {
|
|
|
300
304
|
// Relocate-artifacts gate: spawn from the workspace (with SPECRAILS_REPO_DIR)
|
|
301
305
|
// when relocated, else cwd = project.path + empty env (byte-identical legacy).
|
|
302
306
|
const refineExec = (0, workspace_resolution_1.resolveProjectExecution)({ path: this._projectPath });
|
|
303
|
-
const refineEnv = refineExec.relocated ? { ...process.env, ...refineExec.env } :
|
|
307
|
+
const refineEnv = refineExec.relocated ? { ...process.env, ...refineExec.env } : process.env;
|
|
304
308
|
const run = await (0, spawn_lifecycle_1.runAiCliInvocation)({
|
|
305
309
|
adapter: this._adapter,
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
argv: args,
|
|
310
|
+
action,
|
|
311
|
+
buildOpts,
|
|
309
312
|
cwd: refineExec.cwd,
|
|
310
|
-
env: refineEnv,
|
|
313
|
+
env: (0, providers_1.buildProviderEnv)(this._adapter, buildOpts, refineEnv),
|
|
311
314
|
onSpawn: (child) => this._activeProcesses.set(refineId, child),
|
|
312
315
|
onSpawnError: (err) => { spawnState.err = err; },
|
|
313
316
|
onEvent: (ev) => {
|
|
@@ -369,7 +372,7 @@ class AgentRefineManager {
|
|
|
369
372
|
return; // cancel (BUG-LONGTAIL-01)
|
|
370
373
|
}
|
|
371
374
|
if (run.spawnFailed) {
|
|
372
|
-
this._emitError(refineId, `Failed to launch
|
|
375
|
+
this._emitError(refineId, `Failed to launch ${this._adapter.binary}: ${spawnState.err?.message ?? 'spawn error'}`);
|
|
373
376
|
(0, agent_refine_db_1.updateRefineSession)(this._db, refineId, { status: 'error', phase: 'idle' });
|
|
374
377
|
return;
|
|
375
378
|
}
|
|
@@ -377,13 +380,16 @@ class AgentRefineManager {
|
|
|
377
380
|
const adapterEvents = run.events;
|
|
378
381
|
const code = run.code;
|
|
379
382
|
const stderr = run.stderrTail;
|
|
383
|
+
const providerError = adapterEvents.find((event) => event.kind === 'error')?.message;
|
|
380
384
|
// ai_invocations capture (surface='ai-edit'). One row per refine turn.
|
|
381
|
-
const invStatus = code === 0 && fullDraft.trim() ? 'success' : 'failed';
|
|
385
|
+
const invStatus = code === 0 && !providerError && fullDraft.trim() ? 'success' : 'failed';
|
|
382
386
|
this._recordRefineInvocation(refineId, adapterEvents, refineModel, invStatus, turnStartedAt);
|
|
383
|
-
if (code !== 0 || !fullDraft.trim()) {
|
|
384
|
-
this._emitError(refineId,
|
|
385
|
-
?
|
|
386
|
-
:
|
|
387
|
+
if (code !== 0 || providerError || !fullDraft.trim()) {
|
|
388
|
+
this._emitError(refineId, providerError
|
|
389
|
+
? providerError
|
|
390
|
+
: code !== 0
|
|
391
|
+
? `${this._adapter.binary} exited with code ${code}${stderr ? `: ${stderr.slice(-300)}` : ''}`
|
|
392
|
+
: `${this._adapter.binary} returned empty output`);
|
|
387
393
|
(0, agent_refine_db_1.updateRefineSession)(this._db, refineId, { status: 'error', phase: 'idle' });
|
|
388
394
|
return;
|
|
389
395
|
}
|
|
@@ -442,7 +448,11 @@ class AgentRefineManager {
|
|
|
442
448
|
if (!this._projectId)
|
|
443
449
|
return;
|
|
444
450
|
try {
|
|
445
|
-
const
|
|
451
|
+
const finishedAt = new Date().toISOString();
|
|
452
|
+
const { result: normalised, estimated } = (0, result_event_1.finaliseInvocationResult)(this._adapter, adapterEvents, {
|
|
453
|
+
fallbackModel: model,
|
|
454
|
+
durationMs: Math.max(0, Date.parse(finishedAt) - Date.parse(startedAt)),
|
|
455
|
+
});
|
|
446
456
|
(0, ai_invocations_1.recordInvocation)(this._db, {
|
|
447
457
|
id: (0, crypto_1.randomUUID)(),
|
|
448
458
|
project_id: this._projectId,
|
|
@@ -451,7 +461,7 @@ class AgentRefineManager {
|
|
|
451
461
|
surface_ref_id: refineId,
|
|
452
462
|
status,
|
|
453
463
|
started_at: startedAt,
|
|
454
|
-
finished_at:
|
|
464
|
+
finished_at: finishedAt,
|
|
455
465
|
total_cost_usd_estimated: estimated,
|
|
456
466
|
...normalised,
|
|
457
467
|
});
|
|
@@ -466,11 +476,12 @@ class AgentRefineManager {
|
|
|
466
476
|
return;
|
|
467
477
|
(0, agent_refine_db_1.updateRefineSession)(this._db, refineId, { phase: 'testing' });
|
|
468
478
|
this._emitPhase(refineId, 'testing');
|
|
469
|
-
const sampleTask = pickSampleTask(this._db, agentId);
|
|
479
|
+
const sampleTask = pickSampleTask(this._db, this._adapter.id, agentId);
|
|
470
480
|
try {
|
|
471
481
|
const result = await (0, agent_generator_1.testCustomAgent)(this._projectPath, {
|
|
472
482
|
draftBody,
|
|
473
483
|
sampleTask,
|
|
484
|
+
providerId: this._adapter.id,
|
|
474
485
|
// MED-3: the default-on per-refine-turn auto-test is a billable claude
|
|
475
486
|
// spawn — record it (surface='agent-studio') when we have a project DB.
|
|
476
487
|
record: this._projectId
|
|
@@ -479,8 +490,9 @@ class AgentRefineManager {
|
|
|
479
490
|
});
|
|
480
491
|
if (this._disposed)
|
|
481
492
|
return;
|
|
482
|
-
this._db.prepare(`INSERT INTO agent_tests
|
|
483
|
-
|
|
493
|
+
this._db.prepare(`INSERT INTO agent_tests
|
|
494
|
+
(provider, agent_name, draft_hash, sample_task_id, tokens, duration_ms, output, created_at)
|
|
495
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(this._adapter.id, agentId, result.draftHash, null, result.tokens, result.durationMs, result.output, Date.now());
|
|
484
496
|
(0, agent_refine_db_1.updateRefineSession)(this._db, refineId, {
|
|
485
497
|
last_test_at: Date.now(),
|
|
486
498
|
last_test_hash: draftHash,
|
|
@@ -564,6 +576,15 @@ function validateAgentBody(body, providerId = 'claude') {
|
|
|
564
576
|
return { ok: true };
|
|
565
577
|
}
|
|
566
578
|
function buildFirstTurnPrompt(opts) {
|
|
579
|
+
const modelRule = (opts.providerId ?? 'claude') === 'claude'
|
|
580
|
+
? [
|
|
581
|
+
' 1. Frontmatter MUST include `name`, `description`, and `model` (one of',
|
|
582
|
+
' `sonnet`, `opus`, `haiku`).',
|
|
583
|
+
]
|
|
584
|
+
: [
|
|
585
|
+
' 1. Frontmatter MUST include `name` and `description`.',
|
|
586
|
+
' Do not add provider-specific model metadata.',
|
|
587
|
+
];
|
|
567
588
|
return [
|
|
568
589
|
'You are refining an existing custom agent. The user wants to iterate on its',
|
|
569
590
|
'definition. Output the COMPLETE refined `.md` file — full YAML frontmatter',
|
|
@@ -571,8 +592,7 @@ function buildFirstTurnPrompt(opts) {
|
|
|
571
592
|
'commentary, or explanations. Start at `---`.',
|
|
572
593
|
'',
|
|
573
594
|
'Hard rules:',
|
|
574
|
-
|
|
575
|
-
' `sonnet`, `opus`, `haiku`).',
|
|
595
|
+
...modelRule,
|
|
576
596
|
` 2. The frontmatter \`name\` MUST remain exactly: ${opts.agentId}`,
|
|
577
597
|
' 3. The id `name` is locked; renaming is a separate explicit action.',
|
|
578
598
|
'',
|
|
@@ -589,13 +609,13 @@ function buildFirstTurnPrompt(opts) {
|
|
|
589
609
|
'Output only the new file content, starting with `---`.',
|
|
590
610
|
].join('\n');
|
|
591
611
|
}
|
|
592
|
-
function pickSampleTask(db, agentId) {
|
|
612
|
+
function pickSampleTask(db, provider, agentId) {
|
|
593
613
|
try {
|
|
594
614
|
const row = db
|
|
595
615
|
.prepare(`SELECT output FROM agent_tests
|
|
596
|
-
WHERE agent_name = ? AND sample_task_id IS NOT NULL
|
|
616
|
+
WHERE provider = ? AND agent_name = ? AND sample_task_id IS NOT NULL
|
|
597
617
|
ORDER BY created_at DESC LIMIT 1`)
|
|
598
|
-
.get(agentId);
|
|
618
|
+
.get(provider, agentId);
|
|
599
619
|
if (row?.output)
|
|
600
620
|
return row.output;
|
|
601
621
|
}
|
|
@@ -57,44 +57,89 @@ function getTicketSpendingSummary(db, ticketId) {
|
|
|
57
57
|
tokens_in, tokens_out, tokens_cache_read, tokens_cache_create
|
|
58
58
|
FROM ai_invocations WHERE ticket_id = ?`).all(ticketId);
|
|
59
59
|
const bySurface = {
|
|
60
|
-
job: { count: 0, costUsd: 0 },
|
|
61
|
-
'quick-spec': { count: 0, costUsd: 0 },
|
|
62
|
-
'explore-spec': { count: 0, costUsd: 0 },
|
|
63
|
-
'ai-edit': { count: 0, costUsd: 0 },
|
|
64
|
-
smash: { count: 0, costUsd: 0 },
|
|
65
|
-
'file-summary': { count: 0, costUsd: 0 },
|
|
66
|
-
loop: { count: 0, costUsd: 0 },
|
|
67
|
-
'chat-sidebar': { count: 0, costUsd: 0 },
|
|
68
|
-
'spec-launcher': { count: 0, costUsd: 0 },
|
|
69
|
-
proposal: { count: 0, costUsd: 0 },
|
|
70
|
-
'agent-studio': { count: 0, costUsd: 0 },
|
|
71
|
-
setup: { count: 0, costUsd: 0 },
|
|
60
|
+
job: { count: 0, costUsd: 0, unpricedCount: 0 },
|
|
61
|
+
'quick-spec': { count: 0, costUsd: 0, unpricedCount: 0 },
|
|
62
|
+
'explore-spec': { count: 0, costUsd: 0, unpricedCount: 0 },
|
|
63
|
+
'ai-edit': { count: 0, costUsd: 0, unpricedCount: 0 },
|
|
64
|
+
smash: { count: 0, costUsd: 0, unpricedCount: 0 },
|
|
65
|
+
'file-summary': { count: 0, costUsd: 0, unpricedCount: 0 },
|
|
66
|
+
loop: { count: 0, costUsd: 0, unpricedCount: 0 },
|
|
67
|
+
'chat-sidebar': { count: 0, costUsd: 0, unpricedCount: 0 },
|
|
68
|
+
'spec-launcher': { count: 0, costUsd: 0, unpricedCount: 0 },
|
|
69
|
+
proposal: { count: 0, costUsd: 0, unpricedCount: 0 },
|
|
70
|
+
'agent-studio': { count: 0, costUsd: 0, unpricedCount: 0 },
|
|
71
|
+
setup: { count: 0, costUsd: 0, unpricedCount: 0 },
|
|
72
72
|
};
|
|
73
73
|
let totalCostUsd = 0;
|
|
74
74
|
let estimatedCostUsd = 0;
|
|
75
75
|
let totalTokens = 0;
|
|
76
76
|
let totalTurns = 0;
|
|
77
|
+
let pricedRuns = 0;
|
|
78
|
+
let unpricedRuns = 0;
|
|
79
|
+
let usageReportedRuns = 0;
|
|
80
|
+
let usageUnavailableRuns = 0;
|
|
81
|
+
let turnsReportedRuns = 0;
|
|
82
|
+
let turnsUnavailableRuns = 0;
|
|
77
83
|
let activeDurationMs = 0;
|
|
78
84
|
for (const r of rows) {
|
|
79
85
|
// Defensive: a row written by a newer schema with a surface this build
|
|
80
86
|
// doesn't know must not crash the summary.
|
|
81
87
|
if (!bySurface[r.surface])
|
|
82
|
-
bySurface[r.surface] = { count: 0, costUsd: 0 };
|
|
88
|
+
bySurface[r.surface] = { count: 0, costUsd: 0, unpricedCount: 0 };
|
|
83
89
|
bySurface[r.surface].count += 1;
|
|
84
90
|
bySurface[r.surface].costUsd += r.total_cost_usd ?? 0;
|
|
91
|
+
if (r.total_cost_usd == null) {
|
|
92
|
+
unpricedRuns += 1;
|
|
93
|
+
bySurface[r.surface].unpricedCount = (bySurface[r.surface].unpricedCount ?? 0) + 1;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
pricedRuns += 1;
|
|
97
|
+
}
|
|
85
98
|
totalCostUsd += r.total_cost_usd ?? 0;
|
|
86
99
|
if (r.total_cost_usd_estimated === 1)
|
|
87
100
|
estimatedCostUsd += r.total_cost_usd ?? 0;
|
|
88
101
|
// Real total tokens = fresh input + output + cache-read + cache-create.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
102
|
+
const hasUsage = r.tokens_in != null || r.tokens_out != null
|
|
103
|
+
|| r.tokens_cache_read != null || r.tokens_cache_create != null;
|
|
104
|
+
if (hasUsage) {
|
|
105
|
+
usageReportedRuns += 1;
|
|
106
|
+
totalTokens +=
|
|
107
|
+
(r.tokens_in ?? 0) +
|
|
108
|
+
(r.tokens_out ?? 0) +
|
|
109
|
+
(r.tokens_cache_read ?? 0) +
|
|
110
|
+
(r.tokens_cache_create ?? 0);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
usageUnavailableRuns += 1;
|
|
114
|
+
}
|
|
115
|
+
if (r.num_turns != null) {
|
|
116
|
+
turnsReportedRuns += 1;
|
|
117
|
+
totalTurns += r.num_turns;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
turnsUnavailableRuns += 1;
|
|
121
|
+
}
|
|
95
122
|
activeDurationMs += r.duration_ms ?? 0;
|
|
96
123
|
}
|
|
97
|
-
|
|
124
|
+
for (const bucket of Object.values(bySurface)) {
|
|
125
|
+
if ((bucket.unpricedCount ?? 0) === 0)
|
|
126
|
+
delete bucket.unpricedCount;
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
totalCostUsd,
|
|
130
|
+
estimatedCostUsd,
|
|
131
|
+
totalTokens: usageReportedRuns > 0 ? totalTokens : rows.length > 0 ? null : 0,
|
|
132
|
+
totalTurns: turnsReportedRuns > 0 ? totalTurns : rows.length > 0 ? null : 0,
|
|
133
|
+
pricedRuns,
|
|
134
|
+
unpricedRuns,
|
|
135
|
+
usageReportedRuns,
|
|
136
|
+
usageUnavailableRuns,
|
|
137
|
+
turnsReportedRuns,
|
|
138
|
+
turnsUnavailableRuns,
|
|
139
|
+
activeDurationMs,
|
|
140
|
+
bySurface,
|
|
141
|
+
totalRuns: rows.length,
|
|
142
|
+
};
|
|
98
143
|
}
|
|
99
144
|
/**
|
|
100
145
|
* Per-provider aggregation for the Analytics dashboard. Returns counts +
|
|
@@ -78,6 +78,37 @@ function sanitizeFilename(name) {
|
|
|
78
78
|
function escapeUserAttachmentTag(s) {
|
|
79
79
|
return s.replace(/<\/user-attachment>/gi, '<\\/user-attachment>');
|
|
80
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Resolve a sidecar-controlled stored filename without ever allowing it to
|
|
83
|
+
* escape the app-managed attachment directory. Sidecars live on disk and can
|
|
84
|
+
* be edited by the local user (or left behind by an older version), so treating
|
|
85
|
+
* `storedName` as trusted would let an attachment prompt expose an arbitrary
|
|
86
|
+
* absolute path to the provider. The realpath check also rejects symlink
|
|
87
|
+
* escapes, which a lexical `path.resolve` containment check alone misses.
|
|
88
|
+
*/
|
|
89
|
+
function resolveManagedAttachmentPath(dir, storedName) {
|
|
90
|
+
if (typeof storedName !== 'string' ||
|
|
91
|
+
storedName.length === 0 ||
|
|
92
|
+
storedName !== path_1.default.basename(storedName) ||
|
|
93
|
+
storedName.includes('/') ||
|
|
94
|
+
storedName.includes('\\')) {
|
|
95
|
+
throw new Error(`Unsafe attachment stored name: ${JSON.stringify(storedName)}`);
|
|
96
|
+
}
|
|
97
|
+
const candidate = path_1.default.resolve(dir, storedName);
|
|
98
|
+
const lexicalRelative = path_1.default.relative(path_1.default.resolve(dir), candidate);
|
|
99
|
+
if (lexicalRelative === '..' || lexicalRelative.startsWith(`..${path_1.default.sep}`) || path_1.default.isAbsolute(lexicalRelative)) {
|
|
100
|
+
throw new Error(`Unsafe attachment path: ${JSON.stringify(storedName)}`);
|
|
101
|
+
}
|
|
102
|
+
if (!fs_1.default.existsSync(candidate))
|
|
103
|
+
return null;
|
|
104
|
+
const realDir = fs_1.default.realpathSync(dir);
|
|
105
|
+
const realCandidate = fs_1.default.realpathSync(candidate);
|
|
106
|
+
const realRelative = path_1.default.relative(realDir, realCandidate);
|
|
107
|
+
if (realRelative === '..' || realRelative.startsWith(`..${path_1.default.sep}`) || path_1.default.isAbsolute(realRelative)) {
|
|
108
|
+
throw new Error(`Unsafe attachment symlink: ${JSON.stringify(storedName)}`);
|
|
109
|
+
}
|
|
110
|
+
return realCandidate;
|
|
111
|
+
}
|
|
81
112
|
class AttachmentManager {
|
|
82
113
|
homeDir;
|
|
83
114
|
constructor(homeDir = os_1.default.homedir()) {
|
|
@@ -180,8 +211,7 @@ class AttachmentManager {
|
|
|
180
211
|
const meta = this.readMeta(slug, ticketKey, attachmentId);
|
|
181
212
|
if (!meta)
|
|
182
213
|
return null;
|
|
183
|
-
|
|
184
|
-
return fs_1.default.existsSync(abs) ? abs : null;
|
|
214
|
+
return resolveManagedAttachmentPath(this.ticketDir(slug, ticketKey), meta.storedName);
|
|
185
215
|
}
|
|
186
216
|
getMeta(slug, ticketKey, attachmentId) {
|
|
187
217
|
return this.readMeta(slug, ticketKey, attachmentId);
|
|
@@ -191,8 +221,11 @@ class AttachmentManager {
|
|
|
191
221
|
if (!meta)
|
|
192
222
|
return false;
|
|
193
223
|
const dir = this.ticketDir(opts.slug, opts.ticketKey);
|
|
194
|
-
|
|
195
|
-
|
|
224
|
+
// Validate the sidecar-controlled path before deleting either the sidecar
|
|
225
|
+
// or ticket-store reference. On rejection the operation is atomic from the
|
|
226
|
+
// caller's perspective and an out-of-root target is never touched.
|
|
227
|
+
const bin = resolveManagedAttachmentPath(dir, meta.storedName);
|
|
228
|
+
if (bin)
|
|
196
229
|
fs_1.default.unlinkSync(bin);
|
|
197
230
|
const side = this.sidecarPath(opts.slug, opts.ticketKey, opts.attachmentId);
|
|
198
231
|
if (fs_1.default.existsSync(side))
|
|
@@ -246,14 +279,16 @@ class AttachmentManager {
|
|
|
246
279
|
*/
|
|
247
280
|
async getClaudeArgs(slug, ticketKey, attachmentIds) {
|
|
248
281
|
const textBlocks = [];
|
|
282
|
+
const imagePaths = [];
|
|
249
283
|
for (const id of attachmentIds) {
|
|
250
284
|
const meta = this.readMeta(slug, ticketKey, id);
|
|
251
285
|
if (!meta)
|
|
252
286
|
continue;
|
|
253
|
-
const abs =
|
|
254
|
-
if (!
|
|
287
|
+
const abs = resolveManagedAttachmentPath(this.ticketDir(slug, ticketKey), meta.storedName);
|
|
288
|
+
if (!abs)
|
|
255
289
|
continue;
|
|
256
290
|
if (meta.mimeType.startsWith(IMAGE_MIME_PREFIX)) {
|
|
291
|
+
imagePaths.push(abs);
|
|
257
292
|
textBlocks.push(wrapUserAttachment(meta, `@${abs}`));
|
|
258
293
|
continue;
|
|
259
294
|
}
|
|
@@ -265,7 +300,7 @@ class AttachmentManager {
|
|
|
265
300
|
textBlocks.push(wrapUserAttachment(meta, '[extraction failed]'));
|
|
266
301
|
}
|
|
267
302
|
}
|
|
268
|
-
return { imageFlags: [], textBlocks };
|
|
303
|
+
return { imageFlags: [], textBlocks, imagePaths };
|
|
269
304
|
}
|
|
270
305
|
/**
|
|
271
306
|
* Synchronous prompt blocks for long-running implement flows where we need to
|
|
@@ -282,8 +317,8 @@ class AttachmentManager {
|
|
|
282
317
|
const meta = this.readMeta(slug, ticketKey, id);
|
|
283
318
|
if (!meta)
|
|
284
319
|
continue;
|
|
285
|
-
const abs =
|
|
286
|
-
if (!
|
|
320
|
+
const abs = resolveManagedAttachmentPath(this.ticketDir(slug, ticketKey), meta.storedName);
|
|
321
|
+
if (!abs)
|
|
287
322
|
continue;
|
|
288
323
|
if (meta.mimeType.startsWith(IMAGE_MIME_PREFIX)) {
|
|
289
324
|
textBlocks.push(wrapUserAttachment(meta, `@${abs}`));
|
|
@@ -385,16 +420,15 @@ class AttachmentManager {
|
|
|
385
420
|
const meta = this.readAgentMeta(conversationId, attachmentId);
|
|
386
421
|
if (!meta)
|
|
387
422
|
return null;
|
|
388
|
-
|
|
389
|
-
return fs_1.default.existsSync(abs) ? abs : null;
|
|
423
|
+
return resolveManagedAttachmentPath(this.agentDir(conversationId), meta.storedName);
|
|
390
424
|
}
|
|
391
425
|
async deleteAgent(conversationId, attachmentId) {
|
|
392
426
|
const meta = this.readAgentMeta(conversationId, attachmentId);
|
|
393
427
|
if (!meta)
|
|
394
428
|
return false;
|
|
395
429
|
const dir = this.agentDir(conversationId);
|
|
396
|
-
const bin =
|
|
397
|
-
if (
|
|
430
|
+
const bin = resolveManagedAttachmentPath(dir, meta.storedName);
|
|
431
|
+
if (bin)
|
|
398
432
|
fs_1.default.unlinkSync(bin);
|
|
399
433
|
const side = this.agentSidecarPath(conversationId, attachmentId);
|
|
400
434
|
if (fs_1.default.existsSync(side))
|
|
@@ -422,8 +456,8 @@ class AttachmentManager {
|
|
|
422
456
|
const meta = this.readAgentMeta(conversationId, id);
|
|
423
457
|
if (!meta)
|
|
424
458
|
continue;
|
|
425
|
-
const abs =
|
|
426
|
-
if (!
|
|
459
|
+
const abs = resolveManagedAttachmentPath(this.agentDir(conversationId), meta.storedName);
|
|
460
|
+
if (!abs)
|
|
427
461
|
continue;
|
|
428
462
|
if (meta.mimeType.startsWith(IMAGE_MIME_PREFIX)) {
|
|
429
463
|
imagePaths.push(abs);
|
|
@@ -7,6 +7,7 @@ exports.BlueprintChatManager = void 0;
|
|
|
7
7
|
const crypto_1 = require("crypto");
|
|
8
8
|
const tree_kill_1 = __importDefault(require("tree-kill"));
|
|
9
9
|
const providers_1 = require("./providers");
|
|
10
|
+
const runtime_1 = require("./providers/runtime");
|
|
10
11
|
const spawn_lifecycle_1 = require("./spawn-lifecycle");
|
|
11
12
|
const cli_prompt_1 = require("./util/cli-prompt");
|
|
12
13
|
const result_event_1 = require("./result-event");
|
|
@@ -129,12 +130,17 @@ class BlueprintChatManager {
|
|
|
129
130
|
async _runTurn(conversation, userText, options) {
|
|
130
131
|
const conversationId = conversation.id;
|
|
131
132
|
const adapter = (0, providers_1.getAdapter)(conversation.provider);
|
|
132
|
-
const
|
|
133
|
+
const toolPolicy = (0, runtime_1.pureOutputToolPolicy)(adapter);
|
|
134
|
+
if (!toolPolicy) {
|
|
135
|
+
throw new Error(`provider_tool_policy_unsupported:${adapter.id}:pure-output`);
|
|
136
|
+
}
|
|
133
137
|
const requested = options.model || conversation.model;
|
|
134
|
-
const model = requested &&
|
|
138
|
+
const model = requested && (0, providers_1.isModelAvailableForAdapter)(adapter, requested)
|
|
139
|
+
? requested
|
|
140
|
+
: adapter.defaultModel();
|
|
135
141
|
// Reasoning effort: only for providers with the knob, only when the value
|
|
136
142
|
// is in their catalog (else undefined = provider default).
|
|
137
|
-
const efforts = adapter
|
|
143
|
+
const efforts = (0, providers_1.reasoningEffortsForModel)(adapter, model);
|
|
138
144
|
const requestedEffort = options.reasoningEffort;
|
|
139
145
|
const reasoningEffort = requestedEffort
|
|
140
146
|
&& efforts.includes(requestedEffort)
|
|
@@ -156,20 +162,29 @@ class BlueprintChatManager {
|
|
|
156
162
|
let streamed = '';
|
|
157
163
|
let capturedSessionId = useResume ? conversation.session_id ?? null : null;
|
|
158
164
|
let capturedError = null;
|
|
165
|
+
// `chat-turn` / `chat-resume` intentionally ignore `systemPrompt` for
|
|
166
|
+
// providers whose normal project-chat stance lives in their cwd
|
|
167
|
+
// instructions file. The day-0 Builder has no project instructions, so
|
|
168
|
+
// its schema/operator prompt must ride in the user prompt instead.
|
|
169
|
+
const prompt = adapter.capabilities.systemPromptArg
|
|
170
|
+
? userText
|
|
171
|
+
: `${blueprint_operator_prompt_1.BUILDER_SYSTEM_PROMPT}\n\n---\n\n${userText}`;
|
|
159
172
|
console.log(`[blueprint-chat] turn start conv=${conversationId} provider=${adapter.id} action=${action} model=${model}`);
|
|
173
|
+
const buildOpts = {
|
|
174
|
+
prompt,
|
|
175
|
+
systemPrompt: adapter.capabilities.systemPromptArg ? blueprint_operator_prompt_1.BUILDER_SYSTEM_PROMPT : undefined,
|
|
176
|
+
model,
|
|
177
|
+
sessionId: useResume ? conversation.session_id ?? undefined : undefined,
|
|
178
|
+
reasoning_effort: reasoningEffort,
|
|
179
|
+
toolPolicy,
|
|
180
|
+
};
|
|
160
181
|
const invocation = this._awaitWhileLive((0, spawn_lifecycle_1.runAiCliInvocation)({
|
|
161
182
|
adapter,
|
|
162
183
|
action,
|
|
163
184
|
cwd,
|
|
164
|
-
env:
|
|
185
|
+
env: (0, runtime_1.buildProviderEnv)(adapter, buildOpts),
|
|
165
186
|
spawn: this._spawnOwned.bind(this),
|
|
166
|
-
buildOpts
|
|
167
|
-
prompt: userText,
|
|
168
|
-
systemPrompt: adapter.capabilities.systemPromptArg ? blueprint_operator_prompt_1.BUILDER_SYSTEM_PROMPT : undefined,
|
|
169
|
-
model,
|
|
170
|
-
sessionId: useResume ? conversation.session_id ?? undefined : undefined,
|
|
171
|
-
reasoning_effort: reasoningEffort,
|
|
172
|
-
},
|
|
187
|
+
buildOpts,
|
|
173
188
|
onSpawn: (child) => {
|
|
174
189
|
if (this._disposed) {
|
|
175
190
|
this._terminate(child);
|
|
@@ -265,7 +280,7 @@ class BlueprintChatManager {
|
|
|
265
280
|
return;
|
|
266
281
|
}
|
|
267
282
|
// Auto-heal a stale session: a resume with no text retries once fresh.
|
|
268
|
-
if (canResume && !r.spawnFailed && !r.text) {
|
|
283
|
+
if (canResume && !r.spawnFailed && !r.error && !r.text) {
|
|
269
284
|
(0, blueprint_store_1.updateBlueprintConversation)(this._db, conversationId, { session_id: null });
|
|
270
285
|
r = await invoke(false);
|
|
271
286
|
if (this._disposed || r.disposed)
|
|
@@ -286,15 +301,23 @@ class BlueprintChatManager {
|
|
|
286
301
|
record(r, 'failed');
|
|
287
302
|
return;
|
|
288
303
|
}
|
|
304
|
+
if (r.error || r.code !== 0) {
|
|
305
|
+
persistSession(null);
|
|
306
|
+
const reason = r.error ||
|
|
307
|
+
(r.stderrTail ? r.stderrTail.split('\n').filter(Boolean).slice(-3).join(' ').slice(0, 300) : '') ||
|
|
308
|
+
`${adapter.binary} exited with code ${r.code ?? 'unknown'}`;
|
|
309
|
+
this._emitError(conversationId, reason);
|
|
310
|
+
record(r, 'failed');
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
289
313
|
if (r.text) {
|
|
290
314
|
settleText(r);
|
|
291
315
|
record(r, 'success');
|
|
292
316
|
return;
|
|
293
317
|
}
|
|
294
318
|
persistSession(null);
|
|
295
|
-
const reason = r.
|
|
296
|
-
|
|
297
|
-
(r.code !== 0 ? `${adapter.binary} exited with code ${r.code}` : 'The Builder returned no output.');
|
|
319
|
+
const reason = (r.stderrTail ? r.stderrTail.split('\n').filter(Boolean).slice(-3).join(' ').slice(0, 300) : '') ||
|
|
320
|
+
'The Builder returned no output.';
|
|
298
321
|
this._emitError(conversationId, reason);
|
|
299
322
|
record(r, 'failed');
|
|
300
323
|
}
|
|
@@ -302,8 +325,10 @@ class BlueprintChatManager {
|
|
|
302
325
|
if (this._disposed)
|
|
303
326
|
return;
|
|
304
327
|
try {
|
|
328
|
+
const finishedAt = new Date().toISOString();
|
|
305
329
|
const { result, estimated } = (0, result_event_1.finaliseInvocationResult)(adapter, outcome.events, {
|
|
306
330
|
fallbackModel: model,
|
|
331
|
+
durationMs: Math.max(0, Date.parse(finishedAt) - Date.parse(outcome.startedAt)),
|
|
307
332
|
});
|
|
308
333
|
(0, desktop_db_1.recordAgentInvocation)(this._db, {
|
|
309
334
|
id: (0, crypto_1.randomUUID)(),
|
|
@@ -312,7 +337,7 @@ class BlueprintChatManager {
|
|
|
312
337
|
provider: adapter.id,
|
|
313
338
|
status,
|
|
314
339
|
started_at: outcome.startedAt,
|
|
315
|
-
finished_at:
|
|
340
|
+
finished_at: finishedAt,
|
|
316
341
|
total_cost_usd_estimated: estimated,
|
|
317
342
|
...result,
|
|
318
343
|
});
|