@arnilo/prism 0.0.96 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +285 -2
- package/README.md +17 -3
- package/dist/agent-definitions.js +2 -3
- package/dist/agent-event-source.d.ts +11 -0
- package/dist/agent-event-source.js +512 -0
- package/dist/agent-loops.d.ts +5 -0
- package/dist/agent-loops.js +99 -14
- package/dist/agent-run-lifecycle.d.ts +5 -2
- package/dist/agent-run-lifecycle.js +18 -2
- package/dist/agent-run-state.d.ts +27 -1
- package/dist/agent-run-state.js +113 -7
- package/dist/agents.d.ts +3 -1
- package/dist/agents.js +1255 -129
- package/dist/artifacts.d.ts +132 -0
- package/dist/artifacts.js +44 -0
- package/dist/cache-helpers.js +18 -9
- package/dist/checkpoints.d.ts +4 -0
- package/dist/checkpoints.js +17 -9
- package/dist/cli-init.js +3 -7
- package/dist/cli-runner.d.ts +2 -6
- package/dist/cli-runner.js +71 -33
- package/dist/compaction.js +5 -4
- package/dist/config.js +7 -4
- package/dist/content.js +26 -24
- package/dist/context-budget.d.ts +67 -0
- package/dist/context-budget.js +288 -0
- package/dist/contracts.d.ts +590 -8
- package/dist/contracts.js +142 -1
- package/dist/contribution-parsing.js +6 -2
- package/dist/contributions.d.ts +2 -0
- package/dist/contributions.js +3 -0
- package/dist/conversations.d.ts +50 -0
- package/dist/conversations.js +98 -0
- package/dist/credentials.d.ts +22 -2
- package/dist/credentials.js +18 -3
- package/dist/devices.d.ts +94 -0
- package/dist/devices.js +138 -0
- package/dist/event-multiplexer.js +18 -4
- package/dist/extensions.d.ts +18 -1
- package/dist/extensions.js +79 -6
- package/dist/feedback.js +12 -10
- package/dist/guardrails.d.ts +1 -1
- package/dist/guardrails.js +26 -17
- package/dist/identity.d.ts +92 -0
- package/dist/identity.js +265 -0
- package/dist/index.d.ts +94 -72
- package/dist/index.js +48 -36
- package/dist/input.d.ts +10 -1
- package/dist/input.js +152 -52
- package/dist/instruction-injection.d.ts +1 -1
- package/dist/middleware.js +9 -1
- package/dist/models.d.ts +2 -0
- package/dist/models.js +3 -0
- package/dist/node/agent-definitions.js +16 -8
- package/dist/node/contribution-discovery.d.ts +1 -2
- package/dist/node/contribution-discovery.js +3 -3
- package/dist/node/session-store-jsonl.js +13 -7
- package/dist/node/settings.d.ts +1 -1
- package/dist/node/settings.js +1 -1
- package/dist/node/system-project-prompts.js +2 -4
- package/dist/node/trust.js +1 -1
- package/dist/persistence-lifecycle.d.ts +103 -0
- package/dist/persistence-lifecycle.js +202 -0
- package/dist/provider-events.d.ts +1 -0
- package/dist/provider-events.js +6 -1
- package/dist/provider-request-policy.js +3 -4
- package/dist/providers/media.d.ts +1 -1
- package/dist/providers/openai-compatible.d.ts +46 -1
- package/dist/providers/openai-compatible.js +123 -53
- package/dist/providers/openai-primitives.js +10 -7
- 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.d.ts +1 -0
- package/dist/redaction.js +26 -9
- package/dist/resources.d.ts +2 -2
- package/dist/resources.js +2 -2
- package/dist/retry.d.ts +5 -0
- package/dist/retry.js +8 -1
- package/dist/rpc.js +55 -11
- package/dist/run-ledger.d.ts +6 -0
- package/dist/run-ledger.js +16 -13
- package/dist/run-limits.js +49 -10
- package/dist/secure-agent.js +8 -2
- package/dist/security.js +7 -2
- package/dist/session-stores.d.ts +7 -2
- package/dist/session-stores.js +195 -21
- package/dist/skill-disclosure.d.ts +35 -0
- package/dist/skill-disclosure.js +101 -0
- package/dist/skill-load.d.ts +25 -0
- package/dist/skill-load.js +112 -0
- package/dist/structured-output.d.ts +5 -1
- package/dist/structured-output.js +20 -2
- package/dist/system-prompts.js +7 -2
- package/dist/testing/agent-event-source-conformance.d.ts +4 -0
- package/dist/testing/agent-event-source-conformance.js +54 -0
- package/dist/testing/compaction-conformance.js +5 -1
- package/dist/testing/extension-conformance.js +15 -3
- package/dist/testing/feedback.d.ts +1 -3
- package/dist/testing/feedback.js +1 -1
- package/dist/testing/persistence-schema.d.ts +2 -2
- package/dist/testing/persistence-schema.js +280 -35
- package/dist/testing/provider-conformance.js +3 -3
- package/dist/testing/run-ledger-conformance.js +1 -1
- package/dist/testing/session-store-conformance.d.ts +6 -0
- package/dist/testing/session-store-conformance.js +37 -2
- package/dist/testing/tool-conformance.js +30 -5
- package/dist/testing/tool-effect-store-conformance.d.ts +9 -0
- package/dist/testing/tool-effect-store-conformance.js +85 -0
- package/dist/thinking.js +4 -1
- package/dist/tool-effects.d.ts +15 -0
- package/dist/tool-effects.js +352 -0
- package/dist/tool-result-fold.d.ts +40 -0
- package/dist/tool-result-fold.js +176 -0
- package/dist/tools.d.ts +8 -3
- package/dist/tools.js +248 -13
- package/docs/0.1.0-readiness.md +202 -0
- package/docs/a2a.md +33 -2
- package/docs/acp.md +126 -0
- package/docs/ag-ui-adoption.md +77 -0
- package/docs/ag-ui.md +225 -0
- package/docs/agent-events.md +34 -3
- package/docs/agent-identity.md +144 -0
- package/docs/agent-loops.md +17 -2
- package/docs/agent-session-runtime.md +21 -4
- package/docs/browser-automation.md +5 -0
- package/docs/caveman.md +129 -0
- package/docs/cli-rpc.md +3 -6
- package/docs/coding-agent-tools.md +229 -25
- package/docs/coding-security.md +77 -11
- package/docs/compaction-and-retry.md +5 -2
- package/docs/compaction-llm.md +20 -1
- package/docs/compaction-observational-memory.md +52 -8
- package/docs/context-and-skills.md +94 -7
- package/docs/contribution-registries.md +1 -0
- package/docs/conversations.md +135 -0
- package/docs/credential-storage.md +34 -1
- package/docs/credentials-and-redaction.md +11 -1
- package/docs/database-persistence.md +27 -7
- package/docs/device-adapters.md +97 -0
- package/docs/enterprise-postgres-state.md +178 -0
- package/docs/evaluations.md +14 -1
- package/docs/extensions.md +4 -1
- package/docs/forge-integration.md +113 -0
- package/docs/guardrails.md +16 -2
- package/docs/host-security.md +35 -4
- package/docs/index.md +69 -37
- package/docs/input-and-prompt-assembly.md +8 -7
- package/docs/language-intelligence.md +162 -0
- package/docs/mcp-tools.md +62 -5
- package/docs/middleware-hooks.md +2 -2
- package/docs/migration.md +423 -2
- package/docs/model-routing.md +111 -0
- package/docs/multimodal-content.md +8 -5
- package/docs/node-jsonl-session-store.md +1 -1
- package/docs/observability.md +2 -0
- package/docs/openapi-tools.md +56 -0
- package/docs/performance.md +282 -0
- package/docs/policy-and-audit.md +171 -0
- package/docs/ponytail.md +127 -0
- package/docs/postgres-persistence.md +8 -4
- package/docs/process-sessions.md +147 -0
- package/docs/provider-caching.md +13 -1
- package/docs/provider-conformance.md +29 -5
- package/docs/provider-packages.md +43 -2
- package/docs/provider-request-policies.md +2 -0
- package/docs/providers/ai-sdk.md +24 -7
- package/docs/providers/alibaba.md +179 -0
- package/docs/providers/anthropic.md +93 -0
- package/docs/providers/azure.md +74 -0
- package/docs/providers/bedrock.md +72 -0
- package/docs/providers/google.md +89 -0
- package/docs/providers/ollama.md +166 -0
- package/docs/providers/openai-compatible.md +31 -2
- package/docs/providers/openai.md +24 -5
- package/docs/providers/openrouter.md +2 -0
- package/docs/providers/vertex.md +71 -0
- package/docs/public-contracts.md +61 -4
- package/docs/rag.md +41 -12
- package/docs/release-and-install.md +323 -206
- package/docs/resource-loading.md +3 -0
- package/docs/runs-and-usage.md +3 -0
- package/docs/server.md +44 -6
- package/docs/session-store-conformance.md +2 -0
- package/docs/session-stores.md +41 -2
- package/docs/sqlite-persistence.md +11 -3
- package/docs/structured-output.md +7 -1
- package/docs/supervisors.md +8 -0
- package/docs/tool-effects.md +95 -0
- package/docs/tools.md +5 -0
- package/docs/work-artifacts-and-review.md +102 -0
- package/docs/work-connectors.md +32 -0
- package/docs/work-tools.md +137 -0
- package/docs/workflows.md +6 -0
- package/docs/working-and-semantic-memory.md +40 -7
- package/package.json +30 -8
- package/templates/init/providers.json +22 -0
- package/docs/review-coverage-2026-07-14.md +0 -260
- package/docs/review-coverage-2026-07-15.md +0 -193
- package/docs/review-coverage-2026-07-17-provider-validation.md +0 -192
- package/docs/review-coverage-2026-07-19-phase-3.md +0 -174
- package/docs/review-coverage-2026-07-20-phase-4.md +0 -175
package/dist/devices.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { redactSecrets } from "./redaction.js";
|
|
2
|
+
/** Audio / screenshot / stream chunk: 1 MiB default / 8 MiB hard. */
|
|
3
|
+
export const DEFAULT_DEVICE_MAX_CHUNK_BYTES = 1 * 1024 * 1024;
|
|
4
|
+
export const HARD_DEVICE_MAX_CHUNK_BYTES = 8 * 1024 * 1024;
|
|
5
|
+
/** Concurrent device sessions per identity: 1 default / 4 hard. */
|
|
6
|
+
export const DEFAULT_DEVICE_MAX_CONCURRENT_SESSIONS = 1;
|
|
7
|
+
export const HARD_DEVICE_MAX_CONCURRENT_SESSIONS = 4;
|
|
8
|
+
export class DevicePolicyError extends Error {
|
|
9
|
+
code;
|
|
10
|
+
constructor(code, message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.name = "DevicePolicyError";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function validateCap(name, value, def, hard) {
|
|
17
|
+
const resolved = value ?? def;
|
|
18
|
+
if (!Number.isSafeInteger(resolved) || resolved < 1 || resolved > hard) {
|
|
19
|
+
throw new DevicePolicyError("ERR_PRISM_DEVICE_INPUT", `${name} must be a positive safe integer at most ${hard}`);
|
|
20
|
+
}
|
|
21
|
+
return resolved;
|
|
22
|
+
}
|
|
23
|
+
export function resolveDevicePolicy(adapter, options = {}) {
|
|
24
|
+
if (!adapter || (adapter.kind !== "voice" && adapter.kind !== "desktop-control")) {
|
|
25
|
+
throw new DevicePolicyError("ERR_PRISM_DEVICE_INPUT", "device kind must be 'voice' or 'desktop-control'");
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
kind: adapter.kind,
|
|
29
|
+
// Deny-by-default: anything but an explicit `true` resolves to disabled.
|
|
30
|
+
enabled: adapter.enabled === true,
|
|
31
|
+
// Approval is required unless the host explicitly opts out (it should not).
|
|
32
|
+
requireApproval: adapter.requireApproval !== false,
|
|
33
|
+
maxChunkBytes: validateCap("maxChunkBytes", options.maxChunkBytes ?? adapter.limits?.maxChunkBytes, DEFAULT_DEVICE_MAX_CHUNK_BYTES, HARD_DEVICE_MAX_CHUNK_BYTES),
|
|
34
|
+
maxConcurrentSessions: validateCap("maxConcurrentSessions", options.maxConcurrentSessions ?? adapter.limits?.maxConcurrentSessions, DEFAULT_DEVICE_MAX_CONCURRENT_SESSIONS, HARD_DEVICE_MAX_CONCURRENT_SESSIONS),
|
|
35
|
+
...(adapter.sandbox ? { sandbox: adapter.sandbox } : {}),
|
|
36
|
+
...(adapter.network ? { network: adapter.network } : {}),
|
|
37
|
+
...(options.runLimits ? { runLimits: options.runLimits } : {}),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Fail-closed admission gate. Denies unless the device is explicitly enabled,
|
|
42
|
+
* sandboxed, approved (when required), under the concurrent-session budget, and
|
|
43
|
+
* bound to shared run accounting. Side effects never replay after reconnect:
|
|
44
|
+
* hosts must re-admit on every resume.
|
|
45
|
+
*/
|
|
46
|
+
export function assertDeviceAdmit(policy, request) {
|
|
47
|
+
if (!policy.enabled) {
|
|
48
|
+
throw new DevicePolicyError("ERR_PRISM_DEVICE_DISABLED", `${policy.kind} device is disabled by default`);
|
|
49
|
+
}
|
|
50
|
+
if (!policy.sandbox) {
|
|
51
|
+
throw new DevicePolicyError("ERR_PRISM_DEVICE_DISABLED", `${policy.kind} device requires an explicit sandbox`);
|
|
52
|
+
}
|
|
53
|
+
if (policy.requireApproval && !request.approved) {
|
|
54
|
+
throw new DevicePolicyError("ERR_PRISM_DEVICE_APPROVAL", `${policy.kind} device side effect requires approval`);
|
|
55
|
+
}
|
|
56
|
+
if (!Number.isSafeInteger(request.activeSessions) || request.activeSessions < 0) {
|
|
57
|
+
throw new DevicePolicyError("ERR_PRISM_DEVICE_INPUT", "activeSessions must be a non-negative safe integer");
|
|
58
|
+
}
|
|
59
|
+
if (request.activeSessions >= policy.maxConcurrentSessions) {
|
|
60
|
+
throw new DevicePolicyError("ERR_PRISM_DEVICE_SESSIONS", `concurrent ${policy.kind} sessions capped at ${policy.maxConcurrentSessions}`);
|
|
61
|
+
}
|
|
62
|
+
if (!policy.runLimits) {
|
|
63
|
+
throw new DevicePolicyError("ERR_PRISM_DEVICE_RUN_LIMITS", `${policy.kind} device must consume shared RunLimits`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Stream bound: oversize audio/screenshot/stream chunks are dropped with a marker, never forwarded. */
|
|
67
|
+
export function acceptDeviceChunk(policy, bytes) {
|
|
68
|
+
if (!Number.isSafeInteger(bytes) || bytes < 0) {
|
|
69
|
+
throw new DevicePolicyError("ERR_PRISM_DEVICE_CHUNK", "chunk bytes must be a non-negative safe integer");
|
|
70
|
+
}
|
|
71
|
+
if (bytes > policy.maxChunkBytes) {
|
|
72
|
+
return { accepted: false, bytes, marker: "dropped_oversize" };
|
|
73
|
+
}
|
|
74
|
+
return { accepted: true, bytes };
|
|
75
|
+
}
|
|
76
|
+
/** Telemetry must be metadata-safe: apply the host redactor before any emit/persist. */
|
|
77
|
+
export function redactDeviceTelemetry(redactor, telemetry) {
|
|
78
|
+
return redactor ? redactor.redact(telemetry) : telemetry;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Conformance pair for future voice / desktop-control adapters. Runs the
|
|
82
|
+
* deny-by-default fixtures (denial, approval, stream bounds, session budget,
|
|
83
|
+
* run accounting, redaction) against a resolved policy and throws on any
|
|
84
|
+
* regression. Tested in 0.0.14 via fixtures only.
|
|
85
|
+
*/
|
|
86
|
+
export function runDevicePolicyConformance(adapter, options = {}) {
|
|
87
|
+
const runLimits = options.runLimits ?? { maxTurns: 1 };
|
|
88
|
+
const expectThrow = (code, fn) => {
|
|
89
|
+
let threw = false;
|
|
90
|
+
try {
|
|
91
|
+
fn();
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
threw = error instanceof DevicePolicyError && error.code === code;
|
|
95
|
+
}
|
|
96
|
+
if (!threw)
|
|
97
|
+
throw new DevicePolicyError(code, `conformance: expected ${code}`);
|
|
98
|
+
};
|
|
99
|
+
const passed = [];
|
|
100
|
+
// 1. Denial by default: a disabled adapter never admits.
|
|
101
|
+
const disabled = resolveDevicePolicy({ ...adapter, enabled: false }, { ...options, runLimits });
|
|
102
|
+
expectThrow("ERR_PRISM_DEVICE_DISABLED", () => assertDeviceAdmit(disabled, { approved: true, activeSessions: 0 }));
|
|
103
|
+
passed.push("denial-by-default");
|
|
104
|
+
// 2. Approval gate: enabled+sandboxed but unapproved denies; approved admits.
|
|
105
|
+
const enabled = resolveDevicePolicy({ ...adapter, enabled: true, requireApproval: true, sandbox: adapter.sandbox ?? "sandbox" }, { ...options, runLimits });
|
|
106
|
+
expectThrow("ERR_PRISM_DEVICE_APPROVAL", () => assertDeviceAdmit(enabled, { approved: false, activeSessions: 0 }));
|
|
107
|
+
assertDeviceAdmit(enabled, { approved: true, activeSessions: 0 });
|
|
108
|
+
passed.push("approval-gate");
|
|
109
|
+
// 3. Session budget: at/over the concurrent cap denies.
|
|
110
|
+
expectThrow("ERR_PRISM_DEVICE_SESSIONS", () => assertDeviceAdmit(enabled, { approved: true, activeSessions: enabled.maxConcurrentSessions }));
|
|
111
|
+
passed.push("session-budget");
|
|
112
|
+
// 4. Run accounting: no shared RunLimits denies.
|
|
113
|
+
const unaccounted = resolveDevicePolicy({ ...adapter, enabled: true, sandbox: "sandbox" }, { ...options, runLimits: undefined });
|
|
114
|
+
expectThrow("ERR_PRISM_DEVICE_RUN_LIMITS", () => assertDeviceAdmit(unaccounted, { approved: true, activeSessions: 0 }));
|
|
115
|
+
passed.push("run-accounting");
|
|
116
|
+
// 5. Stream bounds: oversize chunk dropped with marker; in-bound accepted.
|
|
117
|
+
const over = acceptDeviceChunk(enabled, enabled.maxChunkBytes + 1);
|
|
118
|
+
if (over.accepted || over.marker !== "dropped_oversize") {
|
|
119
|
+
throw new DevicePolicyError("ERR_PRISM_DEVICE_CHUNK", "conformance: oversize chunk must be dropped");
|
|
120
|
+
}
|
|
121
|
+
if (!acceptDeviceChunk(enabled, enabled.maxChunkBytes).accepted) {
|
|
122
|
+
throw new DevicePolicyError("ERR_PRISM_DEVICE_CHUNK", "conformance: in-bound chunk must accept");
|
|
123
|
+
}
|
|
124
|
+
passed.push("stream-bounds");
|
|
125
|
+
// 6. Redaction: telemetry passes through the host redactor (secrets stripped), passthrough when absent.
|
|
126
|
+
const secret = "super-secret-device-token";
|
|
127
|
+
const redactor = { redact: (value) => redactSecrets(value, [secret]) };
|
|
128
|
+
const redacted = redactDeviceTelemetry(redactor, { note: `leak ${secret}` });
|
|
129
|
+
if (redacted.note.includes(secret)) {
|
|
130
|
+
throw new DevicePolicyError("ERR_PRISM_DEVICE_INPUT", "conformance: telemetry secret must be redacted");
|
|
131
|
+
}
|
|
132
|
+
if (redactDeviceTelemetry(undefined, { note: "ok" }).note !== "ok") {
|
|
133
|
+
throw new DevicePolicyError("ERR_PRISM_DEVICE_INPUT", "conformance: telemetry must pass through without a redactor");
|
|
134
|
+
}
|
|
135
|
+
passed.push("redaction");
|
|
136
|
+
return { passed };
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=devices.js.map
|
|
@@ -13,10 +13,21 @@ export function createEventMultiplexer(options = {}) {
|
|
|
13
13
|
isClosed = true;
|
|
14
14
|
else
|
|
15
15
|
options.signal?.addEventListener("abort", abort, { once: true });
|
|
16
|
+
// With compare set, publish always enqueues and the parked consumer is woken with
|
|
17
|
+
// this token instead of a value, so every event drains through the sorted queue —
|
|
18
|
+
// delivery order follows the comparator even when the consumer is caught up.
|
|
19
|
+
const WAKE = Symbol("wake");
|
|
20
|
+
const wakeWaiter = () => {
|
|
21
|
+
if (!waiter)
|
|
22
|
+
return;
|
|
23
|
+
const resolve = waiter;
|
|
24
|
+
waiter = undefined;
|
|
25
|
+
resolve({ value: WAKE, done: false });
|
|
26
|
+
};
|
|
16
27
|
function publish(event) {
|
|
17
28
|
if (isClosed)
|
|
18
29
|
return;
|
|
19
|
-
if (waiter) {
|
|
30
|
+
if (!options.compare && waiter) {
|
|
20
31
|
const resolve = waiter;
|
|
21
32
|
waiter = undefined;
|
|
22
33
|
resolve({ value: event, done: false });
|
|
@@ -24,12 +35,11 @@ export function createEventMultiplexer(options = {}) {
|
|
|
24
35
|
}
|
|
25
36
|
if (queue.length < maxQueuedEvents) {
|
|
26
37
|
queue.push(event);
|
|
38
|
+
wakeWaiter();
|
|
27
39
|
return;
|
|
28
40
|
}
|
|
29
41
|
droppedEvents += 1;
|
|
30
|
-
const notice = !overflowNotified && options.overflowEvent
|
|
31
|
-
? options.overflowEvent({ droppedEvents, maxQueuedEvents, policy: overflow })
|
|
32
|
-
: undefined;
|
|
42
|
+
const notice = !overflowNotified && options.overflowEvent ? options.overflowEvent({ droppedEvents, maxQueuedEvents, policy: overflow }) : undefined;
|
|
33
43
|
overflowNotified = true;
|
|
34
44
|
if (overflow === "close") {
|
|
35
45
|
queue.length = 0;
|
|
@@ -43,6 +53,7 @@ export function createEventMultiplexer(options = {}) {
|
|
|
43
53
|
if (overflow === "drop_newest") {
|
|
44
54
|
if (notice !== undefined)
|
|
45
55
|
queue[queue.length - 1] = notice;
|
|
56
|
+
wakeWaiter();
|
|
46
57
|
return;
|
|
47
58
|
}
|
|
48
59
|
queue.shift();
|
|
@@ -52,6 +63,7 @@ export function createEventMultiplexer(options = {}) {
|
|
|
52
63
|
queue.shift();
|
|
53
64
|
}
|
|
54
65
|
queue.push(event);
|
|
66
|
+
wakeWaiter();
|
|
55
67
|
}
|
|
56
68
|
function observe(source, map) {
|
|
57
69
|
if (isClosed)
|
|
@@ -117,6 +129,8 @@ export function createEventMultiplexer(options = {}) {
|
|
|
117
129
|
});
|
|
118
130
|
if (next.done)
|
|
119
131
|
return;
|
|
132
|
+
if (next.value === WAKE)
|
|
133
|
+
continue;
|
|
120
134
|
yield next.value;
|
|
121
135
|
}
|
|
122
136
|
}
|
package/dist/extensions.d.ts
CHANGED
|
@@ -4,22 +4,39 @@ import { type MiddlewareRegistry } from "./middleware.js";
|
|
|
4
4
|
import { type PermissionPolicy } from "./security.js";
|
|
5
5
|
export type ExtensionEventHandler = (event: ExtensionEvent) => void | Promise<void>;
|
|
6
6
|
export type ExtensionErrorPolicy = "event" | "throw";
|
|
7
|
+
export interface ExtensionLoadPolicy {
|
|
8
|
+
/** When set, only listed extension names may load. */
|
|
9
|
+
readonly allowList?: readonly string[];
|
|
10
|
+
/**
|
|
11
|
+
* Host signature / attestation check. Return false or throw to deny.
|
|
12
|
+
* Unsigned extensions fail closed when this callback is provided.
|
|
13
|
+
*/
|
|
14
|
+
readonly verifySignature?: (extension: Extension) => boolean | Promise<boolean>;
|
|
15
|
+
}
|
|
7
16
|
export interface ExtensionKernelOptions {
|
|
8
17
|
readonly registries?: ContributionRegistries;
|
|
9
18
|
readonly middleware?: MiddlewareRegistry;
|
|
10
19
|
readonly errorPolicy?: ExtensionErrorPolicy;
|
|
11
20
|
readonly secrets?: readonly (string | undefined)[];
|
|
12
21
|
readonly permission?: PermissionPolicy;
|
|
22
|
+
/** Optional allow-list / signature policy evaluated before `setup`. */
|
|
23
|
+
readonly loadPolicy?: ExtensionLoadPolicy;
|
|
13
24
|
}
|
|
14
25
|
export interface ExtensionEventBus {
|
|
15
26
|
on(type: ExtensionLifecycleEventName | string, handler: ExtensionEventHandler): () => void;
|
|
16
27
|
emit(event: ExtensionEvent): Promise<void>;
|
|
17
28
|
}
|
|
29
|
+
export interface LoadedExtension {
|
|
30
|
+
readonly name: string;
|
|
31
|
+
/** Remove this extension's registry contributions and middleware/event subscriptions.
|
|
32
|
+
* Best-effort and idempotent; side effects outside the registries are NOT unwound. */
|
|
33
|
+
dispose(): void;
|
|
34
|
+
}
|
|
18
35
|
export interface ExtensionKernel {
|
|
19
36
|
readonly registries: ContributionRegistries;
|
|
20
37
|
readonly middleware: MiddlewareRegistry;
|
|
21
38
|
readonly events: ExtensionEventBus;
|
|
22
|
-
load(extensions: readonly Extension[]): Promise<
|
|
39
|
+
load(extensions: readonly Extension[]): Promise<LoadedExtension[]>;
|
|
23
40
|
}
|
|
24
41
|
export declare function createExtensionEventBus(options?: Pick<ExtensionKernelOptions, "errorPolicy" | "secrets">): ExtensionEventBus;
|
|
25
42
|
export declare function createExtensionKernel(options?: ExtensionKernelOptions): ExtensionKernel;
|
package/dist/extensions.js
CHANGED
|
@@ -45,90 +45,163 @@ export function createExtensionKernel(options = {}) {
|
|
|
45
45
|
const middleware = options.middleware ?? createMiddlewareRegistry({ ...options, onError: events.emit });
|
|
46
46
|
const errorPolicy = options.errorPolicy ?? "event";
|
|
47
47
|
const secrets = options.secrets ?? [];
|
|
48
|
-
|
|
48
|
+
// Per-extension tracked API: every registration records an undo so a dispose handle
|
|
49
|
+
// (or a failed setup) can unwind exactly what that extension added.
|
|
50
|
+
const createApi = (track) => ({
|
|
49
51
|
registries,
|
|
50
52
|
middleware,
|
|
51
|
-
on
|
|
53
|
+
on(type, handler) {
|
|
54
|
+
const off = events.on(type, handler);
|
|
55
|
+
track?.(off);
|
|
56
|
+
return off;
|
|
57
|
+
},
|
|
52
58
|
emit: events.emit,
|
|
53
|
-
use
|
|
59
|
+
use(hook, mw) {
|
|
60
|
+
const off = middleware.use(hook, mw);
|
|
61
|
+
track?.(off);
|
|
62
|
+
return off;
|
|
63
|
+
},
|
|
54
64
|
registerProvider(provider) {
|
|
55
65
|
registries.providers.register(provider);
|
|
66
|
+
track?.(() => registries.providers.unregister(provider.id));
|
|
56
67
|
},
|
|
57
68
|
registerModel(model) {
|
|
58
69
|
registries.models.register(model);
|
|
70
|
+
track?.(() => registries.models.unregister(model.provider, model.model));
|
|
59
71
|
},
|
|
60
72
|
registerTool(tool) {
|
|
61
73
|
registries.tools.register(tool.name, tool);
|
|
74
|
+
track?.(() => registries.tools.unregister(tool.name));
|
|
62
75
|
},
|
|
63
76
|
registerContextProvider(provider) {
|
|
64
77
|
registries.contextProviders.register(provider.name, provider);
|
|
78
|
+
track?.(() => registries.contextProviders.unregister(provider.name));
|
|
65
79
|
},
|
|
66
80
|
registerSkill(skill) {
|
|
67
81
|
registries.skills.register(skill.name, skill);
|
|
82
|
+
track?.(() => registries.skills.unregister(skill.name));
|
|
68
83
|
},
|
|
69
84
|
registerCommand(command) {
|
|
70
85
|
registries.commands.register(command.name, command);
|
|
86
|
+
track?.(() => registries.commands.unregister(command.name));
|
|
71
87
|
},
|
|
72
88
|
registerAgent(agent) {
|
|
73
89
|
registries.agents.register(agent.name, agent);
|
|
90
|
+
track?.(() => registries.agents.unregister(agent.name));
|
|
74
91
|
},
|
|
75
92
|
registerInputBuilder(builder) {
|
|
76
93
|
registries.inputBuilders.register(builder.name, builder);
|
|
94
|
+
track?.(() => registries.inputBuilders.unregister(builder.name));
|
|
77
95
|
},
|
|
78
96
|
registerPromptBuilder(builder) {
|
|
79
97
|
registries.promptBuilders.register(builder.name, builder);
|
|
98
|
+
track?.(() => registries.promptBuilders.unregister(builder.name));
|
|
80
99
|
},
|
|
81
100
|
registerCompactionStrategy(strategy) {
|
|
82
101
|
registries.compactionStrategies.register(strategy.name, strategy);
|
|
102
|
+
track?.(() => registries.compactionStrategies.unregister(strategy.name));
|
|
83
103
|
},
|
|
84
104
|
registerRetryPolicy(policy) {
|
|
85
105
|
registries.retryPolicies.register(policy.name, policy);
|
|
106
|
+
track?.(() => registries.retryPolicies.unregister(policy.name));
|
|
86
107
|
},
|
|
87
108
|
registerStoreFactory(factory) {
|
|
88
109
|
registries.storeFactories.register(factory.name, factory);
|
|
110
|
+
track?.(() => registries.storeFactories.unregister(factory.name));
|
|
89
111
|
},
|
|
90
112
|
registerResourceLoader(key, loader) {
|
|
91
113
|
registries.resourceLoaders.register(key, loader);
|
|
114
|
+
track?.(() => registries.resourceLoaders.unregister(key));
|
|
92
115
|
},
|
|
93
116
|
registerSettingsProvider(key, provider) {
|
|
94
117
|
registries.settingsProviders.register(key, provider);
|
|
118
|
+
track?.(() => registries.settingsProviders.unregister(key));
|
|
95
119
|
},
|
|
96
120
|
registerCredentialResolver(key, resolver) {
|
|
97
121
|
registries.credentialResolvers.register(key, resolver);
|
|
122
|
+
track?.(() => registries.credentialResolvers.unregister(key));
|
|
98
123
|
},
|
|
99
124
|
registerProviderPackage(providerPackage) {
|
|
100
125
|
registries.providerPackages.register(providerPackage.name, providerPackage);
|
|
126
|
+
track?.(() => registries.providerPackages.unregister(providerPackage.name));
|
|
101
127
|
},
|
|
102
128
|
registerAuthMethod(method) {
|
|
103
|
-
|
|
129
|
+
const key = authMethodKey(method);
|
|
130
|
+
registries.authMethods.register(key, method);
|
|
131
|
+
track?.(() => registries.authMethods.unregister(key));
|
|
104
132
|
},
|
|
105
133
|
registerProviderRequestPolicy(policy) {
|
|
106
134
|
registries.providerRequestPolicies.register(policy.name, policy);
|
|
135
|
+
track?.(() => registries.providerRequestPolicies.unregister(policy.name));
|
|
107
136
|
},
|
|
108
137
|
registerSystemPromptContribution(contribution) {
|
|
109
|
-
|
|
138
|
+
const key = systemPromptContributionKey(contribution);
|
|
139
|
+
registries.systemPromptContributions.register(key, contribution);
|
|
140
|
+
track?.(() => registries.systemPromptContributions.unregister(key));
|
|
110
141
|
},
|
|
111
142
|
registerInstructionInjector(injector) {
|
|
112
143
|
registries.instructionInjectors.register(injector.name, injector);
|
|
144
|
+
track?.(() => registries.instructionInjectors.unregister(injector.name));
|
|
113
145
|
},
|
|
146
|
+
});
|
|
147
|
+
const unwind = (undo) => {
|
|
148
|
+
for (const fn of undo.reverse()) {
|
|
149
|
+
try {
|
|
150
|
+
fn();
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// best-effort: one stuck undo must not block the rest
|
|
154
|
+
}
|
|
155
|
+
}
|
|
114
156
|
};
|
|
115
157
|
return {
|
|
116
158
|
registries,
|
|
117
159
|
middleware,
|
|
118
160
|
events,
|
|
119
161
|
async load(extensions) {
|
|
162
|
+
const loaded = [];
|
|
120
163
|
for (const extension of extensions) {
|
|
164
|
+
const undo = [];
|
|
121
165
|
try {
|
|
122
166
|
await assertPermission(options.permission, { kind: "extension", action: "setup", target: extension.name });
|
|
123
|
-
await extension
|
|
167
|
+
await assertExtensionLoadPolicy(options.loadPolicy, extension);
|
|
168
|
+
await extension.setup(createApi((fn) => undo.push(fn)));
|
|
124
169
|
}
|
|
125
170
|
catch (error) {
|
|
171
|
+
// A failed setup must not leave partial contributions behind.
|
|
172
|
+
unwind(undo);
|
|
126
173
|
if (errorPolicy === "throw")
|
|
127
174
|
throw error;
|
|
128
175
|
await events.emit(extensionError(error, extension.name, secrets));
|
|
176
|
+
continue;
|
|
129
177
|
}
|
|
178
|
+
let disposed = false;
|
|
179
|
+
loaded.push({
|
|
180
|
+
name: extension.name,
|
|
181
|
+
dispose() {
|
|
182
|
+
if (disposed)
|
|
183
|
+
return;
|
|
184
|
+
disposed = true;
|
|
185
|
+
unwind(undo);
|
|
186
|
+
},
|
|
187
|
+
});
|
|
130
188
|
}
|
|
189
|
+
return loaded;
|
|
131
190
|
},
|
|
132
191
|
};
|
|
133
192
|
}
|
|
193
|
+
async function assertExtensionLoadPolicy(policy, extension) {
|
|
194
|
+
if (!policy)
|
|
195
|
+
return;
|
|
196
|
+
if (policy.allowList && !policy.allowList.includes(extension.name)) {
|
|
197
|
+
throw new Error(`Extension "${extension.name}" is not allow-listed`);
|
|
198
|
+
}
|
|
199
|
+
if (policy.verifySignature) {
|
|
200
|
+
if (!extension.signature)
|
|
201
|
+
throw new Error(`Extension "${extension.name}" is unsigned`);
|
|
202
|
+
const ok = await policy.verifySignature(extension);
|
|
203
|
+
if (!ok)
|
|
204
|
+
throw new Error(`Extension "${extension.name}" failed signature verification`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
134
207
|
//# sourceMappingURL=extensions.js.map
|
package/dist/feedback.js
CHANGED
|
@@ -48,10 +48,14 @@ export async function prepareRunFeedback(input, options) {
|
|
|
48
48
|
const tags = normalizeList(input.tags, maxTags, MAX_FEEDBACK_TAG_LENGTH, "tag");
|
|
49
49
|
const scorerIds = normalizeList(input.scorerIds, maxLinks, MAX_FEEDBACK_ID_LENGTH, "scorer id", true);
|
|
50
50
|
const evaluationIds = normalizeList(input.evaluationIds, maxLinks, MAX_FEEDBACK_ID_LENGTH, "evaluation id", true);
|
|
51
|
-
if (input.rating === undefined &&
|
|
51
|
+
if (input.rating === undefined &&
|
|
52
|
+
input.comment === undefined &&
|
|
53
|
+
tags.length === 0 &&
|
|
54
|
+
scorerIds.length === 0 &&
|
|
55
|
+
evaluationIds.length === 0) {
|
|
52
56
|
throw new RunFeedbackError("feedback requires rating, comment, tag, scorer, or evaluation", "ERR_PRISM_RUN_FEEDBACK_EMPTY");
|
|
53
57
|
}
|
|
54
|
-
const redact = (value) => options.redactor ? options.redactor.redact(value) : value;
|
|
58
|
+
const redact = (value) => (options.redactor ? options.redactor.redact(value) : value);
|
|
55
59
|
const comment = input.comment === undefined ? undefined : redact(input.comment);
|
|
56
60
|
const safeTags = Object.freeze(normalizeList(redact(tags), maxTags, MAX_FEEDBACK_TAG_LENGTH, "tag"));
|
|
57
61
|
const metadata = input.metadata === undefined ? undefined : redact(input.metadata);
|
|
@@ -121,18 +125,16 @@ export function runFeedbackPageLimit(limit, maximum = HARD_FEEDBACK_PAGE_SIZE) {
|
|
|
121
125
|
return pageLimit(limit, maximum);
|
|
122
126
|
}
|
|
123
127
|
function requireOwnership(input) {
|
|
124
|
-
if (!input.tenantId?.trim()
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
+
if (!input.tenantId?.trim() ||
|
|
129
|
+
(input.accountId !== undefined && !input.accountId.trim()) ||
|
|
130
|
+
(input.userId !== undefined && !input.userId.trim()) ||
|
|
131
|
+
(!input.accountId && !input.userId)) {
|
|
128
132
|
throw new RunFeedbackError("tenantId and non-empty accountId or userId are required", "ERR_PRISM_RUN_FEEDBACK_OWNERSHIP");
|
|
129
133
|
}
|
|
130
134
|
return { tenantId: input.tenantId, accountId: input.accountId, userId: input.userId };
|
|
131
135
|
}
|
|
132
136
|
function sameOwnership(expected, actual) {
|
|
133
|
-
return expected.tenantId === actual.tenantId
|
|
134
|
-
&& expected.accountId === actual.accountId
|
|
135
|
-
&& expected.userId === actual.userId;
|
|
137
|
+
return expected.tenantId === actual.tenantId && expected.accountId === actual.accountId && expected.userId === actual.userId;
|
|
136
138
|
}
|
|
137
139
|
function requireId(value, label) {
|
|
138
140
|
if (!value || value.length > MAX_FEEDBACK_ID_LENGTH || !/^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(value)) {
|
|
@@ -214,7 +216,7 @@ function freezeRecord(record) {
|
|
|
214
216
|
});
|
|
215
217
|
}
|
|
216
218
|
function cloneFrozenJsonObject(value) {
|
|
217
|
-
const cloned =
|
|
219
|
+
const cloned = structuredClone(value);
|
|
218
220
|
if (!cloned || typeof cloned !== "object" || Array.isArray(cloned))
|
|
219
221
|
throw new RunFeedbackError("metadata must be a JSON object");
|
|
220
222
|
return deepFreeze(cloned);
|
package/dist/guardrails.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentEvent, GuardrailContext, GuardrailRecord,
|
|
1
|
+
import type { AgentEvent, GuardrailContext, GuardrailRecord, GuardrailStage, Guardrails, GuardrailValue } from "./contracts.js";
|
|
2
2
|
import type { SecretRedactor } from "./redaction.js";
|
|
3
3
|
export declare const MAX_GUARDRAIL_CONCURRENCY = 16;
|
|
4
4
|
export declare class GuardrailError extends Error {
|
package/dist/guardrails.js
CHANGED
|
@@ -5,11 +5,11 @@ export class GuardrailError extends Error {
|
|
|
5
5
|
code;
|
|
6
6
|
record;
|
|
7
7
|
constructor(record) {
|
|
8
|
-
super(record.action === "interrupt"
|
|
8
|
+
super(record.action === "interrupt"
|
|
9
|
+
? `Guardrail interruption is unavailable at stage "${record.stage}"; interrupt suspends only at the input stage of durable runs`
|
|
10
|
+
: "Guardrail blocked run");
|
|
9
11
|
this.name = "GuardrailError";
|
|
10
|
-
this.code = record.action === "interrupt"
|
|
11
|
-
? "ERR_PRISM_GUARDRAIL_INTERRUPT_UNAVAILABLE"
|
|
12
|
-
: "ERR_PRISM_GUARDRAIL_BLOCKED";
|
|
12
|
+
this.code = record.action === "interrupt" ? "ERR_PRISM_GUARDRAIL_INTERRUPT_UNAVAILABLE" : "ERR_PRISM_GUARDRAIL_BLOCKED";
|
|
13
13
|
this.record = record;
|
|
14
14
|
}
|
|
15
15
|
}
|
|
@@ -20,9 +20,7 @@ export async function runGuardrails(options) {
|
|
|
20
20
|
return { records: [] };
|
|
21
21
|
const maxConcurrency = resolveConcurrency(options.guardrails?.maxConcurrency);
|
|
22
22
|
const controller = new AbortController();
|
|
23
|
-
const signal = options.context.signal
|
|
24
|
-
? AbortSignal.any([options.context.signal, controller.signal])
|
|
25
|
-
: controller.signal;
|
|
23
|
+
const signal = options.context.signal ? AbortSignal.any([options.context.signal, controller.signal]) : controller.signal;
|
|
26
24
|
const records = new Array(guards.length);
|
|
27
25
|
let next = 0;
|
|
28
26
|
let stopped = false;
|
|
@@ -66,14 +64,23 @@ function stageGuards(guardrails, stage) {
|
|
|
66
64
|
if (!guardrails)
|
|
67
65
|
return [];
|
|
68
66
|
switch (stage) {
|
|
69
|
-
case "input":
|
|
70
|
-
|
|
71
|
-
case "
|
|
72
|
-
|
|
67
|
+
case "input":
|
|
68
|
+
return (guardrails.input ?? []);
|
|
69
|
+
case "output":
|
|
70
|
+
return (guardrails.output ?? []);
|
|
71
|
+
case "tool_input":
|
|
72
|
+
return (guardrails.toolInput ?? []);
|
|
73
|
+
case "tool_output":
|
|
74
|
+
return (guardrails.toolOutput ?? []);
|
|
73
75
|
}
|
|
74
76
|
}
|
|
75
77
|
async function evaluate(guardrail, options, signal) {
|
|
76
|
-
if (!guardrail ||
|
|
78
|
+
if (!guardrail ||
|
|
79
|
+
typeof guardrail.name !== "string" ||
|
|
80
|
+
!guardrail.name ||
|
|
81
|
+
guardrail.name.length > 128 ||
|
|
82
|
+
guardrail.stage !== options.stage ||
|
|
83
|
+
typeof guardrail.evaluate !== "function") {
|
|
77
84
|
return record(guardrail?.name ?? "invalid", options.stage, "tripwire", "guardrail_invalid", undefined, options.redactor);
|
|
78
85
|
}
|
|
79
86
|
try {
|
|
@@ -83,8 +90,12 @@ async function evaluate(guardrail, options, signal) {
|
|
|
83
90
|
}
|
|
84
91
|
return record(guardrail.name, options.stage, decision.action, decision.reason, decision.metadata, options.redactor);
|
|
85
92
|
}
|
|
86
|
-
catch {
|
|
87
|
-
|
|
93
|
+
catch (error) {
|
|
94
|
+
// Keep the cause diagnosable without leaking internals: message only, bounded and
|
|
95
|
+
// passed through the same redact+bound metadata path as guardrail-provided metadata.
|
|
96
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
97
|
+
const redacted = options.redactor?.redact(message) ?? message;
|
|
98
|
+
return record(guardrail.name, options.stage, "tripwire", "guardrail_failed", { error: boundText(redacted, MAX_REASON_BYTES) }, options.redactor);
|
|
88
99
|
}
|
|
89
100
|
}
|
|
90
101
|
function resolveConcurrency(value) {
|
|
@@ -115,9 +126,7 @@ function boundedMetadata(value, redactor) {
|
|
|
115
126
|
if (!json || byteLength(json) > MAX_METADATA_BYTES)
|
|
116
127
|
return { truncated: true };
|
|
117
128
|
const parsed = JSON.parse(json);
|
|
118
|
-
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
119
|
-
? parsed
|
|
120
|
-
: undefined;
|
|
129
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : undefined;
|
|
121
130
|
}
|
|
122
131
|
catch {
|
|
123
132
|
return { invalid: true };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { OwnershipScope } from "./contracts.js";
|
|
2
|
+
/** Host-authenticated actor (user, service, or agent principal). */
|
|
3
|
+
export interface Principal {
|
|
4
|
+
readonly kind: "user" | "service" | "agent" | string;
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly displayName?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Host-verified identity context. Projects onto {@link OwnershipScope}.
|
|
10
|
+
* `verified: true` marks host-verifier output; trust boundaries must obtain this
|
|
11
|
+
* via {@link IdentityVerifier}, never by accepting caller-asserted claims alone.
|
|
12
|
+
*/
|
|
13
|
+
export interface AgentIdentity {
|
|
14
|
+
readonly tenantId: string;
|
|
15
|
+
readonly accountId?: string;
|
|
16
|
+
readonly userId?: string;
|
|
17
|
+
/** Acting principal for this run/tool/delegation. */
|
|
18
|
+
readonly principal: Principal;
|
|
19
|
+
/** Human sponsor accountable for the agent lifecycle (Entra-style). */
|
|
20
|
+
readonly sponsor?: Principal;
|
|
21
|
+
/** Resource owner when distinct from sponsor. */
|
|
22
|
+
readonly owner?: Principal;
|
|
23
|
+
/** Parent principal when this identity is a narrowed delegation. */
|
|
24
|
+
readonly delegatedFrom?: Principal;
|
|
25
|
+
readonly scopes: readonly string[];
|
|
26
|
+
/** Late-bound credential resolver keys — never secret material. */
|
|
27
|
+
readonly credentialRefs?: readonly string[];
|
|
28
|
+
readonly issuedAt: string;
|
|
29
|
+
readonly expiresAt?: string;
|
|
30
|
+
readonly revokedAt?: string;
|
|
31
|
+
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
32
|
+
readonly verified: true;
|
|
33
|
+
}
|
|
34
|
+
export interface IdentityVerifier {
|
|
35
|
+
verify(input: unknown): AgentIdentity | Promise<AgentIdentity>;
|
|
36
|
+
}
|
|
37
|
+
export interface AssertIdentityActiveOptions {
|
|
38
|
+
readonly now?: number;
|
|
39
|
+
/** When set, identity.tenantId must equal this value. */
|
|
40
|
+
readonly expectedTenantId?: string;
|
|
41
|
+
readonly limits?: IdentityLimits;
|
|
42
|
+
}
|
|
43
|
+
export interface NarrowIdentityOptions {
|
|
44
|
+
readonly scopes: readonly string[];
|
|
45
|
+
readonly principal?: Principal;
|
|
46
|
+
readonly credentialRefs?: readonly string[];
|
|
47
|
+
readonly expiresAt?: string;
|
|
48
|
+
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
49
|
+
readonly limits?: IdentityLimits;
|
|
50
|
+
}
|
|
51
|
+
export interface IdentityLimits {
|
|
52
|
+
readonly maxScopes?: number;
|
|
53
|
+
readonly maxScopeBytes?: number;
|
|
54
|
+
readonly maxMetadataBytes?: number;
|
|
55
|
+
readonly maxCredentialRefBytes?: number;
|
|
56
|
+
readonly maxPrincipalIdBytes?: number;
|
|
57
|
+
}
|
|
58
|
+
export interface ResolvedIdentityLimits {
|
|
59
|
+
readonly maxScopes: number;
|
|
60
|
+
readonly maxScopeBytes: number;
|
|
61
|
+
readonly maxMetadataBytes: number;
|
|
62
|
+
readonly maxCredentialRefBytes: number;
|
|
63
|
+
readonly maxPrincipalIdBytes: number;
|
|
64
|
+
}
|
|
65
|
+
export declare const DEFAULT_IDENTITY_LIMITS: ResolvedIdentityLimits;
|
|
66
|
+
export declare const HARD_IDENTITY_LIMITS: ResolvedIdentityLimits;
|
|
67
|
+
export declare class IdentityError extends Error {
|
|
68
|
+
readonly reason: string;
|
|
69
|
+
readonly code = "ERR_PRISM_IDENTITY";
|
|
70
|
+
constructor(message: string, reason: string);
|
|
71
|
+
}
|
|
72
|
+
export declare function resolveIdentityLimits(input?: IdentityLimits): ResolvedIdentityLimits;
|
|
73
|
+
/** Project verified identity onto ownership scope fields used by persistence/server. */
|
|
74
|
+
export declare function ownershipFromIdentity(identity: AgentIdentity): OwnershipScope;
|
|
75
|
+
/**
|
|
76
|
+
* Fail closed on shape/limits/expiry/revocation/tenant mismatch.
|
|
77
|
+
* Sync and network-free — host {@link IdentityVerifier} owns remote checks.
|
|
78
|
+
*/
|
|
79
|
+
export declare function assertIdentityActive(identity: AgentIdentity, options?: AssertIdentityActiveOptions): void;
|
|
80
|
+
/** Delegation may only narrow scopes; tenant and ownership ids stay immutable. */
|
|
81
|
+
export declare function narrowIdentity(parent: AgentIdentity, options: NarrowIdentityOptions): AgentIdentity;
|
|
82
|
+
/** Refuse ownership that widens or conflicts with a verified identity. */
|
|
83
|
+
export declare function assertIdentityMatchesOwnership(identity: AgentIdentity, ownership?: OwnershipScope): void;
|
|
84
|
+
/**
|
|
85
|
+
* Propagation check: child must share tenant, not widen scopes, and remain active.
|
|
86
|
+
* Used by supervisor/A2A/MCP/server when forwarding identity across a boundary.
|
|
87
|
+
*/
|
|
88
|
+
export declare function assertIdentityPropagation(parent: AgentIdentity, child: AgentIdentity, options?: AssertIdentityActiveOptions): void;
|
|
89
|
+
/** Redacted identity refs safe for telemetry/ledger metadata (no secrets, no raw JWT). */
|
|
90
|
+
export declare function identityTelemetryAttributes(identity: AgentIdentity): Readonly<Record<string, string>>;
|
|
91
|
+
/** Resolve run identity: explicit option wins; otherwise agent default. Assert when present. */
|
|
92
|
+
export declare function resolveRunIdentity(runIdentity: AgentIdentity | undefined, agentIdentity: AgentIdentity | undefined, ownership: OwnershipScope | undefined, options?: AssertIdentityActiveOptions): AgentIdentity | undefined;
|