@zaip-ai/cli 4.5.0 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/services/adapters.d.ts +1 -1
- package/dist/services/adapters.js +4 -4
- package/dist/services/adapters.js.map +1 -1
- package/dist/services/codex-native-tool-authority-template.d.ts.map +1 -1
- package/dist/services/codex-native-tool-authority-template.js +188 -9
- package/dist/services/codex-native-tool-authority-template.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 +50 -128
- package/dist/services/codex-pre-tool-hook-template.js.map +1 -1
- package/dist/services/codex-pre-tool-hook-template.test.js +4 -0
- package/dist/services/codex-pre-tool-hook-template.test.js.map +1 -1
- package/dist/services/codex-pre-tool-support-template.d.ts.map +1 -1
- package/dist/services/codex-pre-tool-support-template.js +1 -4
- package/dist/services/codex-pre-tool-support-template.js.map +1 -1
- package/dist/services/codex-stale-git-lock-template.d.ts +2 -0
- package/dist/services/codex-stale-git-lock-template.d.ts.map +1 -0
- package/dist/services/codex-stale-git-lock-template.js +64 -0
- package/dist/services/codex-stale-git-lock-template.js.map +1 -0
- package/dist/services/global-codex-config-migration.test.js +1 -4
- package/dist/services/global-codex-config-migration.test.js.map +1 -1
- package/dist/services/install-template-policy.d.ts.map +1 -1
- package/dist/services/install-template-policy.js +2 -0
- package/dist/services/install-template-policy.js.map +1 -1
- package/dist/services/logger.js +2 -1
- package/dist/services/logger.js.map +1 -1
- package/dist/services/logger.test.js +71 -0
- package/dist/services/logger.test.js.map +1 -1
- package/dist/services/runtime-template-test-support.d.ts +3 -0
- package/dist/services/runtime-template-test-support.d.ts.map +1 -0
- package/dist/services/runtime-template-test-support.js +32 -0
- package/dist/services/runtime-template-test-support.js.map +1 -0
- package/dist/services/runtime-template.test.js +23 -31
- package/dist/services/runtime-template.test.js.map +1 -1
- package/package.json +1 -1
- package/templates/.agents/README.md +8 -0
- package/templates/.agents/config/agents/discovery.json +1 -1
- package/templates/.agents/config/deploy-policy.json +5 -0
- package/templates/.agents/dashboard/.zaip-managed.json +211 -75
- package/templates/.agents/dashboard/README.md +2 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-f4ZGJRg3.js → CostCalculationGuidePage-BRtiPijr.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-CkQ_y8kg.js → DashboardHelpDialog-BR4ZeYpE.js} +99 -10
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-v9zGRTlk.js → PricingSettingsDialog-Dr4QDFmn.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-doIMhei7.js → ProjectSettingsPage-BG0N5dw_.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-C_4ftjyB.js → TraceabilityWorkspace-BvoM-YQ0.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-fTQhSL85.js +68 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-Cv3WBFIF.js → icons-vendor-Bc6vux5t.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-BMfzKaAR.js +2 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-DZhppOIk.css +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-mHgsmxb6.js → react-vendor-DNJQFov3.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-B0CPyEVq.js → traceability-B7tjIB7W.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/index.html +6 -6
- package/templates/.agents/dashboard/apps/frontend/package.json +1 -1
- package/templates/.agents/dashboard/apps/frontend/src/AppOverlays.tsx +6 -34
- package/templates/.agents/dashboard/apps/frontend/src/ReconciliationFeedback.dom.test.ts +130 -0
- package/templates/.agents/dashboard/apps/frontend/src/ReconciliationFeedback.tsx +226 -0
- package/templates/.agents/dashboard/apps/frontend/src/dashboard-api-sqlite-view.ts +45 -0
- package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.test.ts +15 -0
- package/templates/.agents/dashboard/apps/frontend/src/dashboard-api.ts +17 -46
- package/templates/.agents/dashboard/apps/frontend/src/reconciliation-feedback-fixtures.ts +23 -0
- package/templates/.agents/dashboard/apps/frontend/src/reconciliation-feedback-model.test.ts +69 -0
- package/templates/.agents/dashboard/apps/frontend/src/reconciliation-feedback-model.ts +59 -0
- package/templates/.agents/dashboard/apps/frontend/src/reconciliation-feedback.css +131 -0
- package/templates/.agents/dashboard/apps/frontend/src/styles.css +0 -32
- package/templates/.agents/dashboard/apps/frontend/src/use-dashboard-app-model.tsx +2 -2
- package/templates/.agents/dashboard/modules/cost-monitor/package.json +1 -1
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.test.ts +2 -2
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +1 -1
- package/templates/.agents/dashboard/modules/operations/package.json +1 -1
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +1 -4
- package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkBoard.navigation.test.ts +236 -0
- package/templates/.agents/dashboard/modules/operations/src/react/UnifiedWorkBoard.tsx +1 -24
- package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +3 -2
- package/templates/.agents/dashboard/modules/traceability/package.json +1 -1
- package/templates/.agents/dashboard/packages/branding/package.json +1 -1
- package/templates/.agents/dashboard/packages/contracts/package.json +1 -1
- package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
- package/templates/.agents/dashboard/packages/local-runtime/DIAGNOSTICS.md +42 -0
- package/templates/.agents/dashboard/packages/local-runtime/dashboard-diagnostics.d.mts +10 -0
- package/templates/.agents/dashboard/packages/local-runtime/dashboard-diagnostics.mjs +101 -0
- package/templates/.agents/dashboard/packages/local-runtime/dashboard-diagnostics.test.mjs +58 -0
- package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +232 -217
- package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +201 -188
- package/templates/.agents/dashboard/packages/local-runtime/package.json +1 -1
- package/templates/.agents/dashboard/packages/local-runtime/scripts/test-sqlite-dashboard-startup.mjs +233 -0
- package/templates/.agents/dashboard/packages/local-runtime/scripts/validate-provider-nonzero.mjs +45 -0
- package/templates/.agents/dashboard/packages/local-runtime/scripts/verify-dashboard-executable-startup.mjs +107 -0
- package/templates/.agents/dashboard/packages/local-runtime/scripts/verify-dashboard-reconciliation-executable.mjs +271 -0
- package/templates/.agents/dashboard/packages/local-runtime/server.mjs +232 -217
- package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.test.ts +78 -12
- package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.ts +36 -56
- package/templates/.agents/dashboard/packages/local-runtime/src/canonical-source-revision-cache.test.ts +125 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/canonical-source-revision-cache.ts +168 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/dashboard-diagnostics.test.ts +57 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-agent-catalog-routes.ts +76 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-dual-reads-manifest-summaries-without-losing.test.ts +5 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-diagnostics.test.ts +118 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-result.test.ts +35 -6
- package/templates/.agents/dashboard/packages/local-runtime/src/server-reconciliation-route.ts +105 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-synchronization.test.ts +64 -9
- package/templates/.agents/dashboard/packages/local-runtime/src/server-snapshot-synchronization.ts +19 -27
- package/templates/.agents/dashboard/packages/local-runtime/src/server-telemetry-command.test.ts +111 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-telemetry-refresh.test.ts +59 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-telemetry-refresh.ts +44 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.test.ts +20 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-work-reconciliation.ts +12 -7
- package/templates/.agents/dashboard/packages/local-runtime/src/server-workspace.ts +48 -6
- package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +2 -2
- package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +126 -134
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-agent-page.ts +14 -2
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-dashboard-startup.test.ts +163 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-dashboard-startup.ts +78 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-header-timestamp.test.ts +31 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-provider-nonzero.test.ts +271 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-provider-visibility.ts +32 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-operations-duration.ts +58 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-provider-dimension.ts +12 -3
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-view-page.ts +9 -58
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-rebuild.ts +9 -35
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-schema.ts +10 -8
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-source-fingerprint.ts +43 -45
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +5 -9
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +5 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-startup-seed.test.ts +87 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-logging.mjs +6 -2
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-server.mjs +7 -1
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +4 -5
- package/templates/.agents/dashboard/packages/ui/package.json +1 -1
- package/templates/.agents/dashboard/packages/ui/src/DashboardHeader.test.mjs +33 -4
- package/templates/.agents/dashboard/packages/ui/src/DashboardHeader.tsx +8 -7
- package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +46 -3
- package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
- package/templates/.agents/dashboard/tests/e2e/qualify-kanban-popup.mjs +56 -0
- package/templates/.agents/dashboard/tests/e2e/specs/dashboard-update-label.spec.ts +49 -0
- package/templates/.agents/design/.zaip-managed.json +17 -5
- package/templates/.agents/design/apps/catalog/dist/assets/index-D3uhpycC.js +72 -0
- package/templates/.agents/design/apps/catalog/dist/index.html +1 -1
- package/templates/.agents/design/apps/catalog/package.json +1 -1
- package/templates/.agents/design/apps/catalog/src/pages/ComponentsPage.tsx +13 -1
- package/templates/.agents/design/apps/catalog/src/specimens/ReconciliationSpecimen.stories.tsx +6 -0
- package/templates/.agents/design/apps/catalog/src/specimens/ReconciliationSpecimen.tsx +101 -0
- package/templates/.agents/design/packages/local-runtime/package.json +1 -1
- package/templates/.agents/requirements/autonomous-assurance.md +1 -1
- package/templates/.agents/requirements/cloud-hostinger-autonomy.md +1 -1
- 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 +1 -1
- package/templates/.agents/requirements/kanban-dashboard.md +1 -1
- package/templates/.agents/requirements/model-orchestration.md +21 -6
- package/templates/.agents/requirements/public-distribution.md +4 -4
- 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 +1 -1
- package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
- package/templates/.agents/rules/core.md +3 -1
- package/templates/.agents/rules/workflow.md +3 -2
- package/templates/.agents/runtime/VERSION +1 -1
- package/templates/.agents/runtime/bin/zaip.mjs +366 -196
- package/templates/.agents/runtime/checksums.sha256 +5 -5
- package/templates/.agents/runtime/manifest.json +1 -1
- package/templates/.agents/runtime/provenance.json +4 -4
- package/templates/.agents/runtime/sbom.cdx.json +1 -1
- package/templates/.agents/skills/zaip-skill-config/SKILL.md +25 -3
- package/templates/.agents/skills/zaip-skill-execution/SKILL.md +4 -0
- package/templates/.agents/skills/zaip-skill-execution/references/autonomous-continuation.md +4 -0
- package/templates/.agents/skills/zaip-skill-execution/references/candidate-workflow.md +1 -1
- package/templates/.agents/skills/zaip-skill-execution/references/runtime-governance.md +2 -2
- package/templates/.agents/skills/zaip-skill-general-operations/SKILL.md +1 -0
- package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +36 -48
- package/templates/.agents/skills/zaip-skill-github-deploy/agents/openai.yaml +2 -2
- package/templates/.agents/skills/zaip-skill-github-deploy/references/deployment-workflow.md +67 -99
- package/templates/.agents/skills/zaip-skill-github-deploy/references/release-identity.md +33 -30
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/deploy-contract.mjs +303 -0
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/deploy-contract.test.mjs +334 -0
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/github-deploy-contract.test.mjs +41 -28
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/guarded-commits.mjs +212 -0
- package/templates/.agents/skills/zaip-skill-lifecycle-closure/SKILL.md +4 -0
- package/templates/.agents/skills/zaip-skill-prd/SKILL.md +4 -0
- package/templates/.agents/skills/zaip-skill-release-qualification/SKILL.md +44 -68
- package/templates/.agents/skills/zaip-skill-release-qualification/references/local-preparation.md +91 -44
- package/templates/.agents/skills/zaip-skill-send-email/SKILL.md +15 -0
- package/templates/.agents/skills/zaip-skill-send-email/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-setup/SKILL.md +5 -2
- package/templates/.agents/skills/zaip-skill-spec/SKILL.md +4 -0
- package/templates/.agents/skills/zaip-skill-task/SKILL.md +4 -0
- package/templates/.agents/skills/zaip-skill-testing-strategy/SKILL.md +19 -0
- package/templates/.agents/skills/zaip-skill-tool-setup/SKILL.md +2 -0
- package/templates/.agents/skills/zaip-skill-use-connectors/SKILL.md +15 -0
- package/templates/.agents/skills/zaip-skill-use-connectors/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-use-connectors/scripts/connector-guidance.test.mjs +29 -0
- package/templates/.agents/skills/zaip-skill-use-connectors/scripts/verify-connector-lock.mjs +30 -0
- package/templates/.agents/tools/cloud-runner.integration.test.mjs +3 -0
- package/templates/.agents/zaip-aios/VERSION +1 -1
- package/templates/.agents/zaip-aios/guides/03-tasks.md +6 -0
- package/templates/.agents/zaip-aios/guides/04-specs.md +6 -0
- package/templates/.agents/zaip-aios/guides/05-prds.md +4 -0
- package/templates/.agents/zaip-aios/guides/06-execucao.md +6 -0
- package/templates/.agents/zaip-aios/guides/16-lifecycle-e-reconciliacao.md +6 -0
- package/templates/.agents/zaip-aios/guides/19-assurance-e-autonomia.md +4 -0
- package/templates/.agents/zaip-aios/guides/22-fluxos-e-roteamento.md +6 -0
- package/templates/.agents/zaip-aios/guides/23-orquestracao-de-modelos.md +36 -4
- package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +15 -4
- package/templates/.agents/zaip-aios/guides/28-lifecycle-completo.md +8 -0
- package/templates/template-manifest.json +330 -142
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-D7PrDZy9.js +0 -68
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-OBJXZOwe.css +0 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-Rbmpz4UO.js +0 -2
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import { rebuildSqliteReadModel } from "./sqlite-read-model-rebuild.js";
|
|
3
|
+
import {
|
|
4
|
+
hasReusableSnapshotPayloads,
|
|
5
|
+
sqliteReadModelPath,
|
|
6
|
+
validateSqliteReadModelSchema,
|
|
7
|
+
} from "./sqlite-read-model-schema.js";
|
|
8
|
+
import type { SqliteRebuildOptions } from "./sqlite-rebuild-fingerprint-options.js";
|
|
9
|
+
import type { SqliteRebuildResult } from "./sqlite-rebuild-result.js";
|
|
10
|
+
|
|
11
|
+
export type SqliteDashboardStartupOptions = Pick<
|
|
12
|
+
SqliteRebuildOptions,
|
|
13
|
+
"forceCanonical" | "sourceRoots"
|
|
14
|
+
>;
|
|
15
|
+
|
|
16
|
+
function hasReadableFirstFoldPayloads(database: DatabaseSync): boolean {
|
|
17
|
+
const rows = database
|
|
18
|
+
.prepare(`SELECT payload_json FROM zaip_aios_activities
|
|
19
|
+
WHERE payload_type IN ('first_fold_viewport_cache', 'first_fold_all_time_cache',
|
|
20
|
+
'first_fold_all_time_board_cache', 'cost_monitor_summary_cache',
|
|
21
|
+
'cost_monitor_summary_month_cache', 'first_fold_monthly_quick_cache')`)
|
|
22
|
+
.all() as Array<{ payload_json: string }>;
|
|
23
|
+
return rows.every(({ payload_json }) => {
|
|
24
|
+
const payload: unknown = JSON.parse(payload_json);
|
|
25
|
+
return payload !== null && typeof payload === "object" && !Array.isArray(payload);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Startup validates only the committed database. Canonical source discovery
|
|
31
|
+
* belongs to the asynchronous refresh after the server starts listening.
|
|
32
|
+
* Even an unchanged seed is stale until that refresh proves its freshness.
|
|
33
|
+
*/
|
|
34
|
+
export async function prepareDashboardSqliteStartup(
|
|
35
|
+
workspaceRoot: string,
|
|
36
|
+
options: SqliteDashboardStartupOptions = {},
|
|
37
|
+
): Promise<SqliteRebuildResult> {
|
|
38
|
+
const path = sqliteReadModelPath(workspaceRoot);
|
|
39
|
+
if (!options.forceCanonical) {
|
|
40
|
+
try {
|
|
41
|
+
validateSqliteReadModelSchema(path);
|
|
42
|
+
const database = new DatabaseSync(path, { readOnly: true });
|
|
43
|
+
try {
|
|
44
|
+
if (hasReusableSnapshotPayloads(database) && hasReadableFirstFoldPayloads(database)) {
|
|
45
|
+
const row = database
|
|
46
|
+
.prepare("SELECT value FROM zaip_vw_read_model_metadata WHERE key='source_file_count'")
|
|
47
|
+
.get() as { value?: unknown } | undefined;
|
|
48
|
+
const files =
|
|
49
|
+
typeof row?.value === "string" && /^\d+$/.test(row.value)
|
|
50
|
+
? Number(row.value)
|
|
51
|
+
: Number.NaN;
|
|
52
|
+
if (Number.isSafeInteger(files) && files >= 0) {
|
|
53
|
+
return {
|
|
54
|
+
files,
|
|
55
|
+
path,
|
|
56
|
+
cache: "stale_seed",
|
|
57
|
+
fingerprint_ms: 0,
|
|
58
|
+
materialize_ms: 0,
|
|
59
|
+
refresh: {
|
|
60
|
+
reused_sources: 0,
|
|
61
|
+
processed_sources: 0,
|
|
62
|
+
added_sources: 0,
|
|
63
|
+
changed_sources: 0,
|
|
64
|
+
removed_sources: 0,
|
|
65
|
+
canonical_runs_refolded: 0,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} finally {
|
|
71
|
+
database.close();
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
// Missing, damaged, incompatible or incomplete databases are never served.
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return rebuildSqliteReadModel(workspaceRoot, { ...options, preferExisting: false });
|
|
78
|
+
}
|
package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-header-timestamp.test.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { expect, it } from "vitest";
|
|
4
|
+
import { rebuildSqliteReadModel, SqliteReadModelFileSystem } from "./sqlite-read-model.js";
|
|
5
|
+
import { prepareMultiProviderFixture } from "./sqlite-read-model-multi-provider-fixture.js";
|
|
6
|
+
|
|
7
|
+
it("status-only reconciliation promotes the snapshot without faking a new token collection time", async () => {
|
|
8
|
+
const base = resolve(import.meta.dirname, "../../../../../../.tmp/dashboard-header-time");
|
|
9
|
+
await mkdir(base, { recursive: true });
|
|
10
|
+
const root = await mkdtemp(join(base, "fixture-"));
|
|
11
|
+
let filesystem: SqliteReadModelFileSystem | undefined;
|
|
12
|
+
try {
|
|
13
|
+
await prepareMultiProviderFixture(root);
|
|
14
|
+
await rebuildSqliteReadModel(root);
|
|
15
|
+
filesystem = new SqliteReadModelFileSystem(root);
|
|
16
|
+
const collected = filesystem.latestTokenCollectedAt();
|
|
17
|
+
const snapshot = filesystem.projectionUpdatedAt();
|
|
18
|
+
expect(collected).toBe("2026-08-04T16:00:01.000Z");
|
|
19
|
+
await mkdir(join(root, ".agents/records/tasks"), { recursive: true });
|
|
20
|
+
await writeFile(
|
|
21
|
+
join(root, ".agents/records/tasks/TASK-STATUS.md"),
|
|
22
|
+
"---\nschema_version: 2\nid: TASK-STATUS\ntitle: Status only\nstatus: blocked\ncreated_at: 2026-09-29T10:00:00Z\n---\n# Status\n",
|
|
23
|
+
);
|
|
24
|
+
await filesystem.rebuildAndRefresh(() => rebuildSqliteReadModel(root));
|
|
25
|
+
expect(filesystem.latestTokenCollectedAt()).toBe(collected);
|
|
26
|
+
expect(filesystem.projectionUpdatedAt()).not.toBe(snapshot);
|
|
27
|
+
} finally {
|
|
28
|
+
filesystem?.close();
|
|
29
|
+
await rm(root, { recursive: true, force: true });
|
|
30
|
+
}
|
|
31
|
+
});
|
package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-provider-nonzero.test.ts
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
3
|
+
import { hasVisibleProviderMetrics, providerVisibleMetrics } from "./sqlite-provider-visibility.js";
|
|
4
|
+
import { readCachedCostViewPage } from "./sqlite-query-cached-cost-page.js";
|
|
5
|
+
import type { SqliteQueryContext } from "./sqlite-query-context.js";
|
|
6
|
+
import { queryUncachedViewPage } from "./sqlite-query-view-page.js";
|
|
7
|
+
|
|
8
|
+
const databases: DatabaseSync[] = [];
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
while (databases.length) databases.pop()?.close();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
function fixture() {
|
|
14
|
+
const database = new DatabaseSync(":memory:");
|
|
15
|
+
databases.push(database);
|
|
16
|
+
database.exec(`
|
|
17
|
+
CREATE TABLE invocations (
|
|
18
|
+
agent_invocation_id TEXT, task_id TEXT, duration_ms REAL, source_event_count REAL,
|
|
19
|
+
input_tokens REAL, cached_input_tokens REAL, output_tokens REAL, reasoning_tokens REAL,
|
|
20
|
+
total_tokens REAL, cost_usd REAL, cost_brl REAL, source_started_at TEXT,
|
|
21
|
+
source_occurred_at TEXT, occurred_at TEXT, zaip_agent TEXT, phase TEXT,
|
|
22
|
+
status TEXT, work_type TEXT, prd_id TEXT, spec_id TEXT
|
|
23
|
+
);
|
|
24
|
+
CREATE TABLE tasks (id TEXT, status TEXT, priority TEXT, risk TEXT);
|
|
25
|
+
CREATE TABLE cost_invocation_dimension_facts (
|
|
26
|
+
agent_invocation_id TEXT, provider TEXT, model TEXT, input_tokens REAL,
|
|
27
|
+
cached_input_tokens REAL, output_tokens REAL, reasoning_tokens REAL,
|
|
28
|
+
total_tokens REAL, cost_usd REAL, cost_brl REAL, occurred_day_sp TEXT
|
|
29
|
+
);
|
|
30
|
+
CREATE TABLE zaip_aios_activities (
|
|
31
|
+
payload_type TEXT, payload_json TEXT, activity_source_kind TEXT,
|
|
32
|
+
agent_invocation_id TEXT, task_id TEXT, duration_ms REAL, source_event_count REAL,
|
|
33
|
+
input_tokens REAL, cached_input_tokens REAL, output_tokens REAL, reasoning_tokens REAL,
|
|
34
|
+
total_tokens REAL, cost_usd REAL, cost_brl REAL, occurred_day_sp TEXT
|
|
35
|
+
);
|
|
36
|
+
`);
|
|
37
|
+
const context = {
|
|
38
|
+
database,
|
|
39
|
+
technicalPayloadRows: () => [],
|
|
40
|
+
} as unknown as SqliteQueryContext;
|
|
41
|
+
const payload = (row: Record<string, unknown>) =>
|
|
42
|
+
database
|
|
43
|
+
.prepare("INSERT INTO zaip_aios_activities(payload_type,payload_json) VALUES (?,?)")
|
|
44
|
+
.run("cost_dimension_aggregate_v1", JSON.stringify({ dimension: "provider", ...row }));
|
|
45
|
+
const invocation = (id: string, tokens: number, duration: number | null = null) => {
|
|
46
|
+
database
|
|
47
|
+
.prepare(`INSERT INTO invocations
|
|
48
|
+
(agent_invocation_id,total_tokens,cost_usd,cost_brl,duration_ms,source_event_count,
|
|
49
|
+
source_started_at,phase) VALUES (?,?,0,0,?,9,'2026-09-29T12:00:00Z','execution')`)
|
|
50
|
+
.run(id, tokens, duration);
|
|
51
|
+
database
|
|
52
|
+
.prepare(`INSERT INTO zaip_aios_activities
|
|
53
|
+
(activity_source_kind,agent_invocation_id,total_tokens,cost_usd,cost_brl,
|
|
54
|
+
duration_ms,source_event_count,occurred_day_sp) VALUES ('agent_run',?,?,0,0,?,9,'2026-09-29')`)
|
|
55
|
+
.run(id, tokens, duration);
|
|
56
|
+
};
|
|
57
|
+
const segment = (id: string, provider: string | null, tokens: number) =>
|
|
58
|
+
database
|
|
59
|
+
.prepare(`INSERT INTO cost_invocation_dimension_facts
|
|
60
|
+
(agent_invocation_id,provider,model,total_tokens,cost_usd,cost_brl,occurred_day_sp)
|
|
61
|
+
VALUES (?,?,'model',?,0,0,'2026-09-29')`)
|
|
62
|
+
.run(id, provider, tokens);
|
|
63
|
+
return { database, context, payload, invocation, segment };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const empty = {
|
|
67
|
+
invocation_count: 0,
|
|
68
|
+
task_count: 0,
|
|
69
|
+
total_duration: null,
|
|
70
|
+
total_tokens: 0,
|
|
71
|
+
total_cost_usd: 0,
|
|
72
|
+
total_cost_brl: null,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
describe("provider visibility", () => {
|
|
76
|
+
it.each(
|
|
77
|
+
providerVisibleMetrics,
|
|
78
|
+
)("retains activity in %s independently of cost or invocation", (field) => {
|
|
79
|
+
const { context, payload } = fixture();
|
|
80
|
+
payload({ key: "unavailable", ...empty });
|
|
81
|
+
payload({ key: "active", ...empty, [field]: 1 });
|
|
82
|
+
const page = queryUncachedViewPage(context, "cost_monitor_providers", {});
|
|
83
|
+
expect(page.rows.map((row) => row.key)).toEqual(["active"]);
|
|
84
|
+
expect(page.total_count).toBe(1);
|
|
85
|
+
expect(page.rows[0]?.[field]).toBe(1);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("ignores unavailable/zero metrics and hidden metrics, retaining negative adjustments", () => {
|
|
89
|
+
expect(hasVisibleProviderMetrics({ ...empty, source_event_count: 9 })).toBe(false);
|
|
90
|
+
expect(hasVisibleProviderMetrics({})).toBe(false);
|
|
91
|
+
expect(hasVisibleProviderMetrics({ ...empty, total_cost_usd: -1 })).toBe(true);
|
|
92
|
+
expect(hasVisibleProviderMetrics({ ...empty, total_cost_usd: 0.0049 })).toBe(false);
|
|
93
|
+
expect(hasVisibleProviderMetrics({ ...empty, total_cost_usd: 0.005 })).toBe(true);
|
|
94
|
+
expect(hasVisibleProviderMetrics({ ...empty, total_cost_brl: -0.005 })).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("hides sub-cent residual costs from payload while preserving raw financial totals", () => {
|
|
98
|
+
const { context, payload } = fixture();
|
|
99
|
+
const residual = {
|
|
100
|
+
total_cost_usd: 0.00004640000001435857,
|
|
101
|
+
total_cost_brl: 0.000010780000006496704,
|
|
102
|
+
};
|
|
103
|
+
payload({ key: "unavailable", ...empty, ...residual });
|
|
104
|
+
const page = queryUncachedViewPage(context, "cost_monitor_providers", {});
|
|
105
|
+
expect(page).toMatchObject({ total_count: 0, rows: [], has_more: false });
|
|
106
|
+
expect(page.totals).toMatchObject(residual);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it.each([
|
|
110
|
+
{ phase: "execution" },
|
|
111
|
+
{ from: "2026-09-29" },
|
|
112
|
+
])("hides SQL residual-cost-only rows at displayed precision: %j", (input) => {
|
|
113
|
+
const { context, invocation, segment, database } = fixture();
|
|
114
|
+
invocation("residual", 0);
|
|
115
|
+
segment("residual", null, 0);
|
|
116
|
+
database.exec(`
|
|
117
|
+
UPDATE invocations SET cost_usd=0.00004640000001435857,cost_brl=0.000010780000006496704;
|
|
118
|
+
UPDATE zaip_aios_activities SET cost_usd=0.00004640000001435857,cost_brl=0.000010780000006496704;
|
|
119
|
+
UPDATE cost_invocation_dimension_facts SET cost_usd=0.00004640000001435857,cost_brl=0.000010780000006496704;
|
|
120
|
+
`);
|
|
121
|
+
const page = queryUncachedViewPage(context, "cost_monitor_providers", input);
|
|
122
|
+
expect(page).toMatchObject({ total_count: 0, rows: [], has_more: false });
|
|
123
|
+
expect(page.totals).toMatchObject({
|
|
124
|
+
total_cost_usd: 0.00004640000001435857,
|
|
125
|
+
total_cost_brl: 0.000010780000006496704,
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("filters payload before pagination without changing factual totals or null coverage", () => {
|
|
130
|
+
const { context, payload } = fixture();
|
|
131
|
+
payload({ key: "unavailable", ...empty, source_event_count: 9 });
|
|
132
|
+
payload({ key: "known-empty", ...empty });
|
|
133
|
+
payload({ key: "active", ...empty, total_tokens: 7 });
|
|
134
|
+
const page = queryUncachedViewPage(context, "cost_monitor_providers", { limit: 1 });
|
|
135
|
+
expect(page).toMatchObject({ total_count: 1, has_more: false });
|
|
136
|
+
expect(page.rows).toEqual([
|
|
137
|
+
expect.objectContaining({ key: "active", total_duration: null, total_cost_brl: null }),
|
|
138
|
+
]);
|
|
139
|
+
expect(page.totals).toMatchObject({
|
|
140
|
+
total_tokens: 7,
|
|
141
|
+
total_cost_usd: 0,
|
|
142
|
+
total_cost_brl: null,
|
|
143
|
+
total_duration: null,
|
|
144
|
+
source_event_count: 9,
|
|
145
|
+
});
|
|
146
|
+
const next = queryUncachedViewPage(context, "cost_monitor_providers", { limit: 1, offset: 1 });
|
|
147
|
+
expect(next).toMatchObject({ total_count: 1, rows: [], has_more: false });
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("retains genuine unattributed payload activity and returns a coherent empty page", () => {
|
|
151
|
+
const { context, payload } = fixture();
|
|
152
|
+
payload({ key: "unavailable", ...empty });
|
|
153
|
+
expect(queryUncachedViewPage(context, "cost_monitor_providers", {})).toMatchObject({
|
|
154
|
+
total_count: 0,
|
|
155
|
+
rows: [],
|
|
156
|
+
has_more: false,
|
|
157
|
+
});
|
|
158
|
+
payload({ key: null, ...empty, total_tokens: 3 });
|
|
159
|
+
expect(queryUncachedViewPage(context, "cost_monitor_providers", {}).rows).toEqual([
|
|
160
|
+
expect.objectContaining({ key: null, total_tokens: 3 }),
|
|
161
|
+
]);
|
|
162
|
+
expect(readCachedCostViewPage(context, "cost_monitor_providers", {})).toBeNull();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it.each([
|
|
166
|
+
{ phase: "execution" },
|
|
167
|
+
{ from: "2026-09-29", to: "2026-09-29" },
|
|
168
|
+
])("filters SQL before count and paging for %j while retaining factual populations", (input) => {
|
|
169
|
+
const { context, invocation, segment, database } = fixture();
|
|
170
|
+
invocation("mixed", 10, 100);
|
|
171
|
+
segment("mixed", "openai", 10);
|
|
172
|
+
segment("mixed", "empty-known", 0);
|
|
173
|
+
segment("mixed", null, 0);
|
|
174
|
+
invocation("unattributed", 7);
|
|
175
|
+
segment("unattributed", null, 7);
|
|
176
|
+
invocation("free", 0);
|
|
177
|
+
segment("free", "free-provider", 0);
|
|
178
|
+
const factsBefore = database.prepare("SELECT * FROM cost_invocation_dimension_facts").all();
|
|
179
|
+
const page = queryUncachedViewPage(context, "cost_monitor_providers", { ...input, limit: 2 });
|
|
180
|
+
expect(page).toMatchObject({ total_count: 3, has_more: true });
|
|
181
|
+
expect(page.rows.map((row) => row.key)).toEqual(["free-provider", "openai"]);
|
|
182
|
+
expect(page.rows[0]).toMatchObject({ invocation_count: 1, total_tokens: 0, total_cost_usd: 0 });
|
|
183
|
+
expect(page.rows[1]).toMatchObject({
|
|
184
|
+
invocation_count: 0,
|
|
185
|
+
total_tokens: 10,
|
|
186
|
+
total_cost_brl: 0,
|
|
187
|
+
total_duration: null,
|
|
188
|
+
});
|
|
189
|
+
expect(page.totals).toMatchObject({
|
|
190
|
+
invocation_count: 3,
|
|
191
|
+
total_tokens: 17,
|
|
192
|
+
total_duration: 100,
|
|
193
|
+
total_cost_usd: 0,
|
|
194
|
+
source_event_count: 27,
|
|
195
|
+
});
|
|
196
|
+
const next = queryUncachedViewPage(context, "cost_monitor_providers", {
|
|
197
|
+
...input,
|
|
198
|
+
limit: 2,
|
|
199
|
+
offset: 2,
|
|
200
|
+
});
|
|
201
|
+
expect(next).toMatchObject({ total_count: 3, has_more: false });
|
|
202
|
+
expect(next.rows).toEqual([
|
|
203
|
+
expect.objectContaining({ key: "unavailable", invocation_count: 0, total_tokens: 7 }),
|
|
204
|
+
]);
|
|
205
|
+
expect(next.totals).toEqual(page.totals);
|
|
206
|
+
expect(database.prepare("SELECT * FROM cost_invocation_dimension_facts").all()).toEqual(
|
|
207
|
+
factsBefore,
|
|
208
|
+
);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it.each([
|
|
212
|
+
{ phase: "execution" },
|
|
213
|
+
{ from: "2026-09-29" },
|
|
214
|
+
])("returns empty SQL page for only zero/null rows: %j", (input) => {
|
|
215
|
+
const { context, invocation, segment } = fixture();
|
|
216
|
+
invocation("zero", 0);
|
|
217
|
+
segment("zero", null, 0);
|
|
218
|
+
expect(queryUncachedViewPage(context, "cost_monitor_providers", input)).toMatchObject({
|
|
219
|
+
total_count: 0,
|
|
220
|
+
rows: [],
|
|
221
|
+
has_more: false,
|
|
222
|
+
totals: { invocation_count: 1, total_tokens: 0, total_cost_usd: 0 },
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it.each([
|
|
227
|
+
{ phase: "execution" },
|
|
228
|
+
{ from: "2026-09-29" },
|
|
229
|
+
])("retains zero-cost task-only and meaningful unattributed cost in SQL: %j", (input) => {
|
|
230
|
+
const { context, invocation, segment, database } = fixture();
|
|
231
|
+
invocation("task-only", 0);
|
|
232
|
+
segment("task-only", "provider-a", 0);
|
|
233
|
+
segment("task-only", "provider-b", 0);
|
|
234
|
+
database.exec(
|
|
235
|
+
"UPDATE invocations SET task_id='TASK-1'; UPDATE zaip_aios_activities SET task_id='TASK-1'",
|
|
236
|
+
);
|
|
237
|
+
invocation("cost-only", 0);
|
|
238
|
+
segment("cost-only", null, 0);
|
|
239
|
+
database.exec(`
|
|
240
|
+
UPDATE invocations SET cost_usd=0.005 WHERE agent_invocation_id='cost-only';
|
|
241
|
+
UPDATE zaip_aios_activities SET cost_usd=0.005 WHERE agent_invocation_id='cost-only';
|
|
242
|
+
UPDATE cost_invocation_dimension_facts SET cost_usd=0.005 WHERE agent_invocation_id='cost-only';
|
|
243
|
+
`);
|
|
244
|
+
const page = queryUncachedViewPage(context, "cost_monitor_providers", input);
|
|
245
|
+
expect(page.total_count).toBe(3);
|
|
246
|
+
expect(page.rows).toEqual([
|
|
247
|
+
expect.objectContaining({
|
|
248
|
+
key: "provider-a",
|
|
249
|
+
invocation_count: 0,
|
|
250
|
+
task_count: 1,
|
|
251
|
+
total_tokens: 0,
|
|
252
|
+
total_cost_usd: 0,
|
|
253
|
+
}),
|
|
254
|
+
expect.objectContaining({
|
|
255
|
+
key: "provider-b",
|
|
256
|
+
invocation_count: 0,
|
|
257
|
+
task_count: 1,
|
|
258
|
+
total_tokens: 0,
|
|
259
|
+
total_cost_usd: 0,
|
|
260
|
+
}),
|
|
261
|
+
expect.objectContaining({
|
|
262
|
+
key: "unavailable",
|
|
263
|
+
invocation_count: 0,
|
|
264
|
+
task_count: 0,
|
|
265
|
+
total_tokens: 0,
|
|
266
|
+
total_cost_usd: 0.005,
|
|
267
|
+
}),
|
|
268
|
+
]);
|
|
269
|
+
expect(page.totals).toMatchObject({ task_count: 1, total_cost_usd: 0.005 });
|
|
270
|
+
});
|
|
271
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** Only metrics displayed in Provedores determine whether a row has activity. */
|
|
2
|
+
export const providerVisibleMetrics = [
|
|
3
|
+
"invocation_count",
|
|
4
|
+
"task_count",
|
|
5
|
+
"total_duration",
|
|
6
|
+
"total_tokens",
|
|
7
|
+
"total_cost_usd",
|
|
8
|
+
"total_cost_brl",
|
|
9
|
+
] as const;
|
|
10
|
+
|
|
11
|
+
// Costs are displayed with two decimal places; sub-cent reconciliation
|
|
12
|
+
// remainders remain factual totals but do not constitute visible activity.
|
|
13
|
+
const minimumVisibleCost = 0.005;
|
|
14
|
+
|
|
15
|
+
export function hasVisibleProviderMetrics(row: Record<string, unknown>): boolean {
|
|
16
|
+
return providerVisibleMetrics.some(
|
|
17
|
+
(field) =>
|
|
18
|
+
typeof row[field] === "number" &&
|
|
19
|
+
(field === "total_cost_usd" || field === "total_cost_brl"
|
|
20
|
+
? Math.abs(row[field]) >= minimumVisibleCost
|
|
21
|
+
: row[field] !== 0),
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Applied to aggregated rows, before COUNT, LIMIT and OFFSET. */
|
|
26
|
+
export const providerVisibleMetricsSql = providerVisibleMetrics
|
|
27
|
+
.map((field) =>
|
|
28
|
+
field === "total_cost_usd" || field === "total_cost_brl"
|
|
29
|
+
? `ABS(COALESCE(${field}, 0)) >= ${minimumVisibleCost}`
|
|
30
|
+
: `COALESCE(${field}, 0) <> 0`,
|
|
31
|
+
)
|
|
32
|
+
.join(" OR ");
|
package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-operations-duration.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { SqliteViewPageInput } from "./sqlite-query-cached-cost-page.js";
|
|
2
|
+
import type { SqliteQueryContext } from "./sqlite-query-context.js";
|
|
3
|
+
import type { SqliteViewName } from "./sqlite-read-model-types.js";
|
|
4
|
+
|
|
5
|
+
/** Recompose coarse period rows from daily facts when a date range cuts through them. */
|
|
6
|
+
export function attachOperationsDuration(
|
|
7
|
+
context: SqliteQueryContext,
|
|
8
|
+
view: SqliteViewName,
|
|
9
|
+
input: SqliteViewPageInput,
|
|
10
|
+
rows: Array<Record<string, unknown>>,
|
|
11
|
+
): void {
|
|
12
|
+
if (!["operations_daily", "operations_monthly", "operations_annual"].includes(view)) return;
|
|
13
|
+
const partialCoarse = view !== "operations_daily" && (input.from || input.to);
|
|
14
|
+
const partialFromDay = input.from?.slice(0, 10) ?? null;
|
|
15
|
+
const partialToDay = input.to?.slice(0, 10) ?? null;
|
|
16
|
+
const dailyRows = partialCoarse
|
|
17
|
+
? (context.database
|
|
18
|
+
.prepare(
|
|
19
|
+
`SELECT * FROM operations_daily WHERE (? IS NULL OR day >= ?) AND (? IS NULL OR day <= ?)`,
|
|
20
|
+
)
|
|
21
|
+
.all(partialFromDay, partialFromDay, partialToDay, partialToDay) as Array<
|
|
22
|
+
Record<string, unknown>
|
|
23
|
+
>)
|
|
24
|
+
: [];
|
|
25
|
+
const daily = context.executionDurationByDay({
|
|
26
|
+
from: input.from ?? null,
|
|
27
|
+
to: input.to ?? null,
|
|
28
|
+
});
|
|
29
|
+
for (const row of rows) {
|
|
30
|
+
const key = String(row.day ?? row.period_key);
|
|
31
|
+
if (partialCoarse) {
|
|
32
|
+
const matching = dailyRows.filter((day) => String(day.day).startsWith(key));
|
|
33
|
+
const matchingDays = matching.map((day) => String(day.day)).sort();
|
|
34
|
+
row.start_day = matchingDays[0] ?? null;
|
|
35
|
+
row.end_day = matchingDays.at(-1) ?? null;
|
|
36
|
+
for (const column of [
|
|
37
|
+
"prd_count",
|
|
38
|
+
"spec_count",
|
|
39
|
+
"task_count",
|
|
40
|
+
"validation_count",
|
|
41
|
+
"evidence_count",
|
|
42
|
+
"total_run_count",
|
|
43
|
+
"invocation_count",
|
|
44
|
+
"llm_call_count",
|
|
45
|
+
"total_tokens",
|
|
46
|
+
"total_cost_usd",
|
|
47
|
+
"total_cost_brl",
|
|
48
|
+
] as const)
|
|
49
|
+
row[column] = matching.reduce(
|
|
50
|
+
(sum, day) => sum + (typeof day[column] === "number" ? Number(day[column]) : 0),
|
|
51
|
+
0,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
row.operations_total_duration = [...daily]
|
|
55
|
+
.filter(([day]) => (view === "operations_daily" ? day === key : day.startsWith(key)))
|
|
56
|
+
.reduce((sum, [, duration]) => sum + duration, 0);
|
|
57
|
+
}
|
|
58
|
+
}
|
package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-provider-dimension.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { costDimensionFactDateFilter } from "./sqlite-cost-agent-page.js";
|
|
2
|
+
import { providerVisibleMetricsSql } from "./sqlite-provider-visibility.js";
|
|
2
3
|
import type { SqliteQueryContext, SqliteViewInput } from "./sqlite-query-context.js";
|
|
3
4
|
import type { SqliteViewName } from "./sqlite-read-model-types.js";
|
|
4
5
|
export function readProviderDimension(
|
|
@@ -131,13 +132,21 @@ export function readProviderDimension(
|
|
|
131
132
|
), task_population AS (
|
|
132
133
|
SELECT provider_key AS key, COUNT(*) AS task_count
|
|
133
134
|
FROM task_providers GROUP BY provider_key
|
|
134
|
-
), keys AS (SELECT key FROM financial UNION SELECT key FROM population UNION SELECT key FROM task_population)
|
|
135
|
+
), keys AS (SELECT key FROM financial UNION SELECT key FROM population UNION SELECT key FROM task_population),
|
|
136
|
+
aggregated_rows AS (
|
|
137
|
+
SELECT keys.key, keys.key AS label, COALESCE(population.invocation_count,0) AS invocation_count,
|
|
138
|
+
COALESCE(task_population.task_count,0) AS task_count, population.total_duration,
|
|
139
|
+
population.source_event_count, financial.total_tokens, financial.total_cost_usd,
|
|
140
|
+
financial.total_cost_brl
|
|
141
|
+
FROM keys LEFT JOIN population USING(key) LEFT JOIN task_population USING(key)
|
|
142
|
+
LEFT JOIN financial USING(key)
|
|
143
|
+
), visible_rows AS (SELECT * FROM aggregated_rows WHERE ${providerVisibleMetricsSql})`;
|
|
135
144
|
const limit = Math.min(200, Math.max(1, input.limit ?? 50));
|
|
136
145
|
const offset = Math.max(0, input.offset ?? 0);
|
|
137
146
|
const total_count = Number(
|
|
138
147
|
(
|
|
139
148
|
context.database
|
|
140
|
-
.prepare(`${ctes} ${facts} SELECT COUNT(*) AS count FROM
|
|
149
|
+
.prepare(`${ctes} ${facts} SELECT COUNT(*) AS count FROM visible_rows`)
|
|
141
150
|
.get(...values) as { count: number }
|
|
142
151
|
).count,
|
|
143
152
|
);
|
|
@@ -160,7 +169,7 @@ export function readProviderDimension(
|
|
|
160
169
|
.get(...values) as Record<string, unknown>;
|
|
161
170
|
const rows = context.database
|
|
162
171
|
.prepare(
|
|
163
|
-
`${ctes} ${facts} SELECT
|
|
172
|
+
`${ctes} ${facts} SELECT * FROM visible_rows ORDER BY total_cost_brl DESC, key LIMIT ? OFFSET ?`,
|
|
164
173
|
)
|
|
165
174
|
.all(...values, limit, offset) as Array<Record<string, unknown>>;
|
|
166
175
|
return {
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
operationsWorkListPayloadPage,
|
|
5
5
|
operationsWorkListSqlPage,
|
|
6
6
|
} from "./sqlite-operations-work-list.js";
|
|
7
|
+
import { hasVisibleProviderMetrics } from "./sqlite-provider-visibility.js";
|
|
7
8
|
import { readAgentDimension } from "./sqlite-query-agent-dimension.js";
|
|
8
9
|
import {
|
|
9
10
|
readCachedCostViewPage,
|
|
@@ -13,6 +14,7 @@ import type { SqliteQueryContext, SqliteViewPage } from "./sqlite-query-context.
|
|
|
13
14
|
import { readEntityGroup } from "./sqlite-query-entity-group.js";
|
|
14
15
|
import { readFilteredDimension } from "./sqlite-query-filtered-dimension.js";
|
|
15
16
|
import { readModelDimension } from "./sqlite-query-model-dimension.js";
|
|
17
|
+
import { attachOperationsDuration } from "./sqlite-query-operations-duration.js";
|
|
16
18
|
import { readProviderDimension } from "./sqlite-query-provider-dimension.js";
|
|
17
19
|
import { readTemporalDimension } from "./sqlite-query-temporal-dimension.js";
|
|
18
20
|
import { costMonitorFilterDay } from "./sqlite-read-model-canonical.js";
|
|
@@ -239,7 +241,7 @@ export function queryUncachedViewPage(
|
|
|
239
241
|
const visiblePayloadRows =
|
|
240
242
|
dimension === "model"
|
|
241
243
|
? payloadRows.filter((row) => row.key !== "unavailable" && row.key !== null)
|
|
242
|
-
: payloadRows;
|
|
244
|
+
: payloadRows.filter(hasVisibleProviderMetrics);
|
|
243
245
|
const limit = Math.min(200, Math.max(1, input.limit ?? 50));
|
|
244
246
|
const offset = Math.max(0, input.offset ?? 0);
|
|
245
247
|
const rows = visiblePayloadRows.slice(offset, offset + limit);
|
|
@@ -268,9 +270,11 @@ export function queryUncachedViewPage(
|
|
|
268
270
|
for (const field of modelNumericFields)
|
|
269
271
|
row[field] = typeof row[field] === "number" ? row[field] : 0;
|
|
270
272
|
}
|
|
273
|
+
// Provider visibility must not change factual totals or known-zero coverage.
|
|
274
|
+
const totalRows = dimension === "provider" ? payloadRows : visiblePayloadRows;
|
|
271
275
|
const nullableSum = (field: string) =>
|
|
272
|
-
|
|
273
|
-
?
|
|
276
|
+
totalRows.some((row) => typeof row[field] === "number")
|
|
277
|
+
? totalRows.reduce(
|
|
274
278
|
(sum, row) => sum + (typeof row[field] === "number" ? Number(row[field]) : 0),
|
|
275
279
|
0,
|
|
276
280
|
)
|
|
@@ -284,7 +288,7 @@ export function queryUncachedViewPage(
|
|
|
284
288
|
total_count: visiblePayloadRows.length,
|
|
285
289
|
has_more: offset + rows.length < visiblePayloadRows.length,
|
|
286
290
|
totals: {
|
|
287
|
-
invocation_count:
|
|
291
|
+
invocation_count: totalRows.reduce(
|
|
288
292
|
(sum, row) => sum + Number(row.invocation_count ?? 0),
|
|
289
293
|
0,
|
|
290
294
|
),
|
|
@@ -490,60 +494,7 @@ export function queryUncachedViewPage(
|
|
|
490
494
|
const rows = context.database
|
|
491
495
|
.prepare(`SELECT * FROM ${view}${where} ORDER BY ${meta.order} LIMIT ? OFFSET ?`)
|
|
492
496
|
.all(...values, limit, offset) as Array<Record<string, unknown>>;
|
|
493
|
-
|
|
494
|
-
view === "operations_daily" ||
|
|
495
|
-
view === "operations_monthly" ||
|
|
496
|
-
view === "operations_annual"
|
|
497
|
-
) {
|
|
498
|
-
// A partial date interval intersects a coarse month/year row but does
|
|
499
|
-
// not make its all-time additive metrics applicable. Recompose it from
|
|
500
|
-
// the same daily facts used by Operations cards.
|
|
501
|
-
const partialCoarse = view !== "operations_daily" && (input.from || input.to);
|
|
502
|
-
const partialFromDay = input.from?.slice(0, 10) ?? null;
|
|
503
|
-
const partialToDay = input.to?.slice(0, 10) ?? null;
|
|
504
|
-
const dailyRows = partialCoarse
|
|
505
|
-
? (context.database
|
|
506
|
-
.prepare(
|
|
507
|
-
`SELECT * FROM operations_daily WHERE (? IS NULL OR day >= ?) AND (? IS NULL OR day <= ?)`,
|
|
508
|
-
)
|
|
509
|
-
.all(partialFromDay, partialFromDay, partialToDay, partialToDay) as Array<
|
|
510
|
-
Record<string, unknown>
|
|
511
|
-
>)
|
|
512
|
-
: [];
|
|
513
|
-
const daily = context.executionDurationByDay({
|
|
514
|
-
from: input.from ?? null,
|
|
515
|
-
to: input.to ?? null,
|
|
516
|
-
});
|
|
517
|
-
for (const row of rows) {
|
|
518
|
-
const key = String(row.day ?? row.period_key);
|
|
519
|
-
if (partialCoarse) {
|
|
520
|
-
const matching = dailyRows.filter((day) => String(day.day).startsWith(key));
|
|
521
|
-
const matchingDays = matching.map((day) => String(day.day)).sort();
|
|
522
|
-
row.start_day = matchingDays[0] ?? null;
|
|
523
|
-
row.end_day = matchingDays.at(-1) ?? null;
|
|
524
|
-
for (const column of [
|
|
525
|
-
"prd_count",
|
|
526
|
-
"spec_count",
|
|
527
|
-
"task_count",
|
|
528
|
-
"validation_count",
|
|
529
|
-
"evidence_count",
|
|
530
|
-
"total_run_count",
|
|
531
|
-
"invocation_count",
|
|
532
|
-
"llm_call_count",
|
|
533
|
-
"total_tokens",
|
|
534
|
-
"total_cost_usd",
|
|
535
|
-
"total_cost_brl",
|
|
536
|
-
] as const)
|
|
537
|
-
row[column] = matching.reduce(
|
|
538
|
-
(sum, day) => sum + (typeof day[column] === "number" ? Number(day[column]) : 0),
|
|
539
|
-
0,
|
|
540
|
-
);
|
|
541
|
-
}
|
|
542
|
-
row.operations_total_duration = [...daily]
|
|
543
|
-
.filter(([day]) => (view === "operations_daily" ? day === key : day.startsWith(key)))
|
|
544
|
-
.reduce((sum, [, duration]) => sum + duration, 0);
|
|
545
|
-
}
|
|
546
|
-
}
|
|
497
|
+
attachOperationsDuration(context, view, input, rows);
|
|
547
498
|
return {
|
|
548
499
|
data_source: "sqlite_read_model" as const,
|
|
549
500
|
view,
|