@arnilo/prism 0.0.4 → 0.0.6
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 +46 -1
- package/README.md +34 -10
- package/dist/agent-loops.d.ts +1 -0
- package/dist/agent-loops.js +26 -16
- package/dist/agents.js +147 -21
- package/dist/cli-init.d.ts +41 -0
- package/dist/cli-init.js +390 -0
- package/dist/cli-runner.d.ts +7 -1
- package/dist/cli-runner.js +13 -1
- package/dist/content.d.ts +19 -0
- package/dist/content.js +197 -69
- package/dist/contracts.d.ts +96 -9
- package/dist/contracts.js +8 -0
- package/dist/feedback.d.ts +48 -0
- package/dist/feedback.js +230 -0
- package/dist/ids.d.ts +2 -0
- package/dist/ids.js +6 -0
- package/dist/index.d.ts +10 -4
- package/dist/index.js +6 -3
- package/dist/providers/media.d.ts +3 -1
- package/dist/providers/media.js +11 -1
- package/dist/session-stores.js +2 -3
- package/dist/testing/feedback.d.ts +6 -0
- package/dist/testing/feedback.js +37 -0
- package/dist/testing/persistence-schema.d.ts +48 -10
- package/dist/testing/persistence-schema.js +166 -22
- package/dist/testing/run-ledger-conformance.js +7 -1
- package/dist/thinking.d.ts +42 -0
- package/dist/thinking.js +92 -0
- package/dist/tools.js +2 -3
- package/dist/use-case-model.d.ts +63 -0
- package/dist/use-case-model.js +52 -0
- package/docs/a2a.md +75 -0
- package/docs/agent-events.md +14 -21
- package/docs/agent-loops.md +12 -9
- package/docs/agent-session-runtime.md +14 -16
- package/docs/cli-rpc.md +35 -7
- package/docs/coding-agent-tools.md +35 -14
- package/docs/coding-security.md +7 -3
- package/docs/compaction-llm.md +17 -7
- package/docs/compaction-observational-memory.md +30 -4
- package/docs/context-and-skills.md +1 -0
- package/docs/credential-storage.md +58 -9
- package/docs/credentials-and-redaction.md +3 -3
- package/docs/database-persistence.md +17 -9
- package/docs/evaluations.md +122 -0
- package/docs/extensions.md +2 -2
- package/docs/host-security.md +26 -5
- package/docs/index.md +43 -28
- package/docs/mcp-tools.md +74 -13
- package/docs/migration.md +177 -3
- package/docs/multimodal-content.md +14 -6
- package/docs/node-filesystem-config.md +1 -0
- package/docs/node-jsonl-session-store.md +5 -4
- package/docs/observability.md +14 -6
- package/docs/performance.md +209 -0
- package/docs/postgres-persistence.md +8 -6
- package/docs/provider-caching.md +16 -4
- package/docs/provider-conformance.md +40 -1
- package/docs/provider-packages.md +62 -3
- package/docs/providers/ai-sdk.md +149 -0
- package/docs/providers/kimi.md +124 -61
- package/docs/providers/neuralwatt.md +19 -13
- package/docs/providers/openai.md +56 -13
- package/docs/providers/opencode-go.md +118 -30
- package/docs/providers/openrouter.md +105 -35
- package/docs/providers/zai.md +94 -45
- package/docs/public-contracts.md +6 -5
- package/docs/rag.md +113 -0
- package/docs/release-and-install.md +100 -79
- package/docs/review-coverage-2026-07-15.md +193 -0
- package/docs/review-coverage-2026-07-17-provider-validation.md +192 -0
- package/docs/runs-and-usage.md +42 -5
- package/docs/server.md +139 -0
- package/docs/settings-auth-trust-security.md +5 -5
- package/docs/sqlite-persistence.md +6 -5
- package/docs/structured-output.md +1 -1
- package/docs/supervisors.md +71 -0
- package/docs/thinking-and-reasoning.md +98 -0
- package/docs/tool-execution-primitives.md +3 -3
- package/docs/tools.md +15 -0
- package/docs/use-case-model-selection.md +109 -0
- package/docs/workflow-orchestration-primitives.md +20 -3
- package/docs/workflows.md +114 -33
- package/docs/working-and-semantic-memory.md +170 -0
- package/package.json +13 -3
- package/templates/init/README.md.tmpl +28 -0
- package/templates/init/env.example.tmpl +1 -0
- package/templates/init/gitignore.tmpl +11 -0
- package/templates/init/optional/evals-example.ts.tmpl +17 -0
- package/templates/init/optional/workflows-example.ts.tmpl +27 -0
- package/templates/init/package.json.tmpl +22 -0
- package/templates/init/providers.json +76 -0
- package/templates/init/src/agent.ts.tmpl +10 -0
- package/templates/init/src/index.ts.tmpl +12 -0
- package/templates/init/src/tests/agent.test.ts.tmpl +24 -0
- package/templates/init/tsconfig.json.tmpl +15 -0
|
@@ -8,6 +8,10 @@ Interactive TUI (**C-012**) is **out of scope** for Plan 057 and deferred. Workf
|
|
|
8
8
|
|
|
9
9
|
**Final Task 7 architecture (2026-07-14):** DAG/coordinator semantics remain package-local. Reusable versioned checkpoints, atomic leases, and bounded async event fan-in live in core as `CheckpointStore`, `LeaseStore`, and `EventMultiplexer`. Workflow adapters are thin domain facades; lease fencing plus checkpoint CAS prevents stale-worker commits.
|
|
10
10
|
|
|
11
|
+
**Phase 8 addendum (2026-07-15):** `@arnilo/prism-workflows` extends the same checkpoint JSON/state machine with `suspended` and terminal `denied` statuses, `suspend()`, persisted suspension/resume records, and `workflow_suspended` / `workflow_resumed` events. An approved resume requires the displayed checkpoint `expectedVersion`; the first checkpoint write CAS-claims it before node execution. Suspended runs are absent from coordinator `queued`/`running` polls, so no worker or polling loop remains active. No SQL migration or second approval store is required.
|
|
12
|
+
|
|
13
|
+
**Phase 11 addendum (2026-07-16):** schedules use a separate generic checkpoint namespace plus per-fire `LeaseStore` claims and deterministic queued-run IDs; SQLite/PostgreSQL need no workflow-specific table or migration. Background execution remains `enqueueWorkflow` + the existing coordinator. Nested workflow nodes call the same runner with inherited policy/ownership/checkpoint/event seams. Shared JSON state is validated, redacted, byte/history bounded, and checkpointed by version. Replay creates a new checkpoint with immutable source lineage and copied terminal evidence; approval-bearing prior paths cannot be copied.
|
|
14
|
+
|
|
11
15
|
## When to use it
|
|
12
16
|
|
|
13
17
|
- **Workflow package authors** should start here, then follow [Agent/session runtime](agent-session-runtime.md), [Agent loops](agent-loops.md), [Runs and usage ledger](runs-and-usage.md), [CLI/RPC](cli-rpc.md), and [Database persistence](database-persistence.md).
|
|
@@ -121,7 +125,7 @@ Static review of `src/agents.ts`, `src/agent-loops.ts`, `src/rpc.ts`, `src/cli-r
|
|
|
121
125
|
| Event fan-in | Generic core multiplexer | Package queue duplication | **Core multiplexer + package facade** | One bounded/abort-aware implementation |
|
|
122
126
|
| Distributed ownership | Process-local active map | Generic leases + package coordinator | **LeaseStore + fenced checkpoint CAS** | Atomic claims and monotonic fences prevent split brain across processes |
|
|
123
127
|
| Host control (no TUI) | Interactive terminal package | Public APIs + optional RPC/`CommandDefinition` | **Public APIs + optional commands** | Replaces former TUI Tasks for feature completeness |
|
|
124
|
-
| Approval prompts | Core `ApprovalHandler` |
|
|
128
|
+
| Approval prompts | Core `ApprovalHandler` | Durable workflow suspension + host `ExecutionPolicy` | **Checkpoint suspension** | Survives restart; approved tool execution still rechecks current host policy |
|
|
125
129
|
| Interactive TUI | Ship in Plan 057 | Defer C-012 | **Defer** | Explicit product decision after Task 0 |
|
|
126
130
|
|
|
127
131
|
## Locked package adapter contracts (Task 1)
|
|
@@ -383,6 +387,7 @@ const review = functionNode({ execute: async (ctx) => lint(ctx.upstream.draft) }
|
|
|
383
387
|
|
|
384
388
|
const workflow = defineWorkflow({
|
|
385
389
|
id: "research-draft-review",
|
|
390
|
+
revision: "2026-07-19.1",
|
|
386
391
|
nodes: { research, draft, review },
|
|
387
392
|
edges: [
|
|
388
393
|
["research", "draft"],
|
|
@@ -435,6 +440,12 @@ runRpcServer({
|
|
|
435
440
|
| Workflow `maxCheckpointBytes` | Full checkpoint blob | 1 MiB | Resume metadata only; not full transcripts |
|
|
436
441
|
| Workflow event buffer | Per run merge queue | 2048 | Coalesce node status; drop with `workflow_event_overflow` |
|
|
437
442
|
| Workflow list/status page size | Status helper default | 100 | Bounded run listing for hosts |
|
|
443
|
+
| Nested depth | Default / hard | 8 / 32 | Prevent recursive composition exhaustion |
|
|
444
|
+
| Shared state | Default / hard bytes | 64 KiB / 512 KiB | Keep node context/checkpoints bounded |
|
|
445
|
+
| State history | Default / hard snapshots | 32 / 128 | Preserve replay state without unbounded history |
|
|
446
|
+
| Replay lineage | Default / hard depth | 8 / 32 | Prevent replay-chain abuse |
|
|
447
|
+
| Schedule input | Default / hard bytes | 256 KiB / 1 MiB | Bound persisted trigger payload |
|
|
448
|
+
| Schedule due claims | Default / hard per poll | 16 / 256 | Bound one poll; idle waits 1s by default |
|
|
438
449
|
|
|
439
450
|
## Threat model and design matrix
|
|
440
451
|
|
|
@@ -444,7 +455,7 @@ runRpcServer({
|
|
|
444
455
|
| 2 | Unbounded fan-out (dynamic list) | Workflow package | Cap `maxFanOut`; fail `node_failed` when exceeded |
|
|
445
456
|
| 3 | Resumed checkpoint tampered (wrong tenant/version) | Workflow adapter | Fail closed; no partial node execution |
|
|
446
457
|
| 4 | Checkpoint contains secrets | Workflow + redactor | Redact before persist; `redacted: true` metadata |
|
|
447
|
-
| 5 | Shell/tool approval during workflow | Host +
|
|
458
|
+
| 5 | Shell/tool approval during workflow | Host + workflow | Opt-in tool approval suspends before side effects; resume uses ownership + expected-version CAS, then rechecks current `ExecutionPolicy` with workflow/node metadata |
|
|
448
459
|
| 6 | Cancel during node execution | Workflow | `signal` abort → in-flight `session.abort()`; checkpoint marks `aborted` |
|
|
449
460
|
| 7 | Untrusted workflow definition file | Host | Load from trusted path only; schema-validate before `runWorkflow` |
|
|
450
461
|
| 8 | Node output passed to next node | Workflow | Size-bound; type validate; redact at boundary |
|
|
@@ -452,6 +463,12 @@ runRpcServer({
|
|
|
452
463
|
| 10 | Cross-tenant list/status query | Workflow adapter | Scope by ownership; never return other tenants' runs |
|
|
453
464
|
| 11 | RPC workflow cancel races session abort | Workflow commands | Cancel is idempotent; fails closed if run unknown/unauthorized |
|
|
454
465
|
| 12 | 1000-node workflow checkpoint growth | Workflow | Store ready set + bounded outputs only; no full transcript duplication |
|
|
466
|
+
| 13 | Two reviewers resume one suspension | Workflow adapter | Expected-version CAS claims checkpoint before execution; one succeeds, stale reviewer fails |
|
|
467
|
+
| 14 | Forged/cross-tenant resume | Workflow + host | Ownership and definition hash checked; declared schema requires host validator; payload redacted before persistence |
|
|
468
|
+
| 15 | Duplicate/crashed schedule fire | Workflow schedules | Per-fire lease, deterministic run ID, queued checkpoint idempotency, schedule CAS |
|
|
469
|
+
| 16 | Nested workflow broadens capability | Workflow runner | Child inherits parent tool/agent/policy/ownership/signal seams; bounded inherited depth |
|
|
470
|
+
| 17 | Replay mutates evidence or reuses approval | Workflow replay | New checkpoint + immutable lineage; source untouched; copied approval-bearing path rejected |
|
|
471
|
+
| 18 | State/history resource exhaustion | Workflow runner/checkpoint | Host validation plus state byte/history and aggregate checkpoint ceilings |
|
|
455
472
|
|
|
456
473
|
## Final primitive decisions (Task 1, superseded where noted by Task 6)
|
|
457
474
|
|
|
@@ -547,7 +564,7 @@ await session.run("Hi", { signal: AbortSignal.timeout(60_000) });
|
|
|
547
564
|
- Workflow agent nodes call public `AgentSession` APIs only; no imports from `src/agents.ts` internals.
|
|
548
565
|
- Workflow checkpoints adapt `ProductionPersistenceStore.checkpoints` (or any `CheckpointStore`); no raw database handles enter the workflow package.
|
|
549
566
|
- Multimodal and credential packages from Plan 056 compose unchanged in workflow examples (Task 4).
|
|
550
|
-
-
|
|
567
|
+
- `@arnilo/prism-workflows` is available directly and through `prism-sdk`/`prism-all`; installation does not start a worker or workflow.
|
|
551
568
|
- C-012 interactive TUI remains a future optional package if needed; it is not required for workflow feature completeness.
|
|
552
569
|
|
|
553
570
|
## Related APIs
|
package/docs/workflows.md
CHANGED
|
@@ -2,21 +2,22 @@
|
|
|
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 nodes; the package runs a Kahn-style scheduler with a bounded worker pool, emits package-local `WorkflowEvent`s, checkpoints progress, and can coordinate queued runs across multiple host processes using durable leases and fencing.
|
|
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, and can coordinate queued runs across multiple host processes using durable leases and fencing.
|
|
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` | Typed node factories |
|
|
13
|
-
| `runWorkflow` / `resumeWorkflow` | Execute
|
|
12
|
+
| `agentNode`, `functionNode`, `toolNode`, `conditionalNode`, `fanOutNode`, `joinNode`, `workflowNode` | Typed node factories, including composition through the same runner |
|
|
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 |
|
|
16
16
|
| `createWorkflowEventBus` | Bounded pub/sub for `WorkflowEvent` with overflow policy |
|
|
17
17
|
| `getWorkflowRun` / `listWorkflowRuns` / `cancelWorkflowRun` | Status, paginated list, and cancel helpers |
|
|
18
|
-
| `createWorkflowCommands` | Optional `CommandDefinition[]` for
|
|
19
|
-
| `enqueueWorkflow` / `createWorkflowCoordinator` | Persist queued work and atomically claim/renew/execute it across processes using `LeaseStore` |
|
|
18
|
+
| `createWorkflowCommands` | Optional `CommandDefinition[]` for direct/background/replay/status/list/cancel/resume and, when selected, schedule control |
|
|
19
|
+
| `enqueueWorkflow` / `startWorkflowBackground` / `createWorkflowCoordinator` | Persist queued work and atomically claim/renew/execute it across processes using `LeaseStore` |
|
|
20
|
+
| `createWorkflowSchedules` | Explicit ownership-scoped one-time/interval/host-calculated schedules over existing checkpoint/lease stores |
|
|
20
21
|
|
|
21
22
|
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).
|
|
22
23
|
|
|
@@ -28,24 +29,32 @@ Use `createWorkflowCoordinator()` when multiple processes share SQLite/PostgreSQ
|
|
|
28
29
|
|
|
29
30
|
## Inputs / request
|
|
30
31
|
|
|
31
|
-
`defineWorkflow({ id, nodes, edges, limits? })`:
|
|
32
|
+
`defineWorkflow({ id, revision, nodes, edges, limits? })`:
|
|
32
33
|
|
|
33
34
|
| Field | Notes |
|
|
34
35
|
| --- | --- |
|
|
35
36
|
| `id` | Stable workflow id (required) |
|
|
37
|
+
| `revision` | Non-empty host-authored definition revision (required); parent and nested revisions enter `definitionHash` |
|
|
36
38
|
| `nodes` | Record of node definitions (`kind` + typed fields) |
|
|
37
39
|
| `edges` | `[from, to]` pairs; must be acyclic; unknown ids rejected |
|
|
38
|
-
| `limits.maxNodes` | Default
|
|
39
|
-
| `limits.maxFanOut` | Default 64 |
|
|
40
|
-
| `limits.maxConcurrency` | Default 8 |
|
|
41
|
-
| `limits.maxNodeOutputBytes` | Default 4 MiB |
|
|
42
|
-
| `limits.maxCheckpointBytes` | Default 1 MiB |
|
|
40
|
+
| `limits.maxNodes` | Default 1,000 / hard cap 10,000 |
|
|
41
|
+
| `limits.maxFanOut` | Default 64 / hard cap 1,024 |
|
|
42
|
+
| `limits.maxConcurrency` | Default 8 / hard cap 256 |
|
|
43
|
+
| `limits.maxNodeOutputBytes` | Default 4 MiB / hard cap 16 MiB |
|
|
44
|
+
| `limits.maxCheckpointBytes` | Default 1 MiB / hard cap 8 MiB |
|
|
45
|
+
| `limits.maxNestedDepth` / hard cap | 8 / 32; inherited by child workflows |
|
|
46
|
+
| `limits.maxStateBytes` / hard cap | 64 KiB / 512 KiB |
|
|
47
|
+
| `limits.maxStateHistory` / hard cap | 32 / 128 state snapshots; updates stop before evidence would be discarded |
|
|
48
|
+
| `limits.maxReplayDepth` / hard cap | 8 / 32 lineage generations |
|
|
49
|
+
| `state.initial` / `state.schema` | Initial shared JSON object and optional host-validated schema |
|
|
50
|
+
|
|
51
|
+
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.
|
|
43
52
|
|
|
44
53
|
`runWorkflow(workflow, input, options?)`:
|
|
45
54
|
|
|
46
55
|
| Option | Notes |
|
|
47
56
|
| --- | --- |
|
|
48
|
-
| `concurrency` | Worker pool size
|
|
57
|
+
| `concurrency` | Worker pool size; positive safe integer, hard cap 256, and capped by the workflow limit |
|
|
49
58
|
| `checkpoints` | `WorkflowCheckpointAdapter` for save/load/list |
|
|
50
59
|
| `agentFactory` | `(agentName) => AgentSession` for agent nodes |
|
|
51
60
|
| `tools` | Tool registry/lookup for tool nodes |
|
|
@@ -56,29 +65,45 @@ Use `createWorkflowCoordinator()` when multiple processes share SQLite/PostgreSQ
|
|
|
56
65
|
| `signal` | Cancels the run and in-flight agent sessions |
|
|
57
66
|
| `onEvent` | Synchronous `WorkflowEvent` sink |
|
|
58
67
|
| `runId` | Caller-supplied id; otherwise generated (`wfr_…`) |
|
|
68
|
+
| `resume` | For suspended runs: `{ decision: "approve" | "deny", input?, expectedVersion }`; version is mandatory for an exact-once CAS claim |
|
|
69
|
+
| `validateResume` | Host validator for resume input; required when `suspend()` declares `resumeSchema` |
|
|
70
|
+
| `validateState` | Host validator for every initial/restored/updated state; required when workflow declares `state.schema` |
|
|
71
|
+
| `initialState` | Optional host initial state override; nested workflows receive parent state automatically |
|
|
59
72
|
|
|
60
|
-
`resumeWorkflow(workflow, { runId }, options)`
|
|
73
|
+
A function node returns `suspend({ reason, data?, resumeSchema? })` to persist `status: "suspended"`. Its next invocation receives `ctx.resume` only after an approved resume. `resumeWorkflow(workflow, { runId }, options)` validates schema/version/ownership/`definitionHash`, claims the checkpoint before node execution, and continues the suspended node. Denial persists terminal `denied` status without invoking it. Existing failed/aborted checkpoint resume remains available without a human decision.
|
|
74
|
+
|
|
75
|
+
Every node receives bounded `ctx.state`, `ctx.stateVersion`, and async `ctx.updateState(patch, { mode: "merge" | "replace" })`. Updates serialize, validate, redact, and snapshot before checkpoint save. `workflowNode({ workflow })` runs its child with the same ownership, agent/tool registries, execution policy, redactor, signal, checkpoints, and event bus; child state replaces parent state after success.
|
|
76
|
+
|
|
77
|
+
`replayWorkflow(workflow, { sourceRunId, fromNodeId, runId? }, options)` requires a succeeded source/node, creates a new checkpoint, copies terminal evidence outside the selected node's downstream closure, restores selected-node pre-state, and records `{ sourceRunId, fromNodeId, rootRunId, depth }`. Source evidence is untouched. Copying any prior nested/tool approval is rejected; replay from that approval node or earlier so Phase 8 approval executes again.
|
|
61
78
|
|
|
62
79
|
`createWorkflowCoordinator({ coordinatorId, workflows, checkpoints, leases, ... })` polls queued/running checkpoints with bounded pages, atomically claims each run, renews its lease, and aborts/fences work after lease loss. Key controls: `leaseTtlMs` (default 30s), `renewalIntervalMs` (default TTL/3), `pollIntervalMs` (default 1s), `maxConcurrentRuns` (default 4), and `pageSize` (default 100, maximum 500).
|
|
63
80
|
|
|
81
|
+
`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.
|
|
82
|
+
|
|
64
83
|
## Outputs / response / events
|
|
65
84
|
|
|
66
85
|
`runWorkflow` / `resumeWorkflow` resolve to `WorkflowRunResult`:
|
|
67
86
|
|
|
68
87
|
| Field | Notes |
|
|
69
88
|
| --- | --- |
|
|
70
|
-
| `runId`, `workflowId`, `status` | `succeeded` / `failed` / `aborted` |
|
|
89
|
+
| `runId`, `workflowId`, `status` | `queued` / `running` / `suspended` / `succeeded` / `failed` / `denied` / `aborted` |
|
|
71
90
|
| `outputs` | Map of succeeded node outputs |
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
91
|
+
| `state` | Final/current bounded shared JSON state |
|
|
92
|
+
| `lineage` | Replay source/root/node/depth record when this is a replay |
|
|
93
|
+
| `suspension` | Current/persisted `{ nodeId, reason, data?, resumeSchema?, requestedAt }` |
|
|
94
|
+
| `resume` | Attributable resume decision/input/version/time record |
|
|
95
|
+
| `version` | Checkpoint CAS identity shown to reviewers and required on suspended resume |
|
|
74
96
|
|
|
75
|
-
|
|
97
|
+
Schedule `onEvent` receives bounded-attribution `schedule_fired` or metadata-only `schedule_failed`; schedule input is never copied into these events.
|
|
98
|
+
|
|
99
|
+
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)`.
|
|
76
100
|
|
|
77
101
|
## Request/response example
|
|
78
102
|
|
|
79
103
|
```json
|
|
80
104
|
{
|
|
81
105
|
"id": "research-draft",
|
|
106
|
+
"revision": "2026-07-19.1",
|
|
82
107
|
"nodes": ["research", "draft"],
|
|
83
108
|
"edges": [["research", "draft"]],
|
|
84
109
|
"limits": { "maxNodes": 256, "maxFanOut": 32, "maxConcurrency": 4 }
|
|
@@ -111,6 +136,10 @@ import {
|
|
|
111
136
|
cancelWorkflowRun,
|
|
112
137
|
enqueueWorkflow,
|
|
113
138
|
createWorkflowCoordinator,
|
|
139
|
+
createWorkflowSchedules,
|
|
140
|
+
replayWorkflow,
|
|
141
|
+
workflowNode,
|
|
142
|
+
suspend,
|
|
114
143
|
} from "@arnilo/prism-workflows";
|
|
115
144
|
import { runRpcServer } from "@arnilo/prism";
|
|
116
145
|
import { createSqlitePersistence } from "@arnilo/prism-session-store-sqlite";
|
|
@@ -122,11 +151,21 @@ const research = agentNode({
|
|
|
122
151
|
const draft = functionNode({
|
|
123
152
|
execute: async (ctx) => `Draft from ${String(ctx.upstream.research)}`,
|
|
124
153
|
});
|
|
154
|
+
const publish = functionNode({
|
|
155
|
+
execute: async (ctx) => ctx.resume
|
|
156
|
+
? publishDraft(ctx.upstream.draft, ctx.resume.input)
|
|
157
|
+
: suspend({
|
|
158
|
+
reason: "publish",
|
|
159
|
+
data: { draft: ctx.upstream.draft },
|
|
160
|
+
resumeSchema: { type: "object", required: ["reviewer"] },
|
|
161
|
+
}),
|
|
162
|
+
});
|
|
125
163
|
|
|
126
164
|
const workflow = defineWorkflow({
|
|
127
165
|
id: "research-draft",
|
|
128
|
-
|
|
129
|
-
|
|
166
|
+
revision: "2026-07-19.1",
|
|
167
|
+
nodes: { research, draft, publish },
|
|
168
|
+
edges: [["research", "draft"], ["draft", "publish"]],
|
|
130
169
|
limits: { maxNodes: 256, maxFanOut: 32, maxConcurrency: 4 },
|
|
131
170
|
});
|
|
132
171
|
|
|
@@ -157,26 +196,58 @@ const result = await runWorkflow(workflow, { topic: "hooks" }, {
|
|
|
157
196
|
onEvent: (event) => sink.push(event),
|
|
158
197
|
});
|
|
159
198
|
|
|
160
|
-
// Durable resume after process restart
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
199
|
+
// Durable human resume after process restart. Use result.version shown to reviewer.
|
|
200
|
+
if (result.status === "suspended") {
|
|
201
|
+
await resumeWorkflow(workflow, { runId: result.runId }, {
|
|
202
|
+
checkpoints,
|
|
203
|
+
agentFactory: (name) => agents.resolve(name).createSession(),
|
|
204
|
+
ownership: { tenantId: "t1" },
|
|
205
|
+
resume: {
|
|
206
|
+
decision: "approve",
|
|
207
|
+
input: { reviewer: "Ada" },
|
|
208
|
+
expectedVersion: result.version,
|
|
209
|
+
},
|
|
210
|
+
validateResume: ({ value }) => validateResumePayload(value),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
166
213
|
|
|
167
214
|
await cancelWorkflowRun({
|
|
168
215
|
workflowId: workflow.id,
|
|
169
216
|
runId: result.runId,
|
|
217
|
+
workflow,
|
|
170
218
|
checkpoints,
|
|
171
219
|
ownership: { tenantId: "t1" },
|
|
172
220
|
});
|
|
173
221
|
|
|
174
222
|
// Optional host control via existing CLI/RPC CommandDefinition seam:
|
|
223
|
+
const schedules = createWorkflowSchedules({
|
|
224
|
+
store: persistence.checkpoints,
|
|
225
|
+
leases: persistence.leases,
|
|
226
|
+
checkpoints,
|
|
227
|
+
workflows: { [workflow.id]: workflow },
|
|
228
|
+
ownership: { tenantId: "t1", userId: "ops" },
|
|
229
|
+
ownerId: process.env.HOSTNAME ?? "scheduler-1",
|
|
230
|
+
});
|
|
231
|
+
await schedules.create({
|
|
232
|
+
id: "daily-research",
|
|
233
|
+
workflowId: workflow.id,
|
|
234
|
+
nextRunAt: "2026-07-17T00:00:00.000Z",
|
|
235
|
+
intervalMs: 86_400_000,
|
|
236
|
+
input: { topic: "hooks" },
|
|
237
|
+
});
|
|
238
|
+
// Host calls schedules.pollOnce() from an existing timer, or explicitly starts schedules.run({ signal }).
|
|
239
|
+
|
|
240
|
+
const replay = await replayWorkflow(workflow, {
|
|
241
|
+
sourceRunId: result.runId,
|
|
242
|
+
fromNodeId: "draft",
|
|
243
|
+
}, { checkpoints, ownership: { tenantId: "t1" }, agentFactory });
|
|
244
|
+
|
|
175
245
|
runRpcServer({
|
|
176
246
|
createSession,
|
|
177
247
|
commands: createWorkflowCommands({
|
|
178
248
|
workflows: { [workflow.id]: workflow },
|
|
179
249
|
checkpoints,
|
|
250
|
+
schedules,
|
|
180
251
|
runOptions: { ownership: { tenantId: "t1" }, agentFactory },
|
|
181
252
|
}),
|
|
182
253
|
});
|
|
@@ -187,27 +258,37 @@ runRpcServer({
|
|
|
187
258
|
- Workflow semantics stay in this optional package; generic checkpoint persistence and bounded event fan-in live in core.
|
|
188
259
|
- `ProductionPersistenceStore.checkpoints` and `.leases` are optional generic capabilities. First-party SQLite/PostgreSQL adapters own `prism_checkpoints` / `prism_leases`; workflows only adapt them.
|
|
189
260
|
- `createWorkflowEventBus()` delegates queueing, source fan-in, overflow, abort, and close behavior to core `createEventMultiplexer()`.
|
|
190
|
-
- `createWorkflowCommands()` is optional; hosts
|
|
261
|
+
- `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.
|
|
191
262
|
- Hosts may bridge `WorkflowEvent` into OpenTelemetry or custom sinks; there is no built-in TUI.
|
|
192
263
|
- Agent exclusivity is per session: one active `run()` at a time, same as core.
|
|
193
264
|
|
|
194
265
|
## Security and performance notes
|
|
195
266
|
|
|
196
|
-
- Definitions fail closed on cycles, unknown edges, self-edges, and `maxNodes` overflow.
|
|
197
|
-
- Fan-out length is bounded by `maxFanOut`; concurrency by `maxConcurrency
|
|
198
|
-
- Node outputs and checkpoints are byte
|
|
267
|
+
- 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.
|
|
268
|
+
- Fan-out length is bounded by `maxFanOut`; concurrency by `maxConcurrency`; every count/byte/runtime option has a finite hard cap.
|
|
269
|
+
- Node outputs, shared state/history, schedule input/records, and checkpoints are byte/count/depth bounded. Checkpoint size remains the final aggregate ceiling.
|
|
199
270
|
- Event buses use a bounded buffer (default 2048) with `close` / `drop_oldest` / `drop_newest` overflow.
|
|
200
|
-
- Checkpoints redact via `SecretRedactor` / `secrets` before save; resume rejects tenant, schema,
|
|
201
|
-
-
|
|
271
|
+
- Checkpoints redact suspension/resume payloads via `SecretRedactor` / `secrets` before save; resume rejects tenant, schema, definition-hash, and expected-version mismatch.
|
|
272
|
+
- Suspension requires a checkpoint adapter, consumes no worker/polling slot, and is ignored by distributed coordinators until explicit resume.
|
|
273
|
+
- Concurrent resumes race on checkpoint CAS before node execution; one wins and stale/duplicate reviewers fail closed. Approved tool nodes then re-run current `ExecutionPolicy`, so durable approval cannot grant stale permissions.
|
|
274
|
+
- `toolNode({ approval: { reason, data?, resumeSchema? } })` suspends before tool execution. Denial is terminal `denied`; no tool side effect occurs.
|
|
275
|
+
- `cancelWorkflowRun` requires the current workflow definition and exact tenant/account/user ownership. It verifies recursive definition hash before abort/mutation, then aborts local runs or writes a durable cancellation request for remotely leased work. Tenant-only or missing ownership cannot cancel a more-specific owned run.
|
|
276
|
+
- 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.
|
|
202
277
|
- Tool nodes attach `workflowId` / `nodeId` on `ExecutionAction.metadata` for approval/audit context.
|
|
203
|
-
-
|
|
278
|
+
- 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.
|
|
279
|
+
- 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.
|
|
280
|
+
- 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.
|
|
281
|
+
- Scheduler stores O(nodes + active outputs + bounded state history); ready-node work uses indegree maps, not repeated full scans.
|
|
204
282
|
- 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.
|
|
205
283
|
|
|
284
|
+
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.
|
|
285
|
+
|
|
206
286
|
## Related APIs
|
|
207
287
|
|
|
208
288
|
- Examples: `examples/workflow-research-and-review.ts`, `examples/workflow-parallel-research.ts`, `examples/workflow-tool-approval.ts`, `examples/workflow-multimodal-document.ts`, `examples/workflow-sqlite-resume.ts`, `examples/workflow-postgres-resume.ts`, `examples/workflow-event-sink.ts`, `examples/workflow-rpc-cancel.ts`, `examples/workflow-distributed-coordinator.ts` — offline runnable demos; PostgreSQL safely skips unless `PRISM_TEST_POSTGRES_URL` is set.
|
|
209
289
|
- [Workflow orchestration primitives](workflow-orchestration-primitives.md): Task 0–1 inventory and locked adapter contracts
|
|
210
|
-
- [Agent/session runtime](agent-session-runtime.md): `AgentSession.run()`, abort, subscribe
|
|
290
|
+
- [Agent/session runtime](agent-session-runtime.md): `AgentSession.run()`/`stream()`, abort, subscribe
|
|
291
|
+
- [Supervisor delegation](supervisors.md): bounded dynamic child selection.
|
|
211
292
|
- [Agent events](agent-events.md): core `AgentEvent` wrapped by `agent_event`
|
|
212
293
|
- [Session stores and branching](session-stores-and-branching.md): session `leafId` reuse on resume
|
|
213
294
|
- [CLI/RPC](cli-rpc.md): host control seam; wire `createWorkflowCommands()` into `runRpcServer`
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Working and semantic memory
|
|
2
|
+
|
|
3
|
+
## What it does
|
|
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.
|
|
6
|
+
|
|
7
|
+
## When to use it
|
|
8
|
+
|
|
9
|
+
Use it when a host needs durable per-tenant profile/state (working memory) or top-K semantic retrieval over prior thread entries. Do not use it as a replacement for observational memory compaction: observational memory compresses source-backed observations; semantic memory retrieves embeddings; working memory stores the current structured profile.
|
|
10
|
+
|
|
11
|
+
Ordinary Prism sessions do not require this package or any vector backend.
|
|
12
|
+
|
|
13
|
+
## Inputs / request
|
|
14
|
+
|
|
15
|
+
`createMemory(options)`:
|
|
16
|
+
|
|
17
|
+
| Field | Required | Meaning |
|
|
18
|
+
| --- | --- | --- |
|
|
19
|
+
| `tenantId` | yes | Tenant isolation key |
|
|
20
|
+
| `resourceId` | yes | Resource/user isolation key |
|
|
21
|
+
| `threadId` | for semantic ops | Thread isolation; optional for resource-scoped working memory |
|
|
22
|
+
| `embedder` | yes | Host-owned or package hash embedder |
|
|
23
|
+
| `vectorStore` / `workingStore` | no | Defaults to in-memory adapters |
|
|
24
|
+
| `schema` / `validateWorkingMemory` | no | Working-memory shape checks (JSON Schema subset or host hook) |
|
|
25
|
+
| `workingMemoryTemplate` | no | `{{path}}` template for context injection |
|
|
26
|
+
| `limits` | no | top-K, adjacent range, batch, payload, injected-token caps |
|
|
27
|
+
| `redactor` / `secrets` | no | Redact text/metadata before persist/inject |
|
|
28
|
+
|
|
29
|
+
Semantic indexing:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
await memory.remember({ entries: [{ id, text, metadata?, sequence? }] }, { wait?: boolean })
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Semantic recall:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
await memory.recall(query, { topK?, messageRange?, signal? })
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Outputs / response / events
|
|
42
|
+
|
|
43
|
+
| API | Result |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| `updateWorking` / `getWorking` | Versioned `WorkingMemoryRecord` |
|
|
46
|
+
| `remember` | `{ accepted, pending, done }` — default `wait: false` indexes asynchronously |
|
|
47
|
+
| `recall` | `{ hits, adjacent }` tenant/thread scoped |
|
|
48
|
+
| `createContextProvider()` | Inert `ContextProvider` blocks for working and/or semantic text |
|
|
49
|
+
| `createWorkingMemoryProcessor({ extract })` | Explicit host-invoked updater; never auto-runs |
|
|
50
|
+
|
|
51
|
+
No package-owned agent events are emitted. Injection uses existing context assembly only.
|
|
52
|
+
|
|
53
|
+
## Request/response example
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"tenantId": "t1",
|
|
58
|
+
"resourceId": "user-ada",
|
|
59
|
+
"threadId": "thread-1",
|
|
60
|
+
"working": { "name": "Ada", "preferences": { "format": "concise" } },
|
|
61
|
+
"recall": {
|
|
62
|
+
"query": "preferred response format",
|
|
63
|
+
"topK": 5,
|
|
64
|
+
"messageRange": 1
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Implementation example
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { createAgent, createMockProvider, providerDone, providerTextDelta } from "@arnilo/prism";
|
|
73
|
+
import { createHashEmbedder, createMemory } from "@arnilo/prism-memory";
|
|
74
|
+
|
|
75
|
+
const memory = createMemory({
|
|
76
|
+
tenantId: "t1",
|
|
77
|
+
resourceId: "user-ada",
|
|
78
|
+
threadId: "thread-1",
|
|
79
|
+
embedder: createHashEmbedder(),
|
|
80
|
+
workingMemoryTemplate: "Name: {{name}}; Format: {{preferences.format}}",
|
|
81
|
+
schema: {
|
|
82
|
+
type: "object",
|
|
83
|
+
properties: {
|
|
84
|
+
name: { type: "string" },
|
|
85
|
+
preferences: {
|
|
86
|
+
type: "object",
|
|
87
|
+
properties: { format: { type: "string" } },
|
|
88
|
+
required: ["format"],
|
|
89
|
+
additionalProperties: false,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
required: ["name"],
|
|
93
|
+
additionalProperties: false,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
await memory.updateWorking({ name: "Ada", preferences: { format: "concise" } });
|
|
98
|
+
await memory.remember({ entries: [{ id: "m1", text: "Prefers concise answers" }] });
|
|
99
|
+
|
|
100
|
+
const agent = createAgent({
|
|
101
|
+
model: { provider: "mock", model: "demo" },
|
|
102
|
+
provider: createMockProvider([providerTextDelta("Got it."), providerDone()]),
|
|
103
|
+
context: [memory.createContextProvider()],
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
PostgreSQL/pgvector:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { createPostgresMemoryStores, createMemory, createHashEmbedder } from "@arnilo/prism-memory";
|
|
111
|
+
|
|
112
|
+
const stores = await createPostgresMemoryStores({
|
|
113
|
+
connectionString: process.env.DATABASE_URL!,
|
|
114
|
+
schema: "prism_memory",
|
|
115
|
+
dimensions: 32,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const memory = createMemory({
|
|
119
|
+
tenantId: "t1",
|
|
120
|
+
resourceId: "user-ada",
|
|
121
|
+
threadId: "thread-1",
|
|
122
|
+
embedder: createHashEmbedder({ dimensions: 32 }),
|
|
123
|
+
workingStore: stores.workingStore,
|
|
124
|
+
vectorStore: stores.vectorStore,
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Extension and configuration notes
|
|
129
|
+
|
|
130
|
+
- Hosts wire the context provider into `AgentConfig.context` or `resolveContextProviders()`.
|
|
131
|
+
- The working-memory processor is opt-in and host-invoked; middleware is not required.
|
|
132
|
+
- `createHashEmbedder()` is for tests/demos only; production hosts supply a real `Embedder`.
|
|
133
|
+
- Observational memory (`@arnilo/prism-compaction-observational-memory`) remains unchanged and composable.
|
|
134
|
+
- Profile bundles do not include this package yet.
|
|
135
|
+
|
|
136
|
+
Shared conformance:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { runMemoryConformance, createHashEmbedder, createMemoryVectorStore, createMemoryWorkingStore } from "@arnilo/prism-memory";
|
|
140
|
+
|
|
141
|
+
await runMemoryConformance(() => ({
|
|
142
|
+
embedder: createHashEmbedder(),
|
|
143
|
+
vectorStore: createMemoryVectorStore(),
|
|
144
|
+
workingStore: createMemoryWorkingStore(),
|
|
145
|
+
}));
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Security and performance notes
|
|
149
|
+
|
|
150
|
+
- Every write/query/delete requires `tenantId` + `resourceId`; semantic paths also require `threadId`.
|
|
151
|
+
- Cross-tenant and cross-thread access is denied.
|
|
152
|
+
- Configure `secrets` / `redactor` so memory text and metadata cannot persist or inject raw canaries.
|
|
153
|
+
- Injected context is inert text — it cannot grant tools or permissions.
|
|
154
|
+
- Hard caps: top-K ≤ 32, messageRange ≤ 4, embed batch ≤ 128, injected tokens ≤ 8000, payload/working-memory byte limits enforced.
|
|
155
|
+
- Every embedding is a non-empty finite number vector. `embedBatched()`, in-memory `VectorStore` upserts/queries, and PostgreSQL/pgvector parameters reject NaN, ±Infinity, non-numbers, and wrong configured dimensions before similarity scoring or SQL. Custom adapters can call `assertFiniteVector(vector, label, expectedLength?)` at their trust boundary.
|
|
156
|
+
- Default `remember()` does not block agent completion; pass `{ wait: true }` when indexing must finish first.
|
|
157
|
+
- PostgreSQL live suite is gated by `PRISM_TEST_POSTGRES_URL` and requires the `vector` extension.
|
|
158
|
+
|
|
159
|
+
## Delegated-agent isolation
|
|
160
|
+
|
|
161
|
+
Supervisor child factories receive unique derived `resourceId` and `threadId` values. Construct each child's `createMemory()` facade from those exact values; never reuse parent memory scope or let model-supplied IDs select another resource.
|
|
162
|
+
|
|
163
|
+
## Related APIs
|
|
164
|
+
|
|
165
|
+
- [Supervisor delegation](supervisors.md): package-derived child resource/thread scope.
|
|
166
|
+
- [Retrieval-augmented generation](rag.md): bounded document chunks reuse this package's embed/vector contracts.
|
|
167
|
+
- [Context and skills](context-and-skills.md): `ContextProvider` injection seam.
|
|
168
|
+
- [Observational memory compaction package](compaction-observational-memory.md): source-backed observation/reflection memory distinction.
|
|
169
|
+
- [PostgreSQL persistence](postgres-persistence.md): session/run persistence; memory vectors live in this optional package instead.
|
|
170
|
+
- [Middleware hooks](middleware-hooks.md): reuse existing `context` hook if hosts transform injected blocks.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arnilo/prism",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "Agent harness for AI providers, agents, sessions, and tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -54,6 +54,10 @@
|
|
|
54
54
|
"types": "./dist/testing/run-ledger-conformance.d.ts",
|
|
55
55
|
"default": "./dist/testing/run-ledger-conformance.js"
|
|
56
56
|
},
|
|
57
|
+
"./testing/feedback": {
|
|
58
|
+
"types": "./dist/testing/feedback.d.ts",
|
|
59
|
+
"default": "./dist/testing/feedback.js"
|
|
60
|
+
},
|
|
57
61
|
"./node/config": {
|
|
58
62
|
"types": "./dist/node/config.d.ts",
|
|
59
63
|
"default": "./dist/node/config.js"
|
|
@@ -88,13 +92,14 @@
|
|
|
88
92
|
}
|
|
89
93
|
},
|
|
90
94
|
"bin": {
|
|
91
|
-
"prism": "
|
|
95
|
+
"prism": "dist/cli.js"
|
|
92
96
|
},
|
|
93
97
|
"files": [
|
|
94
98
|
"dist",
|
|
95
99
|
"!dist/__tests__",
|
|
96
100
|
"!dist/**/*.map",
|
|
97
101
|
"docs",
|
|
102
|
+
"templates",
|
|
98
103
|
"CHANGELOG.md"
|
|
99
104
|
],
|
|
100
105
|
"workspaces": [
|
|
@@ -108,6 +113,11 @@
|
|
|
108
113
|
"packages/coding-agent",
|
|
109
114
|
"packages/coding-security",
|
|
110
115
|
"packages/workflows",
|
|
116
|
+
"packages/evals",
|
|
117
|
+
"packages/memory",
|
|
118
|
+
"packages/rag",
|
|
119
|
+
"packages/server",
|
|
120
|
+
"packages/supervisor",
|
|
111
121
|
"packages/prism-*"
|
|
112
122
|
],
|
|
113
123
|
"scripts": {
|
|
@@ -116,7 +126,7 @@
|
|
|
116
126
|
"typecheck": "npm run build && npm run typecheck --workspaces --if-present && tsc -p examples --noEmit",
|
|
117
127
|
"test": "npm run build && node --test dist/__tests__/*.test.js && npm run test --workspaces --if-present",
|
|
118
128
|
"pack:dry-run": "npm pack --dry-run && npm run pack:dry-run --workspaces --if-present",
|
|
119
|
-
"test:postgres": "npm run test:postgres --workspace @arnilo/prism-session-store-postgres",
|
|
129
|
+
"test:postgres": "npm run test:postgres --workspace @arnilo/prism-session-store-postgres && npm run test:postgres --workspace @arnilo/prism-memory",
|
|
120
130
|
"release:dry-run": "npm run sdk:ready",
|
|
121
131
|
"release:check": "node scripts/release.mjs check",
|
|
122
132
|
"release:publish": "node scripts/release.mjs publish",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# __PROJECT_NAME__
|
|
2
|
+
|
|
3
|
+
Minimal Prism agent scaffold generated by `prism init`.
|
|
4
|
+
|
|
5
|
+
__PROVIDER_README_NOTE__
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install
|
|
11
|
+
npm test
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`npm test` always uses the built-in mock provider (no network, no credentials).
|
|
15
|
+
|
|
16
|
+
__NEXT_STEPS_LIVE__
|
|
17
|
+
|
|
18
|
+
## Layout
|
|
19
|
+
|
|
20
|
+
- `src/agent.ts` — creates the agent with the selected provider.
|
|
21
|
+
- `src/index.ts` — runs one prompt and prints `AgentRunResult.text`.
|
|
22
|
+
- `src/__tests__/agent.test.ts` — offline mock smoke test.
|
|
23
|
+
__OPTIONAL_DOCS__
|
|
24
|
+
## Notes
|
|
25
|
+
|
|
26
|
+
- Prism does not auto-load credentials, databases, telemetry, or tools.
|
|
27
|
+
- Keep real secrets out of git; `.gitignore` excludes `.env` and local stores.
|
|
28
|
+
- Docs: https://github.com/ashiqrniloy/prism
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__ENV_EXAMPLE__
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AgentRunResult } from "@arnilo/prism";
|
|
2
|
+
import { defineScorer, scoreRun } from "@arnilo/prism-evals";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Deterministic scorer example. Not wired into `npm start` — call after a run.
|
|
6
|
+
*/
|
|
7
|
+
export const greetsScorer = defineScorer({
|
|
8
|
+
id: "greets",
|
|
9
|
+
score: ({ result }) => ({
|
|
10
|
+
score: /\bhello\b/i.test(result.text) ? 1 : 0,
|
|
11
|
+
reason: "checks for a hello greeting",
|
|
12
|
+
}),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export async function scoreGreeting(result: AgentRunResult) {
|
|
16
|
+
return scoreRun({ result, scorers: [greetsScorer] });
|
|
17
|
+
}
|