@arnilo/prism 0.3.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/CHANGELOG.md +50 -1
  2. package/README.md +42 -62
  3. package/dist/agent-run-lifecycle.js +4 -0
  4. package/dist/agent-run-state.d.ts +5 -2
  5. package/dist/agent-run-state.js +18 -8
  6. package/dist/agent-session/session/assemble.d.ts +6 -0
  7. package/dist/agent-session/session/assemble.js +391 -0
  8. package/dist/agent-session/session/persist.d.ts +28 -0
  9. package/dist/agent-session/session/persist.js +166 -0
  10. package/dist/agent-session/session/provider-round.d.ts +6 -0
  11. package/dist/agent-session/session/provider-round.js +231 -0
  12. package/dist/agent-session/session/tool-round.d.ts +31 -0
  13. package/dist/agent-session/session/tool-round.js +473 -0
  14. package/dist/agent-session/session/types.d.ts +115 -0
  15. package/dist/agent-session/session/types.js +5 -0
  16. package/dist/agent-session/session.d.ts +54 -41
  17. package/dist/agent-session/session.js +23 -1132
  18. package/dist/capture.d.ts +63 -0
  19. package/dist/capture.js +67 -0
  20. package/dist/cli-dev.d.ts +29 -0
  21. package/dist/cli-dev.js +52 -0
  22. package/dist/cli-init.d.ts +34 -3
  23. package/dist/cli-init.js +192 -24
  24. package/dist/cli-runner.d.ts +6 -2
  25. package/dist/cli-runner.js +57 -10
  26. package/dist/content.d.ts +3 -3
  27. package/dist/content.js +3 -1
  28. package/dist/contracts-core/agent.d.ts +8 -0
  29. package/dist/contracts-core/batch.d.ts +97 -0
  30. package/dist/contracts-core/batch.js +65 -0
  31. package/dist/contracts-core/content.d.ts +72 -1
  32. package/dist/contracts-core/embeddings.d.ts +30 -0
  33. package/dist/contracts-core/embeddings.js +17 -0
  34. package/dist/contracts-core/images.d.ts +60 -0
  35. package/dist/contracts-core/images.js +17 -0
  36. package/dist/contracts-core/moderation.d.ts +46 -0
  37. package/dist/contracts-core/moderation.js +34 -0
  38. package/dist/contracts-core/speech.d.ts +39 -0
  39. package/dist/contracts-core/speech.js +17 -0
  40. package/dist/contracts-core/transcription.d.ts +48 -0
  41. package/dist/contracts-core/transcription.js +17 -0
  42. package/dist/contracts-core/video.d.ts +61 -0
  43. package/dist/contracts-core/video.js +17 -0
  44. package/dist/contracts-core.d.ts +7 -0
  45. package/dist/contracts-core.js +7 -0
  46. package/dist/contracts-protocol.d.ts +18 -0
  47. package/dist/contracts-run-state.d.ts +1 -2
  48. package/dist/index.d.ts +7 -3
  49. package/dist/index.js +5 -3
  50. package/dist/input.d.ts +8 -0
  51. package/dist/input.js +4 -0
  52. package/dist/node/agent-definitions.d.ts +1 -8
  53. package/dist/node/agent-definitions.js +0 -34
  54. package/dist/node/settings.d.ts +0 -1
  55. package/dist/node/settings.js +0 -5
  56. package/dist/pinned-fetch.js +29 -3
  57. package/dist/provider-events.js +3 -4
  58. package/dist/providers/media.d.ts +1 -2
  59. package/dist/providers/media.js +1 -4
  60. package/dist/rpc.d.ts +1 -1
  61. package/dist/rpc.js +4 -4
  62. package/dist/testing/persistence-schema.d.ts +1 -1
  63. package/dist/testing/persistence-schema.js +32 -28
  64. package/dist/testing/provider-conformance.d.ts +114 -5
  65. package/dist/testing/provider-conformance.js +342 -0
  66. package/dist/testing/tool-conformance.d.ts +25 -0
  67. package/dist/testing/tool-conformance.js +128 -1
  68. package/dist/testing/tool-effect-store-conformance.d.ts +0 -1
  69. package/dist/testing/tool-effect-store-conformance.js +0 -3
  70. package/dist/thinking.d.ts +48 -9
  71. package/dist/thinking.js +134 -8
  72. package/dist/tool-search.d.ts +76 -0
  73. package/dist/tool-search.js +199 -0
  74. package/docs/0.1.0-readiness.md +3 -3
  75. package/docs/a2a.md +2 -2
  76. package/docs/acp-agent.md +1 -1
  77. package/docs/acp.md +3 -3
  78. package/docs/ag-ui-adoption.md +1 -1
  79. package/docs/ag-ui.md +1 -2
  80. package/docs/agent-definitions.md +1 -1
  81. package/docs/agent-events.md +5 -5
  82. package/docs/agent-identity.md +13 -2
  83. package/docs/audit-export.md +3 -3
  84. package/docs/batch-jobs.md +120 -0
  85. package/docs/browser-automation.md +5 -5
  86. package/docs/caveman.md +2 -2
  87. package/docs/cli-rpc.md +43 -9
  88. package/docs/coding-agent-tools.md +19 -19
  89. package/docs/coding-review-and-diagnostics.md +2 -2
  90. package/docs/coding-security.md +5 -5
  91. package/docs/coding-tools.md +82 -0
  92. package/docs/coding-workspaces.md +2 -2
  93. package/docs/compaction-and-retry.md +2 -2
  94. package/docs/compaction-llm.md +4 -4
  95. package/docs/compaction-observational-memory.md +3 -3
  96. package/docs/computer-use-linux.md +13 -2
  97. package/docs/context-and-skills.md +3 -1
  98. package/docs/conversations.md +4 -4
  99. package/docs/core.md +85 -0
  100. package/docs/credential-storage.md +12 -8
  101. package/docs/credentials-and-redaction.md +1 -1
  102. package/docs/data-classification.md +1 -1
  103. package/docs/database-persistence.md +7 -3
  104. package/docs/dev-inspector.md +103 -0
  105. package/docs/device-adapters.md +2 -2
  106. package/docs/diagrams.md +247 -0
  107. package/docs/document-reader.md +6 -6
  108. package/docs/documents.md +214 -0
  109. package/docs/embeddings.md +112 -0
  110. package/docs/enterprise-postgres-state.md +7 -7
  111. package/docs/evaluations.md +41 -7
  112. package/docs/extensions.md +3 -3
  113. package/docs/forge-integration.md +3 -3
  114. package/docs/graft.md +5 -5
  115. package/docs/guardrails.md +2 -2
  116. package/docs/host-security.md +16 -15
  117. package/docs/image-generation.md +129 -0
  118. package/docs/impeccable.md +7 -5
  119. package/docs/index.md +84 -46
  120. package/docs/indexed-code-search.md +2 -2
  121. package/docs/language-intelligence.md +4 -4
  122. package/docs/live-testing.md +126 -0
  123. package/docs/mcp-tools.md +44 -13
  124. package/docs/middleware-hooks.md +1 -1
  125. package/docs/migrate-to-0.4.md +312 -0
  126. package/docs/migrate-to-0.5.md +122 -0
  127. package/docs/migration.md +51 -1
  128. package/docs/model-registry.md +38 -0
  129. package/docs/model-routing.md +6 -6
  130. package/docs/moderation.md +117 -0
  131. package/docs/multi-agent-patterns.md +177 -0
  132. package/docs/multimodal-content.md +27 -3
  133. package/docs/obscura.md +12 -12
  134. package/docs/observability.md +32 -7
  135. package/docs/openapi-tools.md +14 -4
  136. package/docs/operations.md +11 -0
  137. package/docs/performance.md +30 -10
  138. package/docs/persistence-credentials-multimodality-primitives.md +7 -7
  139. package/docs/policy-and-audit.md +18 -8
  140. package/docs/ponytail.md +3 -3
  141. package/docs/postgres-persistence.md +5 -5
  142. package/docs/process-sessions.md +2 -2
  143. package/docs/prompt-registry.md +106 -0
  144. package/docs/provider-caching.md +36 -32
  145. package/docs/provider-conformance.md +24 -2
  146. package/docs/provider-packages.md +58 -22
  147. package/docs/provider-primitives.md +5 -5
  148. package/docs/provider-request-policies.md +1 -1
  149. package/docs/providers/ai-sdk.md +18 -6
  150. package/docs/providers/alibaba.md +10 -6
  151. package/docs/providers/anthropic.md +10 -6
  152. package/docs/providers/azure.md +20 -4
  153. package/docs/providers/bedrock.md +18 -3
  154. package/docs/providers/clinepass.md +7 -3
  155. package/docs/providers/commandcode.md +253 -0
  156. package/docs/providers/deepseek.md +7 -3
  157. package/docs/providers/google.md +8 -4
  158. package/docs/providers/hyper.md +284 -0
  159. package/docs/providers/kimi.md +7 -3
  160. package/docs/providers/neuralwatt.md +12 -8
  161. package/docs/providers/ollama.md +18 -3
  162. package/docs/providers/openai-compatible.md +5 -1
  163. package/docs/providers/openai.md +9 -5
  164. package/docs/providers/opencode-go.md +8 -4
  165. package/docs/providers/openrouter.md +8 -4
  166. package/docs/providers/vertex.md +21 -5
  167. package/docs/providers/xai.md +7 -3
  168. package/docs/providers/zai.md +7 -3
  169. package/docs/rag.md +31 -9
  170. package/docs/release-and-install.md +181 -76
  171. package/docs/resource-loading.md +1 -1
  172. package/docs/runs-and-usage.md +28 -3
  173. package/docs/server.md +94 -5
  174. package/docs/settings-auth-trust-security.md +7 -5
  175. package/docs/sheets.md +229 -0
  176. package/docs/speech.md +126 -0
  177. package/docs/sqlite-persistence.md +4 -4
  178. package/docs/supervisors.md +4 -3
  179. package/docs/thinking-and-reasoning.md +93 -60
  180. package/docs/tool-conformance.md +28 -3
  181. package/docs/tool-execution-primitives.md +8 -8
  182. package/docs/tools.md +32 -5
  183. package/docs/web-tools.md +3 -3
  184. package/docs/wiki.md +7 -7
  185. package/docs/work-artifacts-and-review.md +17 -6
  186. package/docs/work-connectors.md +4 -4
  187. package/docs/work-tools.md +5 -5
  188. package/docs/workflow-orchestration-primitives.md +35 -11
  189. package/docs/workflows.md +74 -13
  190. package/docs/working-and-semantic-memory.md +53 -5
  191. package/package.json +14 -31
  192. package/templates/README.md +23 -0
  193. package/templates/deep-research/README.md.tmpl +47 -0
  194. package/templates/deep-research/env.example.tmpl +12 -0
  195. package/templates/deep-research/gitignore.tmpl +7 -0
  196. package/templates/deep-research/manifest.json +12 -0
  197. package/templates/deep-research/package.json.tmpl +23 -0
  198. package/templates/deep-research/src/agent.ts.tmpl +81 -0
  199. package/templates/deep-research/src/index.ts.tmpl +53 -0
  200. package/templates/deep-research/src/tests/research.test.ts.tmpl +114 -0
  201. package/templates/deep-research/src/tools.ts.tmpl +86 -0
  202. package/templates/deep-research/src/types.ts.tmpl +45 -0
  203. package/templates/deep-research/src/workflow.ts.tmpl +156 -0
  204. package/templates/deep-research/tsconfig.json.tmpl +15 -0
  205. package/templates/init/manifest.json +5 -0
  206. package/templates/init/package.json.tmpl +2 -1
  207. package/templates/init/providers.json +40 -24
  208. package/docs/antigravity-agent.md +0 -207
