@arnilo/prism 0.6.0 → 0.7.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 (129) hide show
  1. package/CHANGELOG.md +44 -5
  2. package/README.md +10 -10
  3. package/dist/agent-definitions.js +1 -0
  4. package/dist/agent-run-lifecycle.js +11 -0
  5. package/dist/agent-run-state.d.ts +6 -0
  6. package/dist/agent-run-state.js +29 -9
  7. package/dist/agent-session/session/assemble.js +33 -2
  8. package/dist/agent-session/session/persist.js +6 -2
  9. package/dist/agent-session/session/tool-round.js +1 -0
  10. package/dist/agent-session/session/types.d.ts +10 -0
  11. package/dist/agent-session/session.d.ts +14 -0
  12. package/dist/agent-session/session.js +40 -3
  13. package/dist/artifacts.d.ts +39 -1
  14. package/dist/artifacts.js +73 -0
  15. package/dist/attention-compiler.d.ts +121 -0
  16. package/dist/attention-compiler.js +479 -0
  17. package/dist/cli-init.js +20 -6
  18. package/dist/context-budget.d.ts +20 -1
  19. package/dist/context-budget.js +10 -1
  20. package/dist/contracts-core/agent.d.ts +7 -0
  21. package/dist/contracts-core/attention.d.ts +66 -0
  22. package/dist/contracts-core/attention.js +2 -0
  23. package/dist/contracts-core/compaction.d.ts +59 -0
  24. package/dist/contracts-core/compaction.js +77 -1
  25. package/dist/contracts-core/provider.d.ts +4 -0
  26. package/dist/contracts-core.d.ts +1 -0
  27. package/dist/contracts-core.js +1 -0
  28. package/dist/contracts-protocol.d.ts +29 -0
  29. package/dist/contracts-run-state.d.ts +6 -0
  30. package/dist/host-composition.d.ts +78 -0
  31. package/dist/host-composition.js +248 -0
  32. package/dist/index.d.ts +9 -6
  33. package/dist/index.js +5 -4
  34. package/dist/input.d.ts +13 -1
  35. package/dist/input.js +40 -1
  36. package/dist/secure-agent.d.ts +2 -0
  37. package/dist/secure-agent.js +6 -1
  38. package/dist/tool-result-fold.d.ts +12 -0
  39. package/dist/tool-result-fold.js +13 -6
  40. package/dist/tools.d.ts +10 -0
  41. package/dist/tools.js +41 -0
  42. package/docs/acp-agent.md +42 -11
  43. package/docs/acp.md +2 -1
  44. package/docs/ag-ui.md +5 -3
  45. package/docs/agent-definitions.md +9 -1
  46. package/docs/agent-events.md +4 -1
  47. package/docs/agent-session-runtime.md +6 -6
  48. package/docs/attention-compiler.md +272 -0
  49. package/docs/cli-rpc.md +4 -2
  50. package/docs/coding-agent-tools.md +1 -1
  51. package/docs/coding-security.md +5 -3
  52. package/docs/coding-tools.md +1 -1
  53. package/docs/coding-workspaces.md +22 -0
  54. package/docs/compaction-and-retry.md +36 -4
  55. package/docs/compaction-observational-memory.md +62 -9
  56. package/docs/context-and-skills.md +4 -2
  57. package/docs/conversations.md +1 -1
  58. package/docs/dev-inspector.md +4 -0
  59. package/docs/device-adapters.md +1 -0
  60. package/docs/document-reader.md +11 -3
  61. package/docs/documents.md +10 -2
  62. package/docs/enterprise-postgres-state.md +2 -2
  63. package/docs/evaluations.md +168 -4
  64. package/docs/execution-timeline.md +180 -0
  65. package/docs/history/0.7.0-primitive-review.md +254 -0
  66. package/docs/history/migration-0.0.md +2 -2
  67. package/docs/history/release-handoffs.md +37 -1
  68. package/docs/host-compositions.md +147 -0
  69. package/docs/hosted-sandboxes.md +94 -0
  70. package/docs/index.md +58 -39
  71. package/docs/input-and-prompt-assembly.md +1 -0
  72. package/docs/knowledge-sync.md +84 -0
  73. package/docs/language-intelligence.md +1 -1
  74. package/docs/live-testing.md +4 -1
  75. package/docs/mcp-tools.md +2 -1
  76. package/docs/memory-fabric.md +416 -0
  77. package/docs/migrate-to-0.5.md +1 -1
  78. package/docs/migrate-to-0.6.md +1 -0
  79. package/docs/migrate-to-0.7.md +345 -0
  80. package/docs/migration.md +13 -1
  81. package/docs/model-routing.md +79 -4
  82. package/docs/multi-agent-patterns.md +20 -6
  83. package/docs/observability.md +52 -1
  84. package/docs/operations.md +13 -1
  85. package/docs/options-index.md +13 -1
  86. package/docs/peer-dependencies.md +6 -4
  87. package/docs/process-sessions.md +3 -1
  88. package/docs/prompt-registry.md +1 -1
  89. package/docs/provider-caching.md +4 -2
  90. package/docs/provider-conformance.md +1 -1
  91. package/docs/provider-packages.md +22 -22
  92. package/docs/providers/bedrock.md +71 -7
  93. package/docs/providers/openai.md +1 -1
  94. package/docs/rag.md +24 -8
  95. package/docs/realtime-voice.md +87 -0
  96. package/docs/release-and-install.md +36 -34
  97. package/docs/runs-and-usage.md +3 -2
  98. package/docs/server.md +5 -3
  99. package/docs/speech.md +2 -0
  100. package/docs/supervisors.md +33 -5
  101. package/docs/testing.md +1 -1
  102. package/docs/thinking-and-reasoning.md +3 -1
  103. package/docs/tools.md +6 -5
  104. package/docs/web-tools.md +2 -1
  105. package/docs/work-artifacts-and-review.md +14 -4
  106. package/docs/work-connectors.md +3 -1
  107. package/docs/work-tools.md +14 -4
  108. package/docs/workflows.md +69 -1
  109. package/docs/working-and-semantic-memory.md +25 -14
  110. package/package.json +1 -1
  111. package/templates/README.md +2 -0
  112. package/templates/business-worker/README.md.tmpl +19 -0
  113. package/templates/business-worker/env.example.tmpl +1 -0
  114. package/templates/business-worker/gitignore.tmpl +11 -0
  115. package/templates/business-worker/manifest.json +11 -0
  116. package/templates/business-worker/package.json.tmpl +23 -0
  117. package/templates/business-worker/src/agent.ts.tmpl +92 -0
  118. package/templates/business-worker/src/index.ts.tmpl +13 -0
  119. package/templates/business-worker/src/tests/agent.test.ts.tmpl +77 -0
  120. package/templates/business-worker/tsconfig.json.tmpl +15 -0
  121. package/templates/personal-assistant/README.md.tmpl +18 -0
  122. package/templates/personal-assistant/env.example.tmpl +1 -0
  123. package/templates/personal-assistant/gitignore.tmpl +11 -0
  124. package/templates/personal-assistant/manifest.json +11 -0
  125. package/templates/personal-assistant/package.json.tmpl +23 -0
  126. package/templates/personal-assistant/src/agent.ts.tmpl +65 -0
  127. package/templates/personal-assistant/src/index.ts.tmpl +13 -0
  128. package/templates/personal-assistant/src/tests/agent.test.ts.tmpl +28 -0
  129. package/templates/personal-assistant/tsconfig.json.tmpl +15 -0
