@arnilo/prism 0.0.16 → 0.0.18
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 +33 -0
- package/README.md +9 -2
- package/dist/agent-run-state.js +13 -1
- package/dist/agents.js +42 -10
- package/dist/checkpoints.d.ts +4 -0
- package/dist/checkpoints.js +12 -0
- package/dist/cli-runner.d.ts +1 -5
- package/dist/cli-runner.js +5 -28
- package/dist/context-budget.js +10 -7
- package/dist/contracts.d.ts +13 -0
- package/dist/contributions.d.ts +2 -0
- package/dist/contributions.js +3 -0
- package/dist/credentials.d.ts +7 -1
- package/dist/credentials.js +6 -2
- package/dist/event-multiplexer.js +17 -1
- package/dist/extensions.d.ts +7 -1
- package/dist/extensions.js +64 -6
- package/dist/feedback.js +1 -1
- package/dist/guardrails.js +9 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/input.js +12 -5
- package/dist/middleware.js +9 -1
- package/dist/models.d.ts +2 -0
- package/dist/models.js +3 -0
- package/dist/providers/openai-compatible.d.ts +42 -1
- package/dist/providers/openai-compatible.js +109 -47
- package/dist/providers/transport.d.ts +6 -0
- package/dist/providers/transport.js +21 -0
- package/dist/providers.d.ts +2 -0
- package/dist/providers.js +3 -0
- package/dist/redaction.js +21 -7
- package/dist/retry.d.ts +5 -0
- package/dist/retry.js +8 -1
- package/dist/run-ledger.d.ts +6 -0
- package/dist/run-ledger.js +3 -9
- package/dist/session-stores.js +15 -11
- package/docs/0.1.0-readiness.md +35 -21
- package/docs/agent-events.md +2 -1
- package/docs/agent-session-runtime.md +3 -3
- package/docs/cli-rpc.md +1 -5
- package/docs/coding-agent-tools.md +10 -6
- package/docs/compaction-and-retry.md +3 -1
- package/docs/contribution-registries.md +1 -0
- package/docs/credentials-and-redaction.md +1 -1
- package/docs/extensions.md +1 -1
- package/docs/guardrails.md +13 -2
- package/docs/index.md +4 -4
- package/docs/input-and-prompt-assembly.md +6 -8
- package/docs/mcp-tools.md +3 -3
- package/docs/middleware-hooks.md +2 -2
- package/docs/migration.md +24 -1
- package/docs/provider-caching.md +1 -1
- package/docs/provider-conformance.md +1 -1
- package/docs/provider-packages.md +1 -1
- package/docs/providers/ai-sdk.md +2 -1
- package/docs/providers/openai-compatible.md +28 -1
- package/docs/public-contracts.md +2 -2
- package/docs/release-and-install.md +59 -17
- package/docs/session-stores.md +1 -1
- package/package.json +1 -1
package/dist/redaction.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
const REDACTED = "[REDACTED]";
|
|
2
|
+
// Depth bound matching agent-run-state.ts; hostile deep structures yield a placeholder
|
|
3
|
+
// instead of a stack overflow.
|
|
4
|
+
const MAX_REDACT_DEPTH = 32;
|
|
2
5
|
export function createSecretRedactor(secrets) {
|
|
3
6
|
return { redact: (value) => redactSecrets(value, secrets) };
|
|
4
7
|
}
|
|
@@ -43,11 +46,13 @@ export function redactSecrets(value, secrets) {
|
|
|
43
46
|
// ponytail: active-path WeakSet marks only ancestor cycles as [Circular]; shared
|
|
44
47
|
// references (diamonds) are visited again on separate branches. Map/Set normalize
|
|
45
48
|
// to JSON-shaped output; string keys are redacted like values.
|
|
46
|
-
const redact = (input, active = new WeakSet()) => {
|
|
49
|
+
const redact = (input, active = new WeakSet(), depth = 0) => {
|
|
47
50
|
if (typeof input === "string")
|
|
48
51
|
return redactString(input);
|
|
49
52
|
if (input === null || typeof input !== "object")
|
|
50
53
|
return input;
|
|
54
|
+
if (depth >= MAX_REDACT_DEPTH)
|
|
55
|
+
return "[MaxDepth]";
|
|
51
56
|
if (input instanceof Date || input instanceof RegExp)
|
|
52
57
|
return input;
|
|
53
58
|
if (ArrayBuffer.isView(input) || input instanceof ArrayBuffer)
|
|
@@ -57,18 +62,18 @@ export function redactSecrets(value, secrets) {
|
|
|
57
62
|
active.add(input);
|
|
58
63
|
try {
|
|
59
64
|
if (Array.isArray(input))
|
|
60
|
-
return input.map((item) => redact(item, active));
|
|
65
|
+
return input.map((item) => redact(item, active, depth + 1));
|
|
61
66
|
if (input instanceof Map) {
|
|
62
67
|
const out = {};
|
|
63
68
|
for (const [key, item] of input)
|
|
64
|
-
assignKey(out, redactKey(key), redact(item, active));
|
|
69
|
+
assignKey(out, redactKey(key), redact(item, active, depth + 1));
|
|
65
70
|
return out;
|
|
66
71
|
}
|
|
67
72
|
if (input instanceof Set)
|
|
68
|
-
return [...input].map((item) => redact(item, active));
|
|
73
|
+
return [...input].map((item) => redact(item, active, depth + 1));
|
|
69
74
|
const out = {};
|
|
70
75
|
for (const [key, item] of Object.entries(input))
|
|
71
|
-
assignKey(out, redactKey(key), redact(item, active));
|
|
76
|
+
assignKey(out, redactKey(key), redact(item, active, depth + 1));
|
|
72
77
|
return out;
|
|
73
78
|
}
|
|
74
79
|
finally {
|
|
@@ -79,18 +84,21 @@ export function redactSecrets(value, secrets) {
|
|
|
79
84
|
}
|
|
80
85
|
export function errorToErrorInfo(error, secrets = []) {
|
|
81
86
|
const code = readErrorCode(error);
|
|
87
|
+
const retry = readRetryAfterMs(error);
|
|
88
|
+
const retryAfter = retry !== undefined ? { retryAfterMs: retry } : {};
|
|
82
89
|
if (error instanceof Error) {
|
|
83
90
|
return {
|
|
84
91
|
name: error.name,
|
|
85
92
|
message: redactSecrets(error.message, secrets),
|
|
86
93
|
code,
|
|
94
|
+
...retryAfter,
|
|
87
95
|
cause: error.cause ? redactSecrets(String(error.cause), secrets) : undefined,
|
|
88
96
|
};
|
|
89
97
|
}
|
|
90
98
|
if (error && typeof error === "object" && "message" in error) {
|
|
91
|
-
return { message: redactSecrets(String(error.message), secrets), code };
|
|
99
|
+
return { message: redactSecrets(String(error.message), secrets), code, ...retryAfter };
|
|
92
100
|
}
|
|
93
|
-
return { message: redactSecrets(String(error), secrets), code };
|
|
101
|
+
return { message: redactSecrets(String(error), secrets), code, ...retryAfter };
|
|
94
102
|
}
|
|
95
103
|
function readErrorCode(error) {
|
|
96
104
|
if (!error || typeof error !== "object" || !("code" in error))
|
|
@@ -98,4 +106,10 @@ function readErrorCode(error) {
|
|
|
98
106
|
const code = error.code;
|
|
99
107
|
return typeof code === "string" || typeof code === "number" ? code : undefined;
|
|
100
108
|
}
|
|
109
|
+
function readRetryAfterMs(error) {
|
|
110
|
+
if (!error || typeof error !== "object" || !("retryAfterMs" in error))
|
|
111
|
+
return undefined;
|
|
112
|
+
const value = error.retryAfterMs;
|
|
113
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
114
|
+
}
|
|
101
115
|
//# sourceMappingURL=redaction.js.map
|
package/dist/retry.d.ts
CHANGED
|
@@ -5,6 +5,11 @@ export interface DefaultRetryPolicyOptions {
|
|
|
5
5
|
readonly baseDelayMs?: number;
|
|
6
6
|
readonly maxDelayMs?: number;
|
|
7
7
|
readonly transientCodes?: readonly (string | number)[];
|
|
8
|
+
/** Symmetric jitter fraction applied to computed delays (default 0.25 → ±25%).
|
|
9
|
+
* Prevents thundering-herd retries under a shared outage. Set 0 to disable. */
|
|
10
|
+
readonly jitter?: number;
|
|
11
|
+
/** Random source for jitter (tests); defaults to Math.random. */
|
|
12
|
+
readonly random?: () => number;
|
|
8
13
|
}
|
|
9
14
|
export declare function createDefaultRetryPolicy(options?: DefaultRetryPolicyOptions): RetryPolicy;
|
|
10
15
|
export declare function isTransientErrorInfo(error: ErrorInfo, transientCodes?: ReadonlySet<string | number>): boolean;
|
package/dist/retry.js
CHANGED
|
@@ -22,13 +22,20 @@ export function createDefaultRetryPolicy(options = {}) {
|
|
|
22
22
|
const maxAttempts = Math.max(1, options.maxAttempts ?? 3);
|
|
23
23
|
const baseDelayMs = Math.max(0, options.baseDelayMs ?? 100);
|
|
24
24
|
const maxDelayMs = Math.max(baseDelayMs, options.maxDelayMs ?? 1000);
|
|
25
|
+
const jitter = Math.min(1, Math.max(0, options.jitter ?? 0.25));
|
|
26
|
+
const random = options.random ?? Math.random;
|
|
25
27
|
const transientCodes = new Set([...TRANSIENT_CODES, ...(options.transientCodes ?? [])]);
|
|
26
28
|
return {
|
|
27
29
|
name: options.name ?? "default-retry",
|
|
28
30
|
decide(context) {
|
|
29
31
|
if (context.signal?.aborted || context.attempt >= maxAttempts || !isTransientErrorInfo(context.error, transientCodes))
|
|
30
32
|
return { retry: false };
|
|
31
|
-
|
|
33
|
+
// Provider backpressure hint (Retry-After) wins over computed backoff, always
|
|
34
|
+
// capped at maxDelayMs so a hostile/huge hint cannot pin a run.
|
|
35
|
+
const hint = context.error.retryAfterMs;
|
|
36
|
+
const base = Math.min(hint !== undefined && Number.isFinite(hint) && hint >= 0 ? hint : baseDelayMs * 2 ** Math.max(0, context.attempt - 1), maxDelayMs);
|
|
37
|
+
const delayMs = jitter > 0 ? Math.round(base * (1 - jitter + random() * 2 * jitter)) : base;
|
|
38
|
+
return { retry: true, delayMs };
|
|
32
39
|
},
|
|
33
40
|
};
|
|
34
41
|
}
|
package/dist/run-ledger.d.ts
CHANGED
|
@@ -6,9 +6,15 @@ export declare const HARD_LEDGER_BATCH_BYTES: number;
|
|
|
6
6
|
export declare const DEFAULT_LEDGER_BATCH_DELAY_MS = 25;
|
|
7
7
|
export declare const HARD_LEDGER_BATCH_DELAY_MS = 60000;
|
|
8
8
|
export interface BatchedRunLedgerOptions {
|
|
9
|
+
/** Flush triggers, not write grouping: a pending flush fires once the buffer reaches
|
|
10
|
+
* this many entries. Writes still go to the target one at a time (RunLedger has no
|
|
11
|
+
* batch append); these bounds shape flush *timing*, not per-write coalescing. */
|
|
9
12
|
readonly maxBatchEntries?: number;
|
|
13
|
+
/** Flush trigger: pending flush fires once buffered bytes reach this bound. */
|
|
10
14
|
readonly maxBatchBytes?: number;
|
|
15
|
+
/** Backpressure bound: enqueue flushes synchronously before exceeding this. */
|
|
11
16
|
readonly maxBufferedEntries?: number;
|
|
17
|
+
/** Backpressure bound (bytes): enqueue flushes synchronously before exceeding this. */
|
|
12
18
|
readonly maxBufferedBytes?: number;
|
|
13
19
|
readonly maxDelayMs?: number;
|
|
14
20
|
readonly durability?: RunLedgerDurability;
|
package/dist/run-ledger.js
CHANGED
|
@@ -58,20 +58,14 @@ export function createBatchedRunLedger(target, options = {}) {
|
|
|
58
58
|
const flush = () => {
|
|
59
59
|
cancelTimer();
|
|
60
60
|
const operation = flushChain.then(async () => {
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
// One write per record — RunLedger has no batch append API. maxBatchEntries/
|
|
62
|
+
// maxBatchBytes only decide when a flush fires (see enqueue), never how writes group.
|
|
63
63
|
while (queue.length) {
|
|
64
64
|
const item = queue[0];
|
|
65
|
-
|
|
66
|
-
entries = 0;
|
|
67
|
-
bytes = 0;
|
|
68
|
-
}
|
|
69
|
-
await write(item);
|
|
65
|
+
await write(item); // shifts only after success — a failed write stays buffered for retry
|
|
70
66
|
queue.shift();
|
|
71
67
|
bufferedBytes -= item.bytes;
|
|
72
68
|
flushed += 1;
|
|
73
|
-
entries += 1;
|
|
74
|
-
bytes += item.bytes;
|
|
75
69
|
}
|
|
76
70
|
return status();
|
|
77
71
|
});
|
package/dist/session-stores.js
CHANGED
|
@@ -31,8 +31,7 @@ async function readBranchFromReader(reader, query) {
|
|
|
31
31
|
// indexEntries + the parentId walk order it and still reject missing parents / dupes.
|
|
32
32
|
return getSessionBranchEntriesCore(items, { leafId: query.leafId });
|
|
33
33
|
}
|
|
34
|
-
function getSessionBranchEntriesCore(entries, options = {}) {
|
|
35
|
-
const index = indexEntries(entries);
|
|
34
|
+
function getSessionBranchEntriesCore(entries, options = {}, index = indexEntries(entries)) {
|
|
36
35
|
const leafId = options.leafId ?? entries.at(-1)?.id;
|
|
37
36
|
if (!leafId)
|
|
38
37
|
return [];
|
|
@@ -52,7 +51,7 @@ export function listSessionBranches(entries) {
|
|
|
52
51
|
const index = indexEntries(entries);
|
|
53
52
|
return entries
|
|
54
53
|
.filter((entry) => !index.parentIds.has(entry.id))
|
|
55
|
-
.map((entry) => ({ leafId: entry.id, entries:
|
|
54
|
+
.map((entry) => ({ leafId: entry.id, entries: getSessionBranchEntriesCore(entries, { leafId: entry.id }, index) }));
|
|
56
55
|
}
|
|
57
56
|
export function rebuildSessionContext(input, options = {}) {
|
|
58
57
|
if (typeof input === "function") {
|
|
@@ -133,17 +132,22 @@ export function createMemorySessionStore(initialEntries = [], options = {}) {
|
|
|
133
132
|
if (dedupKey !== undefined && idempotencySeen.has(dedupKey)) {
|
|
134
133
|
throw new SessionAppendConflictError({ code: SESSION_APPEND_CONFLICT_CODE, idempotencyDuplicate: true });
|
|
135
134
|
}
|
|
136
|
-
// expectedParentId is existence validation (the parent must already
|
|
137
|
-
// store or be undefined for a root).
|
|
135
|
+
// expectedParentId is same-session existence validation (the parent must already
|
|
136
|
+
// be in the store for THIS session, or be undefined for a root). A parent from
|
|
137
|
+
// another session is rejected: the write would be unreadable, since every branch
|
|
138
|
+
// walk is per-session. Tip-CAS is intentionally NOT used: prism
|
|
138
139
|
// allows branching from any existing leaf (checkout + append), so a stale-but-
|
|
139
140
|
// existing parent is a valid branch, not a conflict. DB adapters may layer
|
|
140
141
|
// stricter tip-CAS via unique constraints for linear-only sessions.
|
|
141
|
-
if (options?.expectedParentId !== undefined
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
142
|
+
if (options?.expectedParentId !== undefined) {
|
|
143
|
+
const parent = byId.get(options.expectedParentId);
|
|
144
|
+
if (!parent || parent.sessionId !== entry.sessionId) {
|
|
145
|
+
throw new SessionAppendConflictError({
|
|
146
|
+
code: SESSION_APPEND_CONFLICT_CODE,
|
|
147
|
+
expectedParentId: options.expectedParentId,
|
|
148
|
+
currentLeafId: leafBySession.get(entry.sessionId),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
147
151
|
}
|
|
148
152
|
if (byId.has(entry.id))
|
|
149
153
|
throw new Error(`Duplicate session entry id: ${entry.id}`);
|
package/docs/0.1.0-readiness.md
CHANGED
|
@@ -1,30 +1,41 @@
|
|
|
1
1
|
# 0.1.0 / 1.0 Readiness Gates
|
|
2
2
|
|
|
3
|
-
Status: **0.0.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
Status: **0.0.18** is the current release line (Phase 1 restore integrity); **1.0** readiness remains operator-gated, not automatic.
|
|
4
|
+
|
|
5
|
+
This page distills runnable readiness gates into one command-per-gate table. The
|
|
6
|
+
**Last evidence** column records the 2026-07-26 **0.0.16** baseline snapshot
|
|
7
|
+
(Phase 11, Node v24.18.0, Linux x86_64). Treat it as historical floor evidence,
|
|
8
|
+
not the current release tag. Re-run each gate on the target release tree before
|
|
9
|
+
cutting 0.0.18 / 1.0. The decision to cut 1.0 stays with the operator after
|
|
10
|
+
operator-gated legs run in a protected environment and Phase 12 demand evidence
|
|
11
|
+
exists.
|
|
10
12
|
|
|
11
13
|
Evidence trail: [`docs/review-coverage-2026-07-26-phase-11.md`](./review-coverage-2026-07-26-phase-11.md)
|
|
12
14
|
(addenda 0–9), [`docs/release-and-install.md`](./release-and-install.md),
|
|
13
15
|
[`docs/migration.md`](./migration.md), [`docs/performance.md`](./performance.md).
|
|
14
16
|
|
|
17
|
+
## Current line (0.0.18)
|
|
18
|
+
|
|
19
|
+
| Item | Status |
|
|
20
|
+
|---|---|
|
|
21
|
+
| Published graph | **44** publishable manifests at **0.0.18** (`docs/release-and-install.md`) |
|
|
22
|
+
| Phase 1 integrity | `repo_search` literal-only, atomic write/edit, `cache_aware` default layout, oldest-first history eviction, MCP SDK 1.30.0, README/readiness alignment |
|
|
23
|
+
| Docs tripwires | `node --test dist/__tests__/docs.test.js` — migration section `0.0.17 → 0.0.18` documents intentional breaks |
|
|
24
|
+
| Readiness table below | **0.0.16 measured values** — refresh evidence columns when 1.0 RC gates are recorded |
|
|
25
|
+
|
|
15
26
|
## Gate table
|
|
16
27
|
|
|
17
|
-
| Gate | Command | Last evidence (2026-07-26) | Owner |
|
|
28
|
+
| Gate | Command | Last evidence (2026-07-26 baseline @ 0.0.16) | Owner |
|
|
18
29
|
|---|---|---|---|
|
|
19
30
|
| Full quality gate | `npm run sdk:ready` | RC=0: typecheck (+examples), lint 0, format clean, full test, coverage, pack, release:gate | CI |
|
|
20
|
-
| Exact version graph | `node scripts/release.mjs check --version <v>` | 0.0.16
|
|
21
|
-
| Frozen public API surface + compat gate | `node scripts/release.mjs gate` | 0 breaks / 0 errors vs
|
|
22
|
-
| Migration coverage
|
|
31
|
+
| Exact version graph | `node scripts/release.mjs check --version <v>` | pass at 0.0.16: exact versions/ranges/lockfile/access + registry-collision check, 44 manifests | CI + operator |
|
|
32
|
+
| Frozen public API surface + compat gate | `node scripts/release.mjs gate` | 0 breaks / 0 errors vs checked-in baselines (`scripts/compat-baseline/`); only additive delta at 0.0.16 was `resolveRedactor` | CI |
|
|
33
|
+
| Migration coverage + docs tripwires | `node --test dist/__tests__/docs.test.js` | 112/112 at 0.0.16; `docs/migration.md` sections tripwired per release | Maintainer |
|
|
23
34
|
| Deterministic artifact budget | `node --test dist/__tests__/budget-gate.test.mjs` | root 579.2 kB / 2.1 MB / 270 files within +5% of baseline; startup 38 ms < 250 ms ceiling | CI (in `npm test`) |
|
|
24
35
|
| Performance benchmark medians | `node scripts/benchmark-0.0.16.mjs` | 6 network-free scenarios within ±25%; 0 backpressure / 0 resource-limit signals | On-demand release evidence |
|
|
25
36
|
| Secret scan | `node scripts/scan-secrets.mjs` | 3095 files / 0 findings | CI |
|
|
26
37
|
| License / SBOM | `node scripts/verify-sbom.mjs` | 188 packages / 8 licenses, all allow-listed | CI |
|
|
27
|
-
| Dependency audit | `npm audit --audit-level=high` | rc=0 (2 moderate, 0 high) | CI |
|
|
38
|
+
| Dependency audit | `npm audit --audit-level=high` | rc=0 (2 moderate, 0 high) at 0.0.16 | CI |
|
|
28
39
|
| Whitespace hygiene | `git diff --check` | clean | CI |
|
|
29
40
|
| Publish order + tarball validation | `node scripts/release.mjs publish --version <v> --dry-run --allow-dirty --allow-untagged` | 44/44 packages `dry-run`, deterministic dependency order, no failures | Operator (dry-run), CI |
|
|
30
41
|
| Node 20 compatibility | CI `node20-compat` (build + public-import smoke) | all 21 root exports import cleanly on Node 20.20.2 | CI |
|
|
@@ -48,12 +59,13 @@ Regenerate only after review with `node scripts/release.mjs gate --update-baseli
|
|
|
48
59
|
having first confirmed zero removed exports (the gate's order-sensitive
|
|
49
60
|
signatures can drift on a TypeScript bump without any real API change).
|
|
50
61
|
|
|
51
|
-
## Migration coverage
|
|
62
|
+
## Migration coverage
|
|
52
63
|
|
|
53
|
-
`docs/migration.md` carries
|
|
54
|
-
`docs.test.ts`
|
|
55
|
-
|
|
56
|
-
|
|
64
|
+
`docs/migration.md` carries release migration sections tripwired by
|
|
65
|
+
`docs.test.ts` (headings and key phrases). A missing or gutted section fails
|
|
66
|
+
the suite. **0.0.18** adds intentional pre-1.0 breaks (`repo_search` literal-only,
|
|
67
|
+
`cache_aware` default layout, oldest-first history eviction, atomic write/edit
|
|
68
|
+
durability, MCP SDK bump) — see `0.0.17 → 0.0.18 restore integrity`.
|
|
57
69
|
|
|
58
70
|
## Budget table
|
|
59
71
|
|
|
@@ -95,13 +107,13 @@ protected environment, never faked.
|
|
|
95
107
|
|
|
96
108
|
## Security matrix
|
|
97
109
|
|
|
98
|
-
| Control | Command / source | 0.0.16
|
|
110
|
+
| Control | Command / source | 0.0.16 baseline |
|
|
99
111
|
|---|---|---|
|
|
100
112
|
| Secret scan | `node scripts/scan-secrets.mjs` | 3095 files / 0 findings |
|
|
101
113
|
| License / SBOM | `node scripts/verify-sbom.mjs` | 188 packages / 8 licenses, allow-listed |
|
|
102
114
|
| Dependency audit | `npm audit --audit-level=high` | 0 high (2 moderate) |
|
|
103
115
|
| SAST | GitHub CodeQL workflow | CI-gated |
|
|
104
|
-
| Sandbox / protocol / tenant threat suites | `npm test` (coding-security, MCP, policy, guardrail suites) | green |
|
|
116
|
+
| Sandbox / protocol / tenant threat suites | `npm test` (coding-security, MCP, policy, guardrail suites) | green at 0.0.16 |
|
|
105
117
|
| Signed deterministic publication | `release.mjs publish` on clean tagged tree | operator-gated |
|
|
106
118
|
|
|
107
119
|
## Remaining for 1.0 (operator / protected environment)
|
|
@@ -121,6 +133,8 @@ Exact prerequisites that must be satisfied before cutting 1.0:
|
|
|
121
133
|
checked-in baseline.
|
|
122
134
|
7. **Phase 12 demand evidence** (below) recorded for any capability that 1.0
|
|
123
135
|
is expected to anchor.
|
|
136
|
+
8. **0.0.18+ integrity gates green** on the release candidate (docs suite,
|
|
137
|
+
MCP SDK advisory cleared, coding-tool durability fixes, layout/eviction defaults).
|
|
124
138
|
|
|
125
139
|
## Phase 12 demand-evidence entry criteria
|
|
126
140
|
|
|
@@ -135,5 +149,5 @@ alone. Each candidate must present, before it becomes a numbered plan:
|
|
|
135
149
|
- then the pipeline: demand evidence → primitive review → threat model →
|
|
136
150
|
optional package/service → conformance → release gate.
|
|
137
151
|
|
|
138
|
-
The
|
|
139
|
-
|
|
152
|
+
The readiness gates above are the stable API/compat/budget/security floor that
|
|
153
|
+
Phase 12 capabilities must consume and must not regress.
|
package/docs/agent-events.md
CHANGED
|
@@ -44,7 +44,7 @@ The `AgentEvent` union (grouped by concern):
|
|
|
44
44
|
| Assistant messages | `message_started`, `message_delta`, `message_finished` |
|
|
45
45
|
| Tool execution | `tool_execution_started`, `tool_execution_progress`, `tool_execution_finished`, `tool_execution_error`, `tool_execution_blocked` |
|
|
46
46
|
| Guardrails | `guardrail_decision` |
|
|
47
|
-
| Queue/subscribers | `queue_updated`, `event_subscriber_overflow` |
|
|
47
|
+
| Queue/subscribers | `queue_updated`, `event_subscriber_overflow`, `steer_rejected` |
|
|
48
48
|
| Compaction | `compaction_started`, `compaction_finished` |
|
|
49
49
|
| Retry | `retry_scheduled` |
|
|
50
50
|
| Artifacts | `artifact_validation_started`, `artifact_validation_finished`, `artifact_revision_started`, `artifact_finished`, `artifact_failed` |
|
|
@@ -92,6 +92,7 @@ Queue / subscriber / compaction / retry / provider events:
|
|
|
92
92
|
| Variant | Fields |
|
|
93
93
|
| --- | --- |
|
|
94
94
|
| `queue_updated` | `sessionId`, `runId`, `size: number` |
|
|
95
|
+
| `steer_rejected` | `sessionId`, `runId`, `message: Message` (redacted), `record: GuardrailRecord` — a steered message dropped by a terminal input guardrail; the run continues without it |
|
|
95
96
|
| `event_subscriber_overflow` | `sessionId`, `droppedEvents: number`, `maxQueuedEvents: number`, `overflow: "close" \| "drop_oldest" \| "drop_newest"` |
|
|
96
97
|
| `compaction_started` | `sessionId`, `runId?` |
|
|
97
98
|
| `compaction_finished` | `sessionId`, `runId?`, `summary: string` |
|
|
@@ -49,7 +49,7 @@ string | Message | readonly Message[]
|
|
|
49
49
|
|
|
50
50
|
`AgentConfig.limits` sets run ceilings; `RunOptions.limits` may only narrow configured agent values. Limits cover turns, provider attempts, tool rounds/calls, wall time, request/response bytes, tokens, and optional single-currency cost. A breach emits one `run_limit_exceeded` event and throws `AgentRunError` with `result.limit`; see [Runs and usage ledger](runs-and-usage.md#run-limits).
|
|
51
51
|
|
|
52
|
-
`RunOptions.model` can override the request model for a run. Model overrides append a `model_change` entry. `AgentConfig.inputLayout` selects the default input assembly layout (`"
|
|
52
|
+
`RunOptions.model` can override the request model for a run. Model overrides append a `model_change` entry. `AgentConfig.inputLayout` selects the default input assembly layout (`"cache_aware"` by default, or opt-in `"legacy"`); `RunOptions.inputLayout` wins for one run. `AgentConfig.providerOptions`/`RunOptions.providerOptions` supply generic provider request options; `timeoutMs`, `maxRetries`, and `maxRetryDelayMs` are deprecated inert provider-level hints in first-party providers. Use `RunOptions.signal`/host abort controllers for timeouts and `AgentConfig.retry`/`RunOptions.retry` for retry. `AgentConfig.providerRequestPolicies`/`RunOptions.providerRequestPolicies` run before `AIProvider.generate()` and before `provider_request` middleware. `AgentConfig.systemPrompt` and `RunOptions.systemPrompt` add explicit layered system prompt contributions; `RunOptions.systemPrompt: false` disables configured prompt layers for that run while keeping `AgentConfig.instructions` as the base path. `RunOptions.compaction` can enable auto-compaction for that run or use `false` to disable configured auto-compaction. `RunOptions.retry` can enable provider-turn retry for that run or use `false` to disable configured retry. `RunOptions.metadata` is merged with agent/session metadata for assembly, provider requests, and tool contexts. Deprecated `RunOptions.maxToolRounds` narrows `limits.maxToolRounds`. `RunOptions.signal` is bridged into the per-run abort signal passed to assembly, providers, tools, auto-compaction, and retry backoff.
|
|
53
53
|
|
|
54
54
|
## Outputs / response / events
|
|
55
55
|
|
|
@@ -85,7 +85,7 @@ Only one `run()` may be active per session. Concurrent `run()` / `prompt` / `fol
|
|
|
85
85
|
|
|
86
86
|
### Mid-run steer (0.0.11)
|
|
87
87
|
|
|
88
|
-
`session.steer(input, options?)` enqueues user text into the **same** active run (fail closed when no run). Default: inject at the next turn boundary (after tool rounds / before next provider assemble). `options.softInterrupt: true` aborts only the current provider stream, then continues the same `runId` with steered text. Pending queue caps: **8** messages / **64 KiB** UTF-8 total (`DEFAULT_MAX_PENDING_STEERS` / `DEFAULT_MAX_PENDING_STEER_BYTES`); overflow throws. Steered messages pass input guardrails + normal session append/redaction. Loops drain via optional `LoopContext.hasPendingSteers` / `applyPendingSteers`.
|
|
88
|
+
`session.steer(input, options?)` enqueues user text into the **same** active run (fail closed when no run). Default: inject at the next turn boundary (after tool rounds / before next provider assemble). `options.softInterrupt: true` aborts only the current provider stream, then continues the same `runId` with steered text. Pending queue caps: **8** messages / **64 KiB** UTF-8 total (`DEFAULT_MAX_PENDING_STEERS` / `DEFAULT_MAX_PENDING_STEER_BYTES`); overflow throws. Steered messages pass input guardrails + normal session append/redaction. A `block`/`tripwire` on a steered message drops just that message: Prism emits `guardrail_decision` plus a `steer_rejected` event (redacted message + `GuardrailRecord`) and the run continues; the message never enters history or the session store. Run-start input blocking still fails the run. `interrupt` on a steered message fails closed (durable suspension is only for run-start input). Loops drain via optional `LoopContext.hasPendingSteers` / `applyPendingSteers`.
|
|
89
89
|
|
|
90
90
|
`session.abort(reason)` aborts the active run. The abort signal is passed to input assembly, provider requests, and tool execution; if a tool/provider path aborts after a tool call, Prism does not start another provider turn.
|
|
91
91
|
|
|
@@ -187,7 +187,7 @@ if (result.status === "suspended") {
|
|
|
187
187
|
}
|
|
188
188
|
```
|
|
189
189
|
|
|
190
|
-
Resume requires exact checkpoint ownership, version, agent fingerprint, and revision. Prism CAS-claims approval before work, rechecks normal guardrail/permission/validation/limit paths, and marks a pending tool dispatched before its side effect. `createAgentRunLifecycle()` wraps the same core path for server/MCP hosts: adapters pass only authorized ownership, status returns only `{ state, version }`, and `resolveAgent()` supplies current agent/revision. `resumeStream()` uses that same claim path and bounded subscriber, so adapters do not poll or duplicate resume logic. Remote restart requires both checkpoint and session stores to be durable. A crash after that mark is ambiguous and is never replayed automatically; use host tool idempotency keyed by `runId`/`toolCallId` or resolve it manually. Checkpoints contain bounded redacted state plus session/leaf references, never provider objects, callbacks, signals, credentials, or raw secrets. Only built-in loop options are durable; custom `AgentLoopStrategy` rejects before provider work.
|
|
190
|
+
Resume requires exact checkpoint ownership, version, agent fingerprint, and revision. The fingerprint hashes the agent id/name, `definitionRevision`, model, instructions, system-prompt contributions, skills (name/instructions/tool names), tool definitions (name/parameters/exclusive), guardrail definitions (name/stage/revision), and loop strategy — changing any of them without bumping `definitionRevision` fails resume closed instead of silently continuing with different agent semantics. Prism CAS-claims approval before work, rechecks normal guardrail/permission/validation/limit paths, and marks a pending tool dispatched before its side effect. `createAgentRunLifecycle()` wraps the same core path for server/MCP hosts: adapters pass only authorized ownership, status returns only `{ state, version }`, and `resolveAgent()` supplies current agent/revision. `resumeStream()` uses that same claim path and bounded subscriber, so adapters do not poll or duplicate resume logic. Remote restart requires both checkpoint and session stores to be durable. A crash after that mark is ambiguous and is never replayed automatically; use host tool idempotency keyed by `runId`/`toolCallId` or resolve it manually. Checkpoints contain bounded redacted state plus session/leaf references, never provider objects, callbacks, signals, credentials, or raw secrets. State is bounded at save by `runState.maxStateBytes` (default 256 KB, at most the 1 MB hard cap); load bounds against the 1 MB hard cap only, so state saved with a raised limit stays resumable while oversized records are still rejected. Only built-in loop options are durable; custom `AgentLoopStrategy` rejects before provider work.
|
|
191
191
|
|
|
192
192
|
## Secure composition
|
|
193
193
|
|
package/docs/cli-rpc.md
CHANGED
|
@@ -45,10 +45,6 @@ Default generation installs only `@arnilo/prism` (mock provider). Selecting a re
|
|
|
45
45
|
| `--provider <name>` | Explicit provider id. The built-in `mock` id is only a smoke-test provider. |
|
|
46
46
|
| `--model <name>` | Explicit model name. |
|
|
47
47
|
| `--session <id>` | Session id. |
|
|
48
|
-
| `--config <path>` | Explicit config path recorded by the adapter; not auto-loaded. |
|
|
49
|
-
| `--resource <uri>` | Explicit resource URI recorded by the adapter; not auto-loaded. |
|
|
50
|
-
| `--extension <name>` | Explicit extension name recorded by the adapter; not auto-loaded/imported. |
|
|
51
|
-
| `--tool <name>` | Explicit tool name recorded by the adapter; not auto-enabled. |
|
|
52
48
|
| `--system <text>` | System instructions. |
|
|
53
49
|
| `--context <text>` | Context text reserved for host adapters. |
|
|
54
50
|
| `--compact <entries>` | Auto-compaction threshold for the run. |
|
|
@@ -202,6 +198,6 @@ Suspended workflow resume parameters are `{ workflowId, runId, decision: "approv
|
|
|
202
198
|
- [Observational memory compaction package](compaction-observational-memory.md): optional `om:status` and `om:view` command factories for explicitly wired hosts.
|
|
203
199
|
- [Workflows](workflows.md): optional `createWorkflowCommands()` for direct/background/replay/status/cancel/resume and selected schedule control over the same RPC `command` seam.
|
|
204
200
|
|
|
205
|
-
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.
|
|
201
|
+
The CLI records flags but does not auto-load project-local resources, extensions, tools, or config. `--config`, `--resource`, `--extension`, and `--tool` were parsed-and-recorded in earlier builds without any effect; they are now rejected loudly (`<flag> is not supported in this build`) until a CLI-harness plan wires them. 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.
|
|
206
202
|
|
|
207
203
|
For app-controlled agent bundles under `<configRoot>/agents/<name>/AGENT.md` (including the three-layer `SYSTEM.md` → `AGENT.md` body → repo `AGENTS.md` prompt append and the union skill/tool scopes), see [Agent definitions](agent-definitions.md).
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
| `createWriteTool(cwd, options?)` | `write` tool: create or overwrite a file, creating parent directories. |
|
|
12
12
|
| `createEditTool(cwd, options?)` | `edit` tool: precise exact-then-fuzzy text replacement in an existing file. |
|
|
13
13
|
| `createRepoListTool(cwd, options?)` | `repo_list` tool: bounded deterministic repository listing. |
|
|
14
|
-
| `createRepoSearchTool(cwd, options?)` | `repo_search` tool: bounded literal
|
|
14
|
+
| `createRepoSearchTool(cwd, options?)` | `repo_search` tool: bounded literal text search. |
|
|
15
15
|
| `createCodingTools(cwd, options?)` | Default six tools (`shell`, `read`, `write`, `edit`, `repo_list`, `repo_search`). |
|
|
16
16
|
| `createReadOnlyTools(cwd, options?)` | Read-only subset: `read`, `repo_list`, `repo_search`. |
|
|
17
17
|
| `createAllTools(cwd, options?)` | Identical to `createCodingTools` (Git tools remain opt-in via `createGitTools`). |
|
|
@@ -152,6 +152,8 @@ Create or overwrite a file, creating parent directories as needed.
|
|
|
152
152
|
|
|
153
153
|
**Outputs:** a `TextContent` confirmation naming the **absolute path** with UTF-8 byte and line counts (e.g. `Successfully wrote 42 bytes (3 lines) to /abs/path.txt`). `maxInputBytes` defaults to 8 MiB (64 MiB hard cap); oversized UTF-8 input fails before policy evaluation, directory creation, or write. Write failures and abort are error results. Empty `content` is valid.
|
|
154
154
|
|
|
155
|
+
Default local `writeFile` uses same-directory temp + `rename` so a crash mid-write cannot truncate the target; custom `WriteOperations` should provide equivalent durability.
|
|
156
|
+
|
|
155
157
|
`write` result `metadata`: `{ bytes, lines, path }` (absolute path). Concurrent writes to the same path serialize through `withFileMutationQueue`; writes to different paths run in parallel.
|
|
156
158
|
|
|
157
159
|
### `edit`
|
|
@@ -165,7 +167,7 @@ Precise text replacement in an existing file via exact-then-fuzzy matching.
|
|
|
165
167
|
| `path` | `string` | Path to the file to edit. Required. |
|
|
166
168
|
| `edits` | `Array<{ oldText: string, newText: string }>` | Targeted replacements, each matched against the **original** file (not incrementally). No overlapping/nested edits. Required, non-empty. |
|
|
167
169
|
|
|
168
|
-
Each `edits[].oldText` must match a unique, non-overlapping region of the original file. Matching is exact first, then fuzzy (unicode normalization / whitespace collapse). A BOM is stripped before matching and re-prepended on write; original line endings are restored. Defaults reject targets over 8 MiB, aggregate old/new UTF-8 input over 2 MiB, or more than 100 edits (hard caps: 64 MiB, 16 MiB, and 1,000). Stat and bounded read checks run before matching or mutation.
|
|
170
|
+
Each `edits[].oldText` must match a unique, non-overlapping region of the original file. Matching is exact first, then fuzzy (unicode normalization / whitespace collapse). **Fuzzy matching can apply a wrong region when `oldText` is slightly off** — tradeoff for imprecise models; prefer exact `oldText` when possible. A BOM is stripped before matching and re-prepended on write; original line endings are restored. Defaults reject targets over 8 MiB, aggregate old/new UTF-8 input over 2 MiB, or more than 100 edits (hard caps: 64 MiB, 16 MiB, and 1,000). Stat and bounded read checks run before matching or mutation. Default local `writeFile` uses same-directory temp + `rename` (crash-safe replace).
|
|
169
171
|
|
|
170
172
|
**Outputs:** a `TextContent` confirmation (`Successfully replaced N block(s) in {path}.`) plus `metadata`. Any failure — missing/unreadable file, no match, duplicate (non-unique) match, overlap, empty `oldText`, no-op edit, or abort — is an error result, and the file is left **unchanged** (the match runs before the write).
|
|
171
173
|
|
|
@@ -189,15 +191,15 @@ List repository entries with deterministic relative paths. Uses Node `opendir`/`
|
|
|
189
191
|
|
|
190
192
|
### `repo_search`
|
|
191
193
|
|
|
192
|
-
Search text files under the workspace
|
|
194
|
+
Search text files under the workspace using literal substring match. Binary files (NUL in a bounded prefix) and oversize files are skipped. Aggregate scanned bytes, matches, line bytes, pattern bytes, and wall time are finite.
|
|
193
195
|
|
|
194
196
|
**Inputs:**
|
|
195
197
|
|
|
196
198
|
| Field | Type | Purpose |
|
|
197
199
|
| --- | --- | --- |
|
|
198
|
-
| `query` | `string` | Literal
|
|
200
|
+
| `query` | `string` | Literal substring (required). |
|
|
199
201
|
| `path` | `string` | Workspace-relative start path. |
|
|
200
|
-
| `mode` | `"literal"
|
|
202
|
+
| `mode` | `"literal"` | Literal only (default). `regex` removed in 0.0.18. |
|
|
201
203
|
| `caseSensitive` | `boolean` | Default false. |
|
|
202
204
|
| `includeHidden` | `boolean` | Default false. |
|
|
203
205
|
| `context` | `number` | Context lines before/after each match (default 5, hard 20). |
|
|
@@ -367,6 +369,8 @@ const shell = createShellTool("/repo", {
|
|
|
367
369
|
maxLines: 500,
|
|
368
370
|
timeout: 600,
|
|
369
371
|
maxTotalOutputBytes: 64 * 1024 * 1024,
|
|
372
|
+
// Optional: scrub the environment the spawn hook and child process see (default: full process.env clone).
|
|
373
|
+
envAllowlist: ["PATH", "HOME", "LANG"],
|
|
370
374
|
});
|
|
371
375
|
|
|
372
376
|
const remoteWrite = createWriteTool("/repo", {
|
|
@@ -408,7 +412,7 @@ const remoteWrite = createWriteTool("/repo", {
|
|
|
408
412
|
| Shell total stdout+stderr | 64 MiB | 1 GiB | process-tree kill; spill removal |
|
|
409
413
|
| Repo depth / entries / files / page | 32 / 10,000 / 10,000 / 1,000 | 128 / 100,000 / 100,000 / 10,000 | before descending/retaining next entry |
|
|
410
414
|
| Search scan / file / matches | 64 MiB / 8 MiB / 1,000 | 1 GiB / 64 MiB / 10,000 | before next file/match retention |
|
|
411
|
-
| Search pattern / line / context / time | 512 B / 50 KiB / 5 / 30 s | 4 KiB / 1 MiB / 20 / 300 s | before
|
|
415
|
+
| Search pattern / line / context / time | 512 B / 50 KiB / 5 / 30 s | 4 KiB / 1 MiB / 20 / 300 s | before pattern compile / line retain / deadline |
|
|
412
416
|
| Git paths / refs / message | 1,000 / 1 KiB / 64 KiB | 10,000 / 4 KiB / 256 KiB | before process/temp-file creation |
|
|
413
417
|
| Git output / diff lines / changed files / patch | 4 MiB / 10,000 / 1,000 / 16 MiB | 64 MiB / 100,000 / 10,000 / 64 MiB | stream before retain; artifact spill optional |
|
|
414
418
|
| Worktrees | 4 | 16 | before add |
|
|
@@ -68,10 +68,12 @@ createDefaultRetryPolicy(options?: DefaultRetryPolicyOptions): RetryPolicy
|
|
|
68
68
|
| `maxAttempts` | Total provider-turn attempts; defaults to `3`. |
|
|
69
69
|
| `baseDelayMs` | First retry delay; defaults to `100`. |
|
|
70
70
|
| `maxDelayMs` | Backoff cap; defaults to `1000`. |
|
|
71
|
+
| `jitter` | Symmetric jitter fraction on computed delays; defaults to `0.25` (±25%). Set `0` for exact delays. |
|
|
72
|
+
| `random` | Random source for jitter (tests); defaults to `Math.random`. |
|
|
71
73
|
| `secrets` | Exact known secret strings to redact from retry errors/events. |
|
|
72
74
|
| `metadata` | Explicit host metadata for retry policy context. |
|
|
73
75
|
|
|
74
|
-
`RunOptions.retry: false` disables configured retry for that run. Default classification retries generic transient codes/messages such as `ETIMEDOUT`, `ECONNRESET`, `429`, `500`, `502`, `503`, `504`, `timeout`, `rate_limit`, and `temporarily_unavailable`; aborts and non-transient errors fail closed.
|
|
76
|
+
`RunOptions.retry: false` disables configured retry for that run. Default classification retries generic transient codes/messages such as `ETIMEDOUT`, `ECONNRESET`, `429`, `500`, `502`, `503`, `504`, `timeout`, `rate_limit`, and `temporarily_unavailable`; aborts and non-transient errors fail closed. Delays are exponential (`baseDelayMs * 2^(attempt-1)`, capped at `maxDelayMs`) with symmetric jitter, so concurrent sessions do not retry in lockstep during a shared outage. When `ErrorInfo.retryAfterMs` is set — first-party HTTP providers populate it from the `Retry-After` response header via `httpStatusError()` — the hint wins over computed backoff, jitter still applies, and the result is always capped at `maxDelayMs` so a hostile or huge hint cannot pin a run.
|
|
75
77
|
|
|
76
78
|
`CompactionEntryData` is stored in `SessionEntry.data` for compaction entries:
|
|
77
79
|
|
|
@@ -27,6 +27,7 @@ createContributionRegistries(options?: { duplicate?: "replace" | "error" }): Con
|
|
|
27
27
|
| Method | Input | Result |
|
|
28
28
|
| --- | --- | --- |
|
|
29
29
|
| `register(key, contribution)` | string key and contribution | Stores/replaces the contribution for that key; throws `Duplicate <label>: <key>` when `duplicate: "error"`. |
|
|
30
|
+
| `unregister(key)` | string key | Removes the contribution; returns `false` when the key was not registered. `providers.unregister(id)` and `models.unregister(provider, model)` mirror this on the specialized registries. |
|
|
30
31
|
| `get(key)` | string key | Returns the contribution or `undefined`. |
|
|
31
32
|
| `resolve(key)` | string key | Returns the contribution or throws `Unknown <label>: <key>`. |
|
|
32
33
|
| `list()` | none | Returns contributions in insertion order. |
|
|
@@ -132,4 +132,4 @@ A future provider-local OAuth adapter needs published permission for third-party
|
|
|
132
132
|
- [LLM compaction package](compaction-llm.md): resolves optional summary-provider credentials per compaction call and redacts exact known values.
|
|
133
133
|
- [OpenAI-compatible provider](providers/openai-compatible.md): resolves API keys per request and redacts known values from adapter errors.
|
|
134
134
|
|
|
135
|
-
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).
|
|
135
|
+
Phase 10 added `createMemoryCredentialStore()`, `createChainedCredentialResolver()`, and `createSecretRedactor()` for opt-in in-memory auth and runtime redaction. By default the memory store serves a providerless record for a provider-scoped request of the same name — that record is then shared across every provider; pass `{ allowProviderFallback: false }` for exact-match-only resolution (strict provider scoping). 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).
|
package/docs/extensions.md
CHANGED
|
@@ -37,7 +37,7 @@ createExtensionEventBus(options?: { errorPolicy?: "event" | "throw"; secrets?: r
|
|
|
37
37
|
|
|
38
38
|
## Outputs / response / events
|
|
39
39
|
|
|
40
|
-
- `kernel.load(extensions)` calls each extension's `setup(api)` in host-provided order.
|
|
40
|
+
- `kernel.load(extensions)` calls each extension's `setup(api)` in host-provided order and resolves to `LoadedExtension[]` (`{ name, dispose() }`). A failed `setup` unwinds that extension's partial registrations (no orphaned half-loads). `dispose()` removes the extension's registry contributions and middleware/event subscriptions via each registry's `unregister(key)` — best-effort, idempotent, and limited to registries/subscriptions: side effects outside the registries (files, network, spawned work) are not unwound, and load-order/dependency graphs between extensions are out of scope.
|
|
41
41
|
- `kernel.registries` exposes the explicit contribution registry bundle.
|
|
42
42
|
- `kernel.events.on(type, handler)` registers ordered event handlers and returns an unsubscribe function.
|
|
43
43
|
- `kernel.events.emit(event)` calls matching handlers in registration order.
|
package/docs/guardrails.md
CHANGED
|
@@ -26,11 +26,22 @@ const guardrails: Guardrails = { input: [pii], maxConcurrency: 1 };
|
|
|
26
26
|
|
|
27
27
|
Set `AgentConfig.guardrails` for every session run or `RunOptions.guardrails` to append checks for one run. `DispatchToolCallOptions.guardrails`, workflow `RunWorkflowOptions.guardrails`, and MCP server `CreatePrismMcpServerOptions.guardrails` apply tool stages to direct calls. A stage has `Guardrail<"input" | "output" | "tool_input" | "tool_output">`, a name, optional revision, and `evaluate(context)` result.
|
|
28
28
|
|
|
29
|
-
Decisions are `allow`, `block`, `tripwire`, or `interrupt`. Evaluation defaults to declaration-order sequential. `maxConcurrency` may be 1–16; records are emitted in declaration order. Thrown or malformed decisions become a fail-closed tripwire. Decision reasons are capped at 4 KiB and metadata at 16 KiB after JSON normalization and optional redaction.
|
|
29
|
+
Decisions are `allow`, `block`, `tripwire`, or `interrupt`. Evaluation defaults to declaration-order sequential. `maxConcurrency` may be 1–16; records are emitted in declaration order. Thrown or malformed decisions become a fail-closed tripwire. A throwing guardrail produces a `guardrail_failed` record whose `metadata.error` carries the underlying error message — redacted and bounded to 4 KiB — so failures stay diagnosable without leaking internals. Decision reasons are capped at 4 KiB and metadata at 16 KiB after JSON normalization and optional redaction.
|
|
30
30
|
|
|
31
31
|
## Outputs / response / events
|
|
32
32
|
|
|
33
|
-
Every evaluated guard produces a redacted `guardrail_decision` `AgentEvent` with a bounded `GuardrailRecord`. Optional OpenTelemetry instrumentation records only controlled stage/action on a short run-child span; guardrail name, reason, and metadata are excluded. An input or output terminal decision rejects the run with `GuardrailError`; `tripwire` stops remaining evaluation. A tool-input or tool-output `block` returns a redacted blocked `ToolResult`; a `tripwire` rejects the enclosing run. `interrupt` is reserved for durable runs
|
|
33
|
+
Every evaluated guard produces a redacted `guardrail_decision` `AgentEvent` with a bounded `GuardrailRecord`. Optional OpenTelemetry instrumentation records only controlled stage/action on a short run-child span; guardrail name, reason, and metadata are excluded. An input or output terminal decision rejects the run with `GuardrailError`; `tripwire` stops remaining evaluation. A tool-input or tool-output `block` returns a redacted blocked `ToolResult`; a `tripwire` rejects the enclosing run. `interrupt` is reserved for durable runs: at the input stage of a fresh durable run it suspends the run for approval (persisted `input_guardrail` interruption); anywhere else it currently fails closed with `ERR_PRISM_GUARDRAIL_INTERRUPT_UNAVAILABLE`. Resuming a suspended durable run re-evaluates input guardrails on the stored input, and the resume decision itself counts as the approval: a repeated input-stage `interrupt` does not re-suspend or fail the resumed run, while `block`/`tripwire` still reject it.
|
|
34
|
+
|
|
35
|
+
Action outcome by stage:
|
|
36
|
+
|
|
37
|
+
| Stage | `block` | `tripwire` | `interrupt` |
|
|
38
|
+
| --- | --- | --- | --- |
|
|
39
|
+
| `input` | run rejected (`GuardrailError`); steered message: dropped + `steer_rejected`, run continues | run rejected; steered message: dropped + `steer_rejected`, run continues | fresh durable run: suspends for approval; otherwise fails closed |
|
|
40
|
+
| `output` | run rejected | run rejected | fails closed (`ERR_PRISM_GUARDRAIL_INTERRUPT_UNAVAILABLE`) |
|
|
41
|
+
| `tool_input` | blocked `ToolResult`, run continues | run rejected | fails closed |
|
|
42
|
+
| `tool_output` | blocked `ToolResult`, run continues | run rejected | fails closed |
|
|
43
|
+
|
|
44
|
+
The `GuardrailError` message names the stage so unsupported `interrupt` placements are diagnosable without reading core source.
|
|
34
45
|
|
|
35
46
|
Ordering is fixed:
|
|
36
47
|
|
package/docs/index.md
CHANGED
|
@@ -50,11 +50,11 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
|
|
|
50
50
|
- Phase 12 package workspaces: [`@arnilo/prism-provider-openai`](providers/openai.md) (Responses hosted-tool attribution, bounded continuation, Realtime session seam), [`@arnilo/prism-provider-anthropic`](providers/anthropic.md) (native Messages, `cache_control`, thinking, caller-gated `listAnthropicModels`), [`@arnilo/prism-provider-google`](providers/google.md) (native Gemini `generateContent` SSE, caller-gated `listGoogleModels`), [`@arnilo/prism-provider-opencode-go`](providers/opencode-go.md) (official Go open models, dual-route Anthropic/OpenAI, caller-gated `listOpenCodeGoModels`, `reasoning_content`/thinking preserve), [`@arnilo/prism-provider-openrouter`](providers/openrouter.md) (app-controlled catalog, caller-gated `listOpenRouterModels`, `reasoning` merge/preserve, `cache_control` + sticky `session_id`), [`@arnilo/prism-provider-zai`](providers/zai.md) (official `thinking`/`reasoning_effort`/`tool_stream`, implicit cache, caller-gated `listZaiModels`), [`@arnilo/prism-provider-kimi`](providers/kimi.md), [`@arnilo/prism-provider-alibaba`](providers/alibaba.md) (Alibaba Cloud Model Studio / DashScope + Coding Plan, OpenAI-compatible, caller-gated `listAlibabaModels`, implicit + explicit `cache_control` caching, Qwen `enable_thinking`), [`@arnilo/prism-provider-ollama`](providers/ollama.md) (Ollama Cloud + local, OpenAI-compatible, caller-gated `listOllamaModels`, implicit-only caching, `reasoning_effort`), and [`@arnilo/prism-provider-neuralwatt`](providers/neuralwatt.md) with implicit vLLM prefix caching, reasoning controls (`reasoning_effort`/`thinking_token_budget`/`enable_thinking`/`preserve_thinking`/`clear_thinking`), reasoning preservation, OpenAI-style tool-call loop, quota, telemetry, and retry classification helpers.
|
|
51
51
|
- Phase 8 enterprise cloud (workload identity; separate from consumer Anthropic/Google): [`@arnilo/prism-provider-azure`](providers/azure.md) (Entra / Foundry), [`@arnilo/prism-provider-bedrock`](providers/bedrock.md) (IAM/IRSA + region/PrivateLink), [`@arnilo/prism-provider-vertex`](providers/vertex.md) (ADC / Vertex OpenAPI).
|
|
52
52
|
- Optional AI SDK adapter: [`@arnilo/prism-provider-ai-sdk`](providers/ai-sdk.md) maps host-owned pinned `LanguageModelV4` models onto Prism `AIProvider` streams (offline-tested `@ai-sdk/provider` version matrix; no Prism catalog; maps metadata/tool authority/`finish.usage` cache tokens; reasoning is host-model-owned).
|
|
53
|
-
- [OpenAI-compatible provider](providers/openai-compatible.md): optional provider subpath using native or injected `fetch` for Chat Completions streaming (`chatCompletionsUrl` / `authStyle` overrides for enterprise adapters).
|
|
53
|
+
- [OpenAI-compatible provider](providers/openai-compatible.md): optional provider subpath using native or injected `fetch` for Chat Completions streaming (`chatCompletionsUrl` / `authStyle` overrides for enterprise adapters; `buildBodyExtra` / `mapMessages` / `mapUsage` / `extraHeaders` hooks for vendor variants).
|
|
54
54
|
|
|
55
55
|
## Input, prompt, and context assembly
|
|
56
56
|
- [SDK customization guide](customization.md): map provider resolution, middleware, context, builders, injectors, loops, compaction, retry, stores, and skills to explicit host-wired APIs.
|
|
57
|
-
- [Input and prompt assembly](input-and-prompt-assembly.md): render tiny prompt templates and turn common host input, history, attachments, explicit resources, summaries, and tool results into messages with replaceable builders, provider-input assembly,
|
|
57
|
+
- [Input and prompt assembly](input-and-prompt-assembly.md): render tiny prompt templates and turn common host input, history, attachments, explicit resources, summaries, and tool results into messages with replaceable builders, provider-input assembly, cache-aware default order, opt-in legacy ordering, and optional `contextBudget` eviction + omission reports. Audio/file/document `ContentBlock` types and capability checks are documented there.
|
|
58
58
|
- [Multimodal content](multimodal-content.md): complete-request media resolution and aggregate bounds, DNS-classified/address-pinned URLs, SSRF/MIME policy, `ModelCapabilities.input` tags, and first-party content-type mapping.
|
|
59
59
|
- [System prompts](system-prompts.md): compose explicit user/package/app/run system prompt layers, auto-load the standard `AGENTS.md` (workspace) / `SYSTEM.md` prompt files via the Node `loadSystemPromptFiles` loader (trust-gated for `AGENTS.md`), and append `SYSTEM.md` → per-agent `AGENT.md` body → repo `AGENTS.md` layers from a discovered agent bundle via `resolveAgentBundle`.
|
|
60
60
|
- [Instruction injection](instruction-injection.md): register package injectors that layer redacted instructions/context blocks without granting tools, permissions, or resource escapes.
|
|
@@ -117,8 +117,8 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
|
|
|
117
117
|
- `examples/`: compile-checked typed examples and runnable mock demos (SDK basics, provider registration, auth, tools, [`examples/ag-ui-server.ts`](../examples/ag-ui-server.ts), [`examples/enterprise-identity.ts`](../examples/enterprise-identity.ts), [`examples/enterprise-policy-audit.ts`](../examples/enterprise-policy-audit.ts), [`examples/enterprise-work-connectors.ts`](../examples/enterprise-work-connectors.ts), [`examples/conversation-durable-replay.ts`](../examples/conversation-durable-replay.ts), [`examples/artifact-review-delivery.ts`](../examples/artifact-review-delivery.ts), [`examples/server-deployment-seams.ts`](../examples/server-deployment-seams.ts), cache-aware prompt assembly, NeuralWatt agent run ([`examples/neuralwatt-agent-run.ts`](../examples/neuralwatt-agent-run.ts)), [`examples/coding-compaction.ts`](../examples/coding-compaction.ts), stores/branching, structured-output/artifact-loop, CLI, RPC, workflow orchestration).
|
|
118
118
|
|
|
119
119
|
## Release and install
|
|
120
|
-
- [Release and install](release-and-install.md): current **0.0.
|
|
121
|
-
- [0.1.0 / 1.0 readiness gates](0.1.0-readiness.md): command-per-gate 1.0 readiness table — frozen API surface + compat gate, migration
|
|
120
|
+
- [Release and install](release-and-install.md): current **0.0.18** 44-package graph (Phase 1 restore integrity; plan 001), exact-peer/install/tarball rules, deterministic resumable publication and publish dry-run, pinned supply-chain gates, offline tests, the 0.0.15 provider/AI-SDK/RAG/memory protected live-canary matrix, and sandbox-browser Docker/Playwright gates.
|
|
121
|
+
- [0.1.0 / 1.0 readiness gates](0.1.0-readiness.md): command-per-gate 1.0 readiness table — frozen API surface + compat gate, migration/docs tripwires, budget table, live-suite matrix, security matrix, current-line status (**0.0.18** published target), signed-publication/live-canary prerequisites for 1.0, and Phase 12 demand-evidence entry criteria.
|
|
122
122
|
- [Review coverage (2026-07-26 Phase 11)](review-coverage-2026-07-26-phase-11.md): Plan 079 evidence freeze — baseline size/startup/benchmark budgets, hotspot domain extraction table, confirmed duplication survivors (redactor/cleanJson/row-codecs/checkpoints/exec-runner/approval/ownership), profile adoption recommendations, and tarball artifact-diet findings for 0.0.16.
|
|
123
123
|
- [Review coverage (2026-07-26 Phase 10)](review-coverage-2026-07-26-phase-10.md): Plan 078 evidence freeze — OpenAI hosted tools/continuation/realtime, AI SDK version matrix, remaining provider metadata parity, RAG replaceSource/loaders/parsers/reranker/provenance/ingestion-status, memory export/rebuild/conformance, and 0.0.15 (43 → 43 manifests; no new package) release gates.
|
|
124
124
|
- [Review coverage (2026-07-25 Phase 9)](review-coverage-2026-07-25-phase-9.md): Plan 077 evidence freeze — conversation service, memory consent/lifecycle, artifact co-work review, AG-UI co-work events, scoped M365/GWS OAuth, browser checkpoint composition, and deny-by-default device contracts for 0.0.14 (41 → 43 manifests; only the two provider packages are new).
|