@zaip-ai/cli 4.8.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.
Files changed (195) hide show
  1. package/dist/services/adapter-hook-sources.d.ts +3 -0
  2. package/dist/services/adapter-hook-sources.d.ts.map +1 -1
  3. package/dist/services/adapter-hook-sources.js +3 -0
  4. package/dist/services/adapter-hook-sources.js.map +1 -1
  5. package/dist/services/adapters.d.ts +1 -1
  6. package/dist/services/adapters.d.ts.map +1 -1
  7. package/dist/services/adapters.js +5 -2
  8. package/dist/services/adapters.js.map +1 -1
  9. package/dist/services/adapters.test.js +1 -1
  10. package/dist/services/adapters.test.js.map +1 -1
  11. package/dist/services/codex-conversation-lifecycle-hook.d.ts.map +1 -1
  12. package/dist/services/codex-conversation-lifecycle-hook.js +16 -6
  13. package/dist/services/codex-conversation-lifecycle-hook.js.map +1 -1
  14. package/dist/services/codex-hook-activation.d.ts +1 -1
  15. package/dist/services/codex-hook-activation.d.ts.map +1 -1
  16. package/dist/services/codex-hook-activation.js +116 -10
  17. package/dist/services/codex-hook-activation.js.map +1 -1
  18. package/dist/services/codex-pre-tool-cli-capture-template.d.ts +2 -0
  19. package/dist/services/codex-pre-tool-cli-capture-template.d.ts.map +1 -0
  20. package/dist/services/codex-pre-tool-cli-capture-template.js +104 -0
  21. package/dist/services/codex-pre-tool-cli-capture-template.js.map +1 -0
  22. package/dist/services/codex-pre-tool-cloud-template.d.ts +2 -0
  23. package/dist/services/codex-pre-tool-cloud-template.d.ts.map +1 -0
  24. package/dist/services/codex-pre-tool-cloud-template.js +190 -0
  25. package/dist/services/codex-pre-tool-cloud-template.js.map +1 -0
  26. package/dist/services/codex-pre-tool-dependencies.test-support.d.ts +2 -0
  27. package/dist/services/codex-pre-tool-dependencies.test-support.d.ts.map +1 -0
  28. package/dist/services/codex-pre-tool-dependencies.test-support.js +9 -0
  29. package/dist/services/codex-pre-tool-dependencies.test-support.js.map +1 -0
  30. package/dist/services/codex-pre-tool-hook-fixture.test-support.d.ts +5 -0
  31. package/dist/services/codex-pre-tool-hook-fixture.test-support.d.ts.map +1 -0
  32. package/dist/services/codex-pre-tool-hook-fixture.test-support.js +19 -0
  33. package/dist/services/codex-pre-tool-hook-fixture.test-support.js.map +1 -0
  34. package/dist/services/codex-pre-tool-hook-template-bootstrap.test.d.ts +2 -0
  35. package/dist/services/codex-pre-tool-hook-template-bootstrap.test.d.ts.map +1 -0
  36. package/dist/services/codex-pre-tool-hook-template-bootstrap.test.js +107 -0
  37. package/dist/services/codex-pre-tool-hook-template-bootstrap.test.js.map +1 -0
  38. package/dist/services/codex-pre-tool-hook-template.d.ts.map +1 -1
  39. package/dist/services/codex-pre-tool-hook-template.js +152 -190
  40. package/dist/services/codex-pre-tool-hook-template.js.map +1 -1
  41. package/dist/services/codex-pre-tool-hook-template.test.js +63 -75
  42. package/dist/services/codex-pre-tool-hook-template.test.js.map +1 -1
  43. package/dist/services/codex-pre-tool-host-context-template.d.ts +2 -0
  44. package/dist/services/codex-pre-tool-host-context-template.d.ts.map +1 -0
  45. package/dist/services/codex-pre-tool-host-context-template.js +139 -0
  46. package/dist/services/codex-pre-tool-host-context-template.js.map +1 -0
  47. package/dist/services/codex-pre-tool-support-template.d.ts.map +1 -1
  48. package/dist/services/codex-pre-tool-support-template.js +77 -100
  49. package/dist/services/codex-pre-tool-support-template.js.map +1 -1
  50. package/dist/services/codex-subagent-hook-template.d.ts +1 -1
  51. package/dist/services/codex-subagent-hook-template.d.ts.map +1 -1
  52. package/dist/services/codex-subagent-hook-template.js +59 -15
  53. package/dist/services/codex-subagent-hook-template.js.map +1 -1
  54. package/dist/services/codex-validation-runtime-template.d.ts.map +1 -1
  55. package/dist/services/codex-validation-runtime-template.js +37 -10
  56. package/dist/services/codex-validation-runtime-template.js.map +1 -1
  57. package/dist/services/runtime-template-test-support.d.ts +1 -0
  58. package/dist/services/runtime-template-test-support.d.ts.map +1 -1
  59. package/dist/services/runtime-template-test-support.js +23 -2
  60. package/dist/services/runtime-template-test-support.js.map +1 -1
  61. package/dist/services/runtime-template.test.js +7 -24
  62. package/dist/services/runtime-template.test.js.map +1 -1
  63. package/package.json +1 -1
  64. package/templates/.agents/README-ENG.md +49 -7
  65. package/templates/.agents/README.md +41 -10
  66. package/templates/.agents/config/agents/discovery.json +1 -1
  67. package/templates/.agents/dashboard/.zaip-managed.json +125 -73
  68. package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-BW7Vy1cp.js → CostCalculationGuidePage-w6pjt4OM.js} +1 -1
  69. package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-BN8KQPrr.js → DashboardHelpDialog-DhlntQcR.js} +117 -17
  70. package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-Prcs-ydy.js → PricingSettingsDialog-BHoZT2an.js} +1 -1
  71. package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-HqGUImIG.js → ProjectSettingsPage-IcncIZ9Y.js} +1 -1
  72. package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-CNpmQMO4.js → TraceabilityWorkspace-C6rORjpr.js} +1 -1
  73. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-uPnOBShc.js +68 -0
  74. package/templates/.agents/dashboard/apps/frontend/dist/assets/{dashboard-localization-CF7B1Hjn.js → dashboard-localization-DWXjEIgc.js} +49 -7
  75. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DOhy6D2-.js → icons-vendor-Hc9fp_gs.js} +1 -1
  76. package/templates/.agents/dashboard/apps/frontend/dist/assets/{index-DX65Ufuu.js → index-BqeOEZW5.js} +2 -2
  77. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-CkPC-yMz.js → react-vendor-C9ta_O10.js} +1 -1
  78. package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-BEdGoIDm.js → traceability-IcefoTOv.js} +1 -1
  79. package/templates/.agents/dashboard/apps/frontend/dist/index.html +5 -5
  80. package/templates/.agents/dashboard/apps/frontend/package.json +1 -1
  81. package/templates/.agents/dashboard/modules/cost-monitor/package.json +1 -1
  82. package/templates/.agents/dashboard/modules/cost-monitor/src/contracts.ts +10 -3
  83. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.run-page.test.ts +5 -1
  84. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostGroupsTable.period-values.test.tsx +2 -2
  85. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables-keeps-legacy-codex-identity-fields-blank.test.ts +5 -5
  86. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +36 -43
  87. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-loaders.ts +1 -0
  88. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-table-formatters.ts +6 -4
  89. package/templates/.agents/dashboard/modules/cost-monitor/src/server/aggregate.ts +2 -2
  90. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.ts +1 -1
  91. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +11 -25
  92. package/templates/.agents/dashboard/modules/cost-monitor/src/server/cost-summary-defaults.ts +26 -0
  93. package/templates/.agents/dashboard/modules/operations/package.json +1 -1
  94. package/templates/.agents/dashboard/modules/operations/src/contracts.ts +11 -16
  95. package/templates/.agents/dashboard/modules/operations/src/planning-contracts.ts +14 -0
  96. package/templates/.agents/dashboard/modules/operations/src/react/DecisionTable.tsx +65 -0
  97. package/templates/.agents/dashboard/modules/operations/src/react/EntityTables.tsx +5 -71
  98. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +10 -36
  99. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +7 -2
  100. package/templates/.agents/dashboard/modules/operations/src/react/use-operations-filter-sync.ts +66 -0
  101. package/templates/.agents/dashboard/modules/traceability/package.json +1 -1
  102. package/templates/.agents/dashboard/packages/branding/package.json +1 -1
  103. package/templates/.agents/dashboard/packages/contracts/package.json +1 -1
  104. package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
  105. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +229 -222
  106. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +167 -167
  107. package/templates/.agents/dashboard/packages/local-runtime/package.json +3 -3
  108. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +229 -222
  109. package/templates/.agents/dashboard/packages/local-runtime/src/active-capture-failure.test.ts +38 -0
  110. package/templates/.agents/dashboard/packages/local-runtime/src/active-capture-failure.ts +35 -0
  111. package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.test.ts +69 -6
  112. package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.ts +62 -15
  113. package/templates/.agents/dashboard/packages/local-runtime/src/manifest-runs.ts +8 -2
  114. package/templates/.agents/dashboard/packages/local-runtime/src/server-capture-projection-order.test.ts +118 -0
  115. package/templates/.agents/dashboard/packages/local-runtime/src/server-cost-run-search.test.ts +90 -0
  116. package/templates/.agents/dashboard/packages/local-runtime/src/server-cost-run-search.ts +86 -0
  117. package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-routes.ts +8 -1
  118. package/templates/.agents/dashboard/packages/local-runtime/src/server-projection-loaders.ts +13 -0
  119. package/templates/.agents/dashboard/packages/local-runtime/src/server-projections.ts +30 -74
  120. package/templates/.agents/dashboard/packages/local-runtime/src/server-route-handlers.ts +42 -0
  121. package/templates/.agents/dashboard/packages/local-runtime/src/server-routes.test.ts +60 -0
  122. package/templates/.agents/dashboard/packages/local-runtime/src/server-run-projections.test.ts +12 -2
  123. package/templates/.agents/dashboard/packages/local-runtime/src/server-telemetry-command.test.ts +29 -4
  124. package/templates/.agents/dashboard/packages/local-runtime/src/server-telemetry-refresh.ts +7 -0
  125. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +51 -54
  126. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +37 -48
  127. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-filter-matrix.test.ts +5 -0
  128. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.test.ts +49 -0
  129. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.ts +44 -1
  130. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-cached-cost-page.ts +11 -10
  131. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-summary.ts +1 -1
  132. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-work-item-index.test.ts +18 -1
  133. package/templates/.agents/dashboard/packages/local-runtime/vitest.config.ts +12 -0
  134. package/templates/.agents/dashboard/packages/ui/package.json +1 -1
  135. package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
  136. package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-pagination.spec.ts +13 -6
  137. package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +1 -1
  138. package/templates/.agents/design/.zaip-managed.json +3 -3
  139. package/templates/.agents/design/apps/catalog/package.json +1 -1
  140. package/templates/.agents/design/packages/local-runtime/package.json +1 -1
  141. package/templates/.agents/requirements/autonomous-assurance.md +39 -3
  142. package/templates/.agents/requirements/cloud-hostinger-autonomy.md +1 -1
  143. package/templates/.agents/requirements/comparative-audit.md +1 -1
  144. package/templates/.agents/requirements/context-engine.md +3 -3
  145. package/templates/.agents/requirements/cost-observability.md +1 -1
  146. package/templates/.agents/requirements/dashboard-launch.md +1 -1
  147. package/templates/.agents/requirements/data-export.md +1 -1
  148. package/templates/.agents/requirements/design-system.md +1 -1
  149. package/templates/.agents/requirements/execution-engine.md +11 -2
  150. package/templates/.agents/requirements/kanban-dashboard.md +1 -1
  151. package/templates/.agents/requirements/model-orchestration.md +1 -1
  152. package/templates/.agents/requirements/public-distribution.md +1 -1
  153. package/templates/.agents/requirements/software-architecture.md +1 -1
  154. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  155. package/templates/.agents/requirements/work-lifecycle.md +2 -1
  156. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  157. package/templates/.agents/rules/cloud-hostinger-autonomy.md +1 -1
  158. package/templates/.agents/rules/production-readiness.md +24 -0
  159. package/templates/.agents/rules/run-lifecycle-closure.md +2 -1
  160. package/templates/.agents/rules/security.md +2 -2
  161. package/templates/.agents/rules/workflow.md +4 -3
  162. package/templates/.agents/runtime/VERSION +1 -1
  163. package/templates/.agents/runtime/bin/zaip.mjs +1185 -610
  164. package/templates/.agents/runtime/checksums.sha256 +6 -6
  165. package/templates/.agents/runtime/manifest.json +1 -1
  166. package/templates/.agents/runtime/pnpm-lock.sha256 +1 -1
  167. package/templates/.agents/runtime/provenance.json +5 -5
  168. package/templates/.agents/runtime/sbom.cdx.json +71 -71
  169. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +4 -2
  170. package/templates/.agents/skills/zaip-skill-execution/references/controlled-release-recovery.md +44 -0
  171. package/templates/.agents/skills/zaip-skill-execution/references/runtime-governance.md +1 -1
  172. package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +7 -1
  173. package/templates/.agents/skills/zaip-skill-github-deploy/references/release-gate-matrix.md +15 -20
  174. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/deploy-contract.mjs +38 -2
  175. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/deploy-contract.test.mjs +62 -4
  176. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/github-deploy-contract.test.mjs +8 -6
  177. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/guarded-commits.mjs +5 -3
  178. package/templates/.agents/skills/zaip-skill-lifecycle-closure/SKILL.md +7 -0
  179. package/templates/.agents/skills/zaip-skill-maintenance-enter/SKILL.md +23 -0
  180. package/templates/.agents/skills/zaip-skill-maintenance-enter/agents/openai.yaml +4 -0
  181. package/templates/.agents/skills/zaip-skill-maintenance-enter/scripts/maintenance.mjs +213 -0
  182. package/templates/.agents/skills/zaip-skill-maintenance-enter/scripts/maintenance.test.mjs +207 -0
  183. package/templates/.agents/skills/zaip-skill-maintenance-exit/SKILL.md +21 -0
  184. package/templates/.agents/skills/zaip-skill-maintenance-exit/agents/openai.yaml +4 -0
  185. package/templates/.agents/skills/zaip-skill-prd/SKILL.md +6 -0
  186. package/templates/.agents/skills/zaip-skill-release-qualification/SKILL.md +26 -0
  187. package/templates/.agents/skills/zaip-skill-release-qualification/references/local-preparation.md +46 -11
  188. package/templates/.agents/skills/zaip-skill-spec/SKILL.md +9 -0
  189. package/templates/.agents/skills/zaip-skill-testing-strategy/SKILL.md +15 -0
  190. package/templates/.agents/zaip-aios/VERSION +1 -1
  191. package/templates/.agents/zaip-aios/guides/11-observabilidade-e-custos.md +22 -0
  192. package/templates/.agents/zaip-aios/guides/14-troubleshooting.md +1 -1
  193. package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +52 -5
  194. package/templates/template-manifest.json +210 -126
  195. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-Co2Jiuzh.js +0 -68
@@ -205,7 +205,14 @@ export function registerOperationsRoutes(app: Hono, dependencies: DashboardRoute
205
205
  );
206
206
  }
207
207
  });
208
- registerOperationsRunsRoute(app, filesystem, readAggregate, headOrJson);
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
+ }
@@ -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, page, lifecycleSummary] = await Promise.all([
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 = (lifecycleSummary.run_metrics ?? [])
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: run.task_id === null ? "conversation" : "task",
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
- /** Keeps Cost Run transport incremental without changing aggregate semantics. */
419
- export function pageCostRunRows(
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
+ });
@@ -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 run = compactRunProjection({
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,
@@ -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: async () => {
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: { code: "active_telemetry_capture_failed" },
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
  });