@@ -1,6 +1,6 @@
1
1
  # Work tools
2
2
 
3
- Optional `@arnilo/prism-work-tools` package: identity-scoped Microsoft 365 and Google Workspace connectors. Host-pinned CLI binaries only; hard-coded `execFile` argv templates; draft-then-approve mutations; side-effect idempotency; shared mail/calendar/file/task result shapes.
3
+ Optional `@arnilo/prism-core/integrations/work` package: identity-scoped Microsoft 365 and Google Workspace connectors. Host-pinned CLI binaries only; hard-coded `execFile` argv templates; draft-then-approve mutations; side-effect idempotency; shared mail/calendar/file/task result shapes.
4
4
 
5
5
  ## When to use
6
6
 
@@ -9,7 +9,7 @@ Use when agents must read or mutate tenant mail/calendar/files/tasks through the
9
9
  ## Install
10
10
 
11
11
  ```bash
12
- npm install @arnilo/prism-work-tools
12
+ npm install @arnilo/prism-core/integrations/work
13
13
  # host separately:
14
14
  # npm i -g @pnp/cli-microsoft365
15
15
  # npm i -g @googleworkspace/cli
@@ -23,8 +23,8 @@ import {
23
23
  createMicrosoft365CliAdapter,
24
24
  createGoogleWorkspaceCliAdapter,
25
25
  createMemoryIdempotencyStore,
26
- } from "@arnilo/prism-work-tools";
27
- // or: import { createGoogleWorkspaceCliAdapter } from "@arnilo/prism-work-tools/google-workspace";
26
+ } from "@arnilo/prism-core/integrations/work";
27
+ // or: import { createGoogleWorkspaceCliAdapter } from "@arnilo/prism-core/integrations/work/google-workspace";
28
28
 
29
29
  const microsoft365 = createMicrosoft365CliAdapter({
30
30
  binary: process.env.M365_BIN!,
@@ -145,7 +145,7 @@ Approved mutations require core-derived `context.idempotencyKey` and a configure
145
145
  ## Security
146
146
 
147
147
  - Require host-verified `AgentIdentity`; no cross-identity configDir reuse.
148
- - Connector tokens (0.0.14): an optional `tokenProvider` resolves a per-identity access token into an env var per call — never argv, never model context. A missing/expired/revoked/cross-identity/wrong-tenant token fails the call closed before any exec. Refresh is late-bound and single-flighted per account (no refresh storm under reconnect). Build one with `createOAuthWorkTokenProvider()` from `@arnilo/prism-credentials-node`.
148
+ - Connector tokens (0.0.14): an optional `tokenProvider` resolves a per-identity access token into an env var per call — never argv, never model context. A missing/expired/revoked/cross-identity/wrong-tenant token fails the call closed before any exec. Refresh is late-bound and single-flighted per account (no refresh storm under reconnect). Build one with `createOAuthWorkTokenProvider()` from `@arnilo/prism-core/credentials/node`.
149
149
  - External mail recipients fail closed unless `externalRecipients.allow` returns true.
150
150
  - Anonymous / `anyone` sharing denied.
151
151
  - CLI stdout/stderr capped (linear chunk capture, killed/rejected before bytes beyond the cap are retained); NDJSON page streams strictly parsed and page-capped; process killed on timeout/abort/overflow.
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## What it does
4
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`.
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-core/runtime/workflows`.
6
6
 
7
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
8
 
@@ -12,6 +12,8 @@ Interactive TUI (**C-012**) is **out of scope** for Plan 057 and deferred. Workf
12
12
 
13
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
14
 
15
+ **Plan 045 Task 2 addendum (2026-08-31):** loop nodes keep one acyclic graph node while durable checkpoints append bounded, versioned iteration records. A tool sub-step can suspend before side effects; approved resume re-enters only the incomplete iteration. `node_iteration_started` / `node_iteration_finished` expose stable iteration IDs and bounded/redacted outputs. Replay starts a fresh cursor and emits new iteration events. A host saga treats the loop as one aggregate step and compensates its iteration IDs in reverse order; no workflow-specific SQL or implicit saga coupling is added.
16
+
15
17
  ## When to use it
16
18
 
17
19
  - **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).
@@ -39,7 +41,7 @@ Static review of `src/agents.ts`, `src/agent-loops.ts`, `src/rpc.ts`, `src/cli-r
39
41
  | Middleware | `src/middleware.ts` | Ordered hooks at provider/input/tool/compaction/retry/session boundaries | Workflow does not need new hooks for v1 |
40
42
  | 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
43
 
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`.
44
+ **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-core/runtime/workflows`.
43
45
 
44
46
  ### CLI/RPC host seam (shipped)
45
47
 
