@zaip-ai/cli 4.9.0 → 5.1.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 +58 -7
- package/templates/.agents/README.md +46 -10
- package/templates/.agents/config/agents/discovery.json +1 -1
- package/templates/.agents/dashboard/.zaip-managed.json +174 -86
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-BW7Vy1cp.js → CostCalculationGuidePage-uR-asJv_.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-Cp_hEVd8.js → DashboardHelpDialog-CngnpoLL.js} +87 -13
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-Prcs-ydy.js → PricingSettingsDialog-Bszw3ekd.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-Cu40wAFF.js → ProjectSettingsPage-IJ1znG4w.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-Bh3YBd3C.js → TraceabilityWorkspace-CsBpHqq_.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-B0Jwz_rk.js +68 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{dashboard-localization-CF7B1Hjn.js → dashboard-localization-D41g5rdo.js} +58 -7
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DOhy6D2-.js → icons-vendor-CjGGux6h.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-Bedlm0f7.js +2 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-CkPC-yMz.js → react-vendor-B9jZSAKf.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-BEdGoIDm.js → traceability-DFpwb0TN.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/index.html +5 -5
- package/templates/.agents/dashboard/apps/frontend/package.json +2 -2
- package/templates/.agents/dashboard/apps/frontend/src/AppDashboardContent.tsx +3 -0
- package/templates/.agents/dashboard/apps/frontend/src/AppOverlays.tsx +16 -7
- package/templates/.agents/dashboard/apps/frontend/src/__tests__/app-composition-support.ts +1 -0
- package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +1 -1
- package/templates/.agents/dashboard/apps/frontend/src/operations-period-render.test.ts +2 -0
- package/templates/.agents/dashboard/apps/frontend/src/use-app-trace-documents.navigation.test.tsx +125 -0
- package/templates/.agents/dashboard/apps/frontend/src/use-app-trace-documents.ts +67 -9
- package/templates/.agents/dashboard/apps/frontend/src/use-dashboard-app-model.tsx +15 -15
- package/templates/.agents/dashboard/apps/frontend/src/use-dashboard-locale.ts +18 -0
- 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/OperationsOverview.tsx +4 -39
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverviewTopPanel.navigation.test.tsx +150 -0
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsOverviewTopPanel.tsx +7 -3
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +14 -43
- package/templates/.agents/dashboard/modules/operations/src/react/OperationsPageOverview.tsx +3 -0
- package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-contract.ts +51 -0
- package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +12 -3
- package/templates/.agents/dashboard/modules/operations/src/react/operations-page-contract.ts +1 -0
- package/templates/.agents/dashboard/modules/operations/src/react/use-operations-default-range.ts +16 -0
- 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/dashboard-build-fingerprint.mjs +87 -0
- package/templates/.agents/dashboard/packages/local-runtime/dashboard-build-fingerprint.test.mjs +48 -0
- 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/scripts/build-frontend.mjs +29 -0
- package/templates/.agents/dashboard/packages/local-runtime/scripts/build-sqlite-runtime.mjs +5 -0
- 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/local-runtime/start-dashboard-build.mjs +12 -5
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-paths.mjs +8 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +1 -0
- package/templates/.agents/dashboard/packages/ui/package.json +1 -1
- package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
- package/templates/.agents/dashboard/tests/e2e/qualify-top-five-trace.mjs +53 -0
- package/templates/.agents/dashboard/tests/e2e/verify-top-five-served.mjs +78 -0
- package/templates/.agents/design/.zaip-managed.json +14 -6
- package/templates/.agents/design/apps/catalog/dist/assets/index-BGxXxXTo.js +72 -0
- package/templates/.agents/design/apps/catalog/dist/index.html +1 -1
- package/templates/.agents/design/apps/catalog/package.json +1 -1
- package/templates/.agents/design/apps/catalog/src/components/TopEntitiesSpecimen.tsx +45 -0
- package/templates/.agents/design/apps/catalog/src/pages/ComponentsPage.tsx +3 -1
- package/templates/.agents/design/apps/catalog/src/stories/Components.stories.tsx +2 -0
- 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-cli.test.mjs +53 -0
- 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 +26 -32
- 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/10-seguranca.md +2 -2
- package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +38 -0
- package/templates/template-manifest.json +259 -135
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-Co2Jiuzh.js +0 -68
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-qVSJnJ0_.js +0 -2
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { activeCaptureFailure } from "./active-capture-failure.js";
|
|
3
|
+
|
|
4
|
+
describe("public active capture diagnostics", () => {
|
|
5
|
+
it.each([
|
|
6
|
+
[{ code: "ETIMEDOUT" }, "timeout"],
|
|
7
|
+
[{ code: "ERR_CHILD_PROCESS_STDIO_MAXBUFFER" }, "output_limit"],
|
|
8
|
+
[{ killed: true, signal: "SIGTERM" }, "process_terminated"],
|
|
9
|
+
[{ code: 1 }, "process_exit"],
|
|
10
|
+
[{ code: "ENOENT" }, "spawn_failure"],
|
|
11
|
+
])("classifies process failures without guessing their cause", (fields, kind) => {
|
|
12
|
+
expect(activeCaptureFailure(Object.assign(new Error("private"), fields))).toMatchObject({
|
|
13
|
+
code: "active_telemetry_capture_failed",
|
|
14
|
+
failure_kind: kind,
|
|
15
|
+
exit_code: "code" in fields && typeof fields.code === "number" ? fields.code : null,
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("never exposes child output, command, paths or arbitrary error properties", () => {
|
|
20
|
+
const error = Object.assign(new Error("Bearer sensitive-value"), {
|
|
21
|
+
name: "sensitive-value",
|
|
22
|
+
stdout: "sensitive-value",
|
|
23
|
+
stderr: "sensitive-value",
|
|
24
|
+
cmd: "sensitive-value",
|
|
25
|
+
code: "sensitive-value",
|
|
26
|
+
signal: "sensitive-value",
|
|
27
|
+
});
|
|
28
|
+
const result = activeCaptureFailure(error);
|
|
29
|
+
expect(JSON.stringify(result)).not.toContain("sensitive-value");
|
|
30
|
+
expect(result).toMatchObject({
|
|
31
|
+
failure_kind: "capture_error",
|
|
32
|
+
process_code: null,
|
|
33
|
+
exit_code: null,
|
|
34
|
+
signal: null,
|
|
35
|
+
});
|
|
36
|
+
expect(activeCaptureFailure(null).cause).toBe("unknown_error");
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Public health diagnostics never include command lines, environment or process output. */
|
|
2
|
+
export function activeCaptureFailure(error: unknown) {
|
|
3
|
+
const failure = error instanceof Error ? error : null;
|
|
4
|
+
const fields = failure as (Error & { code?: unknown; signal?: unknown; killed?: unknown }) | null;
|
|
5
|
+
const code = fields?.code;
|
|
6
|
+
const signal = fields?.signal;
|
|
7
|
+
const exitCode = Number.isSafeInteger(code) ? (code as number) : null;
|
|
8
|
+
const processCode =
|
|
9
|
+
typeof code === "string" &&
|
|
10
|
+
["ENOENT", "EACCES", "EPERM", "ETIMEDOUT", "ERR_CHILD_PROCESS_STDIO_MAXBUFFER"].includes(code)
|
|
11
|
+
? code
|
|
12
|
+
: null;
|
|
13
|
+
const processSignal =
|
|
14
|
+
typeof signal === "string" && /^SIG[A-Z0-9]{1,12}$/.test(signal) ? signal : null;
|
|
15
|
+
return {
|
|
16
|
+
code: "active_telemetry_capture_failed",
|
|
17
|
+
occurred_at: new Date().toISOString(),
|
|
18
|
+
cause: failure === null ? "unknown_error" : "Error",
|
|
19
|
+
failure_kind:
|
|
20
|
+
processCode === "ETIMEDOUT"
|
|
21
|
+
? "timeout"
|
|
22
|
+
: processCode === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER"
|
|
23
|
+
? "output_limit"
|
|
24
|
+
: fields?.killed === true || processSignal !== null
|
|
25
|
+
? "process_terminated"
|
|
26
|
+
: exitCode !== null
|
|
27
|
+
? "process_exit"
|
|
28
|
+
: processCode !== null
|
|
29
|
+
? "spawn_failure"
|
|
30
|
+
: "capture_error",
|
|
31
|
+
process_code: processCode,
|
|
32
|
+
exit_code: exitCode,
|
|
33
|
+
signal: processSignal,
|
|
34
|
+
};
|
|
35
|
+
}
|
package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.test.ts
CHANGED
|
@@ -2,10 +2,11 @@ import { mkdir, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises"
|
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
-
|
|
5
|
+
import { activeCaptureFailure } from "./active-capture-failure.js";
|
|
6
6
|
import {
|
|
7
7
|
captureActiveCodexTelemetry,
|
|
8
8
|
captureRepositoryActiveCodexTelemetry,
|
|
9
|
+
ObservedCodexCaptureDeadlineError,
|
|
9
10
|
} from "./active-codex-telemetry.js";
|
|
10
11
|
import * as sources from "./sqlite-read-model-source-fingerprint.js";
|
|
11
12
|
|
|
@@ -29,11 +30,61 @@ describe("active Codex telemetry capture", () => {
|
|
|
29
30
|
"bounded pending progress",
|
|
30
31
|
);
|
|
31
32
|
expect(execute.mock.calls[0]?.[2]).toMatchObject({
|
|
32
|
-
timeout:
|
|
33
|
+
timeout: 125_000,
|
|
34
|
+
signal: expect.any(AbortSignal),
|
|
33
35
|
windowsHide: true,
|
|
34
36
|
cwd: root,
|
|
35
37
|
});
|
|
36
38
|
});
|
|
39
|
+
it("marks timeout only when its own child deadline aborts the project process", async () => {
|
|
40
|
+
const root = await runtimeFixture("");
|
|
41
|
+
const deadline = new AbortController();
|
|
42
|
+
const execute = vi.fn(
|
|
43
|
+
async (_file: string, _args: string[], options: { signal: AbortSignal }) =>
|
|
44
|
+
new Promise<never>((_resolve, reject) => {
|
|
45
|
+
options.signal.addEventListener(
|
|
46
|
+
"abort",
|
|
47
|
+
() =>
|
|
48
|
+
reject(
|
|
49
|
+
Object.assign(new Error("private child output"), {
|
|
50
|
+
name: "AbortError",
|
|
51
|
+
code: "ABORT_ERR",
|
|
52
|
+
}),
|
|
53
|
+
),
|
|
54
|
+
{ once: true },
|
|
55
|
+
);
|
|
56
|
+
deadline.abort();
|
|
57
|
+
}),
|
|
58
|
+
);
|
|
59
|
+
await expect(
|
|
60
|
+
captureActiveCodexTelemetry(root, root, execute as never, deadline.signal),
|
|
61
|
+
).rejects.toBeInstanceOf(ObservedCodexCaptureDeadlineError);
|
|
62
|
+
const failure = activeCaptureFailure(new ObservedCodexCaptureDeadlineError());
|
|
63
|
+
expect(failure).toMatchObject({ failure_kind: "timeout", process_code: "ETIMEDOUT" });
|
|
64
|
+
expect(JSON.stringify(failure)).not.toContain("private child output");
|
|
65
|
+
expect(
|
|
66
|
+
activeCaptureFailure(
|
|
67
|
+
Object.assign(new Error("external termination"), {
|
|
68
|
+
killed: true,
|
|
69
|
+
signal: "SIGTERM",
|
|
70
|
+
}),
|
|
71
|
+
).failure_kind,
|
|
72
|
+
).toBe("process_terminated");
|
|
73
|
+
const externalAbort = Object.assign(new Error("external cancellation"), {
|
|
74
|
+
name: "AbortError",
|
|
75
|
+
code: "ABORT_ERR",
|
|
76
|
+
});
|
|
77
|
+
await expect(
|
|
78
|
+
captureActiveCodexTelemetry(
|
|
79
|
+
root,
|
|
80
|
+
root,
|
|
81
|
+
(async () => {
|
|
82
|
+
throw externalAbort;
|
|
83
|
+
}) as never,
|
|
84
|
+
new AbortController().signal,
|
|
85
|
+
),
|
|
86
|
+
).rejects.toBe(externalAbort);
|
|
87
|
+
});
|
|
37
88
|
it("uses the primary CLI for registered worktrees while preserving each target workspace and cwd", async () => {
|
|
38
89
|
const primary = await runtimeFixture(`import {writeFileSync} from 'node:fs';
|
|
39
90
|
import {resolve} from 'node:path';
|
|
@@ -9,6 +9,14 @@ const execFileAsync = promisify(execFile);
|
|
|
9
9
|
// One automatic pass examines at most 32 Runs. Manual --drain remains separate.
|
|
10
10
|
export const RECENT_CODEX_CAPTURE_TIMEOUT_MS = 120_000;
|
|
11
11
|
|
|
12
|
+
export class ObservedCodexCaptureDeadlineError extends Error {
|
|
13
|
+
readonly code = "ETIMEDOUT";
|
|
14
|
+
readonly deadline_observed = true;
|
|
15
|
+
constructor() {
|
|
16
|
+
super("Recent Codex capture exceeded its observed process deadline");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
export type ActiveCodexTelemetryCapture = {
|
|
13
21
|
captured_runs: number;
|
|
14
22
|
catch_up_complete?: boolean;
|
|
@@ -37,6 +45,7 @@ export async function captureActiveCodexTelemetry(
|
|
|
37
45
|
workspaceRoot: string,
|
|
38
46
|
runtimeRoot = workspaceRoot,
|
|
39
47
|
execute = execFileAsync,
|
|
48
|
+
deadline = AbortSignal.timeout(RECENT_CODEX_CAPTURE_TIMEOUT_MS),
|
|
40
49
|
): Promise<ActiveCodexTelemetryCapture> {
|
|
41
50
|
// The CLI intentionally treats an absent active-work index as an empty pass.
|
|
42
51
|
// Validate the canonical manifest first so that exit 0 cannot mask missing lifecycle.
|
|
@@ -69,7 +78,9 @@ export async function captureActiveCodexTelemetry(
|
|
|
69
78
|
cwd: workspaceRoot,
|
|
70
79
|
encoding: "utf8",
|
|
71
80
|
windowsHide: true,
|
|
72
|
-
|
|
81
|
+
signal: deadline,
|
|
82
|
+
// A secondary child-process guard covers a broken abort delivery.
|
|
83
|
+
timeout: RECENT_CODEX_CAPTURE_TIMEOUT_MS + 5_000,
|
|
73
84
|
maxBuffer: 1_024 * 1_024,
|
|
74
85
|
},
|
|
75
86
|
);
|
|
@@ -95,6 +106,12 @@ export async function captureActiveCodexTelemetry(
|
|
|
95
106
|
last_capture_at: new Date().toISOString(),
|
|
96
107
|
};
|
|
97
108
|
} catch (error) {
|
|
109
|
+
if (
|
|
110
|
+
deadline.aborted &&
|
|
111
|
+
error instanceof Error &&
|
|
112
|
+
(error.name === "AbortError" || ("code" in error && error.code === "ABORT_ERR"))
|
|
113
|
+
)
|
|
114
|
+
throw new ObservedCodexCaptureDeadlineError();
|
|
98
115
|
if (isManifestUninitialized(error)) {
|
|
99
116
|
return {
|
|
100
117
|
captured_runs: 0,
|
|
@@ -160,7 +160,12 @@ export function manifestRun(
|
|
|
160
160
|
artifact_path: `.agents/records/runs/${summary.run_id}`,
|
|
161
161
|
status: summary.status ?? "completed",
|
|
162
162
|
phase: summary.phase ?? "execution",
|
|
163
|
-
entrypoint:
|
|
163
|
+
entrypoint:
|
|
164
|
+
summary.purpose === "observation"
|
|
165
|
+
? "observation"
|
|
166
|
+
: summary.task_id === null
|
|
167
|
+
? "conversation"
|
|
168
|
+
: "task",
|
|
164
169
|
work_type: "new",
|
|
165
170
|
run_role: delegated ? ("subagent" as const) : ("orchestrator" as const),
|
|
166
171
|
run_kind: delegated ? ("subrun" as const) : ("run" as const),
|
|
@@ -187,7 +192,7 @@ export function manifestRun(
|
|
|
187
192
|
terminal_call_count: summary.llm_call_count,
|
|
188
193
|
failed_call_count: summary.status === "failed" ? summary.llm_call_count : 0,
|
|
189
194
|
cancelled_call_count: summary.status === "cancelled" ? summary.llm_call_count : 0,
|
|
190
|
-
conversation_id: summary.agent_invocation_id,
|
|
195
|
+
conversation_id: summary.purpose === "observation" ? null : summary.agent_invocation_id,
|
|
191
196
|
execution_id: summary.execution_id,
|
|
192
197
|
task_id: summary.task_id,
|
|
193
198
|
spec_id: summary.spec_id,
|
|
@@ -226,6 +231,7 @@ function manifestRunPurpose(
|
|
|
226
231
|
delegated: boolean,
|
|
227
232
|
): RunProjection["run_purpose"] {
|
|
228
233
|
if (
|
|
234
|
+
value === "observation" ||
|
|
229
235
|
value === "intake" ||
|
|
230
236
|
value === "orchestration" ||
|
|
231
237
|
value === "execution" ||
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
vi.mock("./server-snapshot-synchronization.js", () => ({
|
|
4
|
+
startSnapshotSynchronization: vi.fn(),
|
|
5
|
+
}));
|
|
6
|
+
|
|
7
|
+
import { createDashboardApp } from "./server.js";
|
|
8
|
+
import { withSqliteReadModelFixture } from "./server.test-fixtures.js";
|
|
9
|
+
import { startSnapshotSynchronization } from "./server-snapshot-synchronization.js";
|
|
10
|
+
|
|
11
|
+
afterEach(() => vi.clearAllMocks());
|
|
12
|
+
|
|
13
|
+
describe("source changes during active telemetry", () => {
|
|
14
|
+
it.each([
|
|
15
|
+
false,
|
|
16
|
+
true,
|
|
17
|
+
])("waits for an earlier projection before capture (failure=%s)", async (fail) => {
|
|
18
|
+
await withSqliteReadModelFixture("projection-first", async ({ root, filesystem }) => {
|
|
19
|
+
let settle!: () => void;
|
|
20
|
+
const refresh = vi
|
|
21
|
+
.fn(async () => undefined)
|
|
22
|
+
.mockImplementationOnce(async () => {
|
|
23
|
+
await new Promise<void>((resolve) => {
|
|
24
|
+
settle = resolve;
|
|
25
|
+
});
|
|
26
|
+
if (fail) throw new Error("projection failed");
|
|
27
|
+
});
|
|
28
|
+
const capture = vi.fn(async () => ({
|
|
29
|
+
captured_runs: 0,
|
|
30
|
+
captured_segments: null,
|
|
31
|
+
manifest_state: "initialized" as const,
|
|
32
|
+
skipped_reason: null,
|
|
33
|
+
last_capture_at: new Date().toISOString(),
|
|
34
|
+
}));
|
|
35
|
+
const app = createDashboardApp(root, "session", undefined, {
|
|
36
|
+
filesystem,
|
|
37
|
+
refreshReadModel: refresh,
|
|
38
|
+
captureTelemetry: capture,
|
|
39
|
+
});
|
|
40
|
+
const synchronization = vi.mocked(startSnapshotSynchronization).mock.calls.at(-1)?.[0];
|
|
41
|
+
if (!synchronization?.synchronizeTelemetry) throw new Error("Synchronization was not wired");
|
|
42
|
+
const source = synchronization.refresh().catch(() => undefined);
|
|
43
|
+
const capturing = synchronization.synchronizeTelemetry();
|
|
44
|
+
const joined = synchronization.refresh();
|
|
45
|
+
try {
|
|
46
|
+
await Promise.resolve();
|
|
47
|
+
expect(capture).not.toHaveBeenCalled();
|
|
48
|
+
expect(refresh).toHaveBeenCalledOnce();
|
|
49
|
+
} finally {
|
|
50
|
+
settle();
|
|
51
|
+
await Promise.all([source, capturing, joined]);
|
|
52
|
+
}
|
|
53
|
+
expect(capture).toHaveBeenCalledOnce();
|
|
54
|
+
expect(refresh).toHaveBeenCalledTimes(2);
|
|
55
|
+
expect(await (await app.request("/api/v2/health")).json()).toEqual(
|
|
56
|
+
expect.objectContaining({
|
|
57
|
+
active_capture_failure: null,
|
|
58
|
+
projection_refresh_failure: null,
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
it.each([
|
|
64
|
+
false,
|
|
65
|
+
true,
|
|
66
|
+
])("defers source projection until capture settles (failure=%s)", async (fail) => {
|
|
67
|
+
await withSqliteReadModelFixture("capture-order", async ({ root, filesystem }) => {
|
|
68
|
+
let settle!: () => void;
|
|
69
|
+
const refresh = vi.fn(async () => undefined);
|
|
70
|
+
const capture = vi.fn(async () => {
|
|
71
|
+
await new Promise<void>((resolve) => {
|
|
72
|
+
settle = resolve;
|
|
73
|
+
});
|
|
74
|
+
if (fail) throw Object.assign(new Error("capture failed"), { code: 1 });
|
|
75
|
+
return {
|
|
76
|
+
captured_runs: 1,
|
|
77
|
+
captured_segments: null,
|
|
78
|
+
manifest_state: "initialized" as const,
|
|
79
|
+
skipped_reason: null,
|
|
80
|
+
last_capture_at: new Date().toISOString(),
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
const app = createDashboardApp(root, "session", undefined, {
|
|
84
|
+
filesystem,
|
|
85
|
+
refreshReadModel: refresh,
|
|
86
|
+
captureTelemetry: capture,
|
|
87
|
+
});
|
|
88
|
+
const synchronization = vi.mocked(startSnapshotSynchronization).mock.calls.at(-1)?.[0];
|
|
89
|
+
if (!synchronization?.synchronizeTelemetry) throw new Error("Synchronization was not wired");
|
|
90
|
+
const capturing = synchronization.synchronizeTelemetry();
|
|
91
|
+
const sourceChange = synchronization.refresh();
|
|
92
|
+
const secondChange = synchronization.refresh();
|
|
93
|
+
try {
|
|
94
|
+
await Promise.resolve();
|
|
95
|
+
expect(refresh).not.toHaveBeenCalled();
|
|
96
|
+
} finally {
|
|
97
|
+
settle();
|
|
98
|
+
await Promise.all([capturing, sourceChange, secondChange]);
|
|
99
|
+
}
|
|
100
|
+
expect(capture).toHaveBeenCalledOnce();
|
|
101
|
+
expect(refresh).toHaveBeenCalledOnce();
|
|
102
|
+
const health = await (await app.request("/api/v2/health")).json();
|
|
103
|
+
expect(health).toEqual(
|
|
104
|
+
expect.objectContaining({
|
|
105
|
+
active_capture_failure: fail
|
|
106
|
+
? expect.objectContaining({
|
|
107
|
+
failure_kind: "process_exit",
|
|
108
|
+
exit_code: 1,
|
|
109
|
+
})
|
|
110
|
+
: null,
|
|
111
|
+
}),
|
|
112
|
+
);
|
|
113
|
+
await synchronization.refresh();
|
|
114
|
+
expect(refresh).toHaveBeenCalledTimes(2);
|
|
115
|
+
expect(capture).toHaveBeenCalledOnce();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { WorkspaceFileSystem, type WorkspaceLayout } from "@zaip-ai/core";
|
|
5
|
+
import { LifecycleManifestStore } from "@zaip-ai/lifecycle-manifests";
|
|
6
|
+
import { Hono } from "hono";
|
|
7
|
+
import { expect, it } from "vitest";
|
|
8
|
+
import { buildManifestCostRunPage, type CompactRunMetric } from "./server-projections.js";
|
|
9
|
+
|
|
10
|
+
it("searches Task titles through the real compact projection before Run pagination", async () => {
|
|
11
|
+
const root = await mkdtemp(join(tmpdir(), "zaip-cost-title-"));
|
|
12
|
+
try {
|
|
13
|
+
const filesystem = new WorkspaceFileSystem(root);
|
|
14
|
+
await new LifecycleManifestStore(filesystem).replace([
|
|
15
|
+
{
|
|
16
|
+
schema_version: 1,
|
|
17
|
+
event_id: "task-title",
|
|
18
|
+
operation: "upsert",
|
|
19
|
+
occurred_at: "2026-08-09T12:00:00.000Z",
|
|
20
|
+
entity: {
|
|
21
|
+
kind: "task",
|
|
22
|
+
id: "TASK-1",
|
|
23
|
+
title: "Needle title",
|
|
24
|
+
status: "completed",
|
|
25
|
+
risk: "lite",
|
|
26
|
+
priority: null,
|
|
27
|
+
updated_at: "2026-08-09T12:00:00.000Z",
|
|
28
|
+
relations: [],
|
|
29
|
+
usage: null,
|
|
30
|
+
data: { created_at: "2026-08-09T12:00:00.000Z", finished_at: "2026-08-09T12:00:00.000Z" },
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
]);
|
|
34
|
+
const metric = (id: string, task_id: string | null, hour: number): CompactRunMetric => ({
|
|
35
|
+
id,
|
|
36
|
+
task_id,
|
|
37
|
+
started_at: `2026-08-09T${hour}:00:00.000Z`,
|
|
38
|
+
finished_at: null,
|
|
39
|
+
execution_id: null,
|
|
40
|
+
conversation_id: null,
|
|
41
|
+
agent: null,
|
|
42
|
+
agent_name: null,
|
|
43
|
+
provider: null,
|
|
44
|
+
model: null,
|
|
45
|
+
spec_id: null,
|
|
46
|
+
prd_id: null,
|
|
47
|
+
change_id: null,
|
|
48
|
+
phase: "execution",
|
|
49
|
+
parent_run_id: null,
|
|
50
|
+
status: "completed",
|
|
51
|
+
duration_ms: null,
|
|
52
|
+
total_tokens: null,
|
|
53
|
+
cost: null,
|
|
54
|
+
currency: null,
|
|
55
|
+
events: null,
|
|
56
|
+
});
|
|
57
|
+
const summaryPath = ".agents/records/lifecycle/summary.json";
|
|
58
|
+
const summary = JSON.parse(await filesystem.readText(summaryPath));
|
|
59
|
+
summary.run_metrics = [
|
|
60
|
+
metric("RUN-UNRELATED", null, 15),
|
|
61
|
+
metric("RUN-SECOND", "TASK-1", 14),
|
|
62
|
+
metric("RUN-FIRST", "TASK-1", 13),
|
|
63
|
+
];
|
|
64
|
+
await filesystem.writeTextAtomic(summaryPath, JSON.stringify(summary));
|
|
65
|
+
const app = new Hono();
|
|
66
|
+
app.get("/", async (context) =>
|
|
67
|
+
context.json(
|
|
68
|
+
await buildManifestCostRunPage(
|
|
69
|
+
filesystem,
|
|
70
|
+
{ version: 2 } as WorkspaceLayout,
|
|
71
|
+
context,
|
|
72
|
+
{ from: "2026-08-09", to: "2026-08-09" },
|
|
73
|
+
1,
|
|
74
|
+
1,
|
|
75
|
+
),
|
|
76
|
+
),
|
|
77
|
+
);
|
|
78
|
+
const response = await app.request("/?query=needle%20title¤cy=USD");
|
|
79
|
+
expect(response.status).toBe(200);
|
|
80
|
+
expect(await response.json()).toMatchObject({
|
|
81
|
+
page_only: true,
|
|
82
|
+
run_page: { total: 2, offset: 1, limit: 1, has_more: false },
|
|
83
|
+
runs: [
|
|
84
|
+
{ run_id: "RUN-FIRST", task_title: "Needle title", input_tokens: null, total_tokens: null },
|
|
85
|
+
],
|
|
86
|
+
});
|
|
87
|
+
} finally {
|
|
88
|
+
await rm(root, { recursive: true, force: true });
|
|
89
|
+
}
|
|
90
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { CostMonitorProjection } from "@zaip-ai/dashboard-cost-monitor/contracts";
|
|
2
|
+
import type { CompactRunMetric } from "./server-projections.js";
|
|
3
|
+
|
|
4
|
+
/** Match the complete compact population before either lifecycle or manifest pagination. */
|
|
5
|
+
export function filterCompactCostRuns(
|
|
6
|
+
runs: readonly CompactRunMetric[],
|
|
7
|
+
query: string,
|
|
8
|
+
tasks: readonly { id: string; title: string }[],
|
|
9
|
+
): CompactRunMetric[] {
|
|
10
|
+
const needle = query.trim().toLowerCase();
|
|
11
|
+
if (!needle) return [...runs];
|
|
12
|
+
const titles = new Map(tasks.map((task) => [task.id, task.title]));
|
|
13
|
+
return runs.filter((run) =>
|
|
14
|
+
[
|
|
15
|
+
run.id,
|
|
16
|
+
run.task_id,
|
|
17
|
+
run.task_id === null ? null : titles.get(run.task_id),
|
|
18
|
+
run.provider,
|
|
19
|
+
run.agent,
|
|
20
|
+
run.agent_name,
|
|
21
|
+
run.model,
|
|
22
|
+
run.phase,
|
|
23
|
+
run.spec_id,
|
|
24
|
+
run.prd_id,
|
|
25
|
+
run.execution_id,
|
|
26
|
+
run.conversation_id,
|
|
27
|
+
].some((value) => value?.toLowerCase().includes(needle)),
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Keeps Cost Run transport incremental without changing aggregate semantics. */
|
|
32
|
+
export function pageCostRunRows(
|
|
33
|
+
projection: CostMonitorProjection,
|
|
34
|
+
offset: number,
|
|
35
|
+
limit: number,
|
|
36
|
+
): CostMonitorProjection {
|
|
37
|
+
const safeOffset = Math.max(0, offset);
|
|
38
|
+
const safeLimit = Math.min(500, Math.max(1, limit));
|
|
39
|
+
return {
|
|
40
|
+
...projection,
|
|
41
|
+
runs: projection.runs.slice(safeOffset, safeOffset + safeLimit),
|
|
42
|
+
run_page: {
|
|
43
|
+
offset: safeOffset,
|
|
44
|
+
limit: safeLimit,
|
|
45
|
+
total: projection.runs.length,
|
|
46
|
+
has_more: safeOffset + safeLimit < projection.runs.length,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The Runs endpoint advertises these dimensions as server-side filters. Apply
|
|
53
|
+
* them before offset pagination so the reported total and every page have the
|
|
54
|
+
* same population. Aggregate fields intentionally retain their full filtered
|
|
55
|
+
* projection semantics and are not derived from the transport page.
|
|
56
|
+
*/
|
|
57
|
+
export function filterCostRunRows(
|
|
58
|
+
projection: CostMonitorProjection,
|
|
59
|
+
filters: {
|
|
60
|
+
provider: readonly string[];
|
|
61
|
+
model: readonly string[];
|
|
62
|
+
agent: readonly string[];
|
|
63
|
+
phase: readonly string[];
|
|
64
|
+
status: readonly string[];
|
|
65
|
+
},
|
|
66
|
+
): CostMonitorProjection {
|
|
67
|
+
const includes = (values: readonly string[], value: string) =>
|
|
68
|
+
values.length === 0 || values.includes(value);
|
|
69
|
+
const provider = (value: string | null) => {
|
|
70
|
+
const normalized = value?.trim().toLowerCase();
|
|
71
|
+
return !normalized || normalized === "unknown" || normalized === "codex"
|
|
72
|
+
? "openai"
|
|
73
|
+
: normalized;
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
...projection,
|
|
77
|
+
runs: projection.runs.filter(
|
|
78
|
+
(run) =>
|
|
79
|
+
includes(filters.provider, provider(run.provider)) &&
|
|
80
|
+
includes(filters.model, run.model) &&
|
|
81
|
+
includes(filters.agent, run.agent_name) &&
|
|
82
|
+
includes(filters.phase, run.phase) &&
|
|
83
|
+
includes(filters.status, run.task_status),
|
|
84
|
+
),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -205,7 +205,14 @@ export function registerOperationsRoutes(app: Hono, dependencies: DashboardRoute
|
|
|
205
205
|
);
|
|
206
206
|
}
|
|
207
207
|
});
|
|
208
|
-
|
|
208
|
+
// The compact Run page reads the canonical lifecycle summary, which is not
|
|
209
|
+
// materialized as a SQLite file. Keep SQL-backed Operations views separate.
|
|
210
|
+
registerOperationsRunsRoute(
|
|
211
|
+
app,
|
|
212
|
+
isSqliteSnapshot ? canonicalFilesystem : filesystem,
|
|
213
|
+
readAggregate,
|
|
214
|
+
headOrJson,
|
|
215
|
+
);
|
|
209
216
|
// Explicit SQL read-model API for the comparison dashboard. It never falls
|
|
210
217
|
// back to filesystem pagination, making first-fold/entity query plans
|
|
211
218
|
// inspectable while preserving the established Operations contracts.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { WorkspaceFileSystem } from "@zaip-ai/core";
|
|
2
|
+
import { PricingRepository } from "@zaip-ai/costs";
|
|
3
|
+
import type { Context } from "hono";
|
|
4
|
+
|
|
5
|
+
export async function readCurrentFxRate(filesystem: WorkspaceFileSystem) {
|
|
6
|
+
return new PricingRepository(filesystem).readLatestFxRate();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function isUnfilteredCostRequest(context: Context): boolean {
|
|
10
|
+
return ["provider", "model", "agent", "phase", "status"].every(
|
|
11
|
+
(name) => (context.req.queries(name)?.length ?? 0) === 0,
|
|
12
|
+
);
|
|
13
|
+
}
|