@arnilo/prism 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +62 -26
  3. package/dist/agent-loops.d.ts +8 -1
  4. package/dist/agent-loops.js +57 -11
  5. package/dist/agents.js +212 -32
  6. package/dist/checkpoints.d.ts +11 -0
  7. package/dist/checkpoints.js +144 -0
  8. package/dist/cli-init.d.ts +41 -0
  9. package/dist/cli-init.js +390 -0
  10. package/dist/cli-runner.d.ts +7 -1
  11. package/dist/cli-runner.js +13 -1
  12. package/dist/compaction.js +9 -1
  13. package/dist/content.d.ts +121 -0
  14. package/dist/content.js +538 -0
  15. package/dist/contracts.d.ts +236 -11
  16. package/dist/contracts.js +8 -0
  17. package/dist/event-multiplexer.d.ts +23 -0
  18. package/dist/event-multiplexer.js +136 -0
  19. package/dist/execution-policy.d.ts +28 -0
  20. package/dist/execution-policy.js +24 -0
  21. package/dist/feedback.d.ts +48 -0
  22. package/dist/feedback.js +230 -0
  23. package/dist/index.d.ts +20 -6
  24. package/dist/index.js +13 -5
  25. package/dist/input.js +11 -1
  26. package/dist/leases.d.ts +8 -0
  27. package/dist/leases.js +111 -0
  28. package/dist/node/agent-definitions.js +3 -5
  29. package/dist/node/config.d.ts +1 -0
  30. package/dist/node/config.js +5 -3
  31. package/dist/node/contribution-discovery.js +5 -8
  32. package/dist/node/session-store-jsonl.js +8 -5
  33. package/dist/node/settings.js +2 -2
  34. package/dist/node/trust.js +2 -4
  35. package/dist/observability.d.ts +3 -0
  36. package/dist/observability.js +18 -0
  37. package/dist/providers/media.d.ts +44 -0
  38. package/dist/providers/media.js +126 -0
  39. package/dist/providers/openai-compatible.js +18 -119
  40. package/dist/providers/openai-primitives.d.ts +9 -0
  41. package/dist/providers/openai-primitives.js +129 -0
  42. package/dist/providers/transport.d.ts +40 -0
  43. package/dist/providers/transport.js +221 -0
  44. package/dist/redaction.js +40 -13
  45. package/dist/resources.d.ts +5 -0
  46. package/dist/resources.js +4 -0
  47. package/dist/structured-output.d.ts +11 -0
  48. package/dist/structured-output.js +59 -0
  49. package/dist/testing/feedback.d.ts +6 -0
  50. package/dist/testing/feedback.js +37 -0
  51. package/dist/testing/persistence-schema.d.ts +102 -0
  52. package/dist/testing/persistence-schema.js +487 -0
  53. package/dist/testing/provider-conformance.js +10 -1
  54. package/dist/testing/run-ledger-conformance.d.ts +33 -0
  55. package/dist/testing/run-ledger-conformance.js +178 -0
  56. package/dist/testing/session-store-conformance.d.ts +16 -0
  57. package/dist/testing/session-store-conformance.js +73 -0
  58. package/dist/tools.d.ts +17 -0
  59. package/dist/tools.js +29 -2
  60. package/docs/a2a.md +73 -0
  61. package/docs/agent-events.md +17 -10
  62. package/docs/agent-loops.md +11 -5
  63. package/docs/agent-session-runtime.md +15 -16
  64. package/docs/cli-rpc.md +36 -5
  65. package/docs/coding-agent-tools.md +43 -9
  66. package/docs/coding-security.md +88 -0
  67. package/docs/compaction-observational-memory.md +2 -0
  68. package/docs/context-and-skills.md +1 -0
  69. package/docs/credential-storage.md +177 -0
  70. package/docs/credentials-and-redaction.md +4 -3
  71. package/docs/database-persistence.md +52 -7
  72. package/docs/evaluations.md +122 -0
  73. package/docs/extensions.md +2 -2
  74. package/docs/host-security.md +33 -2
  75. package/docs/index.md +46 -18
  76. package/docs/input-and-prompt-assembly.md +6 -5
  77. package/docs/mcp-tools.md +184 -0
  78. package/docs/middleware-hooks.md +2 -0
  79. package/docs/migration.md +51 -28
  80. package/docs/model-registry.md +5 -3
  81. package/docs/multimodal-content.md +156 -0
  82. package/docs/observability.md +171 -0
  83. package/docs/performance.md +249 -1
  84. package/docs/persistence-credentials-multimodality-primitives.md +303 -0
  85. package/docs/postgres-persistence.md +143 -0
  86. package/docs/provider-conformance.md +18 -0
  87. package/docs/provider-layer.md +1 -1
  88. package/docs/provider-packages.md +2 -0
  89. package/docs/provider-primitives.md +281 -0
  90. package/docs/providers/ai-sdk.md +113 -0
  91. package/docs/providers/kimi.md +1 -0
  92. package/docs/providers/neuralwatt.md +1 -0
  93. package/docs/providers/openai-compatible.md +2 -1
  94. package/docs/providers/openai.md +8 -1
  95. package/docs/providers/opencode-go.md +1 -0
  96. package/docs/providers/openrouter.md +1 -0
  97. package/docs/providers/zai.md +1 -0
  98. package/docs/public-contracts.md +13 -5
  99. package/docs/rag.md +113 -0
  100. package/docs/release-and-install.md +237 -30
  101. package/docs/resource-loading.md +14 -4
  102. package/docs/review-coverage-2026-07-14.md +260 -0
  103. package/docs/review-coverage-2026-07-15.md +193 -0
  104. package/docs/run-ledger-conformance.md +96 -0
  105. package/docs/runs-and-usage.md +43 -4
  106. package/docs/server.md +139 -0
  107. package/docs/session-store-conformance.md +16 -0
  108. package/docs/session-stores-and-branching.md +1 -0
  109. package/docs/settings-auth-trust-security.md +6 -5
  110. package/docs/sqlite-persistence.md +123 -0
  111. package/docs/structured-output.md +9 -0
  112. package/docs/supervisors.md +71 -0
  113. package/docs/tool-conformance.md +1 -0
  114. package/docs/tool-execution-primitives.md +374 -0
  115. package/docs/tools.md +39 -1
  116. package/docs/workflow-orchestration-primitives.md +581 -0
  117. package/docs/workflow-tui-primitives.md +5 -0
  118. package/docs/workflows.md +293 -0
  119. package/docs/working-and-semantic-memory.md +169 -0
  120. package/package.json +43 -5
  121. package/templates/init/README.md.tmpl +28 -0
  122. package/templates/init/env.example.tmpl +1 -0
  123. package/templates/init/gitignore.tmpl +11 -0
  124. package/templates/init/optional/evals-example.ts.tmpl +17 -0
  125. package/templates/init/optional/workflows-example.ts.tmpl +27 -0
  126. package/templates/init/package.json.tmpl +22 -0
  127. package/templates/init/providers.json +76 -0
  128. package/templates/init/src/agent.ts.tmpl +10 -0
  129. package/templates/init/src/index.ts.tmpl +12 -0
  130. package/templates/init/src/tests/agent.test.ts.tmpl +24 -0
  131. package/templates/init/tsconfig.json.tmpl +15 -0
