@zaip-ai/cli 4.9.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/services/adapter-hook-sources.d.ts +3 -0
- package/dist/services/adapter-hook-sources.d.ts.map +1 -1
- package/dist/services/adapter-hook-sources.js +3 -0
- package/dist/services/adapter-hook-sources.js.map +1 -1
- package/dist/services/adapters.d.ts +1 -1
- package/dist/services/adapters.d.ts.map +1 -1
- package/dist/services/adapters.js +5 -2
- package/dist/services/adapters.js.map +1 -1
- package/dist/services/adapters.test.js +1 -1
- package/dist/services/adapters.test.js.map +1 -1
- package/dist/services/codex-conversation-lifecycle-hook.d.ts.map +1 -1
- package/dist/services/codex-conversation-lifecycle-hook.js +16 -6
- package/dist/services/codex-conversation-lifecycle-hook.js.map +1 -1
- package/dist/services/codex-hook-activation.d.ts +1 -1
- package/dist/services/codex-hook-activation.d.ts.map +1 -1
- package/dist/services/codex-hook-activation.js +116 -10
- package/dist/services/codex-hook-activation.js.map +1 -1
- package/dist/services/codex-pre-tool-cli-capture-template.d.ts +2 -0
- package/dist/services/codex-pre-tool-cli-capture-template.d.ts.map +1 -0
- package/dist/services/codex-pre-tool-cli-capture-template.js +104 -0
- package/dist/services/codex-pre-tool-cli-capture-template.js.map +1 -0
- package/dist/services/codex-pre-tool-cloud-template.d.ts +2 -0
- package/dist/services/codex-pre-tool-cloud-template.d.ts.map +1 -0
- package/dist/services/codex-pre-tool-cloud-template.js +190 -0
- package/dist/services/codex-pre-tool-cloud-template.js.map +1 -0
- package/dist/services/codex-pre-tool-dependencies.test-support.d.ts +2 -0
- package/dist/services/codex-pre-tool-dependencies.test-support.d.ts.map +1 -0
- package/dist/services/codex-pre-tool-dependencies.test-support.js +9 -0
- package/dist/services/codex-pre-tool-dependencies.test-support.js.map +1 -0
- package/dist/services/codex-pre-tool-hook-fixture.test-support.d.ts +5 -0
- package/dist/services/codex-pre-tool-hook-fixture.test-support.d.ts.map +1 -0
- package/dist/services/codex-pre-tool-hook-fixture.test-support.js +19 -0
- package/dist/services/codex-pre-tool-hook-fixture.test-support.js.map +1 -0
- package/dist/services/codex-pre-tool-hook-template-bootstrap.test.d.ts +2 -0
- package/dist/services/codex-pre-tool-hook-template-bootstrap.test.d.ts.map +1 -0
- package/dist/services/codex-pre-tool-hook-template-bootstrap.test.js +107 -0
- package/dist/services/codex-pre-tool-hook-template-bootstrap.test.js.map +1 -0
- package/dist/services/codex-pre-tool-hook-template.d.ts.map +1 -1
- package/dist/services/codex-pre-tool-hook-template.js +96 -210
- package/dist/services/codex-pre-tool-hook-template.js.map +1 -1
- package/dist/services/codex-pre-tool-hook-template.test.js +32 -80
- package/dist/services/codex-pre-tool-hook-template.test.js.map +1 -1
- package/dist/services/codex-pre-tool-host-context-template.d.ts +2 -0
- package/dist/services/codex-pre-tool-host-context-template.d.ts.map +1 -0
- package/dist/services/codex-pre-tool-host-context-template.js +139 -0
- package/dist/services/codex-pre-tool-host-context-template.js.map +1 -0
- package/dist/services/codex-pre-tool-support-template.d.ts.map +1 -1
- package/dist/services/codex-pre-tool-support-template.js +61 -101
- package/dist/services/codex-pre-tool-support-template.js.map +1 -1
- package/dist/services/codex-subagent-hook-template.d.ts +1 -1
- package/dist/services/codex-subagent-hook-template.d.ts.map +1 -1
- package/dist/services/codex-subagent-hook-template.js +13 -2
- package/dist/services/codex-subagent-hook-template.js.map +1 -1
- package/dist/services/codex-validation-runtime-template.d.ts.map +1 -1
- package/dist/services/codex-validation-runtime-template.js +10 -4
- package/dist/services/codex-validation-runtime-template.js.map +1 -1
- package/dist/services/runtime-template-test-support.d.ts +1 -0
- package/dist/services/runtime-template-test-support.d.ts.map +1 -1
- package/dist/services/runtime-template-test-support.js +23 -2
- package/dist/services/runtime-template-test-support.js.map +1 -1
- package/dist/services/runtime-template.test.js +4 -23
- package/dist/services/runtime-template.test.js.map +1 -1
- package/package.json +1 -1
- package/templates/.agents/README-ENG.md +49 -7
- package/templates/.agents/README.md +37 -10
- package/templates/.agents/config/agents/discovery.json +1 -1
- package/templates/.agents/dashboard/.zaip-managed.json +121 -73
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-BW7Vy1cp.js → CostCalculationGuidePage-w6pjt4OM.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-Cp_hEVd8.js → DashboardHelpDialog-DhlntQcR.js} +76 -11
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-Prcs-ydy.js → PricingSettingsDialog-BHoZT2an.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-Cu40wAFF.js → ProjectSettingsPage-IcncIZ9Y.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-Bh3YBd3C.js → TraceabilityWorkspace-C6rORjpr.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-uPnOBShc.js +68 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{dashboard-localization-CF7B1Hjn.js → dashboard-localization-DWXjEIgc.js} +49 -7
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DOhy6D2-.js → icons-vendor-Hc9fp_gs.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{index-qVSJnJ0_.js → index-BqeOEZW5.js} +2 -2
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-CkPC-yMz.js → react-vendor-C9ta_O10.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-BEdGoIDm.js → traceability-IcefoTOv.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/index.html +5 -5
- package/templates/.agents/dashboard/apps/frontend/package.json +1 -1
- package/templates/.agents/dashboard/modules/cost-monitor/package.json +1 -1
- package/templates/.agents/dashboard/modules/cost-monitor/src/contracts.ts +10 -3
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.run-page.test.ts +5 -1
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostGroupsTable.period-values.test.tsx +2 -2
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables-keeps-legacy-codex-identity-fields-blank.test.ts +5 -5
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +36 -43
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-loaders.ts +1 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-table-formatters.ts +6 -4
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/aggregate.ts +2 -2
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.ts +1 -1
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +11 -25
- package/templates/.agents/dashboard/modules/cost-monitor/src/server/cost-summary-defaults.ts +26 -0
- package/templates/.agents/dashboard/modules/operations/package.json +1 -1
- package/templates/.agents/dashboard/modules/operations/src/contracts.ts +11 -16
- package/templates/.agents/dashboard/modules/operations/src/planning-contracts.ts +14 -0
- package/templates/.agents/dashboard/modules/operations/src/react/DecisionTable.tsx +65 -0
- package/templates/.agents/dashboard/modules/operations/src/react/EntityTables.tsx +5 -71
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +10 -36
- package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +7 -2
- package/templates/.agents/dashboard/modules/operations/src/react/use-operations-filter-sync.ts +66 -0
- package/templates/.agents/dashboard/modules/traceability/package.json +1 -1
- package/templates/.agents/dashboard/packages/branding/package.json +1 -1
- package/templates/.agents/dashboard/packages/contracts/package.json +1 -1
- package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
- package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +193 -191
- package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +169 -169
- package/templates/.agents/dashboard/packages/local-runtime/package.json +1 -1
- package/templates/.agents/dashboard/packages/local-runtime/server.mjs +193 -191
- package/templates/.agents/dashboard/packages/local-runtime/src/active-capture-failure.test.ts +38 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/active-capture-failure.ts +35 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.test.ts +53 -2
- package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.ts +18 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/manifest-runs.ts +8 -2
- package/templates/.agents/dashboard/packages/local-runtime/src/server-capture-projection-order.test.ts +118 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-cost-run-search.test.ts +90 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-cost-run-search.ts +86 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-routes.ts +8 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/server-projection-loaders.ts +13 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-projections.ts +30 -74
- package/templates/.agents/dashboard/packages/local-runtime/src/server-route-handlers.ts +42 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-routes.test.ts +60 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server-run-projections.test.ts +12 -2
- package/templates/.agents/dashboard/packages/local-runtime/src/server-telemetry-command.test.ts +29 -4
- package/templates/.agents/dashboard/packages/local-runtime/src/server-telemetry-refresh.ts +7 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +51 -54
- package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +28 -45
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-filter-matrix.test.ts +5 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.test.ts +49 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.ts +44 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-cached-cost-page.ts +11 -10
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-summary.ts +1 -1
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-work-item-index.test.ts +18 -1
- package/templates/.agents/dashboard/packages/ui/package.json +1 -1
- package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
- package/templates/.agents/design/.zaip-managed.json +3 -3
- package/templates/.agents/design/apps/catalog/package.json +1 -1
- package/templates/.agents/design/packages/local-runtime/package.json +1 -1
- package/templates/.agents/requirements/autonomous-assurance.md +11 -2
- package/templates/.agents/requirements/cloud-hostinger-autonomy.md +1 -1
- package/templates/.agents/requirements/comparative-audit.md +1 -1
- package/templates/.agents/requirements/context-engine.md +1 -1
- package/templates/.agents/requirements/cost-observability.md +1 -1
- package/templates/.agents/requirements/dashboard-launch.md +1 -1
- package/templates/.agents/requirements/data-export.md +1 -1
- package/templates/.agents/requirements/design-system.md +1 -1
- package/templates/.agents/requirements/execution-engine.md +1 -1
- package/templates/.agents/requirements/kanban-dashboard.md +1 -1
- package/templates/.agents/requirements/model-orchestration.md +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 +2 -1
- package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
- package/templates/.agents/rules/run-lifecycle-closure.md +2 -1
- package/templates/.agents/rules/workflow.md +1 -0
- package/templates/.agents/runtime/VERSION +1 -1
- package/templates/.agents/runtime/bin/zaip.mjs +1178 -764
- package/templates/.agents/runtime/checksums.sha256 +6 -6
- package/templates/.agents/runtime/manifest.json +1 -1
- package/templates/.agents/runtime/pnpm-lock.sha256 +1 -1
- package/templates/.agents/runtime/provenance.json +5 -5
- package/templates/.agents/runtime/sbom.cdx.json +59 -59
- package/templates/.agents/skills/zaip-skill-execution/SKILL.md +1 -1
- package/templates/.agents/skills/zaip-skill-execution/references/controlled-release-recovery.md +14 -0
- package/templates/.agents/skills/zaip-skill-execution/references/runtime-governance.md +1 -1
- package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +5 -1
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/deploy-contract.mjs +12 -0
- package/templates/.agents/skills/zaip-skill-github-deploy/scripts/deploy-contract.test.mjs +25 -0
- package/templates/.agents/skills/zaip-skill-maintenance-enter/SKILL.md +23 -0
- package/templates/.agents/skills/zaip-skill-maintenance-enter/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-maintenance-enter/scripts/maintenance.mjs +213 -0
- package/templates/.agents/skills/zaip-skill-maintenance-enter/scripts/maintenance.test.mjs +207 -0
- package/templates/.agents/skills/zaip-skill-maintenance-exit/SKILL.md +21 -0
- package/templates/.agents/skills/zaip-skill-maintenance-exit/agents/openai.yaml +4 -0
- package/templates/.agents/skills/zaip-skill-release-qualification/SKILL.md +2 -0
- package/templates/.agents/skills/zaip-skill-release-qualification/references/local-preparation.md +37 -8
- package/templates/.agents/zaip-aios/VERSION +1 -1
- package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +38 -0
- package/templates/template-manifest.json +188 -116
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-Co2Jiuzh.js +0 -68
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { WorkspaceFileSystem, WorkspaceLayout } from "@zaip-ai/core";
|
|
2
2
|
import { PricingRepository, queryInvocationManifestPage } from "@zaip-ai/costs";
|
|
3
|
-
import type { CostMonitorProjection } from "@zaip-ai/dashboard-cost-monitor/contracts";
|
|
4
3
|
import { buildCostMonitorProjection } from "@zaip-ai/dashboard-cost-monitor/server";
|
|
5
4
|
import type { RunProjection } from "@zaip-ai/dashboard-operations/contracts";
|
|
6
5
|
import {
|
|
@@ -10,6 +9,7 @@ import {
|
|
|
10
9
|
} from "@zaip-ai/dashboard-operations/server";
|
|
11
10
|
import type { Context } from "hono";
|
|
12
11
|
import { mergeManifestRuns } from "./manifest-runs.js";
|
|
12
|
+
import { filterCompactCostRuns } from "./server-cost-run-search.js";
|
|
13
13
|
import {
|
|
14
14
|
configuredAgentNames,
|
|
15
15
|
invocationSummariesForPeriod,
|
|
@@ -190,18 +190,21 @@ export async function buildManifestCostRunPage(
|
|
|
190
190
|
// API callers may request Run terminal status; absent telemetry never
|
|
191
191
|
// matches an explicit status and remains visible only without this filter.
|
|
192
192
|
filters.statuses.length > 0;
|
|
193
|
-
const [operations,
|
|
193
|
+
const [operations, lifecycleSummary] = await Promise.all([
|
|
194
194
|
// This compact projection reads the durable manifest summary plus Tasks;
|
|
195
195
|
// it intentionally does not materialise Run records.
|
|
196
196
|
import("@zaip-ai/dashboard-operations/server").then(({ buildQuickOperationsProjection }) =>
|
|
197
|
-
buildQuickOperationsProjection(filesystem, layout, { from, to }),
|
|
197
|
+
buildQuickOperationsProjection(filesystem, layout, { from, to }, { includeBoardTasks: true }),
|
|
198
198
|
),
|
|
199
|
-
queryInvocationManifestPage(filesystem, { from, to, filters, offset, limit }),
|
|
200
199
|
filesystem
|
|
201
200
|
.readText(".agents/records/lifecycle/summary.json")
|
|
202
201
|
.then((value) => JSON.parse(value) as { run_metrics?: CompactRunMetric[] }),
|
|
203
202
|
]);
|
|
204
|
-
const compactRuns = (
|
|
203
|
+
const compactRuns = filterCompactCostRuns(
|
|
204
|
+
lifecycleSummary.run_metrics ?? [],
|
|
205
|
+
context.req.query("query") ?? "",
|
|
206
|
+
operations.tasks,
|
|
207
|
+
)
|
|
205
208
|
.filter((run) => runTimestampInRange(run.finished_at ?? run.started_at, from, to))
|
|
206
209
|
.sort(
|
|
207
210
|
(left, right) =>
|
|
@@ -215,6 +218,16 @@ export async function buildManifestCostRunPage(
|
|
|
215
218
|
filters.phases.length === 0
|
|
216
219
|
? compactRuns
|
|
217
220
|
: compactRuns.filter((run) => run.phase !== null && filters.phases.includes(run.phase));
|
|
221
|
+
const page = await queryInvocationManifestPage(filesystem, {
|
|
222
|
+
from,
|
|
223
|
+
to,
|
|
224
|
+
filters,
|
|
225
|
+
offset,
|
|
226
|
+
limit,
|
|
227
|
+
...(context.req.query("query")
|
|
228
|
+
? { runIds: new Set(lifecycleCandidates.map((run) => run.id)) }
|
|
229
|
+
: {}),
|
|
230
|
+
});
|
|
218
231
|
const telemetryRunIds = new Set(page.summaries.map((summary) => summary.run_id));
|
|
219
232
|
const baseRuns = hasTelemetryFilters
|
|
220
233
|
? // Provider/model/agent filters are telemetry dimensions. Their candidate
|
|
@@ -284,6 +297,8 @@ export function mergeLifecycleRunPage(
|
|
|
284
297
|
|
|
285
298
|
export interface CompactRunMetric {
|
|
286
299
|
id: string;
|
|
300
|
+
entrypoint?: string | null;
|
|
301
|
+
run_purpose?: string | null;
|
|
287
302
|
started_at: string;
|
|
288
303
|
finished_at: string | null;
|
|
289
304
|
task_id: string | null;
|
|
@@ -334,11 +349,16 @@ export function compactRunProjection(run: CompactRunMetric): RunProjection {
|
|
|
334
349
|
? run.status
|
|
335
350
|
: "completed",
|
|
336
351
|
phase: run.phase ?? "execution",
|
|
337
|
-
entrypoint:
|
|
352
|
+
entrypoint:
|
|
353
|
+
run.entrypoint === "observation"
|
|
354
|
+
? "observation"
|
|
355
|
+
: run.task_id === null
|
|
356
|
+
? "conversation"
|
|
357
|
+
: "task",
|
|
338
358
|
work_type: "new",
|
|
339
359
|
run_role: run.run_kind === "subrun" ? "subagent" : "orchestrator",
|
|
340
360
|
run_kind: run.run_kind ?? "run",
|
|
341
|
-
run_purpose: "execution",
|
|
361
|
+
run_purpose: run.run_purpose === "observation" ? "observation" : "execution",
|
|
342
362
|
requires_finalizer: false,
|
|
343
363
|
parent_run_id: run.parent_run_id,
|
|
344
364
|
client: "unknown",
|
|
@@ -354,7 +374,7 @@ export function compactRunProjection(run: CompactRunMetric): RunProjection {
|
|
|
354
374
|
terminal_call_count: null,
|
|
355
375
|
failed_call_count: 0,
|
|
356
376
|
cancelled_call_count: 0,
|
|
357
|
-
conversation_id: run.conversation_id ?? run.id,
|
|
377
|
+
conversation_id: run.entrypoint === "observation" ? null : (run.conversation_id ?? run.id),
|
|
358
378
|
execution_id: run.execution_id,
|
|
359
379
|
task_id: run.task_id,
|
|
360
380
|
spec_id: run.spec_id,
|
|
@@ -415,69 +435,5 @@ function normalizeRunRangeBoundary(value: string, boundary: "start" | "end"): st
|
|
|
415
435
|
return value;
|
|
416
436
|
}
|
|
417
437
|
|
|
418
|
-
|
|
419
|
-
export
|
|
420
|
-
projection: CostMonitorProjection,
|
|
421
|
-
offset: number,
|
|
422
|
-
limit: number,
|
|
423
|
-
): CostMonitorProjection {
|
|
424
|
-
const safeOffset = Math.max(0, offset);
|
|
425
|
-
const safeLimit = Math.min(500, Math.max(1, limit));
|
|
426
|
-
return {
|
|
427
|
-
...projection,
|
|
428
|
-
runs: projection.runs.slice(safeOffset, safeOffset + safeLimit),
|
|
429
|
-
run_page: {
|
|
430
|
-
offset: safeOffset,
|
|
431
|
-
limit: safeLimit,
|
|
432
|
-
total: projection.runs.length,
|
|
433
|
-
has_more: safeOffset + safeLimit < projection.runs.length,
|
|
434
|
-
},
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
/**
|
|
439
|
-
* The Runs endpoint advertises these dimensions as server-side filters. Apply
|
|
440
|
-
* them before offset pagination so the reported total and every page have the
|
|
441
|
-
* same population. Aggregate fields intentionally retain their full filtered
|
|
442
|
-
* projection semantics and are not derived from the transport page.
|
|
443
|
-
*/
|
|
444
|
-
export function filterCostRunRows(
|
|
445
|
-
projection: CostMonitorProjection,
|
|
446
|
-
filters: {
|
|
447
|
-
provider: readonly string[];
|
|
448
|
-
model: readonly string[];
|
|
449
|
-
agent: readonly string[];
|
|
450
|
-
phase: readonly string[];
|
|
451
|
-
status: readonly string[];
|
|
452
|
-
},
|
|
453
|
-
): CostMonitorProjection {
|
|
454
|
-
const includes = (values: readonly string[], value: string) =>
|
|
455
|
-
values.length === 0 || values.includes(value);
|
|
456
|
-
const provider = (value: string | null) => {
|
|
457
|
-
const normalized = value?.trim().toLowerCase();
|
|
458
|
-
return !normalized || normalized === "unknown" || normalized === "codex"
|
|
459
|
-
? "openai"
|
|
460
|
-
: normalized;
|
|
461
|
-
};
|
|
462
|
-
return {
|
|
463
|
-
...projection,
|
|
464
|
-
runs: projection.runs.filter(
|
|
465
|
-
(run) =>
|
|
466
|
-
includes(filters.provider, provider(run.provider)) &&
|
|
467
|
-
includes(filters.model, run.model) &&
|
|
468
|
-
includes(filters.agent, run.agent_name) &&
|
|
469
|
-
includes(filters.phase, run.phase) &&
|
|
470
|
-
includes(filters.status, run.task_status),
|
|
471
|
-
),
|
|
472
|
-
};
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
export async function readCurrentFxRate(filesystem: WorkspaceFileSystem) {
|
|
476
|
-
return new PricingRepository(filesystem).readLatestFxRate();
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
export function isUnfilteredCostRequest(context: Context): boolean {
|
|
480
|
-
return ["provider", "model", "agent", "phase", "status"].every(
|
|
481
|
-
(name) => (context.req.queries(name)?.length ?? 0) === 0,
|
|
482
|
-
);
|
|
483
|
-
}
|
|
438
|
+
export { filterCostRunRows, pageCostRunRows } from "./server-cost-run-search.js";
|
|
439
|
+
export { isUnfilteredCostRequest, readCurrentFxRate } from "./server-projection-loaders.js";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { WorkspaceFileSystem } from "@zaip-ai/core";
|
|
2
|
+
import { fxRateSchema, PricingRepository, pricingCatalogSchema } from "@zaip-ai/costs";
|
|
3
|
+
import type { Hono } from "hono";
|
|
4
|
+
import { API_PREFIX, headOrJson } from "./server-http-contracts.js";
|
|
5
|
+
|
|
6
|
+
/** Keep mutable pricing routes together behind the dashboard session middleware. */
|
|
7
|
+
export function registerPricingRoutes(
|
|
8
|
+
app: Hono,
|
|
9
|
+
filesystem: WorkspaceFileSystem,
|
|
10
|
+
clearSnapshotCache: () => void,
|
|
11
|
+
): void {
|
|
12
|
+
app.on(["GET", "HEAD"], `${API_PREFIX}/pricing`, async (context) =>
|
|
13
|
+
headOrJson(
|
|
14
|
+
context,
|
|
15
|
+
pricingCatalogSchema.parse(
|
|
16
|
+
JSON.parse(await filesystem.readText(".agents/config/pricing/models.json")),
|
|
17
|
+
),
|
|
18
|
+
),
|
|
19
|
+
);
|
|
20
|
+
app.put(`${API_PREFIX}/pricing`, async (context) => {
|
|
21
|
+
const catalog = pricingCatalogSchema.parse({
|
|
22
|
+
...(await context.req.json()),
|
|
23
|
+
updated_at: new Date().toISOString(),
|
|
24
|
+
});
|
|
25
|
+
await filesystem.writeTextAtomic(
|
|
26
|
+
".agents/config/pricing/models.json",
|
|
27
|
+
`${JSON.stringify(catalog, null, 2)}\n`,
|
|
28
|
+
);
|
|
29
|
+
clearSnapshotCache();
|
|
30
|
+
return context.json(catalog);
|
|
31
|
+
});
|
|
32
|
+
app.on(["GET", "HEAD"], `${API_PREFIX}/fx-rate`, async (context) => {
|
|
33
|
+
const rate = await new PricingRepository(filesystem).readLatestFxRate();
|
|
34
|
+
return headOrJson(context, rate);
|
|
35
|
+
});
|
|
36
|
+
app.put(`${API_PREFIX}/fx-rate`, async (context) => {
|
|
37
|
+
const input = fxRateSchema.pick({ rate: true }).parse(await context.req.json());
|
|
38
|
+
const rate = await new PricingRepository(filesystem).writeCurrentFxRate(input.rate);
|
|
39
|
+
clearSnapshotCache();
|
|
40
|
+
return context.json(rate);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { createDashboardApp, projectionPeriodFilter } from "./server.js";
|
|
4
|
+
|
|
5
|
+
const workspaceRoot = resolve(import.meta.dirname, "../../../../..");
|
|
6
|
+
describe("dashboard route safety and calendar filters", () => {
|
|
7
|
+
it("serves only allowlisted canonical agent portraits", async () => {
|
|
8
|
+
const app = createDashboardApp(workspaceRoot, "test-session");
|
|
9
|
+
const agentIds = [
|
|
10
|
+
"agent-orchestrator",
|
|
11
|
+
"agent-builder",
|
|
12
|
+
"agent-task-executor",
|
|
13
|
+
"agent-analyst",
|
|
14
|
+
"agent-finalizer",
|
|
15
|
+
"agent-reviewer",
|
|
16
|
+
"agent-integrator",
|
|
17
|
+
"agent-auditor",
|
|
18
|
+
"agent-text-summarizer",
|
|
19
|
+
"agent-data-analyst",
|
|
20
|
+
"agent-refactoring-specialist",
|
|
21
|
+
"agent-frontend",
|
|
22
|
+
"agent-backend",
|
|
23
|
+
"agent-website-developer",
|
|
24
|
+
"agent-designer",
|
|
25
|
+
"agent-cloud-engineer",
|
|
26
|
+
"agent-security-auditor",
|
|
27
|
+
"agent-marketing-analyst",
|
|
28
|
+
];
|
|
29
|
+
for (const agentId of agentIds) {
|
|
30
|
+
const portrait = await app.request(
|
|
31
|
+
`/api/v2/agent-catalog-assets/${agentId}?token=test-session`,
|
|
32
|
+
);
|
|
33
|
+
expect(portrait.status, agentId).toBe(200);
|
|
34
|
+
expect(portrait.headers.get("content-type"), agentId).toBe("image/png");
|
|
35
|
+
expect((await portrait.arrayBuffer()).byteLength, agentId).toBeGreaterThan(1_000);
|
|
36
|
+
}
|
|
37
|
+
const traversal = await app.request(
|
|
38
|
+
"/api/v2/agent-catalog-assets/%2E%2E%2Fproject?token=test-session",
|
|
39
|
+
);
|
|
40
|
+
expect(traversal.status).toBe(404);
|
|
41
|
+
});
|
|
42
|
+
it("treats date-only dashboard filters as whole calendar days", () => {
|
|
43
|
+
const context = {
|
|
44
|
+
req: { query: (name: string) => (name === "from" ? "2026-07-10" : "2026-07-26") },
|
|
45
|
+
};
|
|
46
|
+
expect(projectionPeriodFilter(context as never)).toEqual({
|
|
47
|
+
from: "2026-07-10T03:00:00.000Z",
|
|
48
|
+
to: "2026-07-27T02:59:59.999Z",
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
it("uses the IANA Sao Paulo offset for historical date-only filters", () => {
|
|
52
|
+
const context = {
|
|
53
|
+
req: { query: (name: string) => (name === "from" ? "2018-02-16" : "2018-02-16") },
|
|
54
|
+
};
|
|
55
|
+
expect(projectionPeriodFilter(context as never)).toEqual({
|
|
56
|
+
from: "2018-02-16T02:00:00.000Z",
|
|
57
|
+
to: "2018-02-17T01:59:59.999Z",
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
});
|
package/templates/.agents/dashboard/packages/local-runtime/src/server-run-projections.test.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { filterCompactCostRuns } from "./server-cost-run-search.js";
|
|
2
3
|
import { compactRunProjection, runTimestampInRange } from "./server-projections.js";
|
|
3
4
|
|
|
4
5
|
describe("dashboard run projections", () => {
|
|
@@ -8,7 +9,7 @@ describe("dashboard run projections", () => {
|
|
|
8
9
|
});
|
|
9
10
|
|
|
10
11
|
it("keeps a lifecycle Run with no InvocationSummary as a null-telemetry page row", () => {
|
|
11
|
-
const
|
|
12
|
+
const metric = {
|
|
12
13
|
id: "RUN-NO-SUMMARY",
|
|
13
14
|
started_at: "2026-08-07T02:59:59.999Z",
|
|
14
15
|
finished_at: null,
|
|
@@ -30,7 +31,16 @@ describe("dashboard run projections", () => {
|
|
|
30
31
|
cost: null,
|
|
31
32
|
currency: null,
|
|
32
33
|
events: null,
|
|
33
|
-
}
|
|
34
|
+
};
|
|
35
|
+
const run = compactRunProjection(metric);
|
|
36
|
+
const older = { ...metric, id: "RUN-OLDER", task_id: "TASK-OLDER" };
|
|
37
|
+
expect(
|
|
38
|
+
filterCompactCostRuns([metric, older], "older title", [
|
|
39
|
+
{ id: "TASK-OLDER", title: "Older title" },
|
|
40
|
+
]),
|
|
41
|
+
).toEqual([older]);
|
|
42
|
+
expect(filterCompactCostRuns([metric, older], "no-summary", [])).toEqual([metric]);
|
|
43
|
+
expect(filterCompactCostRuns([metric, older], "missing", [])).toEqual([]);
|
|
34
44
|
expect(run).toMatchObject({
|
|
35
45
|
id: "RUN-NO-SUMMARY",
|
|
36
46
|
duration_ms: null,
|
package/templates/.agents/dashboard/packages/local-runtime/src/server-telemetry-command.test.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { describe, expect, it, vi } from "vitest";
|
|
4
|
+
import type { ActiveCodexTelemetryCapture } from "./active-codex-telemetry.js";
|
|
4
5
|
import { createDashboardApp } from "./server.js";
|
|
5
6
|
import { withSqliteReadModelFixture } from "./server.test-fixtures.js";
|
|
6
7
|
|
|
@@ -84,12 +85,13 @@ describe("recent telemetry command readiness", () => {
|
|
|
84
85
|
it("cannot advertise current after a failed capture even when canonical projection succeeds", async () => {
|
|
85
86
|
await withSqliteReadModelFixture("telemetry-failed", async ({ root, filesystem }) => {
|
|
86
87
|
const commit = vi.fn(async () => undefined);
|
|
88
|
+
const capture = vi.fn(async (): Promise<ActiveCodexTelemetryCapture> => {
|
|
89
|
+
throw Object.assign(new Error("private command output"), { code: "ETIMEDOUT" });
|
|
90
|
+
});
|
|
87
91
|
const app = createDashboardApp(root, "session", undefined, {
|
|
88
92
|
filesystem,
|
|
89
93
|
refreshReadModel: commit,
|
|
90
|
-
captureTelemetry:
|
|
91
|
-
throw new Error("transcript timed out");
|
|
92
|
-
},
|
|
94
|
+
captureTelemetry: capture,
|
|
93
95
|
});
|
|
94
96
|
const headers = { "x-zaip-session": "session" };
|
|
95
97
|
await app.request("/api/v2/capture-telemetry", { method: "POST", headers });
|
|
@@ -98,7 +100,12 @@ describe("recent telemetry command readiness", () => {
|
|
|
98
100
|
expect(await ready.json()).toMatchObject({
|
|
99
101
|
status: "degraded",
|
|
100
102
|
snapshot_state: "updating",
|
|
101
|
-
active_capture_failure: {
|
|
103
|
+
active_capture_failure: {
|
|
104
|
+
code: "active_telemetry_capture_failed",
|
|
105
|
+
failure_kind: "timeout",
|
|
106
|
+
process_code: "ETIMEDOUT",
|
|
107
|
+
exit_code: null,
|
|
108
|
+
},
|
|
102
109
|
});
|
|
103
110
|
const manual = await app.request("/api/v2/refresh-projection", { method: "POST", headers });
|
|
104
111
|
expect(manual.status).toBe(503);
|
|
@@ -106,6 +113,24 @@ describe("recent telemetry command readiness", () => {
|
|
|
106
113
|
error: "Telemetry capture is unavailable",
|
|
107
114
|
snapshot_state: "updating",
|
|
108
115
|
});
|
|
116
|
+
|
|
117
|
+
capture.mockImplementation(async () => ({
|
|
118
|
+
captured_runs: 0,
|
|
119
|
+
captured_segments: 0,
|
|
120
|
+
manifest_state: "initialized" as const,
|
|
121
|
+
skipped_reason: "no_new_segments" as const,
|
|
122
|
+
catch_up_complete: true,
|
|
123
|
+
pending_runs: 0,
|
|
124
|
+
last_capture_at: new Date().toISOString(),
|
|
125
|
+
}));
|
|
126
|
+
await app.request("/api/v2/capture-telemetry", { method: "POST", headers });
|
|
127
|
+
await vi.waitFor(async () => {
|
|
128
|
+
const response = await app.request("/api/v2/ready", { headers });
|
|
129
|
+
expect(await response.json()).toMatchObject({
|
|
130
|
+
active_capture_failure: null,
|
|
131
|
+
active_capture: { catch_up_complete: true, pending_runs: 0 },
|
|
132
|
+
});
|
|
133
|
+
});
|
|
109
134
|
});
|
|
110
135
|
});
|
|
111
136
|
});
|
|
@@ -8,6 +8,7 @@ export function createTelemetryProjectionRefresh(input: {
|
|
|
8
8
|
onCaptureFailure?: (error: unknown) => void;
|
|
9
9
|
}) {
|
|
10
10
|
let inFlight: Promise<void> | null = null;
|
|
11
|
+
let capturing = false;
|
|
11
12
|
let projecting = false;
|
|
12
13
|
const refresh = (catchUp = false): Promise<void> => {
|
|
13
14
|
if (inFlight !== null) {
|
|
@@ -19,11 +20,14 @@ export function createTelemetryProjectionRefresh(input: {
|
|
|
19
20
|
inFlight = (async () => {
|
|
20
21
|
try {
|
|
21
22
|
if (input.capture !== undefined) {
|
|
23
|
+
capturing = true;
|
|
22
24
|
const result = await input.capture();
|
|
23
25
|
input.onCaptureSuccess?.(result);
|
|
24
26
|
}
|
|
25
27
|
} catch (error) {
|
|
26
28
|
input.onCaptureFailure?.(error);
|
|
29
|
+
} finally {
|
|
30
|
+
capturing = false;
|
|
27
31
|
}
|
|
28
32
|
// Keep canonical records readable even when telemetry is unavailable.
|
|
29
33
|
// The caller retains the capture failure separately from SQLite health.
|
|
@@ -40,5 +44,8 @@ export function createTelemetryProjectionRefresh(input: {
|
|
|
40
44
|
get pending() {
|
|
41
45
|
return inFlight !== null;
|
|
42
46
|
},
|
|
47
|
+
get capturing() {
|
|
48
|
+
return capturing;
|
|
49
|
+
},
|
|
43
50
|
};
|
|
44
51
|
}
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
createDashboardApp,
|
|
8
8
|
dashboardWorkspaceKey,
|
|
9
9
|
manifestRun,
|
|
10
|
-
projectionPeriodFilter,
|
|
11
10
|
SqliteReadModelFileSystem,
|
|
12
11
|
} from "./server.js";
|
|
13
12
|
import { invocationSummary, withSqliteReadModelFixture } from "./server.test-fixtures.js";
|
|
@@ -564,6 +563,57 @@ describe("dashboard backend", () => {
|
|
|
564
563
|
rows: expect.any(Array),
|
|
565
564
|
});
|
|
566
565
|
});
|
|
566
|
+
it("serves Operations Run pages from the canonical summary when SQLite omits that file", async () => {
|
|
567
|
+
await withSqliteReadModelFixture(
|
|
568
|
+
"operations-canonical-summary",
|
|
569
|
+
async ({ root, filesystem }) => {
|
|
570
|
+
const summaryPath = join(root, ".agents/records/lifecycle/summary.json");
|
|
571
|
+
await mkdir(join(root, ".agents/records/lifecycle"), { recursive: true });
|
|
572
|
+
await writeFile(
|
|
573
|
+
summaryPath,
|
|
574
|
+
JSON.stringify({
|
|
575
|
+
schema_version: 1,
|
|
576
|
+
run_metrics: [
|
|
577
|
+
{
|
|
578
|
+
id: "RUN-CANONICAL",
|
|
579
|
+
started_at: "2026-10-06T03:00:00.000Z",
|
|
580
|
+
finished_at: null,
|
|
581
|
+
phase: "execution",
|
|
582
|
+
status: "completed",
|
|
583
|
+
parent_run_id: null,
|
|
584
|
+
total_tokens: null,
|
|
585
|
+
cost: null,
|
|
586
|
+
},
|
|
587
|
+
],
|
|
588
|
+
}),
|
|
589
|
+
);
|
|
590
|
+
await expect(
|
|
591
|
+
filesystem.readText(".agents/records/lifecycle/summary.json"),
|
|
592
|
+
).rejects.toMatchObject({ code: "ENOENT" });
|
|
593
|
+
const headers = { "x-zaip-session": "test-session" };
|
|
594
|
+
const app = createDashboardApp(root, "test-session", undefined, { filesystem });
|
|
595
|
+
const response = await app.request("/api/v2/operations/runs?kind=runs&offset=0&limit=10", {
|
|
596
|
+
headers,
|
|
597
|
+
});
|
|
598
|
+
expect(response.status).toBe(200);
|
|
599
|
+
await expect(response.json()).resolves.toMatchObject({
|
|
600
|
+
data_source: "lifecycle_manifest_summary",
|
|
601
|
+
rows: [
|
|
602
|
+
{ id: "RUN-CANONICAL", total_tokens: null, started_at: "2026-10-06T03:00:00.000Z" },
|
|
603
|
+
],
|
|
604
|
+
});
|
|
605
|
+
await writeFile(summaryPath, "{invalid-json");
|
|
606
|
+
const invalid = createDashboardApp(root, "test-session", undefined, { filesystem });
|
|
607
|
+
const rejected = await invalid.request(
|
|
608
|
+
"/api/v2/operations/runs?kind=runs&offset=0&limit=10",
|
|
609
|
+
{
|
|
610
|
+
headers,
|
|
611
|
+
},
|
|
612
|
+
);
|
|
613
|
+
expect(rejected.status).toBe(500);
|
|
614
|
+
},
|
|
615
|
+
);
|
|
616
|
+
});
|
|
567
617
|
it("serves a Cost Run transport page without materializing aggregate totals", async () => {
|
|
568
618
|
const app = createDashboardApp(workspaceRoot, "test-session");
|
|
569
619
|
const headers = { "x-zaip-session": "test-session" };
|
|
@@ -602,57 +652,4 @@ describe("dashboard backend", () => {
|
|
|
602
652
|
expect(page.runs.length).toBeLessThanOrEqual(1);
|
|
603
653
|
expect(page.runs.every((run) => run.phase === "execution")).toBe(true);
|
|
604
654
|
});
|
|
605
|
-
it("serves only allowlisted canonical agent portraits", async () => {
|
|
606
|
-
const app = createDashboardApp(workspaceRoot, "test-session");
|
|
607
|
-
const agentIds = [
|
|
608
|
-
"agent-orchestrator",
|
|
609
|
-
"agent-builder",
|
|
610
|
-
"agent-task-executor",
|
|
611
|
-
"agent-analyst",
|
|
612
|
-
"agent-finalizer",
|
|
613
|
-
"agent-reviewer",
|
|
614
|
-
"agent-integrator",
|
|
615
|
-
"agent-auditor",
|
|
616
|
-
"agent-text-summarizer",
|
|
617
|
-
"agent-data-analyst",
|
|
618
|
-
"agent-refactoring-specialist",
|
|
619
|
-
"agent-frontend",
|
|
620
|
-
"agent-backend",
|
|
621
|
-
"agent-website-developer",
|
|
622
|
-
"agent-designer",
|
|
623
|
-
"agent-cloud-engineer",
|
|
624
|
-
"agent-security-auditor",
|
|
625
|
-
"agent-marketing-analyst",
|
|
626
|
-
];
|
|
627
|
-
for (const agentId of agentIds) {
|
|
628
|
-
const portrait = await app.request(
|
|
629
|
-
`/api/v2/agent-catalog-assets/${agentId}?token=test-session`,
|
|
630
|
-
);
|
|
631
|
-
expect(portrait.status, agentId).toBe(200);
|
|
632
|
-
expect(portrait.headers.get("content-type"), agentId).toBe("image/png");
|
|
633
|
-
expect((await portrait.arrayBuffer()).byteLength, agentId).toBeGreaterThan(1_000);
|
|
634
|
-
}
|
|
635
|
-
const traversal = await app.request(
|
|
636
|
-
"/api/v2/agent-catalog-assets/%2E%2E%2Fproject?token=test-session",
|
|
637
|
-
);
|
|
638
|
-
expect(traversal.status).toBe(404);
|
|
639
|
-
});
|
|
640
|
-
it("treats date-only dashboard filters as whole calendar days", () => {
|
|
641
|
-
const context = {
|
|
642
|
-
req: { query: (name: string) => (name === "from" ? "2026-07-10" : "2026-07-26") },
|
|
643
|
-
};
|
|
644
|
-
expect(projectionPeriodFilter(context as never)).toEqual({
|
|
645
|
-
from: "2026-07-10T03:00:00.000Z",
|
|
646
|
-
to: "2026-07-27T02:59:59.999Z",
|
|
647
|
-
});
|
|
648
|
-
});
|
|
649
|
-
it("uses the IANA Sao Paulo offset for historical date-only filters", () => {
|
|
650
|
-
const context = {
|
|
651
|
-
req: { query: (name: string) => (name === "from" ? "2018-02-16" : "2018-02-16") },
|
|
652
|
-
};
|
|
653
|
-
expect(projectionPeriodFilter(context as never)).toEqual({
|
|
654
|
-
from: "2018-02-16T02:00:00.000Z",
|
|
655
|
-
to: "2018-02-17T01:59:59.999Z",
|
|
656
|
-
});
|
|
657
|
-
});
|
|
658
655
|
});
|
|
@@ -3,10 +3,10 @@ import { basename, resolve } from "node:path";
|
|
|
3
3
|
import { serve } from "@hono/node-server";
|
|
4
4
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
5
5
|
import { WorkspaceFileSystem, type WorkspaceLayout } from "@zaip-ai/core";
|
|
6
|
-
import { fxRateSchema, PricingRepository, pricingCatalogSchema } from "@zaip-ai/costs";
|
|
7
6
|
import { type Context, Hono } from "hono";
|
|
8
7
|
import { getCookie, setCookie } from "hono/cookie";
|
|
9
8
|
import { createDashboardDiagnostics } from "../dashboard-diagnostics.mjs";
|
|
9
|
+
import { activeCaptureFailure as describeActiveCaptureFailure } from "./active-capture-failure.js";
|
|
10
10
|
import {
|
|
11
11
|
type ActiveCodexTelemetryCapture,
|
|
12
12
|
captureRepositoryActiveCodexTelemetry,
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
registerReconciliationRoute,
|
|
38
38
|
} from "./server-reconciliation-route.js";
|
|
39
39
|
import { isAllowedDashboardAuthority } from "./server-request-authority.js";
|
|
40
|
+
import { registerPricingRoutes } from "./server-route-handlers.js";
|
|
40
41
|
import { refreshSqliteSnapshotInWorker } from "./server-snapshot-refresh.js";
|
|
41
42
|
import { readCommittedSnapshotRevision } from "./server-snapshot-revision.js";
|
|
42
43
|
import { startSnapshotSynchronization } from "./server-snapshot-synchronization.js";
|
|
@@ -188,14 +189,17 @@ export function createDashboardApp(
|
|
|
188
189
|
};
|
|
189
190
|
let observedSnapshotRevision: string | null = null;
|
|
190
191
|
const projectionRefreshDiagnostics = createProjectionRefreshDiagnostics();
|
|
191
|
-
let activeCaptureFailure:
|
|
192
|
+
let activeCaptureFailure: ReturnType<typeof describeActiveCaptureFailure> | null = null;
|
|
192
193
|
let activeCapture: ActiveCodexTelemetryCapture | null = null;
|
|
193
194
|
let activeCapturePending = false;
|
|
194
195
|
let projectionRefreshInFlight: Promise<void> | null = null;
|
|
195
196
|
let projectionRefreshRequested = false;
|
|
196
197
|
const associatedRefreshJobs = new Set<string>();
|
|
197
|
-
const rebuildProjection = async (associatedJobId: string | null = null) => {
|
|
198
|
+
const rebuildProjection = async (associatedJobId: string | null = null): Promise<void> => {
|
|
198
199
|
if (associatedJobId !== null) associatedRefreshJobs.add(associatedJobId);
|
|
200
|
+
// Capture writes canonical records. Watcher events from those writes must
|
|
201
|
+
// join the ordered pass, not launch a competing SQLite rebuild.
|
|
202
|
+
if (telemetryRefresh.capturing) return telemetryRefresh.refresh();
|
|
199
203
|
if (projectionRefreshInFlight !== null) {
|
|
200
204
|
projectionRefreshRequested = true;
|
|
201
205
|
return projectionRefreshInFlight;
|
|
@@ -259,26 +263,34 @@ export function createDashboardApp(
|
|
|
259
263
|
});
|
|
260
264
|
return projectionRefreshInFlight;
|
|
261
265
|
};
|
|
266
|
+
const captureTelemetry =
|
|
267
|
+
options.captureTelemetry ??
|
|
268
|
+
(isSqliteSnapshot &&
|
|
269
|
+
options.watchSources === true &&
|
|
270
|
+
process.env.ZAIP_DASHBOARD_WATCH_SOURCES !== "0"
|
|
271
|
+
? () => captureRepositoryActiveCodexTelemetry(workspaceRoot)
|
|
272
|
+
: undefined);
|
|
262
273
|
const telemetryRefresh = createTelemetryProjectionRefresh({
|
|
263
274
|
refresh: rebuildProjection,
|
|
264
|
-
...(
|
|
265
|
-
? {
|
|
266
|
-
:
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
275
|
+
...(captureTelemetry === undefined
|
|
276
|
+
? {}
|
|
277
|
+
: {
|
|
278
|
+
capture: async () => {
|
|
279
|
+
// A source-only projection can precede the periodic capture. Drain it
|
|
280
|
+
// while the capture reservation prevents new watcher projections.
|
|
281
|
+
if (projectionRefreshInFlight !== null)
|
|
282
|
+
await projectionRefreshInFlight.catch(() => undefined);
|
|
283
|
+
return captureTelemetry();
|
|
284
|
+
},
|
|
285
|
+
}),
|
|
271
286
|
onCaptureSuccess: (result) => {
|
|
272
287
|
activeCapture = result;
|
|
273
288
|
activeCaptureFailure = null;
|
|
274
289
|
activeCapturePending = result.catch_up_complete === false;
|
|
275
290
|
},
|
|
276
291
|
onCaptureFailure: (error) => {
|
|
277
|
-
activeCaptureFailure =
|
|
278
|
-
|
|
279
|
-
occurred_at: new Date().toISOString(),
|
|
280
|
-
cause: error instanceof Error ? error.name : "unknown_error",
|
|
281
|
-
};
|
|
292
|
+
activeCaptureFailure = describeActiveCaptureFailure(error);
|
|
293
|
+
diagnostics.log("active_telemetry_capture_failed", activeCaptureFailure);
|
|
282
294
|
},
|
|
283
295
|
});
|
|
284
296
|
const visibleSnapshotState = () =>
|
|
@@ -612,36 +624,7 @@ export function createDashboardApp(
|
|
|
612
624
|
readAggregate,
|
|
613
625
|
clearSnapshotCache,
|
|
614
626
|
});
|
|
615
|
-
app
|
|
616
|
-
headOrJson(
|
|
617
|
-
context,
|
|
618
|
-
pricingCatalogSchema.parse(
|
|
619
|
-
JSON.parse(await filesystem.readText(".agents/config/pricing/models.json")),
|
|
620
|
-
),
|
|
621
|
-
),
|
|
622
|
-
);
|
|
623
|
-
app.put(`${API_PREFIX}/pricing`, async (context) => {
|
|
624
|
-
const catalog = pricingCatalogSchema.parse({
|
|
625
|
-
...(await context.req.json()),
|
|
626
|
-
updated_at: new Date().toISOString(),
|
|
627
|
-
});
|
|
628
|
-
await filesystem.writeTextAtomic(
|
|
629
|
-
".agents/config/pricing/models.json",
|
|
630
|
-
`${JSON.stringify(catalog, null, 2)}\n`,
|
|
631
|
-
);
|
|
632
|
-
clearSnapshotCache();
|
|
633
|
-
return context.json(catalog);
|
|
634
|
-
});
|
|
635
|
-
app.on(["GET", "HEAD"], `${API_PREFIX}/fx-rate`, async (context) => {
|
|
636
|
-
const rate = await new PricingRepository(filesystem).readLatestFxRate();
|
|
637
|
-
return headOrJson(context, rate);
|
|
638
|
-
});
|
|
639
|
-
app.put(`${API_PREFIX}/fx-rate`, async (context) => {
|
|
640
|
-
const input = fxRateSchema.pick({ rate: true }).parse(await context.req.json());
|
|
641
|
-
const rate = await new PricingRepository(filesystem).writeCurrentFxRate(input.rate);
|
|
642
|
-
clearSnapshotCache();
|
|
643
|
-
return context.json(rate);
|
|
644
|
-
});
|
|
627
|
+
registerPricingRoutes(app, filesystem, clearSnapshotCache);
|
|
645
628
|
registerAgentCatalogRoutes(app, {
|
|
646
629
|
filesystem,
|
|
647
630
|
workspaceRoot,
|
package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-filter-matrix.test.ts
CHANGED
|
@@ -60,6 +60,11 @@ it.each([
|
|
|
60
60
|
);
|
|
61
61
|
await rebuildSqliteReadModel(root);
|
|
62
62
|
fs = new SqliteReadModelFileSystem(root);
|
|
63
|
+
if (status === "completed") {
|
|
64
|
+
const fast = fs.viewPage("cost_monitor_agent_invocations", { query: "ainv-low", limit: 1 });
|
|
65
|
+
const sql = fs.viewPage("cost_monitor_agent_invocations", { query: "%AINV-LOW%", limit: 1 });
|
|
66
|
+
expect(fast).toEqual(sql);
|
|
67
|
+
}
|
|
63
68
|
const summary = fs.costMonitorSummaryAggregate({ from: null, to: null });
|
|
64
69
|
// Summary first paint remains aggregate-only, but its controls retain
|
|
65
70
|
// factual filter choices from the same SQLite population.
|