@@ -65,9 +67,9 @@ Static review of `src/agents.ts`, `src/agent-loops.ts`, `src/rpc.ts`, `src/cli-r
65
67
  | `redactAgentEvent` | `src/redaction.ts` | All subscriber/ledger events redacted when redactor active | Workflow persists only redacted node outputs/checkpoints |
66
68
  | `RunLedger` | `src/contracts.ts` | Durable `appendRun`, `appendEvent`, `appendToolCall`, `appendUsage` | Workflow run record + per-node run ids; serialized `ledgerChain` (R-004) |
67
69
  | 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 |
70
+ | OpenTelemetry adapter | `@arnilo/prism-core/governance/observability` | Optional span/metric mapping | Workflow examples may attach |
69
71
 
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.
72
+ **Final architecture (Task 6):** Core exports generic `createEventMultiplexer<T>()`. `@arnilo/prism-core/runtime/workflows` keeps its domain `WorkflowEvent` union but delegates bounded queues, source fan-in, overflow, abort, and close behavior to the core primitive.
71
73
 
72
74
  ### Approval and execution policy (shipped)
73
75
 
@@ -100,7 +102,7 @@ Static review of `src/agents.ts`, `src/agent-loops.ts`, `src/rpc.ts`, `src/cli-r
100
102
 
101
103
  | ID | Capability | Review rank | Status after Task 0 rework | Owner |
102
104
  | --- | --- | ---: | --- | --- |
103
- | C-009 | Workflow/graph orchestration | 9 | Task 7 shipped durable multi-process coordination (enqueue/claim/renew/takeover/fencing/cancel) | `@arnilo/prism-workflows` |
105
+ | C-009 | Workflow/graph orchestration | 9 | Task 7 shipped durable multi-process coordination (enqueue/claim/renew/takeover/fencing/cancel) | `@arnilo/prism-core/runtime/workflows` |
104
106
  | C-012 | Interactive TUI | 12 | **Deferred / out of scope for Plan 057** | Future optional plan/package only |
105
107
 
106
108
  ## Rejected options
@@ -130,7 +132,7 @@ Static review of `src/agents.ts`, `src/agent-loops.ts`, `src/rpc.ts`, `src/cli-r
130
132
 
131
133
  ## Locked package adapter contracts (Task 1)
132
134
 
133
- These TypeScript shapes are the frozen public contracts for Tasks 2–3. Implementations live in `@arnilo/prism-workflows` only.
135
+ These TypeScript shapes are the frozen public contracts for Tasks 2–3. Implementations live in `@arnilo/prism-core/runtime/workflows` only.
134
136
 
135
137
  ### Checkpoint adapter
136
138
 
@@ -139,6 +141,7 @@ import type { OwnershipScope, SecretRedactor } from "@arnilo/prism";
139
141
 
140
142
  /** Schema version for checkpoint payload layout (package-owned). */
141
143
  export const WORKFLOW_CHECKPOINT_SCHEMA_VERSION = 1 as const;
144
+ export const WORKFLOW_LOOP_ITERATION_SCHEMA_VERSION = 1 as const;
142
145
 
143
146
  export type WorkflowRunStatus =
144
147
  | "queued"
@@ -158,6 +161,16 @@ export interface WorkflowNodeCheckpoint {
158
161
  readonly sessionId?: string;
159
162
  readonly leafId?: string;
160
163
  readonly runId?: string;
164
+ /** Optional additive loop cursor/ledger; absent on legacy checkpoints. */
165
+ readonly iteration?: number;
166
+ readonly lastOutput?: unknown;
167
+ readonly iterations?: readonly {
168
+ readonly schemaVersion: typeof WORKFLOW_LOOP_ITERATION_SCHEMA_VERSION;
169
+ readonly iteration: number;
170
+ readonly iterationId: string;
171
+ readonly done: boolean;
172
+ readonly output?: unknown;
173
+ }[];
161
174
  }
162
175
 
163
176
  export interface WorkflowCheckpointValue {
@@ -259,6 +272,17 @@ export type WorkflowEvent =
259
272
  | { readonly type: "workflow_finished"; readonly workflowId: string; readonly runId: string; readonly status: WorkflowRunStatus; readonly timestamp: string }
260
273
  | { readonly type: "node_started"; readonly workflowId: string; readonly runId: string; readonly nodeId: string; readonly timestamp: string }
261
274
  | { readonly type: "node_finished"; readonly workflowId: string; readonly runId: string; readonly nodeId: string; readonly timestamp: string }
275
+ | {
276
+ readonly type: "node_iteration_started" | "node_iteration_finished";
277
+ readonly workflowId: string;
278
+ readonly runId: string;
279
+ readonly nodeId: string;
280
+ readonly iteration: number;
281
+ readonly iterationId: string;
282
+ readonly done?: boolean;
283
+ readonly output?: unknown;
284
+ readonly timestamp: string;
285
+ }
262
286
  | { 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
287
  | { readonly type: "node_skipped"; readonly workflowId: string; readonly runId: string; readonly nodeId: string; readonly reason?: string; readonly timestamp: string }
264
288
  | { readonly type: "checkpoint_saved"; readonly workflowId: string; readonly runId: string; readonly version: number; readonly timestamp: string }
@@ -374,9 +398,9 @@ import {
374
398
  createWorkflowCommands,
375
399
  agentNode,
376
400
  functionNode,
377
- } from "@arnilo/prism-workflows";
401
+ } from "@arnilo/prism-core/runtime/workflows";
378
402
  import { runRpcServer } from "@arnilo/prism";
379
- import { createSqlitePersistence } from "@arnilo/prism-session-store-sqlite";
403
+ import { createSqlitePersistence } from "@arnilo/prism-core/sessions/sqlite";
380
404
 
381
405
  const persistence = createSqlitePersistence({ filename: "prism.db" });
382
406
  const checkpoints = createWorkflowCheckpoints({ store: persistence.checkpoints });
@@ -478,7 +502,7 @@ runRpcServer({
478
502
  | Core event multiplexer | **Yes (Task 6)** | `createEventMultiplexer<T>()`; `WorkflowEventBus` delegates fan-in/overflow/abort/close | Removes duplicate queue logic and remains domain-neutral. |
479
503
  | 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. |
480
504
  | 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. |
481
- | Core workflow types | **No** | Stay in `@arnilo/prism-workflows` | Prevents graph vocabulary leaking into non-workflow hosts. |
505
+ | Core workflow types | **No** | Stay in `@arnilo/prism-core/runtime/workflows` | Prevents graph vocabulary leaking into non-workflow hosts. |
482
506
  | Interactive TUI package | **No (Plan 057)** | Deferred (C-012); APIs + optional RPC commands | CLI/RPC `CommandDefinition` already is the host control seam. |
483
507
 
484
508
  Task 1's original no-core choice was superseded by Task 6 after review. DAG, approval, and TUI decisions are unchanged.
@@ -560,11 +584,11 @@ await session.run("Hi", { signal: AbortSignal.timeout(60_000) });
560
584
 
561
585
  ## Extension and configuration notes
562
586
 
563
- - `@arnilo/prism-workflows` is an optional workspace member; core `package.json` does not depend on it.
587
+ - `@arnilo/prism-core/runtime/workflows` is an optional workspace member; core `package.json` does not depend on it.
564
588
  - Workflow agent nodes call public `AgentSession` APIs only; no imports from `src/agents.ts` internals.
565
589
  - Workflow checkpoints adapt `ProductionPersistenceStore.checkpoints` (or any `CheckpointStore`); no raw database handles enter the workflow package.
566
590
  - Multimodal and credential packages from Plan 056 compose unchanged in workflow examples (Task 4).
567
- - `@arnilo/prism-workflows` is available directly and through `prism-sdk`/`prism-all`; installation does not start a worker or workflow.
591
+ - `@arnilo/prism-core/runtime/workflows` is available directly and through `prism-sdk`/`prism-all`; installation does not start a worker or workflow.
568
592
  - C-012 interactive TUI remains a future optional package if needed; it is not required for workflow feature completeness.
569
593
 
570
594
  ## Related APIs
package/docs/workflows.md CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  ## What it does
4
4
 
5
- `@arnilo/prism-workflows` is an optional package for typed, bounded DAG orchestration over Prism sessions, tools, events, and persistence seams. Hosts define acyclic workflows with agent/function/tool/conditional/fan-out/join/nested-workflow nodes; the package runs a Kahn-style scheduler with a bounded worker pool, emits package-local `WorkflowEvent`s, checkpoints progress, can coordinate queued runs across multiple host processes using durable leases and fencing, and can run bounded linear sagas with durable compensation.
5
+ `@arnilo/prism-core/runtime/workflows` is an optional package for typed, bounded DAG orchestration over Prism sessions, tools, events, and persistence seams. Hosts define acyclic workflows with agent/function/tool/conditional/fan-out/join/nested-workflow/loop nodes; the package runs a Kahn-style scheduler with a bounded worker pool, emits package-local `WorkflowEvent`s, checkpoints progress, can coordinate queued runs across multiple host processes using durable leases and fencing, and can run bounded linear sagas with durable compensation.
6
6
 
7
7
  Primary exports:
8
8
 
9
9
  | Export | Purpose |
10
10
  | --- | --- |
11
11
  | `defineWorkflow` / `buildGraph` | Validate definitions (acyclicity, edge refs, limits) and build deterministic successor/indegree maps |
12
- | `agentNode`, `functionNode`, `toolNode`, `conditionalNode`, `fanOutNode`, `joinNode`, `workflowNode` | Typed node factories, including composition through the same runner |
12
+ | `agentNode`, `functionNode`, `loopNode`, `toolNode`, `conditionalNode`, `fanOutNode`, `joinNode`, `workflowNode` | Typed node factories, including bounded iterative refinement and composition through the same runner |
13
13
  | `runWorkflow` / `resumeWorkflow` / `suspend` / `replayWorkflow` | Execute, durably suspend, exactly-once resume, or create an immutable-lineage replay from a succeeded node |
14
14
  | `createMemoryWorkflowCheckpoints` | In-process `WorkflowCheckpointAdapter` over core `createMemoryCheckpointStore()` |
15
15
  | `createWorkflowCheckpoints` | Adapt core `CheckpointStore` (including SQLite/PostgreSQL persistence capabilities) to workflow checkpoint shapes |
@@ -21,7 +21,7 @@ Primary exports:
21
21
  | `createWorkflowSchedules` | Explicit ownership-scoped one-time/interval/host-calculated schedules over existing checkpoint/lease stores |
22
22
  | `createProactiveScheduleCapabilities` | Scoped, expiring, revocable capability tokens that enable proactive schedules; revocation stops firing fail-closed |
23
23
 
24
- Included through `@arnilo/prism-sdk` and `@arnilo/prism-all`; installing either profile does not start workflows. Interactive TUI is out of scope (C-012 deferred).
24
+ Included through the `@arnilo/prism` / `@arnilo/prism-core` family packages; installing them does not start workflows. Interactive TUI is out of scope (C-012 deferred).
25
25
 
26
26
  ## When to use it
27
27
 
@@ -50,8 +50,22 @@ Use `defineSaga`/`runSaga` for a linear business sequence whose remote effects n
50
50
  | `limits.maxStateBytes` / hard cap | 64 KiB / 512 KiB |
51
51
  | `limits.maxStateHistory` / hard cap | 32 / 128 state snapshots; updates stop before evidence would be discarded |
52
52
  | `limits.maxReplayDepth` / hard cap | 8 / 32 lineage generations |
53
+ | loop `maxIterations` | Required per loop / hard cap 64 |
53
54
  | `state.initial` / `state.schema` | Initial shared JSON object and optional host-validated schema |
54
55
 
56
+ ### Node kinds
57
+
58
+ | Kind | Factory | Behavior |
59
+ | --- | --- | --- |
60
+ | `agent` | `agentNode` | Runs `AgentSession` from `agentFactory` |
61
+ | `function` | `functionNode` | Runs one host async function |
62
+ | `loop` | `loopNode` | Runs one bounded inline or function/tool body repeatedly until `until(ctx)` is true |
63
+ | `tool` | `toolNode` | Dispatches one registered tool, optionally behind durable approval |
64
+ | `conditional` | `conditionalNode` | Evaluates a predicate and skips configured successors |
65
+ | `fan_out` | `fanOutNode` | Maps a bounded list with workflow concurrency |
66
+ | `join` | `joinNode` | Reduces an upstream array |
67
+ | `workflow` | `workflowNode` | Runs a nested workflow with inherited capabilities |
68
+
55
69
  All workflow limits and runtime `concurrency` reject non-safe integers, zero, negatives, NaN, `Infinity`, and values above the named hard cap. Node retries allow 0–100; an explicit node timeout allows 1–86,400,000 ms. Omitting `timeoutMs` remains an explicit host choice.
56
70
 
57
71
  `runWorkflow(workflow, input, options?)`:
@@ -100,7 +114,7 @@ Saga statuses are `running → completed`, `running → compensating → compens
100
114
 
