@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
@@ -1,4 +1,6 @@
1
1
  import { readFileSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { fileURLToPath } from "node:url";
2
4
  import { describe, expect, it } from "vitest";
3
5
  import {
4
6
  allPhaseMetricTotals,
@@ -24,6 +26,7 @@ import {
24
26
  matchesPeriodEntity,
25
27
  resolveCompactCostPageRange,
26
28
  resolveCostGroupPageRange,
29
+ resolveDeveloperAttributionRange,
27
30
  selectDimensionalCostRows,
28
31
  visibleExecutionRows,
29
32
  } from "./CostMonitorPage.js";
@@ -33,13 +36,110 @@ import {
33
36
  reconcileCostGroupTotalsWithCards,
34
37
  } from "./use-cost-group-page.js";
35
38
 
39
+ declare global {
40
+ interface Window {
41
+ __costMonitorProbe: { localeChanges: number };
42
+ }
43
+ }
44
+
36
45
  describe("cost monitor navigation", () => {
46
+ it("keeps an open filter popup through the embedded DOM cycle", async () => {
47
+ const browserRequire = createRequire(
48
+ fileURLToPath(new URL("../../../../tests/e2e/package.json", import.meta.url)),
49
+ );
50
+ const esbuild = browserRequire("esbuild");
51
+ const { chromium, expect: expectPlaywright } = browserRequire("@playwright/test");
52
+ const harness = await esbuild.build({
53
+ bundle: true,
54
+ define: { "import.meta.url": '"http://localhost/"' },
55
+ format: "iife",
56
+ loader: { ".css": "text", ".png": "dataurl" },
57
+ platform: "browser",
58
+ stdin: {
59
+ contents: `
60
+ import { createRoot } from "react-dom/client";
61
+ import React, { useState } from "react";
62
+ import { CostMonitorPage } from "../../../modules/cost-monitor/src/react/CostMonitorPage.js";
63
+ const coverage = { events: 0, tokens: 0, usd: 0, brl: 0 };
64
+ const data = {
65
+ currency: "BRL",
66
+ pricing: { catalog_updated_at: null, fx_rate: null, conversion_applied: false },
67
+ filters: { from: null, to: null },
68
+ overview: { task_count: 0, run_count: 0, event_count: 0, total_tokens: 0, estimated_usd: 0, estimated_brl: 0, recorded_usd: 0, recorded_brl: 0, average_run_cost_usd: null, average_run_cost_brl: null, average_run_duration_ms: null, known_tokens_per_cycle: null, top_10_token_share: null, coverage },
69
+ filters_available: { providers: ["OpenAI"], agents: [], models: [], statuses: [], priorities: [], risks: [], phases: [], origins: [], work_types: [] },
70
+ configured_agents: [], usage: {}, origins: {}, prds: [], specs: [], runs: [],
71
+ groups: { conversations: [], tasks: [], providers: [], agents: [], models: [], daily: [], monthly: [], annual: [] },
72
+ };
73
+ const probe = { localeChanges: 0 };
74
+ Object.assign(window, { __costMonitorProbe: probe });
75
+ function Harness() {
76
+ const [embedded, setEmbedded] = useState(false);
77
+ return <>
78
+ <button onClick={() => setEmbedded((current) => !current)} type="button">Alternar incorporado</button>
79
+ <CostMonitorPage data={data as any} embedded={embedded} lastUpdatedAt={null} locale="pt-BR" loading={false} loadPeriod={async () => data as any} onLocaleChange={() => { probe.localeChanges += 1; }} onOpenTrace={() => {}} onRefresh={() => {}} />
80
+ </>;
81
+ }
82
+ createRoot(document.getElementById("root")).render(<Harness />);
83
+ `,
84
+ loader: "tsx",
85
+ resolveDir: fileURLToPath(new URL("../../../../apps/frontend/src", import.meta.url)),
86
+ sourcefile: "CostMonitorPage.browser-harness.tsx",
87
+ },
88
+ write: false,
89
+ });
90
+ const browser = await chromium.launch({ headless: true });
91
+ try {
92
+ const page = await browser.newPage();
93
+ const pageErrors: string[] = [];
94
+ page.on("pageerror", (error: Error) => pageErrors.push(error.message));
95
+ await page.setContent('<div id="root"></div>');
96
+ await page.addScriptTag({ content: harness.outputFiles[0].text });
97
+ await page.waitForTimeout(100);
98
+ expect(pageErrors).toEqual([]);
99
+
100
+ const navigation = page.getByLabel("Navegação do Cost Monitor");
101
+ await navigation.getByRole("button", { name: "Filtros" }).click();
102
+ await expectPlaywright(page.locator(".cost-nav-filter-popup")).toBeVisible();
103
+ await page.getByRole("button", { name: "Alternar incorporado" }).click();
104
+ await expectPlaywright(page.getByLabel("Navegação do Cost Monitor")).toHaveCount(0);
105
+ await page.getByRole("button", { name: "Alternar incorporado" }).click();
106
+ await expectPlaywright(page.locator(".cost-nav-filter-popup")).toBeVisible();
107
+ await navigation.getByRole("button", { name: /PT-BR/ }).click();
108
+ await page.getByRole("button", { name: /English/ }).click();
109
+ await expectPlaywright
110
+ .poll(() => page.evaluate(() => window.__costMonitorProbe.localeChanges))
111
+ .toBe(1);
112
+ } finally {
113
+ await browser.close();
114
+ }
115
+ });
116
+
37
117
  it("hides Excel export from the overview while retaining it on tabular views", () => {
38
118
  expect(isCostViewExportEligible("overview")).toBe(false);
39
119
  expect(isCostViewExportEligible("daily")).toBe(true);
40
120
  expect(isCostViewExportEligible("agentCalls")).toBe(true);
41
121
  });
42
122
 
123
+ it("keeps embedded developers all-time despite residual local filters", () => {
124
+ const monthRange = { from: "2026-08-01", to: "2026-08-31" };
125
+ expect(
126
+ resolveDeveloperAttributionRange({
127
+ embedded: true,
128
+ runPageRange: monthRange,
129
+ }),
130
+ ).toBeUndefined();
131
+ expect(
132
+ resolveDeveloperAttributionRange({
133
+ embedded: true,
134
+ globalDateRange: { start: "2026-07-01", end: "2026-08-31" },
135
+ runPageRange: { from: "2026-07-01", to: "2026-08-31" },
136
+ }),
137
+ ).toEqual({ from: "2026-07-01", to: "2026-08-31" });
138
+ expect(resolveDeveloperAttributionRange({ embedded: false, runPageRange: monthRange })).toEqual(
139
+ monthRange,
140
+ );
141
+ });
142
+
43
143
  it("keeps the embedded default group pager on the all-time snapshot without explicit filters", () => {
44
144
  const monthRange = costPeriodDateRange(defaultCostPeriod, new Date("2026-08-17T12:00:00.000Z"));
45
145
  expect(
@@ -365,16 +465,36 @@ describe("cost monitor navigation", () => {
365
465
  expect(content).toContain('{view === "developers" ? developerPanel : null}');
366
466
  });
367
467
 
468
+ it("prepares the complete developer API population with the same all-time range as the table", () => {
469
+ const page = readFileSync(new URL("./CostMonitorPage.tsx", import.meta.url), "utf8");
470
+ const exportSection = page.slice(
471
+ page.indexOf("const developerExportSource"),
472
+ page.indexOf("const costGroupExportSource"),
473
+ );
474
+
475
+ expect(exportSection).toContain('view === "developers"');
476
+ expect(exportSection).toContain("loadCostDeveloperAttribution");
477
+ expect(exportSection).toContain(
478
+ "...(developerAttributionRange === undefined ? {} : developerAttributionRange)",
479
+ );
480
+ expect(exportSection).toContain("totalRows: developerRows.length");
481
+ expect(page).toContain("const developerAttributionRange = resolveDeveloperAttributionRange");
482
+ expect(page).toContain("...(developerAttributionRange === undefined");
483
+ expect(page).toMatch(
484
+ /agentInvocationExportSource \?\?\s*developerExportSource \?\?\s*costGroupExportSource/,
485
+ );
486
+ });
487
+
368
488
  it("prepares Agent Invocations from every server page for export", () => {
369
489
  const page = readFileSync(new URL("./CostMonitorPage.tsx", import.meta.url), "utf8");
370
490
  const exportSection = page.slice(
371
491
  page.indexOf("const agentInvocationExportSource"),
372
- page.indexOf("const costGroupExportSource"),
492
+ page.indexOf("const developerExportSource"),
373
493
  );
374
494
  expect(page).toContain('view === "agentCalls" && serverPagingEligible');
375
495
  expect(page).toContain("agentInvocationExportSource");
376
496
  expect(page).toMatch(
377
- /pageSource=\{\s*compactExportSource \?\? agentInvocationExportSource \?\? costGroupExportSource\s*\}/,
497
+ /pageSource=\{\s*compactExportSource \?\?\s*agentInvocationExportSource \?\?\s*developerExportSource \?\?\s*costGroupExportSource\s*\}/,
378
498
  );
379
499
  expect(page).toContain("if (page.runs.length === 0) return");
380
500
  expect(exportSection.match(/limit: 100/g)).toHaveLength(2);
@@ -11,7 +11,10 @@ import type { CostMonitorProjection } from "../contracts.js";
11
11
  import { normalizeCostProvider } from "../provider.js";
12
12
  import { aggregateTaskCostRows } from "../task-cost-groups.js";
13
13
  import { withTokenBackedCompactDuration } from "./CompactLifecycleCostTable.js";
14
- import { CostDeveloperAttributionPanel } from "./CostDeveloperAttributionPanel.js";
14
+ import {
15
+ CostDeveloperAttributionPanel,
16
+ loadCostDeveloperAttribution,
17
+ } from "./CostDeveloperAttributionPanel.js";
15
18
  import {
16
19
  type CostFilterState,
17
20
  CostFilters,
@@ -25,8 +28,8 @@ import {
25
28
  useCostMonitorPeriod,
26
29
  } from "./CostFilters.js";
27
30
  import { CostMonitorContent } from "./CostMonitorContent.js";
28
- import { ChevronDownIcon, ListFilterIcon, LocaleFlag, SettingsIcon } from "./CostMonitorIcons.js";
29
31
  import { CostMonitorLoadErrors } from "./CostMonitorLoadErrors.js";
32
+ import { CostMonitorNavigation, type CostNavigationPopup } from "./CostMonitorNavigation.js";
30
33
  import { CostSummary } from "./CostSummary.js";
31
34
  import {
32
35
  groupRows,
@@ -64,8 +67,6 @@ import "./cost-monitor-responsive.css";
64
67
  import "./cost-run-origin.css";
65
68
  import "./cost-monitor-embedded.css";
66
69
 
67
- const zaipLogoUrl = new URL("../../../../../config/src/logo_novo_azul.png", import.meta.url).href;
68
-
69
70
  export {
70
71
  findRunDetails,
71
72
  isCurrentCostRunPageGeneration,
@@ -88,6 +89,20 @@ export function isCostViewExportEligible(view: CostView): boolean {
88
89
  return view !== "overview";
89
90
  }
90
91
 
92
+ /** Developers has no local provider/model filter contract. In the embedded
93
+ * all-time surface, do not let those residual UI filters narrow it to month. */
94
+ export function resolveDeveloperAttributionRange({
95
+ embedded,
96
+ globalDateRange,
97
+ runPageRange,
98
+ }: {
99
+ embedded: boolean;
100
+ globalDateRange?: { start: string; end: string };
101
+ runPageRange?: { from: string; to: string };
102
+ }): { from: string; to: string } | undefined {
103
+ return embedded && globalDateRange === undefined ? undefined : runPageRange;
104
+ }
105
+
91
106
  /**
92
107
  * The Cost period request filters Runs, while Operations is the global snapshot
93
108
  * used for shared entity detail. Keep the Executions tab on that same active
@@ -183,9 +198,7 @@ export function CostMonitorPage({
183
198
  onCurrencyChange?.(next);
184
199
  };
185
200
  const [period, setPeriod] = useState<CostPeriod>(defaultCostPeriod);
186
- const [filtersOpen, setFiltersOpen] = useState(false);
187
- const [localeOpen, setLocaleOpen] = useState(false);
188
- const [settingsOpen, setSettingsOpen] = useState(false);
201
+ const [navigationPopup, setNavigationPopup] = useState<CostNavigationPopup>(null);
189
202
  const [selectedRun, setSelectedRun] = useState<RunProjection | null>(null);
190
203
  const [detailData, setDetailData] = useState<CostMonitorProjection | null>(null);
191
204
  const [detailLoading, setDetailLoading] = useState(false);
@@ -216,6 +229,13 @@ export function CostMonitorPage({
216
229
  period,
217
230
  runPageRange,
218
231
  });
232
+ // Developers has no local provider/model filter contract. The embedded
233
+ // surface therefore remains all-time unless the host supplies a date range.
234
+ const developerAttributionRange = resolveDeveloperAttributionRange({
235
+ embedded,
236
+ ...(globalFilters?.dateRange === undefined ? {} : { globalDateRange: globalFilters.dateRange }),
237
+ ...(runPageRange === undefined ? {} : { runPageRange }),
238
+ });
219
239
  const compactPageRange = resolveCompactCostPageRange({
220
240
  embedded,
221
241
  globalDateRange: globalFilters?.dateRange,
@@ -545,6 +565,7 @@ export function CostMonitorPage({
545
565
  const usingCompactPage = compactKind !== null && loadReadModelPage !== undefined;
546
566
  const exportRows = useMemo(() => {
547
567
  if (usingCompactPage) return compactPage?.rows ?? staleCompactPage?.rows ?? [];
568
+ if (view === "developers") return [];
548
569
  if (view === "executions") return visibleExecutions;
549
570
  if (view === "conversations") return groups.conversations;
550
571
  if (view === "tasks") return groups.tasks;
@@ -651,6 +672,25 @@ export function CostMonitorPage({
651
672
  },
652
673
  }
653
674
  : undefined;
675
+ // The developer table is paginated only by the DataTable in the browser.
676
+ // Its API response is the full attributed population, so export from the
677
+ // same endpoint and exact active period instead of the visible ten rows.
678
+ const developerExportSource =
679
+ view === "developers"
680
+ ? {
681
+ prepare: async () => {
682
+ const developerRows = await loadCostDeveloperAttribution({
683
+ ...(developerAttributionRange === undefined ? {} : developerAttributionRange),
684
+ });
685
+ return {
686
+ totalRows: developerRows.length,
687
+ pages: async function* () {
688
+ yield developerRows as readonly Record<string, unknown>[];
689
+ },
690
+ };
691
+ },
692
+ }
693
+ : undefined;
654
694
  // Period views are paginated SQLite projections too. Exporting their
655
695
  // already-rendered group rows truncates the workbook to the visible page
656
696
  // (and can lose the projection's factual duration). Capture the same
@@ -749,151 +789,23 @@ export function CostMonitorPage({
749
789
  retryGroup={groupPageState.retry}
750
790
  />
751
791
  {embedded ? null : (
752
- <>
753
- <nav aria-label="Navegação do Cost Monitor" className="cost-monitor-nav">
754
- <div className="cost-monitor-brand">
755
- <strong className="cost-monitor-title">{text.title}</strong>
756
- <small className="cost-last-updated">{updatedLabel}</small>
757
- </div>
758
- <img alt="ZAIP" className="cost-monitor-logo" src={zaipLogoUrl} />
759
- <div className="cost-nav-actions">
760
- <button
761
- aria-label={text.refresh}
762
- className="cost-nav-icon cost-refresh-action"
763
- disabled={loading}
764
- onClick={onRefresh}
765
- type="button"
766
- >
767
- ↻
768
- </button>
769
- <div className="cost-nav-popup-anchor cost-filter-action">
770
- <button
771
- className={filtersOpen ? "cost-nav-button is-active" : "cost-nav-button"}
772
- onClick={() => {
773
- setFiltersOpen((open) => !open);
774
- setLocaleOpen(false);
775
- setSettingsOpen(false);
776
- }}
777
- type="button"
778
- >
779
- <ListFilterIcon /> {text.filters}
780
- </button>
781
- {filtersOpen ? (
782
- <div className="cost-nav-popup cost-nav-filter-popup">
783
- <CostFilters
784
- available={data.filters_available}
785
- hideQuery={embedded}
786
- locale={locale}
787
- onChange={setFilters}
788
- value={filters}
789
- />
790
- </div>
791
- ) : null}
792
- </div>
793
- <div className="cost-nav-popup-anchor cost-locale-action">
794
- <button
795
- className={localeOpen ? "cost-nav-button is-active" : "cost-nav-button"}
796
- onClick={() => {
797
- setLocaleOpen((open) => !open);
798
- setFiltersOpen(false);
799
- setSettingsOpen(false);
800
- }}
801
- type="button"
802
- >
803
- <LocaleFlag locale={locale} />
804
- {locale === "pt-BR" ? "PT-BR" : "EN-US"}
805
- <ChevronDownIcon />
806
- </button>
807
- {localeOpen ? (
808
- <div className="cost-nav-popup cost-nav-locale-popup">
809
- <button onClick={() => changeLocale("pt-BR")} type="button">
810
- <LocaleFlag locale="pt-BR" /> Português do Brasil{" "}
811
- {locale === "pt-BR" ? "✓" : ""}
812
- </button>
813
- <button onClick={() => changeLocale("en-US")} type="button">
814
- <LocaleFlag locale="en-US" /> English {locale === "en-US" ? "✓" : ""}
815
- </button>
816
- </div>
817
- ) : null}
818
- </div>
819
- <div className="cost-nav-popup-anchor cost-settings-action">
820
- <button
821
- aria-label={text.settings}
822
- className={settingsOpen ? "cost-nav-icon is-active" : "cost-nav-icon"}
823
- onClick={() => {
824
- setSettingsOpen((open) => !open);
825
- setFiltersOpen(false);
826
- setLocaleOpen(false);
827
- }}
828
- type="button"
829
- >
830
- <SettingsIcon />
831
- </button>
832
- {settingsOpen ? (
833
- <div className="cost-nav-popup cost-nav-settings-popup">
834
- <div className="cost-mobile-settings">
835
- <small className="cost-mobile-updated">{updatedLabel}</small>
836
- <button disabled={loading} onClick={onRefresh} type="button">
837
- ↻{" "}
838
- {loading
839
- ? locale === "pt-BR"
840
- ? "Atualizando…"
841
- : "Refreshing…"
842
- : text.refresh}
843
- </button>
844
- <label>
845
- <span>{locale === "pt-BR" ? "Idioma" : "Language"}</span>
846
- <select
847
- onChange={(event) => changeLocale(event.target.value as CostLocale)}
848
- value={locale}
849
- >
850
- <option value="pt-BR">Português do Brasil</option>
851
- <option value="en-US">English</option>
852
- </select>
853
- </label>
854
- <div className="cost-mobile-filters">
855
- <strong>{text.filters}</strong>
856
- <CostFilters
857
- available={data.filters_available}
858
- hideQuery={embedded}
859
- locale={locale}
860
- onChange={setFilters}
861
- value={filters}
862
- />
863
- </div>
864
- </div>
865
- <label>
866
- <span>{text.currency}</span>
867
- <select
868
- onChange={(event) => setCurrency(event.target.value as CostCurrency)}
869
- value={currency}
870
- >
871
- <option value="BRL">BRL · R$</option>
872
- <option value="USD">USD · $</option>
873
- </select>
874
- </label>
875
- </div>
876
- ) : null}
877
- </div>
878
- </div>
879
- </nav>
880
- <header className="cost-hero">
881
- <p className="cost-hero-description">{text.subtitle}</p>
882
- <label className="cost-period">
883
- <span>{text.period}</span>
884
- <select
885
- onChange={(event) => setPeriod(event.target.value as CostPeriod)}
886
- value={period}
887
- >
888
- <option value="all">{text.allTime}</option>
889
- <option value="7">{text.last7}</option>
890
- <option value="30">{text.last30}</option>
891
- <option value="90">{text.last90}</option>
892
- <option value="month">{text.thisMonth}</option>
893
- </select>
894
- </label>
895
- </header>
896
- </>
792
+ <CostMonitorNavigation
793
+ currency={currency}
794
+ filters={filters}
795
+ filtersAvailable={data.filters_available}
796
+ loading={loading}
797
+ locale={locale}
798
+ onCurrencyChange={setCurrency}
799
+ onFiltersChange={setFilters}
800
+ onLocaleChange={changeLocale}
801
+ onPopupChange={setNavigationPopup}
802
+ onPeriodChange={setPeriod}
803
+ onRefresh={onRefresh}
804
+ period={period}
805
+ popup={navigationPopup}
806
+ text={text}
807
+ updatedLabel={updatedLabel}
808
+ />
897
809
  )}
898
810
  {embedded ? null : (
899
811
  <>
@@ -922,7 +834,10 @@ export function CostMonitorPage({
922
834
  <ExcelExportAction
923
835
  filters={{ ...filters, ...globalFilters, period }}
924
836
  pageSource={
925
- compactExportSource ?? agentInvocationExportSource ?? costGroupExportSource
837
+ compactExportSource ??
838
+ agentInvocationExportSource ??
839
+ developerExportSource ??
840
+ costGroupExportSource
926
841
  }
927
842
  rows={exportRows as unknown as readonly Record<string, unknown>[]}
928
843
  view={`cost-${view}`}
@@ -944,9 +859,9 @@ export function CostMonitorPage({
944
859
  view === "developers" ? (
945
860
  <CostDeveloperAttributionPanel
946
861
  locale={locale}
947
- {...(runPageRange === undefined
862
+ {...(developerAttributionRange === undefined
948
863
  ? {}
949
- : { from: runPageRange.from, to: runPageRange.to })}
864
+ : { from: developerAttributionRange.from, to: developerAttributionRange.to })}
950
865
  />
951
866
  ) : null
952
867
  }
@@ -537,7 +537,8 @@ export function formatExecutionAverageHint(
537
537
  totalDays: number,
538
538
  locale: OperationsLocale,
539
539
  ): string {
540
- if (totalDurationMs <= 0 || totalTasks <= 0 || totalDays <= 0) {
540
+ if (totalTasks <= 0) return "0";
541
+ if (totalDurationMs <= 0 || totalDays <= 0) {
541
542
  return locale === "pt-BR"
542
543
  ? "Médias de execução indisponíveis"
543
544
  : "Execution averages unavailable";
@@ -12,6 +12,7 @@ import {
12
12
  findLatestCreatedAt,
13
13
  findLatestTaskCreatedAt,
14
14
  formatExecutionAverageHint,
15
+ formatSummaryMetricValue,
15
16
  formatTokenThroughputHint,
16
17
  macroClassificationStats,
17
18
  macroStatusDistribution,
@@ -278,11 +279,12 @@ describe("execution average summary", () => {
278
279
  );
279
280
  });
280
281
 
281
- it("localizes English and preserves unavailable coverage", () => {
282
+ it("localizes English and shows zero for an empty population", () => {
282
283
  expect(formatExecutionAverageHint(3_600_000, 3, 1, "en-US")).toBe(
283
284
  "20 min / task (1 h / day) (1 day)",
284
285
  );
285
- expect(formatExecutionAverageHint(0, 0, 0, "pt-BR")).toBe("Médias de execução indisponíveis");
286
+ expect(formatExecutionAverageHint(0, 0, 0, "pt-BR")).toBe("0");
287
+ expect(formatExecutionAverageHint(0, 0, 0, "en-US")).toBe("0");
286
288
  });
287
289
  });
288
290
 
@@ -351,14 +353,20 @@ describe("overviewMetricAverages", () => {
351
353
  });
352
354
  });
353
355
 
354
- it("passes unknown totals through as unavailable instead of coercing them to zero", () => {
356
+ it("keeps unknown totals null internally and renders zero only for an empty population", () => {
355
357
  const averages = overviewMetricAverages({ durationMs: null, tokens: null, costBrl: null }, 3);
356
358
  expect(averages).toEqual({ durationMs: null, tokens: null, costBrl: null });
357
359
  const source = readOverviewSource();
358
360
  expect(source).toContain(
359
361
  "durationMs: totalDurationMetric, tokens: totalTokensMetric, costBrl: totalCostMetric",
360
362
  );
361
- expect(source).toContain("formatEntityDuration(averageDuration, locale)");
363
+ expect(formatSummaryMetricValue(null, 0, (value) => String(value), "pt-BR")).toBe("0");
364
+ expect(formatSummaryMetricValue(null, 0, (value) => String(value), "en-US")).toBe("0");
365
+ expect(formatSummaryMetricValue(null, 1, (value) => String(value), "pt-BR")).toBe(
366
+ "Indisponível",
367
+ );
368
+ expect(formatSummaryMetricValue(42, 0, (value) => String(value), "pt-BR")).toBe("42");
369
+ expect(source).toContain("formatSummaryMetricValue(\n averageDuration,");
362
370
  expect(source).not.toContain("executionMetrics.durationMs ?? 0");
363
371
  });
364
372
  });
@@ -98,6 +98,20 @@ function formatEntityDuration(value: number | null, locale: OperationsLocale): s
98
98
  if (value !== null) return formatDurationMs(value, locale);
99
99
  return locale === "pt-BR" ? "Indisponível" : "Unavailable";
100
100
  }
101
+
102
+ /**
103
+ * Keeps missing source facts as null while making a genuinely empty selected
104
+ * population readable as the factual numeric zero in the overview.
105
+ */
106
+ export function formatSummaryMetricValue(
107
+ value: number | null,
108
+ populationCount: number | null,
109
+ formatKnownValue: (knownValue: number) => string,
110
+ locale: OperationsLocale,
111
+ ): string {
112
+ if (value !== null) return formatKnownValue(value);
113
+ return populationCount === 0 ? "0" : formatEntityDuration(null, locale);
114
+ }
101
115
  export function OperationsOverview({
102
116
  currency,
103
117
  data,
@@ -399,18 +413,32 @@ export function OperationsOverview({
399
413
  : findLatestCreatedAt(tasks)
400
414
  : (selectedMacroAggregate?.latest_created_at ?? findLatestCreatedAt(selectedMacros));
401
415
  const metricStats = [
402
- [text.averageTime, formatEntityDuration(averageDuration, locale)],
416
+ [
417
+ text.averageTime,
418
+ formatSummaryMetricValue(
419
+ averageDuration,
420
+ selectedCount,
421
+ (value) => formatDurationMs(value, locale),
422
+ locale,
423
+ ),
424
+ ],
403
425
  [
404
426
  text.averageTokens,
405
- averageTokens === null
406
- ? formatEntityDuration(null, locale)
407
- : formatCompactNumber(averageTokens, locale),
427
+ formatSummaryMetricValue(
428
+ averageTokens,
429
+ selectedCount,
430
+ (value) => formatCompactNumber(value, locale),
431
+ locale,
432
+ ),
408
433
  ],
409
434
  [
410
435
  text.averageCost,
411
- averageCost === null
412
- ? formatEntityDuration(null, locale)
413
- : formatCurrency(averageCost, currency, locale),
436
+ formatSummaryMetricValue(
437
+ averageCost,
438
+ selectedCount,
439
+ (value) => formatCurrency(value, currency, locale),
440
+ locale,
441
+ ),
414
442
  ],
415
443
  ] as const;
416
444
  const aggregateTopTasks: EntityMetric[] = (overviewMetrics?.top_tasks ?? []).map((metric) => ({
@@ -580,14 +608,23 @@ export function OperationsOverview({
580
608
  ? `${text.totalExecutionTime} de ${copy.shortPlural}`
581
609
  : `${copy.shortPlural} ${text.totalExecutionTime.toLowerCase()}`
582
610
  }
583
- value={formatEntityDuration(totalDurationMetric, locale)}
611
+ value={formatSummaryMetricValue(
612
+ totalDurationMetric,
613
+ metricPopulationCount,
614
+ (value) => formatDurationMs(value, locale),
615
+ locale,
616
+ )}
584
617
  />
585
618
  <Kpi
586
619
  hint={
587
620
  tokensPerTask === null || tokensPerHour === null
588
- ? locale === "pt-BR"
589
- ? "Tokens por tarefa e por hora indisponíveis"
590
- : "Tokens per task and hour unavailable"
621
+ ? metricPopulationCount === 0
622
+ ? locale === "pt-BR"
623
+ ? `0 / ${metricUnit} (0 / hora)`
624
+ : `0 / ${metricUnit} (0 / hour)`
625
+ : locale === "pt-BR"
626
+ ? "Tokens por tarefa e por hora indisponíveis"
627
+ : "Tokens per task and hour unavailable"
591
628
  : locale === "pt-BR"
592
629
  ? `${formatCompactNumber(tokensPerTask, locale)} / ${metricUnit} (${formatCompactNumber(tokensPerHour, locale, 0)} / hora)`
593
630
  : `${formatCompactNumber(tokensPerTask, locale)} / ${metricUnit} (${formatCompactNumber(tokensPerHour, locale, 0)} / hour)`
@@ -601,18 +638,23 @@ export function OperationsOverview({
601
638
  ? `Tokens gastos por ${copy.shortPlural}`
602
639
  : `${copy.shortPlural} tokens spent`
603
640
  }
604
- value={
605
- totalTokensMetric === null
606
- ? formatEntityDuration(null, locale)
607
- : formatCompactNumber(totalTokensMetric, locale)
608
- }
641
+ value={formatSummaryMetricValue(
642
+ totalTokensMetric,
643
+ metricPopulationCount,
644
+ (value) => formatCompactNumber(value, locale),
645
+ locale,
646
+ )}
609
647
  />
610
648
  <Kpi
611
649
  hint={
612
650
  hourlyCost === null || costPerTask === null
613
- ? locale === "pt-BR"
614
- ? "Custo por tarefa e por hora indispon\u00edvel"
615
- : "Cost per task and hour unavailable"
651
+ ? metricPopulationCount === 0
652
+ ? locale === "pt-BR"
653
+ ? `${formatRoundedCurrency(0, currency, locale)} / ${metricUnit} (${formatRoundedCurrency(0, currency, locale)} / hora)`
654
+ : `${formatRoundedCurrency(0, currency, locale)} / ${metricUnit} (${formatRoundedCurrency(0, currency, locale)} / hour)`
655
+ : locale === "pt-BR"
656
+ ? "Custo por tarefa e por hora indispon\u00edvel"
657
+ : "Cost per task and hour unavailable"
616
658
  : locale === "pt-BR"
617
659
  ? `${formatRoundedCurrency(costPerTask, currency, locale)} / ${metricUnit} (${formatRoundedCurrency(hourlyCost, currency, locale)} / hora)`
618
660
  : `${formatRoundedCurrency(costPerTask, currency, locale)} / ${metricUnit} (${formatRoundedCurrency(hourlyCost, currency, locale)} / hour)`
@@ -626,11 +668,12 @@ export function OperationsOverview({
626
668
  ? `Custo de ${copy.shortPlural}`
627
669
  : `${copy.shortPlural} cost`
628
670
  }
629
- value={
630
- totalCostMetric === null
631
- ? formatEntityDuration(null, locale)
632
- : formatRoundedCurrency(totalCostMetric, currency, locale)
633
- }
671
+ value={formatSummaryMetricValue(
672
+ totalCostMetric,
673
+ metricPopulationCount,
674
+ (value) => formatRoundedCurrency(value, currency, locale),
675
+ locale,
676
+ )}
634
677
  />
635
678
  </div>
636
679