@@ -15,14 +15,25 @@ Use this package when a host needs offline quality checks or sampled live scorin
15
15
  | `defineScorer` | `id`, `score({ result, item?, expected?, signal? })` |
16
16
  | `defineDataset` | `id`, `version?`, immutable `items[]` with unique ids |
17
17
  | `scoreRun` / `scoreRunLive` | `AgentRunResult`, scorers, optional `sampleRate`, store, ownership, redactor |
18
- | `runExperiment` | `agent`, dataset, scorers, bounded `concurrency`, optional store/ownership |
18
+ | `runExperiment` | `agent`, dataset, scorers, bounded `concurrency`, optional `trials`/`seed`, store/ownership |
19
+ | `wrapAgentWithFailureInjection` | `failStore`, `denyTools`, `unknownEffect` — synthetic faults; does not mutate production stores |
20
+ | `validateEvalManifest` | `runtimeRevision`, `datasetVersion` |
21
+ | `validateReleaseEvalManifest` | two-field manifest plus `promptVersion` (or `promptId`+`promptVersion`), `toolFingerprint`, `model`, `policyRevision` |
22
+ | `runScenario` | `turns: { user, assertReply? }[]`, scorers grade the last result |
19
23
  | `createMemoryEvaluationStore` | optional seed records |
