@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.
- package/CHANGELOG.md +50 -1
- package/README.md +42 -62
- package/dist/agent-run-lifecycle.js +4 -0
- package/dist/agent-run-state.d.ts +5 -2
- package/dist/agent-run-state.js +18 -8
- package/dist/agent-session/session/assemble.d.ts +6 -0
- package/dist/agent-session/session/assemble.js +391 -0
- package/dist/agent-session/session/persist.d.ts +28 -0
- package/dist/agent-session/session/persist.js +166 -0
- package/dist/agent-session/session/provider-round.d.ts +6 -0
- package/dist/agent-session/session/provider-round.js +231 -0
- package/dist/agent-session/session/tool-round.d.ts +31 -0
- package/dist/agent-session/session/tool-round.js +473 -0
- package/dist/agent-session/session/types.d.ts +115 -0
- package/dist/agent-session/session/types.js +5 -0
- package/dist/agent-session/session.d.ts +54 -41
- package/dist/agent-session/session.js +23 -1132
- package/dist/capture.d.ts +63 -0
- package/dist/capture.js +67 -0
- package/dist/cli-dev.d.ts +29 -0
- package/dist/cli-dev.js +52 -0
- package/dist/cli-init.d.ts +34 -3
- package/dist/cli-init.js +192 -24
- package/dist/cli-runner.d.ts +6 -2
- package/dist/cli-runner.js +57 -10
- package/dist/content.d.ts +3 -3
- package/dist/content.js +3 -1
- package/dist/contracts-core/agent.d.ts +8 -0
- package/dist/contracts-core/batch.d.ts +97 -0
- package/dist/contracts-core/batch.js +65 -0
- package/dist/contracts-core/content.d.ts +72 -1
- package/dist/contracts-core/embeddings.d.ts +30 -0
- package/dist/contracts-core/embeddings.js +17 -0
- package/dist/contracts-core/images.d.ts +60 -0
- package/dist/contracts-core/images.js +17 -0
- package/dist/contracts-core/moderation.d.ts +46 -0
- package/dist/contracts-core/moderation.js +34 -0
- package/dist/contracts-core/speech.d.ts +39 -0
- package/dist/contracts-core/speech.js +17 -0
- package/dist/contracts-core/transcription.d.ts +48 -0
- package/dist/contracts-core/transcription.js +17 -0
- package/dist/contracts-core/video.d.ts +61 -0
- package/dist/contracts-core/video.js +17 -0
- package/dist/contracts-core.d.ts +7 -0
- package/dist/contracts-core.js +7 -0
- package/dist/contracts-protocol.d.ts +18 -0
- package/dist/contracts-run-state.d.ts +1 -2
- package/dist/index.d.ts +7 -3
- package/dist/index.js +5 -3
- package/dist/input.d.ts +8 -0
- package/dist/input.js +4 -0
- package/dist/node/agent-definitions.d.ts +1 -8
- package/dist/node/agent-definitions.js +0 -34
- package/dist/node/settings.d.ts +0 -1
- package/dist/node/settings.js +0 -5
- package/dist/pinned-fetch.js +29 -3
- package/dist/provider-events.js +3 -4
- package/dist/providers/media.d.ts +1 -2
- package/dist/providers/media.js +1 -4
- package/dist/rpc.d.ts +1 -1
- package/dist/rpc.js +4 -4
- package/dist/testing/persistence-schema.d.ts +1 -1
- package/dist/testing/persistence-schema.js +32 -28
- package/dist/testing/provider-conformance.d.ts +114 -5
- package/dist/testing/provider-conformance.js +342 -0
- package/dist/testing/tool-conformance.d.ts +25 -0
- package/dist/testing/tool-conformance.js +128 -1
- package/dist/testing/tool-effect-store-conformance.d.ts +0 -1
- package/dist/testing/tool-effect-store-conformance.js +0 -3
- package/dist/thinking.d.ts +48 -9
- package/dist/thinking.js +134 -8
- package/dist/tool-search.d.ts +76 -0
- package/dist/tool-search.js +199 -0
- package/docs/0.1.0-readiness.md +3 -3
- package/docs/a2a.md +2 -2
- package/docs/acp-agent.md +1 -1
- package/docs/acp.md +3 -3
- package/docs/ag-ui-adoption.md +1 -1
- package/docs/ag-ui.md +1 -2
- package/docs/agent-definitions.md +1 -1
- package/docs/agent-events.md +5 -5
- package/docs/agent-identity.md +13 -2
- package/docs/audit-export.md +3 -3
- package/docs/batch-jobs.md +120 -0
- package/docs/browser-automation.md +5 -5
- package/docs/caveman.md +2 -2
- package/docs/cli-rpc.md +43 -9
- package/docs/coding-agent-tools.md +19 -19
- package/docs/coding-review-and-diagnostics.md +2 -2
- package/docs/coding-security.md +5 -5
- package/docs/coding-tools.md +82 -0
- package/docs/coding-workspaces.md +2 -2
- package/docs/compaction-and-retry.md +2 -2
- package/docs/compaction-llm.md +4 -4
- package/docs/compaction-observational-memory.md +3 -3
- package/docs/computer-use-linux.md +13 -2
- package/docs/context-and-skills.md +3 -1
- package/docs/conversations.md +4 -4
- package/docs/core.md +85 -0
- package/docs/credential-storage.md +12 -8
- package/docs/credentials-and-redaction.md +1 -1
- package/docs/data-classification.md +1 -1
- package/docs/database-persistence.md +7 -3
- package/docs/dev-inspector.md +103 -0
- package/docs/device-adapters.md +2 -2
- package/docs/diagrams.md +247 -0
- package/docs/document-reader.md +6 -6
- package/docs/documents.md +214 -0
- package/docs/embeddings.md +112 -0
- package/docs/enterprise-postgres-state.md +7 -7
- package/docs/evaluations.md +41 -7
- package/docs/extensions.md +3 -3
- package/docs/forge-integration.md +3 -3
- package/docs/graft.md +5 -5
- package/docs/guardrails.md +2 -2
- package/docs/host-security.md +16 -15
- package/docs/image-generation.md +129 -0
- package/docs/impeccable.md +7 -5
- package/docs/index.md +84 -46
- package/docs/indexed-code-search.md +2 -2
- package/docs/language-intelligence.md +4 -4
- package/docs/live-testing.md +126 -0
- package/docs/mcp-tools.md +44 -13
- package/docs/middleware-hooks.md +1 -1
- package/docs/migrate-to-0.4.md +312 -0
- package/docs/migrate-to-0.5.md +122 -0
- package/docs/migration.md +51 -1
- package/docs/model-registry.md +38 -0
- package/docs/model-routing.md +6 -6
- package/docs/moderation.md +117 -0
- package/docs/multi-agent-patterns.md +177 -0
- package/docs/multimodal-content.md +27 -3
- package/docs/obscura.md +12 -12
- package/docs/observability.md +32 -7
- package/docs/openapi-tools.md +14 -4
- package/docs/operations.md +11 -0
- package/docs/performance.md +30 -10
- package/docs/persistence-credentials-multimodality-primitives.md +7 -7
- package/docs/policy-and-audit.md +18 -8
- package/docs/ponytail.md +3 -3
- package/docs/postgres-persistence.md +5 -5
- package/docs/process-sessions.md +2 -2
- package/docs/prompt-registry.md +106 -0
- package/docs/provider-caching.md +36 -32
- package/docs/provider-conformance.md +24 -2
- package/docs/provider-packages.md +58 -22
- package/docs/provider-primitives.md +5 -5
- package/docs/provider-request-policies.md +1 -1
- package/docs/providers/ai-sdk.md +18 -6
- package/docs/providers/alibaba.md +10 -6
- package/docs/providers/anthropic.md +10 -6
- package/docs/providers/azure.md +20 -4
- package/docs/providers/bedrock.md +18 -3
- package/docs/providers/clinepass.md +7 -3
- package/docs/providers/commandcode.md +253 -0
- package/docs/providers/deepseek.md +7 -3
- package/docs/providers/google.md +8 -4
- package/docs/providers/hyper.md +284 -0
- package/docs/providers/kimi.md +7 -3
- package/docs/providers/neuralwatt.md +12 -8
- package/docs/providers/ollama.md +18 -3
- package/docs/providers/openai-compatible.md +5 -1
- package/docs/providers/openai.md +9 -5
- package/docs/providers/opencode-go.md +8 -4
- package/docs/providers/openrouter.md +8 -4
- package/docs/providers/vertex.md +21 -5
- package/docs/providers/xai.md +7 -3
- package/docs/providers/zai.md +7 -3
- package/docs/rag.md +31 -9
- package/docs/release-and-install.md +181 -76
- package/docs/resource-loading.md +1 -1
- package/docs/runs-and-usage.md +28 -3
- package/docs/server.md +94 -5
- package/docs/settings-auth-trust-security.md +7 -5
- package/docs/sheets.md +229 -0
- package/docs/speech.md +126 -0
- package/docs/sqlite-persistence.md +4 -4
- package/docs/supervisors.md +4 -3
- package/docs/thinking-and-reasoning.md +93 -60
- package/docs/tool-conformance.md +28 -3
- package/docs/tool-execution-primitives.md +8 -8
- package/docs/tools.md +32 -5
- package/docs/web-tools.md +3 -3
- package/docs/wiki.md +7 -7
- package/docs/work-artifacts-and-review.md +17 -6
- package/docs/work-connectors.md +4 -4
- package/docs/work-tools.md +5 -5
- package/docs/workflow-orchestration-primitives.md +35 -11
- package/docs/workflows.md +74 -13
- package/docs/working-and-semantic-memory.md +53 -5
- package/package.json +14 -31
- package/templates/README.md +23 -0
- package/templates/deep-research/README.md.tmpl +47 -0
- package/templates/deep-research/env.example.tmpl +12 -0
- package/templates/deep-research/gitignore.tmpl +7 -0
- package/templates/deep-research/manifest.json +12 -0
- package/templates/deep-research/package.json.tmpl +23 -0
- package/templates/deep-research/src/agent.ts.tmpl +81 -0
- package/templates/deep-research/src/index.ts.tmpl +53 -0
- package/templates/deep-research/src/tests/research.test.ts.tmpl +114 -0
- package/templates/deep-research/src/tools.ts.tmpl +86 -0
- package/templates/deep-research/src/types.ts.tmpl +45 -0
- package/templates/deep-research/src/workflow.ts.tmpl +156 -0
- package/templates/deep-research/tsconfig.json.tmpl +15 -0
- package/templates/init/manifest.json +5 -0
- package/templates/init/package.json.tmpl +2 -1
- package/templates/init/providers.json +40 -24
- package/docs/antigravity-agent.md +0 -207
package/docs/work-tools.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Work tools
|
|
2
2
|
|
|
3
|
-
Optional `@arnilo/prism-work
|
|
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
|
|
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
|
|
27
|
-
// or: import { createGoogleWorkspaceCliAdapter } from "@arnilo/prism-work
|
|
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
|
|
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
|
|
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-
|
|
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
|
|
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-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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 (
|
|
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
|
+
"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/
|
|
135
|
-
"packages/
|
|
136
|
-
"packages/
|
|
137
|
-
"packages/coding-
|
|
138
|
-
"packages/
|
|
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/
|
|
149
|
-
"packages/
|
|
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": "
|
|
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-
|
|
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.
|
|
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
|
+
```
|