101
115
  `createWorkflowSchedules({ store, leases, checkpoints, workflows, ownership, ownerId, calculators? })` is inert until its host calls `pollOnce()` or `run({ signal })`). Ownership requires `tenantId` plus `accountId` or `userId`. Methods are `create`, `get`, `list`, `pause`, `resume`, `trigger`, `delete`, `pollOnce`, and `run`. A record has one required `nextRunAt`, optional fixed `intervalMs` or registered `calculatorId` (never both), bounded input/metadata, status, version, and last-fire attribution. Manual trigger requires an idempotency key. Scheduled run IDs derive from schedule ID plus fire timestamp, so retry after enqueue-before-advance finds the same queued checkpoint instead of duplicating it. Defaults: page 100/hard 500, due claims 16/hard 256, input 256 KiB/hard 1 MiB, poll 1s, fire lease 30s.
102
116
 
103
- `createProactiveScheduleCapabilities({ schedules, store, ownership, ownerId, defaultTtlMs?, maxTtlMs?, onCapability? })` wraps a `WorkflowSchedules` facade in explicit user enablement. `enable({ workflowId, scope, actor, nextRunAt, intervalMs?|calculatorId?, input?, ttlMs? })` creates the schedule plus a scoped, expiring `ScheduleCapabilityToken` (default TTL 24h / hard 31d, record ≤ 16 KiB) stamped with redacted actor refs. `revoke(tokenId, actor)` marks the token revoked and pauses the underlying schedule so `pollOnce()` never fires it (fail-closed). `assertActive(tokenId)` is a fail-closed guard for manual trigger paths — it throws on missing/revoked/expired tokens. `onCapability` emits `capability_enabled` / `capability_revoked` / `capability_denied` events (redacted refs only) that hosts bridge to `@arnilo/prism-policy` for an auditable ledger. Tokens are ownership-scoped checkpoint records; no cron expression or secret is persisted.
117
+ `createProactiveScheduleCapabilities({ schedules, store, ownership, ownerId, defaultTtlMs?, maxTtlMs?, onCapability? })` wraps a `WorkflowSchedules` facade in explicit user enablement. `enable({ workflowId, scope, actor, nextRunAt, intervalMs?|calculatorId?, input?, ttlMs? })` creates the schedule plus a scoped, expiring `ScheduleCapabilityToken` (default TTL 24h / hard 31d, record ≤ 16 KiB) stamped with redacted actor refs. `revoke(tokenId, actor)` marks the token revoked and pauses the underlying schedule so `pollOnce()` never fires it (fail-closed). `assertActive(tokenId)` is a fail-closed guard for manual trigger paths — it throws on missing/revoked/expired tokens. `onCapability` emits `capability_enabled` / `capability_revoked` / `capability_denied` events (redacted refs only) that hosts bridge to `@arnilo/prism-core/governance/policy` for an auditable ledger. Tokens are ownership-scoped checkpoint records; no cron expression or secret is persisted.
104
118
 
105
119
  ## Outputs / response / events
106
120
 
@@ -130,7 +144,7 @@ Saga `onEvent` callbacks receive metadata-only `saga_transition` events with ten
130
144
 
131
145
  Schedule `onEvent` receives bounded-attribution `schedule_fired` or metadata-only `schedule_failed`; schedule input is never copied into these events.
132
146
 