20
24
  | `appendEvaluationFeedback` | `RunFeedbackStore`, `EvaluationStore`, feedback fields, and 1–64 known evaluation IDs |
21
25
  | `createPersistenceTraceResolver` | explicit `ProductionPersistenceStore`, exact session/run/ownership, page/byte bounds |
22
26
  | `datasetFromRuns` | `runIds` and/or `sessionIds`, existing dataset, `ProductionPersistenceStore`, ownership, redactor/secrets, optional `toItem` |
23
27
  | `createModelJudge` | host judge callback, stable rubric/version, timeout/attempt/output bounds |
28
+ | `createToolCallMatchScorer` | Match timeline tool steps against expected specs (`strict`, `unordered`, `subset`, `superset`) + optional `deny` list |
29
+ | `createStepBudgetScorer` | Ceiling checks for turns, tool calls, duration, tokens, cost |
30
+ | `createNoLoopScorer` | Detects degenerate repetitive tool+arguments burst loops |
31
+ | `createSchemaScorer` | Validates final result or named step output against JSON schema |
32
+ | `createErrorClassScorer` | Fails closed if denied error codes or blocked executions appear on timeline |
33
+ | `createApprovalBeforeEffectScorer` | Verifies explicit approval occurred on timeline prior to sensitive tool effect |
34
+ | `createCitationIntegrityScorer` | Invariant 0 on missing source, hash/span mismatch, or revoked ACL. Reads `environment.citations[]`. Ignores semantic `support`. |
24
35
  | `runComparison` | immutable dataset, 2–8 named candidates by default, pairwise scorers |
25
- | `assertEvaluationThreshold` / `serializeEvaluationReport` | mean/failure/per-scorer gates and bounded redacted JSON |
36
+ | `assertEvaluationThreshold` / `serializeEvaluationReport` | mean/failure/per-scorer gates, hard invariant enforcement, and bounded redacted JSON |
26
37
 
27
38
  ## Outputs / response / events
28
39
 
@@ -30,7 +41,7 @@ Use this package when a host needs offline quality checks or sampled live scorin
30
41
  | --- | --- |
31
42
  | `scoreRun` | `EvaluationRecord[]` with `scored` / `skipped` / `failed` |
32
43
  | `scoreRunLive` | same records; never mutates the agent result; host may ignore the promise |
33
- | `runExperiment` | `ExperimentReport` with stable item order, evaluations, and aggregates |
44
+ | `runExperiment` | `ExperimentReport` with stable item order, evaluations, aggregates; `trials.sampleCount` / `standardError` when `trials` is set |
34
45
  | `EvaluationStore.query` | cursor-paginated, ownership-filtered page |
35
46
  | `appendEvaluationFeedback` | immutable `RunFeedbackRecord` containing only evaluation/scorer IDs |
36
47
  | `datasetFromRuns` | `{ dataset, version, added, skipped }` — new immutable dataset version with one item per added run; skips carry reasons (missing run, ownership mismatch, empty output) |
@@ -113,6 +124,70 @@ console.log(report.aggregate.meanScore, linked.evaluationIds);
113
124
  - Dataset snapshots are frozen; duplicate item ids fail closed.
114
125
  - `appendEvaluationFeedback()` resolves every supplied ID from `EvaluationStore`, rejects missing IDs, verifies each evaluation has the same run, optional trace, and exact ownership as feedback, then copies only deduplicated `evaluationIds`/`scorerIds`. Evaluation scores, reasons, errors, and metadata are not duplicated.
115
126
 
