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