133
- Package-local `WorkflowEvent` types: `workflow_started`, `workflow_suspended`, `workflow_resumed`, `workflow_finished`, `node_started`, `node_finished`, `node_failed`, `node_skipped`, `checkpoint_saved`, `agent_event` (wraps a redacted `AgentEvent`), `workflow_event_overflow`. Sequences are monotonic; drain/order is deterministic by `(sequence, nodeId)`.
147
+ Package-local `WorkflowEvent` types: `workflow_started`, `workflow_suspended`, `workflow_resumed`, `workflow_finished`, `node_started`, `node_finished`, `node_iteration_started`, `node_iteration_finished`, `node_failed`, `node_skipped`, `checkpoint_saved`, `agent_event` (wraps a redacted `AgentEvent`), `workflow_event_overflow`. Loop iteration-finished events carry bounded/redacted output and stable `iterationId`. Sequences are monotonic; drain/order is deterministic by `(sequence, nodeId)`.
134
148
 
135
149
  ## Request/response example
136
150
 
@@ -164,6 +178,7 @@ import {
164
178
  runWorkflow,
165
179
  resumeWorkflow,
166
180
  functionNode,
181
+ loopNode,
167
182
  agentNode,
168
183
  createWorkflowCheckpoints,
169
184
  createWorkflowCommands,
@@ -177,9 +192,9 @@ import {
177
192
  runSaga,
178
193
  workflowNode,
179
194
  suspend,
180
- } from "@arnilo/prism-workflows";
195
+ } from "@arnilo/prism-core/runtime/workflows";
181
196
  import { runRpcServer } from "@arnilo/prism";
182
- import { createSqlitePersistence } from "@arnilo/prism-session-store-sqlite";
197
+ import { createSqlitePersistence } from "@arnilo/prism-core/sessions/sqlite";
183
198
 
