@arnilo/prism 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/README.md +62 -26
- package/dist/agent-loops.d.ts +8 -1
- package/dist/agent-loops.js +57 -11
- package/dist/agents.js +212 -32
- package/dist/checkpoints.d.ts +11 -0
- package/dist/checkpoints.js +144 -0
- 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/compaction.js +9 -1
- package/dist/content.d.ts +121 -0
- package/dist/content.js +538 -0
- package/dist/contracts.d.ts +236 -11
- package/dist/contracts.js +8 -0
- package/dist/event-multiplexer.d.ts +23 -0
- package/dist/event-multiplexer.js +136 -0
- package/dist/execution-policy.d.ts +28 -0
- package/dist/execution-policy.js +24 -0
- package/dist/feedback.d.ts +48 -0
- package/dist/feedback.js +230 -0
- package/dist/index.d.ts +20 -6
- package/dist/index.js +13 -5
- package/dist/input.js +11 -1
- package/dist/leases.d.ts +8 -0
- package/dist/leases.js +111 -0
- package/dist/node/agent-definitions.js +3 -5
- package/dist/node/config.d.ts +1 -0
- package/dist/node/config.js +5 -3
- package/dist/node/contribution-discovery.js +5 -8
- package/dist/node/session-store-jsonl.js +8 -5
- package/dist/node/settings.js +2 -2
- package/dist/node/trust.js +2 -4
- package/dist/observability.d.ts +3 -0
- package/dist/observability.js +18 -0
- package/dist/providers/media.d.ts +44 -0
- package/dist/providers/media.js +126 -0
- package/dist/providers/openai-compatible.js +18 -119
- package/dist/providers/openai-primitives.d.ts +9 -0
- package/dist/providers/openai-primitives.js +129 -0
- package/dist/providers/transport.d.ts +40 -0
- package/dist/providers/transport.js +221 -0
- package/dist/redaction.js +40 -13
- package/dist/resources.d.ts +5 -0
- package/dist/resources.js +4 -0
- package/dist/structured-output.d.ts +11 -0
- package/dist/structured-output.js +59 -0
- package/dist/testing/feedback.d.ts +6 -0
- package/dist/testing/feedback.js +37 -0
- package/dist/testing/persistence-schema.d.ts +102 -0
- package/dist/testing/persistence-schema.js +487 -0
- package/dist/testing/provider-conformance.js +10 -1
- package/dist/testing/run-ledger-conformance.d.ts +33 -0
- package/dist/testing/run-ledger-conformance.js +178 -0
- package/dist/testing/session-store-conformance.d.ts +16 -0
- package/dist/testing/session-store-conformance.js +73 -0
- package/dist/tools.d.ts +17 -0
- package/dist/tools.js +29 -2
- package/docs/a2a.md +73 -0
- package/docs/agent-events.md +17 -10
- package/docs/agent-loops.md +11 -5
- package/docs/agent-session-runtime.md +15 -16
- package/docs/cli-rpc.md +36 -5
- package/docs/coding-agent-tools.md +43 -9
- package/docs/coding-security.md +88 -0
- package/docs/compaction-observational-memory.md +2 -0
- package/docs/context-and-skills.md +1 -0
- package/docs/credential-storage.md +177 -0
- package/docs/credentials-and-redaction.md +4 -3
- package/docs/database-persistence.md +52 -7
- package/docs/evaluations.md +122 -0
- package/docs/extensions.md +2 -2
- package/docs/host-security.md +33 -2
- package/docs/index.md +46 -18
- package/docs/input-and-prompt-assembly.md +6 -5
- package/docs/mcp-tools.md +184 -0
- package/docs/middleware-hooks.md +2 -0
- package/docs/migration.md +51 -28
- package/docs/model-registry.md +5 -3
- package/docs/multimodal-content.md +156 -0
- package/docs/observability.md +171 -0
- package/docs/performance.md +249 -1
- package/docs/persistence-credentials-multimodality-primitives.md +303 -0
- package/docs/postgres-persistence.md +143 -0
- package/docs/provider-conformance.md +18 -0
- package/docs/provider-layer.md +1 -1
- package/docs/provider-packages.md +2 -0
- package/docs/provider-primitives.md +281 -0
- package/docs/providers/ai-sdk.md +113 -0
- package/docs/providers/kimi.md +1 -0
- package/docs/providers/neuralwatt.md +1 -0
- package/docs/providers/openai-compatible.md +2 -1
- package/docs/providers/openai.md +8 -1
- package/docs/providers/opencode-go.md +1 -0
- package/docs/providers/openrouter.md +1 -0
- package/docs/providers/zai.md +1 -0
- package/docs/public-contracts.md +13 -5
- package/docs/rag.md +113 -0
- package/docs/release-and-install.md +237 -30
- package/docs/resource-loading.md +14 -4
- package/docs/review-coverage-2026-07-14.md +260 -0
- package/docs/review-coverage-2026-07-15.md +193 -0
- package/docs/run-ledger-conformance.md +96 -0
- package/docs/runs-and-usage.md +43 -4
- package/docs/server.md +139 -0
- package/docs/session-store-conformance.md +16 -0
- package/docs/session-stores-and-branching.md +1 -0
- package/docs/settings-auth-trust-security.md +6 -5
- package/docs/sqlite-persistence.md +123 -0
- package/docs/structured-output.md +9 -0
- package/docs/supervisors.md +71 -0
- package/docs/tool-conformance.md +1 -0
- package/docs/tool-execution-primitives.md +374 -0
- package/docs/tools.md +39 -1
- package/docs/workflow-orchestration-primitives.md +581 -0
- package/docs/workflow-tui-primitives.md +5 -0
- package/docs/workflows.md +293 -0
- package/docs/working-and-semantic-memory.md +169 -0
- package/package.json +43 -5
- 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
|
@@ -7,8 +7,9 @@ The agent/session runtime adds the minimal shared SDK surface for running provid
|
|
|
7
7
|
- `createAgent(config)`
|
|
8
8
|
- `createAgentSession(config)`
|
|
9
9
|
- `agent.createSession(config)`
|
|
10
|
-
- `session.run(input, options)`
|
|
11
|
-
- `session.prompt(input, options)`
|
|
10
|
+
- `session.run(input, options)` → `AgentRunResult`
|
|
11
|
+
- `session.prompt(input, options)` → `AgentRunResult`
|
|
12
|
+
- `session.stream(input, options)` → owned-run `AsyncIterable<AgentEvent>`
|
|
12
13
|
- `session.compact(options?)`
|
|
13
14
|
- `session.subscribe(options?)`
|
|
14
15
|
- `session.abort()`
|
|
@@ -46,7 +47,11 @@ string | Message | readonly Message[]
|
|
|
46
47
|
|
|
47
48
|
## Outputs / response / events
|
|
48
49
|
|
|
49
|
-
`session.
|
|
50
|
+
`session.run()` / `session.prompt()` resolve to an `AgentRunResult` with `sessionId`, `runId`, `status`, `text`, `content`, optional `message`/`usage`/`leafId`, and terminal `error`/`abortReason` when applicable. Callers may ignore the return value. Failed and aborted runs still emit their terminal events, then reject with `AgentRunError` whose `.result` carries the same shape.
|
|
51
|
+
|
|
52
|
+
`session.stream(input, options?)` subscribes first, starts exactly one run, yields only that run's events, and terminates when the run succeeds, fails, or aborts. Early consumer return aborts the owned run and releases the session. `SubscribeOptions.maxQueuedEvents` / `overflow` may be passed alongside `RunOptions`.
|
|
53
|
+
|
|
54
|
+
`session.subscribe(options?)` remains available for hosts that want a long-lived subscriber across runs. Subscribe before `run()` to observe that run's events. The consumer loop and `session.run()` must run concurrently (e.g. start the `for await` consumer, then `await Promise.all([consumer, session.run("Hi")])`): events are only emitted during a live run, so awaiting the subscribe loop before calling `run()` deadlocks. Prefer `session.stream()` when you only need one run's events. `SubscribeOptions.maxQueuedEvents` defaults to `1024` (minimum `1`) and caps events queued while the consumer is not awaiting `next()`. `SubscribeOptions.overflow` defaults to `"close"`; it clears queued payload events, delivers one `event_subscriber_overflow` notice to that subscriber, then closes it. `"drop_oldest"` keeps newest events; `"drop_newest"` ignores new events while full.
|
|
50
55
|
|
|
51
56
|
For a text-only provider turn, the runtime emits:
|
|
52
57
|
|
|
@@ -101,29 +106,22 @@ const agent = createAgent({
|
|
|
101
106
|
});
|
|
102
107
|
|
|
103
108
|
const session = agent.createSession({ id: "s1" });
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
109
|
+
const result = await session.run("Hi", { maxToolRounds: 1, compaction: { thresholdEntries: 20, keepRecentEntries: 6 }, retry: { maxAttempts: 3, baseDelayMs: 50 } });
|
|
110
|
+
console.log(result.text, result.usage?.totalTokens);
|
|
111
|
+
|
|
112
|
+
for await (const event of session.stream("Follow up")) console.log(event.type);
|
|
107
113
|
|
|
108
|
-
await session.run("Hi", { maxToolRounds: 1, compaction: { thresholdEntries: 20, keepRecentEntries: 6 }, retry: { maxAttempts: 3, baseDelayMs: 50 } });
|
|
109
114
|
await session.compact({ keepRecentEntries: 4 });
|
|
110
115
|
const branch = await session.entries();
|
|
111
116
|
await session.checkout(branch.at(-1)?.id);
|
|
112
117
|
const clone = await session.clone({ id: "s2" });
|
|
113
|
-
await reader;
|
|
114
118
|
```
|
|
115
119
|
|
|
116
120
|
## Extension and configuration notes
|
|
117
121
|
|
|
118
122
|
The runtime calls `assembleProviderInput()` on every turn and uses only runtime-consumed values supplied on `AgentConfig`: `instructions`, `systemPrompt`, `inputBuilder`, `promptBuilder`, `inputLayout`, `context`, selected `skills`, active `tools`, `middleware`, `resourceLoader`, metadata, `compaction`, `retry`, and `RunOptions.model`/`systemPrompt`/`inputLayout`/`compaction`/`retry`. Contributions remain inert until a host passes selected values into the agent config.
|
|
119
123
|
|
|
120
|
-
`AgentConfig` fields
|
|
121
|
-
|
|
122
|
-
| Field | Runtime behavior |
|
|
123
|
-
| --- | --- |
|
|
124
|
-
| `extensions` | Preserved on `agent.config` only. `createAgent()` / `session.run()` do not call `setup()`, load packages, or auto-register contributions. Load extensions with `createExtensionKernel()` before building config. |
|
|
125
|
-
| `settings` | Preserved on `agent.config` only. The runtime does not call `settings.get()`; hosts or provider packages read settings before passing concrete runtime options. |
|
|
126
|
-
| `credentials` | Preserved on `agent.config` only. The runtime does not call `credentials.resolve()`; provider adapters/request policies resolve credentials at the provider edge and pass exact secret values to redaction when needed. |
|
|
124
|
+
`AgentConfig` no longer accepts inert `extensions`, `settings`, or `credentials` fields. Load extensions with `createExtensionKernel()` before building config; read settings in the host before passing concrete runtime options; resolve credentials at the provider edge and pass exact secret values to redaction when needed.
|
|
127
125
|
|
|
128
126
|
The runtime calls `middleware.run("compaction", { context, result })` after a compaction strategy returns and before appending the standard compaction entry. Middleware can adjust the result summary/data, but the runtime still owns store append ordering and branch parent ids.
|
|
129
127
|
|
|
@@ -131,7 +129,7 @@ Provider request policy application is one ordered in-memory pass per provider t
|
|
|
131
129
|
|
|
132
130
|
The runtime calls `middleware.run("retry", { context, decision })` after the retry policy decision and before emitting `retry_scheduled`. Middleware can stop retrying or adjust the delay. Retry wraps only the current provider turn, reuses the same assembled request, and never retries after assistant output has been emitted.
|
|
133
131
|
|
|
134
|
-
`createAgent()` is a thin wrapper over explicit config. It does not
|
|
132
|
+
`createAgent()` is a thin wrapper over explicit config. It does not scan packages, resolve credentials, read settings, call `Extension.setup()`, or consult hidden registries. External `AgentDefinition` implementations can call it from their own `create()` method:
|
|
135
133
|
|
|
136
134
|
```ts
|
|
137
135
|
import { createAgent, createContributionRegistries } from "@arnilo/prism";
|
|
@@ -173,6 +171,7 @@ await agent.createSession().run("Hi", { model: overrideModel });
|
|
|
173
171
|
- [Tools](tools.md): host-owned tool harness used by the bounded runtime tool loop.
|
|
174
172
|
- [Middleware hooks](middleware-hooks.md): hooks that configured assembly/runtime can run.
|
|
175
173
|
- [CLI/RPC](cli-rpc.md): terminal and JSONL adapters over this runtime.
|
|
174
|
+
- [Workflows](workflows.md): optional DAG orchestration that calls `AgentSession.run()` for agent nodes.
|
|
176
175
|
|
|
177
176
|
`AgentConfig.loop` and `RunOptions.loop` select a replaceable per-run control loop (`singleShotLoop` default, or `generate-validate-revise` with host callbacks); see [Agent loops](agent-loops.md). `RunOptions.loop` wins over `AgentConfig.loop`. Built-in loops emit the same normal turn/message envelope around provider turns, and both add the first run input to live history once after the first provider turn so later turns see the same transcript shape.
|
|
178
177
|
|
package/docs/cli-rpc.md
CHANGED
|
@@ -2,23 +2,41 @@
|
|
|
2
2
|
|
|
3
3
|
## What it does
|
|
4
4
|
|
|
5
|
-
The `prism` bin is a thin adapter over `AgentSession
|
|
5
|
+
The `prism` bin is a thin adapter over `AgentSession` plus a tiny project scaffold:
|
|
6
6
|
|
|
7
7
|
- `prism -p "prompt"`: print assistant text deltas.
|
|
8
8
|
- `prism --mode json -p "prompt"`: write one normalized event envelope per line.
|
|
9
9
|
- `prism --mode rpc`: read LF-delimited JSON requests from stdin and write correlated JSON responses/events to stdout.
|
|
10
|
+
- `prism init <dir>`: create a minimal TypeScript project with one selected provider, `.env.example`, and one offline mock test.
|
|
10
11
|
|
|
11
|
-
It does not add a TUI, app tools, provider globals, extension discovery, resource discovery, or credential storage.
|
|
12
|
+
It does not add a TUI, app tools, provider globals, extension discovery, resource discovery, or credential storage. `init` uses Node standard-library filesystem APIs and checked-in templates only — no interactive prompts or template-engine dependency.
|
|
12
13
|
|
|
13
14
|
## When to use it
|
|
14
15
|
|
|
15
|
-
Use the CLI for terminal smoke tests, scriptable JSON event streams,
|
|
16
|
+
Use the CLI for terminal smoke tests, scriptable JSON event streams, simple non-Node clients that can speak newline-delimited JSON, and bootstrapping a tiny host project with `prism init`.
|
|
16
17
|
|
|
17
18
|
Use the SDK directly when an app needs custom providers, tools, resources, credentials, trust prompts, or UI behavior.
|
|
18
19
|
|
|
19
20
|
## Inputs / request
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
### `prism init`
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
prism init <dir> [--provider <name>] [--with-workflows] [--with-evals] [--force]
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
| Flag / arg | Purpose |
|
|
29
|
+
| --- | --- |
|
|
30
|
+
| `<dir>` | Destination directory (created if missing). |
|
|
31
|
+
| `--provider <name>` | `mock` (default), `openai`, `openrouter`, `kimi`, `zai`, `opencode-go`, or `neuralwatt`. |
|
|
32
|
+
| `--with-workflows` | Add `@arnilo/prism-workflows` and `src/workflows-example.ts`. |
|
|
33
|
+
| `--with-evals` | Add `@arnilo/prism-evals` and `src/evals-example.ts`. |
|
|
34
|
+
| `--force` | Overwrite generated files when the destination already exists. |
|
|
35
|
+
| `-h`, `--help` | Print init usage. |
|
|
36
|
+
|
|
37
|
+
Default generation installs only `@arnilo/prism` (mock provider). Selecting a real provider adds exactly one `@arnilo/prism-provider-*` package. Storage, telemetry, memory, and server packages are never added unless a later phase introduces an explicit flag for them. Rerunning without `--force` refuses non-empty destinations and existing generated files. `.env.example` contains placeholders only; `.gitignore` excludes `.env` and local stores.
|
|
38
|
+
|
|
39
|
+
### Run/RPC CLI flags
|
|
22
40
|
|
|
23
41
|
| Flag | Purpose |
|
|
24
42
|
| --- | --- |
|
|
@@ -127,6 +145,11 @@ Events streamed during a run keep the original prompt request id, even when an `
|
|
|
127
145
|
prism --provider mock --model demo -p "Hi"
|
|
128
146
|
prism --provider mock --mode json -p "Hi"
|
|
129
147
|
printf '{"id":"1","command":"prompt","params":{"input":"Hi"}}\n' | prism --provider mock --mode rpc
|
|
148
|
+
|
|
149
|
+
prism init my-agent
|
|
150
|
+
prism init my-agent --provider openai
|
|
151
|
+
prism init my-agent --provider openrouter --with-workflows --with-evals
|
|
152
|
+
cd my-agent && npm install && npm test
|
|
130
153
|
```
|
|
131
154
|
|
|
132
155
|
Programmatic hosts should use the public runtime directly:
|
|
@@ -147,6 +170,10 @@ CLI/RPC are adapters over `AgentSession`. They do not scan packages, import exte
|
|
|
147
170
|
|
|
148
171
|
RPC `command` executes only explicitly registered `CommandDefinition` values. `setModel` stores a model override for later prompt/follow-up calls. `compact`, `switchSession`, `forkSession`, `cloneSession`, and `checkout` call the existing session APIs.
|
|
149
172
|
|
|
173
|
+
Optional workflow control (from `@arnilo/prism-workflows`) registers `workflow.start`, `workflow.enqueue`, `workflow.replay`, `workflow.status`, `workflow.list`, `workflow.cancel`, and `workflow.resume` via `createWorkflowCommands({ workflows, checkpoints, runOptions? })`. Supplying an ownership-scoped `schedules` service additionally registers `schedule.create`, `schedule.list`, `schedule.pause`, `schedule.resume`, `schedule.trigger`, and `schedule.delete`. Pass the returned `CommandDefinition[]` into `runRpcServer({ commands })` the same way as observational-memory commands. Cancel aborts in-process runs through the package active-run registry; orphaned durable checkpoints still marked `running` are fail-closed to `aborted`.
|
|
174
|
+
|
|
175
|
+
Suspended workflow resume parameters are `{ workflowId, runId, decision: "approve" | "deny", input?, expectedVersion, ownership? }`. Read `expectedVersion` from `workflow.status`/`workflow.list`; stale or duplicate decisions fail checkpoint CAS before node execution. Ordinary recovery resume for failed/aborted runs remains backward-compatible without decision fields.
|
|
176
|
+
|
|
150
177
|
`forkSession` creates another handle for the same `sessionId` and selected `leafId`; it no longer overwrites the parent handle in the RPC map. Keep the returned `handleId` when a UI needs to switch among sibling branches. `switchSession` accepts `handleId` (preferred), `sessionId`, or `id`; with multiple branch handles, use `handleId` to avoid ambiguity. `checkout` requires `params.leafId`, calls `AgentSession.checkout(leafId)`, and keeps the active handle id unchanged while moving that handle to the existing leaf. `messages` returns entries for the active branch path.
|
|
151
178
|
|
|
152
179
|
## Security and performance notes
|
|
@@ -155,7 +182,10 @@ RPC `command` executes only explicitly registered `CommandDefinition` values. `s
|
|
|
155
182
|
- No hidden provider, credential, extension, resource, config, settings, or tool globals are created.
|
|
156
183
|
- No full TUI or sandbox is provided or implied.
|
|
157
184
|
- JSONL is processed line by line with Node stdlib; no parser dependency, worker, watcher, or queue is added.
|
|
158
|
-
- Unknown or malformed CLI/RPC input fails closed.
|
|
185
|
+
- Unknown or malformed CLI/RPC input fails closed. Workflow resume validates decision and positive `expectedVersion`; ownership remains host-selected and checkpoint-enforced.
|
|
186
|
+
- `prism init` refuses non-empty destinations without `--force`, keeps writes inside the destination root, and never executes downloaded code beyond the user's later `npm install`.
|
|
187
|
+
- Generated `.env.example` values are placeholders only; `.gitignore` excludes `.env` and local store files.
|
|
188
|
+
- Default generated install stays small (~27 MB with TypeScript tooling in a clean consumer install versus Mastra's measured 439 MB scaffold); unselected storage/telemetry/eval/workflow packages are omitted.
|
|
159
189
|
- Branch handles (`handleId`, `sessionId`, `leafId`) are identifiers only; do not encode credentials, tokens, provider objects, or secrets into them.
|
|
160
190
|
- Do not put resolved credential values, tokens, headers, or secrets in prompts, CLI flags, config, events, or docs examples.
|
|
161
191
|
|
|
@@ -169,6 +199,7 @@ RPC `command` executes only explicitly registered `CommandDefinition` values. `s
|
|
|
169
199
|
- [Resource loading](resource-loading.md): explicit resource loading primitives.
|
|
170
200
|
- [Credentials and redaction](credentials-and-redaction.md): secret redaction helpers and credential boundaries.
|
|
171
201
|
- [Observational memory compaction package](compaction-observational-memory.md): optional `om:status` and `om:view` command factories for explicitly wired hosts.
|
|
202
|
+
- [Workflows](workflows.md): optional `createWorkflowCommands()` for direct/background/replay/status/cancel/resume and selected schedule control over the same RPC `command` seam.
|
|
172
203
|
|
|
173
204
|
The CLI records flags but does not auto-load project-local resources, extensions, tools, or config. The two system/project prompt files are the exception: in print/json modes the CLI auto-loads `<workspaceRoot>/AGENTS.md` (trust-gated) and an app-supplied `SYSTEM.md` layer as `AgentConfig.systemPrompt` layers composed with `--system` (base); `--no-agents-md` / `--no-system-md` skip them and `--agents-md-file` / `--system-md-file` override the paths. The CLI does not default `globalRoot` to the user's home directory — pass it from a host adapter or use `--agents-config <path>` for the app-config bundle layout. RPC mode does not auto-read these files (the host owns the session factory). Hosts must make explicit trust and permission decisions before wiring any other local loading.
|
|
174
205
|
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
| `createReadOnlyTools(cwd, options?)` | Read-only subset: `read` only. |
|
|
15
15
|
| `createAllTools(cwd, options?)` | Every tool the package provides (currently identical to `createCodingTools`). |
|
|
16
16
|
| `detectSupportedImageMimeType(buf)` / `detectSupportedImageMimeTypeFromFile(path)` | Magic-byte image MIME detection (PNG/JPEG/GIF/WebP/BMP) used by `read`. |
|
|
17
|
+
| `DEFAULT_MAX_IMAGE_BYTES` | Default `read` image size ceiling (10 MB). |
|
|
18
|
+
| `TransformImage` / `TransformImageInput` | Types for the optional `read` `transformImage` callback. |
|
|
17
19
|
| `withFileMutationQueue(path, fn)` | Per-path serialization primitive re-exported for hosts. |
|
|
18
20
|
|
|
19
21
|
Each factory returns a plain `ToolDefinition` (no auto-registration). Register what you need:
|
|
@@ -29,7 +31,19 @@ const tools = createToolRegistry(createCodingTools(process.cwd()));
|
|
|
29
31
|
|
|
30
32
|
Use this package when a host wants ready-made coding tools for an agent, session, or run, registered explicitly into a `ToolRegistry` and dispatched through the normal Prism tool harness. The tools perform **real** shell and filesystem operations on the host — they are not mocked or sandboxed. Use the individual factories when you need per-tool options or custom operation backends; use the aggregators when you want the default set.
|
|
31
33
|
|
|
32
|
-
Do not use this package as a sandbox, permission policy, secret store, or provider loop. Prism gates tool dispatch with `PermissionPolicy` / `ToolValidator` / trust policies;
|
|
34
|
+
Do not use this package as a sandbox, permission policy, secret store, or provider loop. Prism gates tool dispatch with `PermissionPolicy` / `ToolValidator` / trust policies; pass an optional `ExecutionPolicy` (for example from `@arnilo/prism-coding-security`) for path/command approval before side effects. Do not register these tools for an untrusted provider.
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { createCodingTools } from "@arnilo/prism-coding-agent";
|
|
38
|
+
import { createCodingApprovalPolicy } from "@arnilo/prism-coding-security";
|
|
39
|
+
|
|
40
|
+
const tools = createCodingTools(workspaceRoot, {
|
|
41
|
+
executionPolicy: createCodingApprovalPolicy({
|
|
42
|
+
roots: [workspaceRoot],
|
|
43
|
+
approve: async ({ action }) => host.confirm(action),
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
```
|
|
33
47
|
|
|
34
48
|
### pi name mapping
|
|
35
49
|
|
|
@@ -40,7 +54,7 @@ Do not use this package as a sandbox, permission policy, secret store, or provid
|
|
|
40
54
|
| `write` | `write` |
|
|
41
55
|
| `edit` | `edit` |
|
|
42
56
|
|
|
43
|
-
##
|
|
57
|
+
## Inputs / request
|
|
44
58
|
|
|
45
59
|
### `shell`
|
|
46
60
|
|
|
@@ -77,16 +91,36 @@ Read a text or image file.
|
|
|
77
91
|
| `offset` | `number` | Line to start reading from (1-indexed). |
|
|
78
92
|
| `limit` | `number` | Maximum number of lines to read. |
|
|
79
93
|
|
|
80
|
-
**Outputs:** text files become a single `TextContent`, truncated to `maxLines`/`maxBytes` (defaults 2000 lines / 50 KB) with a `Use offset=N to continue` footer when more remains. Image files (PNG/JPEG/GIF/WebP/BMP by magic bytes) become `[TextContent note, ImageContent]` with base64 `data` and `mimeType`. Read failures (missing file, offset beyond end, abort) are error results.
|
|
94
|
+
**Outputs:** text files become a single `TextContent`, truncated to `maxLines`/`maxBytes` (defaults 2000 lines / 50 KB) with a `Use offset=N to continue` footer when more remains. Image files (PNG/JPEG/GIF/WebP/BMP by **magic bytes**, not extension) become `[TextContent note, ImageContent]` with base64 `data` and `mimeType`. Oversize images are rejected by `stat` (when available) or `buffer.length` against `maxImageBytes` (default 10 MB) before base64 encoding. An optional `transformImage` callback lets hosts resize or re-encode images without adding image-processing dependencies to the base package. Read failures (missing file, offset beyond end, oversize image, abort) are error results.
|
|
95
|
+
|
|
96
|
+
`read` tool options (via `createReadTool(cwd, options)` or `ToolsOptions.read`):
|
|
97
|
+
|
|
98
|
+
| Option | Default | Purpose |
|
|
99
|
+
| --- | --- | --- |
|
|
100
|
+
| `maxImageBytes` | `DEFAULT_MAX_IMAGE_BYTES` (10 MB) | Reject image reads larger than this many bytes. |
|
|
101
|
+
| `transformImage` | — | Host callback `( { buffer, mimeType } ) => Promise<Buffer>` run after read, before base64. |
|
|
102
|
+
| `autoResizeImages` | — | **Deprecated.** Ignored unless `transformImage` is also set (use `transformImage` instead). |
|
|
103
|
+
| `maxLines` / `maxBytes` | 2000 / 50 KB | Text head truncation limits. |
|
|
104
|
+
| `operations` | local fs | Pluggable `ReadOperations` backend. |
|
|
105
|
+
| `executionPolicy` | — | Structured pre-execution policy (see [Coding security](coding-security.md)). |
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { createReadTool, DEFAULT_MAX_IMAGE_BYTES } from "@arnilo/prism-coding-agent";
|
|
109
|
+
|
|
110
|
+
const read = createReadTool(cwd, {
|
|
111
|
+
maxImageBytes: DEFAULT_MAX_IMAGE_BYTES,
|
|
112
|
+
transformImage: async ({ buffer, mimeType }) => host.resizeImage(buffer, mimeType),
|
|
113
|
+
});
|
|
114
|
+
```
|
|
81
115
|
|
|
82
116
|
`read` result `metadata`:
|
|
83
117
|
|
|
84
118
|
| Field | Present when | Purpose |
|
|
85
119
|
| --- | --- | --- |
|
|
86
120
|
| `truncation` | text reads | `TruncationResult`. |
|
|
87
|
-
| `image` | image reads | `{ mimeType, resized
|
|
121
|
+
| `image` | image reads | `{ mimeType, resized, bytes }`. `resized` is `true` when `transformImage` ran. |
|
|
88
122
|
|
|
89
|
-
> `autoResizeImages` is
|
|
123
|
+
> `autoResizeImages` is deprecated. It has no effect without `transformImage`; use `transformImage` for host-owned resizing.
|
|
90
124
|
|
|
91
125
|
### `write`
|
|
92
126
|
|
|
@@ -187,18 +221,18 @@ const remoteWrite = createWriteTool("/repo", {
|
|
|
187
221
|
## Extension and configuration notes
|
|
188
222
|
|
|
189
223
|
- **Pluggable operation backends.** Every tool accepts an `operations` seam so a host can delegate to a remote system (e.g. SSH) while keeping the tool's matching/serialization behavior: `BashOperations` (`shell`), `ReadOperations` (`read`), `WriteOperations` (`write`), `EditOperations` (`edit`).
|
|
190
|
-
- **Per-tool options.** `ShellToolOptions` (`shellPath`, `commandPrefix`, `maxLines`, `maxBytes`, `tempFilePrefix`, `operations`, `spawnHook`); `ReadToolOptions` (`operations`, `
|
|
191
|
-
- **Aggregator options.** `ToolsOptions` (`{ shell?, read?, write?, edit? }`) threads each sub-object to the matching tool.
|
|
224
|
+
- **Per-tool options.** `ShellToolOptions` (`shellPath`, `commandPrefix`, `maxLines`, `maxBytes`, `tempFilePrefix`, `operations`, `spawnHook`, `executionPolicy`); `ReadToolOptions` (`operations`, `maxImageBytes`, `transformImage`, `maxLines`, `maxBytes`, `executionPolicy`; `autoResizeImages` deprecated); `WriteToolOptions` (`operations`, `executionPolicy`); `EditToolOptions` (`operations`, `executionPolicy`).
|
|
225
|
+
- **Aggregator options.** `ToolsOptions` (`{ executionPolicy?, shell?, read?, write?, edit? }`) threads each sub-object to the matching tool. `createCodingTools()`, `createAllTools()`, and `createReadOnlyTools()` apply the shared policy unless that tool has an explicit per-tool override.
|
|
192
226
|
- **`ToolsOptions`** and the per-tool option types are exported from the package barrel for host configuration.
|
|
193
227
|
- No auto-discovery or manifest registration: import and register explicitly. This package registers no extensions and owns no globals (the mutation queue is a process-wide per-path map — see `ponytail:` note in the source).
|
|
194
228
|
|
|
195
229
|
## Security and performance notes
|
|
196
230
|
|
|
197
|
-
- **Host shell/filesystem access.** These tools run real commands and read/write real files. They provide **no sandbox**. Gate them with Prism `PermissionPolicy` / `ToolValidator` / trust policies before registering them for any provider turn. See [Host security guide](host-security.md) and [Security/auth/trust](settings-auth-trust-security.md).
|
|
231
|
+
- **Host shell/filesystem access.** These tools run real commands and read/write real files. They provide **no sandbox**. Gate them with Prism `PermissionPolicy` / `ToolValidator` / trust policies before registering them for any provider turn. Shared `executionPolicy` applies to both full and read-only aggregators before filesystem/process side effects. See [Host security guide](host-security.md) and [Security/auth/trust](settings-auth-trust-security.md).
|
|
198
232
|
- **Non-zero exit is not an error.** A failing command is a normal `shell` result (exit code in metadata); only timeout/abort/spawn failures are error results. Do not assume `error == undefined` means the command succeeded.
|
|
199
233
|
- **Bounded output.** `shell`/`read` accumulate output into a rolling tail bounded by `maxLines`/`maxBytes`; oversized output spills to a temp file (`fullOutputPath`), so memory use is bounded regardless of command output size.
|
|
200
234
|
- **Per-path serialization.** Concurrent mutations to the same file serialize; concurrent mutations to different files do not block each other. The queue is a process-wide map — across sessions in one process, same-path writes still serialize (upgrade path: scope per registry if throughput matters).
|
|
201
|
-
- **
|
|
235
|
+
- **Bounded image reads.** `read` rejects images over `maxImageBytes` (default 10 MB) by `stat` before read when possible; MIME is detected from magic bytes only. Optional `transformImage` is host-owned — the base package has no image-processing dependency.
|
|
202
236
|
|
|
203
237
|
## Related APIs
|
|
204
238
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Coding execution approval and sandboxing
|
|
2
|
+
|
|
3
|
+
## What it does
|
|
4
|
+
|
|
5
|
+
`@arnilo/prism-coding-security` is an optional package that supplies structured execution policy for `@arnilo/prism-coding-agent` tools. It complements name-based `PermissionPolicy` at dispatch time with path/command context checked **inside** each tool before side effects.
|
|
6
|
+
|
|
7
|
+
| Export | Purpose |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `createCodingApprovalPolicy(options)` | Returns an `ExecutionPolicy` with trusted roots, read-only mode, command allow/deny rules, approval caching, and timeout/abort-aware approval waits. |
|
|
10
|
+
| `createSandboxBashOperations(adapter)` | Maps a host-owned `SandboxAdapter` to coding-agent `BashOperations` for delegated shell execution. |
|
|
11
|
+
| `assertPathInsideRoots`, `isPathInsideReal` | Symlink-aware path containment helpers. |
|
|
12
|
+
| `evaluateCommandRules`, `hasShellMetacharacters` | Command classification helpers. |
|
|
13
|
+
|
|
14
|
+
Core contracts live in `@arnilo/prism`:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import type { ExecutionAction, ExecutionPolicy, ExecutionDecision } from "@arnilo/prism";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## When to use it
|
|
21
|
+
|
|
22
|
+
Use this package when coding tools need path scoping, human approval, command rules, or a pluggable sandbox backend. Wire the returned policy through `createCodingTools(cwd, { executionPolicy })` or per-tool `executionPolicy` options.
|
|
23
|
+
|
|
24
|
+
Prism does **not** claim OS-level isolation unless the host provides a sandbox adapter. Default policy denies shell/write/edit without an `approve` callback and rejects paths outside configured roots. Coding shell definitions are marked `exclusive: true`, matching the approval policy's shell decision, so a single-shot turn containing shell work runs sequentially even when `toolConcurrency > 1`. Non-shell turns retain configured parallelism.
|
|
25
|
+
|
|
26
|
+
## Inputs / request
|
|
27
|
+
|
|
28
|
+
| Option | Default | Purpose |
|
|
29
|
+
| --- | --- | --- |
|
|
30
|
+
| `roots` | required | Realpath-contained filesystem roots. |
|
|
31
|
+
| `readOnly` | `false` | Deny shell/write/edit actions. |
|
|
32
|
+
| `commandRules` | `[]` | Ordered allow/deny/approval command classification. |
|
|
33
|
+
| `approve` | none | Host callback for actions not statically allowed; omission fails closed. |
|
|
34
|
+
| `approvalCacheScope` | `"none"` | Optional `run` or `session` decision cache scope. |
|
|
35
|
+
| `approvalTimeoutMs` | `30000` | Bound approval wait; caller abort also cancels it. |
|
|
36
|
+
|
|
37
|
+
`run` caching keys decisions by the tool execution context's `runId`; `session` uses `sessionId`. Coding tools pass both identities to the policy. A missing/empty identity disables caching for that check rather than creating a global bucket. Identical actions in different runs/sessions never share approvals or denials.
|
|
38
|
+
|
|
39
|
+
## Outputs / response / events
|
|
40
|
+
|
|
41
|
+
`createCodingApprovalPolicy()` returns an `ExecutionPolicy`. Allowed checks return `ExecutionDecision { allowed: true }`; denied checks include a stable reason; shell decisions set `exclusive: true`. Sandbox adapters return coding-agent-compatible `BashOperations`, receive `onData(Buffer)` for ordered stdout/stderr forwarding through the shell tool's existing bounded accumulator, and never grant policy approval themselves.
|
|
42
|
+
|
|
43
|
+
## Request/response example
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"action": { "kind": "shell", "operation": "execute", "command": "npm test", "paths": [] },
|
|
48
|
+
"decision": { "allowed": true, "exclusive": true }
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Implementation example
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { createCodingTools } from "@arnilo/prism-coding-agent";
|
|
56
|
+
import { createCodingApprovalPolicy, createSandboxBashOperations } from "@arnilo/prism-coding-security";
|
|
57
|
+
|
|
58
|
+
const policy = createCodingApprovalPolicy({
|
|
59
|
+
roots: [workspaceRoot],
|
|
60
|
+
approve: async ({ action, signal }) => ui.confirm(action, { signal }),
|
|
61
|
+
approvalCacheScope: "run",
|
|
62
|
+
approvalTimeoutMs: 60_000,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const tools = createCodingTools(workspaceRoot, {
|
|
66
|
+
executionPolicy: policy,
|
|
67
|
+
shell: {
|
|
68
|
+
operations: createSandboxBashOperations(mySandboxAdapter),
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Extension and configuration notes
|
|
74
|
+
|
|
75
|
+
Policies are ordinary host values: attach one globally through `createCodingTools()`/`createReadOnlyTools()` or per tool. A per-tool policy overrides the shared policy. `SandboxAdapter` is replaceable and host-owned; approval policy and sandboxing are separate layers.
|
|
76
|
+
|
|
77
|
+
Callback approval remains process-local. For approval that must survive restart, wrap the action in an opted-in workflow `toolNode({ approval: { reason, data?, resumeSchema? } })`. The workflow persists `suspended` state before any tool side effect. After explicit approve, it recomputes the action and invokes this package's current `ExecutionPolicy`; durable approval never populates or bypasses the process-local approval cache. Adapters should emit chunks through `request.onData` as they arrive and honor `request.signal`/`request.timeout`; buffering is unnecessary. Default caching is `none`; use run-scoped caching only when repeated approval within one run is desired, and session scope only when that wider lifecycle is intentional.
|
|
78
|
+
|
|
79
|
+
## Security and performance notes
|
|
80
|
+
|
|
81
|
+
Containment resolves symlinks and rejects paths outside roots. Command rules are not a shell parser; shell metacharacters require approval. Approval waits and subprocess execution honor abort/timeouts. Durable workflow denial/cancellation is terminal and attributable; approved resume still fails if roots, command rules, read-only mode, or other policy changed while suspended. Cache keys are fixed-size SHA-256 digests of selected identity plus action shape; caches remain process-local, retain at most 1,000 decisions with oldest-entry eviction, and have no default/global mode. Path checks and cache lookup are local; sandbox latency belongs to the supplied adapter.
|
|
82
|
+
|
|
83
|
+
## Related APIs
|
|
84
|
+
|
|
85
|
+
- [Coding agent tools](coding-agent-tools.md)
|
|
86
|
+
- [Host security guide](host-security.md)
|
|
87
|
+
- [Tool execution primitives](tool-execution-primitives.md)
|
|
88
|
+
- [Security/auth/trust](settings-auth-trust-security.md)
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
Current status: ledger/projection/render/recall utilities, explicit worker runtime, fast compaction strategy, inert extension helper, recall tool, and status/view command factories are available.
|
|
8
8
|
|
|
9
|
+
This package is distinct from `@arnilo/prism-memory` working/semantic memory: observational memory compresses and recalls source-backed observations/reflections; semantic memory retrieves embeddings; working memory stores the current structured profile/state. Hosts may compose both.
|
|
10
|
+
|
|
9
11
|
## When to use it
|
|
10
12
|
|
|
11
13
|
Use it when a host wants to opt in to long-session memory that records observations/reflections as session custom entries, renders prepared memory during compaction, and supports exact-id recall.
|
|
@@ -179,6 +179,7 @@ Use `activateAllCapabilities: true` only as a temporary all-skills/all-tools com
|
|
|
179
179
|
- [Agent/session runtime](agent-session-runtime.md): consumes host-selected context providers and skills from explicit agent config.
|
|
180
180
|
- [Input and prompt assembly](input-and-prompt-assembly.md): default prompt builder and provider-input assembly helper.
|
|
181
181
|
- [Instruction injection](instruction-injection.md): package injectors contribute `contextBlocks` that merge after host+skill provider blocks.
|
|
182
|
+
- [Retrieval-augmented generation](rag.md): optional retrieved citations contribute through the same explicit inert context seam.
|
|
182
183
|
- [Public contracts](public-contracts.md): `ContextProvider`, `ContextResolutionContext`, `ContextBlock`, `Skill`, `SkillRegistry`, `PromptBuilder`, and `PromptBuildRequest`.
|
|
183
184
|
- [Middleware hooks](middleware-hooks.md): `context` and `prompt_build` hooks.
|
|
184
185
|
- [Contribution registries](contribution-registries.md): inert context provider and skill contributions.
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Credential storage
|
|
2
|
+
|
|
3
|
+
## What it does
|
|
4
|
+
|
|
5
|
+
The optional `@arnilo/prism-credentials-node` package ships host-owned credential persistence for Node.js CLI and desktop apps:
|
|
6
|
+
|
|
7
|
+
- **Encrypted file store** — AES-256-GCM envelope with scrypt KDF, atomic rename writes, versioned on-disk format
|
|
8
|
+
- **System keychain store** — cross-platform secret service via `@napi-rs/keyring@^1.3.0`
|
|
9
|
+
- **Stored credential resolver** — `createStoredCredentialResolver(store)` for explicit resolver chains
|
|
10
|
+
- **OAuth adapter** — extends the core `OAuthCredentialStore` seam with `get`/`delete` for refresh flows
|
|
11
|
+
|
|
12
|
+
Factories:
|
|
13
|
+
|
|
14
|
+
- `openEncryptedCredentialStore(options)` / `createEncryptedCredentialStore(options)`
|
|
15
|
+
- `createKeychainCredentialStore(options)`
|
|
16
|
+
- `createStoredCredentialResolver(store)`
|
|
17
|
+
- `createOAuthCredentialStoreAdapter(store)`
|
|
18
|
+
- `rotateEncryptedCredentialStorePassphrase(options)`
|
|
19
|
+
|
|
20
|
+
Core `@arnilo/prism` remains storage-free. Hosts choose a backend explicitly at startup; there is no global credential singleton and no silent fallback from keychain to plaintext file storage.
|
|
21
|
+
|
|
22
|
+
## When to use it
|
|
23
|
+
|
|
24
|
+
Use this package when a host needs durable credentials beyond `createMemoryCredentialStore()`:
|
|
25
|
+
|
|
26
|
+
- local CLI tools storing API keys or OAuth tokens between runs
|
|
27
|
+
- desktop hosts integrating with macOS Keychain, Windows Credential Manager, or Linux Secret Service
|
|
28
|
+
- integration tests that need encrypted reopen semantics without a live keychain
|
|
29
|
+
|
|
30
|
+
Do **not** use it when credentials should live in a remote vault, HSM, or cloud secret manager — implement `CredentialResolver` against that service instead.
|
|
31
|
+
|
|
32
|
+
## Inputs / request
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import {
|
|
36
|
+
openEncryptedCredentialStore,
|
|
37
|
+
createKeychainCredentialStore,
|
|
38
|
+
} from "@arnilo/prism-credentials-node";
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Encrypted file
|
|
42
|
+
|
|
43
|
+
| Field | Type | Purpose |
|
|
44
|
+
| --- | --- | --- |
|
|
45
|
+
| `path` | `string` | Vault file path. Parent directories are created as needed. |
|
|
46
|
+
| `getPassphrase` | `() => string \| Promise<string>` | Host-owned passphrase retrieval. Never logged by the adapter. |
|
|
47
|
+
| `scrypt` | `{ N?, r?, p?, keyLength? }` | Optional KDF tuning. Defaults: `N=32768`, `r=8`, `p=1`, `keyLength=32`. Minimum `N=16384`. |
|
|
48
|
+
| `fileMode` | `number` | Unix mode for newly written files. Defaults to `0o600`. |
|
|
49
|
+
|
|
50
|
+
### System keychain
|
|
51
|
+
|
|
52
|
+
| Field | Type | Purpose |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| `service` | `string` | Keychain service name (application identifier). |
|
|
55
|
+
| `namespace` | `string` | Optional prefix separating environments or tenants within one service. |
|
|
56
|
+
| `timeoutMs` | `number` | Operation timeout. Defaults to `5000`. |
|
|
57
|
+
|
|
58
|
+
## Outputs / response / events
|
|
59
|
+
|
|
60
|
+
Both backends implement `StoredCredentialStore`:
|
|
61
|
+
|
|
62
|
+
| Method | Behavior |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| `set(record)` / `get(request)` / `delete(request)` | Namespaced by `(provider, name)` for API keys and bearer tokens. |
|
|
65
|
+
| `setOAuth(provider, credentials, accountId?)` | Stores OAuth tokens per provider/account. |
|
|
66
|
+
| `getOAuth(provider, accountId?)` / `deleteOAuth(...)` | Reads or removes OAuth rows. |
|
|
67
|
+
| `resolve(request)` | `CredentialResolver` compatibility via `createStoredCredentialResolver`. |
|
|
68
|
+
|
|
69
|
+
Encrypted file stores also expose:
|
|
70
|
+
|
|
71
|
+
- `reload()` — re-read and decrypt from disk
|
|
72
|
+
- `flush()` — force rewrite of the encrypted envelope
|
|
73
|
+
|
|
74
|
+
Errors are explicit and fail closed:
|
|
75
|
+
|
|
76
|
+
| Error | Code | When |
|
|
77
|
+
| --- | --- | --- |
|
|
78
|
+
| `CredentialDecryptError` | `credential_decrypt_failed` | Wrong passphrase or tampered ciphertext |
|
|
79
|
+
| `CredentialStoreLockedError` | `credential_store_locked` | Keychain denied or locked |
|
|
80
|
+
| `CredentialStoreUnavailableError` | `credential_store_unavailable` | No OS secret service |
|
|
81
|
+
| `CredentialStoreTimeoutError` | `credential_store_timeout` | Keychain call exceeded `timeoutMs` |
|
|
82
|
+
| `WeakKdfParametersError` | `weak_kdf_parameters` | scrypt work factor below minimum |
|
|
83
|
+
|
|
84
|
+
## Request/response example
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"path": "./credentials.vault",
|
|
89
|
+
"fileMode": 384,
|
|
90
|
+
"keychain": {
|
|
91
|
+
"service": "my-app",
|
|
92
|
+
"namespace": "production",
|
|
93
|
+
"timeoutMs": 5000
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
On-disk envelope (illustrative — ciphertext is base64, secrets are not plaintext):
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"version": 1,
|
|
103
|
+
"kdf": { "algorithm": "scrypt", "N": 32768, "r": 8, "p": 1, "salt": "...", "keyLength": 32 },
|
|
104
|
+
"cipher": { "algorithm": "aes-256-gcm", "iv": "..." },
|
|
105
|
+
"ciphertext": "..."
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Implementation example
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import {
|
|
113
|
+
createExplicitCredentialResolver,
|
|
114
|
+
refreshOAuthCredential,
|
|
115
|
+
resolveCredentialValue,
|
|
116
|
+
} from "@arnilo/prism";
|
|
117
|
+
import {
|
|
118
|
+
createOAuthCredentialStoreAdapter,
|
|
119
|
+
createStoredCredentialResolver,
|
|
120
|
+
openEncryptedCredentialStore,
|
|
121
|
+
} from "@arnilo/prism-credentials-node";
|
|
122
|
+
|
|
123
|
+
const store = await openEncryptedCredentialStore({
|
|
124
|
+
path: "./credentials.vault",
|
|
125
|
+
getPassphrase: () => process.env.MY_APP_CREDENTIAL_PASSPHRASE!,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const resolver = createExplicitCredentialResolver([
|
|
129
|
+
{ name: "stored", resolver: createStoredCredentialResolver(store) },
|
|
130
|
+
]);
|
|
131
|
+
|
|
132
|
+
const apiKey = await resolveCredentialValue(resolver, { name: "apiKey", provider: "demo" });
|
|
133
|
+
|
|
134
|
+
const oauthStore = createOAuthCredentialStoreAdapter(store);
|
|
135
|
+
await refreshOAuthCredential({
|
|
136
|
+
provider: myOAuthProvider,
|
|
137
|
+
credentials: existing,
|
|
138
|
+
store: oauthStore,
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Passphrase rotation:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { rotateEncryptedCredentialStorePassphrase } from "@arnilo/prism-credentials-node";
|
|
146
|
+
|
|
147
|
+
await rotateEncryptedCredentialStorePassphrase({
|
|
148
|
+
path: "./credentials.vault",
|
|
149
|
+
getCurrentPassphrase: () => oldPassphrase,
|
|
150
|
+
getNewPassphrase: () => newPassphrase,
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Extension and configuration notes
|
|
155
|
+
|
|
156
|
+
- Passphrase retrieval, TLS, and OS permission prompts remain host-owned.
|
|
157
|
+
- Use distinct `namespace` or vault paths per tenant/environment.
|
|
158
|
+
- Keychain `list()` / `listOAuth()` are intentionally unsupported — enumerate credentials through host configuration instead of scanning the OS store.
|
|
159
|
+
- Combine with `createExplicitCredentialResolver()` so runtime overrides still win over stored values.
|
|
160
|
+
- Wire `createOAuthCredentialStoreAdapter(store)` into `refreshOAuthCredential()` so refreshed tokens persist durably.
|
|
161
|
+
|
|
162
|
+
## Security and performance notes
|
|
163
|
+
|
|
164
|
+
- Authenticated encryption uses Node built-in `aes-256-gcm` and `scrypt`; no extra crypto dependencies for the file backend.
|
|
165
|
+
- Atomic writes use temp file + rename; partial writes cannot replace a valid vault.
|
|
166
|
+
- Derived keys are zeroed after encrypt/decrypt operations where practical.
|
|
167
|
+
- Default scrypt `N=32768` targets interactive CLI unlock; raise `N` for higher security at the cost of unlock latency.
|
|
168
|
+
- Keychain operations honor `timeoutMs` and surface `CredentialStoreTimeoutError` instead of blocking indefinitely.
|
|
169
|
+
- Never log passphrases, derived keys, or decrypted credential payloads. Error messages do not echo secret values.
|
|
170
|
+
- Live keychain tests are opt-in (`PRISM_TEST_KEYCHAIN=1`); default `npm test` stays offline.
|
|
171
|
+
|
|
172
|
+
## Related APIs
|
|
173
|
+
|
|
174
|
+
- [Credentials and redaction](credentials-and-redaction.md): core resolver helpers and `refreshOAuthCredential()`
|
|
175
|
+
- [Security/auth/trust](settings-auth-trust-security.md): host-owned settings/credentials boundaries
|
|
176
|
+
- [Persistence, credentials, and multimodality primitives](persistence-credentials-multimodality-primitives.md): Plan 056 threat model and conformance matrix rows 7–10
|
|
177
|
+
- `@arnilo/prism`: `CredentialResolver`, `OAuthCredentialStore`, `createMemoryCredentialStore()`
|
|
@@ -95,7 +95,7 @@ console.log(error.message);
|
|
|
95
95
|
## Extension and configuration notes
|
|
96
96
|
|
|
97
97
|
- Hosts and extension packages can implement `CredentialResolver` and pass it explicitly to code that needs credentials.
|
|
98
|
-
-
|
|
98
|
+
- Credentials stay host-owned outside `AgentConfig`. `createAgent()` / `session.run()` do not call `credentials.resolve()`. Provider adapters, compaction workers, or request policies should receive and resolve credentials at the provider edge.
|
|
99
99
|
- Use `createExplicitCredentialResolver()` when documenting a fixed order such as runtime override, stored credential, caller-provided env object, then fallback resolver.
|
|
100
100
|
- Use `createEnvCredentialResolver()` only with an object supplied by the host; Prism does not read `process.env` for you.
|
|
101
101
|
- Provider adapters should resolve credentials as late as possible, per request.
|
|
@@ -110,9 +110,10 @@ console.log(error.message);
|
|
|
110
110
|
- Cycle and non-JSON value handling: `redactSecrets()` is cycle-safe via a `WeakSet` visited-set. Self-referential or mutually referenced objects render `"[Circular]"` at the back-reference instead of throwing. `Date` and `RegExp` values are passed through unchanged; `ArrayBuffer` and typed arrays are passed through unchanged; `Map` is normalized to a plain object and `Set` to an array so the output stays JSON-compatible. `errorToErrorInfo()` tolerates a cyclic `error.cause` (rendered via `String()`).
|
|
111
111
|
- Use placeholders in tests and docs. Never commit real tokens.
|
|
112
112
|
- Live provider/worker tests are gated behind explicit environment variables and skipped by default: `PRISM_LIVE_PROVIDER_TESTS`, `PRISM_LIVE_COMPACTION_TESTS`, `PRISM_LIVE_OBSERVATIONAL_MEMORY_TESTS`. Default `npm test` is network-free; do not add ungated network calls to default tests.
|
|
113
|
-
-
|
|
113
|
+
- Credentials are not eagerly resolved by the core runtime, serialized into provider requests/events/stores, or passed to loops/compaction.
|
|
114
114
|
- `resolveCredentialValue()` and `createExplicitCredentialResolver()` do not cache values. Add host-side caching only if a real credential source needs it.
|
|
115
115
|
- `refreshOAuthCredential()` only calls the supplied OAuth provider and optional store; it has no built-in persistence or retry loop.
|
|
116
|
+
- OpenAI Codex device-code OAuth polls inside `createOpenAICodexOAuthProvider().login()` with bounded delays and abort support via `OAuthLoginCallbacks.signal`. Token-endpoint failures redact authorization codes, PKCE verifiers, device/user codes, and access/refresh tokens when those values are known.
|
|
116
117
|
|
|
117
118
|
## Related APIs
|
|
118
119
|
|
|
@@ -121,4 +122,4 @@ console.log(error.message);
|
|
|
121
122
|
- [LLM compaction package](compaction-llm.md): resolves optional summary-provider credentials per compaction call and redacts exact known values.
|
|
122
123
|
- [OpenAI-compatible provider](providers/openai-compatible.md): resolves API keys per request and redacts known values from adapter errors.
|
|
123
124
|
|
|
124
|
-
Phase 10 added `createMemoryCredentialStore()`, `createChainedCredentialResolver()`, and `createSecretRedactor()` for opt-in in-memory auth and runtime redaction. Phase 11 adds OAuth/API-key contracts plus explicit resolver order helpers. Core still has no persistent secret store and does not read environment variables or files for credentials. See [Security/auth/trust](settings-auth-trust-security.md).
|
|
125
|
+
Phase 10 added `createMemoryCredentialStore()`, `createChainedCredentialResolver()`, and `createSecretRedactor()` for opt-in in-memory auth and runtime redaction. Phase 11 adds OAuth/API-key contracts plus explicit resolver order helpers. Core still has no persistent secret store and does not read environment variables or files for credentials. For durable storage, use [`@arnilo/prism-credentials-node`](credential-storage.md) encrypted-file or keychain backends. See [Security/auth/trust](settings-auth-trust-security.md).
|