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
package/server/dist/spending.js
CHANGED
|
@@ -20,7 +20,7 @@ const ALL_SURFACES = [
|
|
|
20
20
|
function emptyBySurface() {
|
|
21
21
|
const out = {};
|
|
22
22
|
for (const s of ALL_SURFACES)
|
|
23
|
-
out[s] = { count: 0, costUsd: 0 };
|
|
23
|
+
out[s] = { count: 0, costUsd: 0, unpricedCount: 0 };
|
|
24
24
|
return out;
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
@@ -200,17 +200,30 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
|
|
|
200
200
|
COALESCE(SUM(COALESCE(tokens_in, 0) + COALESCE(tokens_out, 0)
|
|
201
201
|
+ COALESCE(tokens_cache_read, 0) + COALESCE(tokens_cache_create, 0)), 0) AS totalTokens,
|
|
202
202
|
COUNT(*) AS totalRuns,
|
|
203
|
+
COUNT(total_cost_usd) AS pricedRuns,
|
|
204
|
+
SUM(CASE WHEN total_cost_usd IS NULL THEN 1 ELSE 0 END) AS unpricedRuns,
|
|
205
|
+
SUM(CASE WHEN tokens_in IS NOT NULL OR tokens_out IS NOT NULL
|
|
206
|
+
OR tokens_cache_read IS NOT NULL OR tokens_cache_create IS NOT NULL
|
|
207
|
+
THEN 1 ELSE 0 END) AS usageReportedRuns,
|
|
208
|
+
SUM(CASE WHEN tokens_in IS NULL AND tokens_out IS NULL
|
|
209
|
+
AND tokens_cache_read IS NULL AND tokens_cache_create IS NULL
|
|
210
|
+
THEN 1 ELSE 0 END) AS usageUnavailableRuns,
|
|
203
211
|
SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) AS failed,
|
|
212
|
+
SUM(CASE WHEN status = 'success' AND total_cost_usd IS NULL THEN 1 ELSE 0 END) AS successfulUnpriced,
|
|
204
213
|
AVG(CASE WHEN status = 'success' THEN total_cost_usd END) AS avgCost
|
|
205
214
|
FROM ai_invocations WHERE ${where.sql}
|
|
206
215
|
`).get(...where.params);
|
|
207
216
|
// prev period
|
|
208
217
|
const prevWhere = buildWhere(projectId, filters, { from: range.prevFrom, to: range.prevTo });
|
|
209
218
|
const prevRow = db.prepare(`
|
|
210
|
-
SELECT
|
|
219
|
+
SELECT
|
|
220
|
+
COALESCE(SUM(total_cost_usd), 0) AS totalCost,
|
|
221
|
+
SUM(CASE WHEN total_cost_usd IS NULL THEN 1 ELSE 0 END) AS unpricedRuns
|
|
211
222
|
FROM ai_invocations WHERE ${prevWhere.sql}
|
|
212
223
|
`).get(...prevWhere.params);
|
|
213
|
-
const deltaPct =
|
|
224
|
+
const deltaPct = (summaryRow.unpricedRuns ?? 0) === 0
|
|
225
|
+
&& (prevRow.unpricedRuns ?? 0) === 0
|
|
226
|
+
&& prevRow.totalCost > 0
|
|
214
227
|
? ((summaryRow.totalCost - prevRow.totalCost) / prevRow.totalCost) * 100
|
|
215
228
|
: null;
|
|
216
229
|
// byModel (top 10) + per-mode dominant models — one GROUP BY model, surface
|
|
@@ -225,21 +238,37 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
|
|
|
225
238
|
model, surface,
|
|
226
239
|
COUNT(*) AS cnt,
|
|
227
240
|
COALESCE(SUM(total_cost_usd), 0) AS cost,
|
|
228
|
-
COALESCE(SUM(CASE WHEN total_cost_usd_estimated = 1 THEN total_cost_usd ELSE 0 END), 0) AS estCost
|
|
241
|
+
COALESCE(SUM(CASE WHEN total_cost_usd_estimated = 1 THEN total_cost_usd ELSE 0 END), 0) AS estCost,
|
|
242
|
+
SUM(CASE WHEN total_cost_usd IS NULL THEN 1 ELSE 0 END) AS unpriced
|
|
229
243
|
FROM ai_invocations WHERE ${where.sql} AND model IS NOT NULL
|
|
230
244
|
GROUP BY provider, model, surface
|
|
231
245
|
`).all(...where.params);
|
|
232
246
|
const modelTotals = new Map();
|
|
233
247
|
for (const r of modelRows) {
|
|
234
|
-
const key = `${r.provider}
|
|
235
|
-
const agg = modelTotals.get(key) ?? {
|
|
248
|
+
const key = `${r.provider}\0${r.model}`;
|
|
249
|
+
const agg = modelTotals.get(key) ?? {
|
|
250
|
+
provider: r.provider,
|
|
251
|
+
model: r.model,
|
|
252
|
+
cnt: 0,
|
|
253
|
+
cost: 0,
|
|
254
|
+
est: 0,
|
|
255
|
+
unpriced: 0,
|
|
256
|
+
};
|
|
236
257
|
agg.cnt += r.cnt;
|
|
237
258
|
agg.cost += r.cost ?? 0;
|
|
238
259
|
agg.est += r.estCost ?? 0;
|
|
260
|
+
agg.unpriced += r.unpriced ?? 0;
|
|
239
261
|
modelTotals.set(key, agg);
|
|
240
262
|
}
|
|
241
263
|
const byModel = Array.from(modelTotals.values())
|
|
242
|
-
.map((t) => ({
|
|
264
|
+
.map((t) => ({
|
|
265
|
+
provider: t.provider,
|
|
266
|
+
model: t.model,
|
|
267
|
+
count: t.cnt,
|
|
268
|
+
costUsd: t.cost,
|
|
269
|
+
estimatedCostUsd: t.est,
|
|
270
|
+
...(t.unpriced > 0 ? { unpricedCount: t.unpriced } : {}),
|
|
271
|
+
}))
|
|
243
272
|
.sort((a, b) => b.costUsd - a.costUsd)
|
|
244
273
|
.slice(0, 10);
|
|
245
274
|
// dominantModel is the most-frequent model per surface (provider-agnostic —
|
|
@@ -247,11 +276,11 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
|
|
|
247
276
|
const dominantBySurface = new Map();
|
|
248
277
|
const dominantTally = new Map();
|
|
249
278
|
for (const r of modelRows) {
|
|
250
|
-
const k = `${r.surface}
|
|
279
|
+
const k = `${r.surface}\0${r.model}`;
|
|
251
280
|
dominantTally.set(k, (dominantTally.get(k) ?? 0) + r.cnt);
|
|
252
281
|
}
|
|
253
282
|
for (const [k, cnt] of dominantTally.entries()) {
|
|
254
|
-
const sep = k.indexOf('
|
|
283
|
+
const sep = k.indexOf('\0');
|
|
255
284
|
const surface = k.slice(0, sep);
|
|
256
285
|
const model = k.slice(sep + 1);
|
|
257
286
|
const cur = dominantBySurface.get(surface);
|
|
@@ -262,7 +291,11 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
|
|
|
262
291
|
// calendar day (BUG-ANALYTICS-21): the client passes its local offset so
|
|
263
292
|
// "today" lines up with the user's wall clock; offset 0 ⇒ legacy UTC days.
|
|
264
293
|
const dayRows = db.prepare(`
|
|
265
|
-
SELECT
|
|
294
|
+
SELECT
|
|
295
|
+
${dayBucketExpr(tzOffset)} AS day,
|
|
296
|
+
surface,
|
|
297
|
+
COALESCE(SUM(total_cost_usd), 0) AS cost,
|
|
298
|
+
SUM(CASE WHEN total_cost_usd IS NULL THEN 1 ELSE 0 END) AS unpriced
|
|
266
299
|
FROM ai_invocations WHERE ${where.sql}
|
|
267
300
|
GROUP BY day, surface
|
|
268
301
|
`).all(...where.params);
|
|
@@ -279,7 +312,7 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
|
|
|
279
312
|
const dayMap = new Map();
|
|
280
313
|
for (const day of days) {
|
|
281
314
|
dayMap.set(day, {
|
|
282
|
-
date: day, jobsCostUsd: 0, quickCostUsd: 0, exploreCostUsd: 0, aiEditCostUsd: 0, smashCostUsd: 0, fileSummaryCostUsd: 0, loopCostUsd: 0, totalCostUsd: 0,
|
|
315
|
+
date: day, jobsCostUsd: 0, quickCostUsd: 0, exploreCostUsd: 0, aiEditCostUsd: 0, smashCostUsd: 0, fileSummaryCostUsd: 0, loopCostUsd: 0, totalCostUsd: 0, unpricedCount: 0,
|
|
283
316
|
});
|
|
284
317
|
}
|
|
285
318
|
for (const r of dayRows) {
|
|
@@ -302,8 +335,13 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
|
|
|
302
335
|
else if (r.surface === 'loop')
|
|
303
336
|
entry.loopCostUsd += c;
|
|
304
337
|
entry.totalCostUsd += c;
|
|
338
|
+
entry.unpricedCount = (entry.unpricedCount ?? 0) + (r.unpriced ?? 0);
|
|
305
339
|
}
|
|
306
340
|
const dailyTimeline = Array.from(dayMap.values());
|
|
341
|
+
for (const day of dailyTimeline) {
|
|
342
|
+
if ((day.unpricedCount ?? 0) === 0)
|
|
343
|
+
delete day.unpricedCount;
|
|
344
|
+
}
|
|
307
345
|
// byMode (Quick vs Explore) — one GROUP BY surface query for both modes;
|
|
308
346
|
// dominant models come from modelRows and sparklines from dayRows, which
|
|
309
347
|
// already carry exactly these aggregates (H24: previously 3 extra queries
|
|
@@ -320,8 +358,11 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
|
|
|
320
358
|
COUNT(DISTINCT ticket_id) AS ticketsCreated,
|
|
321
359
|
COALESCE(SUM(total_cost_usd), 0) AS totalCost,
|
|
322
360
|
COALESCE(SUM(CASE WHEN total_cost_usd_estimated = 1 THEN total_cost_usd ELSE 0 END), 0) AS estCost,
|
|
361
|
+
SUM(CASE WHEN total_cost_usd IS NULL THEN 1 ELSE 0 END) AS unpricedRuns,
|
|
323
362
|
COALESCE(SUM(CASE WHEN status = 'success' AND ticket_id IS NOT NULL THEN total_cost_usd ELSE 0 END), 0) AS specCostSum,
|
|
324
363
|
COUNT(DISTINCT CASE WHEN status = 'success' AND ticket_id IS NOT NULL THEN ticket_id END) AS specCount,
|
|
364
|
+
COUNT(DISTINCT CASE WHEN status = 'success' AND ticket_id IS NOT NULL
|
|
365
|
+
AND total_cost_usd IS NULL THEN ticket_id END) AS unpricedSpecCount,
|
|
325
366
|
AVG(CASE WHEN status = 'success' THEN duration_ms END) AS avgDur
|
|
326
367
|
FROM ai_invocations WHERE ${where.sql} AND surface IN ('quick-spec', 'explore-spec')
|
|
327
368
|
GROUP BY surface
|
|
@@ -333,7 +374,12 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
|
|
|
333
374
|
const byMode = ['quick-spec', 'explore-spec'].map((surface) => {
|
|
334
375
|
const modeKey = surface === 'quick-spec' ? 'quick' : 'explore';
|
|
335
376
|
const r = modeAggBySurface.get(surface);
|
|
336
|
-
|
|
377
|
+
// A partial average over only providers that report cost is worse than no
|
|
378
|
+
// average: it looks authoritative while silently excluding Kimi. Preserve
|
|
379
|
+
// unavailable until every successful ticket in the denominator is priced.
|
|
380
|
+
const avgCostPerSpec = r && r.specCount > 0 && r.unpricedSpecCount === 0
|
|
381
|
+
? r.specCostSum / r.specCount
|
|
382
|
+
: null;
|
|
337
383
|
const sparkline = days.map((d) => costByDaySurface.get(`${d}|${surface}`) ?? 0);
|
|
338
384
|
return {
|
|
339
385
|
mode: modeKey,
|
|
@@ -341,6 +387,7 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
|
|
|
341
387
|
ticketsCreated: r?.ticketsCreated ?? 0,
|
|
342
388
|
totalCostUsd: r?.totalCost ?? 0,
|
|
343
389
|
estimatedCostUsd: r?.estCost ?? 0,
|
|
390
|
+
...((r?.unpricedRuns ?? 0) > 0 ? { unpricedCount: r?.unpricedRuns ?? 0 } : {}),
|
|
344
391
|
avgCostPerSpec,
|
|
345
392
|
avgDurationMs: r?.avgDur ?? null,
|
|
346
393
|
dominantModel: dominantBySurface.get(surface)?.model ?? null,
|
|
@@ -396,7 +443,12 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
|
|
|
396
443
|
}
|
|
397
444
|
// topTickets (cross-surface aggregation)
|
|
398
445
|
const ticketRows = db.prepare(`
|
|
399
|
-
SELECT
|
|
446
|
+
SELECT
|
|
447
|
+
ticket_id,
|
|
448
|
+
surface,
|
|
449
|
+
COUNT(*) AS cnt,
|
|
450
|
+
COALESCE(SUM(total_cost_usd), 0) AS cost,
|
|
451
|
+
SUM(CASE WHEN total_cost_usd IS NULL THEN 1 ELSE 0 END) AS unpriced
|
|
400
452
|
FROM ai_invocations WHERE ${where.sql}
|
|
401
453
|
GROUP BY ticket_id, surface
|
|
402
454
|
`).all(...where.params);
|
|
@@ -409,6 +461,7 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
|
|
|
409
461
|
ticketTitle: null,
|
|
410
462
|
totalCostUsd: 0,
|
|
411
463
|
totalRuns: 0,
|
|
464
|
+
unpricedCount: 0,
|
|
412
465
|
bySurface: emptyBySurface(),
|
|
413
466
|
isUnattributed: r.ticket_id === null ? true : undefined,
|
|
414
467
|
});
|
|
@@ -416,12 +469,23 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
|
|
|
416
469
|
const entry = ticketMap.get(key);
|
|
417
470
|
entry.bySurface[r.surface].count += r.cnt;
|
|
418
471
|
entry.bySurface[r.surface].costUsd += r.cost ?? 0;
|
|
472
|
+
entry.bySurface[r.surface].unpricedCount =
|
|
473
|
+
(entry.bySurface[r.surface].unpricedCount ?? 0) + (r.unpriced ?? 0);
|
|
419
474
|
entry.totalRuns += r.cnt;
|
|
420
475
|
entry.totalCostUsd += r.cost ?? 0;
|
|
476
|
+
entry.unpricedCount = (entry.unpricedCount ?? 0) + (r.unpriced ?? 0);
|
|
421
477
|
}
|
|
422
478
|
const topTickets = Array.from(ticketMap.values())
|
|
423
479
|
.sort((a, b) => b.totalCostUsd - a.totalCostUsd)
|
|
424
480
|
.slice(0, 10);
|
|
481
|
+
for (const ticket of topTickets) {
|
|
482
|
+
if ((ticket.unpricedCount ?? 0) === 0)
|
|
483
|
+
delete ticket.unpricedCount;
|
|
484
|
+
for (const bucket of Object.values(ticket.bySurface)) {
|
|
485
|
+
if ((bucket.unpricedCount ?? 0) === 0)
|
|
486
|
+
delete bucket.unpricedCount;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
425
489
|
// BUG-ANALYTICS-18/36: resolve committed-ticket titles + deleted state via the
|
|
426
490
|
// injected store reader so the card (and the summary-CSV / modal consumers
|
|
427
491
|
// built on this same shape) show names instead of "Deleted ticket #N". Without
|
|
@@ -440,14 +504,20 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
|
|
|
440
504
|
// (H24: previously a dedicated query re-scanned the same rows).
|
|
441
505
|
const surfaceTotals = new Map();
|
|
442
506
|
for (const r of ticketRows) {
|
|
443
|
-
const agg = surfaceTotals.get(r.surface) ?? { cnt: 0, cost: 0 };
|
|
507
|
+
const agg = surfaceTotals.get(r.surface) ?? { cnt: 0, cost: 0, unpriced: 0 };
|
|
444
508
|
agg.cnt += r.cnt;
|
|
445
509
|
agg.cost += r.cost ?? 0;
|
|
510
|
+
agg.unpriced += r.unpriced ?? 0;
|
|
446
511
|
surfaceTotals.set(r.surface, agg);
|
|
447
512
|
}
|
|
448
513
|
const bySurface = ALL_SURFACES.map((s) => {
|
|
449
514
|
const t = surfaceTotals.get(s);
|
|
450
|
-
return {
|
|
515
|
+
return {
|
|
516
|
+
surface: s,
|
|
517
|
+
count: t?.cnt ?? 0,
|
|
518
|
+
costUsd: t?.cost ?? 0,
|
|
519
|
+
...((t?.unpriced ?? 0) > 0 ? { unpricedCount: t?.unpriced ?? 0 } : {}),
|
|
520
|
+
};
|
|
451
521
|
});
|
|
452
522
|
// tracking start (project's first invocation)
|
|
453
523
|
const trackingRow = db.prepare(`
|
|
@@ -461,6 +531,14 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
|
|
|
461
531
|
SELECT
|
|
462
532
|
COALESCE(provider, 'claude') AS provider,
|
|
463
533
|
COUNT(*) AS cnt,
|
|
534
|
+
COUNT(total_cost_usd) AS priced,
|
|
535
|
+
SUM(CASE WHEN total_cost_usd IS NULL THEN 1 ELSE 0 END) AS unpriced,
|
|
536
|
+
SUM(CASE WHEN tokens_in IS NOT NULL OR tokens_out IS NOT NULL
|
|
537
|
+
OR tokens_cache_read IS NOT NULL OR tokens_cache_create IS NOT NULL
|
|
538
|
+
THEN 1 ELSE 0 END) AS usageReported,
|
|
539
|
+
SUM(CASE WHEN tokens_in IS NULL AND tokens_out IS NULL
|
|
540
|
+
AND tokens_cache_read IS NULL AND tokens_cache_create IS NULL
|
|
541
|
+
THEN 1 ELSE 0 END) AS usageUnavailable,
|
|
464
542
|
COALESCE(SUM(CASE WHEN total_cost_usd_estimated = 0 THEN total_cost_usd ELSE 0 END), 0) AS authoritativeCost,
|
|
465
543
|
COALESCE(SUM(CASE WHEN total_cost_usd_estimated = 1 THEN total_cost_usd ELSE 0 END), 0) AS estimatedCost
|
|
466
544
|
FROM ai_invocations WHERE ${where.sql}
|
|
@@ -472,17 +550,32 @@ function getSpending(db, projectId, filters = {}, resolveTitle) {
|
|
|
472
550
|
count: r.cnt,
|
|
473
551
|
costUsd: r.authoritativeCost,
|
|
474
552
|
estimatedCostUsd: r.estimatedCost,
|
|
553
|
+
pricedCount: r.priced,
|
|
554
|
+
unpricedCount: r.unpriced ?? 0,
|
|
555
|
+
usageReportedCount: r.usageReported ?? 0,
|
|
556
|
+
usageUnavailableCount: r.usageUnavailable ?? 0,
|
|
475
557
|
}));
|
|
476
558
|
return {
|
|
477
559
|
summary: {
|
|
478
560
|
totalCostUsd: summaryRow.totalCost,
|
|
479
561
|
totalEstimatedCostUsd: summaryRow.totalEstimatedCost,
|
|
480
|
-
totalTokens: summaryRow.
|
|
562
|
+
totalTokens: (summaryRow.usageReportedRuns ?? 0) > 0
|
|
563
|
+
? summaryRow.totalTokens
|
|
564
|
+
: summaryRow.totalRuns > 0
|
|
565
|
+
? null
|
|
566
|
+
: 0,
|
|
481
567
|
totalRuns: summaryRow.totalRuns,
|
|
568
|
+
pricedRuns: summaryRow.pricedRuns,
|
|
569
|
+
unpricedRuns: summaryRow.unpricedRuns ?? 0,
|
|
570
|
+
usageReportedRuns: summaryRow.usageReportedRuns ?? 0,
|
|
571
|
+
usageUnavailableRuns: summaryRow.usageUnavailableRuns ?? 0,
|
|
572
|
+
prevUnpricedRuns: prevRow.unpricedRuns ?? 0,
|
|
482
573
|
failureRate: summaryRow.totalRuns > 0 ? (summaryRow.failed ?? 0) / summaryRow.totalRuns : 0,
|
|
483
574
|
prevTotalCostUsd: prevRow.totalCost,
|
|
484
575
|
deltaPct,
|
|
485
|
-
avgCostPerRun: summaryRow.
|
|
576
|
+
avgCostPerRun: (summaryRow.successfulUnpriced ?? 0) > 0
|
|
577
|
+
? null
|
|
578
|
+
: summaryRow.avgCost,
|
|
486
579
|
},
|
|
487
580
|
bySurface,
|
|
488
581
|
byModel,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// Centralized
|
|
2
|
+
// Centralized AI CLI spawn wrapper.
|
|
3
3
|
//
|
|
4
4
|
// Why this exists:
|
|
5
5
|
//
|
|
@@ -19,7 +19,12 @@
|
|
|
19
19
|
// The helpers below detect multi-line argv values on Windows,
|
|
20
20
|
// reroute them through child stdin (claude reads stdin when
|
|
21
21
|
// `-p`/`--print` has no positional argument; codex `exec -` does the
|
|
22
|
-
// equivalent), and call spawnCli.
|
|
22
|
+
// equivalent), and call spawnCli. Kimi's required `-p <prompt>` has no stdin
|
|
23
|
+
// equivalent, so its Windows npm shim is unwrapped and its JavaScript entry is
|
|
24
|
+
// launched with Node directly. POSIX is unchanged byte-for-byte.
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
23
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
29
|
exports.appendCodexHeadroomRelayOverride = appendCodexHeadroomRelayOverride;
|
|
25
30
|
exports.transformClaudeArgsForWindows = transformClaudeArgsForWindows;
|
|
@@ -28,7 +33,11 @@ exports.ensureStdinPipe = ensureStdinPipe;
|
|
|
28
33
|
exports.spawnClaude = spawnClaude;
|
|
29
34
|
exports.spawnCodex = spawnCodex;
|
|
30
35
|
exports.spawnGemini = spawnGemini;
|
|
36
|
+
exports.parseNpmCmdShimEntry = parseNpmCmdShimEntry;
|
|
37
|
+
exports.spawnKimi = spawnKimi;
|
|
31
38
|
exports.spawnAiCli = spawnAiCli;
|
|
39
|
+
const node_fs_1 = require("node:fs");
|
|
40
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
32
41
|
const win_spawn_1 = require("./win-spawn");
|
|
33
42
|
const headroom_routing_1 = require("../headroom-routing");
|
|
34
43
|
const process_admission_1 = require("../process-admission");
|
|
@@ -149,7 +158,10 @@ function ensureStdinPipe(stdio) {
|
|
|
149
158
|
}
|
|
150
159
|
if (Array.isArray(stdio)) {
|
|
151
160
|
return [
|
|
152
|
-
|
|
161
|
+
// The transformed prompt always travels on fd 0. `inherit`,
|
|
162
|
+
// `overlapped`, numeric fds, and streams leave child.stdin null just as
|
|
163
|
+
// `ignore` does, so every array form must be upgraded unconditionally.
|
|
164
|
+
'pipe',
|
|
153
165
|
stdio[1] ?? 'pipe',
|
|
154
166
|
stdio[2] ?? 'pipe',
|
|
155
167
|
];
|
|
@@ -252,10 +264,133 @@ function spawnGemini(args, options = {}) {
|
|
|
252
264
|
}
|
|
253
265
|
return (0, win_spawn_1.spawnCli)('gemini', args, { ...options, env });
|
|
254
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Extract the JavaScript entry from a standard npm-generated Windows `.cmd`
|
|
269
|
+
* launcher. npm writes the target as `%dp0%\<package path>.mjs %*`; resolving
|
|
270
|
+
* that path lets us bypass cmd.exe while preserving the exact argv payload.
|
|
271
|
+
*/
|
|
272
|
+
function parseNpmCmdShimEntry(shimPath, contents) {
|
|
273
|
+
const match = contents.match(/%dp0%[\\/]([^"\r\n]*?\.(?:mjs|cjs|js))["']?\s+%\*/i);
|
|
274
|
+
if (!match?.[1])
|
|
275
|
+
return null;
|
|
276
|
+
return node_path_1.default.win32.join(node_path_1.default.win32.dirname(shimPath), match[1]);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Spawn Kimi in daemon-free prompt mode.
|
|
280
|
+
*
|
|
281
|
+
* Kimi 0.27 requires `-p <prompt>` and cannot itself read that prompt from
|
|
282
|
+
* stdin. On Windows, a native executable receives argv directly. For npm's
|
|
283
|
+
* `.cmd` shim, launch a tiny Node bootstrap that reads the complete prompt from
|
|
284
|
+
* stdin, restores it into process.argv at the `-p` value slot, then imports the
|
|
285
|
+
* generated JavaScript entry. This bypasses both cmd.exe newline reparsing and
|
|
286
|
+
* CreateProcess's ~32K command-line ceiling without changing Kimi semantics.
|
|
287
|
+
* A non-standard or unreadable command shim fails closed.
|
|
288
|
+
*/
|
|
289
|
+
const KIMI_WINDOWS_ARGV_SAFE_CHARS = 30_000;
|
|
290
|
+
const KIMI_WINDOWS_STDIN_BOOTSTRAP = [
|
|
291
|
+
'const fs=require("node:fs");',
|
|
292
|
+
'const {pathToFileURL}=require("node:url");',
|
|
293
|
+
'const entry=process.argv[1];',
|
|
294
|
+
'const slot=Number(process.argv[2]);',
|
|
295
|
+
'const args=process.argv.slice(3);',
|
|
296
|
+
'args[slot]=fs.readFileSync(0,"utf8");',
|
|
297
|
+
'process.argv=[process.execPath,entry,...args];',
|
|
298
|
+
'import(pathToFileURL(entry).href).catch((error)=>{console.error(error);process.exitCode=1;});',
|
|
299
|
+
].join('');
|
|
300
|
+
function kimiPromptValueIndex(args) {
|
|
301
|
+
const flagIndex = args.indexOf('-p');
|
|
302
|
+
return flagIndex >= 0 && flagIndex + 1 < args.length ? flagIndex + 1 : -1;
|
|
303
|
+
}
|
|
304
|
+
function estimatedWindowsArgvChars(binary, args) {
|
|
305
|
+
return binary.length + 1 + args.reduce((total, arg) => total + arg.length + 3, 0);
|
|
306
|
+
}
|
|
307
|
+
function spawnKimi(args, options = {}) {
|
|
308
|
+
(0, process_admission_1.assertProcessAdmission)();
|
|
309
|
+
if (!isWin())
|
|
310
|
+
return (0, win_spawn_1.spawnCli)('kimi', args, options);
|
|
311
|
+
/* c8 ignore start -- Windows-only branch; unit tests force process.platform */
|
|
312
|
+
const resolved = (0, win_spawn_1.resolveWindowsBinary)('kimi');
|
|
313
|
+
const extension = node_path_1.default.win32.extname(resolved).toLowerCase();
|
|
314
|
+
if (extension !== '.cmd' && extension !== '.bat') {
|
|
315
|
+
const promptIndex = kimiPromptValueIndex(args);
|
|
316
|
+
if (promptIndex >= 0 &&
|
|
317
|
+
estimatedWindowsArgvChars(resolved, args) > KIMI_WINDOWS_ARGV_SAFE_CHARS) {
|
|
318
|
+
throw new Error(`kimi_windows_native_prompt_too_large:${args[promptIndex].length}: ` +
|
|
319
|
+
'the native Kimi executable cannot receive this prompt within the Windows ' +
|
|
320
|
+
'CreateProcess limit; install the npm Kimi Code CLI shim or shorten the prompt.');
|
|
321
|
+
}
|
|
322
|
+
return (0, win_spawn_1.spawnCli)(resolved, args, options);
|
|
323
|
+
}
|
|
324
|
+
let entry;
|
|
325
|
+
try {
|
|
326
|
+
entry = parseNpmCmdShimEntry(resolved, (0, node_fs_1.readFileSync)(resolved, 'utf8'));
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
throw new Error(`unsupported_kimi_windows_shim:${resolved}:` +
|
|
330
|
+
(error instanceof Error ? error.message : String(error)));
|
|
331
|
+
}
|
|
332
|
+
if (!entry) {
|
|
333
|
+
throw new Error(`unsupported_kimi_windows_shim:${resolved}`);
|
|
334
|
+
}
|
|
335
|
+
const localNode = node_path_1.default.win32.join(node_path_1.default.win32.dirname(resolved), 'node.exe');
|
|
336
|
+
const nodeBinary = (0, node_fs_1.existsSync)(localNode) ? localNode : 'node';
|
|
337
|
+
const promptIndex = kimiPromptValueIndex(args);
|
|
338
|
+
if (promptIndex < 0) {
|
|
339
|
+
return (0, win_spawn_1.spawnCli)(nodeBinary, [entry, ...args], options);
|
|
340
|
+
}
|
|
341
|
+
const prompt = args[promptIndex];
|
|
342
|
+
const forwardedArgs = [...args];
|
|
343
|
+
// Preserve the exact argv shape for Kimi; the bootstrap replaces only this
|
|
344
|
+
// placeholder after reading stdin. The real prompt never reaches
|
|
345
|
+
// CreateProcess, cmd.exe, environment variables, or a temporary file.
|
|
346
|
+
forwardedArgs[promptIndex] = '';
|
|
347
|
+
const spawnOptions = {
|
|
348
|
+
...options,
|
|
349
|
+
stdio: ensureStdinPipe(options.stdio),
|
|
350
|
+
};
|
|
351
|
+
const child = (0, win_spawn_1.spawnCli)(nodeBinary, [
|
|
352
|
+
'-e',
|
|
353
|
+
KIMI_WINDOWS_STDIN_BOOTSTRAP,
|
|
354
|
+
entry,
|
|
355
|
+
String(promptIndex),
|
|
356
|
+
...forwardedArgs,
|
|
357
|
+
], spawnOptions);
|
|
358
|
+
if (!child.stdin) {
|
|
359
|
+
try {
|
|
360
|
+
child.kill('SIGTERM');
|
|
361
|
+
}
|
|
362
|
+
catch { /* best-effort */ }
|
|
363
|
+
throw new Error('kimi_windows_bootstrap_stdin_unavailable');
|
|
364
|
+
}
|
|
365
|
+
// A short-lived bootstrap can exit before the buffered write settles.
|
|
366
|
+
// Without an error listener, the resulting EPIPE is an unhandled EventEmitter
|
|
367
|
+
// error and can terminate Desktop itself. Broken pipes are already reflected
|
|
368
|
+
// by the child's terminal status; log any different transport failure.
|
|
369
|
+
child.stdin.on('error', (error) => {
|
|
370
|
+
if (error.code === 'EPIPE')
|
|
371
|
+
return;
|
|
372
|
+
console.error('[cli-prompt] Kimi Windows prompt transport failed:', error);
|
|
373
|
+
// A failed transport must never leave the bootstrap free to invoke Kimi
|
|
374
|
+
// with an empty or partial prompt. Managers will observe the terminated
|
|
375
|
+
// child through their normal lifecycle/error path.
|
|
376
|
+
try {
|
|
377
|
+
child.kill('SIGTERM');
|
|
378
|
+
}
|
|
379
|
+
catch { /* best-effort */ }
|
|
380
|
+
});
|
|
381
|
+
try {
|
|
382
|
+
child.stdin.end(prompt);
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
if (error?.code !== 'EPIPE')
|
|
386
|
+
throw error;
|
|
387
|
+
}
|
|
388
|
+
return child;
|
|
389
|
+
/* c8 ignore stop */
|
|
390
|
+
}
|
|
255
391
|
/**
|
|
256
392
|
* Convenience: dispatch on binary name. Use when callsite picks the
|
|
257
|
-
* binary dynamically
|
|
258
|
-
* the underlying spawnCli unchanged.
|
|
393
|
+
* binary dynamically. Anything else routes through the underlying spawnCli.
|
|
259
394
|
*/
|
|
260
395
|
function spawnAiCli(binary, args, options = {}) {
|
|
261
396
|
(0, process_admission_1.assertProcessAdmission)();
|
|
@@ -265,5 +400,7 @@ function spawnAiCli(binary, args, options = {}) {
|
|
|
265
400
|
return spawnCodex(args, options);
|
|
266
401
|
if (binary === 'gemini')
|
|
267
402
|
return spawnGemini(args, options);
|
|
403
|
+
if (binary === 'kimi')
|
|
404
|
+
return spawnKimi(args, options);
|
|
268
405
|
return (0, win_spawn_1.spawnCli)(binary, args, options);
|
|
269
406
|
}
|
|
@@ -14,6 +14,52 @@ exports.extractDisplayText = extractDisplayText;
|
|
|
14
14
|
*/
|
|
15
15
|
function extractDisplayText(event) {
|
|
16
16
|
const type = event.type;
|
|
17
|
+
// ── Kimi `--output-format stream-json` ────────────────────────────────
|
|
18
|
+
// Kimi assistant frames are role-discriminated (no top-level `type`) and
|
|
19
|
+
// can contain both narration and OpenAI-shaped function calls in one line.
|
|
20
|
+
if (!type && event.role === 'assistant') {
|
|
21
|
+
const parts = [];
|
|
22
|
+
const content = event.content;
|
|
23
|
+
const text = typeof content === 'string'
|
|
24
|
+
? content
|
|
25
|
+
: Array.isArray(content)
|
|
26
|
+
? content.map((part) => {
|
|
27
|
+
if (typeof part === 'string')
|
|
28
|
+
return part;
|
|
29
|
+
if (!part || typeof part !== 'object')
|
|
30
|
+
return '';
|
|
31
|
+
const candidate = part;
|
|
32
|
+
return typeof candidate.text === 'string' ? candidate.text : '';
|
|
33
|
+
}).join('')
|
|
34
|
+
: '';
|
|
35
|
+
if (text)
|
|
36
|
+
parts.push(text);
|
|
37
|
+
const toolCalls = Array.isArray(event.tool_calls) ? event.tool_calls : [];
|
|
38
|
+
for (const call of toolCalls) {
|
|
39
|
+
if (!call || typeof call !== 'object')
|
|
40
|
+
continue;
|
|
41
|
+
const fn = call.function;
|
|
42
|
+
if (!fn || typeof fn !== 'object')
|
|
43
|
+
continue;
|
|
44
|
+
const record = fn;
|
|
45
|
+
const name = typeof record.name === 'string' ? record.name : '<unnamed>';
|
|
46
|
+
const rawArgs = record.arguments;
|
|
47
|
+
let args;
|
|
48
|
+
if (typeof rawArgs === 'string') {
|
|
49
|
+
args = rawArgs;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
try {
|
|
53
|
+
args = JSON.stringify(rawArgs ?? {});
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
args = String(rawArgs ?? '');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
parts.push(`[tool: ${name}] ${args.slice(0, 120)}`);
|
|
60
|
+
}
|
|
61
|
+
return parts.join('\n') || null;
|
|
62
|
+
}
|
|
17
63
|
// ── Claude `--output-format stream-json` ───────────────────────────────
|
|
18
64
|
if (type === 'assistant') {
|
|
19
65
|
const content = event.message;
|