@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.
- package/LICENSE +6 -3
- package/README.md +8 -2
- package/dist/services/codex-pre-tool-hook-template.js +1 -1
- package/dist/services/runtime-template.test.js +9 -8
- package/dist/services/runtime-template.test.js.map +1 -1
- package/package.json +2 -2
- package/templates/.agents/dashboard/.zaip-managed.json +414 -394
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-ovxurkIk.js → CostCalculationGuidePage-D1mpyYwh.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-BXy6nti1.js → DashboardHelpDialog-CKO47D5s.js} +30 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-vSvvOHQp.js → PricingSettingsDialog-BuSlCbyZ.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-zH758vfs.js → ProjectSettingsPage-CUHFkAhZ.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-CTH2wDs5.js → TraceabilityWorkspace-BmASq2gW.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-Csj6LiAt.js +68 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-B_lDDHn1.js → icons-vendor-VyTTl_ph.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-DIwDvCu5.js +2 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-hPPfVGQv.css +1 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-B3TJrD4O.js → react-vendor-CTC6vtln.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-7f1j5wx3.js → traceability-9Mtvbwrg.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/index.html +6 -6
- package/templates/.agents/dashboard/apps/frontend/src/App.tsx +74 -10
- package/templates/.agents/dashboard/apps/frontend/src/AppRequestGuards.tsx +44 -0
- package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +103 -2
- package/templates/.agents/dashboard/apps/frontend/src/dashboard-capability-nav.css +11 -0
- package/templates/.agents/dashboard/apps/frontend/src/operations-period-contract.test.ts +14 -0
- package/templates/.agents/dashboard/apps/frontend/src/operations-period.test.ts +3 -6
- package/templates/.agents/dashboard/apps/frontend/src/operations-period.ts +3 -2
- package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +2 -2
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperAttributionPanel.test.ts +60 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperAttributionPanel.tsx +26 -9
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorNavigation.tsx +189 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.test.ts +122 -2
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +70 -155
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.helpers.tsx +2 -1
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.test.ts +12 -4
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.tsx +67 -24
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +19 -4
- package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +59 -0
- package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +55 -2
- package/templates/.agents/dashboard/modules/operations/src/react/operation-date-range.test.ts +25 -0
- package/templates/.agents/dashboard/modules/operations/src/react/operation-date-range.ts +29 -0
- package/templates/.agents/dashboard/modules/operations/src/react/operations-coverage-toast.test.ts +3 -2
- package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +12 -2
- package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +274 -246
- package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +255 -227
- package/templates/.agents/dashboard/packages/local-runtime/server.mjs +263 -235
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-dimension-facts.test.ts +282 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-dimension-facts.ts +71 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-launch-progress.ts +18 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-stale-seed.ts +50 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +9 -6
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +45 -73
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-seed.test.ts +79 -2
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-work-item-index.test.ts +56 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.mjs +26 -8
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.test.mjs +45 -4
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-lifecycle.mjs +58 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-lifecycle.test.mjs +81 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +171 -71
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +152 -11
- package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +4 -9
- package/templates/.agents/dashboard/tests/e2e/playwright.config.ts +9 -1
- package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-loading.spec.ts +1 -1
- package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-pagination.spec.ts +4 -0
- package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-period-transition.spec.ts +238 -0
- package/templates/.agents/dashboard/tests/e2e/specs/dashboard-performance-matrix.spec.ts +4 -0
- package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +60 -17
- package/templates/.agents/requirements/autonomous-assurance.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 +1 -1
- package/templates/.agents/requirements/public-distribution.md +1 -1
- package/templates/.agents/requirements/software-architecture.md +1 -1
- package/templates/.agents/requirements/strategic-improvements.md +1 -1
- package/templates/.agents/requirements/work-lifecycle.md +1 -1
- package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
- package/templates/.agents/runtime/bin/zaip.mjs +7 -7
- package/templates/.agents/runtime/checksums.sha256 +3 -3
- package/templates/.agents/runtime/provenance.json +3 -3
- package/templates/.agents/runtime/sbom.cdx.json +1595 -3491
- package/templates/.agents/skills/zaip-skill-lifecycle-closure/SKILL.md +16 -0
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-local-telemetry.mjs +19 -20
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-local-telemetry.test.mjs +143 -0
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.mjs +14 -0
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.mjs +3 -1
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.test.mjs +21 -0
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/source-size-telemetry/model-identity-reconciliation.mjs +3 -0
- package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +29 -0
- package/templates/template-manifest.json +539 -935
- package/templates/.agents/dashboard/.tmp-run-list.json +0 -254
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-DKwP1stV.js +0 -68
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-B31GMG5O.css +0 -1
- 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
|
|
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
|
|
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 {
|
|
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 [
|
|
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
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
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 ??
|
|
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
|
-
{...(
|
|
862
|
+
{...(developerAttributionRange === undefined
|
|
948
863
|
? {}
|
|
949
|
-
: { from:
|
|
864
|
+
: { from: developerAttributionRange.from, to: developerAttributionRange.to })}
|
|
950
865
|
/>
|
|
951
866
|
) : null
|
|
952
867
|
}
|
package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverview.helpers.tsx
CHANGED
|
@@ -537,7 +537,8 @@ export function formatExecutionAverageHint(
|
|
|
537
537
|
totalDays: number,
|
|
538
538
|
locale: OperationsLocale,
|
|
539
539
|
): string {
|
|
540
|
-
if (
|
|
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
|
|
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("
|
|
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("
|
|
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(
|
|
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
|
-
[
|
|
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
|
-
|
|
406
|
-
|
|
407
|
-
|
|
427
|
+
formatSummaryMetricValue(
|
|
428
|
+
averageTokens,
|
|
429
|
+
selectedCount,
|
|
430
|
+
(value) => formatCompactNumber(value, locale),
|
|
431
|
+
locale,
|
|
432
|
+
),
|
|
408
433
|
],
|
|
409
434
|
[
|
|
410
435
|
text.averageCost,
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
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={
|
|
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
|
-
?
|
|
589
|
-
?
|
|
590
|
-
|
|
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
|
|
606
|
-
|
|
607
|
-
|
|
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
|
-
?
|
|
614
|
-
?
|
|
615
|
-
|
|
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
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
671
|
+
value={formatSummaryMetricValue(
|
|
672
|
+
totalCostMetric,
|
|
673
|
+
metricPopulationCount,
|
|
674
|
+
(value) => formatRoundedCurrency(value, currency, locale),
|
|
675
|
+
locale,
|
|
676
|
+
)}
|
|
634
677
|
/>
|
|
635
678
|
</div>
|
|
636
679
|
|