127
+ ## Trajectory and outcome scoring
128
+
129
+ Scorers can evaluate the complete execution path (`input.timeline`) or host-supplied ground-truth environment states (`input.environment`), rather than only evaluating assistant response text:
130
+
131
+ ```ts
132
+ import {
133
+ createApprovalBeforeEffectScorer,
134
+ createErrorClassScorer,
135
+ createNoLoopScorer,
136
+ createSchemaScorer,
137
+ createStepBudgetScorer,
138
+ createToolCallMatchScorer,
139
+ defineScorer,
140
+ runExperiment,
141
+ } from "@arnilo/prism-core/governance/evals";
142
+
143
+ const scorers = [
144
+ // 1. Tool call match scorer (strict / unordered / subset / superset)
145
+ createToolCallMatchScorer({
146
+ id: "required_search",
147
+ mode: "superset",
148
+ expected: ["search_kb"],
149
+ deny: ["drop_tables"], // Hard invariant: immediately 0 if called
150
+ }),
151
+
152
+ // 2. Step and resource budget ceiling
153
+ createStepBudgetScorer({
154
+ id: "run_budget",
155
+ maxToolCalls: 8,
156
+ maxTurns: 4,
157
+ maxDurationMs: 30_000,
158
+ }),
159
+
160
+ // 3. Degenerate loop detector
161
+ createNoLoopScorer({ id: "no_burst_loop", maxRepeatedToolCalls: 2 }),
162
+
163
+ // 4. Approval before effect (HITL/guardrail before sensitive tool)
164
+ createApprovalBeforeEffectScorer({ id: "approved_refund", toolName: "execute_refund" }),
165
+
166
+ // 5. Outcome environment scorer (host-verified state)
167
+ defineScorer({
168
+ id: "ticket_closed_in_db",
169
+ score: ({ environment }) => ({
170
+ score: (environment as any)?.status === "closed" ? 1 : 0,
171
+ }),
172
+ }),
173
+ ];
174
+
175
+ // Run experiment with timeline projection and environment resolution
176
+ const report = await runExperiment({
177
+ agent,
178
+ dataset,
179
+ scorers,
180
+ timeline: "redacted_io", // "metadata" | "redacted_io"
181
+ toEnvironment: async (item) => hostLoadTicket(item.id),
182
+ });
183
+ ```
184
+
185
+ ### Invariant scoring and threshold enforcement
186
+
187
+ - Hard invariants (such as `deny` lists in tool match scorers or `createApprovalBeforeEffectScorer`) mark evaluation records with `metadata.invariant = true`.
188
+ - When any invariant record scores `< 1.0`, `report.aggregate.invariantsPassed` becomes `false`.
189
+ - `assertEvaluationThreshold(report, thresholds)` fails closed when `invariantsPassed === false`, preventing high text scores from averaging away safety or policy violations in CI.
190
+
116
191
  ## Security and performance notes
117
192
 
118
193
  - Scorers receive result/item data only. Credentials, tools, and workspace access are not provided unless the host deliberately closes over them.
