@zaip-ai/cli 1.3.2 → 1.3.5

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.
Files changed (98) hide show
  1. package/LICENSE +6 -3
  2. package/README.md +8 -2
  3. package/dist/services/codex-pre-tool-hook-template.js +1 -1
  4. package/dist/services/runtime-template.test.js +9 -8
  5. package/dist/services/runtime-template.test.js.map +1 -1
  6. package/package.json +2 -2
  7. package/templates/.agents/dashboard/.zaip-managed.json +414 -394
  8. package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-ovxurkIk.js → CostCalculationGuidePage-D1mpyYwh.js} +1 -1
  9. package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-BXy6nti1.js → DashboardHelpDialog-CKO47D5s.js} +30 -1
  10. package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-vSvvOHQp.js → PricingSettingsDialog-BuSlCbyZ.js} +1 -1
  11. package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-zH758vfs.js → ProjectSettingsPage-CUHFkAhZ.js} +1 -1
  12. package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-CTH2wDs5.js → TraceabilityWorkspace-BmASq2gW.js} +1 -1
  13. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-Csj6LiAt.js +68 -0
  14. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-B_lDDHn1.js → icons-vendor-VyTTl_ph.js} +1 -1
  15. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-DIwDvCu5.js +2 -0
  16. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-hPPfVGQv.css +1 -0
  17. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-B3TJrD4O.js → react-vendor-CTC6vtln.js} +1 -1
  18. package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-7f1j5wx3.js → traceability-9Mtvbwrg.js} +1 -1
  19. package/templates/.agents/dashboard/apps/frontend/dist/index.html +6 -6
  20. package/templates/.agents/dashboard/apps/frontend/src/App.tsx +74 -10
  21. package/templates/.agents/dashboard/apps/frontend/src/AppRequestGuards.tsx +44 -0
  22. package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +103 -2
  23. package/templates/.agents/dashboard/apps/frontend/src/dashboard-capability-nav.css +11 -0
  24. package/templates/.agents/dashboard/apps/frontend/src/operations-period-contract.test.ts +14 -0
  25. package/templates/.agents/dashboard/apps/frontend/src/operations-period.test.ts +3 -6
  26. package/templates/.agents/dashboard/apps/frontend/src/operations-period.ts +3 -2
  27. package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +2 -2
  28. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperAttributionPanel.test.ts +60 -0
  29. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperAttributionPanel.tsx +26 -9
  30. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorNavigation.tsx +189 -0
  31. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.test.ts +122 -2
  32. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +70 -155
  33. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.helpers.tsx +2 -1
  34. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.test.ts +12 -4
  35. package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.tsx +67 -24
  36. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +19 -4
  37. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +59 -0
  38. package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +55 -2
  39. package/templates/.agents/dashboard/modules/operations/src/react/operation-date-range.test.ts +25 -0
  40. package/templates/.agents/dashboard/modules/operations/src/react/operation-date-range.ts +29 -0
  41. package/templates/.agents/dashboard/modules/operations/src/react/operations-coverage-toast.test.ts +3 -2
  42. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +12 -2
  43. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +274 -246
  44. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +255 -227
  45. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +263 -235
  46. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-dimension-facts.test.ts +282 -0
  47. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-dimension-facts.ts +71 -0
  48. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-launch-progress.ts +18 -0
  49. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-stale-seed.ts +50 -0
  50. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +9 -6
  51. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +45 -73
  52. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-seed.test.ts +79 -2
  53. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-work-item-index.test.ts +56 -0
  54. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.mjs +26 -8
  55. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.test.mjs +45 -4
  56. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-lifecycle.mjs +58 -0
  57. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-lifecycle.test.mjs +81 -0
  58. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +171 -71
  59. package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +152 -11
  60. package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +4 -9
  61. package/templates/.agents/dashboard/tests/e2e/playwright.config.ts +9 -1
  62. package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-loading.spec.ts +1 -1
  63. package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-pagination.spec.ts +4 -0
  64. package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-period-transition.spec.ts +238 -0
  65. package/templates/.agents/dashboard/tests/e2e/specs/dashboard-performance-matrix.spec.ts +4 -0
  66. package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +60 -17
  67. package/templates/.agents/requirements/autonomous-assurance.md +1 -1
  68. package/templates/.agents/requirements/comparative-audit.md +1 -1
  69. package/templates/.agents/requirements/context-engine.md +1 -1
  70. package/templates/.agents/requirements/cost-observability.md +1 -1
  71. package/templates/.agents/requirements/dashboard-launch.md +1 -1
  72. package/templates/.agents/requirements/data-export.md +1 -1
  73. package/templates/.agents/requirements/design-system.md +1 -1
  74. package/templates/.agents/requirements/execution-engine.md +1 -1
  75. package/templates/.agents/requirements/kanban-dashboard.md +1 -1
  76. package/templates/.agents/requirements/model-orchestration.md +1 -1
  77. package/templates/.agents/requirements/public-distribution.md +1 -1
  78. package/templates/.agents/requirements/software-architecture.md +1 -1
  79. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  80. package/templates/.agents/requirements/work-lifecycle.md +1 -1
  81. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  82. package/templates/.agents/runtime/bin/zaip.mjs +7 -7
  83. package/templates/.agents/runtime/checksums.sha256 +3 -3
  84. package/templates/.agents/runtime/provenance.json +3 -3
  85. package/templates/.agents/runtime/sbom.cdx.json +1595 -3491
  86. package/templates/.agents/skills/zaip-skill-lifecycle-closure/SKILL.md +16 -0
  87. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-local-telemetry.mjs +19 -20
  88. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-local-telemetry.test.mjs +143 -0
  89. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.mjs +14 -0
  90. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.mjs +3 -1
  91. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.test.mjs +21 -0
  92. package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/source-size-telemetry/model-identity-reconciliation.mjs +3 -0
  93. package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +29 -0
  94. package/templates/template-manifest.json +539 -935
  95. package/templates/.agents/dashboard/.tmp-run-list.json +0 -254
  96. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-DKwP1stV.js +0 -68
  97. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-B31GMG5O.css +0 -1
  98. package/templates/.agents/dashboard/apps/frontend/dist/assets/index-CuMcoUni.js +0 -2