184
199
  const research = agentNode({
185
200
  agent: "researcher",
@@ -310,9 +325,54 @@ runRpcServer({
310
325
  });
311
326
  ```
312
327
 
328
+ ## Iterative refinement (`loopNode`)
329
+
330
+ `loopNode` keeps the workflow graph acyclic while executing one body repeatedly. `ctx.iteration` is zero-based, `ctx.iterationId` is a stable compensation key (tenant-prefixed when ownership is supplied), and the body receives the prior body output as `ctx.previousOutput`. `until(ctx)` receives the current body output through that same property. Body and predicate can use `ctx.updateState()` for durable accumulation.
331
+
332
+ Use inline `execute` for pure refinement, or `body` for one interior function/tool sub-step. A tool body uses the normal durable approval gate; approval suspends before its side effect and an approved resume re-enters the same iteration. Completed prior iterations remain in the checkpoint ledger and are not re-executed.
333
+
334
+ `maxIterations` is required and capped at 64 (`HARD_MAX_LOOP_ITERATIONS`). The scheduler enforces the cap even when `until` never passes. Exhaustion throws `WorkflowLoopLimitError` with code `ERR_PRISM_WORKFLOW_LOOP_LIMIT`, `iterations`, and a bounded/redacted `lastOutput`. Each completed iteration stores a versioned, bounded/redacted output record before the next body starts; `maxNodeOutputBytes` applies to every body output.
335
+
336
+ ### Frozen budget accounting
337
+
338
+ `maxNodes` counts declared DAG nodes once. `maxIterations` independently caps loop body executions; iterations never consume `maxNodes`. Both limits are validated before execution and fail closed at their hard caps.
339
+
340
+ `node_iteration_started` and `node_iteration_finished` events expose `iteration` and `iterationId`; finished events also expose `done` and bounded/redacted output. A replay started from a completed loop re-runs its body and emits the same iteration sequence for the new run. Hosts that persist events should treat `iterationId` as the idempotency key.
341
+
342
+ ```ts
343
+ const refine = loopNode({
344
+ execute: async (ctx) => ({
345
+ iteration: ctx.iteration,
346
+ draft: improve((ctx.previousOutput as { draft?: string } | undefined)?.draft),
347
+ }),
348
+ until: (ctx) => (ctx.previousOutput as { passed?: boolean } | undefined)?.passed === true,
349
+ maxIterations: 5,
350
+ });
351
+
352
+ const approvedRefine = loopNode({
353
+ body: toolNode({
354
+ tool: publishDraft,
355
+ args: () => ({ action: "refine" }),
356
+ approval: { reason: "approve refinement side effect" },
357
+ }),
358
+ until: (ctx) => ctx.previousOutput === "accepted",
359
+ maxIterations: 3,
360
+ });
361
+
362
+ const workflow = defineWorkflow({
363
+ id: "refine-draft",
364
+ revision: "1",
365
+ nodes: { refine, approvedRefine },
366
+ });
367
+ ```
368
+
369
+ ### Saga compensation boundary
370
+
371
+ A loop remains one DAG node and one host saga step. Persist the loop's `iterations` as that step's aggregate output, and register external compensation under each record's `iterationId`; compensate records in reverse iteration order. The workflow runner does not invoke saga handlers implicitly, so the host retains ownership of side-effect policy and audit records while replay/resume stay deterministic.
372
+
313
373
  ## Bounded iterate-until-done (host-loop pattern)
314
374
 
315
- Workflows stay acyclic. "Loop until the goal passes" is a **host** `for`/`while` over `runWorkflow`, not a graph cycle. One run per iteration; iteration state in workflow **inputs**; the host owns the termination predicate and budgets. No extra runtime. Runnable proof: [`examples/autonomous-coding-loop.ts`](../examples/autonomous-coding-loop.ts) (N iterations, mid-loop human gate with simulated restart, typed budget exhaustion).
375
+ Workflows can now use `loopNode` for bounded in-graph refinement. A host `for`/`while` over `runWorkflow` remains useful when each iteration must be a separate run id, use a different workflow definition, or run on versions before this node kind. Runnable proof: [`examples/autonomous-coding-loop.ts`](../examples/autonomous-coding-loop.ts) (N runs, mid-loop human gate with simulated restart, typed budget exhaustion).
316
376
 
317
377
  1. Keep the DAG acyclic (roadmap → execute → validate → gate → compact).
318
378
  2. Pass `{ goal, iteration }` as `runWorkflow` input — never a back-edge.
@@ -331,13 +391,13 @@ for (let i = 0; i < MAX_ITERATIONS; i++) {
331
391
  if (!passed(last.outputs)) throw new BudgetExhaustedError(MAX_ITERATIONS);
332
392
  ```
333
393
 
334
- Budgets are the host's job until [plan 045](../plans/045-Bounded-Loop-Workflow-Node.md) ships an in-graph `loop` node (`until` + hard `maxIterations`, still finite). Do not wait on that primitive for this pattern.
394
+ For a single bounded refinement, prefer `loopNode`. Keep this host-loop pattern when separate run ids, per-run checkpoints, or a new workflow definition are part of the contract.
335
395
 
336
396
  ## Extension and configuration notes
337
397
 
338
398
  - Workflow semantics stay in this optional package; generic checkpoint persistence and bounded event fan-in live in core.
339
399
  - `ProductionPersistenceStore.checkpoints` and `.leases` are optional generic capabilities. First-party SQLite/PostgreSQL adapters own `prism_checkpoints` / `prism_leases`; workflows only adapt them. Sagas use the same `WorkflowCheckpointAdapter` and `LeaseStore`; they add no SQL table or scheduler.
340
- - `createWorkflowEventBus()` delegates queueing, source fan-in, overflow, abort, and close behavior to core `createEventMultiplexer()`, including its single-consumer contract: a second concurrent `subscribe()` is rejected with `EventMultiplexerError` (`ERR_PRISM_EVENT_MULTIPLEXER_SINGLE_CONSUMER`) instead of silently splitting the stream. Graceful `close()` stops new emits/sources and drains already-queued events (in `(sequence, nodeId)` order) before the subscriber completes; overflow `close` still emits one `workflow_event_overflow` notice and terminates.
400
+ - `createWorkflowEventBus()` delegates queueing, source fan-in, overflow, abort, and close behavior to core `createEventMultiplexer()`, including its single-consumer contract: a second concurrent `subscribe()` is rejected with `EventMultiplexerError` (`ERR_PRISM_EVENT_MULTIPLEXER_SINGLE_CONSUMER`) instead of silently splitting the stream. Graceful `close()` stops new emits/sources and drains already-queued events (in `(sequence, nodeId)` order) before the subscriber completes; overflow `close` still emits one `workflow_event_overflow` notice and terminates. Loop iteration events carry bounded/redacted output and stable `iterationId` values for durable sinks.
341
401
  - The in-process active-run registry (`registerActiveWorkflowRun` / `getActiveWorkflowRun` / `abortActiveWorkflowRun`) is **non-durable, in-process only — it does not survive restart**; durable active-run recovery is a later milestone. It is bounded: every register sweeps aborted/leaked entries (runs whose promise never settled) and the registry fails closed at `MAX_ACTIVE_WORKFLOW_RUNS` (512) rather than evicting a live run; `sweepActiveWorkflowRuns()` is available for hosts. Cross-tenant lookups stay ownership-isolated.
342
402
  - `createWorkflowCommands()` is optional; hosts can drive `workflow.start` / `enqueue` / `replay` / `status` / `list` / `cancel` / `resume`. The six `schedule.*` commands appear only when a scoped `schedules` service is supplied.
343
403
  - Hosts may bridge `WorkflowEvent` into OpenTelemetry or custom sinks; there is no built-in TUI.
@@ -346,7 +406,8 @@ Budgets are the host's job until [plan 045](../plans/045-Bounded-Loop-Workflow-N
346
406
 
347
407
  ## Security and performance notes
348
408
 
349
- - Definitions require a non-empty host-authored `revision` and fail closed on cycles, unknown edges, self-edges, invalid limits, and `maxNodes` overflow. Revision and every nested revision enter the deterministic definition hash; hosts must bump revision when function/tool behavior changes.
409
+ - Definitions require a non-empty host-authored `revision` and fail closed on cycles, unknown edges, self-edges, invalid limits, and `maxNodes` overflow. Revision and every nested revision enter the deterministic definition hash; hosts must bump revision when function/tool behavior changes. Loop `maxIterations` is required and capped at 64.
410
+ - Loop bodies run serially inside one scheduler node; every body output and durable iteration record is bounded/redacted with `maxNodeOutputBytes`, and the scheduler persists the completed-iteration cursor before advancing. Approved durable resumes re-enter only the incomplete iteration.
350
411
  - Fan-out length is bounded by `maxFanOut`. Independent `map` items run in a local worker pool capped by the resolved workflow `maxConcurrency` (and `options.concurrency`); output stays in input order. Abort or the first map failure stops further items. There is no extra global admission service.
351
412
  - Node outputs, shared state/history, schedule input/records, and checkpoints are byte/count/depth bounded. Checkpoint size remains the final aggregate ceiling.
352
413
  - Event buses use a bounded buffer (default 2048) with `close` / `drop_oldest` / `drop_newest` overflow.
@@ -358,13 +419,13 @@ Budgets are the host's job until [plan 045](../plans/045-Bounded-Loop-Workflow-N
358
419
  - Active registry identity includes workflow ID, run ID, and exact ownership. Exact duplicates fail instead of overwriting; distinct owners remain isolated in lookup/list/cancel/unregister.
359
420
  - Tool nodes attach `workflowId` / `nodeId` on `ExecutionAction.metadata` for approval/audit context.
360
421
  - Nested workflows inherit host registries/policies and cannot inject broader tools, agents, ownership, or credentials. Nested depth is inherited; child suspension bubbles to the parent review cursor.
361
- - Replay source ownership/hash/status/node eligibility are checked before a new checkpoint is created. Source records are immutable, lineage is bounded, and copied approval-bearing paths are rejected.
422
+ - Replay source ownership/hash/status/node eligibility are checked before a new checkpoint is created. Source records are immutable, lineage is bounded, and copied approval-bearing paths are rejected. Replaying from a completed loop starts a fresh loop cursor and emits its per-iteration records; it never mutates source evidence.
362
423
  - Schedule services are ownership-scoped and explicitly started. Per-fire leases plus deterministic run IDs/CAS prevent duplicate enqueue across coordinators and crash retry. Host calculator IDs resolve only from the supplied map; no callback or cron expression is persisted.
363
424
  - Proactive schedules require an explicit capability grant. Revocation pauses the schedule (never fired by `pollOnce`) and `assertActive` fails closed on missing/revoked/expired tokens; enable/revoke/deny events carry redacted actor refs for the host policy ledger. Capability TTL is capped (default 24h / hard 31d) and the token record is byte-bounded (≤ 16 KiB); tokens are ownership-scoped, so foreign access fails closed rather than leaking existence.
364
425
  - Scheduler stores O(nodes + active outputs + bounded state history); ready-node work uses indegree maps, not repeated full scans.
365
426
  - Lease acquisition is atomic; opaque tokens protect renew/release; monotonically increasing fencing tokens plus checkpoint compare-and-swap prevent expired workers from committing after takeover. Node functions must honor `ctx.signal` for prompt cooperative cancellation.
366
427
  - Saga runs require `tenantId`; checkpoint keys and leases include tenant ownership. Every transition uses checkpoint CAS plus the current lease fence. Forward/compensation retries are capped at 3 by default / 10 hard; ambiguous outcomes require reconciliation and unresolved state becomes `manual_intervention`.
367
- - Saga input, step outputs, and error text are byte-bounded and passed through the configured `SecretRedactor` before persistence or compensation. Manual resolution requires an active verified actor for the tenant, exact checkpoint version, bounded reason, and a non-empty host audit reference; Prism does not pretend to verify the external audit record.
428
+ - Saga input, step outputs, and error text are byte-bounded and passed through the configured `SecretRedactor` before persistence or compensation. A loop used as one saga step remains one aggregate compensation record; hosts register and compensate its durable iteration IDs in reverse order. Manual resolution requires an active verified actor for the tenant, exact checkpoint version, bounded reason, and a non-empty host audit reference; Prism does not pretend to verify the external audit record.
368
429
 
369
430
  Use workflows for known, durable, replayable graphs. Use optional supervisor delegation only when child selection must be dynamic at runtime; do not replace deterministic nodes with model routing without a concrete need.
370
431
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## What it does
4
4
 
5
- `@arnilo/prism-memory` is an optional package for schema/template-backed working memory and embedding-based semantic recall. It owns narrow `Embedder` and `VectorStore` contracts reused by `@arnilo/prism-rag`, plus an in-memory reference path and one PostgreSQL/pgvector production adapter.
5
+ `@arnilo/prism-memory` is an optional package for schema/template-backed working memory and embedding-based semantic recall. It owns narrow `Embedder` and `VectorStore` contracts reused by the `@arnilo/prism-memory/rag` subpath, plus an in-memory reference path and one PostgreSQL/pgvector production adapter.
6
6
 
7
7
  ## When to use it
8
8
 
@@ -26,6 +26,7 @@ Ordinary Prism sessions do not require this package or any vector backend.
26
26
  | `limits` | no | top-K, adjacent range, batch, payload, injected-token, export, and rebuild caps |
27
27
  | `redactor` / `secrets` | no | Redact text/metadata before persist/inject |
28
28
  | `requireConsent` | no | Strict mode: recall/injection excludes entries lacking explicit consent |
29
+ | `importanceFrom` | no | Host-owned hook deriving importance from a redacted reflection payload (write time only; no default, no LLM) |
29
30
 
30
31
  Semantic indexing (entries carry `MemoryConsent` source/visibility; unset defaults to `{ source: "user", scope: "thread", visible: true }`):
31
32
 
@@ -37,13 +38,60 @@ Semantic indexing (entries carry `MemoryConsent` source/visibility; unset defaul
37
38
  | `grantedAt` / `revokedAt` | Optional host/audit timestamps; a revocation excludes the record. |
38
39
 
39
40
  ```ts
40
- await memory.remember({ entries: [{ id, text, metadata?, consent?, sequence? }] }, { wait?: boolean })
41
+ await memory.remember({ entries: [{ id, text, metadata?, consent?, sequence?, importance?, reflection? }] }, { wait?: boolean })
41
42
  ```
42
43
 
43
44
  Semantic recall (honors consent/visibility at assembly time):
44
45
 
45
46
  ```ts
46
- await memory.recall(query, { topK?, messageRange?, requireConsent?, signal? })
47
+ await memory.recall(query, { topK?, messageRange?, requireConsent?, scoring?, signal? })
48
+ ```
49
+
50
+ #### Composite recall scoring (opt-in)
51
+
52
+ Default recall is pure similarity + lexical scoring and stays unchanged. Hosts opt into blending recency and importance at recall time via `scoring`:
53
+
54
+ | `RecallScoringOptions` field | Meaning |
55
+ | --- | --- |
56
+ | `recencyWeight` | Weight in `[0,1]` for timestamp half-life decay; requires `halfLifeMs` |
57
+ | `importanceWeight` | Weight in `[0,1]` for the stored record `importance` (neutral `1.0` when absent) |
58
+ | `halfLifeMs` | Positive finite recency half-life in milliseconds |
59
+
60
+ The resolver validates weights (finite, in `[0,1]`, no extra dependencies) and sum-normalizes: similarity keeps the remainder of `1`; weights overshooting `1` normalize down (similarity → `0`). Hit order becomes the blended score with the same deterministic tie-break (`score` desc, `sequence` asc, `id` asc), and hits expose the `similarity`, `recency`, `importance`, and `score` components. Both adapters converge on one shared pure re-rank — candidates are fetched at `topK × 4`, blended, then cut to `topK` — so pgvector ordering matches the in-memory adapter by construction.
61
+
62
+ ```ts
63
+ const recalled = await memory.recall("preferred response format", {
64
+ topK: 8,
65
+ scoring: { recencyWeight: 0.3, importanceWeight: 0.2, halfLifeMs: 7 * 24 * 3600 * 1000 },
66
+ });
67
+ // hits[0]: { text, score, similarity, recency, importance, ... }
68
+ ```
69
+
70
+ Security/performance: importance is host-trusted data clamped to `[0,1]` at write and scoring time; scoring is per-hit arithmetic with no extra queries or LLM calls; recall without `scoring` returns today's ordering and hit shape unchanged.
71
+
72
+ #### Importance at write (derivation from existing signals)
73
+
74
+ Stored `importance` never comes from an LLM analysis pass over writes — it derives from existing signals only:
75
+
76
+ - Direct: pass `importance` on a `remember()` entry (clamped to `[0,1]` at write; wins over derivation).
77
+ - Derived: set `importanceFrom` on `createMemory()` and pass a `reflection` object on the entry. The hook runs once at write time over the reflection **after secret redaction**, and its output is clamped to `[0,1]`; a non-finite output fails the write. Entries without `importance`/`reflection` (or without a hook) score at the neutral `1.0`. The hook is never invoked at recall, and the reflection payload itself is not persisted.
78
+
79
+ For observational-memory reflections (`@arnilo/prism-memory/compaction/observational-memory`, `MemoryReflection`), spread the record into the entry. The recipe below is an example heuristic — hosts own the real heuristic, and none ships as a default:
80
+
81
+ ```ts
82
+ const memory = createMemory({
83
+ // ...scope + embedder + stores
84
+ importanceFrom: (reflection) => {
85
+ // frequency/prominence recipe example: normalized mention count, no LLM call
86
+ const mentions = Number(reflection.mentions ?? reflection.supportingObservationIds?.length ?? 1);
87
+ return Number.isFinite(mentions) ? mentions / 10 : 1;
88
+ },
89
+ });
90
+
91
+ await memory.remember(
92
+ { entries: [{ id: reflection.id, text: reflection.content, reflection: { ...reflection } }] },
93
+ { wait: true },
94
+ );
47
95
  ```
48
96
 
49
97
  Consent + lifecycle (real grant/correct/delete/retention on stored entries):
@@ -171,14 +219,14 @@ const store = await createPostgresVectorStore({
171
219
  // getCurrentGeneration/setCurrentGeneration. close() ends adapter-owned pools.
172
220
  ```
173
221
 
174
- `createPostgresVectorStore()` is the production counterpart to `createMemoryVectorStore()` used by `@arnilo/prism-rag`; `createPostgresMemoryStores()` reuses the same vector implementation internally.
222
+ `createPostgresVectorStore()` is the production counterpart to `createMemoryVectorStore()` used by the `rag` subpath; `createPostgresMemoryStores()` reuses the same vector implementation internally.
175
223
 
176
224
  ## Extension and configuration notes
177
225
 
178
226
  - Hosts wire the context provider into `AgentConfig.context` or `resolveContextProviders()`.
179
227
  - The working-memory processor is opt-in and host-invoked; middleware is not required.
180
228
  - `createHashEmbedder()` is for tests/demos only; production hosts supply a real `Embedder`.
181
- - Observational memory (`@arnilo/prism-compaction-observational-memory`) remains unchanged and composable.
229
+ - Observational memory (`/compaction/observational-memory`) remains unchanged and composable.
182
230
  - Consent is enforced at the single `recall()` gate, so both direct recall and `createContextProvider()` injection honor it; `visible: false` (or a revoked grant) keeps an entry out of prompts, events, exports, and telemetry. `setConsent`/`correct` re-upsert in place (consent change does not re-embed); `forget`/`applyRetention` are real deletes, not tombstones. Retention uses indexed oldest-first pages plus a scoped count, deleting one default-500/hard-5000 batch without reading a corpus into memory. The PostgreSQL adapter persists consent in a `consent JSONB` column added by `buildMemoryDdl`.
183
231
  - The PostgreSQL vector path owns its DDL in Prism (`buildMemoryDdl`/`buildVectorSearchDdl` exported): the `<table>_rag_scope_generations` per-scope generation pointer table, `text_tsv` tsvector column + GIN index for the lexical RAG leg, and an HNSW index when the embedding dimension is pinned. DDL runs against the host's **knowledge database** — the host names `schema`/`table` (defaults `prism_memory`/`semantic_memory`), owns backup/retention of that database, and can run migrations manually with `skipMigrations: true`. Identifiers are validated/quoted; values stay parameterized.
184
232
  - `createPostgresVectorStore({ dimension })` pins the embedding column width before building indexes: pgvector can only build HNSW over `vector(N)` columns, and dimension mismatch fails closed instead of drifting.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arnilo/prism",
3
- "version": "0.3.2",
3
+ "version": "0.5.0",
4
4
  "description": "Agent harness for AI providers, agents, sessions, and tools.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -122,34 +122,15 @@
122
122
  "CHANGELOG.md"
123
123
  ],
124
124
  "workspaces": [
125
- "packages/provider-*",
126
- "packages/memory",
127
- "packages/rag",
128
- "packages/compaction-*",
129
- "packages/observability-*",
130
- "packages/tool-validator-*",
131
- "packages/session-store-*",
132
- "packages/credentials-node",
133
125
  "packages/mcp",
134
- "packages/evals",
135
- "packages/workflows",
136
- "packages/coding-agent",
137
- "packages/coding-security",
138
- "packages/server",
139
- "packages/supervisor",
140
- "packages/web-tools",
141
- "packages/work-tools",
142
- "packages/policy",
143
- "packages/model-router",
144
- "packages/enterprise-postgres",
145
- "packages/browser",
146
- "packages/obscura",
126
+ "packages/prism-providers",
127
+ "packages/memory",
128
+ "packages/prism-core",
129
+ "packages/prism-coding-tools",
130
+ "packages/office",
147
131
  "packages/ag-ui",
148
- "packages/acp-agent",
149
- "packages/computer-use-linux",
150
- "packages/document-reader",
151
- "packages/antigravity-agent",
152
- "packages/prism-*"
132
+ "packages/web-tools",
133
+ "packages/acp-agent"
153
134
  ],
154
135
  "scripts": {
155
136
  "build:core": "node scripts/with-build-lock.mjs tsc",
@@ -157,24 +138,26 @@
157
138
  "build": "npm run build:core && npm run build --workspaces --if-present",
158
139
  "typecheck": "npm run build && npm run typecheck --workspaces --if-present && tsc -p examples --noEmit",
159
140
  "sweep:unused": "node scripts/sweep-unused.mjs --json",
160
- "test": "npm run build && node scripts/with-build-lock.mjs node --test dist/__tests__/*.test.js && node scripts/with-build-lock.mjs node --test scripts/release-gate.test.mjs scripts/tooling-gate.test.mjs scripts/budget-gate.test.mjs scripts/phase8-conformance.test.mjs scripts/phase9-conformance.test.mjs scripts/phase10-conformance.test.mjs scripts/phase11-conformance.test.mjs scripts/phase11-freeze.test.mjs scripts/phase12-freeze.test.mjs scripts/phase13-freeze.test.mjs scripts/phase14-freeze.test.mjs scripts/phase15-freeze.test.mjs scripts/phase16-freeze.test.mjs scripts/phase17-freeze.test.mjs scripts/phase18-freeze.test.mjs scripts/phase19-freeze.test.mjs scripts/phase20-freeze.test.mjs scripts/phase21-freeze.test.mjs scripts/benchmark-0.1.0.test.mjs scripts/benchmark-multi-agent.test.mjs scripts/sweep-unused.test.mjs scripts/e2e-enterprise-journey.test.mjs scripts/e2e-coding-journey.test.mjs scripts/phase23-quality-gates.test.mjs scripts/phase24-truth.test.mjs scripts/phase25-bounded-accumulation.test.mjs scripts/phase26-freeze.test.mjs scripts/phase27-freeze.test.mjs scripts/phase27-ha.test.mjs scripts/phase27-erp-journey.test.mjs scripts/phase27-release.test.mjs scripts/phase29-freeze.test.mjs scripts/phase30-freeze.test.mjs scripts/phase30-release.test.mjs scripts/phase34-freeze.test.mjs scripts/phase37-provider-matrix.test.mjs scripts/phase26-index-benchmark.test.mjs scripts/obscura-host-conformance.test.mjs && node --test scripts/phase23-build-race.test.mjs && npm run test --workspaces --if-present",
141
+ "test:live": "node scripts/live-matrix.mjs",
142
+ "test": "npm run build && node scripts/with-build-lock.mjs node --test dist/__tests__/*.test.js && node scripts/with-build-lock.mjs node --test scripts/release-gate.test.mjs scripts/tooling-gate.test.mjs scripts/budget-gate.test.mjs scripts/phase8-conformance.test.mjs scripts/phase9-conformance.test.mjs scripts/phase10-conformance.test.mjs scripts/phase11-conformance.test.mjs scripts/benchmark-0.1.0.test.mjs scripts/benchmark-multi-agent.test.mjs scripts/benchmark-tool-search.test.mjs scripts/benchmark-workflow-loop.test.mjs scripts/sweep-unused.test.mjs scripts/dead-export-verify.test.mjs scripts/e2e-enterprise-journey.test.mjs scripts/e2e-coding-journey.test.mjs scripts/e2e-full-surface.test.mjs scripts/phase23-quality-gates.test.mjs scripts/phase24-truth.test.mjs scripts/phase25-bounded-accumulation.test.mjs scripts/phase27-ha.test.mjs scripts/phase27-erp-journey.test.mjs scripts/phase37-provider-matrix.test.mjs scripts/phase26-index-benchmark.test.mjs scripts/obscura-host-conformance.test.mjs scripts/phase54-package-map.test.mjs scripts/phase54-legacy-registry.test.mjs scripts/truth-current.test.mjs scripts/packaging-current.test.mjs scripts/import-hygiene.test.mjs scripts/live-matrix.test.mjs scripts/e2e-coverage.test.mjs scripts/live-doc-check.test.mjs && node --test scripts/phase23-build-race.test.mjs && npm run test --workspaces --if-present",
161
143
  "test:coverage": "node scripts/with-build-lock.mjs node --test --experimental-test-coverage --test-coverage-lines=60 --test-coverage-functions=70 --test-coverage-branches=75 --test-coverage-exclude='**/__tests__/**' --test-coverage-exclude='**/node_modules/**' --test-coverage-exclude='**/scripts/**' --test-coverage-exclude='**/packages/**' --test-coverage-exclude='**/examples/**' dist/__tests__/*.test.js && node scripts/with-build-lock.mjs node scripts/coverage-summary.mjs && node --test scripts/phase23-coverage.test.mjs && node --test scripts/phase23-skip-manifest.test.mjs",
162
144
  "coverage:summary": "node scripts/with-build-lock.mjs node scripts/coverage-summary.mjs",
163
145
  "lint": "biome lint . --reporter=sarif --reporter-file=scripts/lint-report.sarif",
164
146
  "format": "biome format --write .",
165
147
  "format:check": "biome format .",
166
148
  "pack:dry-run": "npm pack --dry-run && npm run pack:dry-run --workspaces --if-present",
167
- "test:postgres": "node scripts/require-postgres-url.mjs && npm run test:postgres --workspace @arnilo/prism-session-store-postgres && npm run test:postgres --workspace @arnilo/prism-memory && npm run test:postgres --workspace @arnilo/prism-enterprise-postgres && node --test scripts/phase7-conformance.test.mjs scripts/phase12-restart-recovery.test.mjs scripts/phase22-conformance.test.mjs",
149
+ "test:postgres": "node scripts/require-postgres-url.mjs && npm run test:postgres --workspace @arnilo/prism-core --if-present && npm run test:postgres --workspace @arnilo/prism-memory && node --test scripts/phase7-conformance.test.mjs scripts/phase12-restart-recovery.test.mjs scripts/phase22-conformance.test.mjs",
150
+ "test:nats": "node scripts/require-nats-url.mjs && npm run test:nats --workspace @arnilo/prism-core --if-present",
168
151
  "release:dry-run": "npm run sdk:ready",
169
152
  "release:check": "node scripts/release.mjs check",
170
153
  "release:publish": "node scripts/release.mjs publish",
171
154
  "release:evidence": "node scripts/release-skip-manifest.mjs",
172
155
  "sdk:ready": "npm run typecheck && npm run lint && npm run format:check && npm test && npm run test:coverage && npm run pack:dry-run && npm run release:gate",
173
156
  "release:gate": "node scripts/release-skip-manifest.mjs && node scripts/check-client-neutrality.mjs && node scripts/release.mjs gate",
174
- "security:threat-suites": "node --test scripts/phase8-conformance.test.mjs scripts/phase9-conformance.test.mjs scripts/phase10-conformance.test.mjs scripts/phase11-conformance.test.mjs scripts/phase20-security.test.mjs scripts/phase21-security.test.mjs scripts/phase22-security.test.mjs scripts/phase23-security.test.mjs scripts/phase38-codeql-regression.test.mjs"
157
+ "security:threat-suites": "node --test scripts/phase8-conformance.test.mjs scripts/phase9-conformance.test.mjs scripts/phase10-conformance.test.mjs scripts/phase11-conformance.test.mjs scripts/phase20-security.test.mjs scripts/phase21-security.test.mjs scripts/phase22-security.test.mjs scripts/phase23-security.test.mjs scripts/phase38-codeql-regression.test.mjs scripts/phase40-security.test.mjs scripts/phase46-webhooks-security.test.mjs dist/__tests__/pinned-fetch.test.js packages/prism-core/dist/runtime/server/__tests__/webhooks.test.js"
175
158
  },
176
159
  "devDependencies": {
177
- "@biomejs/biome": "^2.5.5",
160
+ "@biomejs/biome": "^2.5.11",
178
161
  "@types/node": "^26.1.1",
179
162
  "typescript": "^7.0.2"
180
163
  },
@@ -0,0 +1,23 @@
1
+ # Prism Template Gallery
2
+
3
+ Ready-to-run project templates for `prism init --template <name>`.
4
+
5
+ ## Available Templates
6
+
7
+ | Template | Description | Included Packages |
8
+ | --- | --- | --- |
9
+ | `init` | Minimal starter Prism agent with one selected provider and offline mock test | `@arnilo/prism` |
10
+ | `deep-research` | Flagship deep research agent: plan -> search -> extract -> refine loop -> citations -> HITL clarify | `@arnilo/prism`, `@arnilo/prism-web-tools`, `@arnilo/prism-memory`, `@arnilo/prism-workflows` |
11
+
12
+ ## Usage
13
+
14
+ ```bash
15
+ # Scaffold the flagship deep-research template
16
+ prism init my-research --template deep-research
17
+
18
+ # List all available templates
19
+ prism init --list-templates
20
+
21
+ # Scaffold the standard minimal agent
22
+ prism init my-agent
23
+ ```