@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
|
@@ -11,26 +11,78 @@ import {
|
|
|
11
11
|
type PersistedTokenUsage,
|
|
12
12
|
type PricingCatalog,
|
|
13
13
|
pricingCatalogSchema,
|
|
14
|
+
resolveHistoricalDeveloperAttribution,
|
|
14
15
|
} from "@zaip-ai/costs";
|
|
16
|
+
import type { CostSummarySource } from "@zaip-ai/dashboard-cost-monitor/server";
|
|
17
|
+
import { allocateSharedCreationValue } from "@zaip-ai/lifecycle-manifests";
|
|
15
18
|
import {
|
|
16
19
|
foldRunV2Records,
|
|
17
20
|
parseRunV2JsonLines,
|
|
18
21
|
type RunV2Fold,
|
|
19
22
|
type UsageRecordedV2,
|
|
20
23
|
} from "../../../../../packages/decisions/src/index.js";
|
|
24
|
+
import { artifactStructuralRowsSql, developerArtifactCountsCte } from "./sqlite-artifact-counts.js";
|
|
25
|
+
import {
|
|
26
|
+
costDimensionFactDateFilter,
|
|
27
|
+
isDateOnlyCostPage,
|
|
28
|
+
readDateOnlyCostDimensionPage,
|
|
29
|
+
} from "./sqlite-cost-agent-page.js";
|
|
30
|
+
import { buildCostDimensionAggregates } from "./sqlite-cost-aggregates.js";
|
|
31
|
+
import { type CostGroupKind, readCostGroupPage } from "./sqlite-cost-group-page.js";
|
|
32
|
+
import { materializeCostGroupDailyProjection } from "./sqlite-cost-group-projection.js";
|
|
33
|
+
import { filterCostInvocationProjection } from "./sqlite-cost-invocation-page.js";
|
|
34
|
+
import {
|
|
35
|
+
readFirstFoldAllTime,
|
|
36
|
+
readFirstFoldBoardAllTime,
|
|
37
|
+
readFirstFoldMonthlyQuick,
|
|
38
|
+
readFirstFoldTopTasksAllTime,
|
|
39
|
+
readFirstFoldViewport,
|
|
40
|
+
} from "./sqlite-first-fold-cache.js";
|
|
41
|
+
import {
|
|
42
|
+
hasRequiredFirstFoldPayloads,
|
|
43
|
+
persistFirstFoldPayloads,
|
|
44
|
+
} from "./sqlite-first-fold-persistence.js";
|
|
45
|
+
import { measureFirstFold } from "./sqlite-first-fold-timing.js";
|
|
46
|
+
import { materializeOperationsWorkProjection } from "./sqlite-operations-projection.js";
|
|
47
|
+
import {
|
|
48
|
+
attachOperationsWorkListDevelopers,
|
|
49
|
+
operationsWorkListPayloadPage,
|
|
50
|
+
operationsWorkListSqlPage,
|
|
51
|
+
operationsWorkListSqlRows,
|
|
52
|
+
operationsWorkRowsInRange,
|
|
53
|
+
} from "./sqlite-operations-work-list.js";
|
|
54
|
+
import {
|
|
55
|
+
installLegacyViewAliases,
|
|
56
|
+
materializeSnapshot,
|
|
57
|
+
mostRecentCompatibleSnapshot,
|
|
58
|
+
pruneSnapshotCache,
|
|
59
|
+
readSnapshotFileCount,
|
|
60
|
+
} from "./sqlite-snapshot-cache.js";
|
|
61
|
+
|
|
62
|
+
export {
|
|
63
|
+
type SqliteReadModelSourceCandidate,
|
|
64
|
+
sourceFingerprint,
|
|
65
|
+
} from "./sqlite-read-model-source-fingerprint.js";
|
|
66
|
+
|
|
67
|
+
import { sourceFingerprint } from "./sqlite-read-model-source-fingerprint.js";
|
|
21
68
|
|
|
22
69
|
const dbRelativePath = ".local/dashboard-sqlite/read-model.sqlite";
|
|
23
|
-
const textExtensions = new Set([".json", ".jsonl", ".md", ".yaml", ".yml", ".txt"]);
|
|
24
70
|
// Schema 58 aligns Operations invocation/day facts with the same factual
|
|
25
71
|
// source day used by tokens, Cost Monitor and attributed Tempo. Seed reuse is
|
|
26
72
|
// safe only when both source and this schema version match.
|
|
27
|
-
|
|
73
|
+
// Allocation rows are materialized after attributed-time reconciliation; a
|
|
74
|
+
// prior seed contains the pre-reconciliation payload and is incompatible.
|
|
75
|
+
const readModelSchemaVersion = "70";
|
|
28
76
|
const requiredPersistedTables = [
|
|
29
77
|
"zaip_aios_activities",
|
|
30
78
|
"zaip_dim_llm_model",
|
|
31
79
|
"zaip_dim_provider",
|
|
32
80
|
"zaip_dim_zaip_agent",
|
|
33
81
|
"cost_invocation_dimension_facts",
|
|
82
|
+
"cost_group_daily_projection",
|
|
83
|
+
"cost_group_entity_projection",
|
|
84
|
+
"developer_cost_projection",
|
|
85
|
+
"operations_work_projection",
|
|
34
86
|
] as const;
|
|
35
87
|
const requiredIndexes = [
|
|
36
88
|
"zaip_activities_agent_run",
|
|
@@ -48,6 +100,9 @@ const requiredIndexes = [
|
|
|
48
100
|
"cost_dimension_facts_day_model",
|
|
49
101
|
"cost_dimension_facts_day_provider",
|
|
50
102
|
"cost_dimension_facts_invocation",
|
|
103
|
+
"cost_group_daily_projection_lookup",
|
|
104
|
+
"cost_group_entity_projection_lookup",
|
|
105
|
+
"developer_cost_projection_lookup",
|
|
51
106
|
] as const;
|
|
52
107
|
const requiredViews = [
|
|
53
108
|
"zaip_vw_read_model_metadata",
|
|
@@ -59,27 +114,6 @@ const requiredViews = [
|
|
|
59
114
|
] as const;
|
|
60
115
|
const MAX_RECONCILED_RUN_INTERVAL_MS = 2 * 3_600_000;
|
|
61
116
|
|
|
62
|
-
type CostDimensionAggregateRow = {
|
|
63
|
-
key: string;
|
|
64
|
-
label: string;
|
|
65
|
-
invocation_count: number;
|
|
66
|
-
task_count: number;
|
|
67
|
-
total_duration: number | null;
|
|
68
|
-
source_event_count: number | null;
|
|
69
|
-
total_tokens: number | null;
|
|
70
|
-
total_cost_usd: number | null;
|
|
71
|
-
total_cost_brl: number | null;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
function observedDimensionValue(value: unknown): string | null {
|
|
75
|
-
if (typeof value !== "string") return null;
|
|
76
|
-
const normalized = value.trim();
|
|
77
|
-
return normalized.length > 0 &&
|
|
78
|
-
!["unavailable", "unknown", "mixed"].includes(normalized.toLowerCase())
|
|
79
|
-
? normalized
|
|
80
|
-
: null;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
117
|
function saoPauloCurrentMonth(): { from: string; to: string } {
|
|
84
118
|
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
85
119
|
timeZone: "America/Sao_Paulo",
|
|
@@ -92,196 +126,7 @@ function saoPauloCurrentMonth(): { from: string; to: string } {
|
|
|
92
126
|
return { from: `${value.year}-${value.month}-01`, to: day };
|
|
93
127
|
}
|
|
94
128
|
|
|
95
|
-
/** Small, seed-time-only aggregates for the unfiltered Models/Providers tables. */
|
|
96
|
-
function buildCostDimensionAggregates(
|
|
97
|
-
database: DatabaseSync,
|
|
98
|
-
): Record<"model" | "provider", CostDimensionAggregateRow[]> {
|
|
99
|
-
const dimensions = ["model", "provider"] as const;
|
|
100
|
-
const allowedProviders = new Set(
|
|
101
|
-
(
|
|
102
|
-
database.prepare("SELECT provider FROM zaip_dim_provider").all() as Array<{
|
|
103
|
-
provider: string;
|
|
104
|
-
}>
|
|
105
|
-
).map((row) => row.provider),
|
|
106
|
-
);
|
|
107
|
-
const activities = database
|
|
108
|
-
.prepare(`SELECT activity_row_id, agent_invocation_id, task_id, duration_ms,
|
|
109
|
-
source_event_count, provider, model, total_tokens, cost_usd, cost_brl
|
|
110
|
-
FROM zaip_aios_activities WHERE payload_type IS NULL`)
|
|
111
|
-
.all() as Array<Record<string, unknown>>;
|
|
112
|
-
const segments = database
|
|
113
|
-
.prepare(`SELECT agent_invocation_id, provider, model, total_tokens, cost_usd, cost_brl
|
|
114
|
-
FROM invocation_usage_segments`)
|
|
115
|
-
.all() as Array<Record<string, unknown>>;
|
|
116
|
-
const activityByInvocation = new Map(
|
|
117
|
-
activities
|
|
118
|
-
.filter((row) => typeof row.agent_invocation_id === "string")
|
|
119
|
-
.map((row) => [String(row.agent_invocation_id), row] as const),
|
|
120
|
-
);
|
|
121
|
-
const rowsByDimension = new Map<"model" | "provider", Map<string, CostDimensionAggregateRow>>(
|
|
122
|
-
dimensions.map((dimension) => [dimension, new Map()]),
|
|
123
|
-
);
|
|
124
|
-
const ensure = (dimension: "model" | "provider", key: string) => {
|
|
125
|
-
const rows = rowsByDimension.get(dimension)!;
|
|
126
|
-
const current = rows.get(key);
|
|
127
|
-
if (current !== undefined) return current;
|
|
128
|
-
const created: CostDimensionAggregateRow = {
|
|
129
|
-
key,
|
|
130
|
-
label: key,
|
|
131
|
-
invocation_count: 0,
|
|
132
|
-
task_count: 0,
|
|
133
|
-
total_duration: null,
|
|
134
|
-
source_event_count: null,
|
|
135
|
-
total_tokens: null,
|
|
136
|
-
total_cost_usd: null,
|
|
137
|
-
total_cost_brl: null,
|
|
138
|
-
};
|
|
139
|
-
rows.set(key, created);
|
|
140
|
-
return created;
|
|
141
|
-
};
|
|
142
|
-
const add = (
|
|
143
|
-
target: CostDimensionAggregateRow,
|
|
144
|
-
field: "total_tokens" | "total_cost_usd" | "total_cost_brl",
|
|
145
|
-
value: unknown,
|
|
146
|
-
) => {
|
|
147
|
-
if (typeof value === "number") target[field] = (target[field] ?? 0) + value;
|
|
148
|
-
};
|
|
149
|
-
const segmentsByInvocation = new Map<string, Array<Record<string, unknown>>>();
|
|
150
|
-
for (const segment of segments) {
|
|
151
|
-
const id = String(segment.agent_invocation_id ?? "");
|
|
152
|
-
if (id.length === 0) continue;
|
|
153
|
-
const invocationSegments = segmentsByInvocation.get(id);
|
|
154
|
-
if (invocationSegments === undefined) segmentsByInvocation.set(id, [segment]);
|
|
155
|
-
else invocationSegments.push(segment);
|
|
156
|
-
}
|
|
157
|
-
for (const [id, invocationSegments] of segmentsByInvocation) {
|
|
158
|
-
const activity = activityByInvocation.get(id);
|
|
159
|
-
if (activity === undefined) continue;
|
|
160
|
-
for (const dimension of dimensions) {
|
|
161
|
-
const financialSegments = invocationSegments.filter(
|
|
162
|
-
(segment) =>
|
|
163
|
-
observedDimensionValue(segment[dimension]) !== null &&
|
|
164
|
-
observedDimensionValue(segment.provider) !== null &&
|
|
165
|
-
allowedProviders.has(observedDimensionValue(segment.provider)!) &&
|
|
166
|
-
(typeof segment.total_tokens === "number" ||
|
|
167
|
-
typeof segment.cost_usd === "number" ||
|
|
168
|
-
typeof segment.cost_brl === "number"),
|
|
169
|
-
);
|
|
170
|
-
const keys = [
|
|
171
|
-
...new Set(financialSegments.map((row) => observedDimensionValue(row[dimension])!)),
|
|
172
|
-
];
|
|
173
|
-
if (keys.length === 0) {
|
|
174
|
-
// Keep auditable, dimensionless invocation facts visible so Models
|
|
175
|
-
// and Providers reconcile to the Cost KPI instead of silently
|
|
176
|
-
// dropping them.
|
|
177
|
-
const target = ensure(dimension, "unavailable");
|
|
178
|
-
target.invocation_count += 1;
|
|
179
|
-
if (typeof activity.duration_ms === "number")
|
|
180
|
-
target.total_duration = (target.total_duration ?? 0) + activity.duration_ms;
|
|
181
|
-
if (typeof activity.source_event_count === "number")
|
|
182
|
-
target.source_event_count =
|
|
183
|
-
(target.source_event_count ?? 0) + activity.source_event_count;
|
|
184
|
-
add(target, "total_tokens", activity.total_tokens);
|
|
185
|
-
add(target, "total_cost_usd", activity.cost_usd);
|
|
186
|
-
add(target, "total_cost_brl", activity.cost_brl);
|
|
187
|
-
}
|
|
188
|
-
for (const segment of financialSegments) {
|
|
189
|
-
const target = ensure(dimension, observedDimensionValue(segment[dimension])!);
|
|
190
|
-
add(target, "total_tokens", segment.total_tokens);
|
|
191
|
-
add(target, "total_cost_usd", segment.cost_usd);
|
|
192
|
-
add(target, "total_cost_brl", segment.cost_brl);
|
|
193
|
-
}
|
|
194
|
-
// An invocation's elapsed time is attributable only when its observed
|
|
195
|
-
// financial segments agree on one dimension. Do not fabricate a
|
|
196
|
-
// mixed bucket or split wall-clock time across models/providers.
|
|
197
|
-
if (keys.length === 1) {
|
|
198
|
-
const target = ensure(dimension, keys[0]!);
|
|
199
|
-
target.invocation_count += 1;
|
|
200
|
-
if (typeof activity.duration_ms === "number")
|
|
201
|
-
target.total_duration = (target.total_duration ?? 0) + activity.duration_ms;
|
|
202
|
-
if (typeof activity.source_event_count === "number")
|
|
203
|
-
target.source_event_count =
|
|
204
|
-
(target.source_event_count ?? 0) + activity.source_event_count;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
const taskKeys = new Map<"model" | "provider", Map<string, Set<string>>>(
|
|
209
|
-
dimensions.map((dimension) => [dimension, new Map()]),
|
|
210
|
-
);
|
|
211
|
-
for (const [id, invocationSegments] of segmentsByInvocation) {
|
|
212
|
-
const taskId = activityByInvocation.get(id)?.task_id;
|
|
213
|
-
if (typeof taskId !== "string") continue;
|
|
214
|
-
for (const dimension of dimensions) {
|
|
215
|
-
const byTask = taskKeys.get(dimension)!;
|
|
216
|
-
const keys = byTask.get(taskId) ?? new Set<string>();
|
|
217
|
-
for (const segment of invocationSegments) {
|
|
218
|
-
const key = observedDimensionValue(segment[dimension]);
|
|
219
|
-
const provider = observedDimensionValue(segment.provider);
|
|
220
|
-
const hasFinancialFact =
|
|
221
|
-
typeof segment.total_tokens === "number" ||
|
|
222
|
-
typeof segment.cost_usd === "number" ||
|
|
223
|
-
typeof segment.cost_brl === "number";
|
|
224
|
-
if (key !== null && provider !== null && allowedProviders.has(provider) && hasFinancialFact)
|
|
225
|
-
keys.add(key);
|
|
226
|
-
}
|
|
227
|
-
byTask.set(taskId, keys);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
for (const dimension of dimensions)
|
|
231
|
-
for (const keys of taskKeys.get(dimension)!.values())
|
|
232
|
-
for (const key of keys) ensure(dimension, key).task_count += 1;
|
|
233
|
-
for (const activity of activities) {
|
|
234
|
-
if (typeof activity.agent_invocation_id === "string") continue;
|
|
235
|
-
const hasFinancialFact =
|
|
236
|
-
typeof activity.total_tokens === "number" ||
|
|
237
|
-
typeof activity.cost_usd === "number" ||
|
|
238
|
-
typeof activity.cost_brl === "number";
|
|
239
|
-
if (!hasFinancialFact) continue;
|
|
240
|
-
const provider = observedDimensionValue(activity.provider);
|
|
241
|
-
if (provider === null || !allowedProviders.has(provider)) continue;
|
|
242
|
-
for (const dimension of dimensions) {
|
|
243
|
-
const key = observedDimensionValue(activity[dimension]) ?? "unavailable";
|
|
244
|
-
const target = ensure(dimension, key);
|
|
245
|
-
target.invocation_count += 1;
|
|
246
|
-
if (typeof activity.task_id === "string") target.task_count += 1;
|
|
247
|
-
if (typeof activity.duration_ms === "number")
|
|
248
|
-
target.total_duration = (target.total_duration ?? 0) + activity.duration_ms;
|
|
249
|
-
if (typeof activity.source_event_count === "number")
|
|
250
|
-
target.source_event_count = (target.source_event_count ?? 0) + activity.source_event_count;
|
|
251
|
-
add(target, "total_tokens", activity.total_tokens);
|
|
252
|
-
add(target, "total_cost_usd", activity.cost_usd);
|
|
253
|
-
add(target, "total_cost_brl", activity.cost_brl);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
return Object.fromEntries(
|
|
257
|
-
dimensions.map((dimension) => [
|
|
258
|
-
dimension,
|
|
259
|
-
[...rowsByDimension.get(dimension)!.values()].sort(
|
|
260
|
-
(left, right) =>
|
|
261
|
-
(right.total_cost_brl ?? -1) - (left.total_cost_brl ?? -1) ||
|
|
262
|
-
left.key.localeCompare(right.key),
|
|
263
|
-
),
|
|
264
|
-
]),
|
|
265
|
-
) as Record<"model" | "provider", CostDimensionAggregateRow[]>;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
129
|
/** Only canonical inputs consumed by the fact or its direct dimensions. */
|
|
269
|
-
function isDashboardCanonicalSource(source: string): boolean {
|
|
270
|
-
if (
|
|
271
|
-
source === ".agents/config/project.yaml" ||
|
|
272
|
-
source === ".agents/config/framework.lock.yaml" ||
|
|
273
|
-
source === ".agents/config/agents/catalog.yaml" ||
|
|
274
|
-
source === ".agents/config/clients.yaml"
|
|
275
|
-
)
|
|
276
|
-
return true;
|
|
277
|
-
if (source.startsWith(".agents/config/pricing/")) return true;
|
|
278
|
-
if (source.startsWith(".agents/records/runs/") && source.endsWith(".jsonl")) return true;
|
|
279
|
-
if (/^\.agents\/records\/(tasks|specs|prds)\/.*\.md$/.test(source)) return true;
|
|
280
|
-
if (/^\.agents\/records\/executions\/.*\/execution\.md$/.test(source)) return true;
|
|
281
|
-
return /^\.agents\/records\/changes\/.*\/(change\.md|decisions\/.*\.md|artifacts\/(evidence|validations)\/.*\.md)$/.test(
|
|
282
|
-
source,
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
130
|
|
|
286
131
|
type AgentProfile = {
|
|
287
132
|
catalogId: string;
|
|
@@ -296,15 +141,21 @@ type CanonicalEntity = {
|
|
|
296
141
|
status: string | null;
|
|
297
142
|
stage: string | null;
|
|
298
143
|
risk: string | null;
|
|
144
|
+
specId: string | null;
|
|
145
|
+
changeId: string | null;
|
|
299
146
|
occurredAt: string | null;
|
|
147
|
+
creationRunId: string | null;
|
|
148
|
+
completedRunId: string | null;
|
|
300
149
|
};
|
|
301
150
|
type CanonicalArtifact = {
|
|
302
151
|
kind: "evidence" | "validation";
|
|
303
152
|
id: string;
|
|
304
153
|
occurredAt: string;
|
|
305
154
|
occurredDaySp: string;
|
|
155
|
+
taskId: string | null;
|
|
156
|
+
changeId: string | null;
|
|
157
|
+
runId: string | null;
|
|
306
158
|
};
|
|
307
|
-
|
|
308
159
|
type CanonicalExecution = {
|
|
309
160
|
id: string;
|
|
310
161
|
status: string | null;
|
|
@@ -315,7 +166,6 @@ type CanonicalExecution = {
|
|
|
315
166
|
finishedAt: string | null;
|
|
316
167
|
sourcePath: string;
|
|
317
168
|
};
|
|
318
|
-
|
|
319
169
|
type CanonicalChange = {
|
|
320
170
|
id: string;
|
|
321
171
|
title: string;
|
|
@@ -332,7 +182,6 @@ type CanonicalChange = {
|
|
|
332
182
|
createdAt: string;
|
|
333
183
|
sourcePath: string;
|
|
334
184
|
};
|
|
335
|
-
|
|
336
185
|
type CanonicalDecision = {
|
|
337
186
|
id: string;
|
|
338
187
|
title: string;
|
|
@@ -342,7 +191,6 @@ type CanonicalDecision = {
|
|
|
342
191
|
createdAt: string;
|
|
343
192
|
sourcePath: string;
|
|
344
193
|
};
|
|
345
|
-
|
|
346
194
|
function frontMatterRecord(content: string): Record<string, unknown> | null {
|
|
347
195
|
const source = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(content)?.[1];
|
|
348
196
|
if (source === undefined) return null;
|
|
@@ -496,7 +344,11 @@ function readCanonicalEntities(
|
|
|
496
344
|
status: scalar("status"),
|
|
497
345
|
stage: scalar("stage"),
|
|
498
346
|
risk: scalar("risk"),
|
|
347
|
+
specId: scalar("spec_id"),
|
|
348
|
+
changeId: scalar("change_id"),
|
|
499
349
|
occurredAt: scalar("created_at") ?? scalar("started_at") ?? scalar("recorded_at"),
|
|
350
|
+
creationRunId: scalar("creation_run_id"),
|
|
351
|
+
completedRunId: scalar("completed_run_id"),
|
|
500
352
|
});
|
|
501
353
|
}
|
|
502
354
|
return entities;
|
|
@@ -1030,34 +882,21 @@ function costMonitorFilterDay(value: string | null | undefined): string | null {
|
|
|
1030
882
|
return /^\d{4}-\d{2}-\d{2}$/.test(value) ? value : saoPauloDay(value);
|
|
1031
883
|
}
|
|
1032
884
|
|
|
1033
|
-
function
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
.formatToParts(new Date(utcGuess))
|
|
1042
|
-
.find((part) => part.type === "timeZoneName")?.value ?? "GMT";
|
|
1043
|
-
const match = /^GMT([+-])(\d{2}):(\d{2})$/.exec(offset);
|
|
1044
|
-
if (match === null) return utcGuess;
|
|
1045
|
-
const offsetMs =
|
|
1046
|
-
(Number(match[2]) * 60 + Number(match[3])) * 60_000 * (match[1] === "+" ? 1 : -1);
|
|
1047
|
-
return utcGuess - offsetMs;
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
function nextSaoPauloDay(day: string): string {
|
|
1051
|
-
const [year, month, date] = day.split("-").map(Number);
|
|
1052
|
-
const next = new Date(Date.UTC(year!, month! - 1, date! + 1));
|
|
1053
|
-
return next.toISOString().slice(0, 10);
|
|
885
|
+
function sumKnownDeveloperMetric(
|
|
886
|
+
rows: readonly Record<string, unknown>[],
|
|
887
|
+
field: string,
|
|
888
|
+
): number | null {
|
|
889
|
+
const values = rows
|
|
890
|
+
.map((row) => row[field])
|
|
891
|
+
.filter((value): value is number => typeof value === "number" && Number.isFinite(value));
|
|
892
|
+
return values.length === 0 ? null : values.reduce((sum, value) => sum + value, 0);
|
|
1054
893
|
}
|
|
1055
894
|
|
|
1056
895
|
/** Extracts the small artifact projection while the canonical file is already being read. */
|
|
1057
896
|
function readCanonicalArtifact(source: string, content: string): CanonicalArtifact | null {
|
|
1058
897
|
const kind = source.includes("/artifacts/evidence/")
|
|
1059
898
|
? "evidence"
|
|
1060
|
-
: source.includes("/artifacts/validations/")
|
|
899
|
+
: source.includes("/artifacts/validations/") || source.includes("/artifacts/tests/")
|
|
1061
900
|
? "validation"
|
|
1062
901
|
: null;
|
|
1063
902
|
if (kind === null) return null;
|
|
@@ -1076,6 +915,9 @@ function readCanonicalArtifact(source: string, content: string): CanonicalArtifa
|
|
|
1076
915
|
id: scalar("id") ?? source.slice(source.lastIndexOf("/") + 1, -3),
|
|
1077
916
|
occurredAt,
|
|
1078
917
|
occurredDaySp: saoPauloDay(occurredAt),
|
|
918
|
+
taskId: scalar("task_id"),
|
|
919
|
+
changeId: scalar("change_id"),
|
|
920
|
+
runId: scalar("run_id"),
|
|
1079
921
|
};
|
|
1080
922
|
}
|
|
1081
923
|
|
|
@@ -1083,6 +925,7 @@ export function sqliteReadModelPath(workspaceRoot: string): string {
|
|
|
1083
925
|
return resolve(process.env.ZAIP_SQLITE_DB_PATH ?? resolve(workspaceRoot, dbRelativePath));
|
|
1084
926
|
}
|
|
1085
927
|
|
|
928
|
+
/** Atomically installs a worker-built immutable generation after its schema is validated. */
|
|
1086
929
|
/**
|
|
1087
930
|
* Refuses stale or incomplete SQLite generations before they are served. This
|
|
1088
931
|
* is intentionally independent of source fingerprints: a matching cache key
|
|
@@ -1116,7 +959,7 @@ export function validateSqliteReadModelSchema(path: string): {
|
|
|
1116
959
|
tables.some((table, index) => table !== expectedTables[index])
|
|
1117
960
|
)
|
|
1118
961
|
throw new Error(
|
|
1119
|
-
`SQLite schema ${readModelSchemaVersion} requires
|
|
962
|
+
`SQLite schema ${readModelSchemaVersion} requires the complete persisted table contract; found ${tables.join(", ") || "none"}`,
|
|
1120
963
|
);
|
|
1121
964
|
const names = new Set(objects.map((object) => object.name));
|
|
1122
965
|
const missingViews = requiredViews.filter((view) => !names.has(view));
|
|
@@ -1172,7 +1015,12 @@ export async function rebuildSqliteReadModel(
|
|
|
1172
1015
|
};
|
|
1173
1016
|
const { fingerprint } = sourceSnapshot;
|
|
1174
1017
|
const fingerprintMs = Math.round(performance.now() - fingerprintStartedAt);
|
|
1175
|
-
const existingFiles = readSnapshotFileCount(
|
|
1018
|
+
const existingFiles = readSnapshotFileCount(
|
|
1019
|
+
path,
|
|
1020
|
+
fingerprint,
|
|
1021
|
+
readModelSchemaVersion,
|
|
1022
|
+
hasRequiredFirstFoldPayloads,
|
|
1023
|
+
);
|
|
1176
1024
|
if (existingFiles !== null) {
|
|
1177
1025
|
validateSqliteReadModelSchema(path);
|
|
1178
1026
|
return {
|
|
@@ -1190,9 +1038,19 @@ export async function rebuildSqliteReadModel(
|
|
|
1190
1038
|
// generation path. A normal fresh-port handoff can therefore be warm.
|
|
1191
1039
|
const cachePath = join(
|
|
1192
1040
|
process.env.ZAIP_SQLITE_CACHE_DIR ?? join(dirname(path), "cache"),
|
|
1193
|
-
|
|
1041
|
+
// The cache payload is part of the immutable generation contract. Keep
|
|
1042
|
+
// a separate namespace when that payload changes, so a schema-compatible
|
|
1043
|
+
// older seed cannot bypass the new all-time precomputation.
|
|
1044
|
+
// Keep the immutable name short enough that SQLite can create its
|
|
1045
|
+
// temporary journal beside deep Windows fixture/workspace paths.
|
|
1046
|
+
`v${readModelSchemaVersion}-ff9-${createHash("sha256").update(fingerprint).digest("hex")}.sqlite`,
|
|
1047
|
+
);
|
|
1048
|
+
const cachedFiles = readSnapshotFileCount(
|
|
1049
|
+
cachePath,
|
|
1050
|
+
fingerprint,
|
|
1051
|
+
readModelSchemaVersion,
|
|
1052
|
+
hasRequiredFirstFoldPayloads,
|
|
1194
1053
|
);
|
|
1195
|
-
const cachedFiles = readSnapshotFileCount(cachePath, fingerprint);
|
|
1196
1054
|
if (cachedFiles !== null) {
|
|
1197
1055
|
const materializeStartedAt = performance.now();
|
|
1198
1056
|
await materializeSnapshot(cachePath, path);
|
|
@@ -1211,7 +1069,11 @@ export async function rebuildSqliteReadModel(
|
|
|
1211
1069
|
// The launcher labels this state as updating and starts an isolated refresh;
|
|
1212
1070
|
// this process still serves only a schema-validated immutable seed.
|
|
1213
1071
|
if (!options.forceCanonical && process.env.ZAIP_SQLITE_PREFER_LAST_VALID_SEED === "1") {
|
|
1214
|
-
const fallback = await mostRecentCompatibleSnapshot(
|
|
1072
|
+
const fallback = await mostRecentCompatibleSnapshot(
|
|
1073
|
+
dirname(cachePath),
|
|
1074
|
+
readModelSchemaVersion,
|
|
1075
|
+
hasRequiredFirstFoldPayloads,
|
|
1076
|
+
);
|
|
1215
1077
|
if (fallback !== null) {
|
|
1216
1078
|
const materializeStartedAt = performance.now();
|
|
1217
1079
|
await materializeSnapshot(fallback.path, path);
|
|
@@ -1231,17 +1093,17 @@ export async function rebuildSqliteReadModel(
|
|
|
1231
1093
|
`Encontradas ${sourceSnapshot.candidates.length} fontes canônicas.`,
|
|
1232
1094
|
);
|
|
1233
1095
|
await mkdir(dirname(cachePath), { recursive: true });
|
|
1234
|
-
const temporaryPath =
|
|
1096
|
+
const temporaryPath = join(dirname(cachePath), `.snapshot-${randomUUID()}.tmp`);
|
|
1235
1097
|
const database = new DatabaseSync(temporaryPath);
|
|
1236
1098
|
let completedFiles = 0;
|
|
1237
1099
|
try {
|
|
1238
1100
|
database.exec("PRAGMA journal_mode=DELETE; BEGIN IMMEDIATE;");
|
|
1239
1101
|
try {
|
|
1240
1102
|
database.exec(`
|
|
1241
|
-
CREATE TEMP TABLE files (path TEXT PRIMARY KEY, content TEXT NOT NULL, mtime_ms INTEGER NOT NULL, size_bytes INTEGER NOT NULL, content_sha256 TEXT NOT NULL, artifact_kind TEXT, artifact_id TEXT, artifact_occurred_at TEXT, artifact_occurred_day_sp TEXT);
|
|
1103
|
+
CREATE TEMP TABLE files (path TEXT PRIMARY KEY, content TEXT NOT NULL, mtime_ms INTEGER NOT NULL, size_bytes INTEGER NOT NULL, content_sha256 TEXT NOT NULL, artifact_kind TEXT, artifact_id TEXT, artifact_occurred_at TEXT, artifact_occurred_day_sp TEXT, artifact_task_id TEXT, artifact_change_id TEXT, artifact_run_id TEXT);
|
|
1242
1104
|
CREATE TEMP TABLE read_model_source_state (source_path TEXT PRIMARY KEY, source_hash TEXT NOT NULL, mtime_ms INTEGER NOT NULL, size_bytes INTEGER NOT NULL, source_kind TEXT NOT NULL, last_processed_at TEXT NOT NULL);
|
|
1243
1105
|
CREATE TEMP TABLE read_model_metadata (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
|
1244
|
-
CREATE TEMP TABLE canonical_work_items (entity_kind TEXT NOT NULL, id TEXT NOT NULL, title TEXT, status TEXT, stage TEXT, risk TEXT, occurred_at TEXT NOT NULL, occurred_day_sp TEXT NOT NULL, PRIMARY KEY(entity_kind, id));
|
|
1106
|
+
CREATE TEMP TABLE canonical_work_items (entity_kind TEXT NOT NULL, id TEXT NOT NULL, title TEXT, status TEXT, stage TEXT, risk TEXT, spec_id TEXT, change_id TEXT, completed_run_id TEXT, occurred_at TEXT NOT NULL, occurred_day_sp TEXT NOT NULL, PRIMARY KEY(entity_kind, id));
|
|
1245
1107
|
CREATE TEMP TABLE canonical_executions (id TEXT PRIMARY KEY, status TEXT, kind TEXT, origin_type TEXT, origin_id TEXT, created_at TEXT NOT NULL, occurred_day_sp TEXT NOT NULL, finished_at TEXT, source_path TEXT NOT NULL);
|
|
1246
1108
|
CREATE TEMP TABLE canonical_changes (id TEXT PRIMARY KEY, title TEXT NOT NULL, status TEXT NOT NULL, risk TEXT NOT NULL, source_type TEXT NOT NULL, source_id TEXT NOT NULL, source_spec_id TEXT, source_prd_id TEXT, requirement_ids_json TEXT NOT NULL, acceptance_ids_json TEXT NOT NULL, task_ids_json TEXT NOT NULL, result TEXT, created_at TEXT NOT NULL, occurred_day_sp TEXT NOT NULL, source_path TEXT NOT NULL);
|
|
1247
1109
|
CREATE TEMP TABLE canonical_decisions (id TEXT PRIMARY KEY, title TEXT NOT NULL, origin TEXT NOT NULL, scope_type TEXT NOT NULL, scope_id TEXT NOT NULL, created_at TEXT NOT NULL, occurred_day_sp TEXT NOT NULL, source_path TEXT NOT NULL);
|
|
@@ -1270,6 +1132,7 @@ export async function rebuildSqliteReadModel(
|
|
|
1270
1132
|
occurred_at TEXT, occurred_day_sp TEXT, execution_kind TEXT, entrypoint TEXT, work_type TEXT, phase TEXT, purpose TEXT, status TEXT, activity_kind TEXT,
|
|
1271
1133
|
zaip_agent TEXT, zaip_agent_persona TEXT, zaip_agent_job TEXT, zaip_agent_role TEXT, agent_catalog_id TEXT, agent_catalog_hash TEXT,
|
|
1272
1134
|
client TEXT, provider TEXT, model TEXT, service_tier TEXT, reasoning_effort TEXT,
|
|
1135
|
+
developer_account_id TEXT, developer_login TEXT, developer_attribution_source TEXT,
|
|
1273
1136
|
source_duration_ms REAL, source_started_at TEXT, source_finished_at TEXT, duration_ms REAL, duration_provenance TEXT, input_tokens REAL, cached_input_tokens REAL, output_tokens REAL, reasoning_tokens REAL, total_tokens REAL, token_source TEXT, usage_event_count INTEGER, llm_call_count INTEGER,
|
|
1274
1137
|
cost_usd REAL, cost_brl REAL, cost_method TEXT, pricing_model TEXT, pricing_source TEXT, fx_rate REAL, source_event_count INTEGER,
|
|
1275
1138
|
root_run_duration REAL, sub_run_duration REAL,
|
|
@@ -1296,7 +1159,7 @@ export async function rebuildSqliteReadModel(
|
|
|
1296
1159
|
PRIMARY KEY(activity_row_id, occurred_day_sp)
|
|
1297
1160
|
);`);
|
|
1298
1161
|
const insert = database.prepare(
|
|
1299
|
-
"INSERT INTO files(path, content, mtime_ms, size_bytes, content_sha256, artifact_kind, artifact_id, artifact_occurred_at, artifact_occurred_day_sp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
1162
|
+
"INSERT INTO files(path, content, mtime_ms, size_bytes, content_sha256, artifact_kind, artifact_id, artifact_occurred_at, artifact_occurred_day_sp, artifact_task_id, artifact_change_id, artifact_run_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
1300
1163
|
);
|
|
1301
1164
|
const insertSourceState = database.prepare(
|
|
1302
1165
|
"INSERT INTO read_model_source_state(source_path, source_hash, mtime_ms, size_bytes, source_kind, last_processed_at) VALUES (?, ?, ?, ?, ?, ?)",
|
|
@@ -1325,7 +1188,7 @@ export async function rebuildSqliteReadModel(
|
|
|
1325
1188
|
? "run"
|
|
1326
1189
|
: source.includes("/artifacts/evidence/")
|
|
1327
1190
|
? "evidence"
|
|
1328
|
-
: source.includes("/artifacts/validations/")
|
|
1191
|
+
: source.includes("/artifacts/validations/") || source.includes("/artifacts/tests/")
|
|
1329
1192
|
? "validation"
|
|
1330
1193
|
: source.startsWith(".agents/config/")
|
|
1331
1194
|
? "config"
|
|
@@ -1341,6 +1204,9 @@ export async function rebuildSqliteReadModel(
|
|
|
1341
1204
|
artifact?.id ?? null,
|
|
1342
1205
|
artifact?.occurredAt ?? null,
|
|
1343
1206
|
artifact?.occurredDaySp ?? null,
|
|
1207
|
+
artifact?.taskId ?? null,
|
|
1208
|
+
artifact?.changeId ?? null,
|
|
1209
|
+
artifact?.runId ?? null,
|
|
1344
1210
|
);
|
|
1345
1211
|
insertSourceState.run(
|
|
1346
1212
|
source,
|
|
@@ -1367,7 +1233,7 @@ export async function rebuildSqliteReadModel(
|
|
|
1367
1233
|
.all() as Array<{ path: string; content: string }>,
|
|
1368
1234
|
);
|
|
1369
1235
|
const insertCanonicalWorkItem = database.prepare(
|
|
1370
|
-
"INSERT INTO canonical_work_items(entity_kind, id, title, status, stage, risk, occurred_at, occurred_day_sp) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
|
1236
|
+
"INSERT INTO canonical_work_items(entity_kind, id, title, status, stage, risk, spec_id, change_id, completed_run_id, occurred_at, occurred_day_sp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
1371
1237
|
);
|
|
1372
1238
|
for (const entity of canonicalEntities.values()) {
|
|
1373
1239
|
if (
|
|
@@ -1383,6 +1249,9 @@ export async function rebuildSqliteReadModel(
|
|
|
1383
1249
|
entity.status,
|
|
1384
1250
|
entity.stage,
|
|
1385
1251
|
entity.risk,
|
|
1252
|
+
entity.specId,
|
|
1253
|
+
entity.changeId,
|
|
1254
|
+
entity.completedRunId,
|
|
1386
1255
|
entity.occurredAt,
|
|
1387
1256
|
saoPauloDay(entity.occurredAt),
|
|
1388
1257
|
);
|
|
@@ -1480,6 +1349,56 @@ export async function rebuildSqliteReadModel(
|
|
|
1480
1349
|
"SELECT path, content, content_sha256 FROM files WHERE path LIKE '.agents/records/runs/%.jsonl' ORDER BY path",
|
|
1481
1350
|
)
|
|
1482
1351
|
.all() as Array<{ path: string; content: string; content_sha256: string }>;
|
|
1352
|
+
// Historical attribution is an append-only, self-verifying overlay. It
|
|
1353
|
+
// never changes a Run stream and therefore participates only after the
|
|
1354
|
+
// native identity carried by the Run itself.
|
|
1355
|
+
const historicalDeveloperManifests = (
|
|
1356
|
+
database
|
|
1357
|
+
.prepare(
|
|
1358
|
+
"SELECT content FROM files WHERE path LIKE '.agents/records/lifecycle/historical-developer-attribution/%.json'",
|
|
1359
|
+
)
|
|
1360
|
+
.all() as Array<{ content: string }>
|
|
1361
|
+
).flatMap((row) => {
|
|
1362
|
+
try {
|
|
1363
|
+
return [JSON.parse(row.content)];
|
|
1364
|
+
} catch {
|
|
1365
|
+
return [];
|
|
1366
|
+
}
|
|
1367
|
+
});
|
|
1368
|
+
const resolveDeveloper = (row: { path: string; content: string }, identity: unknown) => {
|
|
1369
|
+
const native = recordValue(identity);
|
|
1370
|
+
if (
|
|
1371
|
+
native !== null &&
|
|
1372
|
+
typeof native.account_id === "string" &&
|
|
1373
|
+
typeof native.login_snapshot === "string"
|
|
1374
|
+
)
|
|
1375
|
+
return {
|
|
1376
|
+
accountId: native.account_id,
|
|
1377
|
+
login: native.login_snapshot,
|
|
1378
|
+
source:
|
|
1379
|
+
typeof native.attribution_source === "string" ? native.attribution_source : "native",
|
|
1380
|
+
};
|
|
1381
|
+
for (const candidate of historicalDeveloperManifests) {
|
|
1382
|
+
try {
|
|
1383
|
+
const resolved = resolveHistoricalDeveloperAttribution({
|
|
1384
|
+
manifest: candidate,
|
|
1385
|
+
run_id: String(JSON.parse(row.content.split(/\r?\n/u)[0] ?? "{}").run_id ?? ""),
|
|
1386
|
+
path: row.path,
|
|
1387
|
+
content: row.content,
|
|
1388
|
+
});
|
|
1389
|
+
if (resolved !== null)
|
|
1390
|
+
return {
|
|
1391
|
+
accountId: resolved.account_id,
|
|
1392
|
+
login: resolved.login_snapshot,
|
|
1393
|
+
source: resolved.attribution_source,
|
|
1394
|
+
};
|
|
1395
|
+
} catch {
|
|
1396
|
+
// A corrupt or non-matching historical manifest is not an
|
|
1397
|
+
// attribution source. The consumer remains fail-closed.
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
return null;
|
|
1401
|
+
};
|
|
1483
1402
|
const folds = new Map(
|
|
1484
1403
|
runRows.map((row) => {
|
|
1485
1404
|
const fold = foldRunV2Records(parseRunV2JsonLines(row.content));
|
|
@@ -1552,6 +1471,9 @@ export async function rebuildSqliteReadModel(
|
|
|
1552
1471
|
"model",
|
|
1553
1472
|
"service_tier",
|
|
1554
1473
|
"reasoning_effort",
|
|
1474
|
+
"developer_account_id",
|
|
1475
|
+
"developer_login",
|
|
1476
|
+
"developer_attribution_source",
|
|
1555
1477
|
"source_duration_ms",
|
|
1556
1478
|
"source_started_at",
|
|
1557
1479
|
"source_finished_at",
|
|
@@ -1590,10 +1512,11 @@ export async function rebuildSqliteReadModel(
|
|
|
1590
1512
|
"INSERT INTO canonical_run_index(run_id, occurred_at, occurred_day_sp, task_id, phase, run_role, duration_ms, source_event_count, zaip_agent, provider, model, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
1591
1513
|
);
|
|
1592
1514
|
let unassistedActivityCount = 0;
|
|
1593
|
-
|
|
1515
|
+
const excludedUnknownTokenRunCount = 0;
|
|
1594
1516
|
const temporalIntervals: TemporalInterval[] = [];
|
|
1595
1517
|
for (const { row, fold } of folds.values()) {
|
|
1596
1518
|
const started = fold.run;
|
|
1519
|
+
const developer = resolveDeveloper(row, started.developer_identity ?? null);
|
|
1597
1520
|
const usage = fold.effective_usage_records;
|
|
1598
1521
|
const agentRunId = started.agent_invocation_id ?? null;
|
|
1599
1522
|
const isAgentRun = agentRunId !== null;
|
|
@@ -1640,13 +1563,9 @@ export async function rebuildSqliteReadModel(
|
|
|
1640
1563
|
model,
|
|
1641
1564
|
fold.status,
|
|
1642
1565
|
);
|
|
1643
|
-
//
|
|
1644
|
-
//
|
|
1645
|
-
//
|
|
1646
|
-
if (totalTokens === null) {
|
|
1647
|
-
excludedUnknownTokenRunCount += 1;
|
|
1648
|
-
continue;
|
|
1649
|
-
}
|
|
1566
|
+
// Preserve an attributed Run with absent telemetry as an analytical
|
|
1567
|
+
// fact with NULL measures. This makes coverage visibly unknown rather
|
|
1568
|
+
// than silently converting it to a known zero or erasing its owner.
|
|
1650
1569
|
const profile = agentProfiles.get(zaipAgent) ?? agentProfiles.get(started.agent);
|
|
1651
1570
|
const workItem =
|
|
1652
1571
|
started.task_id === null
|
|
@@ -1735,6 +1654,9 @@ export async function rebuildSqliteReadModel(
|
|
|
1735
1654
|
consolidatedValue(usage.map((record) => record.runtime?.reasoning_effort ?? null)) ??
|
|
1736
1655
|
started.reasoning_effort ??
|
|
1737
1656
|
null,
|
|
1657
|
+
developer_account_id: developer?.accountId ?? null,
|
|
1658
|
+
developer_login: developer?.login ?? null,
|
|
1659
|
+
developer_attribution_source: developer?.source ?? null,
|
|
1738
1660
|
source_duration_ms: durations.source,
|
|
1739
1661
|
source_started_at: durations.sourceStartedAt,
|
|
1740
1662
|
source_finished_at: durations.sourceFinishedAt,
|
|
@@ -1761,7 +1683,12 @@ export async function rebuildSqliteReadModel(
|
|
|
1761
1683
|
payload_json: null,
|
|
1762
1684
|
} satisfies Record<(typeof columns)[number], string | number | null>;
|
|
1763
1685
|
canonicalInsertActivity.run(...columns.map((column) => activity[column] ?? null));
|
|
1764
|
-
if (
|
|
1686
|
+
if (
|
|
1687
|
+
totalTokens !== null &&
|
|
1688
|
+
totalTokens > 0 &&
|
|
1689
|
+
durations.effective !== null &&
|
|
1690
|
+
durations.effective >= 0
|
|
1691
|
+
) {
|
|
1765
1692
|
const startMs = Date.parse(durations.sourceStartedAt ?? started.occurred_at);
|
|
1766
1693
|
const endMs =
|
|
1767
1694
|
Date.parse(durations.sourceFinishedAt ?? "") || startMs + durations.effective;
|
|
@@ -1817,6 +1744,188 @@ export async function rebuildSqliteReadModel(
|
|
|
1817
1744
|
}
|
|
1818
1745
|
for (const fact of allocateAttributedTokenActiveMs(temporalIntervals))
|
|
1819
1746
|
insertTemporalFact.run(fact.activityRowId, fact.day, fact.value);
|
|
1747
|
+
// A single authoring sub-Run can create several sibling SPECs. Keep the
|
|
1748
|
+
// Run canonical and attach deterministic shares only to the macro read
|
|
1749
|
+
// model. Payload rows stay outside `activities`, preventing global
|
|
1750
|
+
// Run/Cost totals from counting that Run more than once.
|
|
1751
|
+
const creationSpecsByRun = new Map<string, CanonicalEntity[]>();
|
|
1752
|
+
for (const entity of canonicalEntities.values()) {
|
|
1753
|
+
if (entity.kind !== "spec" || entity.creationRunId === null) continue;
|
|
1754
|
+
creationSpecsByRun.set(entity.creationRunId, [
|
|
1755
|
+
...(creationSpecsByRun.get(entity.creationRunId) ?? []),
|
|
1756
|
+
entity,
|
|
1757
|
+
]);
|
|
1758
|
+
}
|
|
1759
|
+
const materializeSharedCreationAllocations = () => {
|
|
1760
|
+
for (const [runId, recipients] of creationSpecsByRun) {
|
|
1761
|
+
if (recipients.length === 0) continue;
|
|
1762
|
+
const source = folds.get(runId);
|
|
1763
|
+
if (source === undefined) continue;
|
|
1764
|
+
const { fold, row } = source;
|
|
1765
|
+
const started = fold.run;
|
|
1766
|
+
const usage = fold.effective_usage_records;
|
|
1767
|
+
const canonicalActivity = database
|
|
1768
|
+
.prepare(
|
|
1769
|
+
"SELECT duration_ms, duration_provenance, source_started_at, source_finished_at FROM zaip_aios_activities WHERE run_id=? AND payload_type IS NULL LIMIT 1",
|
|
1770
|
+
)
|
|
1771
|
+
.get(runId) as
|
|
1772
|
+
| {
|
|
1773
|
+
duration_ms: number | null;
|
|
1774
|
+
duration_provenance: string | null;
|
|
1775
|
+
source_started_at: string | null;
|
|
1776
|
+
source_finished_at: string | null;
|
|
1777
|
+
}
|
|
1778
|
+
| undefined;
|
|
1779
|
+
const cost = aggregateCost(usage, fold, pricingCatalog, fxRate);
|
|
1780
|
+
const recipientIds = recipients.map((recipient) => recipient.id);
|
|
1781
|
+
// The allocator owns the only partition. Read the already-materialized
|
|
1782
|
+
// canonical activity fact after attributed-token-active reconciliation;
|
|
1783
|
+
// never use the wider JSONL session span or a pre-divided value.
|
|
1784
|
+
const sharedCreationDuration = canonicalActivity?.duration_ms ?? null;
|
|
1785
|
+
const allocations = {
|
|
1786
|
+
source_duration_ms: allocateSharedCreationValue(sharedCreationDuration, recipientIds),
|
|
1787
|
+
// Shared creation attribution uses only the observed source interval;
|
|
1788
|
+
// never propagate an effective/legacy aggregate that can include
|
|
1789
|
+
// more than the creation segment.
|
|
1790
|
+
duration_ms: allocateSharedCreationValue(sharedCreationDuration, recipientIds),
|
|
1791
|
+
input_tokens: allocateSharedCreationValue(knownSum(usage, "input"), recipientIds),
|
|
1792
|
+
cached_input_tokens: allocateSharedCreationValue(
|
|
1793
|
+
knownSum(usage, "cached_input"),
|
|
1794
|
+
recipientIds,
|
|
1795
|
+
),
|
|
1796
|
+
output_tokens: allocateSharedCreationValue(knownSum(usage, "output"), recipientIds),
|
|
1797
|
+
reasoning_tokens: allocateSharedCreationValue(
|
|
1798
|
+
knownSum(usage, "reasoning"),
|
|
1799
|
+
recipientIds,
|
|
1800
|
+
),
|
|
1801
|
+
total_tokens: allocateSharedCreationValue(knownSum(usage, "total"), recipientIds),
|
|
1802
|
+
cost_usd: allocateSharedCreationValue(cost.usd, recipientIds),
|
|
1803
|
+
cost_brl: allocateSharedCreationValue(cost.brl, recipientIds),
|
|
1804
|
+
};
|
|
1805
|
+
for (const recipient of recipients) {
|
|
1806
|
+
const attributed =
|
|
1807
|
+
usage.find((record) => record.attributed_spec_id === recipient.id) ?? null;
|
|
1808
|
+
const attributedDuration =
|
|
1809
|
+
attributed?.source_started_at && attributed?.source_finished_at
|
|
1810
|
+
? Math.max(
|
|
1811
|
+
0,
|
|
1812
|
+
Date.parse(attributed.source_finished_at) -
|
|
1813
|
+
Date.parse(attributed.source_started_at),
|
|
1814
|
+
)
|
|
1815
|
+
: null;
|
|
1816
|
+
const activityRowId = `ACTV-${createHash("sha256")
|
|
1817
|
+
.update(`shared-creation:${runId}:${recipient.id}`)
|
|
1818
|
+
.digest("hex")
|
|
1819
|
+
.slice(0, 24)}`;
|
|
1820
|
+
const lastRecord = fold.records.at(-1);
|
|
1821
|
+
const allocation = {
|
|
1822
|
+
activity_row_id: activityRowId,
|
|
1823
|
+
activity_source_kind: "shared_creation_allocation",
|
|
1824
|
+
activity_key: `shared-creation:${runId}:${recipient.id}`,
|
|
1825
|
+
source_path: row.path,
|
|
1826
|
+
source_hash: row.content_sha256,
|
|
1827
|
+
source_occurred_at: started.occurred_at,
|
|
1828
|
+
recorded_at: lastRecord?.occurred_at ?? started.occurred_at,
|
|
1829
|
+
version_activity_id: null,
|
|
1830
|
+
source_last_event_id: lastRecord?.event_id ?? null,
|
|
1831
|
+
source_last_sequence: lastRecord?.sequence ?? null,
|
|
1832
|
+
work_item_kind: "spec",
|
|
1833
|
+
title: recipient.title,
|
|
1834
|
+
work_item_status: recipient.status,
|
|
1835
|
+
work_stage: recipient.stage,
|
|
1836
|
+
risk: recipient.risk,
|
|
1837
|
+
run_role: "shared_creation_allocation",
|
|
1838
|
+
root_run_id: rootRunId(fold),
|
|
1839
|
+
parent_run_id: started.parent_run_id,
|
|
1840
|
+
agent_run_id: null,
|
|
1841
|
+
root_agent_run_id: null,
|
|
1842
|
+
parent_agent_run_id: null,
|
|
1843
|
+
agent_run_role: null,
|
|
1844
|
+
delegation_depth: started.delegation_depth ?? null,
|
|
1845
|
+
agent_invocation_id: null,
|
|
1846
|
+
root_agent_invocation_id: null,
|
|
1847
|
+
parent_agent_invocation_id: null,
|
|
1848
|
+
invocation_kind: null,
|
|
1849
|
+
execution_id: started.execution_id,
|
|
1850
|
+
task_id: null,
|
|
1851
|
+
spec_id: recipient.id,
|
|
1852
|
+
prd_id: null,
|
|
1853
|
+
change_id: null,
|
|
1854
|
+
conversation_id: started.conversation_id,
|
|
1855
|
+
run_id: started.run_id,
|
|
1856
|
+
occurred_at: started.occurred_at,
|
|
1857
|
+
occurred_day_sp: saoPauloDay(started.occurred_at),
|
|
1858
|
+
execution_kind: started.execution_kind ?? null,
|
|
1859
|
+
entrypoint: started.entrypoint,
|
|
1860
|
+
work_type: started.work_type,
|
|
1861
|
+
phase: started.phase,
|
|
1862
|
+
purpose: started.run_purpose ?? null,
|
|
1863
|
+
status: fold.status,
|
|
1864
|
+
activity_kind: "planning",
|
|
1865
|
+
zaip_agent: started.agent_name ?? started.agent,
|
|
1866
|
+
zaip_agent_persona: null,
|
|
1867
|
+
zaip_agent_job: null,
|
|
1868
|
+
zaip_agent_role: null,
|
|
1869
|
+
agent_catalog_id: null,
|
|
1870
|
+
agent_catalog_hash: catalogHash,
|
|
1871
|
+
client: started.client,
|
|
1872
|
+
provider: null,
|
|
1873
|
+
model: null,
|
|
1874
|
+
service_tier: null,
|
|
1875
|
+
reasoning_effort: started.reasoning_effort ?? null,
|
|
1876
|
+
developer_account_id: null,
|
|
1877
|
+
developer_login: null,
|
|
1878
|
+
developer_attribution_source: null,
|
|
1879
|
+
source_duration_ms:
|
|
1880
|
+
attributedDuration ?? allocations.source_duration_ms.get(recipient.id) ?? null,
|
|
1881
|
+
source_started_at:
|
|
1882
|
+
attributed?.source_started_at ?? canonicalActivity?.source_started_at ?? null,
|
|
1883
|
+
source_finished_at:
|
|
1884
|
+
attributed?.source_finished_at ?? canonicalActivity?.source_finished_at ?? null,
|
|
1885
|
+
duration_ms: attributedDuration ?? allocations.duration_ms.get(recipient.id) ?? null,
|
|
1886
|
+
duration_provenance: attributed
|
|
1887
|
+
? "explicit_spec_turn_recovery"
|
|
1888
|
+
: "shared_creation_allocation",
|
|
1889
|
+
input_tokens:
|
|
1890
|
+
attributed?.tokens.input ?? allocations.input_tokens.get(recipient.id) ?? null,
|
|
1891
|
+
cached_input_tokens:
|
|
1892
|
+
attributed?.tokens.cached_input ??
|
|
1893
|
+
allocations.cached_input_tokens.get(recipient.id) ??
|
|
1894
|
+
null,
|
|
1895
|
+
output_tokens:
|
|
1896
|
+
attributed?.tokens.output ?? allocations.output_tokens.get(recipient.id) ?? null,
|
|
1897
|
+
reasoning_tokens:
|
|
1898
|
+
attributed?.tokens.reasoning ??
|
|
1899
|
+
allocations.reasoning_tokens.get(recipient.id) ??
|
|
1900
|
+
null,
|
|
1901
|
+
total_tokens:
|
|
1902
|
+
attributed?.tokens.total ?? allocations.total_tokens.get(recipient.id) ?? null,
|
|
1903
|
+
token_source: consolidatedValue(usage.map((record) => record.tokens.source)),
|
|
1904
|
+
usage_event_count: usage.length,
|
|
1905
|
+
llm_call_count: null,
|
|
1906
|
+
cost_usd: attributed?.cost.usd ?? allocations.cost_usd.get(recipient.id) ?? null,
|
|
1907
|
+
cost_brl: attributed?.cost.brl ?? allocations.cost_brl.get(recipient.id) ?? null,
|
|
1908
|
+
cost_method: cost.method,
|
|
1909
|
+
pricing_model: cost.pricingModel,
|
|
1910
|
+
pricing_source: cost.pricingSource,
|
|
1911
|
+
fx_rate: cost.fxRate,
|
|
1912
|
+
source_event_count: fold.records.length,
|
|
1913
|
+
root_run_duration: null,
|
|
1914
|
+
sub_run_duration: null,
|
|
1915
|
+
payload_type: "shared_creation_allocation",
|
|
1916
|
+
payload_json: JSON.stringify({
|
|
1917
|
+
provenance: attributed
|
|
1918
|
+
? "explicit_spec_turn_recovery"
|
|
1919
|
+
: "shared_creation_allocation",
|
|
1920
|
+
base_duration_provenance: canonicalActivity?.duration_provenance ?? null,
|
|
1921
|
+
creation_run_id: runId,
|
|
1922
|
+
spec_id: recipient.id,
|
|
1923
|
+
}),
|
|
1924
|
+
} satisfies Record<(typeof columns)[number], string | number | null>;
|
|
1925
|
+
canonicalInsertActivity.run(...columns.map((column) => allocation[column] ?? null));
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
};
|
|
1820
1929
|
database.exec(`UPDATE zaip_aios_activities
|
|
1821
1930
|
SET duration_ms = (
|
|
1822
1931
|
SELECT SUM(f.attributed_token_active_ms)
|
|
@@ -1825,7 +1934,9 @@ export async function rebuildSqliteReadModel(
|
|
|
1825
1934
|
),
|
|
1826
1935
|
duration_provenance = CASE
|
|
1827
1936
|
WHEN EXISTS (SELECT 1 FROM attributed_token_active_facts f WHERE f.activity_row_id=zaip_aios_activities.activity_row_id)
|
|
1828
|
-
THEN 'attributed_token_active' ELSE NULL END
|
|
1937
|
+
THEN 'attributed_token_active' ELSE NULL END
|
|
1938
|
+
WHERE payload_type IS NULL;`);
|
|
1939
|
+
materializeSharedCreationAllocations();
|
|
1829
1940
|
const canonicalRunCount = folds.size;
|
|
1830
1941
|
const invocationCount = (
|
|
1831
1942
|
database
|
|
@@ -1906,7 +2017,7 @@ export async function rebuildSqliteReadModel(
|
|
|
1906
2017
|
insertConfiguredModel.run(configuredModel.id);
|
|
1907
2018
|
insertConfiguredProvider.run(configuredModel.provider);
|
|
1908
2019
|
}
|
|
1909
|
-
const costDimensionAggregates = buildCostDimensionAggregates(database);
|
|
2020
|
+
const costDimensionAggregates = buildCostDimensionAggregates(database, agentProfiles);
|
|
1910
2021
|
// The rebuild scratch tables above are intentionally TEMP only. Persist
|
|
1911
2022
|
// their technical inputs as typed payload activities so the on-disk
|
|
1912
2023
|
// model has one fact plus its three declared dimensions, never hidden
|
|
@@ -1929,11 +2040,11 @@ export async function rebuildSqliteReadModel(
|
|
|
1929
2040
|
);
|
|
1930
2041
|
persistPayload(
|
|
1931
2042
|
"file",
|
|
1932
|
-
"SELECT path AS row_key, json_object('path', path, 'content', content, 'mtime_ms', mtime_ms, 'size_bytes', size_bytes, 'content_sha256', content_sha256, 'artifact_kind', artifact_kind, 'artifact_id', artifact_id, 'artifact_occurred_at', artifact_occurred_at, 'artifact_occurred_day_sp', artifact_occurred_day_sp) AS payload_json FROM files",
|
|
2043
|
+
"SELECT path AS row_key, json_object('path', path, 'content', content, 'mtime_ms', mtime_ms, 'size_bytes', size_bytes, 'content_sha256', content_sha256, 'artifact_kind', artifact_kind, 'artifact_id', artifact_id, 'artifact_occurred_at', artifact_occurred_at, 'artifact_occurred_day_sp', artifact_occurred_day_sp, 'artifact_task_id', artifact_task_id, 'artifact_change_id', artifact_change_id, 'artifact_run_id', artifact_run_id) AS payload_json FROM files",
|
|
1933
2044
|
);
|
|
1934
2045
|
persistPayload(
|
|
1935
2046
|
"work_item",
|
|
1936
|
-
"SELECT entity_kind || ':' || id AS row_key, json_object('entity_kind', entity_kind, 'id', id, 'title', title, 'status', status, 'stage', stage, 'risk', risk, 'occurred_at', occurred_at, 'occurred_day_sp', occurred_day_sp) AS payload_json FROM canonical_work_items",
|
|
2047
|
+
"SELECT entity_kind || ':' || id AS row_key, json_object('entity_kind', entity_kind, 'id', id, 'title', title, 'status', status, 'stage', stage, 'risk', risk, 'spec_id', spec_id, 'change_id', change_id, 'completed_run_id', completed_run_id, 'occurred_at', occurred_at, 'occurred_day_sp', occurred_day_sp) AS payload_json FROM canonical_work_items",
|
|
1937
2048
|
);
|
|
1938
2049
|
persistPayload(
|
|
1939
2050
|
"execution",
|
|
@@ -1991,6 +2102,27 @@ export async function rebuildSqliteReadModel(
|
|
|
1991
2102
|
);
|
|
1992
2103
|
}
|
|
1993
2104
|
}
|
|
2105
|
+
for (const [dimension, projection] of Object.entries(costDimensionAggregates.remote)) {
|
|
2106
|
+
for (const row of projection.rows) {
|
|
2107
|
+
insertCostDimensionAggregate.run(
|
|
2108
|
+
`TECH-${randomUUID().toLowerCase()}`,
|
|
2109
|
+
`cost_dimension_aggregate_v1:${dimension}:${row.key}`,
|
|
2110
|
+
`sqlite://cost_dimension_aggregate_v1/${dimension}`,
|
|
2111
|
+
JSON.stringify({ schema_version: 1, dimension, row_kind: "row", ...row }),
|
|
2112
|
+
);
|
|
2113
|
+
}
|
|
2114
|
+
insertCostDimensionAggregate.run(
|
|
2115
|
+
`TECH-${randomUUID().toLowerCase()}`,
|
|
2116
|
+
`cost_dimension_aggregate_v1:${dimension}:__totals__`,
|
|
2117
|
+
`sqlite://cost_dimension_aggregate_v1/${dimension}`,
|
|
2118
|
+
JSON.stringify({
|
|
2119
|
+
schema_version: 1,
|
|
2120
|
+
dimension,
|
|
2121
|
+
row_kind: "totals",
|
|
2122
|
+
...projection.totals,
|
|
2123
|
+
}),
|
|
2124
|
+
);
|
|
2125
|
+
}
|
|
1994
2126
|
const viewSql = `
|
|
1995
2127
|
CREATE INDEX zaip_activities_agent_run ON zaip_aios_activities(activity_source_kind, agent_run_id, occurred_day_sp, occurred_at DESC);
|
|
1996
2128
|
CREATE INDEX zaip_activities_relationships ON zaip_aios_activities(task_id, spec_id, prd_id, execution_id, conversation_id);
|
|
@@ -1998,7 +2130,7 @@ export async function rebuildSqliteReadModel(
|
|
|
1998
2130
|
CREATE INDEX zaip_activities_spec_day ON zaip_aios_activities(payload_type, spec_id, occurred_day_sp) WHERE spec_id IS NOT NULL;
|
|
1999
2131
|
CREATE INDEX zaip_activities_prd_day ON zaip_aios_activities(payload_type, prd_id, occurred_day_sp) WHERE prd_id IS NOT NULL;
|
|
2000
2132
|
CREATE INDEX zaip_activities_agent ON zaip_aios_activities(zaip_agent, zaip_agent_persona, zaip_agent_job, provider, model);
|
|
2001
|
-
CREATE UNIQUE INDEX zaip_activities_run_id ON zaip_aios_activities(run_id);
|
|
2133
|
+
CREATE UNIQUE INDEX zaip_activities_run_id ON zaip_aios_activities(run_id) WHERE payload_type IS NULL;
|
|
2002
2134
|
CREATE INDEX zaip_activities_run_hierarchy ON zaip_aios_activities(root_run_id, parent_run_id, run_role);
|
|
2003
2135
|
CREATE INDEX zaip_activities_period ON zaip_aios_activities(occurred_day_sp, occurred_at DESC);
|
|
2004
2136
|
-- Dimension residuals first restrict the visible period, then group
|
|
@@ -2008,17 +2140,17 @@ export async function rebuildSqliteReadModel(
|
|
|
2008
2140
|
WHERE agent_invocation_id IS NOT NULL;
|
|
2009
2141
|
CREATE INDEX zaip_activities_status ON zaip_aios_activities(activity_source_kind, status, phase);
|
|
2010
2142
|
CREATE INDEX zaip_activities_payload ON zaip_aios_activities(payload_type, activity_key);
|
|
2011
|
-
CREATE VIEW files AS SELECT json_extract(payload_json, '$.path') AS path, json_extract(payload_json, '$.content') AS content, json_extract(payload_json, '$.mtime_ms') AS mtime_ms, json_extract(payload_json, '$.size_bytes') AS size_bytes, json_extract(payload_json, '$.content_sha256') AS content_sha256, json_extract(payload_json, '$.artifact_kind') AS artifact_kind, json_extract(payload_json, '$.artifact_id') AS artifact_id, json_extract(payload_json, '$.artifact_occurred_at') AS artifact_occurred_at, json_extract(payload_json, '$.artifact_occurred_day_sp') AS artifact_occurred_day_sp FROM zaip_aios_activities WHERE payload_type='file';
|
|
2143
|
+
CREATE VIEW files AS SELECT json_extract(payload_json, '$.path') AS path, json_extract(payload_json, '$.content') AS content, json_extract(payload_json, '$.mtime_ms') AS mtime_ms, json_extract(payload_json, '$.size_bytes') AS size_bytes, json_extract(payload_json, '$.content_sha256') AS content_sha256, json_extract(payload_json, '$.artifact_kind') AS artifact_kind, json_extract(payload_json, '$.artifact_id') AS artifact_id, json_extract(payload_json, '$.artifact_occurred_at') AS artifact_occurred_at, json_extract(payload_json, '$.artifact_occurred_day_sp') AS artifact_occurred_day_sp, json_extract(payload_json, '$.artifact_task_id') AS artifact_task_id, json_extract(payload_json, '$.artifact_change_id') AS artifact_change_id, json_extract(payload_json, '$.artifact_run_id') AS artifact_run_id FROM zaip_aios_activities WHERE payload_type='file';
|
|
2012
2144
|
CREATE VIEW read_model_source_state AS SELECT json_extract(payload_json, '$.source_path') AS source_path, json_extract(payload_json, '$.source_hash') AS source_hash, json_extract(payload_json, '$.mtime_ms') AS mtime_ms, json_extract(payload_json, '$.size_bytes') AS size_bytes, json_extract(payload_json, '$.source_kind') AS source_kind, json_extract(payload_json, '$.last_processed_at') AS last_processed_at FROM zaip_aios_activities WHERE payload_type='source_state';
|
|
2013
2145
|
CREATE VIEW read_model_metadata AS SELECT json_extract(payload_json, '$.key') AS key, json_extract(payload_json, '$.value') AS value FROM zaip_aios_activities WHERE payload_type='metadata';
|
|
2014
|
-
CREATE VIEW canonical_work_items AS SELECT json_extract(payload_json, '$.entity_kind') AS entity_kind, json_extract(payload_json, '$.id') AS id, json_extract(payload_json, '$.title') AS title, json_extract(payload_json, '$.status') AS status, json_extract(payload_json, '$.stage') AS stage, json_extract(payload_json, '$.risk') AS risk, json_extract(payload_json, '$.occurred_at') AS occurred_at, json_extract(payload_json, '$.occurred_day_sp') AS occurred_day_sp FROM zaip_aios_activities WHERE payload_type='work_item';
|
|
2146
|
+
CREATE VIEW canonical_work_items AS SELECT json_extract(payload_json, '$.entity_kind') AS entity_kind, json_extract(payload_json, '$.id') AS id, json_extract(payload_json, '$.title') AS title, json_extract(payload_json, '$.status') AS status, json_extract(payload_json, '$.stage') AS stage, json_extract(payload_json, '$.risk') AS risk, json_extract(payload_json, '$.spec_id') AS spec_id, json_extract(payload_json, '$.change_id') AS change_id, json_extract(payload_json, '$.completed_run_id') AS completed_run_id, json_extract(payload_json, '$.occurred_at') AS occurred_at, json_extract(payload_json, '$.occurred_day_sp') AS occurred_day_sp FROM zaip_aios_activities WHERE payload_type='work_item';
|
|
2015
2147
|
CREATE VIEW canonical_executions AS SELECT json_extract(payload_json, '$.id') AS id, json_extract(payload_json, '$.status') AS status, json_extract(payload_json, '$.kind') AS kind, json_extract(payload_json, '$.origin_type') AS origin_type, json_extract(payload_json, '$.origin_id') AS origin_id, json_extract(payload_json, '$.created_at') AS created_at, json_extract(payload_json, '$.occurred_day_sp') AS occurred_day_sp, json_extract(payload_json, '$.finished_at') AS finished_at, json_extract(payload_json, '$.source_path') AS source_path FROM zaip_aios_activities WHERE payload_type='execution';
|
|
2016
2148
|
CREATE VIEW canonical_changes AS SELECT json_extract(payload_json, '$.id') AS id, json_extract(payload_json, '$.title') AS title, json_extract(payload_json, '$.status') AS status, json_extract(payload_json, '$.risk') AS risk, json_extract(payload_json, '$.source_type') AS source_type, json_extract(payload_json, '$.source_id') AS source_id, json_extract(payload_json, '$.source_spec_id') AS source_spec_id, json_extract(payload_json, '$.source_prd_id') AS source_prd_id, json_extract(payload_json, '$.requirement_ids_json') AS requirement_ids_json, json_extract(payload_json, '$.acceptance_ids_json') AS acceptance_ids_json, json_extract(payload_json, '$.task_ids_json') AS task_ids_json, json_extract(payload_json, '$.result') AS result, json_extract(payload_json, '$.created_at') AS created_at, json_extract(payload_json, '$.occurred_day_sp') AS occurred_day_sp, json_extract(payload_json, '$.source_path') AS source_path FROM zaip_aios_activities WHERE payload_type='change';
|
|
2017
2149
|
CREATE VIEW canonical_decisions AS SELECT json_extract(payload_json, '$.id') AS id, json_extract(payload_json, '$.title') AS title, json_extract(payload_json, '$.origin') AS origin, json_extract(payload_json, '$.scope_type') AS scope_type, json_extract(payload_json, '$.scope_id') AS scope_id, json_extract(payload_json, '$.created_at') AS created_at, json_extract(payload_json, '$.occurred_day_sp') AS occurred_day_sp, json_extract(payload_json, '$.source_path') AS source_path FROM zaip_aios_activities WHERE payload_type='decision';
|
|
2018
2150
|
CREATE VIEW canonical_run_index AS SELECT json_extract(payload_json, '$.run_id') AS run_id, json_extract(payload_json, '$.occurred_at') AS occurred_at, json_extract(payload_json, '$.occurred_day_sp') AS occurred_day_sp, json_extract(payload_json, '$.task_id') AS task_id, json_extract(payload_json, '$.phase') AS phase, json_extract(payload_json, '$.run_role') AS run_role, json_extract(payload_json, '$.duration_ms') AS duration_ms, json_extract(payload_json, '$.source_event_count') AS source_event_count, json_extract(payload_json, '$.zaip_agent') AS zaip_agent, json_extract(payload_json, '$.provider') AS provider, json_extract(payload_json, '$.model') AS model, json_extract(payload_json, '$.status') AS status FROM zaip_aios_activities WHERE payload_type='run_index';
|
|
2019
2151
|
CREATE VIEW invocation_usage_segments AS SELECT json_extract(payload_json, '$.agent_invocation_id') AS agent_invocation_id, json_extract(payload_json, '$.provider') AS provider, json_extract(payload_json, '$.model') AS model, json_extract(payload_json, '$.service_tier') AS service_tier, json_extract(payload_json, '$.reasoning_effort') AS reasoning_effort, json_extract(payload_json, '$.input_tokens') AS input_tokens, json_extract(payload_json, '$.cached_input_tokens') AS cached_input_tokens, json_extract(payload_json, '$.uncached_input_tokens') AS uncached_input_tokens, json_extract(payload_json, '$.output_tokens') AS output_tokens, json_extract(payload_json, '$.reasoning_tokens') AS reasoning_tokens, json_extract(payload_json, '$.visible_output_tokens') AS visible_output_tokens, json_extract(payload_json, '$.total_tokens') AS total_tokens, json_extract(payload_json, '$.input_rate_usd_per_million') AS input_rate_usd_per_million, json_extract(payload_json, '$.cached_input_rate_usd_per_million') AS cached_input_rate_usd_per_million, json_extract(payload_json, '$.output_rate_usd_per_million') AS output_rate_usd_per_million, json_extract(payload_json, '$.reasoning_rate_usd_per_million') AS reasoning_rate_usd_per_million, json_extract(payload_json, '$.context_input_multiplier') AS context_input_multiplier, json_extract(payload_json, '$.context_output_multiplier') AS context_output_multiplier, json_extract(payload_json, '$.speed_cost_multiplier') AS speed_cost_multiplier, json_extract(payload_json, '$.pricing_scenario') AS pricing_scenario, json_extract(payload_json, '$.standard_cost_usd') AS standard_cost_usd, json_extract(payload_json, '$.speed_uplift_usd') AS speed_uplift_usd, json_extract(payload_json, '$.input_cost_usd') AS input_cost_usd, json_extract(payload_json, '$.cached_input_cost_usd') AS cached_input_cost_usd, json_extract(payload_json, '$.output_cost_usd') AS output_cost_usd, json_extract(payload_json, '$.reasoning_cost_usd') AS reasoning_cost_usd, json_extract(payload_json, '$.calculated_cost_usd') AS calculated_cost_usd, json_extract(payload_json, '$.recorded_cost_usd') AS recorded_cost_usd, json_extract(payload_json, '$.cost_variance_usd') AS cost_variance_usd, json_extract(payload_json, '$.cost_usd') AS cost_usd, json_extract(payload_json, '$.cost_brl') AS cost_brl, json_extract(payload_json, '$.cost_method') AS cost_method, json_extract(payload_json, '$.pricing_model') AS pricing_model, json_extract(payload_json, '$.pricing_source') AS pricing_source, json_extract(payload_json, '$.fx_rate') AS fx_rate FROM zaip_aios_activities WHERE payload_type='usage_segment';
|
|
2020
2152
|
CREATE VIEW attributed_token_active_facts AS SELECT json_extract(payload_json, '$.activity_row_id') AS activity_row_id, json_extract(payload_json, '$.occurred_day_sp') AS occurred_day_sp, json_extract(payload_json, '$.attributed_token_active_ms') AS attributed_token_active_ms FROM zaip_aios_activities WHERE payload_type='duration_allocation';
|
|
2021
|
-
CREATE VIEW canonical_artifacts AS SELECT artifact_kind, artifact_id, artifact_occurred_at AS occurred_at, artifact_occurred_day_sp AS occurred_day_sp, path AS source_path FROM files WHERE artifact_kind IS NOT NULL AND artifact_occurred_at IS NOT NULL;
|
|
2153
|
+
CREATE VIEW canonical_artifacts AS SELECT artifact_kind, artifact_id, artifact_occurred_at AS occurred_at, artifact_occurred_day_sp AS occurred_day_sp, artifact_task_id AS task_id, artifact_change_id AS change_id, artifact_run_id AS run_id, path AS source_path FROM files WHERE artifact_kind IS NOT NULL AND artifact_occurred_at IS NOT NULL;
|
|
2022
2154
|
CREATE VIEW activities AS
|
|
2023
2155
|
SELECT a.*, f.attributed_token_active_ms
|
|
2024
2156
|
FROM zaip_aios_activities a
|
|
@@ -2027,6 +2159,19 @@ export async function rebuildSqliteReadModel(
|
|
|
2027
2159
|
FROM attributed_token_active_facts GROUP BY activity_row_id
|
|
2028
2160
|
) f USING(activity_row_id)
|
|
2029
2161
|
WHERE a.payload_type IS NULL;
|
|
2162
|
+
-- Macro-only allocations are persisted as technical payloads to keep
|
|
2163
|
+
-- the global Run population unique. SPEC summaries opt into this
|
|
2164
|
+
-- projection explicitly; all other activity and cost views do not.
|
|
2165
|
+
CREATE VIEW shared_creation_allocations AS
|
|
2166
|
+
SELECT a.*, NULL AS attributed_token_active_ms
|
|
2167
|
+
FROM zaip_aios_activities a WHERE payload_type='shared_creation_allocation';
|
|
2168
|
+
CREATE VIEW macro_spec_activities AS
|
|
2169
|
+
SELECT a.* FROM activities a WHERE a.spec_id IS NOT NULL
|
|
2170
|
+
AND NOT EXISTS (
|
|
2171
|
+
SELECT 1 FROM shared_creation_allocations allocation
|
|
2172
|
+
WHERE allocation.spec_id=a.spec_id AND allocation.run_id=a.run_id
|
|
2173
|
+
)
|
|
2174
|
+
UNION ALL SELECT * FROM shared_creation_allocations;
|
|
2030
2175
|
CREATE VIEW attributed_token_active AS
|
|
2031
2176
|
SELECT f.activity_row_id, f.occurred_day_sp, f.attributed_token_active_ms,
|
|
2032
2177
|
a.run_id, a.execution_id, a.task_id, a.spec_id, a.prd_id, a.conversation_id,
|
|
@@ -2162,7 +2307,10 @@ export async function rebuildSqliteReadModel(
|
|
|
2162
2307
|
SUM(sub_run_duration) AS total_sub_run_duration,
|
|
2163
2308
|
SUM(total_tokens) AS total_tokens,
|
|
2164
2309
|
SUM(cost_usd) AS total_cost_usd,
|
|
2165
|
-
SUM(cost_brl) AS total_cost_brl
|
|
2310
|
+
SUM(cost_brl) AS total_cost_brl,
|
|
2311
|
+
COUNT(total_tokens) AS token_known_runs,
|
|
2312
|
+
COUNT(cost_usd) AS usd_known_runs,
|
|
2313
|
+
COUNT(cost_brl) AS brl_known_runs
|
|
2166
2314
|
FROM activities
|
|
2167
2315
|
GROUP BY date(COALESCE(source_started_at, source_occurred_at, occurred_at), '-3 hours')
|
|
2168
2316
|
), cost_monitor_financial_daily AS (
|
|
@@ -2273,7 +2421,10 @@ export async function rebuildSqliteReadModel(
|
|
|
2273
2421
|
o.total_duration AS total_duration,
|
|
2274
2422
|
f.total_tokens,
|
|
2275
2423
|
f.total_cost_usd,
|
|
2276
|
-
f.total_cost_brl
|
|
2424
|
+
f.total_cost_brl,
|
|
2425
|
+
i.token_known_runs,
|
|
2426
|
+
i.usd_known_runs,
|
|
2427
|
+
i.brl_known_runs
|
|
2277
2428
|
FROM days d
|
|
2278
2429
|
LEFT JOIN work_item_daily w USING(day)
|
|
2279
2430
|
LEFT JOIN artifact_daily a USING(day)
|
|
@@ -2302,7 +2453,7 @@ export async function rebuildSqliteReadModel(
|
|
|
2302
2453
|
-- population rather than Operations' broader Run/activity totals.
|
|
2303
2454
|
CREATE VIEW cost_monitor_overview AS SELECT period_kind, period_key, cost_monitor_invocation_count AS total_run_count, cost_monitor_invocation_count AS invocation_count, llm_call_count, source_event_count, cost_monitor_total_duration, total_tokens, total_cost_usd, total_cost_brl FROM overview;
|
|
2304
2455
|
CREATE VIEW task_summary AS SELECT t.id, t.title, t.status, t.risk, t.occurred_at, t.occurred_day_sp, COUNT(i.run_id) AS invocation_count, SUM(i.llm_call_count) AS llm_call_count, SUM(i.duration_ms) AS total_duration, SUM(i.total_tokens) AS total_tokens, SUM(i.cost_usd) AS total_cost_usd, SUM(i.cost_brl) AS total_cost_brl FROM tasks t LEFT JOIN activities i ON i.task_id = t.id GROUP BY t.id, t.title, t.status, t.risk, t.occurred_at, t.occurred_day_sp;
|
|
2305
|
-
CREATE VIEW spec_summary AS SELECT s.id, s.title, s.status, s.risk, s.occurred_at, s.occurred_day_sp, COUNT(i.run_id) AS invocation_count, SUM(i.llm_call_count) AS llm_call_count, SUM(i.duration_ms) AS total_duration, SUM(i.total_tokens) AS total_tokens, SUM(i.cost_usd) AS total_cost_usd, SUM(i.cost_brl) AS total_cost_brl FROM specs s LEFT JOIN
|
|
2456
|
+
CREATE VIEW spec_summary AS SELECT s.id, s.title, s.status, s.risk, s.occurred_at, s.occurred_day_sp, COUNT(i.run_id) AS invocation_count, SUM(i.llm_call_count) AS llm_call_count, SUM(i.duration_ms) AS total_duration, SUM(i.total_tokens) AS total_tokens, SUM(i.cost_usd) AS total_cost_usd, SUM(i.cost_brl) AS total_cost_brl FROM specs s LEFT JOIN macro_spec_activities i ON i.spec_id = s.id GROUP BY s.id, s.title, s.status, s.risk, s.occurred_at, s.occurred_day_sp;
|
|
2306
2457
|
CREATE VIEW prd_summary AS SELECT p.id, p.title, p.status, p.risk, p.occurred_at, p.occurred_day_sp, COUNT(i.run_id) AS invocation_count, SUM(i.llm_call_count) AS llm_call_count, SUM(i.duration_ms) AS total_duration, SUM(i.total_tokens) AS total_tokens, SUM(i.cost_usd) AS total_cost_usd, SUM(i.cost_brl) AS total_cost_brl FROM prds p LEFT JOIN activities i ON i.prd_id = p.id GROUP BY p.id, p.title, p.status, p.risk, p.occurred_at, p.occurred_day_sp;
|
|
2307
2458
|
CREATE VIEW cost_monitor_group_daily AS SELECT * FROM cost_monitor_group_daily_cache;
|
|
2308
2459
|
CREATE VIEW vw_overview AS SELECT * FROM overview;
|
|
@@ -2426,7 +2577,7 @@ export async function rebuildSqliteReadModel(
|
|
|
2426
2577
|
CREATE VIEW vw_cost_models AS WITH usage AS (SELECT model, agent_invocation_id, SUM(total_tokens) AS total_tokens, SUM(cost_usd) AS cost_usd, SUM(cost_brl) AS cost_brl FROM invocation_usage GROUP BY model, agent_invocation_id), calls AS (SELECT i.agent_invocation_id, CASE WHEN (SELECT COUNT(DISTINCT m.model) FROM invocation_usage m WHERE m.agent_invocation_id = i.agent_invocation_id AND m.model IS NOT NULL) = 1 THEN i.llm_call_count END AS llm_call_count FROM invocations i) SELECT u.model, SUM(c.llm_call_count) AS llm_call_count, SUM(u.total_tokens) AS total_tokens, SUM(u.cost_usd) AS cost_usd, SUM(u.cost_brl) AS cost_brl, COUNT(*) AS invocation_count FROM usage u JOIN calls c USING(agent_invocation_id) GROUP BY u.model;
|
|
2427
2578
|
CREATE VIEW vw_cost_providers AS WITH usage AS (SELECT provider, agent_invocation_id, SUM(total_tokens) AS total_tokens, SUM(cost_usd) AS cost_usd, SUM(cost_brl) AS cost_brl FROM invocation_usage GROUP BY provider, agent_invocation_id), calls AS (SELECT i.agent_invocation_id, CASE WHEN (SELECT COUNT(DISTINCT p.provider) FROM invocation_usage p WHERE p.agent_invocation_id = i.agent_invocation_id AND p.provider IS NOT NULL) = 1 THEN i.llm_call_count END AS llm_call_count FROM invocations i) SELECT u.provider, SUM(c.llm_call_count) AS llm_call_count, SUM(u.total_tokens) AS total_tokens, SUM(u.cost_usd) AS cost_usd, SUM(u.cost_brl) AS cost_brl, COUNT(*) AS invocation_count FROM usage u JOIN calls c USING(agent_invocation_id) GROUP BY u.provider;
|
|
2428
2579
|
CREATE VIEW vw_cost_tasks AS SELECT i.task_id, COALESCE(MAX(t.title), i.task_id) AS title, MAX(t.status) AS status, MAX(t.risk) AS risk, MIN(i.occurred_at) AS occurred_at, MIN(i.occurred_day_sp) AS occurred_day_sp, SUM(i.llm_call_count) AS llm_call_count, SUM(i.duration_ms) AS total_duration, SUM(i.total_tokens) AS total_tokens, SUM(i.cost_usd) AS total_cost_usd, SUM(i.cost_brl) AS total_cost_brl, COUNT(*) AS invocation_count FROM invocations i LEFT JOIN tasks t ON t.id = i.task_id WHERE i.task_id IS NOT NULL GROUP BY i.task_id;
|
|
2429
|
-
CREATE VIEW vw_cost_specs AS SELECT s.id AS spec_id, s.title, s.status, s.risk, COALESCE(MIN(i.occurred_at), s.occurred_at) AS occurred_at, COALESCE(MIN(i.occurred_day_sp), s.occurred_day_sp) AS occurred_day_sp, SUM(i.llm_call_count) AS llm_call_count, SUM(i.duration_ms) AS total_duration, SUM(i.total_tokens) AS total_tokens, SUM(i.cost_usd) AS total_cost_usd, SUM(i.cost_brl) AS total_cost_brl, COUNT(i.
|
|
2580
|
+
CREATE VIEW vw_cost_specs AS SELECT s.id AS spec_id, s.title, s.status, s.risk, COALESCE(MIN(i.occurred_at), s.occurred_at) AS occurred_at, COALESCE(MIN(i.occurred_day_sp), s.occurred_day_sp) AS occurred_day_sp, SUM(i.llm_call_count) AS llm_call_count, SUM(i.duration_ms) AS total_duration, SUM(i.total_tokens) AS total_tokens, SUM(i.cost_usd) AS total_cost_usd, SUM(i.cost_brl) AS total_cost_brl, COUNT(i.run_id) AS invocation_count FROM specs s LEFT JOIN macro_spec_activities i ON i.spec_id = s.id GROUP BY s.id, s.title, s.status, s.risk, s.occurred_at, s.occurred_day_sp;
|
|
2430
2581
|
CREATE VIEW vw_cost_prds AS SELECT p.id AS prd_id, p.title, p.status, p.risk, COALESCE(MIN(i.occurred_at), p.occurred_at) AS occurred_at, COALESCE(MIN(i.occurred_day_sp), p.occurred_day_sp) AS occurred_day_sp, SUM(i.llm_call_count) AS llm_call_count, SUM(i.duration_ms) AS total_duration, SUM(i.total_tokens) AS total_tokens, SUM(i.cost_usd) AS total_cost_usd, SUM(i.cost_brl) AS total_cost_brl, COUNT(i.agent_invocation_id) AS invocation_count FROM prds p LEFT JOIN invocations i ON i.prd_id = p.id GROUP BY p.id, p.title, p.status, p.risk, p.occurred_at, p.occurred_day_sp;
|
|
2431
2582
|
CREATE VIEW vw_cost_conversations AS SELECT i.conversation_id, i.conversation_id AS title, MAX(c.status) AS status, NULL AS risk, MIN(i.occurred_at) AS occurred_at, MIN(i.occurred_day_sp) AS occurred_day_sp, SUM(i.llm_call_count) AS llm_call_count, SUM(i.duration_ms) AS total_duration, SUM(i.total_tokens) AS total_tokens, SUM(i.cost_usd) AS total_cost_usd, SUM(i.cost_brl) AS total_cost_brl, COUNT(*) AS invocation_count FROM invocations i LEFT JOIN conversations c ON c.id = i.conversation_id WHERE i.conversation_id IS NOT NULL GROUP BY i.conversation_id;
|
|
2432
2583
|
CREATE VIEW vw_operations_work_list AS SELECT * FROM vw_entities;
|
|
@@ -2449,7 +2600,7 @@ export async function rebuildSqliteReadModel(
|
|
|
2449
2600
|
GROUP BY spec_id, prd_id
|
|
2450
2601
|
), entity_activity AS (
|
|
2451
2602
|
SELECT 'task' AS entity_kind, task_id AS entity_id, * FROM activities WHERE task_id IS NOT NULL
|
|
2452
|
-
UNION ALL SELECT 'spec', spec_id, * FROM
|
|
2603
|
+
UNION ALL SELECT 'spec', spec_id, * FROM macro_spec_activities
|
|
2453
2604
|
UNION ALL SELECT 'prd', prd_id, * FROM activities WHERE prd_id IS NOT NULL
|
|
2454
2605
|
UNION ALL
|
|
2455
2606
|
SELECT 'task', l.task_id, a.*
|
|
@@ -2688,7 +2839,7 @@ export async function rebuildSqliteReadModel(
|
|
|
2688
2839
|
SUM(total_tokens) AS total_tokens,
|
|
2689
2840
|
SUM(cost_usd) AS total_cost_usd,
|
|
2690
2841
|
SUM(cost_brl) AS total_cost_brl
|
|
2691
|
-
FROM
|
|
2842
|
+
FROM macro_spec_activities GROUP BY spec_id
|
|
2692
2843
|
)
|
|
2693
2844
|
SELECT COUNT(*) AS entity_count,
|
|
2694
2845
|
SUM(usage.total_run_count) AS total_run_count,
|
|
@@ -2733,7 +2884,7 @@ export async function rebuildSqliteReadModel(
|
|
|
2733
2884
|
SUM(total_tokens) AS total_tokens,
|
|
2734
2885
|
SUM(cost_usd) AS total_cost_usd,
|
|
2735
2886
|
SUM(cost_brl) AS total_cost_brl
|
|
2736
|
-
FROM
|
|
2887
|
+
FROM macro_spec_activities GROUP BY spec_id
|
|
2737
2888
|
), ranked AS (
|
|
2738
2889
|
SELECT s.id AS entity_id, s.title, s.status, usage.total_run_count, usage.llm_call_count,
|
|
2739
2890
|
usage.total_duration_ms, usage.total_tokens, usage.total_cost_usd, usage.total_cost_brl,
|
|
@@ -2864,6 +3015,67 @@ export async function rebuildSqliteReadModel(
|
|
|
2864
3015
|
},
|
|
2865
3016
|
);
|
|
2866
3017
|
database.exec(deduplicatedViewSql);
|
|
3018
|
+
// One row per stable GitHub account. Costs are summed from the single
|
|
3019
|
+
// canonical Run fact, while delivery is joined exclusively through the
|
|
3020
|
+
// Task's completed_run_id (never inferred from a branch or latest Run).
|
|
3021
|
+
database.exec(`
|
|
3022
|
+
CREATE TABLE developer_cost_projection AS
|
|
3023
|
+
WITH completed AS (
|
|
3024
|
+
SELECT a.developer_account_id, COUNT(DISTINCT w.id) AS completed_task_count
|
|
3025
|
+
FROM zaip_vw_canonical_work_items w
|
|
3026
|
+
JOIN zaip_vw_activities a ON a.run_id=w.completed_run_id
|
|
3027
|
+
WHERE w.entity_kind='task' AND w.status='completed'
|
|
3028
|
+
AND a.developer_account_id IS NOT NULL
|
|
3029
|
+
GROUP BY a.developer_account_id
|
|
3030
|
+
)
|
|
3031
|
+
SELECT a.developer_account_id AS account_id,
|
|
3032
|
+
MAX(a.developer_login) AS login_snapshot,
|
|
3033
|
+
CASE WHEN COUNT(DISTINCT a.developer_attribution_source)=1 THEN MAX(a.developer_attribution_source) ELSE 'mixed' END AS attribution_source,
|
|
3034
|
+
COUNT(*) AS run_count,
|
|
3035
|
+
SUM(a.source_event_count) AS source_event_count,
|
|
3036
|
+
SUM(a.total_tokens) AS total_tokens,
|
|
3037
|
+
SUM(a.cost_usd) AS total_cost_usd,
|
|
3038
|
+
SUM(a.cost_brl) AS total_cost_brl,
|
|
3039
|
+
COUNT(a.total_tokens) AS token_covered_runs,
|
|
3040
|
+
COUNT(a.cost_usd) AS usd_covered_runs,
|
|
3041
|
+
COUNT(a.cost_brl) AS brl_covered_runs,
|
|
3042
|
+
COALESCE(completed.completed_task_count, 0) AS completed_task_count
|
|
3043
|
+
FROM zaip_vw_activities a LEFT JOIN completed USING(developer_account_id)
|
|
3044
|
+
WHERE a.developer_account_id IS NOT NULL
|
|
3045
|
+
GROUP BY a.developer_account_id
|
|
3046
|
+
ORDER BY total_cost_brl DESC, account_id;
|
|
3047
|
+
CREATE INDEX developer_cost_projection_lookup ON developer_cost_projection(account_id);
|
|
3048
|
+
`);
|
|
3049
|
+
materializeCostGroupDailyProjection(database);
|
|
3050
|
+
materializeOperationsWorkProjection(database);
|
|
3051
|
+
// Persist the complete unfiltered invocation projection while the seed
|
|
3052
|
+
// still owns the compact canonical facts. Runtime pages can then slice
|
|
3053
|
+
// 10/100 rows without expanding the pricing view for every request.
|
|
3054
|
+
const costAgentInvocationProjection = database
|
|
3055
|
+
.prepare(
|
|
3056
|
+
`SELECT * FROM zaip_vw_cost_monitor_agent_invocations
|
|
3057
|
+
ORDER BY occurred_at DESC, agent_invocation_id DESC`,
|
|
3058
|
+
)
|
|
3059
|
+
.all() as Array<Record<string, unknown>>;
|
|
3060
|
+
const insertCostAgentInvocationProjection = database.prepare(
|
|
3061
|
+
`INSERT INTO zaip_aios_activities(
|
|
3062
|
+
activity_row_id, activity_source_kind, activity_key, source_path, source_hash,
|
|
3063
|
+
recorded_at, occurred_day_sp, run_role, root_run_id, payload_type, payload_json
|
|
3064
|
+
) VALUES (?, 'technical_payload', ?, 'sqlite://cost_agent_invocation_projection_v1', '',
|
|
3065
|
+
datetime('now'), ?, 'technical', 'technical', 'cost_agent_invocation_projection_v1', ?)`,
|
|
3066
|
+
);
|
|
3067
|
+
database.exec("BEGIN;");
|
|
3068
|
+
for (const row of costAgentInvocationProjection) {
|
|
3069
|
+
const invocationId = String(row.agent_invocation_id ?? "");
|
|
3070
|
+
if (invocationId.length === 0) continue;
|
|
3071
|
+
insertCostAgentInvocationProjection.run(
|
|
3072
|
+
`TECH-${randomUUID().toLowerCase()}`,
|
|
3073
|
+
`cost_agent_invocation_projection_v1:${invocationId}`,
|
|
3074
|
+
typeof row.occurred_day_sp === "string" ? row.occurred_day_sp : null,
|
|
3075
|
+
JSON.stringify(row),
|
|
3076
|
+
);
|
|
3077
|
+
}
|
|
3078
|
+
database.exec("COMMIT;");
|
|
2867
3079
|
reportLaunchProgress(
|
|
2868
3080
|
"projection_finalize",
|
|
2869
3081
|
80,
|
|
@@ -2877,62 +3089,92 @@ export async function rebuildSqliteReadModel(
|
|
|
2877
3089
|
} finally {
|
|
2878
3090
|
database.close();
|
|
2879
3091
|
}
|
|
2880
|
-
try {
|
|
2881
|
-
await rename(temporaryPath, cachePath);
|
|
2882
|
-
} catch (error: unknown) {
|
|
2883
|
-
// Another launcher may have built the same immutable cache entry while
|
|
2884
|
-
// this process was reading. It is safe only if its fingerprint agrees.
|
|
2885
|
-
if (readSnapshotFileCount(cachePath, fingerprint) === null) {
|
|
2886
|
-
await rm(temporaryPath, { force: true });
|
|
2887
|
-
throw error;
|
|
2888
|
-
}
|
|
2889
|
-
await rm(temporaryPath, { force: true });
|
|
2890
|
-
}
|
|
2891
3092
|
const materializeStartedAt = performance.now();
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
3093
|
+
const timed = <T>(phase: string, action: () => T) =>
|
|
3094
|
+
measureFirstFold(
|
|
3095
|
+
process.env.ZAIP_DASHBOARD_LAUNCH_PROGRESS === "1",
|
|
3096
|
+
reportLaunchProgress,
|
|
3097
|
+
phase,
|
|
3098
|
+
action,
|
|
3099
|
+
);
|
|
3100
|
+
// The shared cache remains private until every expensive first-fold payload
|
|
3101
|
+
// has been committed. A concurrent launcher may freely use `path`, its own
|
|
3102
|
+
// generation, but must never copy a half-published cache seed.
|
|
3103
|
+
await materializeSnapshot(temporaryPath, path);
|
|
2896
3104
|
const firstFoldFilesystem = new SqliteReadModelFileSystem(workspaceRoot);
|
|
2897
|
-
const firstFoldViewport =
|
|
3105
|
+
const firstFoldViewport = timed("first_fold_viewport", () =>
|
|
3106
|
+
firstFoldFilesystem.firstFoldViewportAllTime(),
|
|
3107
|
+
);
|
|
3108
|
+
const firstFoldAllTime = timed("first_fold_all_time", () =>
|
|
3109
|
+
firstFoldFilesystem.firstFoldAllTime(),
|
|
3110
|
+
);
|
|
3111
|
+
const firstFoldAllTimeBoard = timed("first_fold_board", () =>
|
|
3112
|
+
firstFoldFilesystem.firstFoldBoardAllTime(),
|
|
3113
|
+
);
|
|
3114
|
+
const costMonitorSummaryAllTime = timed("first_fold_cost_summary", () =>
|
|
3115
|
+
firstFoldFilesystem.costMonitorSummaryAggregate({ from: null, to: null }),
|
|
3116
|
+
);
|
|
2898
3117
|
const quickMonth = saoPauloCurrentMonth();
|
|
3118
|
+
const costMonitorSummaryCurrentMonth = timed("first_fold_cost_summary_month", () =>
|
|
3119
|
+
firstFoldFilesystem.costMonitorSummaryAggregate(quickMonth),
|
|
3120
|
+
);
|
|
2899
3121
|
const firstFoldMonthlyQuick: SqliteFirstFoldMonthlyQuick = {
|
|
2900
3122
|
from_day: quickMonth.from,
|
|
2901
3123
|
to_day: quickMonth.to,
|
|
2902
|
-
source_latest_day:
|
|
2903
|
-
|
|
3124
|
+
source_latest_day: timed("first_fold_latest_day", () =>
|
|
3125
|
+
firstFoldFilesystem.firstFoldRelevantLatestDay(),
|
|
3126
|
+
),
|
|
3127
|
+
aggregate: timed("first_fold_month_aggregate", () =>
|
|
3128
|
+
firstFoldFilesystem.firstFoldAggregate(quickMonth),
|
|
3129
|
+
),
|
|
2904
3130
|
pages: {
|
|
2905
|
-
task:
|
|
2906
|
-
|
|
2907
|
-
|
|
3131
|
+
task: operationsWorkRowsInRange(
|
|
3132
|
+
firstFoldAllTimeBoard.pages.task,
|
|
3133
|
+
quickMonth.from,
|
|
3134
|
+
quickMonth.to,
|
|
3135
|
+
),
|
|
3136
|
+
spec: operationsWorkRowsInRange(
|
|
3137
|
+
firstFoldAllTimeBoard.pages.spec,
|
|
3138
|
+
quickMonth.from,
|
|
3139
|
+
quickMonth.to,
|
|
3140
|
+
),
|
|
3141
|
+
prd: operationsWorkRowsInRange(
|
|
3142
|
+
firstFoldAllTimeBoard.pages.prd,
|
|
3143
|
+
quickMonth.from,
|
|
3144
|
+
quickMonth.to,
|
|
3145
|
+
),
|
|
2908
3146
|
},
|
|
2909
|
-
top_tasks: firstFoldFilesystem.topTasks(quickMonth),
|
|
3147
|
+
top_tasks: timed("first_fold_month_top_tasks", () => firstFoldFilesystem.topTasks(quickMonth)),
|
|
2910
3148
|
};
|
|
2911
3149
|
firstFoldFilesystem.close();
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
3150
|
+
persistFirstFoldPayloads([temporaryPath, path], {
|
|
3151
|
+
first_fold_viewport_cache: JSON.stringify(firstFoldViewport),
|
|
3152
|
+
first_fold_all_time_cache: JSON.stringify(firstFoldAllTime),
|
|
3153
|
+
first_fold_all_time_board_cache: JSON.stringify(firstFoldAllTimeBoard),
|
|
3154
|
+
cost_monitor_summary_cache: JSON.stringify(costMonitorSummaryAllTime),
|
|
3155
|
+
cost_monitor_summary_month_cache: JSON.stringify({
|
|
3156
|
+
...quickMonth,
|
|
3157
|
+
aggregate: costMonitorSummaryCurrentMonth,
|
|
3158
|
+
}),
|
|
3159
|
+
first_fold_monthly_quick_cache: JSON.stringify(firstFoldMonthlyQuick),
|
|
3160
|
+
});
|
|
3161
|
+
try {
|
|
3162
|
+
await rename(temporaryPath, cachePath);
|
|
3163
|
+
} catch (error: unknown) {
|
|
3164
|
+
// A concurrent builder wins only when it published a complete seed.
|
|
3165
|
+
if (
|
|
3166
|
+
readSnapshotFileCount(
|
|
3167
|
+
cachePath,
|
|
3168
|
+
fingerprint,
|
|
3169
|
+
readModelSchemaVersion,
|
|
3170
|
+
hasRequiredFirstFoldPayloads,
|
|
3171
|
+
) === null
|
|
3172
|
+
) {
|
|
3173
|
+
await rm(temporaryPath, { force: true });
|
|
3174
|
+
throw error;
|
|
2935
3175
|
}
|
|
3176
|
+
await rm(temporaryPath, { force: true });
|
|
3177
|
+
await materializeSnapshot(cachePath, path);
|
|
2936
3178
|
}
|
|
2937
3179
|
reportLaunchProgress("snapshot_ready", 86, "A cópia privada do banco local está pronta.");
|
|
2938
3180
|
validateSqliteReadModelSchema(path);
|
|
@@ -2946,174 +3188,6 @@ export async function rebuildSqliteReadModel(
|
|
|
2946
3188
|
};
|
|
2947
3189
|
}
|
|
2948
3190
|
|
|
2949
|
-
async function pruneSnapshotCache(directory: string, activePath: string): Promise<void> {
|
|
2950
|
-
const entries = await readdir(directory, { withFileTypes: true }).catch(() => []);
|
|
2951
|
-
const stale = await Promise.all(
|
|
2952
|
-
entries
|
|
2953
|
-
.filter(
|
|
2954
|
-
(entry) =>
|
|
2955
|
-
entry.isFile() &&
|
|
2956
|
-
entry.name.endsWith(".sqlite") &&
|
|
2957
|
-
resolve(directory, entry.name) !== activePath,
|
|
2958
|
-
)
|
|
2959
|
-
.map(async (entry) => ({
|
|
2960
|
-
path: resolve(directory, entry.name),
|
|
2961
|
-
mtime: (await stat(resolve(directory, entry.name))).mtimeMs,
|
|
2962
|
-
})),
|
|
2963
|
-
);
|
|
2964
|
-
// Keep the most recent prior seed for a concurrent launcher; never remove tmp
|
|
2965
|
-
// files, which may be an in-progress materialization by another process.
|
|
2966
|
-
for (const entry of stale.sort((a, b) => b.mtime - a.mtime).slice(1))
|
|
2967
|
-
await rm(entry.path, { force: true }).catch(() => {});
|
|
2968
|
-
// Interrupted SQLite builds leave only cache-local temporary files. They
|
|
2969
|
-
// cannot be valid immutable seeds and otherwise inflate the launch folder.
|
|
2970
|
-
for (const entry of entries) {
|
|
2971
|
-
if (!entry.isFile() || !entry.name.includes(".sqlite.") || !entry.name.includes(".tmp"))
|
|
2972
|
-
continue;
|
|
2973
|
-
const candidate = resolve(directory, entry.name);
|
|
2974
|
-
const age = Date.now() - (await stat(candidate)).mtimeMs;
|
|
2975
|
-
if (age > 60_000) await rm(candidate, { force: true }).catch(() => {});
|
|
2976
|
-
}
|
|
2977
|
-
}
|
|
2978
|
-
|
|
2979
|
-
function readSnapshotFileCount(path: string, fingerprint: string): number | null {
|
|
2980
|
-
try {
|
|
2981
|
-
const database = new DatabaseSync(path, { readOnly: true });
|
|
2982
|
-
const previous = (
|
|
2983
|
-
database
|
|
2984
|
-
.prepare("SELECT value FROM zaip_vw_read_model_metadata WHERE key = ?")
|
|
2985
|
-
.get("source_fingerprint") as { value?: string } | undefined
|
|
2986
|
-
)?.value;
|
|
2987
|
-
const schema = (
|
|
2988
|
-
database
|
|
2989
|
-
.prepare("SELECT value FROM zaip_vw_read_model_metadata WHERE key = ?")
|
|
2990
|
-
.get("schema_version") as { value?: string } | undefined
|
|
2991
|
-
)?.value;
|
|
2992
|
-
const files = Number(
|
|
2993
|
-
(
|
|
2994
|
-
database
|
|
2995
|
-
.prepare("SELECT value FROM zaip_vw_read_model_metadata WHERE key = ?")
|
|
2996
|
-
.get("source_file_count") as { value?: string } | undefined
|
|
2997
|
-
)?.value ?? 0,
|
|
2998
|
-
);
|
|
2999
|
-
database.close();
|
|
3000
|
-
return previous === fingerprint && schema === readModelSchemaVersion ? files : null;
|
|
3001
|
-
} catch {
|
|
3002
|
-
return null;
|
|
3003
|
-
}
|
|
3004
|
-
}
|
|
3005
|
-
|
|
3006
|
-
async function mostRecentCompatibleSnapshot(
|
|
3007
|
-
directory: string,
|
|
3008
|
-
): Promise<{ path: string; files: number } | null> {
|
|
3009
|
-
const entries = await readdir(directory, { withFileTypes: true }).catch(() => []);
|
|
3010
|
-
const candidates = await Promise.all(
|
|
3011
|
-
entries
|
|
3012
|
-
.filter((entry) => entry.isFile() && entry.name.endsWith(".sqlite"))
|
|
3013
|
-
.map(async (entry) => ({
|
|
3014
|
-
path: join(directory, entry.name),
|
|
3015
|
-
mtime: (await stat(join(directory, entry.name))).mtimeMs,
|
|
3016
|
-
})),
|
|
3017
|
-
);
|
|
3018
|
-
for (const candidate of candidates.sort((left, right) => right.mtime - left.mtime)) {
|
|
3019
|
-
const files = readCompatibleSnapshotFileCount(candidate.path);
|
|
3020
|
-
if (files !== null) return { path: candidate.path, files };
|
|
3021
|
-
}
|
|
3022
|
-
return null;
|
|
3023
|
-
}
|
|
3024
|
-
|
|
3025
|
-
function readCompatibleSnapshotFileCount(path: string): number | null {
|
|
3026
|
-
try {
|
|
3027
|
-
const database = new DatabaseSync(path, { readOnly: true });
|
|
3028
|
-
const schema = (
|
|
3029
|
-
database
|
|
3030
|
-
.prepare("SELECT value FROM zaip_vw_read_model_metadata WHERE key = ?")
|
|
3031
|
-
.get("schema_version") as { value?: string } | undefined
|
|
3032
|
-
)?.value;
|
|
3033
|
-
const files = Number(
|
|
3034
|
-
(
|
|
3035
|
-
database
|
|
3036
|
-
.prepare("SELECT value FROM zaip_vw_read_model_metadata WHERE key = ?")
|
|
3037
|
-
.get("source_file_count") as { value?: string } | undefined
|
|
3038
|
-
)?.value ?? 0,
|
|
3039
|
-
);
|
|
3040
|
-
database.close();
|
|
3041
|
-
return schema === readModelSchemaVersion ? files : null;
|
|
3042
|
-
} catch {
|
|
3043
|
-
return null;
|
|
3044
|
-
}
|
|
3045
|
-
}
|
|
3046
|
-
|
|
3047
|
-
/**
|
|
3048
|
-
* The persisted schema exposes only zaip_vw_* names. The local runtime keeps
|
|
3049
|
-
* concise historical names as connection-local TEMP aliases while callers are
|
|
3050
|
-
* migrated; they are never written to the database file.
|
|
3051
|
-
*/
|
|
3052
|
-
function installLegacyViewAliases(database: DatabaseSync): void {
|
|
3053
|
-
const views = database
|
|
3054
|
-
.prepare("SELECT name FROM sqlite_master WHERE type = 'view' AND name LIKE 'zaip_vw_%'")
|
|
3055
|
-
.all() as Array<{ name: string }>;
|
|
3056
|
-
for (const { name } of views) {
|
|
3057
|
-
const alias = name.slice("zaip_vw_".length);
|
|
3058
|
-
const quoted = (identifier: string) => `"${identifier.replaceAll('"', '""')}"`;
|
|
3059
|
-
database.exec(`CREATE TEMP VIEW ${quoted(alias)} AS SELECT * FROM main.${quoted(name)}`);
|
|
3060
|
-
}
|
|
3061
|
-
}
|
|
3062
|
-
|
|
3063
|
-
async function materializeSnapshot(source: string, destination: string): Promise<void> {
|
|
3064
|
-
await mkdir(dirname(destination), { recursive: true });
|
|
3065
|
-
const temporaryPath = `${destination}.${process.pid}.${randomUUID()}.tmp`;
|
|
3066
|
-
await copyFile(source, temporaryPath);
|
|
3067
|
-
try {
|
|
3068
|
-
await rename(temporaryPath, destination);
|
|
3069
|
-
} catch (error) {
|
|
3070
|
-
await rm(temporaryPath, { force: true });
|
|
3071
|
-
throw error;
|
|
3072
|
-
}
|
|
3073
|
-
}
|
|
3074
|
-
|
|
3075
|
-
export type SqliteReadModelSourceCandidate = {
|
|
3076
|
-
path: string;
|
|
3077
|
-
source: string;
|
|
3078
|
-
mtimeMs: number;
|
|
3079
|
-
size: number;
|
|
3080
|
-
};
|
|
3081
|
-
|
|
3082
|
-
export async function sourceFingerprint(workspaceRoot: string): Promise<{
|
|
3083
|
-
fingerprint: string;
|
|
3084
|
-
candidates: SqliteReadModelSourceCandidate[];
|
|
3085
|
-
}> {
|
|
3086
|
-
const candidates = (
|
|
3087
|
-
await Promise.all(
|
|
3088
|
-
[".agents/records", ".agents/config"].map((root) => listFiles(resolve(workspaceRoot, root))),
|
|
3089
|
-
)
|
|
3090
|
-
)
|
|
3091
|
-
.flat()
|
|
3092
|
-
.filter(
|
|
3093
|
-
(path) =>
|
|
3094
|
-
textExtensions.has(path.slice(path.lastIndexOf(".")).toLowerCase()) &&
|
|
3095
|
-
isDashboardCanonicalSource(relative(workspaceRoot, path).replaceAll("\\", "/")),
|
|
3096
|
-
)
|
|
3097
|
-
.sort();
|
|
3098
|
-
const candidatesWithMetadata = await Promise.all(
|
|
3099
|
-
candidates.map(async (candidate) => {
|
|
3100
|
-
const metadata = await stat(candidate);
|
|
3101
|
-
return {
|
|
3102
|
-
path: candidate,
|
|
3103
|
-
source: relative(workspaceRoot, candidate).replaceAll("\\", "/"),
|
|
3104
|
-
mtimeMs: Math.trunc(metadata.mtimeMs),
|
|
3105
|
-
size: metadata.size,
|
|
3106
|
-
};
|
|
3107
|
-
}),
|
|
3108
|
-
);
|
|
3109
|
-
return {
|
|
3110
|
-
fingerprint: candidatesWithMetadata
|
|
3111
|
-
.map((candidate) => `${candidate.source}:${candidate.mtimeMs}:${candidate.size}`)
|
|
3112
|
-
.join("|"),
|
|
3113
|
-
candidates: candidatesWithMetadata,
|
|
3114
|
-
};
|
|
3115
|
-
}
|
|
3116
|
-
|
|
3117
3191
|
export type SqliteFirstFoldAggregate = {
|
|
3118
3192
|
days: number;
|
|
3119
3193
|
total_run_count: number;
|
|
@@ -3124,75 +3198,17 @@ export type SqliteFirstFoldAggregate = {
|
|
|
3124
3198
|
cost_brl: number | null;
|
|
3125
3199
|
execution_day_count: number;
|
|
3126
3200
|
};
|
|
3201
|
+
export type {
|
|
3202
|
+
SqliteFirstFoldAllTime,
|
|
3203
|
+
SqliteFirstFoldAllTimeBoard,
|
|
3204
|
+
SqliteFirstFoldMonthlyQuick,
|
|
3205
|
+
} from "./sqlite-read-model-first-fold-types.js";
|
|
3127
3206
|
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
completed_task_count: number;
|
|
3134
|
-
spec_count: number;
|
|
3135
|
-
completed_spec_count: number;
|
|
3136
|
-
prd_count: number;
|
|
3137
|
-
completed_prd_count: number;
|
|
3138
|
-
total_cost_usd: number | null;
|
|
3139
|
-
latest_task_created_at: string | null;
|
|
3140
|
-
};
|
|
3141
|
-
task_status_counts: Record<string, number>;
|
|
3142
|
-
task_risk_counts: Record<string, number>;
|
|
3143
|
-
task_metrics: {
|
|
3144
|
-
average_duration_ms_per_task: number | null;
|
|
3145
|
-
average_tokens_per_task: number | null;
|
|
3146
|
-
average_cost_usd_per_task: number | null;
|
|
3147
|
-
average_cost_brl_per_task: number | null;
|
|
3148
|
-
latest_task_created_at: string | null;
|
|
3149
|
-
};
|
|
3150
|
-
top_tasks: Array<{
|
|
3151
|
-
rank: number;
|
|
3152
|
-
task_id: string;
|
|
3153
|
-
title: string;
|
|
3154
|
-
status: string | null;
|
|
3155
|
-
risk: string | null;
|
|
3156
|
-
total_run_count: number;
|
|
3157
|
-
llm_call_count: number | null;
|
|
3158
|
-
total_duration_ms: number;
|
|
3159
|
-
total_tokens: number | null;
|
|
3160
|
-
total_cost_usd: number | null;
|
|
3161
|
-
total_cost_brl: number | null;
|
|
3162
|
-
duration_share: number;
|
|
3163
|
-
}>;
|
|
3164
|
-
macro_metrics: Record<
|
|
3165
|
-
"specs" | "prds",
|
|
3166
|
-
{
|
|
3167
|
-
status_counts: Record<string, number>;
|
|
3168
|
-
classification_counts: Record<string, number>;
|
|
3169
|
-
entity_count: number;
|
|
3170
|
-
total_run_count: number;
|
|
3171
|
-
total_duration_ms: number | null;
|
|
3172
|
-
total_tokens: number | null;
|
|
3173
|
-
total_cost_usd: number | null;
|
|
3174
|
-
total_cost_brl: number | null;
|
|
3175
|
-
average_duration_ms: number | null;
|
|
3176
|
-
average_tokens: number | null;
|
|
3177
|
-
average_cost_usd: number | null;
|
|
3178
|
-
average_cost_brl: number | null;
|
|
3179
|
-
latest_created_at: string | null;
|
|
3180
|
-
top_entities: Array<{
|
|
3181
|
-
rank: number;
|
|
3182
|
-
entity_id: string;
|
|
3183
|
-
title: string;
|
|
3184
|
-
status: string | null;
|
|
3185
|
-
total_run_count: number;
|
|
3186
|
-
llm_call_count: number | null;
|
|
3187
|
-
total_duration_ms: number | null;
|
|
3188
|
-
total_tokens: number | null;
|
|
3189
|
-
total_cost_usd: number | null;
|
|
3190
|
-
total_cost_brl: number | null;
|
|
3191
|
-
duration_share: number;
|
|
3192
|
-
}>;
|
|
3193
|
-
}
|
|
3194
|
-
>;
|
|
3195
|
-
};
|
|
3207
|
+
import type {
|
|
3208
|
+
SqliteFirstFoldAllTime,
|
|
3209
|
+
SqliteFirstFoldAllTimeBoard,
|
|
3210
|
+
SqliteFirstFoldMonthlyQuick,
|
|
3211
|
+
} from "./sqlite-read-model-first-fold-types.js";
|
|
3196
3212
|
|
|
3197
3213
|
/** The only all-time facts needed before the Operations first fold is painted. */
|
|
3198
3214
|
export type SqliteFirstFoldViewport = Pick<
|
|
@@ -3306,51 +3322,24 @@ export type SqliteSummaryPage = {
|
|
|
3306
3322
|
* immutable seed is built, rather than competing with the browser after the
|
|
3307
3323
|
* launcher reports ready.
|
|
3308
3324
|
*/
|
|
3309
|
-
export type SqliteFirstFoldMonthlyQuick = {
|
|
3310
|
-
from_day: string;
|
|
3311
|
-
to_day: string;
|
|
3312
|
-
source_latest_day: string | null;
|
|
3313
|
-
aggregate: SqliteFirstFoldAggregate;
|
|
3314
|
-
pages: Record<SqliteSummaryKind, Array<Record<string, unknown>>>;
|
|
3315
|
-
top_tasks: Array<{
|
|
3316
|
-
task_id: string;
|
|
3317
|
-
total_tokens: number | null;
|
|
3318
|
-
cost_brl: number | null;
|
|
3319
|
-
run_count: number;
|
|
3320
|
-
duration_ms: number;
|
|
3321
|
-
}>;
|
|
3322
|
-
};
|
|
3323
|
-
|
|
3324
|
-
async function listFiles(directory: string): Promise<string[]> {
|
|
3325
|
-
const entries = await readdir(directory, { withFileTypes: true }).catch(() => []);
|
|
3326
|
-
const paths = await Promise.all(
|
|
3327
|
-
entries.map(async (entry) => {
|
|
3328
|
-
const candidate = join(directory, entry.name);
|
|
3329
|
-
return entry.isDirectory() ? listFiles(candidate) : entry.isFile() ? [candidate] : [];
|
|
3330
|
-
}),
|
|
3331
|
-
);
|
|
3332
|
-
return paths.flat();
|
|
3333
|
-
}
|
|
3334
|
-
|
|
3335
3325
|
/** Read-only filesystem backed by the regenerated SQLite snapshot. */
|
|
3336
3326
|
export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
3337
3327
|
private readonly executionDurationCache = new Map<string, Map<string, number>>();
|
|
3338
3328
|
private dailyAggregateRowsCache: Array<Record<string, number | string | null>> | null = null;
|
|
3339
3329
|
private firstFoldAllTimeCache: SqliteFirstFoldAllTime | null = null;
|
|
3330
|
+
private firstFoldAllTimeBoardCache: SqliteFirstFoldAllTimeBoard | null = null;
|
|
3340
3331
|
private firstFoldTopTasksAllTimeCache: SqliteFirstFoldAllTime["top_tasks"] | null = null;
|
|
3341
3332
|
private firstFoldViewportCache: SqliteFirstFoldViewport | null = null;
|
|
3342
3333
|
private firstFoldMonthlyQuickCache: SqliteFirstFoldMonthlyQuick | null | undefined = undefined;
|
|
3343
3334
|
private firstFoldViewportTimings: Record<string, number> = {};
|
|
3344
3335
|
private metadataValues: Map<string, string> | null = null;
|
|
3345
3336
|
private readonly unfilteredViewPageCache = new Map<string, Array<Record<string, unknown>>>();
|
|
3337
|
+
private readonly technicalPayloadRowsCache = new Map<string, Array<Record<string, unknown>>>();
|
|
3346
3338
|
private readonly viewPageResultCache = new Map<string, Record<string, unknown>>();
|
|
3347
|
-
//
|
|
3348
|
-
//
|
|
3349
|
-
//
|
|
3350
|
-
|
|
3351
|
-
// view and all filtered paths remain the compatibility seam.
|
|
3352
|
-
private operationsWorkListPageCache: Array<Record<string, unknown>> | null = null;
|
|
3353
|
-
|
|
3339
|
+
// Summary cards and temporal Cost tables consume the same aggregate. Keep
|
|
3340
|
+
// exact filter results inside the immutable generation so a tab switch does
|
|
3341
|
+
// not repeat its multi-second SQL scan.
|
|
3342
|
+
private readonly costMonitorSummaryCache = new Map<string, CostSummarySource>();
|
|
3354
3343
|
private dailyAggregateRows(): Array<Record<string, number | string | null>> {
|
|
3355
3344
|
if (this.dailyAggregateRowsCache === null) {
|
|
3356
3345
|
this.dailyAggregateRowsCache = this.database
|
|
@@ -3360,13 +3349,6 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
3360
3349
|
return this.dailyAggregateRowsCache;
|
|
3361
3350
|
}
|
|
3362
3351
|
|
|
3363
|
-
private operationsWorkListRows(): Array<Record<string, unknown>> {
|
|
3364
|
-
this.operationsWorkListPageCache ??= this.database
|
|
3365
|
-
.prepare("SELECT * FROM operations_work_list ORDER BY occurred_at DESC, id DESC")
|
|
3366
|
-
.all() as Array<Record<string, unknown>>;
|
|
3367
|
-
return this.operationsWorkListPageCache;
|
|
3368
|
-
}
|
|
3369
|
-
|
|
3370
3352
|
private unfilteredViewRows(view: SqliteViewName): Array<Record<string, unknown>> {
|
|
3371
3353
|
const cached = this.unfilteredViewPageCache.get(view);
|
|
3372
3354
|
if (cached !== undefined) return cached;
|
|
@@ -3377,6 +3359,27 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
3377
3359
|
this.unfilteredViewPageCache.set(view, rows);
|
|
3378
3360
|
return rows;
|
|
3379
3361
|
}
|
|
3362
|
+
|
|
3363
|
+
private technicalPayloadRows(payloadType: string): Array<Record<string, unknown>> {
|
|
3364
|
+
const cached = this.technicalPayloadRowsCache.get(payloadType);
|
|
3365
|
+
if (cached !== undefined) return cached;
|
|
3366
|
+
const rows = (
|
|
3367
|
+
this.database
|
|
3368
|
+
.prepare("SELECT payload_json FROM zaip_aios_activities WHERE payload_type=?")
|
|
3369
|
+
.all(payloadType) as Array<{ payload_json: string }>
|
|
3370
|
+
).flatMap(({ payload_json }) => {
|
|
3371
|
+
try {
|
|
3372
|
+
const parsed = JSON.parse(payload_json) as unknown;
|
|
3373
|
+
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)
|
|
3374
|
+
? [parsed as Record<string, unknown>]
|
|
3375
|
+
: [];
|
|
3376
|
+
} catch {
|
|
3377
|
+
return [];
|
|
3378
|
+
}
|
|
3379
|
+
});
|
|
3380
|
+
this.technicalPayloadRowsCache.set(payloadType, rows);
|
|
3381
|
+
return rows;
|
|
3382
|
+
}
|
|
3380
3383
|
private database: DatabaseSync;
|
|
3381
3384
|
|
|
3382
3385
|
constructor(root: string) {
|
|
@@ -3451,6 +3454,7 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
3451
3454
|
this.executionDurationCache.clear();
|
|
3452
3455
|
this.dailyAggregateRowsCache = null;
|
|
3453
3456
|
this.firstFoldAllTimeCache = null;
|
|
3457
|
+
this.firstFoldAllTimeBoardCache = null;
|
|
3454
3458
|
this.firstFoldTopTasksAllTimeCache = null;
|
|
3455
3459
|
this.firstFoldViewportCache = null;
|
|
3456
3460
|
this.firstFoldMonthlyQuickCache = undefined;
|
|
@@ -3459,6 +3463,9 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
3459
3463
|
// Public pages belong to the opened immutable SQLite generation. Never
|
|
3460
3464
|
// carry them across refresh/rebuild, even when this filesystem instance is
|
|
3461
3465
|
// reused by the launcher.
|
|
3466
|
+
this.unfilteredViewPageCache.clear();
|
|
3467
|
+
this.technicalPayloadRowsCache.clear();
|
|
3468
|
+
this.costMonitorSummaryCache.clear();
|
|
3462
3469
|
this.viewPageResultCache.clear();
|
|
3463
3470
|
}
|
|
3464
3471
|
|
|
@@ -3647,8 +3654,11 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
3647
3654
|
days.map((day) => {
|
|
3648
3655
|
const row = rowsByDay.get(day) ?? {};
|
|
3649
3656
|
const totalRuns = Number(row.total_run_count ?? 0);
|
|
3650
|
-
const
|
|
3651
|
-
const
|
|
3657
|
+
const knownTokenRuns = Number(row.token_known_runs ?? 0);
|
|
3658
|
+
const knownCostRuns = Math.min(
|
|
3659
|
+
Number(row.usd_known_runs ?? 0),
|
|
3660
|
+
Number(row.brl_known_runs ?? 0),
|
|
3661
|
+
);
|
|
3652
3662
|
return [
|
|
3653
3663
|
day,
|
|
3654
3664
|
{
|
|
@@ -3661,11 +3671,11 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
3661
3671
|
usage: {
|
|
3662
3672
|
total_tokens: {
|
|
3663
3673
|
known_sum: Number(row.total_tokens ?? 0),
|
|
3664
|
-
unknown:
|
|
3674
|
+
unknown: Math.max(0, totalRuns - knownTokenRuns),
|
|
3665
3675
|
},
|
|
3666
3676
|
cost: {
|
|
3667
3677
|
known_sum: Number(row.total_cost_brl ?? 0),
|
|
3668
|
-
unknown:
|
|
3678
|
+
unknown: Math.max(0, totalRuns - knownCostRuns),
|
|
3669
3679
|
},
|
|
3670
3680
|
},
|
|
3671
3681
|
duration: {
|
|
@@ -3715,72 +3725,9 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
3715
3725
|
from: string | null;
|
|
3716
3726
|
to: string | null;
|
|
3717
3727
|
}): SqliteFirstFoldMonthlyQuick | null {
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
"SELECT payload_json FROM zaip_aios_activities WHERE payload_type='first_fold_monthly_quick_cache' ORDER BY recorded_at DESC LIMIT 1",
|
|
3722
|
-
)
|
|
3723
|
-
.get() as { payload_json?: string } | undefined;
|
|
3724
|
-
try {
|
|
3725
|
-
const candidate =
|
|
3726
|
-
typeof persisted?.payload_json === "string"
|
|
3727
|
-
? (JSON.parse(persisted.payload_json) as Partial<SqliteFirstFoldMonthlyQuick>)
|
|
3728
|
-
: null;
|
|
3729
|
-
this.firstFoldMonthlyQuickCache =
|
|
3730
|
-
candidate !== null &&
|
|
3731
|
-
typeof candidate.from_day === "string" &&
|
|
3732
|
-
typeof candidate.to_day === "string" &&
|
|
3733
|
-
candidate.aggregate !== undefined &&
|
|
3734
|
-
candidate.pages !== undefined &&
|
|
3735
|
-
Array.isArray(candidate.top_tasks)
|
|
3736
|
-
? (candidate as SqliteFirstFoldMonthlyQuick)
|
|
3737
|
-
: null;
|
|
3738
|
-
} catch {
|
|
3739
|
-
this.firstFoldMonthlyQuickCache = null;
|
|
3740
|
-
}
|
|
3741
|
-
}
|
|
3742
|
-
const cached = this.firstFoldMonthlyQuickCache;
|
|
3743
|
-
if (cached === null) return null;
|
|
3744
|
-
const from = filter.from?.slice(0, 10) ?? null;
|
|
3745
|
-
const to = filter.to?.slice(0, 10) ?? null;
|
|
3746
|
-
const exactRange = from === cached.from_day && to === cached.to_day;
|
|
3747
|
-
const sameMonthLaterDay =
|
|
3748
|
-
from === cached.from_day &&
|
|
3749
|
-
to !== null &&
|
|
3750
|
-
to > cached.to_day &&
|
|
3751
|
-
to.slice(0, 7) === cached.from_day.slice(0, 7);
|
|
3752
|
-
const noUncoveredFacts =
|
|
3753
|
-
cached.source_latest_day === null || cached.source_latest_day <= cached.to_day;
|
|
3754
|
-
// The payload itself is exact for its own range even if the immutable
|
|
3755
|
-
// generation also contains future-dated facts. Only an extension beyond
|
|
3756
|
-
// its end day needs the no-uncovered-facts guard.
|
|
3757
|
-
if (exactRange || (sameMonthLaterDay && noUncoveredFacts)) return cached;
|
|
3758
|
-
|
|
3759
|
-
const requestedMonthIsLater =
|
|
3760
|
-
from !== null &&
|
|
3761
|
-
to !== null &&
|
|
3762
|
-
from.slice(0, 7) >
|
|
3763
|
-
(cached.source_latest_day === null
|
|
3764
|
-
? cached.from_day.slice(0, 7)
|
|
3765
|
-
: cached.source_latest_day.slice(0, 7));
|
|
3766
|
-
if (!requestedMonthIsLater) return null;
|
|
3767
|
-
return {
|
|
3768
|
-
from_day: from,
|
|
3769
|
-
to_day: to,
|
|
3770
|
-
source_latest_day: cached.source_latest_day,
|
|
3771
|
-
aggregate: {
|
|
3772
|
-
days: 0,
|
|
3773
|
-
total_run_count: 0,
|
|
3774
|
-
agent_invocation_count: 0,
|
|
3775
|
-
llm_call_count: null,
|
|
3776
|
-
duration_ms: null,
|
|
3777
|
-
total_tokens: null,
|
|
3778
|
-
cost_brl: null,
|
|
3779
|
-
execution_day_count: 0,
|
|
3780
|
-
},
|
|
3781
|
-
pages: { task: [], spec: [], prd: [] },
|
|
3782
|
-
top_tasks: [],
|
|
3783
|
-
};
|
|
3728
|
+
const read = readFirstFoldMonthlyQuick(this.database, this.firstFoldMonthlyQuickCache, filter);
|
|
3729
|
+
this.firstFoldMonthlyQuickCache = read.cache;
|
|
3730
|
+
return read.result;
|
|
3784
3731
|
}
|
|
3785
3732
|
|
|
3786
3733
|
firstFoldRelevantLatestDay(): string | null {
|
|
@@ -3796,261 +3743,43 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
3796
3743
|
return typeof row.day === "string" ? row.day : null;
|
|
3797
3744
|
}
|
|
3798
3745
|
|
|
3799
|
-
/**
|
|
3800
|
-
* Compact all-time first fold. A runtime reads an immutable private SQLite
|
|
3801
|
-
* snapshot, so readiness can warm this object and first paint can reuse the
|
|
3802
|
-
* same coherent source values without rescanning entity rows.
|
|
3803
|
-
*/
|
|
3804
3746
|
firstFoldAllTime(): SqliteFirstFoldAllTime {
|
|
3805
3747
|
if (this.firstFoldAllTimeCache !== null) return this.firstFoldAllTimeCache;
|
|
3806
|
-
|
|
3807
|
-
.
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
const topTasks = this.database
|
|
3819
|
-
.prepare("SELECT * FROM zaip_vw_first_fold_top_tasks_all_time ORDER BY rank")
|
|
3820
|
-
.all() as SqliteFirstFoldAllTime["top_tasks"];
|
|
3821
|
-
const number = (value: unknown, fallback = 0) => (typeof value === "number" ? value : fallback);
|
|
3822
|
-
const nullableNumber = (value: unknown) => (typeof value === "number" ? value : null);
|
|
3823
|
-
const readMacroMetrics = (kind: "spec" | "prd") => {
|
|
3824
|
-
const statusRows = this.database
|
|
3825
|
-
.prepare(`SELECT stage, entity_count FROM zaip_vw_first_fold_${kind}_status_all_time`)
|
|
3826
|
-
.all() as Array<{ stage: string; entity_count: number }>;
|
|
3827
|
-
const classificationRows = this.database
|
|
3828
|
-
.prepare(
|
|
3829
|
-
`SELECT classification, entity_count FROM zaip_vw_first_fold_${kind}_classification_all_time`,
|
|
3830
|
-
)
|
|
3831
|
-
.all() as Array<{ classification: string; entity_count: number }>;
|
|
3832
|
-
const macro = this.database
|
|
3833
|
-
.prepare(`SELECT * FROM zaip_vw_first_fold_${kind}_metrics_all_time`)
|
|
3834
|
-
.get() as Record<string, unknown>;
|
|
3835
|
-
const topEntities = this.database
|
|
3836
|
-
.prepare(`SELECT * FROM zaip_vw_first_fold_top_${kind}s_all_time ORDER BY rank`)
|
|
3837
|
-
.all() as SqliteFirstFoldAllTime["macro_metrics"]["specs"]["top_entities"];
|
|
3748
|
+
this.firstFoldAllTimeCache = readFirstFoldAllTime(
|
|
3749
|
+
this.database,
|
|
3750
|
+
() => this.dailyAggregateRows().length,
|
|
3751
|
+
this.firstFoldViewportAllTime(),
|
|
3752
|
+
);
|
|
3753
|
+
return this.firstFoldAllTimeCache;
|
|
3754
|
+
}
|
|
3755
|
+
|
|
3756
|
+
firstFoldBoardAllTime(): SqliteFirstFoldAllTimeBoard {
|
|
3757
|
+
if (this.firstFoldAllTimeBoardCache !== null) return this.firstFoldAllTimeBoardCache;
|
|
3758
|
+
this.firstFoldAllTimeBoardCache = readFirstFoldBoardAllTime(this.database, () => {
|
|
3759
|
+
const rows = operationsWorkListSqlRows(this.database);
|
|
3838
3760
|
return {
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
),
|
|
3845
|
-
entity_count: number(macro.entity_count),
|
|
3846
|
-
total_run_count: number(macro.total_run_count),
|
|
3847
|
-
total_duration_ms: nullableNumber(macro.total_duration_ms),
|
|
3848
|
-
total_tokens: nullableNumber(macro.total_tokens),
|
|
3849
|
-
total_cost_usd: nullableNumber(macro.total_cost_usd),
|
|
3850
|
-
total_cost_brl: nullableNumber(macro.total_cost_brl),
|
|
3851
|
-
average_duration_ms: nullableNumber(macro.average_duration_ms),
|
|
3852
|
-
average_tokens: nullableNumber(macro.average_tokens),
|
|
3853
|
-
average_cost_usd: nullableNumber(macro.average_cost_usd),
|
|
3854
|
-
average_cost_brl: nullableNumber(macro.average_cost_brl),
|
|
3855
|
-
latest_created_at:
|
|
3856
|
-
typeof macro.latest_created_at === "string" ? macro.latest_created_at : null,
|
|
3857
|
-
top_entities: topEntities,
|
|
3761
|
+
pages: {
|
|
3762
|
+
task: rows.filter((row) => row.kind === "task" || row.kind === "standalone-task"),
|
|
3763
|
+
spec: rows.filter((row) => row.kind === "spec"),
|
|
3764
|
+
prd: rows.filter((row) => row.kind === "prd"),
|
|
3765
|
+
},
|
|
3858
3766
|
};
|
|
3859
|
-
};
|
|
3860
|
-
this.
|
|
3861
|
-
aggregate: {
|
|
3862
|
-
days:
|
|
3863
|
-
typeof row.start_day === "string" && typeof row.end_day === "string"
|
|
3864
|
-
? this.dailyAggregateRows().length
|
|
3865
|
-
: 0,
|
|
3866
|
-
start_day: typeof row.start_day === "string" ? row.start_day : null,
|
|
3867
|
-
end_day: typeof row.end_day === "string" ? row.end_day : null,
|
|
3868
|
-
task_count: number(row.task_count),
|
|
3869
|
-
completed_task_count: number(row.completed_task_count),
|
|
3870
|
-
spec_count: number(row.spec_count),
|
|
3871
|
-
completed_spec_count: number(row.completed_spec_count),
|
|
3872
|
-
prd_count: number(row.prd_count),
|
|
3873
|
-
completed_prd_count: number(row.completed_prd_count),
|
|
3874
|
-
total_run_count: number(row.total_run_count),
|
|
3875
|
-
agent_invocation_count: number(row.agent_invocation_count),
|
|
3876
|
-
llm_call_count: nullableNumber(row.llm_call_count),
|
|
3877
|
-
duration_ms: nullableNumber(row.total_duration_ms),
|
|
3878
|
-
total_tokens: nullableNumber(row.total_tokens),
|
|
3879
|
-
total_cost_usd: nullableNumber(row.total_cost_usd),
|
|
3880
|
-
cost_brl: nullableNumber(row.total_cost_brl),
|
|
3881
|
-
execution_day_count: number(row.execution_day_count),
|
|
3882
|
-
latest_task_created_at:
|
|
3883
|
-
typeof row.latest_task_created_at === "string" ? row.latest_task_created_at : null,
|
|
3884
|
-
},
|
|
3885
|
-
task_status_counts: Object.fromEntries(
|
|
3886
|
-
statusRows.map((status) => [status.status, Number(status.task_count)]),
|
|
3887
|
-
),
|
|
3888
|
-
task_risk_counts: Object.fromEntries(
|
|
3889
|
-
riskRows.map((risk) => [risk.risk, Number(risk.task_count)]),
|
|
3890
|
-
),
|
|
3891
|
-
task_metrics: {
|
|
3892
|
-
average_duration_ms_per_task: nullableNumber(metrics.average_duration_ms_per_task),
|
|
3893
|
-
average_tokens_per_task: nullableNumber(metrics.average_tokens_per_task),
|
|
3894
|
-
average_cost_usd_per_task: nullableNumber(metrics.average_cost_usd_per_task),
|
|
3895
|
-
average_cost_brl_per_task: nullableNumber(metrics.average_cost_brl_per_task),
|
|
3896
|
-
latest_task_created_at:
|
|
3897
|
-
typeof metrics.latest_task_created_at === "string"
|
|
3898
|
-
? metrics.latest_task_created_at
|
|
3899
|
-
: null,
|
|
3900
|
-
},
|
|
3901
|
-
top_tasks: topTasks,
|
|
3902
|
-
macro_metrics: {
|
|
3903
|
-
specs: readMacroMetrics("spec"),
|
|
3904
|
-
prds: readMacroMetrics("prd"),
|
|
3905
|
-
},
|
|
3906
|
-
};
|
|
3907
|
-
return this.firstFoldAllTimeCache;
|
|
3767
|
+
});
|
|
3768
|
+
return this.firstFoldAllTimeBoardCache;
|
|
3908
3769
|
}
|
|
3909
3770
|
|
|
3910
|
-
/**
|
|
3911
|
-
* Narrow all-time ranking for the Operations quick projection. This avoids
|
|
3912
|
-
* loading macro metrics and extended aggregates solely to render Top 5.
|
|
3913
|
-
*/
|
|
3914
3771
|
firstFoldTopTasksAllTime(): SqliteFirstFoldAllTime["top_tasks"] {
|
|
3915
3772
|
if (this.firstFoldTopTasksAllTimeCache !== null) return this.firstFoldTopTasksAllTimeCache;
|
|
3916
|
-
this.firstFoldTopTasksAllTimeCache = this.database
|
|
3917
|
-
.prepare("SELECT * FROM zaip_vw_first_fold_top_tasks_all_time ORDER BY rank")
|
|
3918
|
-
.all() as SqliteFirstFoldAllTime["top_tasks"];
|
|
3773
|
+
this.firstFoldTopTasksAllTimeCache = readFirstFoldTopTasksAllTime(this.database);
|
|
3919
3774
|
return this.firstFoldTopTasksAllTimeCache;
|
|
3920
3775
|
}
|
|
3921
3776
|
|
|
3922
|
-
/**
|
|
3923
|
-
* First-paint subset: six KPI facts plus the status and risk charts. Keep
|
|
3924
|
-
* rankings, task averages and macro views out of readiness and scope=quick.
|
|
3925
|
-
*/
|
|
3926
3777
|
firstFoldViewportAllTime(): SqliteFirstFoldViewport {
|
|
3927
3778
|
if (this.firstFoldViewportCache !== null) return this.firstFoldViewportCache;
|
|
3928
|
-
|
|
3929
|
-
.
|
|
3930
|
-
|
|
3931
|
-
)
|
|
3932
|
-
.get() as { payload_json?: string } | undefined;
|
|
3933
|
-
if (typeof persisted?.payload_json === "string") {
|
|
3934
|
-
this.firstFoldViewportCache = JSON.parse(persisted.payload_json) as SqliteFirstFoldViewport;
|
|
3935
|
-
return this.firstFoldViewportCache;
|
|
3936
|
-
}
|
|
3937
|
-
// This is deliberately not the rich overview view: `task_summary` joins
|
|
3938
|
-
// every activity and is appropriate for rankings, but status/risk charts
|
|
3939
|
-
// need only the canonical work-item dimensions. Keep the three viewport
|
|
3940
|
-
// SELECTs independently attributable and free of below-fold joins.
|
|
3941
|
-
const profile = process.env.ZAIP_DASHBOARD_PROFILE_FIRST_FOLD === "1";
|
|
3942
|
-
const timed = <T>(select: string, read: () => T): T => {
|
|
3943
|
-
const startedAt = performance.now();
|
|
3944
|
-
const value = read();
|
|
3945
|
-
if (profile)
|
|
3946
|
-
this.firstFoldViewportTimings[select] =
|
|
3947
|
-
Math.round((performance.now() - startedAt) * 10) / 10;
|
|
3948
|
-
if (profile)
|
|
3949
|
-
console.info(
|
|
3950
|
-
JSON.stringify({
|
|
3951
|
-
event: "dashboard_first_fold_select",
|
|
3952
|
-
select,
|
|
3953
|
-
elapsed_ms: Math.round((performance.now() - startedAt) * 10) / 10,
|
|
3954
|
-
}),
|
|
3955
|
-
);
|
|
3956
|
-
return value;
|
|
3957
|
-
};
|
|
3958
|
-
const row = timed(
|
|
3959
|
-
"viewport_overview",
|
|
3960
|
-
() =>
|
|
3961
|
-
this.database
|
|
3962
|
-
.prepare(`WITH totals AS (
|
|
3963
|
-
SELECT SUM(CASE WHEN activity_source_kind='agent_run' THEN 1 ELSE 0 END) AS agent_invocation_count,
|
|
3964
|
-
SUM(llm_call_count) AS llm_call_count,
|
|
3965
|
-
SUM(total_tokens) AS total_tokens, SUM(cost_usd) AS total_cost_usd,
|
|
3966
|
-
SUM(cost_brl) AS total_cost_brl FROM activities
|
|
3967
|
-
), run_totals AS (
|
|
3968
|
-
SELECT COUNT(*) AS total_run_count FROM canonical_run_index
|
|
3969
|
-
), duration_totals AS (
|
|
3970
|
-
SELECT ROUND(SUM(attributed_token_active_ms)) AS total_duration_ms,
|
|
3971
|
-
COUNT(DISTINCT occurred_day_sp) AS execution_day_count
|
|
3972
|
-
FROM attributed_token_active_facts
|
|
3973
|
-
), bounds AS (
|
|
3974
|
-
SELECT MIN(day) AS start_day, MAX(day) AS end_day, COUNT(*) AS day_count FROM (
|
|
3975
|
-
SELECT occurred_day_sp AS day FROM activities
|
|
3976
|
-
UNION SELECT occurred_day_sp AS day FROM canonical_work_items
|
|
3977
|
-
UNION SELECT occurred_day_sp AS day FROM canonical_artifacts
|
|
3978
|
-
)
|
|
3979
|
-
), canonical_items AS MATERIALIZED (
|
|
3980
|
-
SELECT json_extract(payload_json, '$.entity_kind') AS entity_kind,
|
|
3981
|
-
json_extract(payload_json, '$.id') AS id,
|
|
3982
|
-
json_extract(payload_json, '$.status') AS status,
|
|
3983
|
-
json_extract(payload_json, '$.risk') AS risk,
|
|
3984
|
-
json_extract(payload_json, '$.occurred_at') AS occurred_at
|
|
3985
|
-
FROM zaip_aios_activities WHERE payload_type='work_item'
|
|
3986
|
-
), items AS MATERIALIZED (
|
|
3987
|
-
SELECT entity_kind, id, status, risk, occurred_at FROM canonical_items
|
|
3988
|
-
UNION ALL SELECT 'task', a.task_id, MAX(a.work_item_status), MAX(a.risk), MIN(a.occurred_at)
|
|
3989
|
-
FROM zaip_aios_activities a WHERE a.payload_type IS NULL AND a.task_id IS NOT NULL
|
|
3990
|
-
AND NOT EXISTS (SELECT 1 FROM canonical_items c WHERE c.entity_kind='task' AND c.id=a.task_id)
|
|
3991
|
-
GROUP BY task_id
|
|
3992
|
-
UNION ALL SELECT 'spec', a.spec_id, MAX(a.work_item_status), MAX(a.risk), MIN(a.occurred_at)
|
|
3993
|
-
FROM zaip_aios_activities a WHERE a.payload_type IS NULL AND a.spec_id IS NOT NULL
|
|
3994
|
-
AND NOT EXISTS (SELECT 1 FROM canonical_items c WHERE c.entity_kind='spec' AND c.id=a.spec_id)
|
|
3995
|
-
GROUP BY spec_id
|
|
3996
|
-
UNION ALL SELECT 'prd', a.prd_id, MAX(a.work_item_status), MAX(a.risk), MIN(a.occurred_at)
|
|
3997
|
-
FROM zaip_aios_activities a WHERE a.payload_type IS NULL AND a.prd_id IS NOT NULL
|
|
3998
|
-
AND NOT EXISTS (SELECT 1 FROM canonical_items c WHERE c.entity_kind='prd' AND c.id=a.prd_id)
|
|
3999
|
-
GROUP BY prd_id
|
|
4000
|
-
), item_totals AS (
|
|
4001
|
-
SELECT
|
|
4002
|
-
SUM(entity_kind='task') AS task_count,
|
|
4003
|
-
SUM(entity_kind='task' AND status='completed') AS completed_task_count,
|
|
4004
|
-
MAX(CASE WHEN entity_kind='task' THEN occurred_at END) AS latest_task_created_at,
|
|
4005
|
-
SUM(entity_kind='spec') AS spec_count,
|
|
4006
|
-
SUM(entity_kind='spec' AND status IN ('completed','delivered')) AS completed_spec_count,
|
|
4007
|
-
SUM(entity_kind='prd') AS prd_count,
|
|
4008
|
-
SUM(entity_kind='prd' AND status IN ('completed','delivered')) AS completed_prd_count
|
|
4009
|
-
FROM items
|
|
4010
|
-
), task_status AS (
|
|
4011
|
-
SELECT json_group_object(status, task_count) AS task_status_json FROM (
|
|
4012
|
-
SELECT COALESCE(status, 'draft') AS status, COUNT(*) AS task_count
|
|
4013
|
-
FROM items WHERE entity_kind='task' GROUP BY COALESCE(status, 'draft')
|
|
4014
|
-
)
|
|
4015
|
-
), task_risk AS (
|
|
4016
|
-
SELECT json_group_object(risk, task_count) AS task_risk_json FROM (
|
|
4017
|
-
SELECT COALESCE(risk, 'standard') AS risk, COUNT(*) AS task_count
|
|
4018
|
-
FROM items WHERE entity_kind='task' GROUP BY COALESCE(risk, 'standard')
|
|
4019
|
-
)
|
|
4020
|
-
) SELECT totals.*, run_totals.*, duration_totals.*, bounds.*, item_totals.*, task_status.*, task_risk.*
|
|
4021
|
-
FROM totals CROSS JOIN run_totals CROSS JOIN duration_totals CROSS JOIN bounds
|
|
4022
|
-
CROSS JOIN item_totals CROSS JOIN task_status CROSS JOIN task_risk`)
|
|
4023
|
-
.get() as Record<string, unknown>,
|
|
3779
|
+
this.firstFoldViewportCache = readFirstFoldViewport(
|
|
3780
|
+
this.database,
|
|
3781
|
+
this.firstFoldViewportTimings,
|
|
4024
3782
|
);
|
|
4025
|
-
const number = (value: unknown, fallback = 0) => (typeof value === "number" ? value : fallback);
|
|
4026
|
-
const nullableNumber = (value: unknown) => (typeof value === "number" ? value : null);
|
|
4027
|
-
this.firstFoldViewportCache = {
|
|
4028
|
-
aggregate: {
|
|
4029
|
-
days: number(row.day_count),
|
|
4030
|
-
start_day: typeof row.start_day === "string" ? row.start_day : null,
|
|
4031
|
-
end_day: typeof row.end_day === "string" ? row.end_day : null,
|
|
4032
|
-
task_count: number(row.task_count),
|
|
4033
|
-
completed_task_count: number(row.completed_task_count),
|
|
4034
|
-
spec_count: number(row.spec_count),
|
|
4035
|
-
completed_spec_count: number(row.completed_spec_count),
|
|
4036
|
-
prd_count: number(row.prd_count),
|
|
4037
|
-
completed_prd_count: number(row.completed_prd_count),
|
|
4038
|
-
total_run_count: number(row.total_run_count),
|
|
4039
|
-
agent_invocation_count: number(row.agent_invocation_count),
|
|
4040
|
-
llm_call_count: nullableNumber(row.llm_call_count),
|
|
4041
|
-
duration_ms: nullableNumber(row.total_duration_ms),
|
|
4042
|
-
total_tokens: nullableNumber(row.total_tokens),
|
|
4043
|
-
total_cost_usd: nullableNumber(row.total_cost_usd),
|
|
4044
|
-
cost_brl: nullableNumber(row.total_cost_brl),
|
|
4045
|
-
execution_day_count: number(row.execution_day_count),
|
|
4046
|
-
latest_task_created_at:
|
|
4047
|
-
typeof row.latest_task_created_at === "string" ? row.latest_task_created_at : null,
|
|
4048
|
-
},
|
|
4049
|
-
task_status_counts:
|
|
4050
|
-
typeof row.task_status_json === "string" ? JSON.parse(row.task_status_json) : {},
|
|
4051
|
-
task_risk_counts:
|
|
4052
|
-
typeof row.task_risk_json === "string" ? JSON.parse(row.task_risk_json) : {},
|
|
4053
|
-
};
|
|
4054
3783
|
return this.firstFoldViewportCache;
|
|
4055
3784
|
}
|
|
4056
3785
|
|
|
@@ -4514,12 +4243,281 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
4514
4243
|
* per selected day plus conversation/planning/execution and total rows; no
|
|
4515
4244
|
* activity-shaped record is returned to the server or serialized to HTTP.
|
|
4516
4245
|
*/
|
|
4246
|
+
developerCostAggregate(filter: {
|
|
4247
|
+
from: string | null;
|
|
4248
|
+
to: string | null;
|
|
4249
|
+
accountIds?: readonly string[];
|
|
4250
|
+
}) {
|
|
4251
|
+
const from = costMonitorFilterDay(filter.from);
|
|
4252
|
+
const to = costMonitorFilterDay(filter.to);
|
|
4253
|
+
const clauses = [
|
|
4254
|
+
"a.developer_account_id IS NOT NULL",
|
|
4255
|
+
"(? IS NULL OR a.occurred_day_sp >= ?)",
|
|
4256
|
+
"(? IS NULL OR a.occurred_day_sp <= ?)",
|
|
4257
|
+
];
|
|
4258
|
+
const values: Array<string | null> = [from, from, to, to];
|
|
4259
|
+
if (filter.accountIds !== undefined && filter.accountIds.length > 0) {
|
|
4260
|
+
clauses.push(`a.developer_account_id IN (${filter.accountIds.map(() => "?").join(",")})`);
|
|
4261
|
+
values.push(...filter.accountIds);
|
|
4262
|
+
}
|
|
4263
|
+
const where = clauses.join(" AND ");
|
|
4264
|
+
const rows = this.database
|
|
4265
|
+
.prepare(`
|
|
4266
|
+
WITH selected AS (SELECT * FROM zaip_vw_activities a WHERE ${where}), completed AS (
|
|
4267
|
+
SELECT a.developer_account_id, COUNT(DISTINCT w.id) AS completed_task_count
|
|
4268
|
+
FROM zaip_vw_canonical_work_items w JOIN selected a ON a.run_id=w.completed_run_id
|
|
4269
|
+
WHERE w.entity_kind='task' AND w.status='completed' GROUP BY a.developer_account_id
|
|
4270
|
+
) ${developerArtifactCountsCte()}
|
|
4271
|
+
SELECT a.developer_account_id AS account_id, MAX(a.developer_login) AS login_snapshot,
|
|
4272
|
+
CASE WHEN COUNT(DISTINCT a.developer_attribution_source)=1 THEN MAX(a.developer_attribution_source) ELSE 'mixed' END AS attribution_source,
|
|
4273
|
+
COUNT(DISTINCT a.prd_id) AS prd_count, COUNT(DISTINCT a.spec_id) AS spec_count,
|
|
4274
|
+
COUNT(DISTINCT a.task_id) AS task_count, COUNT(*) AS run_count,
|
|
4275
|
+
COALESCE(artifacts.test_count, 0) AS test_count,
|
|
4276
|
+
COALESCE(artifacts.evidence_count, 0) AS evidence_count,
|
|
4277
|
+
SUM(a.attributed_token_active_ms) AS total_duration_ms,
|
|
4278
|
+
COUNT(a.attributed_token_active_ms) AS duration_covered_runs,
|
|
4279
|
+
SUM(a.source_event_count) AS source_event_count,
|
|
4280
|
+
SUM(a.total_tokens) AS total_tokens, SUM(a.cost_usd) AS total_cost_usd, SUM(a.cost_brl) AS total_cost_brl,
|
|
4281
|
+
COUNT(a.total_tokens) AS token_covered_runs, COUNT(a.cost_usd) AS usd_covered_runs, COUNT(a.cost_brl) AS brl_covered_runs,
|
|
4282
|
+
COALESCE(completed.completed_task_count, 0) AS completed_task_count
|
|
4283
|
+
FROM selected a
|
|
4284
|
+
LEFT JOIN completed USING(developer_account_id)
|
|
4285
|
+
LEFT JOIN artifacts USING(developer_account_id)
|
|
4286
|
+
GROUP BY a.developer_account_id ORDER BY total_cost_brl DESC, account_id`)
|
|
4287
|
+
.all(...values, from, from, to, to) as Array<Record<string, unknown>>;
|
|
4288
|
+
// A delivery is factual only when the canonical Task points at the exact
|
|
4289
|
+
// completed Run. Contributors are the distinct attributed GitHub accounts
|
|
4290
|
+
// on that same Run; no timestamp, commit, or deployment inference enters.
|
|
4291
|
+
const delivered = this.database
|
|
4292
|
+
.prepare(`
|
|
4293
|
+
WITH selected AS (SELECT * FROM zaip_vw_activities a WHERE ${where})
|
|
4294
|
+
SELECT selected.developer_account_id AS delivered_by_account_id, w.id, w.title, w.status, w.completed_run_id,
|
|
4295
|
+
GROUP_CONCAT(DISTINCT contributor.developer_account_id || char(31) || COALESCE(contributor.developer_login, '')) AS contributor_pairs
|
|
4296
|
+
FROM zaip_vw_canonical_work_items w
|
|
4297
|
+
JOIN selected ON selected.run_id=w.completed_run_id
|
|
4298
|
+
LEFT JOIN zaip_vw_activities contributor ON contributor.task_id=w.id
|
|
4299
|
+
AND contributor.developer_account_id IS NOT NULL
|
|
4300
|
+
WHERE w.entity_kind='task' AND w.status='completed'
|
|
4301
|
+
GROUP BY selected.developer_account_id, w.id, w.title, w.status, w.completed_run_id
|
|
4302
|
+
ORDER BY w.id`)
|
|
4303
|
+
.all(...values) as Array<Record<string, unknown>>;
|
|
4304
|
+
const deliveriesByAccount = new Map<string, Array<Record<string, unknown>>>();
|
|
4305
|
+
for (const task of delivered) {
|
|
4306
|
+
const accountId = String(task.delivered_by_account_id);
|
|
4307
|
+
const list = deliveriesByAccount.get(accountId) ?? [];
|
|
4308
|
+
list.push({
|
|
4309
|
+
id: task.id,
|
|
4310
|
+
title: task.title,
|
|
4311
|
+
status: task.status,
|
|
4312
|
+
completed_run_id: task.completed_run_id,
|
|
4313
|
+
delivered_by: { account_id: accountId },
|
|
4314
|
+
contributors: String(task.contributor_pairs ?? "")
|
|
4315
|
+
.split(",")
|
|
4316
|
+
.filter(Boolean)
|
|
4317
|
+
.map((pair) => {
|
|
4318
|
+
const [account_id, login_snapshot] = pair.split(String.fromCharCode(31));
|
|
4319
|
+
return { account_id, login_snapshot: login_snapshot || null };
|
|
4320
|
+
}),
|
|
4321
|
+
});
|
|
4322
|
+
deliveriesByAccount.set(accountId, list);
|
|
4323
|
+
}
|
|
4324
|
+
const qualificationForSource: Record<
|
|
4325
|
+
string,
|
|
4326
|
+
"verified" | "cached_fallback" | "human_assigned" | "mixed"
|
|
4327
|
+
> = {
|
|
4328
|
+
verified_local_account: "verified",
|
|
4329
|
+
cached_fallback: "cached_fallback",
|
|
4330
|
+
human_assigned: "human_assigned",
|
|
4331
|
+
mixed: "mixed",
|
|
4332
|
+
};
|
|
4333
|
+
for (const row of rows) {
|
|
4334
|
+
row.completed_tasks = deliveriesByAccount.get(String(row.account_id)) ?? [];
|
|
4335
|
+
row.qualification = qualificationForSource[String(row.attribution_source)] ?? "unqualified";
|
|
4336
|
+
}
|
|
4337
|
+
return {
|
|
4338
|
+
data_source: "sqlite_read_model" as const,
|
|
4339
|
+
filters: { from, to, account_ids: filter.accountIds ?? [] },
|
|
4340
|
+
rows,
|
|
4341
|
+
totals: {
|
|
4342
|
+
run_count: rows.reduce((sum, row) => sum + Number(row.run_count ?? 0), 0),
|
|
4343
|
+
completed_task_count: rows.reduce(
|
|
4344
|
+
(sum, row) => sum + Number(row.completed_task_count ?? 0),
|
|
4345
|
+
0,
|
|
4346
|
+
),
|
|
4347
|
+
total_cost_usd: sumKnownDeveloperMetric(rows, "total_cost_usd"),
|
|
4348
|
+
total_cost_brl: sumKnownDeveloperMetric(rows, "total_cost_brl"),
|
|
4349
|
+
},
|
|
4350
|
+
};
|
|
4351
|
+
}
|
|
4352
|
+
|
|
4353
|
+
/**
|
|
4354
|
+
* Operations-only developer projection. It deliberately keeps the Cost
|
|
4355
|
+
* Monitor aggregate unchanged: status, kind and risk are derived solely
|
|
4356
|
+
* from canonical Task records linked to the developer's observed Task ids.
|
|
4357
|
+
*/
|
|
4358
|
+
operationsDeveloperAggregate(filter: {
|
|
4359
|
+
from: string | null;
|
|
4360
|
+
to: string | null;
|
|
4361
|
+
accountIds?: readonly string[];
|
|
4362
|
+
}) {
|
|
4363
|
+
const from = costMonitorFilterDay(filter.from);
|
|
4364
|
+
const to = costMonitorFilterDay(filter.to);
|
|
4365
|
+
const clauses = [
|
|
4366
|
+
"a.developer_account_id IS NOT NULL",
|
|
4367
|
+
"(? IS NULL OR a.occurred_day_sp >= ?)",
|
|
4368
|
+
"(? IS NULL OR a.occurred_day_sp <= ?)",
|
|
4369
|
+
];
|
|
4370
|
+
const values: Array<string | null> = [from, from, to, to];
|
|
4371
|
+
if (filter.accountIds !== undefined && filter.accountIds.length > 0) {
|
|
4372
|
+
clauses.push(`a.developer_account_id IN (${filter.accountIds.map(() => "?").join(",")})`);
|
|
4373
|
+
values.push(...filter.accountIds);
|
|
4374
|
+
}
|
|
4375
|
+
const where = clauses.join(" AND ");
|
|
4376
|
+
const rows = this.database
|
|
4377
|
+
.prepare(`
|
|
4378
|
+
WITH selected AS (SELECT * FROM zaip_vw_activities a WHERE ${where}),
|
|
4379
|
+
developer_tasks AS (
|
|
4380
|
+
SELECT DISTINCT developer_account_id AS account_id, task_id
|
|
4381
|
+
FROM selected WHERE task_id IS NOT NULL
|
|
4382
|
+
), canonical_task_facts AS (
|
|
4383
|
+
SELECT developer_tasks.account_id, work.status, work.entity_kind, work.risk
|
|
4384
|
+
FROM developer_tasks JOIN zaip_vw_canonical_work_items work
|
|
4385
|
+
ON work.entity_kind='task' AND work.id=developer_tasks.task_id
|
|
4386
|
+
), status_counts AS (
|
|
4387
|
+
SELECT account_id, json_group_object(status, task_count) AS status_counts_json
|
|
4388
|
+
FROM (SELECT account_id, status, COUNT(*) AS task_count FROM canonical_task_facts
|
|
4389
|
+
WHERE status IS NOT NULL GROUP BY account_id, status) GROUP BY account_id
|
|
4390
|
+
), type_counts AS (
|
|
4391
|
+
SELECT account_id, json_group_object(entity_kind, task_count) AS type_counts_json
|
|
4392
|
+
FROM (SELECT account_id, entity_kind, COUNT(*) AS task_count FROM canonical_task_facts
|
|
4393
|
+
WHERE entity_kind IS NOT NULL GROUP BY account_id, entity_kind) GROUP BY account_id
|
|
4394
|
+
), risk_counts AS (
|
|
4395
|
+
SELECT account_id, json_group_object(risk, task_count) AS risk_counts_json
|
|
4396
|
+
FROM (SELECT account_id, risk, COUNT(*) AS task_count FROM canonical_task_facts
|
|
4397
|
+
WHERE risk IS NOT NULL GROUP BY account_id, risk) GROUP BY account_id
|
|
4398
|
+
), completed AS (
|
|
4399
|
+
SELECT account_id, COUNT(*) AS completed_task_count
|
|
4400
|
+
FROM canonical_task_facts WHERE status='completed'
|
|
4401
|
+
GROUP BY account_id
|
|
4402
|
+
)
|
|
4403
|
+
-- task_count is the distinct, observed Task-id population attributed
|
|
4404
|
+
-- to this developer in the selected period. Canonical records do not
|
|
4405
|
+
-- retain a creator identity, so this projection never infers one.
|
|
4406
|
+
SELECT selected.developer_account_id AS account_id, MAX(selected.developer_login) AS login_snapshot,
|
|
4407
|
+
COUNT(DISTINCT selected.task_id) AS task_count,
|
|
4408
|
+
COALESCE(completed.completed_task_count, 0) AS completed_task_count,
|
|
4409
|
+
SUM(selected.attributed_token_active_ms) AS total_duration_ms,
|
|
4410
|
+
SUM(selected.total_tokens) AS total_tokens,
|
|
4411
|
+
SUM(selected.cost_brl) AS total_cost_brl,
|
|
4412
|
+
status_counts.status_counts_json, type_counts.type_counts_json, risk_counts.risk_counts_json
|
|
4413
|
+
FROM selected
|
|
4414
|
+
LEFT JOIN completed ON completed.account_id=selected.developer_account_id
|
|
4415
|
+
LEFT JOIN status_counts ON status_counts.account_id=selected.developer_account_id
|
|
4416
|
+
LEFT JOIN type_counts ON type_counts.account_id=selected.developer_account_id
|
|
4417
|
+
LEFT JOIN risk_counts ON risk_counts.account_id=selected.developer_account_id
|
|
4418
|
+
GROUP BY selected.developer_account_id
|
|
4419
|
+
ORDER BY total_cost_brl DESC, account_id`)
|
|
4420
|
+
.all(...values) as Array<Record<string, unknown>>;
|
|
4421
|
+
const distribution = (value: unknown): Record<string, number> | null => {
|
|
4422
|
+
if (typeof value !== "string") return null;
|
|
4423
|
+
try {
|
|
4424
|
+
const parsed = JSON.parse(value) as Record<string, unknown>;
|
|
4425
|
+
return Object.fromEntries(
|
|
4426
|
+
Object.entries(parsed).flatMap(([key, count]) =>
|
|
4427
|
+
typeof count === "number" && Number.isFinite(count) ? [[key, count]] : [],
|
|
4428
|
+
),
|
|
4429
|
+
);
|
|
4430
|
+
} catch {
|
|
4431
|
+
return null;
|
|
4432
|
+
}
|
|
4433
|
+
};
|
|
4434
|
+
for (const row of rows) {
|
|
4435
|
+
row.status_counts = distribution(row.status_counts_json);
|
|
4436
|
+
row.type_counts = distribution(row.type_counts_json);
|
|
4437
|
+
row.risk_counts = distribution(row.risk_counts_json);
|
|
4438
|
+
delete row.status_counts_json;
|
|
4439
|
+
delete row.type_counts_json;
|
|
4440
|
+
delete row.risk_counts_json;
|
|
4441
|
+
}
|
|
4442
|
+
return {
|
|
4443
|
+
data_source: "sqlite_read_model" as const,
|
|
4444
|
+
filters: { from, to, account_ids: filter.accountIds ?? [] },
|
|
4445
|
+
rows,
|
|
4446
|
+
};
|
|
4447
|
+
}
|
|
4448
|
+
|
|
4517
4449
|
costMonitorSummaryAggregate(
|
|
4518
4450
|
filter: Parameters<SqliteReadModelFileSystem["activityCostRows"]>[0],
|
|
4519
|
-
) {
|
|
4451
|
+
): CostSummarySource {
|
|
4520
4452
|
const { from, to, providers = [], models = [] } = filter;
|
|
4521
4453
|
const sourceFrom = costMonitorFilterDay(from);
|
|
4522
4454
|
const sourceTo = costMonitorFilterDay(to);
|
|
4455
|
+
const cacheKey = JSON.stringify({ ...filter, from: sourceFrom, to: sourceTo });
|
|
4456
|
+
const generationCached = this.costMonitorSummaryCache.get(cacheKey);
|
|
4457
|
+
if (generationCached !== undefined) return structuredClone(generationCached);
|
|
4458
|
+
const remember = (value: CostSummarySource) => {
|
|
4459
|
+
if (this.costMonitorSummaryCache.size >= 64) {
|
|
4460
|
+
const oldest = this.costMonitorSummaryCache.keys().next().value as string | undefined;
|
|
4461
|
+
if (oldest !== undefined) this.costMonitorSummaryCache.delete(oldest);
|
|
4462
|
+
}
|
|
4463
|
+
this.costMonitorSummaryCache.set(cacheKey, structuredClone(value));
|
|
4464
|
+
return structuredClone(value);
|
|
4465
|
+
};
|
|
4466
|
+
const unfilteredAllTime =
|
|
4467
|
+
sourceFrom === null &&
|
|
4468
|
+
sourceTo === null &&
|
|
4469
|
+
[
|
|
4470
|
+
providers,
|
|
4471
|
+
models,
|
|
4472
|
+
filter.agents,
|
|
4473
|
+
filter.phases,
|
|
4474
|
+
filter.statuses,
|
|
4475
|
+
filter.tasks,
|
|
4476
|
+
filter.specs,
|
|
4477
|
+
filter.prds,
|
|
4478
|
+
filter.executions,
|
|
4479
|
+
filter.changes,
|
|
4480
|
+
].every((values) => values === undefined || values.length === 0);
|
|
4481
|
+
if (unfilteredAllTime) {
|
|
4482
|
+
const cached = this.database
|
|
4483
|
+
.prepare(
|
|
4484
|
+
"SELECT payload_json FROM zaip_aios_activities WHERE payload_type='cost_monitor_summary_cache' ORDER BY recorded_at DESC LIMIT 1",
|
|
4485
|
+
)
|
|
4486
|
+
.get() as { payload_json?: string } | undefined;
|
|
4487
|
+
if (typeof cached?.payload_json === "string")
|
|
4488
|
+
return remember(JSON.parse(cached.payload_json) as CostSummarySource);
|
|
4489
|
+
}
|
|
4490
|
+
if (
|
|
4491
|
+
sourceFrom !== null &&
|
|
4492
|
+
sourceTo !== null &&
|
|
4493
|
+
[
|
|
4494
|
+
providers,
|
|
4495
|
+
models,
|
|
4496
|
+
filter.agents,
|
|
4497
|
+
filter.phases,
|
|
4498
|
+
filter.statuses,
|
|
4499
|
+
filter.tasks,
|
|
4500
|
+
filter.specs,
|
|
4501
|
+
filter.prds,
|
|
4502
|
+
filter.executions,
|
|
4503
|
+
filter.changes,
|
|
4504
|
+
].every((values) => values === undefined || values.length === 0)
|
|
4505
|
+
) {
|
|
4506
|
+
const cached = this.database
|
|
4507
|
+
.prepare(
|
|
4508
|
+
"SELECT payload_json FROM zaip_aios_activities WHERE payload_type='cost_monitor_summary_month_cache' ORDER BY recorded_at DESC LIMIT 1",
|
|
4509
|
+
)
|
|
4510
|
+
.get() as { payload_json?: string } | undefined;
|
|
4511
|
+
if (typeof cached?.payload_json === "string") {
|
|
4512
|
+
const payload = JSON.parse(cached.payload_json) as {
|
|
4513
|
+
from: string;
|
|
4514
|
+
to: string;
|
|
4515
|
+
aggregate: CostSummarySource;
|
|
4516
|
+
};
|
|
4517
|
+
if (payload.from === sourceFrom && payload.to === sourceTo)
|
|
4518
|
+
return remember(payload.aggregate);
|
|
4519
|
+
}
|
|
4520
|
+
}
|
|
4523
4521
|
const sourceDay =
|
|
4524
4522
|
"date(COALESCE(a.source_started_at, a.source_occurred_at, a.occurred_at), '-3 hours')";
|
|
4525
4523
|
const clauses = [
|
|
@@ -4691,16 +4689,7 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
4691
4689
|
AND (? IS NULL OR day >= ?)
|
|
4692
4690
|
AND (? IS NULL OR day <= ?)
|
|
4693
4691
|
GROUP BY day
|
|
4694
|
-
|
|
4695
|
-
SELECT occurred_day_sp,
|
|
4696
|
-
0, 0, 0,
|
|
4697
|
-
SUM(CASE WHEN artifact_kind='validation' THEN 1 ELSE 0 END),
|
|
4698
|
-
SUM(CASE WHEN artifact_kind='evidence' THEN 1 ELSE 0 END)
|
|
4699
|
-
FROM canonical_artifacts
|
|
4700
|
-
WHERE occurred_day_sp IS NOT NULL
|
|
4701
|
-
AND (? IS NULL OR occurred_day_sp >= ?)
|
|
4702
|
-
AND (? IS NULL OR occurred_day_sp <= ?)
|
|
4703
|
-
GROUP BY occurred_day_sp
|
|
4692
|
+
${artifactStructuralRowsSql()}
|
|
4704
4693
|
)
|
|
4705
4694
|
SELECT day,
|
|
4706
4695
|
SUM(prd_count) AS prd_count,
|
|
@@ -4912,7 +4901,7 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
4912
4901
|
coverage: { events: 0, tokens: 0, usd: 0, brl: 0 },
|
|
4913
4902
|
};
|
|
4914
4903
|
});
|
|
4915
|
-
return {
|
|
4904
|
+
return remember({
|
|
4916
4905
|
periods: { daily: periods },
|
|
4917
4906
|
aggregate_usage: {
|
|
4918
4907
|
conversation,
|
|
@@ -4926,7 +4915,7 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
4926
4915
|
model_groups,
|
|
4927
4916
|
cost_monitor_daily_duration: durations,
|
|
4928
4917
|
daily_rows: compactDailyRows,
|
|
4929
|
-
};
|
|
4918
|
+
});
|
|
4930
4919
|
}
|
|
4931
4920
|
|
|
4932
4921
|
/** Keyset cursor or LIMIT/OFFSET pagination for materialized work summaries. */
|
|
@@ -5005,6 +4994,21 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
5005
4994
|
};
|
|
5006
4995
|
}
|
|
5007
4996
|
|
|
4997
|
+
/** Canonical page for the Board: keeps telemetry-only historical rows out. */
|
|
4998
|
+
canonicalWorkPage(
|
|
4999
|
+
kind: SqliteSummaryKind,
|
|
5000
|
+
filter: { from: string | null; to: string | null },
|
|
5001
|
+
offset: number,
|
|
5002
|
+
limit: number,
|
|
5003
|
+
) {
|
|
5004
|
+
return this.viewPage("operations_work_list", {
|
|
5005
|
+
...filter,
|
|
5006
|
+
kind: kind === "task" ? "all-tasks" : kind,
|
|
5007
|
+
offset,
|
|
5008
|
+
limit,
|
|
5009
|
+
});
|
|
5010
|
+
}
|
|
5011
|
+
|
|
5008
5012
|
overviewPage(period: "day" | "month" | "year", offset: number, limit: number) {
|
|
5009
5013
|
const total = Number(
|
|
5010
5014
|
(
|
|
@@ -5037,11 +5041,6 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
5037
5041
|
};
|
|
5038
5042
|
}
|
|
5039
5043
|
|
|
5040
|
-
/**
|
|
5041
|
-
* Generation-local cache of the complete public page response. It caches
|
|
5042
|
-
* post-enrichment rows and totals, never raw facts, so the daily Operations
|
|
5043
|
-
* duration and Cost Monitor pre-pagination subtotals retain their contracts.
|
|
5044
|
-
*/
|
|
5045
5044
|
viewPage(
|
|
5046
5045
|
view: SqliteViewName,
|
|
5047
5046
|
input: {
|
|
@@ -5057,13 +5056,11 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
5057
5056
|
origin?: string;
|
|
5058
5057
|
workType?: string;
|
|
5059
5058
|
priority?: string;
|
|
5059
|
+
kind?: "all" | "all-tasks" | "task" | "standalone-task" | "prd" | "spec";
|
|
5060
5060
|
offset?: number;
|
|
5061
5061
|
limit?: number;
|
|
5062
5062
|
},
|
|
5063
5063
|
): ReturnType<SqliteReadModelFileSystem["uncachedViewPage"]> {
|
|
5064
|
-
// The key includes every filter and pagination input. The cache is scoped
|
|
5065
|
-
// to the immutable opened SQLite generation and reopen() clears it, so a
|
|
5066
|
-
// launcher refresh cannot expose a page from the previous snapshot.
|
|
5067
5064
|
const key = `${view}:${JSON.stringify(input)}`;
|
|
5068
5065
|
const cached = this.viewPageResultCache.get(key);
|
|
5069
5066
|
if (cached !== undefined)
|
|
@@ -5093,10 +5090,151 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
5093
5090
|
origin?: string;
|
|
5094
5091
|
workType?: string;
|
|
5095
5092
|
priority?: string;
|
|
5093
|
+
kind?: "all" | "all-tasks" | "task" | "standalone-task" | "prd" | "spec";
|
|
5096
5094
|
offset?: number;
|
|
5097
5095
|
limit?: number;
|
|
5098
5096
|
},
|
|
5099
5097
|
) {
|
|
5098
|
+
const isUnfilteredAllTimeCostPage =
|
|
5099
|
+
!input.from &&
|
|
5100
|
+
!input.to &&
|
|
5101
|
+
!input.query &&
|
|
5102
|
+
!input.status &&
|
|
5103
|
+
!input.risk &&
|
|
5104
|
+
!input.provider &&
|
|
5105
|
+
!input.model &&
|
|
5106
|
+
!input.agent &&
|
|
5107
|
+
!input.phase &&
|
|
5108
|
+
!input.origin &&
|
|
5109
|
+
!input.workType &&
|
|
5110
|
+
!input.priority;
|
|
5111
|
+
const isDateOnlyCostInvocationPage =
|
|
5112
|
+
view === "cost_monitor_agent_invocations" &&
|
|
5113
|
+
!input.query &&
|
|
5114
|
+
!input.status &&
|
|
5115
|
+
!input.risk &&
|
|
5116
|
+
!input.provider &&
|
|
5117
|
+
!input.model &&
|
|
5118
|
+
!input.agent &&
|
|
5119
|
+
!input.phase &&
|
|
5120
|
+
!input.origin &&
|
|
5121
|
+
!input.workType &&
|
|
5122
|
+
!input.priority;
|
|
5123
|
+
if (isDateOnlyCostInvocationPage) {
|
|
5124
|
+
const projection = filterCostInvocationProjection(
|
|
5125
|
+
this.technicalPayloadRows("cost_agent_invocation_projection_v1"),
|
|
5126
|
+
input,
|
|
5127
|
+
);
|
|
5128
|
+
if (projection.length > 0) {
|
|
5129
|
+
const limit = Math.min(200, Math.max(1, input.limit ?? 100));
|
|
5130
|
+
const offset = Math.max(0, input.offset ?? 0);
|
|
5131
|
+
const rows = projection.slice(offset, offset + limit);
|
|
5132
|
+
return {
|
|
5133
|
+
data_source: "sqlite_read_model" as const,
|
|
5134
|
+
view,
|
|
5135
|
+
offset,
|
|
5136
|
+
limit,
|
|
5137
|
+
total_count: projection.length,
|
|
5138
|
+
has_more: offset + rows.length < projection.length,
|
|
5139
|
+
rows,
|
|
5140
|
+
};
|
|
5141
|
+
}
|
|
5142
|
+
}
|
|
5143
|
+
const remoteAggregateDimensions: Partial<Record<SqliteViewName, string>> = {
|
|
5144
|
+
cost_monitor_zaip_agents: "agent",
|
|
5145
|
+
cost_monitor_executions: "execution",
|
|
5146
|
+
cost_monitor_tasks: "task",
|
|
5147
|
+
cost_monitor_conversations: "conversation",
|
|
5148
|
+
};
|
|
5149
|
+
const remoteAggregateDimension = remoteAggregateDimensions[view];
|
|
5150
|
+
if (isUnfilteredAllTimeCostPage && remoteAggregateDimension !== undefined) {
|
|
5151
|
+
const projection = this.technicalPayloadRows("cost_dimension_aggregate_v1").filter(
|
|
5152
|
+
(row) => row.dimension === remoteAggregateDimension,
|
|
5153
|
+
);
|
|
5154
|
+
const rows = projection
|
|
5155
|
+
.filter((row) => row.row_kind === "row")
|
|
5156
|
+
.map((row) =>
|
|
5157
|
+
view === "cost_monitor_zaip_agents"
|
|
5158
|
+
? {
|
|
5159
|
+
key: row.key,
|
|
5160
|
+
label: row.label,
|
|
5161
|
+
persona: row.persona,
|
|
5162
|
+
job: row.job,
|
|
5163
|
+
latest_started_at: row.latest_started_at,
|
|
5164
|
+
invocation_count: row.invocation_count,
|
|
5165
|
+
task_count: row.task_count,
|
|
5166
|
+
total_duration: row.total_duration,
|
|
5167
|
+
duration_unknown: row.duration_unknown,
|
|
5168
|
+
duration_known: row.duration_known,
|
|
5169
|
+
source_event_count: row.source_event_count,
|
|
5170
|
+
total_tokens: row.total_tokens,
|
|
5171
|
+
total_cost_usd: row.total_cost_usd,
|
|
5172
|
+
total_cost_brl: row.total_cost_brl,
|
|
5173
|
+
}
|
|
5174
|
+
: {
|
|
5175
|
+
id: row.id,
|
|
5176
|
+
key: row.key,
|
|
5177
|
+
title: row.title,
|
|
5178
|
+
status: row.status,
|
|
5179
|
+
risk: row.risk,
|
|
5180
|
+
first_day: row.first_day,
|
|
5181
|
+
last_day: row.last_day,
|
|
5182
|
+
occurred_at: row.occurred_at,
|
|
5183
|
+
invocation_count: row.invocation_count,
|
|
5184
|
+
llm_call_count: row.llm_call_count,
|
|
5185
|
+
total_duration: row.total_duration,
|
|
5186
|
+
total_input_tokens: row.total_input_tokens,
|
|
5187
|
+
total_cached_input_tokens: row.total_cached_input_tokens,
|
|
5188
|
+
total_output_tokens: row.total_output_tokens,
|
|
5189
|
+
total_reasoning_tokens: row.total_reasoning_tokens,
|
|
5190
|
+
total_tokens: row.total_tokens,
|
|
5191
|
+
total_cost_usd: row.total_cost_usd,
|
|
5192
|
+
total_cost_brl: row.total_cost_brl,
|
|
5193
|
+
},
|
|
5194
|
+
)
|
|
5195
|
+
.sort(
|
|
5196
|
+
(left, right) =>
|
|
5197
|
+
Number(right.total_cost_brl ?? -1) - Number(left.total_cost_brl ?? -1) ||
|
|
5198
|
+
String(left.key ?? "").localeCompare(String(right.key ?? "")),
|
|
5199
|
+
) as Array<Record<string, unknown>>;
|
|
5200
|
+
const totals = projection.find((row) => row.row_kind === "totals");
|
|
5201
|
+
if (rows.length > 0 && totals !== undefined) {
|
|
5202
|
+
const limit = Math.min(200, Math.max(1, input.limit ?? 50));
|
|
5203
|
+
const offset = Math.max(0, input.offset ?? 0);
|
|
5204
|
+
const page = rows.slice(offset, offset + limit);
|
|
5205
|
+
const publicTotals =
|
|
5206
|
+
view !== "cost_monitor_zaip_agents"
|
|
5207
|
+
? {
|
|
5208
|
+
entity_count: totals.entity_count,
|
|
5209
|
+
invocation_count: totals.invocation_count,
|
|
5210
|
+
total_duration: totals.total_duration,
|
|
5211
|
+
total_tokens: totals.total_tokens,
|
|
5212
|
+
total_cost_usd: totals.total_cost_usd,
|
|
5213
|
+
total_cost_brl: totals.total_cost_brl,
|
|
5214
|
+
}
|
|
5215
|
+
: {
|
|
5216
|
+
invocation_count: totals.invocation_count,
|
|
5217
|
+
task_count: totals.task_count,
|
|
5218
|
+
total_duration: totals.total_duration,
|
|
5219
|
+
duration_unknown: totals.duration_unknown,
|
|
5220
|
+
duration_known: totals.duration_known,
|
|
5221
|
+
source_event_count: totals.source_event_count,
|
|
5222
|
+
total_tokens: totals.total_tokens,
|
|
5223
|
+
total_cost_usd: totals.total_cost_usd,
|
|
5224
|
+
total_cost_brl: totals.total_cost_brl,
|
|
5225
|
+
};
|
|
5226
|
+
return {
|
|
5227
|
+
data_source: "sqlite_read_model" as const,
|
|
5228
|
+
view,
|
|
5229
|
+
offset,
|
|
5230
|
+
limit,
|
|
5231
|
+
total_count: rows.length,
|
|
5232
|
+
has_more: offset + page.length < rows.length,
|
|
5233
|
+
totals: publicTotals,
|
|
5234
|
+
rows: page,
|
|
5235
|
+
};
|
|
5236
|
+
}
|
|
5237
|
+
}
|
|
5100
5238
|
const cacheableUnfilteredViews: readonly SqliteViewName[] = [
|
|
5101
5239
|
"operations_work_kanban",
|
|
5102
5240
|
"operations_executions",
|
|
@@ -5132,31 +5270,18 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
5132
5270
|
rows: page,
|
|
5133
5271
|
};
|
|
5134
5272
|
}
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
!input.from &&
|
|
5143
|
-
!input.to &&
|
|
5144
|
-
!input.query &&
|
|
5145
|
-
!input.status &&
|
|
5146
|
-
!input.risk
|
|
5147
|
-
) {
|
|
5148
|
-
const rows = this.operationsWorkListRows();
|
|
5149
|
-
const limit = Math.min(200, Math.max(1, input.limit ?? 50));
|
|
5150
|
-
const offset = Math.max(0, input.offset ?? 0);
|
|
5151
|
-
const page = rows.slice(offset, offset + limit);
|
|
5273
|
+
if (view === "operations_work_list") {
|
|
5274
|
+
const payload =
|
|
5275
|
+
input.from || input.to
|
|
5276
|
+
? this.firstFoldMonthlyQuick({ from: input.from ?? null, to: input.to ?? null })
|
|
5277
|
+
: this.technicalPayloadRows("first_fold_all_time_board_cache")[0];
|
|
5278
|
+
const cached = operationsWorkListPayloadPage(input, payload);
|
|
5279
|
+
const result = cached ?? operationsWorkListSqlPage(this.database, input);
|
|
5152
5280
|
return {
|
|
5153
5281
|
data_source: "sqlite_read_model" as const,
|
|
5154
5282
|
view,
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
total_count: rows.length,
|
|
5158
|
-
has_more: offset + page.length < rows.length,
|
|
5159
|
-
rows: page,
|
|
5283
|
+
...result,
|
|
5284
|
+
rows: attachOperationsWorkListDevelopers(this.database, result.rows, input),
|
|
5160
5285
|
};
|
|
5161
5286
|
}
|
|
5162
5287
|
if (view === "cost_monitor_agent_invocations") {
|
|
@@ -5210,13 +5335,15 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
5210
5335
|
const total_count = Number(
|
|
5211
5336
|
(
|
|
5212
5337
|
this.database
|
|
5213
|
-
.prepare(
|
|
5338
|
+
.prepare(
|
|
5339
|
+
`SELECT COUNT(*) AS count FROM cost_monitor_agent_invocations i WHERE ${where}`,
|
|
5340
|
+
)
|
|
5214
5341
|
.get(...values) as { count: number }
|
|
5215
5342
|
).count,
|
|
5216
5343
|
);
|
|
5217
5344
|
const rows = this.database
|
|
5218
5345
|
.prepare(
|
|
5219
|
-
`SELECT i.* FROM
|
|
5346
|
+
`SELECT i.* FROM cost_monitor_agent_invocations i WHERE ${where} ORDER BY i.occurred_at DESC, i.agent_invocation_id DESC LIMIT ? OFFSET ?`,
|
|
5220
5347
|
)
|
|
5221
5348
|
.all(...values, limit, offset) as Array<Record<string, unknown>>;
|
|
5222
5349
|
return {
|
|
@@ -5240,6 +5367,35 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
5240
5367
|
cost_monitor_providers: "provider",
|
|
5241
5368
|
};
|
|
5242
5369
|
const filteredCostGroup = filteredCostGroups[view];
|
|
5370
|
+
if (
|
|
5371
|
+
(filteredCostGroup === "agent" ||
|
|
5372
|
+
filteredCostGroup === "model" ||
|
|
5373
|
+
filteredCostGroup === "provider") &&
|
|
5374
|
+
isDateOnlyCostPage(input)
|
|
5375
|
+
) {
|
|
5376
|
+
const from = costMonitorFilterDay(input.from) ?? "0000-01-01";
|
|
5377
|
+
const to = costMonitorFilterDay(input.to) ?? "9999-12-31";
|
|
5378
|
+
const limit = Math.min(200, Math.max(1, input.limit ?? 50));
|
|
5379
|
+
const offset = Math.max(0, input.offset ?? 0);
|
|
5380
|
+
const fast = readDateOnlyCostDimensionPage(
|
|
5381
|
+
this.database,
|
|
5382
|
+
filteredCostGroup,
|
|
5383
|
+
from,
|
|
5384
|
+
to,
|
|
5385
|
+
limit,
|
|
5386
|
+
offset,
|
|
5387
|
+
);
|
|
5388
|
+
return {
|
|
5389
|
+
data_source: "sqlite_read_model" as const,
|
|
5390
|
+
view,
|
|
5391
|
+
offset,
|
|
5392
|
+
limit,
|
|
5393
|
+
total_count: fast.total_count,
|
|
5394
|
+
has_more: offset + fast.rows.length < fast.total_count,
|
|
5395
|
+
totals: fast.totals,
|
|
5396
|
+
rows: fast.rows,
|
|
5397
|
+
};
|
|
5398
|
+
}
|
|
5243
5399
|
const isUnfilteredCostDimension =
|
|
5244
5400
|
(filteredCostGroup === "model" || filteredCostGroup === "provider") &&
|
|
5245
5401
|
!input.from &&
|
|
@@ -5438,20 +5594,23 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
5438
5594
|
activityValues.push(query, query, query);
|
|
5439
5595
|
}
|
|
5440
5596
|
const where = clauses.join(" AND ");
|
|
5441
|
-
const invocationClauses = [
|
|
5442
|
-
"i.zaip_agent IS NOT NULL",
|
|
5443
|
-
"(i.total_tokens IS NOT NULL OR i.cost_brl IS NOT NULL OR i.cost_usd IS NOT NULL)",
|
|
5444
|
-
];
|
|
5597
|
+
const invocationClauses = ["i.zaip_agent IS NOT NULL"];
|
|
5445
5598
|
const invocationValues: Array<string | number> = [];
|
|
5446
5599
|
const invocationSourceDay =
|
|
5447
5600
|
"date(COALESCE(i.source_started_at, i.source_occurred_at, i.occurred_at), '-3 hours')";
|
|
5448
5601
|
if (input.from) {
|
|
5449
5602
|
invocationClauses.push(`${invocationSourceDay} >= ?`);
|
|
5450
5603
|
invocationValues.push(input.from.slice(0, 10));
|
|
5604
|
+
// Keep the source-day predicate for historical correctness, while
|
|
5605
|
+
// using the materialized day key to seek the immutable fact first.
|
|
5606
|
+
invocationClauses.push("i.occurred_day_sp >= ?");
|
|
5607
|
+
invocationValues.push(input.from.slice(0, 10));
|
|
5451
5608
|
}
|
|
5452
5609
|
if (input.to) {
|
|
5453
5610
|
invocationClauses.push(`${invocationSourceDay} <= ?`);
|
|
5454
5611
|
invocationValues.push(input.to.slice(0, 10));
|
|
5612
|
+
invocationClauses.push("i.occurred_day_sp <= ?");
|
|
5613
|
+
invocationValues.push(input.to.slice(0, 10));
|
|
5455
5614
|
}
|
|
5456
5615
|
if (input.agent) {
|
|
5457
5616
|
invocationClauses.push("i.zaip_agent = ?");
|
|
@@ -5551,6 +5710,7 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
5551
5710
|
if (filteredCostGroup === "model") {
|
|
5552
5711
|
const clauses = ["1=1"];
|
|
5553
5712
|
const values: Array<string | number> = [];
|
|
5713
|
+
const dimensionDateFilter = costDimensionFactDateFilter(input);
|
|
5554
5714
|
if (input.from) {
|
|
5555
5715
|
clauses.push(
|
|
5556
5716
|
"date(COALESCE(i.source_started_at, i.source_occurred_at, i.occurred_at), '-3 hours') >= ?",
|
|
@@ -5677,7 +5837,7 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
5677
5837
|
SUM(output_tokens) AS output_tokens, SUM(reasoning_tokens) AS reasoning_tokens,
|
|
5678
5838
|
SUM(total_tokens) AS total_tokens, SUM(cost_usd) AS cost_usd, SUM(cost_brl) AS cost_brl
|
|
5679
5839
|
FROM cost_invocation_dimension_facts
|
|
5680
|
-
WHERE total_tokens IS NOT NULL OR cost_usd IS NOT NULL OR cost_brl IS NOT NULL
|
|
5840
|
+
WHERE (total_tokens IS NOT NULL OR cost_usd IS NOT NULL OR cost_brl IS NOT NULL)${dimensionDateFilter}
|
|
5681
5841
|
GROUP BY agent_invocation_id
|
|
5682
5842
|
), segments AS (
|
|
5683
5843
|
SELECT agent_invocation_id,
|
|
@@ -5687,7 +5847,7 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
5687
5847
|
SUM(output_tokens) AS output_tokens, SUM(reasoning_tokens) AS reasoning_tokens,
|
|
5688
5848
|
SUM(total_tokens) AS total_tokens, SUM(cost_usd) AS cost_usd, SUM(cost_brl) AS cost_brl
|
|
5689
5849
|
FROM cost_invocation_dimension_facts
|
|
5690
|
-
WHERE total_tokens IS NOT NULL OR cost_usd IS NOT NULL OR cost_brl IS NOT NULL
|
|
5850
|
+
WHERE (total_tokens IS NOT NULL OR cost_usd IS NOT NULL OR cost_brl IS NOT NULL)${dimensionDateFilter}
|
|
5691
5851
|
GROUP BY agent_invocation_id, model_key, provider
|
|
5692
5852
|
UNION ALL
|
|
5693
5853
|
-- Preserve every factual residual from the consolidated invocation:
|
|
@@ -5800,6 +5960,7 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
5800
5960
|
if (filteredCostGroup === "provider") {
|
|
5801
5961
|
const clauses = ["1=1"];
|
|
5802
5962
|
const values: Array<string | number> = [];
|
|
5963
|
+
const dimensionDateFilter = costDimensionFactDateFilter(input);
|
|
5803
5964
|
if (input.from) {
|
|
5804
5965
|
clauses.push(
|
|
5805
5966
|
"date(COALESCE(i.source_started_at, i.source_occurred_at, i.occurred_at), '-3 hours') >= ?",
|
|
@@ -5863,7 +6024,7 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
5863
6024
|
SUM(output_tokens) AS output_tokens, SUM(reasoning_tokens) AS reasoning_tokens,
|
|
5864
6025
|
SUM(total_tokens) AS total_tokens, SUM(cost_usd) AS cost_usd, SUM(cost_brl) AS cost_brl
|
|
5865
6026
|
FROM cost_invocation_dimension_facts
|
|
5866
|
-
WHERE total_tokens IS NOT NULL OR cost_usd IS NOT NULL OR cost_brl IS NOT NULL
|
|
6027
|
+
WHERE (total_tokens IS NOT NULL OR cost_usd IS NOT NULL OR cost_brl IS NOT NULL)${dimensionDateFilter}
|
|
5867
6028
|
GROUP BY agent_invocation_id
|
|
5868
6029
|
), segments AS (
|
|
5869
6030
|
SELECT agent_invocation_id,
|
|
@@ -5873,7 +6034,7 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
5873
6034
|
SUM(output_tokens) AS output_tokens, SUM(reasoning_tokens) AS reasoning_tokens,
|
|
5874
6035
|
SUM(total_tokens) AS total_tokens, SUM(cost_usd) AS cost_usd, SUM(cost_brl) AS cost_brl
|
|
5875
6036
|
FROM cost_invocation_dimension_facts
|
|
5876
|
-
WHERE total_tokens IS NOT NULL OR cost_usd IS NOT NULL OR cost_brl IS NOT NULL
|
|
6037
|
+
WHERE (total_tokens IS NOT NULL OR cost_usd IS NOT NULL OR cost_brl IS NOT NULL)${dimensionDateFilter}
|
|
5877
6038
|
GROUP BY agent_invocation_id, provider_key, model_key
|
|
5878
6039
|
UNION ALL
|
|
5879
6040
|
-- See the equivalent Models branch: attribute only the segment part;
|
|
@@ -5973,10 +6134,6 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
5973
6134
|
if (filteredCostGroup !== undefined && temporalCostGroup && !hasDimensionalCostFilter) {
|
|
5974
6135
|
const from = costMonitorFilterDay(input.from);
|
|
5975
6136
|
const to = costMonitorFilterDay(input.to);
|
|
5976
|
-
// Cost temporal tables reuse the same aggregate-only projection as the
|
|
5977
|
-
// summary cards. This intentionally bypasses daily_aggregate_cache,
|
|
5978
|
-
// whose recursive all-dashboard view used to expand the complete fact
|
|
5979
|
-
// graph before returning a handful of visible columns.
|
|
5980
6137
|
const aggregate = this.costMonitorSummaryAggregate({ from, to });
|
|
5981
6138
|
const financialRows = aggregate.daily_rows as Array<Record<string, unknown>>;
|
|
5982
6139
|
const financialByDay = new Map(financialRows.map((row) => [String(row.key), row]));
|
|
@@ -6629,130 +6786,7 @@ export class SqliteReadModelFileSystem extends WorkspaceFileSystem {
|
|
|
6629
6786
|
rows,
|
|
6630
6787
|
};
|
|
6631
6788
|
}
|
|
6632
|
-
|
|
6633
|
-
task: {
|
|
6634
|
-
table: "tasks",
|
|
6635
|
-
title: "entity.title",
|
|
6636
|
-
status: "entity.status",
|
|
6637
|
-
risk: "entity.risk",
|
|
6638
|
-
},
|
|
6639
|
-
spec: {
|
|
6640
|
-
table: "specs",
|
|
6641
|
-
title: "entity.title",
|
|
6642
|
-
status: "entity.status",
|
|
6643
|
-
risk: "entity.risk",
|
|
6644
|
-
},
|
|
6645
|
-
prd: { table: "prds", title: "entity.title", status: "entity.status", risk: "entity.risk" },
|
|
6646
|
-
execution: {
|
|
6647
|
-
table: "executions",
|
|
6648
|
-
title: "grouped.group_value",
|
|
6649
|
-
status: "entity.status",
|
|
6650
|
-
risk: "NULL",
|
|
6651
|
-
},
|
|
6652
|
-
conversation: {
|
|
6653
|
-
table: "conversations",
|
|
6654
|
-
title: "grouped.group_value",
|
|
6655
|
-
status: "entity.status",
|
|
6656
|
-
risk: "NULL",
|
|
6657
|
-
},
|
|
6658
|
-
} as const;
|
|
6659
|
-
const metadata = entityMetadata[groupKind as keyof typeof entityMetadata];
|
|
6660
|
-
const clauses = ["group_kind = ?", "group_value IS NOT NULL"];
|
|
6661
|
-
const values: Array<string | number> = [groupKind];
|
|
6662
|
-
if (input.from) {
|
|
6663
|
-
clauses.push("day >= ?");
|
|
6664
|
-
values.push(input.from.slice(0, 10));
|
|
6665
|
-
}
|
|
6666
|
-
if (input.to) {
|
|
6667
|
-
clauses.push("day <= ?");
|
|
6668
|
-
values.push(input.to.slice(0, 10));
|
|
6669
|
-
}
|
|
6670
|
-
if (input.query) {
|
|
6671
|
-
clauses.push("group_value LIKE ?");
|
|
6672
|
-
values.push(`%${input.query}%`);
|
|
6673
|
-
}
|
|
6674
|
-
const groupFilter =
|
|
6675
|
-
groupKind === "provider"
|
|
6676
|
-
? input.provider
|
|
6677
|
-
: groupKind === "model"
|
|
6678
|
-
? input.model
|
|
6679
|
-
: groupKind === "zaip_agent"
|
|
6680
|
-
? input.agent
|
|
6681
|
-
: undefined;
|
|
6682
|
-
if (groupFilter) {
|
|
6683
|
-
clauses.push("group_value = ?");
|
|
6684
|
-
values.push(groupFilter);
|
|
6685
|
-
}
|
|
6686
|
-
const where = clauses.join(" AND ");
|
|
6687
|
-
const total_count = Number(
|
|
6688
|
-
(
|
|
6689
|
-
this.database
|
|
6690
|
-
.prepare(
|
|
6691
|
-
`SELECT COUNT(DISTINCT group_value) AS count FROM cost_monitor_group_daily_cache WHERE ${where}`,
|
|
6692
|
-
)
|
|
6693
|
-
.get(...values) as { count: number }
|
|
6694
|
-
).count,
|
|
6695
|
-
);
|
|
6696
|
-
// This aggregate deliberately precedes LIMIT/OFFSET: a compact page is
|
|
6697
|
-
// only a window into the selected entity population, never its subtotal.
|
|
6698
|
-
// SUM preserves a factual 0 and leaves a wholly unavailable metric NULL.
|
|
6699
|
-
const totals = this.database
|
|
6700
|
-
.prepare(
|
|
6701
|
-
`SELECT COUNT(DISTINCT group_value) AS entity_count,
|
|
6702
|
-
COALESCE(SUM(invocation_count), 0) AS invocation_count,
|
|
6703
|
-
ROUND(SUM(total_duration)) AS total_duration,
|
|
6704
|
-
SUM(total_tokens) AS total_tokens,
|
|
6705
|
-
SUM(total_cost_usd) AS total_cost_usd,
|
|
6706
|
-
SUM(total_cost_brl) AS total_cost_brl
|
|
6707
|
-
FROM cost_monitor_group_daily_cache
|
|
6708
|
-
WHERE ${where}`,
|
|
6709
|
-
)
|
|
6710
|
-
.get(...values) as Record<string, unknown>;
|
|
6711
|
-
const limit = Math.min(200, Math.max(1, input.limit ?? 50));
|
|
6712
|
-
const offset = Math.max(0, input.offset ?? 0);
|
|
6713
|
-
const rows = this.database
|
|
6714
|
-
.prepare(
|
|
6715
|
-
`WITH grouped AS (
|
|
6716
|
-
SELECT group_value, MIN(day) AS first_day, MAX(day) AS last_day,
|
|
6717
|
-
SUM(invocation_count) AS invocation_count,
|
|
6718
|
-
SUM(llm_call_count) AS llm_call_count,
|
|
6719
|
-
SUM(total_duration) AS total_duration,
|
|
6720
|
-
SUM(total_input_tokens) AS total_input_tokens,
|
|
6721
|
-
SUM(total_cached_input_tokens) AS total_cached_input_tokens,
|
|
6722
|
-
SUM(total_output_tokens) AS total_output_tokens,
|
|
6723
|
-
SUM(total_reasoning_tokens) AS total_reasoning_tokens,
|
|
6724
|
-
SUM(total_tokens) AS total_tokens,
|
|
6725
|
-
SUM(total_cost_usd) AS total_cost_usd,
|
|
6726
|
-
SUM(total_cost_brl) AS total_cost_brl
|
|
6727
|
-
FROM cost_monitor_group_daily_cache
|
|
6728
|
-
WHERE ${where}
|
|
6729
|
-
GROUP BY group_value
|
|
6730
|
-
)
|
|
6731
|
-
SELECT grouped.group_value AS id, grouped.group_value AS key,
|
|
6732
|
-
COALESCE(${metadata.title}, grouped.group_value) AS title,
|
|
6733
|
-
${metadata.status} AS status, ${metadata.risk} AS risk,
|
|
6734
|
-
grouped.first_day, grouped.last_day,
|
|
6735
|
-
grouped.last_day || 'T12:00:00.000Z' AS occurred_at,
|
|
6736
|
-
grouped.invocation_count, grouped.llm_call_count, grouped.total_duration,
|
|
6737
|
-
grouped.total_input_tokens, grouped.total_cached_input_tokens,
|
|
6738
|
-
grouped.total_output_tokens, grouped.total_reasoning_tokens,
|
|
6739
|
-
grouped.total_tokens, grouped.total_cost_usd, grouped.total_cost_brl
|
|
6740
|
-
FROM grouped
|
|
6741
|
-
LEFT JOIN ${metadata.table} entity ON entity.id = grouped.group_value
|
|
6742
|
-
ORDER BY grouped.total_cost_brl DESC, grouped.group_value
|
|
6743
|
-
LIMIT ? OFFSET ?`,
|
|
6744
|
-
)
|
|
6745
|
-
.all(...values, limit, offset) as Array<Record<string, unknown>>;
|
|
6746
|
-
return {
|
|
6747
|
-
data_source: "sqlite_read_model" as const,
|
|
6748
|
-
view,
|
|
6749
|
-
offset,
|
|
6750
|
-
limit,
|
|
6751
|
-
total_count,
|
|
6752
|
-
has_more: offset + rows.length < total_count,
|
|
6753
|
-
totals,
|
|
6754
|
-
rows,
|
|
6755
|
-
};
|
|
6789
|
+
return readCostGroupPage(this.database, view, groupKind as CostGroupKind, input);
|
|
6756
6790
|
}
|
|
6757
6791
|
const meta = sqliteViewMetadata[view];
|
|
6758
6792
|
const clauses: string[] = [];
|