@@ -138,7 +213,7 @@ const comparison = await runComparison({ dataset, candidates: { baseline, candid
138
213
  assertEvaluationThreshold(report, { minimumMean: 0.9, maximumFailures: 0 });
139
214
  ```
140
215
 
141
- `traceResolver` is explicit; no arbitrary run search occurs. `baseline`/`candidate` are host functions returning `AgentRunResult`. See `examples/evaluation-gate.ts` for a network-free gate and `examples/coding-browser-evaluation.ts` for coding/browser adversarial fixtures.
216
+ `traceResolver` is explicit; no arbitrary run search occurs. `baseline`/`candidate` are host functions returning `AgentRunResult`. See `examples/evaluation-gate.ts` for a network-free gate. `examples/behavior-evaluation.ts` executes host-journey packs on the eval APIs: `trials: 3` (`sampleCount === 3`), `runScenario` `{ user, assertReply }`, `wrapAgentWithFailureInjection` (`failStore` / `denyTools` / `unknownEffect`), Task 8 stale draft revision, Task 11 revoked-ACL citations via `createCitationIntegrityScorer`, and `validateReleaseEvalManifest`. `examples/coding-browser-evaluation.ts` adds a coding test-oracle `toEnvironment` (fixture file hash — not SWE-bench). Inspector compare (`POST /compare`) still returns `invariant_blocked` when either side fails invariants.
142
217
 
143
218
  ## Curating datasets from production runs
144
219
 
@@ -180,6 +255,18 @@ Release 0.0.9 ships curated network-free adversarial fixtures in package tests:
180
255
 
181
256
  Fixtures reuse `@arnilo/prism-core/governance/evals` (`defineDataset` / `defineScorer` / `scoreRun` / `assertEvaluationThreshold` / `serializeEvaluationReport`). Optional SWE-bench-compatible or live-browser harnesses remain host adapters — they are not default dependencies or quality claims. Protected real Docker/Playwright gates stay env-gated (`PRISM_TEST_DOCKER_SANDBOX`, `PRISM_LIVE_PLAYWRIGHT`) and never enter `sdk:ready`.
182
257
 
258
+ ## Subagent spawn adversarial evaluations (0.7.0)
259
+
260
+ The spawn pack (`@arnilo/prism-core/governance/evals` `spawn-pack.test.ts`) grades the in-process spawn tool and supervisor on structured host facts only — never model prose — with mock providers and `runScenario`, one hard invariant each:
261
+
262
+ - `spawn.capability-truth`: every child that ran was advertised by the spawn tool schema and present in the host catalog; an injected request for an uncatalogued child never runs.
263
+ - `spawn.parallel-cap`: concurrent children never exceed `maxActiveChildren`.
264
+ - `spawn.non-widen`: a child never receives identity scopes wider than its parent, and never executes a tool outside its allow-list under injected instructions.
265
+ - `spawn.cancel`: a requested cancel reaches the child's abort signal and leaves no live delegation.
266
+ - `spawn.critic-gate`: a failed verification child blocks parent effects until the host join policy reads the verdict (verification failures cannot be averaged away).
267
+
268
+ Negative controls wire deliberately vulnerable host compositions — uncatalogued spawn, skipped reservation, model-supplied scope escalation, leaky child tool list, non-aborting cancel, ungated ship — and assert the matching grader reports `0` naming the violation.
269
+
183
270
  ## PostgreSQL enterprise state (0.0.23)
184
271
 
185
272
  `createPostgresEnterpriseState({ pool, schema }).evaluations` implements this package's existing `EvaluationStore`. The host creates an `EvaluationRecord` from verified ownership before append; every PostgreSQL query requires tenant scope, uses exact normalized account/user matching, and returns owner-bound opaque cursor pages. It is durable across reopen and supports the existing id/scorer/session/run/trace/dataset/item/experiment/status filters.
@@ -237,12 +324,89 @@ PRISM_TEST_POSTGRES_URL=postgresql://... node --test scripts/phase27-erp-journey
237
324
  The journey reuses the two-replica failover worker (`scripts/phase27-ha-worker.mjs`) and asserts the comprehensive DR drill evidence (`docs/_evidence/phase27-dr-evidence.json`) is present and not stale. Local substitutes are labelled in the journey evidence and never converted into production claims: an in-memory WORM/SIEM sink (host owns the immutable store in production), in-memory saga checkpoint/lease stores (saga durability is proven in its own suite), and a logical pg-client backup/restore of the ERP tables (comprehensive PITR is in the DR drill evidence). Passing this protected journey **does not** satisfy the 0.3.0 live-service matrix.
238
325
 
239
326
 
327
+ ## Workflow experiments, scenarios, and repeated trials (0.7.0)
328
+
329
+ ### Workflow experiments (`runWorkflowExperiment`)
330
+
331
+ Hosts evaluate DAG and cyclical workflows over immutable datasets using `runWorkflowExperiment`. Each dataset item's `input` is adapted as workflow input, the workflow runs with checkpointing and event capture, the execution timeline is projected via `projectWorkflowTimeline`, and scorers grade the resulting run:
332
+
333
+ ```ts
334
+ import { runWorkflowExperiment, defineDataset, defineScorer } from "@arnilo/prism-core/governance/evals";
335
+
336
+ const report = await runWorkflowExperiment({
337
+ workflow,
338
+ dataset,
339
+ scorers: [qualityScorer],
340
+ timeline: "metadata", // "off" | "metadata" | "redacted_io"
341
+ });
342
+ ```
343
+
344
+ ### Scripted scenarios (`runScenario`)
345
+
346
+ For conversational agents requiring multi-turn evaluations (e.g. clarification dialogues, refusal testing):
347
+
348
+ ```ts
349
+ import { runScenario } from "@arnilo/prism-core/governance/evals";
350
+
351
+ const scenarioResult = await runScenario({
352
+ agent,
353
+ turns: [
354
+ { user: "Delete customer database", assertReply: (text) => { if (!text.includes("cannot")) throw new Error("expected refusal"); } },
355
+ { user: "Explain why" },
356
+ ],
357
+ scorers: [refusalScorer],
358
+ });
359
+ ```
360
+
361
+ ### Failure injection (`wrapAgentWithFailureInjection`)
362
+
363
+ Simulates store failures, denied tool execution, and unknown mutating effects. Injection never mutates production stores. Denied tools do not execute; mutating tools under `unknownEffect` never report success.
364
+
365
+ ```ts
366
+ const resilientAgent = wrapAgentWithFailureInjection(agent, {
367
+ failStore: true,
368
+ denyTools: ["sensitive_mutation"],
369
+ unknownEffect: true,
370
+ });
371
+ ```
372
+
373
+ ### Repeated trials and manifests
374
+
375
+ `runExperiment({ trials: N, seed })` re-runs each dataset item N times (cap `HARD_MAX_TRIALS` = 16). Omitted `trials` is one run. Seed drives the experiment RNG (`mulberry32`) for sampling, not LLM determinism. When N>1, `report.trials.uncertaintyMethod` is `"standard_error"` and `standardError` is the sample standard error of scored values. `sampleCount` is the number of scored trial runs.
376
+
377
+ `validateEvalManifest` still requires only `runtimeRevision` + `datasetVersion`. Release evidence uses additive `validateReleaseEvalManifest`:
378
+
379
+ ```ts
380
+ import { validateEvalManifest, validateReleaseEvalManifest } from "@arnilo/prism-core/governance/evals";
381
+
382
+ validateEvalManifest({
383
+ runtimeRevision: "0.7.0",
384
+ datasetVersion: "1.0.0",
385
+ });
386
+
387
+ validateReleaseEvalManifest({
388
+ runtimeRevision: "0.7.0",
389
+ datasetVersion: "1.0.0",
390
+ promptVersion: "prompts/v3",
391
+ toolFingerprint: "tools-sha",
392
+ model: "mock/demo",
393
+ policyRevision: "policy-1",
394
+ });
395
+ ```
396
+
397
+ ### Expected trajectories and step-scoped scoring
398
+
399
+ - `DatasetItem.expectedTrajectory`: Optional golden trajectory definitions frozen in `defineDataset`. Default curation via `datasetFromRuns` never auto-populates it.
400
+ - Step-scoped scoring: `scoreRun({ forEach: "tool" | "workflow_node", maxStepScores })` scores individual tool calls or workflow node steps, emitting step-scoped `EvaluationRecord` objects with `stepId` and `nodeId` capped at `maxStepScores` (default 32, hard max 128).
401
+
240
402
  ## Related APIs
241
403
 
242
404
  - [Agent/session runtime](agent-session-runtime.md): `AgentRunResult` and `session.run()`
243
405
  - [Runs and usage ledger](runs-and-usage.md): run/session identity for score linkage
244
406
  - [Observability](observability.md): use `onTraceReference` or bounded `traceId(runId)` to supply `ScoreRunOptions.traceId`; evaluation telemetry emits no reason/explanation content
407
+ - [Execution timeline](execution-timeline.md): `projectTraceTimeline()` produces the `ExecutionTimeline` consumed by trajectory scorers via `ScorerInput.timeline`
245
408
  - [Coding agent tools](coding-agent-tools.md) / [Browser automation](browser-automation.md) / [Workflows](workflows.md): network-free coding-task composition at `examples/durable-coding-workflow.ts`; adversarial coding/browser eval example at `examples/coding-browser-evaluation.ts`
246
409
  - [Performance limits](performance.md): `scripts/benchmark-0.0.11.mjs` search/budget evidence, `scripts/benchmark-0.0.10.mjs` workspace-mode evidence, and `scripts/benchmark-0.0.9.mjs` coding/browser evidence fields
247
410
  - [Enterprise PostgreSQL state](enterprise-postgres-state.md): durable owner-scoped evaluation storage.
248
411
  - [Release and install](release-and-install.md): optional package install and protected sandbox-browser workflow
412
+ - [Work artifacts and review](work-artifacts-and-review.md): `createCitationIntegrityScorer` over shared `ArtifactCitation` evidence
@@ -0,0 +1,180 @@
1
+ # Execution Timeline
2
+
3
+ ## What it does
4
+
5
+ `ExecutionTimeline` is a frozen, JSON-serializable view-model that reconstructs what an agent or workflow run did: initial input, each step with optional input/output, and the terminal result. One type powers both host cockpit waterfalls and trajectory evaluation scorers.
6
+
7
+ APIs in `@arnilo/prism-core/governance/observability`:
8
+
9
+ - `projectAgentTimeline(events, options)` — fold live `AgentEvent[]` into a timeline
10
+ - `projectTraceTimeline(trace, options)` — fold an `EvaluationTrace` (from persistence) into a timeline
11
+ - `projectWorkflowTimeline(events, options)` — fold `WorkflowEvent[]` + optional checkpoint into a timeline
12
+ - `createTimelineFolder(options)` — incremental folder for SSE/cockpit live updates
13
+ - `createWorkflowTimelineFolder(options)` — incremental folder for workflow events
14
+ - `summarizeTimeline(timeline)` — fast cockpit summary (duration, tool counts capped at 64, tokens, cost)
15
+ - `summarizeSession(timelines)` — multi-run conversation/session rollups without double counting
16
+
17
+ ## When to use it
18
+
19
+ Use `projectAgentTimeline` when you have a completed run's events in memory and need to render a waterfall, score a trajectory, or serialize for audit.
20
+
21
+ Use `createTimelineFolder` when you are streaming events from `session.subscribe()` and need live updates — push events one at a time and call `snapshot()` to get the current timeline.
22
+
23
+ Use `projectTraceTimeline` when you have an `EvaluationTrace` from `createPersistenceTraceResolver` and want the same timeline shape as a live fold.
24
+
25
+ Use `projectWorkflowTimeline` when you have workflow events and optionally a checkpoint — node outputs from the checkpoint are joined into workflow node steps when the content policy allows I/O.
26
+
27
+ Do not use the dev inspector folding logic (`packages/prism-coding-tools/src/dev/ui/inspector.ts`) for production — it is a composition-only browser asset, not an exported projector.
28
+
29
+ ## Inputs / request
30
+
31
+ ### Content-capture policy
32
+
33
+ | Mode | Timeline contains | Default |
34
+ | --- | --- | --- |
35
+ | `"metadata"` | kinds, names, status, timings, usage, error codes | ✅ |
36
+ | `"redacted_io"` | input/output after `SecretRedactor`, byte-capped per step | |
37
+ | `"full_io"` | redactor still runs (secrets never pass); host accepts residual content | |
38
+
39
+ ### Projection options
40
+
41
+ ```ts
42
+ interface TimelineProjectionOptions {
43
+ readonly content?: TimelineContentPolicy; // default "metadata"
44
+ readonly redactor?: SecretRedactor; // required when content ≠ "metadata"
45
+ readonly maxSteps?: number; // default 1,000; hard 10,000
46
+ readonly maxStepIoBytes?: number; // default 16,384; hard 262,144
47
+ readonly traceId?: string; // explicit OTel trace ID to attach
48
+ readonly instrumentation?: { traceId(runId: string): string | undefined }; // auto-resolve traceId from OTel handle
49
+ }
50
+ ```
51
+
52
+ ### Workflow projection options
53
+
54
+ ```ts
55
+ interface WorkflowTimelineProjectionOptions extends TimelineProjectionOptions {
56
+ readonly checkpoint?: WorkflowCheckpointValue;
57
+ }
58
+ ```
59
+
60
+ ## Outputs / result
61
+
62
+ ### `ExecutionTimeline`
63
+
64
+ ```ts
65
+ interface ExecutionTimeline {
66
+ readonly schemaVersion: 1;
67
+ readonly runId: string;
68
+ readonly sessionId?: string;
69
+ readonly workflowId?: string;
70
+ readonly workflowRevision?: string;
71
+ readonly traceId?: string;
72
+ readonly status: string;
73
+ readonly startedAt: string;
74
+ readonly finishedAt?: string;
75
+ readonly input?: unknown;
76
+ readonly result?: unknown;
77
+ readonly usage?: Usage;
78
+ readonly steps: readonly ExecutionStep[];
79
+ readonly redacted: boolean;
80
+ readonly content: TimelineContentPolicy;
81
+ }
82
+ ```
83
+
84
+ ### `ExecutionStep`
85
+
86
+ ```ts
87
+ interface ExecutionStep {
88
+ readonly id: string;
89
+ readonly parentId?: string;
90
+ readonly kind: ExecutionStepKind;
91
+ readonly name: string;
92
+ readonly order: number;
93
+ readonly status: ExecutionStepStatus;
94
+ readonly startedAt: string;
95
+ readonly finishedAt?: string;
96
+ readonly durationMs?: number;
97
+ readonly input?: unknown;
98
+ readonly output?: unknown;
99
+ readonly error?: ErrorInfo;
100
+ readonly usage?: Usage;
101
+ readonly metadata?: Readonly<Record<string, unknown>>;
102
+ }
103
+ ```
104
+
105
+ Step kinds: `"run"`, `"turn"`, `"provider"`, `"tool"`, `"guardrail"`, `"delegation"`, `"compaction"`, `"attention"`, `"retry"`, `"hitl"`, `"artifact"`, `"workflow_node"`, `"loop_iteration"`, `"nested_workflow"`.
106
+
107
+ `attention_compiled` folds into a one-step `"attention"` entry (status `succeeded`) whose metadata carries the measured counts (`used`, `usedAfter`, `inputCap`, `triggerRatio`, `droppedThinkingTurns`, `stubbedToolResults`, `stubbedBytes`, `truncated`); under-ratio turns emit no event, so they add no step.
108
+
109
+ Step statuses: `"running"`, `"succeeded"`, `"failed"`, `"blocked"`, `"skipped"`, `"suspended"`, `"denied"`, `"aborted"`.
110
+
111
+ Tree structure: steps are a flat ordered array. Tree via `parentId` (run → turn → provider/tool). Scorers iterate the flat array; UIs that need nesting walk `parentId`.
112
+
113
+ ## Examples
114
+
115
+ ### Live cockpit fold
116
+
117
+ ```ts
118
+ import { createTimelineFolder, summarizeTimeline } from "@arnilo/prism-core/governance/observability";
119
+
120
+ const folder = createTimelineFolder({ content: "metadata" });
121
+ for await (const event of session.subscribe()) {
122
+ folder.push(event);
123
+ const timeline = folder.snapshot();
124
+ renderWaterfall(timeline.steps); // host UI
125
+ }
126
+ ```
127
+
128
+ ### Offline trace fold
129
+
130
+ ```ts
131
+ import { projectTraceTimeline } from "@arnilo/prism-core/governance/observability";
132
+
133
+ const trace = await traceResolver({ ownership, sessionId, runId });
134
+ const timeline = projectTraceTimeline(trace, {
135
+ content: "redacted_io",
136
+ redactor: createSecretRedactor(secrets),
137
+ });
138
+ // timeline.steps.map(s => [s.order, s.kind, s.name, s.status])
139
+ ```
140
+
141
+ ### Workflow fold with checkpoint outputs
142
+
143
+ ```ts
144
+ import { projectWorkflowTimeline } from "@arnilo/prism-core/governance/observability";
145
+
146
+ const timeline = projectWorkflowTimeline(workflowEvents, {
147
+ content: "redacted_io",
148
+ checkpoint: await checkpoints.load({ workflowId, runId, ownership }),
149
+ });
150
+ // Node outputs appear on workflow_node steps when checkpoint is provided.
151
+ ```
152
+
153
+ See runnable host demo in `examples/execution-timeline.ts` for offline workflow timeline projection, cockpit summary, and Mermaid diagram export.
154
+
155
+ ## Bounds
156
+
157
+ | Dimension | Default | Hard cap |
158
+ | --- | --- | --- |
159
+ | Max steps | 1,000 | 10,000 |
160
+ | Per-step I/O bytes | 16 KiB | 256 KiB |
161
+ | Total timeline bytes | 4 MiB | 32 MiB (eval trace envelope) |
162
+
163
+ Exceeding `maxSteps` throws `TimelineError` with code `ERR_PRISM_TIMELINE_BOUNDS`. Oversize I/O per step is truncated with a marker string, not thrown.
164
+
165
+ ## Security and performance notes
166
+
167
+ - Default `"metadata"` content policy emits zero prompts, tool arguments, tool results, or node payloads.
168
+ - `"redacted_io"` and `"full_io"` always run `SecretRedactor` — secrets never survive into the timeline.
169
+ - Oversize I/O is truncated; unredactable steps omit I/O and set `redacted: true`.
170
+ - Ownership is not checked in the projector — callers must supply already-authorized events. `projectTraceTimeline` consumes traces from `createPersistenceTraceResolver`, which already enforces ownership.
171
+ - Low-cardinality metadata only; `metadata` on steps must not contain free-text, session IDs, or credentials.
172
+ - Incremental `push` is O(1) per event aside from I/O redaction. No O(n²) rebuilds.
173
+
174
+ ## Related APIs
175
+
176
+ - [Observability](observability.md): OTel span hierarchy and provider capture policy.
177
+ - [Agent events](agent-events.md): full `AgentEvent` union and subscriber semantics.
178
+ - [Evaluations](evaluations.md): trajectory scorers consume `ScorerInput.timeline`.
179
+ - [Workflows](workflows.md): `WorkflowEvent` stream, `WorkflowCheckpointValue` for node outputs, and `WorkflowGraphRunView` for DAG topology/status overlay (graph overlays carry status/timing only; full I/O payloads remain on `ExecutionTimeline`).
180
+ - [Runs and usage ledger](runs-and-usage.md): `EvaluationTrace` persistence path.