@@ -0,0 +1,282 @@
1
+ import { DatabaseSync } from "node:sqlite";
2
+ import { afterEach, describe, expect, it } from "vitest";
3
+ import { rebuildCostInvocationDimensionFactsSql } from "./sqlite-cost-dimension-facts.js";
4
+
5
+ const databases: DatabaseSync[] = [];
6
+
7
+ afterEach(() => {
8
+ while (databases.length > 0) databases.pop()?.close();
9
+ });
10
+
11
+ function databaseWithFinancialFixture(): DatabaseSync {
12
+ const database = new DatabaseSync(":memory:");
13
+ databases.push(database);
14
+ database.exec(`
15
+ CREATE TABLE zaip_aios_activities (
16
+ activity_row_id TEXT PRIMARY KEY, payload_type TEXT, activity_source_kind TEXT, agent_run_id TEXT,
17
+ source_started_at TEXT, source_occurred_at TEXT, occurred_at TEXT, occurred_day_sp TEXT,
18
+ input_tokens REAL, cached_input_tokens REAL, output_tokens REAL, reasoning_tokens REAL,
19
+ total_tokens REAL, cost_usd REAL, cost_brl REAL
20
+ );
21
+ CREATE TEMP TABLE invocation_usage_segments (
22
+ agent_invocation_id TEXT, provider TEXT, model TEXT, service_tier TEXT, reasoning_effort TEXT,
23
+ input_tokens REAL, cached_input_tokens REAL, uncached_input_tokens REAL, output_tokens REAL,
24
+ reasoning_tokens REAL, visible_output_tokens REAL, total_tokens REAL, input_rate_usd_per_million REAL,
25
+ cached_input_rate_usd_per_million REAL, output_rate_usd_per_million REAL, reasoning_rate_usd_per_million REAL,
26
+ context_input_multiplier REAL, context_output_multiplier REAL, speed_cost_multiplier REAL, pricing_scenario TEXT,
27
+ standard_cost_usd REAL, speed_uplift_usd REAL, input_cost_usd REAL, cached_input_cost_usd REAL,
28
+ output_cost_usd REAL, reasoning_cost_usd REAL, calculated_cost_usd REAL, recorded_cost_usd REAL,
29
+ cost_variance_usd REAL, cost_usd REAL, cost_brl REAL, cost_method TEXT, pricing_model TEXT,
30
+ pricing_source TEXT, fx_rate REAL
31
+ );
32
+ CREATE TEMP TABLE attributed_token_active_facts (
33
+ activity_row_id TEXT, occurred_day_sp TEXT, attributed_token_active_ms REAL,
34
+ PRIMARY KEY(activity_row_id, occurred_day_sp)
35
+ );
36
+ `);
37
+ const activity = database.prepare(
38
+ "INSERT INTO zaip_aios_activities VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
39
+ );
40
+ activity.run(
41
+ "ACTV-ONE",
42
+ null,
43
+ "agent_run",
44
+ "AINV-ONE",
45
+ "2026-08-31T03:00:00Z",
46
+ null,
47
+ "2026-08-31T03:00:00Z",
48
+ "2026-08-31",
49
+ 100,
50
+ 10,
51
+ 20,
52
+ 5,
53
+ 135,
54
+ 13.5,
55
+ 67.5,
56
+ );
57
+ activity.run(
58
+ "ACTV-TWO",
59
+ null,
60
+ "agent_run",
61
+ "AINV-TWO",
62
+ null,
63
+ null,
64
+ "2026-08-31T04:00:00Z",
65
+ "2026-08-31",
66
+ 30,
67
+ 0,
68
+ 0,
69
+ 0,
70
+ 30,
71
+ null,
72
+ 3,
73
+ );
74
+ activity.run(
75
+ "ACTV-FIN",
76
+ null,
77
+ "manual",
78
+ null,
79
+ null,
80
+ null,
81
+ "2026-08-31T05:00:00Z",
82
+ "2026-08-31",
83
+ 10,
84
+ null,
85
+ null,
86
+ null,
87
+ 10,
88
+ 1,
89
+ 5,
90
+ );
91
+ activity.run(
92
+ "ACTV-UNKNOWN",
93
+ null,
94
+ "manual",
95
+ null,
96
+ null,
97
+ null,
98
+ "2026-08-31T06:00:00Z",
99
+ "2026-08-31",
100
+ null,
101
+ null,
102
+ null,
103
+ null,
104
+ null,
105
+ 2,
106
+ null,
107
+ );
108
+ const usage = database.prepare(
109
+ "INSERT INTO invocation_usage_segments VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
110
+ );
111
+ usage.run(
112
+ "AINV-ONE",
113
+ "openai",
114
+ "model-a",
115
+ null,
116
+ null,
117
+ 60,
118
+ 6,
119
+ 54,
120
+ 10,
121
+ 3,
122
+ 10,
123
+ 79,
124
+ null,
125
+ null,
126
+ null,
127
+ null,
128
+ null,
129
+ null,
130
+ null,
131
+ null,
132
+ null,
133
+ null,
134
+ null,
135
+ null,
136
+ null,
137
+ null,
138
+ null,
139
+ null,
140
+ null,
141
+ 7.9,
142
+ 39.5,
143
+ null,
144
+ null,
145
+ null,
146
+ null,
147
+ );
148
+ usage.run(
149
+ "AINV-ONE",
150
+ null,
151
+ null,
152
+ null,
153
+ null,
154
+ 20,
155
+ 2,
156
+ 18,
157
+ 5,
158
+ 1,
159
+ 5,
160
+ 26,
161
+ null,
162
+ null,
163
+ null,
164
+ null,
165
+ null,
166
+ null,
167
+ null,
168
+ null,
169
+ null,
170
+ null,
171
+ null,
172
+ null,
173
+ null,
174
+ null,
175
+ null,
176
+ null,
177
+ null,
178
+ 2.6,
179
+ 13,
180
+ null,
181
+ null,
182
+ null,
183
+ null,
184
+ );
185
+ const duration = database.prepare("INSERT INTO attributed_token_active_facts VALUES (?,?,?)");
186
+ duration.run("ACTV-ONE", "2026-08-30", 40);
187
+ duration.run("ACTV-ONE", "2026-08-31", 60);
188
+ duration.run("ACTV-FIN", "2026-08-30", 4);
189
+ duration.run("ACTV-FIN", "2026-08-31", 6);
190
+ return database;
191
+ }
192
+
193
+ const legacySql = `
194
+ CREATE TEMP TABLE legacy_usage_payloads AS
195
+ SELECT json_object('agent_invocation_id', agent_invocation_id, 'model', model, 'provider', provider,
196
+ 'input_tokens', input_tokens, 'cached_input_tokens', cached_input_tokens, 'output_tokens', output_tokens,
197
+ 'reasoning_tokens', reasoning_tokens, 'total_tokens', total_tokens, 'cost_usd', cost_usd, 'cost_brl', cost_brl) AS payload_json
198
+ FROM temp.invocation_usage_segments;
199
+ CREATE TEMP VIEW legacy_usage AS
200
+ SELECT json_extract(payload_json, '$.agent_invocation_id') AS agent_invocation_id,
201
+ json_extract(payload_json, '$.model') AS model, json_extract(payload_json, '$.provider') AS provider,
202
+ json_extract(payload_json, '$.input_tokens') AS input_tokens, json_extract(payload_json, '$.cached_input_tokens') AS cached_input_tokens,
203
+ json_extract(payload_json, '$.output_tokens') AS output_tokens, json_extract(payload_json, '$.reasoning_tokens') AS reasoning_tokens,
204
+ json_extract(payload_json, '$.total_tokens') AS total_tokens, json_extract(payload_json, '$.cost_usd') AS cost_usd,
205
+ json_extract(payload_json, '$.cost_brl') AS cost_brl FROM legacy_usage_payloads;
206
+ CREATE TEMP TABLE legacy_duration_payloads AS
207
+ SELECT json_object('activity_row_id', activity_row_id, 'occurred_day_sp', occurred_day_sp,
208
+ 'attributed_token_active_ms', attributed_token_active_ms) AS payload_json FROM temp.attributed_token_active_facts;
209
+ CREATE TEMP VIEW legacy_duration AS
210
+ SELECT json_extract(payload_json, '$.activity_row_id') AS activity_row_id,
211
+ json_extract(payload_json, '$.attributed_token_active_ms') AS attributed_token_active_ms FROM legacy_duration_payloads;
212
+ CREATE TEMP VIEW legacy_activities AS
213
+ SELECT a.*, f.duration_ms FROM zaip_aios_activities a
214
+ LEFT JOIN (SELECT activity_row_id, SUM(attributed_token_active_ms) AS duration_ms FROM legacy_duration GROUP BY activity_row_id) f USING(activity_row_id)
215
+ WHERE a.payload_type IS NULL;
216
+ CREATE TABLE legacy_cost_invocation_dimension_facts AS
217
+ SELECT a.agent_run_id AS agent_invocation_id, date(COALESCE(a.source_started_at, a.source_occurred_at, a.occurred_at), '-3 hours') AS occurred_day_sp,
218
+ CASE WHEN u.model IS NULL OR trim(u.model)='' OR u.model='unavailable' THEN 'unavailable' ELSE u.model END AS model,
219
+ CASE WHEN u.provider IS NULL OR trim(u.provider)='' OR u.provider='unavailable' THEN 'unavailable' ELSE u.provider END AS provider,
220
+ u.input_tokens, u.cached_input_tokens, u.output_tokens, u.reasoning_tokens, u.total_tokens, u.cost_usd, u.cost_brl, a.duration_ms, 0 AS residual
221
+ FROM legacy_activities a JOIN legacy_usage u ON u.agent_invocation_id=a.agent_run_id
222
+ WHERE a.activity_source_kind='agent_run' AND (u.total_tokens IS NOT NULL OR u.cost_usd IS NOT NULL OR u.cost_brl IS NOT NULL);
223
+ INSERT INTO legacy_cost_invocation_dimension_facts
224
+ SELECT a.agent_run_id, date(COALESCE(a.source_started_at, a.source_occurred_at, a.occurred_at), '-3 hours'), 'unavailable', 'unavailable',
225
+ a.input_tokens-COALESCE(s.input_tokens,0), a.cached_input_tokens-COALESCE(s.cached_input_tokens,0), a.output_tokens-COALESCE(s.output_tokens,0), a.reasoning_tokens-COALESCE(s.reasoning_tokens,0), a.total_tokens-COALESCE(s.total_tokens,0), a.cost_usd-COALESCE(s.cost_usd,0), a.cost_brl-COALESCE(s.cost_brl,0), a.duration_ms, 1
226
+ FROM legacy_activities a LEFT JOIN (
227
+ SELECT agent_invocation_id, SUM(input_tokens) input_tokens, SUM(cached_input_tokens) cached_input_tokens, SUM(output_tokens) output_tokens, SUM(reasoning_tokens) reasoning_tokens, SUM(total_tokens) total_tokens, SUM(cost_usd) cost_usd, SUM(cost_brl) cost_brl
228
+ FROM legacy_cost_invocation_dimension_facts GROUP BY agent_invocation_id
229
+ ) s ON s.agent_invocation_id=a.agent_run_id
230
+ WHERE a.activity_source_kind='agent_run' AND (a.total_tokens<>COALESCE(s.total_tokens,0) OR a.cost_usd<>COALESCE(s.cost_usd,0) OR a.cost_brl<>COALESCE(s.cost_brl,0));
231
+ INSERT INTO legacy_cost_invocation_dimension_facts
232
+ SELECT 'activity:' || a.activity_row_id, a.occurred_day_sp, 'unavailable', 'unavailable', a.input_tokens, a.cached_input_tokens, a.output_tokens, a.reasoning_tokens, a.total_tokens, a.cost_usd, a.cost_brl, f.attributed_token_active_ms, 1
233
+ FROM zaip_aios_activities a LEFT JOIN legacy_duration f USING(activity_row_id)
234
+ WHERE a.payload_type IS NULL AND a.activity_source_kind <> 'agent_run'
235
+ AND (a.total_tokens IS NOT NULL OR a.cost_usd IS NOT NULL OR a.cost_brl IS NOT NULL)
236
+ AND NOT EXISTS (SELECT 1 FROM legacy_cost_invocation_dimension_facts d WHERE d.agent_invocation_id='activity:' || a.activity_row_id);
237
+ `;
238
+
239
+ function factRows(database: DatabaseSync, table: string) {
240
+ return database
241
+ .prepare(
242
+ `SELECT * FROM ${table} ORDER BY agent_invocation_id, residual, model, provider, duration_ms`,
243
+ )
244
+ .all();
245
+ }
246
+
247
+ describe("cost invocation dimension rebuild", () => {
248
+ it("preserves the legacy JSON numeric round trip before aggregation and residuals", () => {
249
+ const database = databaseWithFinancialFixture();
250
+ database
251
+ .prepare("UPDATE temp.invocation_usage_segments SET cost_usd=?, cost_brl=?")
252
+ .run(0.1 + 0.2, 1234567.8912345679);
253
+ database
254
+ .prepare("UPDATE temp.attributed_token_active_facts SET attributed_token_active_ms=?")
255
+ .run(1234567.8912345679);
256
+ database.exec(legacySql);
257
+ database.exec(rebuildCostInvocationDimensionFactsSql);
258
+ expect(factRows(database, "cost_invocation_dimension_facts")).toEqual(
259
+ factRows(database, "legacy_cost_invocation_dimension_facts"),
260
+ );
261
+ expect(factRows(database, "cost_invocation_dimension_facts")).toContainEqual(
262
+ expect.objectContaining({ residual: 0, cost_usd: 0.3, cost_brl: 1234567.89123457 }),
263
+ );
264
+ });
265
+
266
+ it("matches the legacy CTAS and both residual inserts while reading TEMP facts", () => {
267
+ const database = databaseWithFinancialFixture();
268
+ database.exec(legacySql);
269
+ database.exec(rebuildCostInvocationDimensionFactsSql);
270
+ expect(factRows(database, "cost_invocation_dimension_facts")).toEqual(
271
+ factRows(database, "legacy_cost_invocation_dimension_facts"),
272
+ );
273
+ expect(factRows(database, "cost_invocation_dimension_facts")).toContainEqual(
274
+ expect.objectContaining({ agent_invocation_id: "activity:ACTV-FIN", duration_ms: 4 }),
275
+ );
276
+ expect(factRows(database, "cost_invocation_dimension_facts")).toContainEqual(
277
+ expect.objectContaining({ agent_invocation_id: "activity:ACTV-FIN", duration_ms: 6 }),
278
+ );
279
+ expect(rebuildCostInvocationDimensionFactsSql).not.toContain("invocation_usage u");
280
+ expect(rebuildCostInvocationDimensionFactsSql).not.toContain("invocations i");
281
+ });
282
+ });
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Financial dimensions are constructed while the canonical rebuild tables are
3
+ * still available. Reusing those TEMP facts avoids reparsing persisted JSON
4
+ * views during the same rebuild without changing the persisted projection.
5
+ */
6
+ export const rebuildCostInvocationDimensionFactsSql = `
7
+ -- Preserve the numeric representation of the old json_object/json_extract
8
+ -- payload path once per input, before sums. Raw REALs can differ at the last
9
+ -- significant digits; removing JSON round trips must not change finances.
10
+ CREATE TEMP TABLE cost_dimension_duration AS
11
+ SELECT activity_row_id, occurred_day_sp,
12
+ json_extract(json_object('v', attributed_token_active_ms), '$.v') AS attributed_token_active_ms
13
+ FROM temp.attributed_token_active_facts;
14
+ CREATE INDEX cost_dimension_duration_activity ON cost_dimension_duration(activity_row_id);
15
+ CREATE TEMP TABLE cost_dimension_duration_by_activity AS
16
+ SELECT activity_row_id, SUM(attributed_token_active_ms) AS duration_ms
17
+ FROM cost_dimension_duration GROUP BY activity_row_id;
18
+ CREATE INDEX cost_dimension_duration_by_activity_row
19
+ ON cost_dimension_duration_by_activity(activity_row_id);
20
+ CREATE TEMP TABLE cost_dimension_usage AS
21
+ SELECT agent_invocation_id, provider, model,
22
+ json_extract(json_object('v', input_tokens), '$.v') AS input_tokens,
23
+ json_extract(json_object('v', cached_input_tokens), '$.v') AS cached_input_tokens,
24
+ json_extract(json_object('v', output_tokens), '$.v') AS output_tokens,
25
+ json_extract(json_object('v', reasoning_tokens), '$.v') AS reasoning_tokens,
26
+ json_extract(json_object('v', total_tokens), '$.v') AS total_tokens,
27
+ json_extract(json_object('v', cost_usd), '$.v') AS cost_usd,
28
+ json_extract(json_object('v', cost_brl), '$.v') AS cost_brl
29
+ FROM temp.invocation_usage_segments;
30
+ CREATE INDEX cost_dimension_usage_invocation ON cost_dimension_usage(agent_invocation_id);
31
+ CREATE TABLE cost_invocation_dimension_facts AS
32
+ SELECT a.agent_run_id AS agent_invocation_id,
33
+ date(COALESCE(a.source_started_at, a.source_occurred_at, a.occurred_at), '-3 hours') AS occurred_day_sp,
34
+ CASE WHEN u.model IS NULL OR trim(u.model)='' OR u.model='unavailable' THEN 'unavailable' ELSE u.model END AS model,
35
+ CASE WHEN u.provider IS NULL OR trim(u.provider)='' OR u.provider='unavailable' THEN 'unavailable' ELSE u.provider END AS provider,
36
+ u.input_tokens, u.cached_input_tokens, u.output_tokens, u.reasoning_tokens,
37
+ u.total_tokens, u.cost_usd, u.cost_brl, d.duration_ms, 0 AS residual
38
+ FROM zaip_aios_activities a
39
+ JOIN cost_dimension_usage u ON u.agent_invocation_id=a.agent_run_id
40
+ LEFT JOIN cost_dimension_duration_by_activity d USING(activity_row_id)
41
+ WHERE a.payload_type IS NULL AND a.activity_source_kind='agent_run'
42
+ AND (u.total_tokens IS NOT NULL OR u.cost_usd IS NOT NULL OR u.cost_brl IS NOT NULL);
43
+ CREATE INDEX cost_dimension_facts_day_model ON cost_invocation_dimension_facts(occurred_day_sp, model);
44
+ CREATE INDEX cost_dimension_facts_day_provider ON cost_invocation_dimension_facts(occurred_day_sp, provider);
45
+ CREATE INDEX cost_dimension_facts_invocation ON cost_invocation_dimension_facts(agent_invocation_id);
46
+ INSERT INTO cost_invocation_dimension_facts
47
+ SELECT a.agent_run_id, date(COALESCE(a.source_started_at, a.source_occurred_at, a.occurred_at), '-3 hours'), 'unavailable', 'unavailable',
48
+ a.input_tokens-COALESCE(s.input_tokens,0), a.cached_input_tokens-COALESCE(s.cached_input_tokens,0),
49
+ a.output_tokens-COALESCE(s.output_tokens,0), a.reasoning_tokens-COALESCE(s.reasoning_tokens,0),
50
+ a.total_tokens-COALESCE(s.total_tokens,0), a.cost_usd-COALESCE(s.cost_usd,0),
51
+ a.cost_brl-COALESCE(s.cost_brl,0), d.duration_ms, 1
52
+ FROM zaip_aios_activities a
53
+ LEFT JOIN (
54
+ SELECT agent_invocation_id, SUM(input_tokens) input_tokens, SUM(cached_input_tokens) cached_input_tokens,
55
+ SUM(output_tokens) output_tokens, SUM(reasoning_tokens) reasoning_tokens, SUM(total_tokens) total_tokens,
56
+ SUM(cost_usd) cost_usd, SUM(cost_brl) cost_brl
57
+ FROM cost_invocation_dimension_facts GROUP BY agent_invocation_id
58
+ ) s ON s.agent_invocation_id=a.agent_run_id
59
+ LEFT JOIN cost_dimension_duration_by_activity d USING(activity_row_id)
60
+ WHERE a.payload_type IS NULL AND a.activity_source_kind='agent_run'
61
+ AND (a.total_tokens<>COALESCE(s.total_tokens,0) OR a.cost_usd<>COALESCE(s.cost_usd,0) OR a.cost_brl<>COALESCE(s.cost_brl,0));
62
+ INSERT INTO cost_invocation_dimension_facts
63
+ SELECT 'activity:' || a.activity_row_id, a.occurred_day_sp, 'unavailable', 'unavailable',
64
+ a.input_tokens, a.cached_input_tokens, a.output_tokens, a.reasoning_tokens,
65
+ a.total_tokens, a.cost_usd, a.cost_brl, f.attributed_token_active_ms, 1
66
+ FROM zaip_aios_activities a
67
+ LEFT JOIN cost_dimension_duration f USING(activity_row_id)
68
+ WHERE a.payload_type IS NULL AND a.activity_source_kind <> 'agent_run'
69
+ AND (a.total_tokens IS NOT NULL OR a.cost_usd IS NOT NULL OR a.cost_brl IS NOT NULL)
70
+ AND NOT EXISTS (SELECT 1 FROM cost_invocation_dimension_facts f WHERE f.agent_invocation_id='activity:' || a.activity_row_id);
71
+ `;
@@ -0,0 +1,18 @@
1
+ const launchProgressStartedAt = performance.now();
2
+
3
+ export function reportSqliteReadModelLaunchProgress(
4
+ phase: string,
5
+ percent: number,
6
+ detail: string,
7
+ ): void {
8
+ if (process.env.ZAIP_DASHBOARD_LAUNCH_PROGRESS !== "1") return;
9
+ process.stderr.write(
10
+ `${JSON.stringify({
11
+ event: "dashboard_sqlite_rebuild_progress",
12
+ phase,
13
+ percent,
14
+ detail,
15
+ elapsed_ms: Math.round(performance.now() - launchProgressStartedAt),
16
+ })}\n`,
17
+ );
18
+ }
@@ -0,0 +1,50 @@
1
+ import { dirname, join } from "node:path";
2
+ import type { DatabaseSync } from "node:sqlite";
3
+ import { reportSqliteReadModelLaunchProgress } from "./sqlite-read-model-launch-progress.js";
4
+ import { materializeSnapshot, mostRecentCompatibleSnapshot } from "./sqlite-snapshot-cache.js";
5
+
6
+ type PreferredStaleSeed = {
7
+ files: number;
8
+ path: string;
9
+ cache: "stale_seed";
10
+ fingerprint_ms: number;
11
+ materialize_ms: number;
12
+ };
13
+
14
+ export async function materializePreferredStaleSeed(input: {
15
+ destination: string;
16
+ enabled: boolean;
17
+ fingerprintMs: number;
18
+ schemaVersion: string;
19
+ hasRequiredPayloads: (database: DatabaseSync) => boolean;
20
+ reportProgress?: boolean;
21
+ }): Promise<PreferredStaleSeed | null> {
22
+ if (!input.enabled) return null;
23
+ if (input.reportProgress)
24
+ reportSqliteReadModelLaunchProgress(
25
+ "stale_seed_probe",
26
+ 4,
27
+ "Verificando o último seed SQLite compatível.",
28
+ );
29
+ const fallback = await mostRecentCompatibleSnapshot(
30
+ process.env.ZAIP_SQLITE_CACHE_DIR ?? join(dirname(input.destination), "cache"),
31
+ input.schemaVersion,
32
+ input.hasRequiredPayloads,
33
+ );
34
+ if (fallback === null) return null;
35
+ const materializeStartedAt = performance.now();
36
+ await materializeSnapshot(fallback.path, input.destination);
37
+ if (input.reportProgress)
38
+ reportSqliteReadModelLaunchProgress(
39
+ "stale_seed_ready",
40
+ 18,
41
+ "Seed SQLite compatível materializado.",
42
+ );
43
+ return {
44
+ files: fallback.files,
45
+ path: input.destination,
46
+ cache: "stale_seed",
47
+ fingerprint_ms: input.fingerprintMs,
48
+ materialize_ms: Math.round(performance.now() - materializeStartedAt),
49
+ };
50
+ }
@@ -1179,12 +1179,15 @@ created_at: 2026-08-10T11:01:00.000Z
1179
1179
  const snapshot = new DatabaseSync(resolve(root, ".local/dashboard-sqlite/read-model.sqlite"), {
1180
1180
  readOnly: true,
1181
1181
  });
1182
- expect(
1183
- snapshot
1184
- .prepare("SELECT value FROM zaip_vw_read_model_metadata WHERE key='schema_version'")
1185
- .get(),
1186
- ).toEqual({ value: "70" });
1187
- snapshot.close();
1182
+ try {
1183
+ expect(
1184
+ snapshot
1185
+ .prepare("SELECT value FROM zaip_vw_read_model_metadata WHERE key='schema_version'")
1186
+ .get(),
1187
+ ).toEqual({ value: "71" });
1188
+ } finally {
1189
+ snapshot.close();
1190
+ }
1188
1191
  const filesystem = new SqliteReadModelFileSystem(root);
1189
1192
  try {
1190
1193
  expect(filesystem.viewPage("operations_work_kanban", { limit: 20 }).rows).toEqual([