@@ -0,0 +1,581 @@
1
+ # Workflow orchestration primitives
2
+
3
+ ## What it does
4
+
5
+ This page freezes the Plan 057 Task 0 inventory and Task 1 adapter-contract lock for workflow orchestration. It maps existing `@arnilo/prism` orchestration, CLI/RPC, event, approval, and persistence seams; records capability gap **C-009** (workflow/graph orchestration); pins performance and security limits for Tasks 2–7; and documents the final public design for `@arnilo/prism-workflows`.
6
+
7
+ Interactive TUI (**C-012**) is **out of scope** for Plan 057 and deferred. Workflow start/status/cancel/resume is delivered through public package APIs and optional RPC/`CommandDefinition` bindings.
8
+
9
+ **Final Task 7 architecture (2026-07-14):** DAG/coordinator semantics remain package-local. Reusable versioned checkpoints, atomic leases, and bounded async event fan-in live in core as `CheckpointStore`, `LeaseStore`, and `EventMultiplexer`. Workflow adapters are thin domain facades; lease fencing plus checkpoint CAS prevents stale-worker commits.
10
+
11
+ **Phase 8 addendum (2026-07-15):** `@arnilo/prism-workflows` extends the same checkpoint JSON/state machine with `suspended` and terminal `denied` statuses, `suspend()`, persisted suspension/resume records, and `workflow_suspended` / `workflow_resumed` events. An approved resume requires the displayed checkpoint `expectedVersion`; the first checkpoint write CAS-claims it before node execution. Suspended runs are absent from coordinator `queued`/`running` polls, so no worker or polling loop remains active. No SQL migration or second approval store is required.
12
+
13
+ **Phase 11 addendum (2026-07-16):** schedules use a separate generic checkpoint namespace plus per-fire `LeaseStore` claims and deterministic queued-run IDs; SQLite/PostgreSQL need no workflow-specific table or migration. Background execution remains `enqueueWorkflow` + the existing coordinator. Nested workflow nodes call the same runner with inherited policy/ownership/checkpoint/event seams. Shared JSON state is validated, redacted, byte/history bounded, and checkpointed by version. Replay creates a new checkpoint with immutable source lineage and copied terminal evidence; approval-bearing prior paths cannot be copied.
14
+
15
+ ## When to use it
16
+
17
+ - **Workflow package authors** should start here, then follow [Agent/session runtime](agent-session-runtime.md), [Agent loops](agent-loops.md), [Runs and usage ledger](runs-and-usage.md), [CLI/RPC](cli-rpc.md), and [Database persistence](database-persistence.md).
18
+ - **Host authors** use CLI/RPC and `CommandDefinition` as the non-interactive control seam for start/status/cancel/resume.
19
+ - **Core maintainers** own generic `CheckpointStore` and `EventMultiplexer`; workflow node/DAG vocabulary remains outside core.
20
+ - **Security reviewers** use the threat model and design matrix on this page as the acceptance baseline for Plan 057 Tasks 2–7.
21
+
22
+ ## Inventory (2026-07-14 baseline)
23
+
24
+ Static review of `src/agents.ts`, `src/agent-loops.ts`, `src/rpc.ts`, `src/cli-runner.ts`, `src/execution-policy.ts`, `src/contracts.ts`, `src/session-stores.ts`, `src/security.ts`, `packages/coding-security/src/approval.ts`, `packages/session-store-sqlite/**`, `packages/session-store-postgres/**`, and docs under `docs/agent-session-runtime.md`, `docs/agent-events.md`, `docs/agent-loops.md`, `docs/cli-rpc.md`, `docs/session-stores*.md`, `docs/runs-and-usage.md`, `docs/host-security.md`, `docs/tool-execution-primitives.md`, `docs/persistence-credentials-multimodality-primitives.md`, Plans 053–056.
25
+
26
+ ### Orchestration and per-run control (shipped)
27
+
28
+ | Surface | Location | Behavior today | Workflow relevance |
29
+ | --- | --- | --- | --- |
30
+ | `AgentSession` | `src/agents.ts` | `run`, `prompt`, `compact`, `subscribe`, `abort`, `entries`, `checkout`, `fork`, `clone` | One agent session = one workflow **agent node** runtime; branch resume via `leafId` |
31
+ | Run exclusivity | `src/agents.ts` | One active `run()` per session; concurrent runs reject | Workflow scheduler owns one session per agent node or serializes runs per session |
32
+ | `AbortSignal` | `RunOptions.signal`, `session.abort()` | Bridges to assembly, provider, tools, compaction, retry | Workflow cancellation propagates `signal` to in-flight node runs |
33
+ | `AgentLoopStrategy` | `src/agent-loops.ts` | Replaceable per-run turn loop | **Not** multi-node DAG; workflow package orchestrates multiple runs |
34
+ | `singleShotLoop` | `src/agent-loops.ts` | Assemble → generate → tools (optional parallel `toolConcurrency`) → next turn | Default node behavior for agent nodes |
35
+ | `generateValidateReviseLoop` | `src/agent-loops.ts` | Generate → validate → revise with `Artifact*` callbacks | Pattern for validate/repair **within** one node; not cross-node DAG |
36
+ | `LoopContext` | `src/agent-loops.ts` | `assemble`, `generate`, `dispatchToolCall`, `appendMessage`, `emit`, `history`, `signal` | Custom loops can be workflow **function nodes** without reimplementing runtime |
37
+ | `maxToolRounds` / `toolConcurrency` | `AgentConfig` / `RunOptions` / `LoopContext` | Bounded tool turns; opt-in parallel dispatch with index-slot ordering | Workflow sets per-node limits; fan-out/join is workflow-owned |
38
+ | `CommandDefinition` | `src/contracts.ts` | Host RPC commands via `runRpcServer({ commands })` | Workflow package exposes optional `createWorkflowCommands()` for start/status/cancel/resume |
39
+ | Middleware | `src/middleware.ts` | Ordered hooks at provider/input/tool/compaction/retry/session boundaries | Workflow does not need new hooks for v1 |
40
+ | Compaction / retry | `src/compaction.ts`, `src/retry.ts` | Per-session/run policies | Workflow nodes inherit agent/session config; graph-level retry is package-owned |
41
+
42
+ **Frozen boundary:** Core owns single-session run lifecycle, provider turns, tool dispatch, store append, redaction, and `AgentEvent` emission. Multi-node dependency scheduling, typed node I/O mapping, fan-out/join, workflow checkpoints, and workflow run control belong in `@arnilo/prism-workflows`.
43
+
44
+ ### CLI/RPC host seam (shipped)
45
+
46
+ | Surface | Location | Behavior today | Workflow relevance |
47
+ | --- | --- | --- | --- |
48
+ | `runCli` / `prism` bin | `src/cli-runner.ts` | `print`, `json`, `rpc` modes; thin `AgentSession` adapter | Non-interactive hosts stay on RPC/print; workflow control uses `command` RPC |
49
+ | `runRpcServer` | `src/rpc.ts` | LF-delimited JSON stdin/stdout; concurrent commands during active run | Hosts register workflow commands beside session commands |
50
+ | RPC commands | `src/rpc.ts` | `prompt`, `followUp`, `abort`, `state`, `messages`, `setModel`, `compact`, `switchSession`, `forkSession`, `cloneSession`, `checkout`, `command` | Workflow start/status/cancel/resume bind through `command` |
51
+ | Branch handles | `src/rpc.ts` | `handleId`, `sessionId`, `leafId`; fork does not overwrite parent handle | Agent-node resume reuses session/`leafId` from checkpoint metadata |
52
+ | Active-run rules | `src/rpc.ts` | Second `prompt`/`followUp` fails closed; `abort` immediate; events keep prompt `id` | Workflow scheduler must not overlap `run()` on the same session |
53
+ | JSON event mode | `src/cli-runner.ts` | One `{ type: "event", event: AgentEvent }` per line | Reference for structured streaming; workflow emits package-local `WorkflowEvent` |
54
+ | Discovery flags | `src/cli-runner.ts` | Opt-in `--discover`, `--agents-config`; no auto-activate | Workflow hosts wire registries explicitly; no hidden globals |
55
+
56
+ **Host-control decision (replaces TUI for Plan 057):** Feature-complete workflow control is programmatic (`runWorkflow` / `resumeWorkflow` / status helpers) plus optional RPC/`CommandDefinition` bindings. No interactive terminal package ships in this plan.
57
+
58
+ ### Events and observability (shipped)
59
+
60
+ | Surface | Location | Behavior today | Workflow relevance |
61
+ | --- | --- | --- | --- |
62
+ | `AgentEvent` | `src/contracts.ts` | Normalized lifecycle, message, tool, compaction, retry, artifact, error variants | Per-session event stream; workflow may subscribe per agent node |
63
+ | `session.subscribe()` | `src/agents.ts` | Bounded `AsyncIterable<AgentEvent>`; default `maxQueuedEvents: 1024`, `overflow: "close"` | Workflow event adapter sets per-session bounds when observing agent nodes |
64
+ | `event_subscriber_overflow` | `src/agents.ts` | Subscriber closed after overflow notice | Workflow summarizer drops/coalesces; emits `workflow_event_overflow` |
65
+ | `redactAgentEvent` | `src/redaction.ts` | All subscriber/ledger events redacted when redactor active | Workflow persists only redacted node outputs/checkpoints |
66
+ | `RunLedger` | `src/contracts.ts` | Durable `appendRun`, `appendEvent`, `appendToolCall`, `appendUsage` | Workflow run record + per-node run ids; serialized `ledgerChain` (R-004) |
67
+ | Provider/tool metadata | `docs/observability.md` | `provider_turn_*`, `ToolExecutionMetadata` | Workflow progress / node diagnostics |
68
+ | OpenTelemetry adapter | `@arnilo/prism-observability-opentelemetry` | Optional span/metric mapping | Workflow examples may attach |
69
+
70
+ **Final architecture (Task 6):** Core exports generic `createEventMultiplexer<T>()`. `@arnilo/prism-workflows` keeps its domain `WorkflowEvent` union but delegates bounded queues, source fan-in, overflow, abort, and close behavior to the core primitive.
71
+
72
+ ### Approval and execution policy (shipped)
73
+
74
+ | Surface | Location | Behavior today | Workflow relevance |
75
+ | --- | --- | --- | --- |
76
+ | `PermissionPolicy` | `src/security.ts` | `tool:<name>:execute` before validation/execute | Workflow propagates host policy into tool/agent nodes |
77
+ | `ExecutionPolicy` | `src/execution-policy.ts` | `check(action)` → `ExecutionDecision`; `ExecutionAction` with `kind`, `paths`, `command`, `risk` | Dangerous workflow tool nodes attach `workflowId`/`nodeId` in `metadata` |
78
+ | `createCodingApprovalPolicy` | `packages/coding-security` | Roots, read-only, command rules, `approve` callback, cache scopes, timeout/abort | Host supplies `approve`; workflow does not own UI |
79
+ | `CodingApprovalFn` | `packages/coding-security` | `(request) => boolean \| Promise<boolean>` with `signal` | Host implements callback (CLI prompt, RPC, CI deny, etc.) |
80
+ | Tool blocked events | `AgentEvent` | `tool_execution_blocked` with `reason` | Surfaces as node failure / workflow event |
81
+ | MCP / shell trust | `docs/host-security.md` | Host configures transport, bounds, registration | Workflow examples document safe policy wiring |
82
+
83
+ **Gap:** No core `ApprovalHandler` type for non-coding actions. Hosts keep owning approval UX. Workflow package only requires that tool nodes honor existing `ExecutionPolicy` / permission seams with workflow/node metadata.
84
+
85
+ ### Persistence and resume (shipped)
86
+
87
+ | Surface | Location | Behavior today | Workflow relevance |
88
+ | --- | --- | --- | --- |
89
+ | `SessionStore` | `src/contracts.ts` | Atomic append, idempotency, `expectedParentId`, branch fork | Agent-node session history; not workflow graph state |
90
+ | `readBranchPath` | `SessionStore` / `ProductionPersistenceStore` | Ancestor chain without full session scan | Resume agent node at `leafId` |
91
+ | `ProductionPersistenceStore` | `src/contracts.ts` | Cursor queries plus optional generic checkpoint and lease capabilities | Workflow adapter consumes versioned storage without SQL handles |
92
+ | SQLite / Postgres packages | `packages/session-store-*` | Session/run/query persistence + package-owned `prism_checkpoints` / `prism_leases` | Expose durable checkpoint and atomic lease capabilities |
93
+ | `RunRecord` / ownership | `src/contracts.ts` | `tenantId`, `accountId`, `userId`, `idempotencyKey` | Workflow run scoped to tenant; propagate to node runs |
94
+ | `SessionEntry` `kind: "custom"` | `src/contracts.ts` | Opaque `data` payload on branch | Dev-only checkpoint embedding; production uses dedicated workflow tables |
95
+ | Redaction before persist | `src/redaction.ts` | `redactSessionEntry`, `redactRunLedgerRecord` | Checkpoints must redact before write |
96
+
97
+ **Final architecture (Tasks 6–7):** Core exports database-neutral `CheckpointStore` / `LeaseStore` plus memory references. SQLite/PostgreSQL persistence exposes `checkpoints` and `leases`. Workflows adapt checkpoints and use leases for bounded polling, exclusive claims, heartbeat renewal, expiry takeover, durable cancellation, and fenced CAS writes; workflow code owns no database table.
98
+
99
+ ## Capability gaps
100
+
101
+ | ID | Capability | Review rank | Status after Task 0 rework | Owner |
102
+ | --- | --- | ---: | --- | --- |
103
+ | C-009 | Workflow/graph orchestration | 9 | Task 7 shipped durable multi-process coordination (enqueue/claim/renew/takeover/fencing/cancel) | `@arnilo/prism-workflows` |
104
+ | C-012 | Interactive TUI | 12 | **Deferred / out of scope for Plan 057** | Future optional plan/package only |
105
+
106
+ ## Rejected options
107
+
108
+ | Option | Why rejected |
109
+ | --- | --- |
110
+ | Workflow state machine in core | Violates bounded core; domain vocabulary stays in optional package |
111
+ | Full-screen or readline TUI in this plan | User deferred C-012; CLI/RPC already provide host control seams |
112
+ | Core `WorkflowEvent` / DAG types | Host apps without workflows should not import graph contracts |
113
+ | Core global approval UI | Host-owned; workflow only propagates policy metadata |
114
+ | Workflow-specific checkpoint tables | Replaced by generic core `CheckpointStore` implemented by persistence packages |
115
+ | Examples-only checkpoint snippets | Rejected; Task 3 shipped first-party adapters + resume/list/cancel APIs |
116
+
117
+ ## ADR decision table (final through Task 7)
118
+
119
+ | Concern | Option A | Option B | **Chosen** | Rationale |
120
+ | --- | --- | --- | --- | --- |
121
+ | Orchestration location | Core DAG engine | Optional package over `AgentSession` | **Optional package** | Matches review gap #9 and loop-strategy boundary |
122
+ | Node execution | New runtime | Existing `session.run()` per agent node | **`session.run()`** | Reuse redaction, ledger, tools, abort |
123
+ | Validate/repair across nodes | Core workflow DSL | Package graph + `generateValidateReviseLoop` inside nodes | **Package graph** | Cross-node deps in workflow; within-node repair in loop |
124
+ | Checkpoints | Generic core `CheckpointStore` | Workflow-owned SQL adapters | **Core store + package facade** | Reusable capability; persistence packages own storage |
125
+ | Event fan-in | Generic core multiplexer | Package queue duplication | **Core multiplexer + package facade** | One bounded/abort-aware implementation |
126
+ | Distributed ownership | Process-local active map | Generic leases + package coordinator | **LeaseStore + fenced checkpoint CAS** | Atomic claims and monotonic fences prevent split brain across processes |
127
+ | Host control (no TUI) | Interactive terminal package | Public APIs + optional RPC/`CommandDefinition` | **Public APIs + optional commands** | Replaces former TUI Tasks for feature completeness |
128
+ | Approval prompts | Core `ApprovalHandler` | Durable workflow suspension + host `ExecutionPolicy` | **Checkpoint suspension** | Survives restart; approved tool execution still rechecks current host policy |
129
+ | Interactive TUI | Ship in Plan 057 | Defer C-012 | **Defer** | Explicit product decision after Task 0 |
130
+
131
+ ## Locked package adapter contracts (Task 1)
132
+
133
+ These TypeScript shapes are the frozen public contracts for Tasks 2–3. Implementations live in `@arnilo/prism-workflows` only.
134
+
135
+ ### Checkpoint adapter
136
+
137
+ ```ts
138
+ import type { OwnershipScope, SecretRedactor } from "@arnilo/prism";
139
+
140
+ /** Schema version for checkpoint payload layout (package-owned). */
141
+ export const WORKFLOW_CHECKPOINT_SCHEMA_VERSION = 1 as const;
142
+
143
+ export type WorkflowRunStatus =
144
+ | "queued"
145
+ | "running"
146
+ | "succeeded"
147
+ | "failed"
148
+ | "aborted";
149
+
150
+ export interface WorkflowNodeCheckpoint {
151
+ readonly nodeId: string;
152
+ readonly status: "pending" | "ready" | "running" | "succeeded" | "failed" | "skipped" | "aborted";
153
+ /** Redacted, size-bounded node output. Omitted when pending/running/skipped. */
154
+ readonly output?: unknown;
155
+ readonly error?: { readonly message: string; readonly code?: string | number };
156
+ readonly attempt?: number;
157
+ /** Agent-node resume pointers only — never full transcripts. */
158
+ readonly sessionId?: string;
159
+ readonly leafId?: string;
160
+ readonly runId?: string;
161
+ }
162
+
163
+ export interface WorkflowCheckpointValue {
164
+ readonly schemaVersion: typeof WORKFLOW_CHECKPOINT_SCHEMA_VERSION;
165
+ readonly workflowId: string;
166
+ readonly runId: string;
167
+ readonly definitionHash: string;
168
+ readonly status: WorkflowRunStatus;
169
+ readonly readyNodeIds: readonly string[];
170
+ readonly completedNodeIds: readonly string[];
171
+ readonly nodes: Readonly<Record<string, WorkflowNodeCheckpoint>>;
172
+ readonly workflowInput?: unknown;
173
+ readonly createdAt: string;
174
+ readonly updatedAt: string;
175
+ readonly redacted: boolean;
176
+ readonly metadata?: Readonly<Record<string, unknown>>;
177
+ }
178
+
179
+ export interface WorkflowCheckpointSaveInput {
180
+ readonly workflowId: string;
181
+ readonly runId: string;
182
+ /** Monotonic adapter version; conflict on stale write fails closed. */
183
+ readonly version: number;
184
+ readonly ownership?: OwnershipScope;
185
+ readonly value: WorkflowCheckpointValue;
186
+ readonly signal?: AbortSignal;
187
+ }
188
+
189
+ export interface WorkflowCheckpointRecord {
190
+ readonly workflowId: string;
191
+ readonly runId: string;
192
+ readonly version: number;
193
+ readonly ownership?: OwnershipScope;
194
+ readonly value: WorkflowCheckpointValue;
195
+ readonly updatedAt: string;
196
+ }
197
+
198
+ export interface WorkflowCheckpointLoadInput {
199
+ readonly workflowId: string;
200
+ readonly runId: string;
201
+ readonly ownership?: OwnershipScope;
202
+ readonly signal?: AbortSignal;
203
+ }
204
+
205
+ export interface WorkflowCheckpointListInput {
206
+ readonly workflowId?: string;
207
+ readonly ownership?: OwnershipScope;
208
+ readonly status?: WorkflowRunStatus | readonly WorkflowRunStatus[];
209
+ readonly cursor?: string;
210
+ /** Default 100; hard-capped by package (see performance limits). */
211
+ readonly limit?: number;
212
+ readonly signal?: AbortSignal;
213
+ }
214
+
215
+ export interface WorkflowCheckpointListPage {
216
+ readonly items: readonly WorkflowCheckpointRecord[];
217
+ readonly nextCursor?: string;
218
+ }
219
+
220
+ /**
221
+ * Package-local workflow checkpoint facade over core CheckpointStore.
222
+ * Implementations MUST: redact before persist, enforce maxCheckpointBytes,
223
+ * fail closed on tenant/version/schema mismatch, honor AbortSignal.
224
+ */
225
+ export interface WorkflowCheckpointAdapter {
226
+ save(input: WorkflowCheckpointSaveInput): Promise<void>;
227
+ load(input: WorkflowCheckpointLoadInput): Promise<WorkflowCheckpointRecord | null>;
228
+ list?(input: WorkflowCheckpointListInput): Promise<WorkflowCheckpointListPage>;
229
+ delete?(input: WorkflowCheckpointLoadInput): Promise<boolean>;
230
+ }
231
+
232
+ export interface WorkflowCheckpointAdapterOptions {
233
+ readonly maxCheckpointBytes?: number; // default 1 MiB
234
+ readonly maxNodeOutputBytes?: number; // default 4 MiB
235
+ readonly redactor?: SecretRedactor;
236
+ /** Required secrets list when redactor omitted but secrets known. */
237
+ readonly secrets?: readonly (string | undefined)[];
238
+ }
239
+ ```
240
+
241
+ **Final factory contracts (Task 6):**
242
+
243
+ ```ts
244
+ createMemoryWorkflowCheckpoints(options?: WorkflowCheckpointAdapterOptions): WorkflowCheckpointAdapter;
245
+ createWorkflowCheckpoints(options: WorkflowCheckpointAdapterOptions & {
246
+ readonly store: import("@arnilo/prism").CheckpointStore;
247
+ }): WorkflowCheckpointAdapter;
248
+ ```
249
+
250
+ First-party persistence packages own generic `prism_checkpoints` tables keyed by namespace/key. Workflow status and definition data remain inside the bounded/redacted checkpoint value; no workflow-specific SQL schema exists.
251
+
252
+ ### Event merge adapter
253
+
254
+ ```ts
255
+ import type { AgentEvent, AgentSession, SubscribeOptions } from "@arnilo/prism";
256
+
257
+ export type WorkflowEvent =
258
+ | { readonly type: "workflow_started"; readonly workflowId: string; readonly runId: string; readonly timestamp: string }
259
+ | { readonly type: "workflow_finished"; readonly workflowId: string; readonly runId: string; readonly status: WorkflowRunStatus; readonly timestamp: string }
260
+ | { readonly type: "node_started"; readonly workflowId: string; readonly runId: string; readonly nodeId: string; readonly timestamp: string }
261
+ | { readonly type: "node_finished"; readonly workflowId: string; readonly runId: string; readonly nodeId: string; readonly timestamp: string }
262
+ | { readonly type: "node_failed"; readonly workflowId: string; readonly runId: string; readonly nodeId: string; readonly error: { readonly message: string; readonly code?: string | number }; readonly timestamp: string }
263
+ | { readonly type: "node_skipped"; readonly workflowId: string; readonly runId: string; readonly nodeId: string; readonly reason?: string; readonly timestamp: string }
264
+ | { readonly type: "checkpoint_saved"; readonly workflowId: string; readonly runId: string; readonly version: number; readonly timestamp: string }
265
+ | {
266
+ readonly type: "agent_event";
267
+ readonly workflowId: string;
268
+ readonly runId: string;
269
+ readonly nodeId: string;
270
+ readonly sequence: number;
271
+ readonly event: AgentEvent; // already redacted by session.subscribe path
272
+ readonly timestamp: string;
273
+ }
274
+ | {
275
+ readonly type: "workflow_event_overflow";
276
+ readonly workflowId: string;
277
+ readonly runId: string;
278
+ readonly droppedEvents: number;
279
+ readonly maxQueuedEvents: number;
280
+ readonly timestamp: string;
281
+ };
282
+
283
+ export interface WorkflowEventMergeOptions {
284
+ readonly workflowId: string;
285
+ readonly runId: string;
286
+ /** Default 2048. */
287
+ readonly maxQueuedEvents?: number;
288
+ readonly overflow?: "close" | "drop_oldest" | "drop_newest";
289
+ readonly subscribeOptions?: SubscribeOptions; // forwarded per agent session
290
+ readonly signal?: AbortSignal;
291
+ }
292
+
293
+ /**
294
+ * Bounded fan-in of scheduler WorkflowEvents + optional per-node AgentSession
295
+ * subscriptions. Deterministic order by (sequence, nodeId). Not a core type.
296
+ */
297
+ export interface WorkflowEventBus {
298
+ emit(event: WorkflowEvent): void;
299
+ subscribe(): AsyncIterable<WorkflowEvent>;
300
+ observeAgentNode(input: {
301
+ readonly nodeId: string;
302
+ readonly session: AgentSession;
303
+ }): () => void; // unsubscribe / stop observing
304
+ close(): void;
305
+ }
306
+
307
+ export function createWorkflowEventBus(options: WorkflowEventMergeOptions): WorkflowEventBus;
308
+ ```
309
+
310
+ ### Run control + optional RPC commands
311
+
312
+ ```ts
313
+ export interface WorkflowRunHandle {
314
+ readonly workflowId: string;
315
+ readonly runId: string;
316
+ readonly status: WorkflowRunStatus;
317
+ readonly version: number;
318
+ }
319
+
320
+ export interface RunWorkflowOptions {
321
+ readonly concurrency?: number;
322
+ readonly checkpoints?: WorkflowCheckpointAdapter;
323
+ readonly agentFactory?: (agentName: string) => AgentSession | Promise<AgentSession>;
324
+ readonly runLedger?: import("@arnilo/prism").RunLedger;
325
+ readonly ownership?: OwnershipScope;
326
+ readonly redactor?: SecretRedactor;
327
+ readonly signal?: AbortSignal;
328
+ readonly onEvent?: (event: WorkflowEvent) => void;
329
+ readonly eventBus?: WorkflowEventBus;
330
+ readonly executionPolicy?: import("@arnilo/prism").ExecutionPolicy;
331
+ }
332
+
333
+ export function runWorkflow(
334
+ workflow: WorkflowDefinition,
335
+ input: unknown,
336
+ options?: RunWorkflowOptions,
337
+ ): Promise<WorkflowRunHandle & { readonly outputs: Readonly<Record<string, unknown>> }>;
338
+
339
+ export function resumeWorkflow(
340
+ workflow: WorkflowDefinition,
341
+ ref: { readonly runId: string; readonly workflowId?: string },
342
+ options: RunWorkflowOptions & { readonly checkpoints: WorkflowCheckpointAdapter },
343
+ ): Promise<WorkflowRunHandle & { readonly outputs: Readonly<Record<string, unknown>> }>;
344
+
345
+ export function getWorkflowRun(
346
+ checkpoints: WorkflowCheckpointAdapter,
347
+ input: WorkflowCheckpointLoadInput,
348
+ ): Promise<WorkflowCheckpointRecord | null>;
349
+
350
+ export function listWorkflowRuns(
351
+ checkpoints: WorkflowCheckpointAdapter,
352
+ input?: WorkflowCheckpointListInput,
353
+ ): Promise<WorkflowCheckpointListPage>;
354
+
355
+ /** Optional host binding — registers CommandDefinition entries for runRpcServer. */
356
+ export function createWorkflowCommands(input: {
357
+ readonly workflows: Readonly<Record<string, WorkflowDefinition>> | ((id: string) => WorkflowDefinition | undefined);
358
+ readonly checkpoints: WorkflowCheckpointAdapter;
359
+ readonly runOptions?: Omit<RunWorkflowOptions, "checkpoints" | "signal">;
360
+ }): import("@arnilo/prism").CommandDefinition[];
361
+ ```
362
+
363
+ Expected command names (Task 3): `workflow.start`, `workflow.status`, `workflow.list`, `workflow.cancel`, `workflow.resume`.
364
+
365
+ ### Usage sketch
366
+
367
+ ```ts
368
+ import {
369
+ defineWorkflow,
370
+ runWorkflow,
371
+ resumeWorkflow,
372
+ createWorkflowCheckpoints,
373
+ createWorkflowEventBus,
374
+ createWorkflowCommands,
375
+ agentNode,
376
+ functionNode,
377
+ } from "@arnilo/prism-workflows";
378
+ import { runRpcServer } from "@arnilo/prism";
379
+ import { createSqlitePersistence } from "@arnilo/prism-session-store-sqlite";
380
+
381
+ const persistence = createSqlitePersistence({ filename: "prism.db" });
382
+ const checkpoints = createWorkflowCheckpoints({ store: persistence.checkpoints });
383
+
384
+ const research = agentNode({ agent: "researcher", input: (ctx) => ctx.workflowInput });
385
+ const draft = agentNode({ agent: "writer", input: (ctx) => ({ outline: ctx.upstream.research }) });
386
+ const review = functionNode({ execute: async (ctx) => lint(ctx.upstream.draft) });
387
+
388
+ const workflow = defineWorkflow({
389
+ id: "research-draft-review",
390
+ nodes: { research, draft, review },
391
+ edges: [
392
+ ["research", "draft"],
393
+ ["draft", "review"],
394
+ ],
395
+ limits: { maxNodes: 256, maxFanOut: 32, maxConcurrency: 4 },
396
+ });
397
+
398
+ const bus = createWorkflowEventBus({ workflowId: workflow.id, runId: "pending" });
399
+ const result = await runWorkflow(workflow, { topic: "hooks" }, {
400
+ agentFactory: (name) => agents.resolve(name).createSession(),
401
+ checkpoints,
402
+ runLedger: persistence,
403
+ ownership: { tenantId: "t1" },
404
+ signal: ac.signal,
405
+ eventBus: bus,
406
+ onEvent: (event) => sink.push(event),
407
+ });
408
+
409
+ await resumeWorkflow(workflow, { runId: result.runId }, {
410
+ checkpoints,
411
+ agentFactory,
412
+ signal: ac.signal,
413
+ });
414
+
415
+ runRpcServer({
416
+ createSession,
417
+ commands: createWorkflowCommands({ workflows: { [workflow.id]: workflow }, checkpoints }),
418
+ });
419
+ ```
420
+
421
+ **Scheduler:** Kahn topological sort, bounded worker pool, deterministic event ordering by `(sequence, nodeId)`.
422
+
423
+ **Nodes:** `agent` (runs `AgentSession`), `function` (async host fn), `tool` (dispatches registered tool with approval), `conditional` (skips downstream), `fanOut`/`join` (bounded list map).
424
+
425
+ **Checkpoints:** redacted node outputs + ready set + version + agent `sessionId`/`leafId` metadata; resume validates tenant and schema version.
426
+
427
+ **Run control:** `runWorkflow`, `resumeWorkflow`, status/list helpers, cancel via `AbortSignal`; optional `createWorkflowCommands()` for RPC hosts.
428
+
429
+ **Events:** package-local `WorkflowEvent` — not core `AgentEvent`.
430
+
431
+ ## Performance limits (pinned)
432
+
433
+ | Surface | Limit | Default | Rationale |
434
+ | --- | --- | ---: | --- |
435
+ | Workflow `maxNodes` | Hard cap at validate | 1000 | 1k-node stress target in Task 5 |
436
+ | Workflow `maxFanOut` | Per fan-out node | 64 | Prevents unbounded dynamic lists |
437
+ | Workflow `maxConcurrency` | Global worker pool | 8 | Matches typical provider rate limits |
438
+ | Workflow `maxNodeOutputBytes` | Serialized checkpoint output | 4 MiB | Keeps DB rows bounded |
439
+ | Workflow `maxCheckpointBytes` | Full checkpoint blob | 1 MiB | Resume metadata only; not full transcripts |
440
+ | Workflow event buffer | Per run merge queue | 2048 | Coalesce node status; drop with `workflow_event_overflow` |
441
+ | Workflow list/status page size | Status helper default | 100 | Bounded run listing for hosts |
442
+ | Nested depth | Default / hard | 8 / 32 | Prevent recursive composition exhaustion |
443
+ | Shared state | Default / hard bytes | 64 KiB / 512 KiB | Keep node context/checkpoints bounded |
444
+ | State history | Default / hard snapshots | 32 / 128 | Preserve replay state without unbounded history |
445
+ | Replay lineage | Default / hard depth | 8 / 32 | Prevent replay-chain abuse |
446
+ | Schedule input | Default / hard bytes | 256 KiB / 1 MiB | Bound persisted trigger payload |
447
+ | Schedule due claims | Default / hard per poll | 16 / 256 | Bound one poll; idle waits 1s by default |
448
+
449
+ ## Threat model and design matrix
450
+
451
+ | # | Scenario | Owner | Expected behavior |
452
+ | ---: | --- | --- | --- |
453
+ | 1 | Cyclic workflow definition | Workflow package | Validate at `defineWorkflow`; reject before run |
454
+ | 2 | Unbounded fan-out (dynamic list) | Workflow package | Cap `maxFanOut`; fail `node_failed` when exceeded |
455
+ | 3 | Resumed checkpoint tampered (wrong tenant/version) | Workflow adapter | Fail closed; no partial node execution |
456
+ | 4 | Checkpoint contains secrets | Workflow + redactor | Redact before persist; `redacted: true` metadata |
457
+ | 5 | Shell/tool approval during workflow | Host + workflow | Opt-in tool approval suspends before side effects; resume uses ownership + expected-version CAS, then rechecks current `ExecutionPolicy` with workflow/node metadata |
458
+ | 6 | Cancel during node execution | Workflow | `signal` abort → in-flight `session.abort()`; checkpoint marks `aborted` |
459
+ | 7 | Untrusted workflow definition file | Host | Load from trusted path only; schema-validate before `runWorkflow` |
460
+ | 8 | Node output passed to next node | Workflow | Size-bound; type validate; redact at boundary |
461
+ | 9 | Event subscriber overflow while observing agent nodes | Workflow | Throttle/coalesce; emit `workflow_event_overflow` once |
462
+ | 10 | Cross-tenant list/status query | Workflow adapter | Scope by ownership; never return other tenants' runs |
463
+ | 11 | RPC workflow cancel races session abort | Workflow commands | Cancel is idempotent; fails closed if run unknown/unauthorized |
464
+ | 12 | 1000-node workflow checkpoint growth | Workflow | Store ready set + bounded outputs only; no full transcript duplication |
465
+ | 13 | Two reviewers resume one suspension | Workflow adapter | Expected-version CAS claims checkpoint before execution; one succeeds, stale reviewer fails |
466
+ | 14 | Forged/cross-tenant resume | Workflow + host | Ownership and definition hash checked; declared schema requires host validator; payload redacted before persistence |
467
+ | 15 | Duplicate/crashed schedule fire | Workflow schedules | Per-fire lease, deterministic run ID, queued checkpoint idempotency, schedule CAS |
468
+ | 16 | Nested workflow broadens capability | Workflow runner | Child inherits parent tool/agent/policy/ownership/signal seams; bounded inherited depth |
469
+ | 17 | Replay mutates evidence or reuses approval | Workflow replay | New checkpoint + immutable lineage; source untouched; copied approval-bearing path rejected |
470
+ | 18 | State/history resource exhaustion | Workflow runner/checkpoint | Host validation plus state byte/history and aggregate checkpoint ceilings |
471
+
472
+ ## Final primitive decisions (Task 1, superseded where noted by Task 6)
473
+
474
+ | Primitive | Needed in core? | Decision | Evidence |
475
+ | --- | --- | --- | --- |
476
+ | Generic `CheckpointStore` | **Yes (Task 6)** | Core contract/reference memory store; optional `ProductionPersistenceStore.checkpoints`; SQLite/PostgreSQL implementations | Removes raw DB handles and workflow-owned tables while preserving bounded/versioned/owned writes. |
477
+ | Core event multiplexer | **Yes (Task 6)** | `createEventMultiplexer<T>()`; `WorkflowEventBus` delegates fan-in/overflow/abort/close | Removes duplicate queue logic and remains domain-neutral. |
478
+ | Generic `LeaseStore` | **Yes (Task 7)** | Core contract/memory reference; optional `ProductionPersistenceStore.leases`; SQLite/PostgreSQL implementations | Reusable atomic ownership, expiry, opaque claims, and monotonic fencing for coordinators. |
479
+ | Core `ApprovalHandler` | **No** | Host `ExecutionPolicy` / `CodingApprovalFn` with `workflowId`/`nodeId` metadata | Coding-security already owns interactive/async approve callbacks; workflow must not invent a parallel UI type. |
480
+ | Core workflow types | **No** | Stay in `@arnilo/prism-workflows` | Prevents graph vocabulary leaking into non-workflow hosts. |
481
+ | Interactive TUI package | **No (Plan 057)** | Deferred (C-012); APIs + optional RPC commands | CLI/RPC `CommandDefinition` already is the host control seam. |
482
+
483
+ Task 1's original no-core choice was superseded by Task 6 after review. DAG, approval, and TUI decisions are unchanged.
484
+
485
+ ### Generic persistence integration
486
+
487
+ `ProductionPersistenceStore` keeps its adapter-facing query methods and adds no generic SQL executor. Its optional `checkpoints?: CheckpointStore` property is the narrow write capability for versioned blobs. Session `kind: "custom"` entries could embed tiny checkpoint blobs for demos, but they:
488
+
489
+ - couple workflow resume to a single agent branch leaf,
490
+ - cannot list workflow runs across sessions with tenant filters without scanning entries,
491
+ - fight `maxCheckpointBytes` / pagination goals,
492
+ - would pollute session history with scheduler state.
493
+
494
+ Task 6 adds an optional generic capability instead of generic SQL execution:
495
+
496
+ ```ts
497
+ const persistence = createSqlitePersistence({ filename: path });
498
+ const checkpoints = createWorkflowCheckpoints({ store: persistence.checkpoints });
499
+ ```
500
+
501
+ ### Event-multiplexing proof
502
+
503
+ | Requirement | Existing seam | Package responsibility |
504
+ | --- | --- | --- |
505
+ | Per-agent-node observation | `session.subscribe({ maxQueuedEvents, overflow })` | Call with finite bounds; map to `WorkflowEvent.agent_event` |
506
+ | Redaction on stream | `redactAgentEvent` before subscriber push | Do not re-emit raw events; never bypass session redaction |
507
+ | Multi-node fan-in | `createEventMultiplexer<T>()` | `WorkflowEventBus` maps sources and creates `workflow_event_overflow` |
508
+ | Deterministic order | Tool/index-slot ordering is per-session only | Order by `(sequence, nodeId)` in package |
509
+ | Abort | `signal` + `session.abort()` | Close bus; stop observing nodes |
510
+
511
+ The core multiplexer is domain-neutral and now supplies one bounded, abort-aware implementation for workflows and future async-source consumers.
512
+
513
+ ### Adapter conformance matrix (Tasks 3 and 6 tests)
514
+
515
+ | Case | Expected |
516
+ | --- | --- |
517
+ | save → load round-trip (core memory/SQLite/Postgres) | Byte-identical redacted value; version preserved |
518
+ | stale `version` write | Fail closed; prior checkpoint retained |
519
+ | `schemaVersion` mismatch on resume | Fail closed; no node execution |
520
+ | ownership/`tenantId` mismatch on load/list | Fail closed / empty page |
521
+ | value exceeds `maxCheckpointBytes` | Reject before write |
522
+ | node output exceeds `maxNodeOutputBytes` | Reject/omit that output before checkpoint |
523
+ | secrets present + redactor/secrets option | Persisted JSON contains no raw secret; `redacted: true` |
524
+ | `signal` aborted during save/load | Abort without partial corrupt row |
525
+ | list pagination | Honors `limit` (default 100); opaque `nextCursor` |
526
+ | delete (optional) | Idempotent; subsequent load returns `null` |
527
+ | event bus overflow | Emits one `workflow_event_overflow`; respects policy |
528
+ | observe agent node after session redaction | `agent_event` payload already redacted |
529
+
530
+ ### Core primitive boundary
531
+
532
+ Core owns only namespace/key/version/ownership checkpoint operations and generic async event fan-in. Workflow schema validation, redaction limits, statuses, node state, and `WorkflowEvent` payloads remain package-local.
533
+
534
+ ## Implementation example
535
+
536
+ ```ts
537
+ import {
538
+ createAgent,
539
+ createMemorySessionStore,
540
+ createMockProvider,
541
+ providerDone,
542
+ providerTextDelta,
543
+ } from "@arnilo/prism";
544
+
545
+ // Workflow builds on these seams today — no new core imports required for prototyping.
546
+
547
+ const agent = createAgent({
548
+ model: { provider: "mock", model: "demo" },
549
+ provider: createMockProvider([providerTextDelta("Hello"), providerDone()]),
550
+ store: createMemorySessionStore(),
551
+ });
552
+
553
+ const session = agent.createSession({ id: "s1" });
554
+ await session.run("Hi", { signal: AbortSignal.timeout(60_000) });
555
+
556
+ // RPC hosts embed the same runtime and can later register createWorkflowCommands():
557
+ // await runRpcServer({ stdin, stdout, createSession: () => agent.createSession(), commands });
558
+ ```
559
+
560
+ ## Extension and configuration notes
561
+
562
+ - `@arnilo/prism-workflows` is an optional workspace member; core `package.json` does not depend on it.
563
+ - Workflow agent nodes call public `AgentSession` APIs only; no imports from `src/agents.ts` internals.
564
+ - Workflow checkpoints adapt `ProductionPersistenceStore.checkpoints` (or any `CheckpointStore`); no raw database handles enter the workflow package.
565
+ - Multimodal and credential packages from Plan 056 compose unchanged in workflow examples (Task 4).
566
+ - `@arnilo/prism-workflows` is available directly and through `prism-sdk`/`prism-all`; installation does not start a worker or workflow.
567
+ - C-012 interactive TUI remains a future optional package if needed; it is not required for workflow feature completeness.
568
+
569
+ ## Related APIs
570
+
571
+ - [Agent/session runtime](agent-session-runtime.md): single-session run surface workflow nodes call.
572
+ - [Agent loops](agent-loops.md): within-node validate/revise; custom `AgentLoopStrategy` for function-equivalent behavior.
573
+ - [Agent events](agent-events.md): per-session stream workflow may observe/merge.
574
+ - [CLI/RPC](cli-rpc.md): non-interactive host seam for optional workflow commands.
575
+ - [Runs and usage ledger](runs-and-usage.md): durable audit for workflow and agent runs.
576
+ - [Database persistence](database-persistence.md): optional generic `CheckpointStore` capability implemented by first-party persistence adapters.
577
+ - [Persistence, credentials, and multimodality primitives](persistence-credentials-multimodality-primitives.md): Plan 056 inventory baseline.
578
+ - [Tool execution primitives](tool-execution-primitives.md): `ExecutionPolicy` and approval pattern.
579
+ - [Host security guide](host-security.md): fail-closed checklist for workflow hosts.
580
+ - [Performance limits](performance.md): subscriber queue defaults workflow tightens.
581
+ - [Review coverage (2026-07-14)](review-coverage-2026-07-14.md): C-009/C-012 traceability.
@@ -0,0 +1,5 @@
1
+ # Workflow and TUI primitives
2
+
3
+ Plan 057 no longer includes interactive TUI work. The Task 0 inventory and frozen workflow design live in [Workflow orchestration primitives](workflow-orchestration-primitives.md).
4
+
5
+ **C-012 (interactive TUI)** is deferred / out of scope for Plan 057. Workflow host control uses public APIs and optional RPC/`CommandDefinition` bindings instead.