@zaip-ai/cli 1.3.0 → 1.3.1
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 +1 -1
- package/dist/services/agent-adapters.js +1 -1
- package/dist/services/agent-adapters.js.map +1 -1
- package/dist/services/agent-adapters.test.js +2 -1
- package/dist/services/agent-adapters.test.js.map +1 -1
- package/dist/services/codex-pre-tool-hook-template.d.ts.map +1 -1
- package/dist/services/codex-pre-tool-hook-template.js +89 -7
- package/dist/services/codex-pre-tool-hook-template.js.map +1 -1
- package/dist/services/codex-rules.d.ts +1 -1
- package/dist/services/codex-rules.d.ts.map +1 -1
- package/dist/services/codex-rules.js +7 -0
- package/dist/services/codex-rules.js.map +1 -1
- package/dist/services/install-agents-template.d.ts.map +1 -1
- package/dist/services/install-agents-template.js +1 -0
- package/dist/services/install-agents-template.js.map +1 -1
- package/dist/services/runtime-template.test.js +31 -7
- package/dist/services/runtime-template.test.js.map +1 -1
- package/dist/services/template-manifest.d.ts.map +1 -1
- package/dist/services/template-manifest.js +7 -3
- package/dist/services/template-manifest.js.map +1 -1
- package/package.json +1 -1
- package/templates/.agents/config/agents/catalog.yaml +7 -7
- package/templates/.agents/config/clients/codex/agents/zaip-agent-backend.toml +2 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-designer.toml +2 -2
- package/templates/.agents/config/clients/codex/agents/zaip-agent-frontend.toml +8 -8
- package/templates/.agents/dashboard/.zaip-managed.json +373 -205
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-DxR_pgSV.js → CostCalculationGuidePage-ovxurkIk.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-4jZLTWE_.js → DashboardHelpDialog-BgVgrfhQ.js} +240 -86
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-CKcsqb-r.js → PricingSettingsDialog-vSvvOHQp.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-DmmJD2Th.js → ProjectSettingsPage-zH758vfs.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-DLn-Mz2M.js +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-CaKwQP43.css → TraceabilityWorkspace-DvvavDLY.css} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-DKwP1stV.js +68 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-PoJA2PI1.css +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-C0fj2uU9.js → icons-vendor-B_lDDHn1.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-B31GMG5O.css +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-CZQlsLV9.js +2 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-BEsJ-FYH.js → react-vendor-B3TJrD4O.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-B6y6Gw12.js → traceability-7f1j5wx3.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/index.html +7 -7
- package/templates/.agents/dashboard/apps/frontend/playwright.dom.config.ts +32 -0
- package/templates/.agents/dashboard/apps/frontend/src/App.tsx +132 -245
- package/templates/.agents/dashboard/apps/frontend/src/AppOverlays.tsx +0 -21
- package/templates/.agents/dashboard/apps/frontend/src/DashboardAlternateContent.tsx +9 -0
- package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.dom.e2e.ts +56 -0
- package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.test.ts +18 -1
- package/templates/.agents/dashboard/apps/frontend/src/TraceabilityWorkspace.tsx +75 -8
- package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +25 -1
- package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +7 -9
- package/templates/.agents/dashboard/apps/frontend/src/operations-period-render.test.ts +4 -4
- package/templates/.agents/dashboard/apps/frontend/src/overview-status-page.test.ts +1 -1
- package/templates/.agents/dashboard/apps/frontend/src/snapshot-banner-semantics.test.ts +7 -0
- package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +20 -8
- package/templates/.agents/dashboard/apps/frontend/src/sqlite-page-export.test.ts +32 -0
- package/templates/.agents/dashboard/apps/frontend/src/sqlite-page-export.ts +21 -0
- package/templates/.agents/dashboard/apps/frontend/src/styles.css +19 -0
- package/templates/.agents/dashboard/apps/frontend/src/traceability-task-pagination.test.ts +32 -0
- package/templates/.agents/dashboard/apps/frontend/src/traceability-task-pagination.ts +36 -0
- package/templates/.agents/dashboard/apps/frontend/src/traceability-workspace.css +6 -2
- package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.test.ts +48 -0
- package/templates/.agents/dashboard/apps/frontend/src/use-app-cost-loaders.ts +31 -27
- package/templates/.agents/dashboard/apps/frontend/src/use-app-trace-documents.ts +7 -1
- package/templates/.agents/dashboard/apps/frontend/src/use-app-work-list-page.test.ts +163 -0
- package/templates/.agents/dashboard/apps/frontend/src/use-app-work-list-page.ts +136 -0
- package/templates/.agents/dashboard/apps/frontend/src/use-operations-detail-export.ts +76 -0
- package/templates/.agents/dashboard/apps/frontend/src/work-list-load-dedup.test.ts +23 -0
- package/templates/.agents/dashboard/apps/frontend/src/work-list-load-dedup.ts +27 -0
- package/templates/.agents/dashboard/apps/frontend/src/work-list-pagination.test.ts +38 -14
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperAttributionPanel.tsx +60 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperAttributionTable.tsx +191 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.loading.test.ts +14 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.run-page.test.ts +18 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.tsx +18 -3
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorContent.tsx +8 -2
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorLoadErrors.tsx +35 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.test.ts +47 -1
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +50 -58
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableLoading.test.ts +132 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTableLoading.tsx +70 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +128 -138
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.tsx +17 -67
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-copy.ts +2 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-views.ts +3 -1
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-page-range.ts +35 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-compact-cost-page.ts +16 -4
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.test.ts +37 -2
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-group-page.ts +44 -8
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/use-cost-run-page.ts +6 -1
- package/templates/.agents/dashboard/modules/operations/src/contracts.ts +20 -3
- package/templates/.agents/dashboard/modules/operations/src/react/DeveloperAttributionPanel.test.ts +54 -0
- package/templates/.agents/dashboard/modules/operations/src/react/DeveloperAttributionPanel.tsx +247 -0
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsMainContent.tsx +39 -13
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.helpers.tsx +107 -2
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.macro-metrics.test.ts +55 -0
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.test.ts +20 -28
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.tsx +162 -165
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverviewTopPanel.tsx +127 -0
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +13 -46
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageOverview.tsx +3 -0
- package/templates/.agents/dashboard/modules/operations/src/react/PagedWorkList.test.tsx +43 -0
- package/templates/.agents/dashboard/modules/operations/src/react/PagedWorkList.tsx +181 -0
- package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.test.ts +4 -2
- package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkList.tsx +13 -0
- package/templates/.agents/dashboard/modules/operations/src/react/developer-identities.test.ts +18 -0
- package/templates/.agents/dashboard/modules/operations/src/react/developer-identities.ts +30 -0
- package/templates/.agents/dashboard/modules/operations/src/react/execution-metrics.ts +1 -0
- package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +8 -0
- package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +3 -0
- package/templates/.agents/dashboard/modules/operations/src/react/export/export-layout.test.ts +14 -5
- package/templates/.agents/dashboard/modules/operations/src/react/index.ts +1 -0
- package/templates/.agents/dashboard/modules/operations/src/react/operations-coverage-toast.test.ts +4 -3
- package/templates/.agents/dashboard/modules/operations/src/react/operations-date-filters.css +1 -1
- package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +21 -11
- package/templates/.agents/dashboard/modules/operations/src/react/operations-overview.css +4 -0
- package/templates/.agents/dashboard/modules/operations/src/react/operations-page-contract.ts +8 -1
- package/templates/.agents/dashboard/modules/operations/src/react/operations-view-order.test.ts +1 -0
- package/templates/.agents/dashboard/modules/operations/src/react/period-summary.ts +1 -0
- package/templates/.agents/dashboard/modules/operations/src/server/metric-coverage.ts +1 -0
- package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +527 -335
- package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +501 -309
- package/templates/.agents/dashboard/packages/local-runtime/gitignore.template +2 -0
- package/templates/.agents/dashboard/packages/local-runtime/scripts/rebuild-sqlite-read-model.mjs +3 -1
- package/templates/.agents/dashboard/packages/local-runtime/scripts/test-sqlite-read-model.mjs +3 -3
- package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-sqlite-read-model.mjs +25 -15
- package/templates/.agents/dashboard/packages/local-runtime/server.mjs +527 -335
- package/templates/.agents/dashboard/packages/local-runtime/src/server-board.test.ts +34 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-board.ts +15 -8
- package/templates/.agents/dashboard/packages/local-runtime/src/server-classification-page.test.ts +87 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-period-macro-metric.test.ts +44 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-period-macro-metric.ts +125 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-refresh.test.ts +66 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-request-authority.test.ts +61 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-request-authority.ts +18 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-run-projections.test.ts +42 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-refresh.ts +97 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot.test.ts +3 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/server-sqlite-task-trace.ts +91 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server.test-fixtures.ts +36 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +98 -123
- package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +111 -135
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-artifact-counts.ts +28 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.test.ts +63 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.ts +109 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-aggregates.ts +497 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-fast-page-parity.test-support.ts +65 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-page.test.ts +176 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-page.ts +119 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-group-projection.ts +41 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.test.ts +16 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.ts +29 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-cache.ts +355 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-persistence.ts +121 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-first-fold-timing.ts +27 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-projection.ts +15 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-work-list.test.ts +223 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-operations-work-list.ts +304 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-cache.test-support.ts +23 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-first-fold-types.ts +89 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider-fixture.ts +227 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-multi-provider.test.ts +691 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.ts +81 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +440 -863
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +1120 -1086
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-cache.ts +129 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-install.test.ts +26 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-install.ts +28 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-seed.test.ts +116 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.mjs +146 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.test.mjs +165 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-refresh.mjs +140 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +188 -299
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +51 -70
- package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +5 -2
- package/templates/.agents/dashboard/tests/e2e/playwright.config.ts +4 -1
- package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-loading.spec.ts +142 -0
- package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-pagination.spec.ts +232 -0
- package/templates/.agents/dashboard/tests/e2e/specs/dashboard-performance-matrix.spec.ts +743 -0
- package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +183 -210
- package/templates/.agents/dashboard/zaip-dashboard-windows.exe +0 -0
- package/templates/.agents/requirements/autonomous-assurance.md +6 -6
- package/templates/.agents/requirements/comparative-audit.md +1 -1
- package/templates/.agents/requirements/context-engine.md +1 -1
- package/templates/.agents/requirements/cost-observability.md +1 -1
- package/templates/.agents/requirements/dashboard-launch.md +1 -1
- package/templates/.agents/requirements/data-export.md +1 -1
- package/templates/.agents/requirements/design-system.md +1 -1
- package/templates/.agents/requirements/execution-engine.md +3 -3
- package/templates/.agents/requirements/kanban-dashboard.md +10 -2
- package/templates/.agents/requirements/model-orchestration.md +1 -1
- package/templates/.agents/requirements/public-distribution.md +1 -1
- package/templates/.agents/requirements/software-architecture.md +1 -1
- package/templates/.agents/requirements/strategic-improvements.md +1 -1
- package/templates/.agents/requirements/work-lifecycle.md +4 -4
- package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
- package/templates/.agents/rules/dashboard-ui-consistency.md +14 -0
- package/templates/.agents/rules/frontend-lite.md +4 -4
- package/templates/.agents/rules/run-lifecycle-closure.md +4 -4
- package/templates/.agents/runtime/bin/zaip.mjs +1137 -1037
- package/templates/.agents/runtime/checksums.sha256 +2 -2
- package/templates/.agents/runtime/provenance.json +2 -2
- package/templates/.agents/skills/criar-pasta-obsidian/scripts/generate-obsidian-vault.test.mjs +20 -16
- package/templates/.agents/skills/zaip-skill-agent-catalog/SKILL.md +22 -0
- package/templates/.agents/skills/zaip-skill-agent-catalog/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-author-skills/SKILL.md +3 -2
- package/templates/.agents/skills/zaip-skill-author-skills/scripts/audit-skill-catalog.mjs +49 -0
- package/templates/.agents/skills/zaip-skill-author-skills/scripts/author-skill-scripts.test.mjs +27 -0
- package/templates/.agents/skills/zaip-skill-author-skills/scripts/run-skill-tests.mjs +37 -0
- package/templates/.agents/skills/zaip-skill-backend/SKILL.md +1 -0
- package/templates/.agents/skills/zaip-skill-change/SKILL.md +1 -1
- package/templates/.agents/skills/zaip-skill-change/references/observable-evidence.md +83 -0
- package/templates/.agents/skills/zaip-skill-dashboard-diagnostics/SKILL.md +22 -0
- package/templates/.agents/skills/zaip-skill-dashboard-diagnostics/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-execution/SKILL.md +1 -1
- package/templates/.agents/skills/zaip-skill-frontend/SKILL.md +4 -3
- package/templates/.agents/skills/zaip-skill-frontend/agents/openai.yaml +2 -2
- package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +10 -10
- package/templates/.agents/skills/zaip-skill-lifecycle-closure/SKILL.md +27 -0
- package/templates/.agents/skills/zaip-skill-lifecycle-closure/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-prd/SKILL.md +4 -0
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-local-telemetry.mjs +72 -22
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-explicit-recovery.mjs +223 -0
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.mjs +42 -1
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.test.mjs +101 -0
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.test.mjs +53 -0
- package/templates/.agents/skills/zaip-skill-release-qualification/SKILL.md +20 -0
- package/templates/.agents/skills/zaip-skill-release-qualification/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-review/SKILL.md +5 -0
- package/templates/.agents/skills/zaip-skill-rules/SKILL.md +1 -1
- package/templates/.agents/skills/zaip-skill-spec/SKILL.md +4 -0
- package/templates/.agents/skills/zaip-skill-sqlite-read-model/SKILL.md +22 -90
- package/templates/.agents/zaip-aios/INDEX.md +57 -32
- package/templates/.agents/zaip-aios/README.md +22 -12
- package/templates/.agents/zaip-aios/guides/02-requirements.md +5 -0
- package/templates/.agents/zaip-aios/guides/06-execucao.md +13 -4
- package/templates/.agents/zaip-aios/guides/07-revisao-e-validacao.md +7 -0
- package/templates/.agents/zaip-aios/guides/10-seguranca.md +23 -0
- package/templates/.agents/zaip-aios/guides/11-observabilidade-e-custos.md +6 -6
- package/templates/.agents/zaip-aios/guides/13-boas-praticas.md +3 -0
- package/templates/.agents/zaip-aios/guides/16-lifecycle-e-reconciliacao.md +12 -3
- package/templates/.agents/zaip-aios/guides/17-como-o-custo-e-calculado.md +5 -5
- package/templates/.agents/zaip-aios/guides/19-assurance-e-autonomia.md +23 -0
- package/templates/.agents/zaip-aios/guides/20-instalacao-detalhada.md +44 -8
- package/templates/.agents/zaip-aios/guides/21-telemetria-monitor-custos.md +8 -14
- package/templates/.agents/zaip-aios/guides/22-fluxos-e-roteamento.md +5 -0
- package/templates/.agents/zaip-aios/guides/23-orquestracao-de-modelos.md +6 -6
- package/templates/.agents/zaip-aios/guides/24-calculo-de-custos-openai.md +8 -8
- package/templates/.agents/zaip-aios/guides/25-fontes-de-precos-openai.md +3 -2
- package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +77 -10
- package/templates/.agents/zaip-aios/guides/27-telemetria-git-native.md +8 -3
- package/templates/.agents/zaip-aios/guides/28-lifecycle-completo.md +5 -0
- package/templates/template-manifest.json +555 -170
- package/templates/.agents/dashboard/apps/frontend/dist/assets/TraceabilityWorkspace-BwtitWa_.js +0 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-BZ5WdaMN.css +0 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-N5Csr0RP.js +0 -68
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-C4aIB1PV.js +0 -2
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-DK_enB5Z.css +0 -1
- package/templates/.agents/dashboard/apps/frontend/src/use-idle-cost-prefetch.ts +0 -45
- package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs.metafile.json +0 -7333
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createHash, randomBytes } from "node:crypto";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
4
|
import { serve } from "@hono/node-server";
|
|
6
5
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
7
6
|
import { loadWorkspaceLayout, WorkspaceFileSystem, type WorkspaceLayout } from "@zaip-ai/core";
|
|
@@ -18,17 +17,11 @@ import {
|
|
|
18
17
|
readSpecDetail,
|
|
19
18
|
readTaskDetail,
|
|
20
19
|
} from "@zaip-ai/dashboard-operations/server";
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
buildTraceStageRecords,
|
|
24
|
-
classifyTraceDocument,
|
|
25
|
-
timestampsFromContent,
|
|
26
|
-
} from "@zaip-ai/dashboard-traceability/server";
|
|
27
|
-
import { type CompactRunMetric, LifecycleManifestStore } from "@zaip-ai/lifecycle-manifests";
|
|
20
|
+
import { buildTaskTrace } from "@zaip-ai/dashboard-traceability/server";
|
|
21
|
+
import { LifecycleManifestStore } from "@zaip-ai/lifecycle-manifests";
|
|
28
22
|
import { type Context, Hono } from "hono";
|
|
29
23
|
import { getCookie, setCookie } from "hono/cookie";
|
|
30
24
|
import type { ContentfulStatusCode } from "hono/utils/http-status";
|
|
31
|
-
import type { TaskTrace, TraceDocument } from "../../../modules/traceability/src/contracts.js";
|
|
32
25
|
import { runAgentAdapterSync } from "./agent-adapter-sync.js";
|
|
33
26
|
import {
|
|
34
27
|
type AgentAdapterSyncResult,
|
|
@@ -57,21 +50,28 @@ import {
|
|
|
57
50
|
} from "./server-operations-pages.js";
|
|
58
51
|
import { registerOperationsRunsRoute } from "./server-operations-runs-route.js";
|
|
59
52
|
import { projectionPeriodFilter } from "./server-period.js";
|
|
53
|
+
import { periodMacroMetric, projectAllTimeTopTasks } from "./server-period-macro-metric.js";
|
|
60
54
|
import {
|
|
61
55
|
buildCanonicalOperationsProjection,
|
|
62
56
|
buildCurrentCostProjection,
|
|
63
57
|
buildManifestCostRunPage,
|
|
64
58
|
buildManifestFirstCostProjection,
|
|
65
|
-
filterCostRunRows,
|
|
66
59
|
isUnfilteredCostRequest,
|
|
67
60
|
knownWorkIds,
|
|
68
61
|
normalizeWorkPriority,
|
|
69
|
-
pageCostRunRows,
|
|
70
62
|
readCurrentFxRate,
|
|
71
63
|
readWorkPriority,
|
|
72
64
|
} from "./server-projections.js";
|
|
65
|
+
import { isAllowedDashboardAuthority } from "./server-request-authority.js";
|
|
66
|
+
import { refreshSqliteSnapshotInWorker } from "./server-snapshot-refresh.js";
|
|
67
|
+
import { buildSqliteTaskTrace } from "./server-sqlite-task-trace.js";
|
|
73
68
|
import { queryValues, selectedCurrency } from "./server-summary-helpers.js";
|
|
74
|
-
import {
|
|
69
|
+
import {
|
|
70
|
+
dashboardIdentity,
|
|
71
|
+
dashboardWorkspaceKey,
|
|
72
|
+
type ReconciliationResult,
|
|
73
|
+
reconcileOpenWork,
|
|
74
|
+
} from "./server-workspace.js";
|
|
75
75
|
import {
|
|
76
76
|
rebuildSqliteReadModel,
|
|
77
77
|
SqliteReadModelFileSystem,
|
|
@@ -80,6 +80,7 @@ import {
|
|
|
80
80
|
|
|
81
81
|
export { manifestRun, mergeManifestRuns } from "./manifest-runs.js";
|
|
82
82
|
export { projectionPeriodFilter } from "./server-period.js";
|
|
83
|
+
export { projectAllTimeTopTasks } from "./server-period-macro-metric.js";
|
|
83
84
|
export {
|
|
84
85
|
dashboardWorkspaceKey,
|
|
85
86
|
githubRepositoryFromRemote,
|
|
@@ -93,28 +94,14 @@ export {
|
|
|
93
94
|
validateSqliteReadModelSchema,
|
|
94
95
|
} from "./sqlite-read-model.js";
|
|
95
96
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
total_duration_ms: number | null;
|
|
99
|
-
total_tokens: number | null;
|
|
100
|
-
total_cost_usd: number | null;
|
|
101
|
-
total_cost_brl: number | null;
|
|
102
|
-
total_run_count: number;
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
export function projectAllTimeTopTasks(
|
|
106
|
-
tasks: readonly AllTimeTopTaskFact[],
|
|
107
|
-
): NonNullable<NonNullable<OperationsProjection["summary"]["overview_metrics"]>["top_tasks"]> {
|
|
108
|
-
return tasks.map((task) => ({
|
|
109
|
-
task_id: task.task_id,
|
|
110
|
-
duration_ms: task.total_duration_ms,
|
|
111
|
-
total_tokens: task.total_tokens,
|
|
112
|
-
total_cost_usd: task.total_cost_usd,
|
|
113
|
-
cost_brl: task.total_cost_brl,
|
|
114
|
-
run_count: task.total_run_count,
|
|
115
|
-
}));
|
|
97
|
+
export function reconciliationNeedsRefresh(result: ReconciliationResult): boolean {
|
|
98
|
+
return result.reconciled_ids.length > 0 || result.blocked.length > 0;
|
|
116
99
|
}
|
|
117
100
|
|
|
101
|
+
type OverviewMacroMetrics = NonNullable<
|
|
102
|
+
NonNullable<OperationsProjection["summary"]["overview_metrics"]>["macro_metrics"]
|
|
103
|
+
>;
|
|
104
|
+
|
|
118
105
|
const API_PREFIX = "/api/v2";
|
|
119
106
|
const HOST = "127.0.0.1";
|
|
120
107
|
const RUNTIME_KIND = "zaip-dashboard-sqlite-runtime";
|
|
@@ -171,6 +158,8 @@ interface DashboardAppOptions {
|
|
|
171
158
|
reconcileOpenWork?: typeof reconcileOpenWork;
|
|
172
159
|
refreshReadModel?: () => Promise<void>;
|
|
173
160
|
snapshotState?: "current" | "updating";
|
|
161
|
+
/** Test seam: health and reconciliation only require the asserted V2 layout. */
|
|
162
|
+
layout?: WorkspaceLayout;
|
|
174
163
|
}
|
|
175
164
|
|
|
176
165
|
class V2WorkspaceRequiredError extends Error {
|
|
@@ -233,7 +222,6 @@ function headOrJson(
|
|
|
233
222
|
if (context.req.method === "HEAD") return context.body(null, status);
|
|
234
223
|
return context.json(payload, status);
|
|
235
224
|
}
|
|
236
|
-
|
|
237
225
|
/**
|
|
238
226
|
* Keep the entity/macro details on the snapshot filesystem, while making the
|
|
239
227
|
* first-fold counters and token/cost facts come from SQL.
|
|
@@ -309,15 +297,23 @@ function buildSqliteOperationsProjection(
|
|
|
309
297
|
const allTime =
|
|
310
298
|
fullAllTime ??
|
|
311
299
|
(filter.from === null && filter.to === null ? filesystem.firstFoldViewportAllTime() : null);
|
|
300
|
+
const allTimeBoard =
|
|
301
|
+
filter.from === null && filter.to === null && options.includeBoardTasks === true
|
|
302
|
+
? filesystem.firstFoldBoardAllTime()
|
|
303
|
+
: null;
|
|
312
304
|
const page = <T extends "task" | "spec" | "prd">(kind: T) =>
|
|
313
|
-
cachedMonthlyQuick?.pages[kind] ??
|
|
305
|
+
cachedMonthlyQuick?.pages[kind] ??
|
|
306
|
+
allTimeBoard?.pages[kind] ??
|
|
307
|
+
filesystem.canonicalWorkPage(kind, filter, 0, 10_000).rows;
|
|
314
308
|
const includeEntities = allTime === null || options.includeBoardTasks === true;
|
|
315
309
|
const tasks = includeEntities ? page("task") : [];
|
|
316
310
|
const specs = includeEntities ? page("spec") : [];
|
|
317
311
|
const prds = includeEntities ? page("prd") : [];
|
|
318
312
|
const aggregate =
|
|
319
313
|
allTime?.aggregate ?? cachedMonthlyQuick?.aggregate ?? filesystem.firstFoldAggregate(filter);
|
|
320
|
-
const
|
|
314
|
+
const legacyDraftCount = allTime?.task_status_counts.draft ?? 0;
|
|
315
|
+
const taskCount =
|
|
316
|
+
allTime === null ? tasks.length : Math.max(0, allTime.aggregate.task_count - legacyDraftCount);
|
|
321
317
|
const allTimeTopTasks =
|
|
322
318
|
filter.from === null &&
|
|
323
319
|
filter.to === null &&
|
|
@@ -336,7 +332,10 @@ function buildSqliteOperationsProjection(
|
|
|
336
332
|
: projectAllTimeTopTasks(allTimeTopTasks);
|
|
337
333
|
const macroMetrics =
|
|
338
334
|
fullAllTime === null
|
|
339
|
-
?
|
|
335
|
+
? ({
|
|
336
|
+
specs: periodMacroMetric(specs, "spec"),
|
|
337
|
+
prds: periodMacroMetric(prds, "prd"),
|
|
338
|
+
} satisfies OverviewMacroMetrics)
|
|
340
339
|
: (Object.fromEntries(
|
|
341
340
|
(["specs", "prds"] as const).map((kind) => {
|
|
342
341
|
const metric = fullAllTime.macro_metrics[kind];
|
|
@@ -372,7 +371,9 @@ function buildSqliteOperationsProjection(
|
|
|
372
371
|
) as NonNullable<
|
|
373
372
|
NonNullable<OperationsProjection["summary"]["overview_metrics"]>["macro_metrics"]
|
|
374
373
|
>);
|
|
375
|
-
const statuses: Record<string, number> =
|
|
374
|
+
const statuses: Record<string, number> = Object.fromEntries(
|
|
375
|
+
Object.entries(allTime?.task_status_counts ?? {}).filter(([status]) => status !== "draft"),
|
|
376
|
+
);
|
|
376
377
|
const risks: Record<string, number> = allTime?.task_risk_counts ?? {};
|
|
377
378
|
if (allTime === null) {
|
|
378
379
|
for (const task of tasks) {
|
|
@@ -417,7 +418,7 @@ function buildSqliteOperationsProjection(
|
|
|
417
418
|
schema_version: 2 as const,
|
|
418
419
|
id: String(row.id),
|
|
419
420
|
revision: 1,
|
|
420
|
-
change_id: "",
|
|
421
|
+
change_id: typeof row.change_id === "string" ? row.change_id : "",
|
|
421
422
|
title: String(row.title ?? row.id),
|
|
422
423
|
short_name: String(row.title ?? row.id),
|
|
423
424
|
objective: "",
|
|
@@ -437,7 +438,8 @@ function buildSqliteOperationsProjection(
|
|
|
437
438
|
result: null,
|
|
438
439
|
artifact_path: ".agents/records",
|
|
439
440
|
progress: 0,
|
|
440
|
-
spec_id: null,
|
|
441
|
+
spec_id: typeof row.spec_id === "string" ? row.spec_id : null,
|
|
442
|
+
origin: typeof row.origin === "string" ? row.origin : null,
|
|
441
443
|
execution_started: false,
|
|
442
444
|
telemetry: {
|
|
443
445
|
duration_ms: nullableMetric(row.total_duration),
|
|
@@ -551,9 +553,6 @@ function buildSqliteOperationsProjection(
|
|
|
551
553
|
origin_tokens_match_total: true,
|
|
552
554
|
},
|
|
553
555
|
usage,
|
|
554
|
-
// The period tabs are part of the first-fold contract too. Supplying an
|
|
555
|
-
// empty object for the all-time fast path made Daily, Monthly and Annual
|
|
556
|
-
// look empty even though the SQLite daily aggregate already existed.
|
|
557
556
|
periods: { daily: options.includePeriods === true ? dailyPeriods : {} },
|
|
558
557
|
},
|
|
559
558
|
origins: { conversation: usage, task: usage, spec: usage, prd: usage },
|
|
@@ -642,88 +641,6 @@ async function firstFoldCostSource(
|
|
|
642
641
|
} satisfies CostSummarySource;
|
|
643
642
|
}
|
|
644
643
|
|
|
645
|
-
function buildSqliteTaskTrace(
|
|
646
|
-
filesystem: SqliteReadModelFileSystem,
|
|
647
|
-
layout: WorkspaceLayout,
|
|
648
|
-
taskId: string,
|
|
649
|
-
): TaskTrace {
|
|
650
|
-
const initial = filesystem.canonicalTraceFiles([taskId]);
|
|
651
|
-
const taskFile = initial.find(
|
|
652
|
-
(row) =>
|
|
653
|
-
row.path.includes("/.agents/records/tasks/") || row.path.includes(".agents/records/tasks/"),
|
|
654
|
-
);
|
|
655
|
-
if (taskFile === undefined) throw new Error(`Canonical Task ${taskId} was not found`);
|
|
656
|
-
const scalar = (key: string): string | null => {
|
|
657
|
-
const frontMatter = /^---\r?\n([\s\S]*?)\r?\n---/.exec(taskFile.content)?.[1] ?? "";
|
|
658
|
-
const value = new RegExp(`^${key}:\\s*(.+?)\\s*$`, "m").exec(frontMatter)?.[1]?.trim();
|
|
659
|
-
if (value === undefined || value === "null") return null;
|
|
660
|
-
return value.replace(/^['"]|['"]$/g, "");
|
|
661
|
-
};
|
|
662
|
-
const relatedId = (key: string, prefix: string): string | null =>
|
|
663
|
-
scalar(key) ?? new RegExp(`\\b${prefix}[A-Z0-9-]+`).exec(taskFile.content)?.[0] ?? null;
|
|
664
|
-
const changeId = relatedId("change_id", "CHG-") ?? taskId;
|
|
665
|
-
const specId = relatedId("spec_id", "SPEC-");
|
|
666
|
-
const prdId = relatedId("prd_id", "PRD-");
|
|
667
|
-
const markers = [taskId, changeId, specId, prdId].filter(
|
|
668
|
-
(value): value is string => value !== null,
|
|
669
|
-
);
|
|
670
|
-
const documents = filesystem
|
|
671
|
-
.canonicalTraceFiles(markers)
|
|
672
|
-
.filter(
|
|
673
|
-
(row) => /\.(?:md|json|jsonl|txt|log)$/i.test(row.path) && row.content.length <= 250_000,
|
|
674
|
-
)
|
|
675
|
-
.map((row) => {
|
|
676
|
-
const classification = classifyTraceDocument(row.path);
|
|
677
|
-
const timestamps = timestampsFromContent(row.content);
|
|
678
|
-
return {
|
|
679
|
-
id: createHash("sha256").update(row.path).digest("hex").slice(0, 16),
|
|
680
|
-
path: row.path,
|
|
681
|
-
title:
|
|
682
|
-
row.content.match(/^#\s+(.+)$/m)?.[1]?.trim() ?? row.path.split("/").at(-1) ?? row.path,
|
|
683
|
-
...classification,
|
|
684
|
-
content: row.content,
|
|
685
|
-
...timestamps,
|
|
686
|
-
} satisfies TraceDocument;
|
|
687
|
-
});
|
|
688
|
-
const stages = buildTraceStageRecords(documents);
|
|
689
|
-
const coveredStages = new Set(documents.map((document) => document.stage));
|
|
690
|
-
const risk = scalar("risk");
|
|
691
|
-
const status = scalar("status");
|
|
692
|
-
return {
|
|
693
|
-
schema_version: 2,
|
|
694
|
-
layout_version: layout.version as 2 | 3,
|
|
695
|
-
readonly: true,
|
|
696
|
-
source: ".agents/records",
|
|
697
|
-
task_id: taskId,
|
|
698
|
-
task_title: scalar("title") ?? taskFile.content.match(/^#\s+(.+)$/m)?.[1]?.trim() ?? taskId,
|
|
699
|
-
task_risk: risk === "lite" || risk === "standard" || risk === "critical" ? risk : null,
|
|
700
|
-
task_created_at: scalar("created_at"),
|
|
701
|
-
task_progress:
|
|
702
|
-
status === "completed"
|
|
703
|
-
? 100
|
|
704
|
-
: status === "verifying"
|
|
705
|
-
? 80
|
|
706
|
-
: status === "executing"
|
|
707
|
-
? 50
|
|
708
|
-
: status === "blocked" || status === "failed" || status === "cancelled"
|
|
709
|
-
? 0
|
|
710
|
-
: 10,
|
|
711
|
-
spec_id: specId,
|
|
712
|
-
prd_id: prdId,
|
|
713
|
-
change_id: changeId,
|
|
714
|
-
usage: filesystem.taskTraceUsage(taskId),
|
|
715
|
-
documents,
|
|
716
|
-
stages,
|
|
717
|
-
coverage: {
|
|
718
|
-
stages_with_documents: coveredStages.size,
|
|
719
|
-
stages_with_timestamps: stages.filter((stage) => stage.status === "recorded").length,
|
|
720
|
-
total_stages: 6,
|
|
721
|
-
evidence_count: documents.filter((document) => document.kind === "evidence").length,
|
|
722
|
-
test_count: documents.filter((document) => document.kind === "test").length,
|
|
723
|
-
},
|
|
724
|
-
};
|
|
725
|
-
}
|
|
726
|
-
|
|
727
644
|
export function createDashboardApp(
|
|
728
645
|
workspaceRoot = process.env.ZAIP_WORKSPACE ?? process.cwd(),
|
|
729
646
|
sessionToken = defaultSessionToken(),
|
|
@@ -737,7 +654,8 @@ export function createDashboardApp(
|
|
|
737
654
|
// its V2 layout repeatedly costs hundreds of milliseconds per navigation;
|
|
738
655
|
// canonical filesystems remain deliberately uncached.
|
|
739
656
|
const sqliteLayout = isSqliteSnapshot ? requireV2Layout(filesystem) : null;
|
|
740
|
-
const readLayout = () =>
|
|
657
|
+
const readLayout = async () =>
|
|
658
|
+
options.layout ?? sqliteLayout ?? (await requireV2Layout(filesystem));
|
|
741
659
|
const snapshotReadOnly = options.snapshotReadOnly ?? isSqliteSnapshot;
|
|
742
660
|
const snapshotState = options.snapshotState ?? "current";
|
|
743
661
|
const canonicalFilesystem = options.canonicalFilesystem ?? new WorkspaceFileSystem(workspaceRoot);
|
|
@@ -745,13 +663,7 @@ export function createDashboardApp(
|
|
|
745
663
|
const refreshReadModel =
|
|
746
664
|
options.refreshReadModel ??
|
|
747
665
|
(async () => {
|
|
748
|
-
if (
|
|
749
|
-
// Explicit user refreshes must replace the immutable generation with the
|
|
750
|
-
// current canonical records. Keep stale-seed fallback exclusively for
|
|
751
|
-
// startup warmth, never for this visible convergence request.
|
|
752
|
-
await filesystem.rebuildAndRefresh(() =>
|
|
753
|
-
rebuildSqliteReadModel(workspaceRoot, { forceCanonical: true }),
|
|
754
|
-
);
|
|
666
|
+
if (isSqliteSnapshot) await refreshSqliteSnapshotInWorker(workspaceRoot, filesystem);
|
|
755
667
|
});
|
|
756
668
|
const workspaceKey = dashboardWorkspaceKey(workspaceRoot);
|
|
757
669
|
const syncAgentAdapters = options.syncAgentAdapters ?? (() => runAgentAdapterSync(workspaceRoot));
|
|
@@ -839,6 +751,9 @@ export function createDashboardApp(
|
|
|
839
751
|
};
|
|
840
752
|
|
|
841
753
|
app.use("*", async (context, next) => {
|
|
754
|
+
if (!isAllowedDashboardAuthority(context.req.url, context.req.header("host"))) {
|
|
755
|
+
return context.json({ error: "Host rejected" }, 403);
|
|
756
|
+
}
|
|
842
757
|
context.header("Cache-Control", "no-store");
|
|
843
758
|
context.header("X-Content-Type-Options", "nosniff");
|
|
844
759
|
context.header("Referrer-Policy", "no-referrer");
|
|
@@ -1089,6 +1004,7 @@ export function createDashboardApp(
|
|
|
1089
1004
|
{ from, to },
|
|
1090
1005
|
{
|
|
1091
1006
|
includeTopTasks: from === null && to === null,
|
|
1007
|
+
includeExtended: from === null && to === null,
|
|
1092
1008
|
},
|
|
1093
1009
|
),
|
|
1094
1010
|
context.req.query("fresh") === "1",
|
|
@@ -1194,6 +1110,13 @@ export function createDashboardApp(
|
|
|
1194
1110
|
const value = context.req.query(name);
|
|
1195
1111
|
return value === undefined ? {} : { [name]: value };
|
|
1196
1112
|
};
|
|
1113
|
+
const workListKind = context.req.query("kind");
|
|
1114
|
+
if (
|
|
1115
|
+
view === "operations_work_list" &&
|
|
1116
|
+
workListKind !== undefined &&
|
|
1117
|
+
!["all", "task", "standalone-task", "prd", "spec"].includes(workListKind)
|
|
1118
|
+
)
|
|
1119
|
+
return headOrJson(context, { error: "Unsupported Operations work-list kind" }, 400);
|
|
1197
1120
|
return headOrJson(
|
|
1198
1121
|
context,
|
|
1199
1122
|
filesystem.viewPage(view as keyof typeof sqliteViewMetadata, {
|
|
@@ -1209,6 +1132,9 @@ export function createDashboardApp(
|
|
|
1209
1132
|
...optional("origin"),
|
|
1210
1133
|
...optional("workType"),
|
|
1211
1134
|
...optional("priority"),
|
|
1135
|
+
...(workListKind === undefined
|
|
1136
|
+
? {}
|
|
1137
|
+
: { kind: workListKind as "all" | "task" | "standalone-task" | "prd" | "spec" }),
|
|
1212
1138
|
offset: Math.max(0, Number.parseInt(context.req.query("offset") ?? "0", 10) || 0),
|
|
1213
1139
|
limit,
|
|
1214
1140
|
}),
|
|
@@ -1295,13 +1221,19 @@ export function createDashboardApp(
|
|
|
1295
1221
|
try {
|
|
1296
1222
|
await readLayout();
|
|
1297
1223
|
// The served snapshot is immutable by design. Reconciliation must use
|
|
1298
|
-
// canonical records,
|
|
1299
|
-
// before the response exposes fresh projections.
|
|
1224
|
+
// canonical records. When those records changed, atomically rebuild and
|
|
1225
|
+
// reopen the snapshot before the response exposes fresh projections.
|
|
1300
1226
|
const reconciledIds = await reconcileOpen(canonicalFilesystem, workspaceRoot);
|
|
1301
|
-
|
|
1227
|
+
// A clean reconciliation is the normal startup path. It changed no
|
|
1228
|
+
// canonical data, so rebuilding the immutable SQLite snapshot only adds
|
|
1229
|
+
// seconds of redundant work before the dashboard can settle.
|
|
1230
|
+
if (reconciliationNeedsRefresh(reconciledIds)) {
|
|
1231
|
+
await rebuildProjection();
|
|
1232
|
+
}
|
|
1302
1233
|
return context.json({
|
|
1303
1234
|
...(await readSnapshot(context)),
|
|
1304
1235
|
reconciliation: reconciledIds,
|
|
1236
|
+
projection_refresh_completed: reconciliationNeedsRefresh(reconciledIds),
|
|
1305
1237
|
});
|
|
1306
1238
|
} catch (error) {
|
|
1307
1239
|
if (error instanceof V2WorkspaceRequiredError) throw error;
|
|
@@ -1409,6 +1341,50 @@ export function createDashboardApp(
|
|
|
1409
1341
|
}
|
|
1410
1342
|
});
|
|
1411
1343
|
|
|
1344
|
+
/** Compact factual attribution view used by both Cost Monitor and Operations.
|
|
1345
|
+
* It is intentionally read-only and is available only from a coherent SQLite
|
|
1346
|
+
* generation, never by reopening mutable JSONL streams on a request. */
|
|
1347
|
+
app.on(["GET", "HEAD"], `${API_PREFIX}/developers`, async (context) => {
|
|
1348
|
+
if (!(filesystem instanceof SqliteReadModelFileSystem))
|
|
1349
|
+
return headOrJson(
|
|
1350
|
+
context,
|
|
1351
|
+
{ error: "Developer attribution requires the SQLite read model" },
|
|
1352
|
+
409,
|
|
1353
|
+
);
|
|
1354
|
+
const { from, to } = projectionPeriodFilter(context);
|
|
1355
|
+
return headOrJson(
|
|
1356
|
+
context,
|
|
1357
|
+
filesystem.developerCostAggregate({
|
|
1358
|
+
from,
|
|
1359
|
+
to,
|
|
1360
|
+
accountIds: queryValues(context, "account_id"),
|
|
1361
|
+
}),
|
|
1362
|
+
200,
|
|
1363
|
+
true,
|
|
1364
|
+
);
|
|
1365
|
+
});
|
|
1366
|
+
|
|
1367
|
+
/** Operations keeps its developer facts separate from Cost Monitor columns. */
|
|
1368
|
+
app.on(["GET", "HEAD"], `${API_PREFIX}/operations/developers`, async (context) => {
|
|
1369
|
+
if (!(filesystem instanceof SqliteReadModelFileSystem))
|
|
1370
|
+
return headOrJson(
|
|
1371
|
+
context,
|
|
1372
|
+
{ error: "Developer attribution requires the SQLite read model" },
|
|
1373
|
+
409,
|
|
1374
|
+
);
|
|
1375
|
+
const { from, to } = projectionPeriodFilter(context);
|
|
1376
|
+
return headOrJson(
|
|
1377
|
+
context,
|
|
1378
|
+
filesystem.operationsDeveloperAggregate({
|
|
1379
|
+
from,
|
|
1380
|
+
to,
|
|
1381
|
+
accountIds: queryValues(context, "account_id"),
|
|
1382
|
+
}),
|
|
1383
|
+
200,
|
|
1384
|
+
true,
|
|
1385
|
+
);
|
|
1386
|
+
});
|
|
1387
|
+
|
|
1412
1388
|
app.on(["GET", "HEAD"], `${API_PREFIX}/pricing`, async (context) =>
|
|
1413
1389
|
headOrJson(
|
|
1414
1390
|
context,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function developerArtifactCountsCte() {
|
|
2
|
+
return `, artifacts AS (
|
|
3
|
+
SELECT a.developer_account_id,
|
|
4
|
+
COUNT(DISTINCT CASE WHEN artifact.artifact_kind='validation' THEN artifact.source_path END) AS test_count,
|
|
5
|
+
COUNT(DISTINCT CASE WHEN artifact.artifact_kind='evidence' THEN artifact.source_path END) AS evidence_count
|
|
6
|
+
FROM selected a
|
|
7
|
+
JOIN canonical_artifacts artifact ON artifact.run_id=a.run_id
|
|
8
|
+
AND artifact.task_id=a.task_id
|
|
9
|
+
AND (artifact.change_id IS NULL OR artifact.change_id=a.change_id)
|
|
10
|
+
WHERE artifact.occurred_day_sp IS NOT NULL
|
|
11
|
+
AND (? IS NULL OR artifact.occurred_day_sp >= ?)
|
|
12
|
+
AND (? IS NULL OR artifact.occurred_day_sp <= ?)
|
|
13
|
+
GROUP BY a.developer_account_id
|
|
14
|
+
)`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function artifactStructuralRowsSql() {
|
|
18
|
+
return `UNION ALL
|
|
19
|
+
SELECT occurred_day_sp,
|
|
20
|
+
0, 0, 0,
|
|
21
|
+
SUM(CASE WHEN artifact_kind='validation' THEN 1 ELSE 0 END),
|
|
22
|
+
SUM(CASE WHEN artifact_kind='evidence' THEN 1 ELSE 0 END)
|
|
23
|
+
FROM canonical_artifacts
|
|
24
|
+
WHERE occurred_day_sp IS NOT NULL
|
|
25
|
+
AND (? IS NULL OR occurred_day_sp >= ?)
|
|
26
|
+
AND (? IS NULL OR occurred_day_sp <= ?)
|
|
27
|
+
GROUP BY occurred_day_sp`;
|
|
28
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { isDateOnlyCostPage, readDateOnlyCostDimensionPage } from "./sqlite-cost-agent-page.js";
|
|
4
|
+
|
|
5
|
+
describe("date-only Cost page", () => {
|
|
6
|
+
it("accepts only period input and keeps all custom filters on the canonical path", () => {
|
|
7
|
+
expect(isDateOnlyCostPage({ from: "2026-08-01", to: "2026-08-31" })).toBe(true);
|
|
8
|
+
expect(isDateOnlyCostPage({ from: "2026-08-01", agent: "leo" })).toBe(false);
|
|
9
|
+
expect(isDateOnlyCostPage({ query: "leo" })).toBe(false);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("keeps residuals, multi-dimension membership, and global totals aligned", () => {
|
|
13
|
+
const database = new DatabaseSync(":memory:");
|
|
14
|
+
try {
|
|
15
|
+
database.exec(
|
|
16
|
+
`CREATE TABLE zaip_aios_activities (payload_type TEXT, activity_source_kind TEXT, occurred_day_sp TEXT, agent_invocation_id TEXT, task_id TEXT, duration_ms REAL, source_event_count INTEGER, input_tokens REAL, cached_input_tokens REAL, output_tokens REAL, reasoning_tokens REAL, total_tokens REAL, cost_usd REAL, cost_brl REAL, model TEXT, provider TEXT, zaip_agent TEXT, zaip_agent_persona TEXT, zaip_agent_job TEXT, occurred_at TEXT); CREATE TABLE cost_invocation_dimension_facts (agent_invocation_id TEXT, occurred_day_sp TEXT, model TEXT, provider TEXT, input_tokens REAL, cached_input_tokens REAL, output_tokens REAL, reasoning_tokens REAL, total_tokens REAL, cost_usd REAL, cost_brl REAL); CREATE TABLE zaip_dim_zaip_agent (zaip_agent TEXT, persona TEXT, job TEXT);`,
|
|
17
|
+
);
|
|
18
|
+
const activity = database.prepare(
|
|
19
|
+
"INSERT INTO zaip_aios_activities(payload_type,activity_source_kind,occurred_day_sp,agent_invocation_id,task_id,duration_ms,source_event_count,total_tokens,cost_usd,cost_brl) VALUES (NULL,'agent_run','2026-08-01',?,?,?,?,?,?,?)",
|
|
20
|
+
);
|
|
21
|
+
activity.run("a", "task-a", 100, 1, 100, 6, 10);
|
|
22
|
+
activity.run("b", "task-b", 200, 2, 100, 10, 10);
|
|
23
|
+
const fact = database.prepare(
|
|
24
|
+
"INSERT INTO cost_invocation_dimension_facts VALUES (?,?,?,?,NULL,NULL,NULL,NULL,?,?,?)",
|
|
25
|
+
);
|
|
26
|
+
fact.run("a", "2026-08-01", "gpt", "openai", 60, 6, 60);
|
|
27
|
+
fact.run("b", "2026-08-01", "gpt", "openai", 50, 5, 50);
|
|
28
|
+
fact.run("b", "2026-08-01", "claude", "anthropic", 50, 5, 50);
|
|
29
|
+
for (const dimension of ["model", "provider"] as const) {
|
|
30
|
+
const page = readDateOnlyCostDimensionPage(
|
|
31
|
+
database,
|
|
32
|
+
dimension,
|
|
33
|
+
"2026-08-01",
|
|
34
|
+
"2026-08-31",
|
|
35
|
+
10,
|
|
36
|
+
0,
|
|
37
|
+
);
|
|
38
|
+
expect(page.totals).toMatchObject({
|
|
39
|
+
invocation_count: 2,
|
|
40
|
+
task_count: 2,
|
|
41
|
+
total_duration: 300,
|
|
42
|
+
source_event_count: 3,
|
|
43
|
+
total_tokens: 200,
|
|
44
|
+
total_cost_brl: 20,
|
|
45
|
+
});
|
|
46
|
+
const known = dimension === "model" ? "gpt" : "openai";
|
|
47
|
+
expect(page.rows.find((row) => row.key === known)).toMatchObject({
|
|
48
|
+
invocation_count: 1,
|
|
49
|
+
task_count: 2,
|
|
50
|
+
total_duration: 100,
|
|
51
|
+
total_tokens: 110,
|
|
52
|
+
});
|
|
53
|
+
expect(page.rows.find((row) => row.key === "unavailable")).toMatchObject({
|
|
54
|
+
invocation_count: 0,
|
|
55
|
+
task_count: 2,
|
|
56
|
+
total_tokens: 40,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
} finally {
|
|
60
|
+
database.close();
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Date-only Cost pages can use immutable, indexed activity facts. Any
|
|
3
|
+
* additional filter deliberately falls back to the canonical SQL path, whose
|
|
4
|
+
* relationship semantics need the richer views.
|
|
5
|
+
*/
|
|
6
|
+
export type DateOnlyCostPageInput = {
|
|
7
|
+
from?: string | null;
|
|
8
|
+
to?: string | null;
|
|
9
|
+
query?: string;
|
|
10
|
+
status?: string;
|
|
11
|
+
risk?: string;
|
|
12
|
+
provider?: string;
|
|
13
|
+
model?: string;
|
|
14
|
+
agent?: string;
|
|
15
|
+
phase?: string;
|
|
16
|
+
origin?: string;
|
|
17
|
+
workType?: string;
|
|
18
|
+
priority?: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function isDateOnlyCostPage(input: DateOnlyCostPageInput): boolean {
|
|
22
|
+
return (
|
|
23
|
+
(input.from !== undefined || input.to !== undefined) &&
|
|
24
|
+
!input.query &&
|
|
25
|
+
!input.status &&
|
|
26
|
+
!input.risk &&
|
|
27
|
+
!input.provider &&
|
|
28
|
+
!input.model &&
|
|
29
|
+
!input.agent &&
|
|
30
|
+
!input.phase &&
|
|
31
|
+
!input.origin &&
|
|
32
|
+
!input.workType &&
|
|
33
|
+
!input.priority
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** SQL is built only from normalized Sao Paulo day keys. */
|
|
38
|
+
export function costDimensionFactDateFilter(input: DateOnlyCostPageInput): string {
|
|
39
|
+
const day = (value: string | null | undefined) =>
|
|
40
|
+
value && /^\d{4}-\d{2}-\d{2}/.test(value) ? value.slice(0, 10) : null;
|
|
41
|
+
const clauses = [
|
|
42
|
+
day(input.from) && `occurred_day_sp >= '${day(input.from)}'`,
|
|
43
|
+
day(input.to) && `occurred_day_sp <= '${day(input.to)}'`,
|
|
44
|
+
].filter((value): value is string => Boolean(value));
|
|
45
|
+
return clauses.length === 0 ? "" : ` AND ${clauses.join(" AND ")}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type CostDimension = "agent" | "model" | "provider";
|
|
49
|
+
type SqliteQuery = {
|
|
50
|
+
prepare(sql: string): {
|
|
51
|
+
get(...values: Array<string | number>): Record<string, unknown> | undefined;
|
|
52
|
+
all(...values: Array<string | number>): Array<Record<string, unknown>>;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** Reads the date picker population from schema-67 physical facts only. */
|
|
57
|
+
export function readDateOnlyCostDimensionPage(
|
|
58
|
+
database: SqliteQuery,
|
|
59
|
+
dimension: CostDimension,
|
|
60
|
+
from: string,
|
|
61
|
+
to: string,
|
|
62
|
+
limit: number,
|
|
63
|
+
offset: number,
|
|
64
|
+
) {
|
|
65
|
+
if (dimension === "agent") {
|
|
66
|
+
const facts = `WITH facts AS (SELECT zaip_agent AS key, zaip_agent_persona AS persona, zaip_agent_job AS job, occurred_at, task_id, duration_ms, source_event_count, total_tokens, cost_usd, cost_brl FROM zaip_aios_activities WHERE payload_type IS NULL AND occurred_day_sp>=? AND occurred_day_sp<=? AND zaip_agent IS NOT NULL AND (activity_source_kind='agent_run' OR (activity_source_kind<>'agent_run' AND (total_tokens IS NOT NULL OR cost_usd IS NOT NULL OR cost_brl IS NOT NULL))))`;
|
|
67
|
+
const total_count = Number(
|
|
68
|
+
database.prepare(`${facts} SELECT COUNT(DISTINCT key) AS count FROM facts`).get(from, to)
|
|
69
|
+
?.count ?? 0,
|
|
70
|
+
);
|
|
71
|
+
const totals =
|
|
72
|
+
database
|
|
73
|
+
.prepare(
|
|
74
|
+
`${facts} SELECT COUNT(*) AS invocation_count, COUNT(DISTINCT task_id) AS task_count, SUM(duration_ms) AS total_duration, SUM(CASE WHEN duration_ms IS NULL THEN 1 ELSE 0 END) AS duration_unknown, SUM(CASE WHEN duration_ms IS NULL THEN 0 ELSE duration_ms END) AS duration_known, SUM(source_event_count) AS source_event_count, SUM(total_tokens) AS total_tokens, SUM(cost_usd) AS total_cost_usd, SUM(cost_brl) AS total_cost_brl FROM facts`,
|
|
75
|
+
)
|
|
76
|
+
.get(from, to) ?? {};
|
|
77
|
+
const rows = database
|
|
78
|
+
.prepare(
|
|
79
|
+
`${facts} SELECT key, key AS label, COALESCE(MAX(persona), (SELECT persona FROM zaip_dim_zaip_agent d WHERE d.zaip_agent=key)) AS persona, COALESCE(MAX(job), (SELECT job FROM zaip_dim_zaip_agent d WHERE d.zaip_agent=key)) AS job, MAX(occurred_at) AS latest_started_at, COUNT(*) AS invocation_count, COUNT(DISTINCT task_id) AS task_count, SUM(duration_ms) AS total_duration, SUM(CASE WHEN duration_ms IS NULL THEN 1 ELSE 0 END) AS duration_unknown, SUM(CASE WHEN duration_ms IS NULL THEN 0 ELSE duration_ms END) AS duration_known, SUM(source_event_count) AS source_event_count, SUM(total_tokens) AS total_tokens, SUM(cost_usd) AS total_cost_usd, SUM(cost_brl) AS total_cost_brl FROM facts GROUP BY key ORDER BY total_cost_brl DESC, key LIMIT ? OFFSET ?`,
|
|
80
|
+
)
|
|
81
|
+
.all(from, to, limit, offset);
|
|
82
|
+
return { total_count, totals, rows };
|
|
83
|
+
}
|
|
84
|
+
const field = dimension === "model" ? "model" : "provider";
|
|
85
|
+
const directFinancial =
|
|
86
|
+
dimension === "model"
|
|
87
|
+
? ` UNION ALL SELECT CASE WHEN a.${field} IS NULL OR trim(a.${field})='' OR lower(a.${field}) IN ('unavailable','unknown','mixed') THEN 'unavailable' ELSE a.${field} END,a.total_tokens,a.cost_usd,a.cost_brl FROM zaip_aios_activities a WHERE a.payload_type IS NULL AND a.activity_source_kind<>'agent_run' AND a.occurred_day_sp>=? AND a.occurred_day_sp<=? AND (a.total_tokens IS NOT NULL OR a.cost_usd IS NOT NULL OR a.cost_brl IS NOT NULL)`
|
|
88
|
+
: "";
|
|
89
|
+
const cte = `WITH base AS (SELECT agent_invocation_id, task_id, duration_ms, source_event_count, input_tokens, cached_input_tokens, output_tokens, reasoning_tokens, total_tokens, cost_usd, cost_brl FROM zaip_aios_activities WHERE payload_type IS NULL AND activity_source_kind='agent_run' AND occurred_day_sp>=? AND occurred_day_sp<=?), segment_totals AS (SELECT f.agent_invocation_id,SUM(f.input_tokens) input_tokens,SUM(f.cached_input_tokens) cached_input_tokens,SUM(f.output_tokens) output_tokens,SUM(f.reasoning_tokens) reasoning_tokens,SUM(f.total_tokens) total_tokens,SUM(f.cost_usd) cost_usd,SUM(f.cost_brl) cost_brl FROM cost_invocation_dimension_facts f JOIN base USING(agent_invocation_id) WHERE f.occurred_day_sp>=? AND f.occurred_day_sp<=? AND (f.total_tokens IS NOT NULL OR f.cost_usd IS NOT NULL OR f.cost_brl IS NOT NULL) GROUP BY f.agent_invocation_id), segments AS (SELECT f.agent_invocation_id,CASE WHEN f.${field} IS NULL OR trim(f.${field})='' OR lower(f.${field}) IN ('unavailable','unknown','mixed') THEN 'unavailable' ELSE f.${field} END key,SUM(f.input_tokens) input_tokens,SUM(f.cached_input_tokens) cached_input_tokens,SUM(f.output_tokens) output_tokens,SUM(f.reasoning_tokens) reasoning_tokens,SUM(f.total_tokens) total_tokens,SUM(f.cost_usd) total_cost_usd,SUM(f.cost_brl) total_cost_brl FROM cost_invocation_dimension_facts f JOIN base USING(agent_invocation_id) WHERE f.occurred_day_sp>=? AND f.occurred_day_sp<=? AND (f.total_tokens IS NOT NULL OR f.cost_usd IS NOT NULL OR f.cost_brl IS NOT NULL) GROUP BY f.agent_invocation_id,key UNION ALL SELECT b.agent_invocation_id,'unavailable',b.input_tokens-COALESCE(t.input_tokens,0),b.cached_input_tokens-COALESCE(t.cached_input_tokens,0),b.output_tokens-COALESCE(t.output_tokens,0),b.reasoning_tokens-COALESCE(t.reasoning_tokens,0),b.total_tokens-COALESCE(t.total_tokens,0),b.cost_usd-COALESCE(t.cost_usd,0),b.cost_brl-COALESCE(t.cost_brl,0) FROM base b LEFT JOIN segment_totals t USING(agent_invocation_id) WHERE (b.total_tokens IS NOT NULL OR b.cost_usd IS NOT NULL OR b.cost_brl IS NOT NULL) AND (b.total_tokens<>COALESCE(t.total_tokens,0) OR b.cost_usd<>COALESCE(t.cost_usd,0) OR b.cost_brl<>COALESCE(t.cost_brl,0))), dimension_segments AS (SELECT agent_invocation_id,key,SUM(input_tokens) input_tokens,SUM(cached_input_tokens) cached_input_tokens,SUM(output_tokens) output_tokens,SUM(reasoning_tokens) reasoning_tokens,SUM(total_tokens) total_tokens,SUM(total_cost_usd) total_cost_usd,SUM(total_cost_brl) total_cost_brl FROM segments GROUP BY agent_invocation_id,key), known_dimensions AS (SELECT agent_invocation_id,COUNT(DISTINCT key) dimension_count FROM dimension_segments WHERE key<>'unavailable' GROUP BY agent_invocation_id), filtered_segments AS (SELECT s.*,b.task_id,b.duration_ms,b.source_event_count,d.dimension_count FROM segments s JOIN base b USING(agent_invocation_id) LEFT JOIN known_dimensions d USING(agent_invocation_id)), financial_components AS (SELECT key,total_tokens,total_cost_usd,total_cost_brl FROM filtered_segments${directFinancial}), financial AS (SELECT key,SUM(total_tokens) total_tokens,SUM(total_cost_usd) total_cost_usd,SUM(total_cost_brl) total_cost_brl FROM financial_components GROUP BY key), population_dimensions AS (SELECT agent_invocation_id,key,MAX(task_id) task_id,MAX(duration_ms) duration_ms,MAX(source_event_count) source_event_count,MAX(dimension_count) dimension_count FROM filtered_segments GROUP BY agent_invocation_id,key), population_ranked AS (SELECT *,ROW_NUMBER() OVER (PARTITION BY agent_invocation_id ORDER BY key) dimension_rank FROM population_dimensions), population AS (SELECT key,COUNT(*) invocation_count,SUM(duration_ms) total_duration,SUM(source_event_count) source_event_count FROM population_ranked WHERE dimension_rank=1 AND dimension_count=1 GROUP BY key), task_population AS (SELECT key,COUNT(*) task_count FROM (SELECT task_id,key FROM population_dimensions WHERE task_id IS NOT NULL GROUP BY task_id,key) GROUP BY key), keys AS (SELECT key FROM financial UNION SELECT key FROM population UNION SELECT key FROM task_population)`;
|
|
90
|
+
const args =
|
|
91
|
+
dimension === "model"
|
|
92
|
+
? [from, to, from, to, from, to, from, to]
|
|
93
|
+
: [from, to, from, to, from, to];
|
|
94
|
+
const total_count = Number(
|
|
95
|
+
database.prepare(`${cte} SELECT COUNT(*) AS count FROM keys`).get(...args)?.count ?? 0,
|
|
96
|
+
);
|
|
97
|
+
const totals =
|
|
98
|
+
database
|
|
99
|
+
.prepare(
|
|
100
|
+
`${cte} SELECT (SELECT COUNT(*) FROM population_ranked WHERE dimension_rank=1) invocation_count, (SELECT COUNT(DISTINCT task_id) FROM population_dimensions) task_count, (SELECT SUM(duration_ms) FROM population_ranked WHERE dimension_rank=1) total_duration, (SELECT SUM(CASE WHEN duration_ms IS NULL THEN 1 ELSE 0 END) FROM population_ranked WHERE dimension_rank=1) duration_unknown, (SELECT SUM(CASE WHEN duration_ms IS NULL THEN 0 ELSE duration_ms END) FROM population_ranked WHERE dimension_rank=1) duration_known, (SELECT SUM(source_event_count) FROM population_ranked WHERE dimension_rank=1) source_event_count, (SELECT SUM(input_tokens) FROM filtered_segments) total_input_tokens, (SELECT SUM(cached_input_tokens) FROM filtered_segments) total_cached_input_tokens, (SELECT SUM(output_tokens) FROM filtered_segments) total_output_tokens, (SELECT SUM(reasoning_tokens) FROM filtered_segments) total_reasoning_tokens, (SELECT SUM(total_tokens) FROM filtered_segments) total_tokens, (SELECT SUM(total_cost_usd) FROM filtered_segments) total_cost_usd, (SELECT SUM(total_cost_brl) FROM filtered_segments) total_cost_brl`,
|
|
101
|
+
)
|
|
102
|
+
.get(...args) ?? {};
|
|
103
|
+
const rows = database
|
|
104
|
+
.prepare(
|
|
105
|
+
`${cte} SELECT keys.key,keys.key AS label,COALESCE(population.invocation_count,0) invocation_count,COALESCE(task_population.task_count,0) task_count,population.total_duration,population.source_event_count,financial.total_tokens,financial.total_cost_usd,financial.total_cost_brl FROM keys LEFT JOIN population USING(key) LEFT JOIN task_population USING(key) LEFT JOIN financial USING(key) ORDER BY financial.total_cost_brl DESC,keys.key LIMIT ? OFFSET ?`,
|
|
106
|
+
)
|
|
107
|
+
.all(...args, limit, offset);
|
|
108
|
+
return { total_count, totals, rows };
|
|
109
|
+
}
|