@arnilo/prism 0.0.12 → 0.0.14

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.
Files changed (75) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +8 -2
  3. package/dist/agents.js +21 -2
  4. package/dist/artifacts.d.ts +78 -0
  5. package/dist/artifacts.js +24 -0
  6. package/dist/contracts.d.ts +35 -1
  7. package/dist/contracts.js +8 -0
  8. package/dist/conversations.d.ts +50 -0
  9. package/dist/conversations.js +97 -0
  10. package/dist/credentials.d.ts +14 -0
  11. package/dist/credentials.js +9 -0
  12. package/dist/devices.d.ts +94 -0
  13. package/dist/devices.js +138 -0
  14. package/dist/extensions.d.ts +11 -0
  15. package/dist/extensions.js +15 -0
  16. package/dist/identity.d.ts +92 -0
  17. package/dist/identity.js +257 -0
  18. package/dist/index.d.ts +15 -5
  19. package/dist/index.js +8 -3
  20. package/dist/persistence-lifecycle.d.ts +103 -0
  21. package/dist/persistence-lifecycle.js +204 -0
  22. package/dist/providers/openai-compatible.d.ts +5 -1
  23. package/dist/providers/openai-compatible.js +15 -6
  24. package/dist/providers/openai-primitives.js +5 -2
  25. package/dist/secure-agent.js +7 -1
  26. package/dist/testing/persistence-schema.d.ts +2 -2
  27. package/dist/testing/persistence-schema.js +35 -2
  28. package/dist/tools.d.ts +2 -0
  29. package/dist/tools.js +6 -0
  30. package/docs/a2a.md +2 -0
  31. package/docs/ag-ui.md +5 -0
  32. package/docs/agent-identity.md +111 -0
  33. package/docs/browser-automation.md +3 -0
  34. package/docs/conversations.md +135 -0
  35. package/docs/credential-storage.md +31 -1
  36. package/docs/credentials-and-redaction.md +2 -0
  37. package/docs/database-persistence.md +22 -7
  38. package/docs/device-adapters.md +97 -0
  39. package/docs/extensions.md +1 -0
  40. package/docs/guardrails.md +3 -0
  41. package/docs/host-security.md +9 -3
  42. package/docs/index.md +26 -13
  43. package/docs/mcp-tools.md +2 -0
  44. package/docs/migration.md +48 -0
  45. package/docs/model-routing.md +102 -0
  46. package/docs/observability.md +2 -0
  47. package/docs/performance.md +21 -0
  48. package/docs/policy-and-audit.md +128 -0
  49. package/docs/postgres-persistence.md +1 -1
  50. package/docs/provider-caching.md +4 -0
  51. package/docs/provider-packages.md +12 -2
  52. package/docs/provider-request-policies.md +2 -0
  53. package/docs/providers/alibaba.md +179 -0
  54. package/docs/providers/azure.md +74 -0
  55. package/docs/providers/bedrock.md +72 -0
  56. package/docs/providers/google.md +1 -0
  57. package/docs/providers/ollama.md +166 -0
  58. package/docs/providers/openai-compatible.md +3 -1
  59. package/docs/providers/openrouter.md +2 -0
  60. package/docs/providers/vertex.md +71 -0
  61. package/docs/public-contracts.md +3 -1
  62. package/docs/release-and-install.md +149 -7
  63. package/docs/review-coverage-2026-07-23-phase-8.md +245 -0
  64. package/docs/review-coverage-2026-07-25-phase-9.md +256 -0
  65. package/docs/runs-and-usage.md +2 -0
  66. package/docs/server.md +37 -4
  67. package/docs/sqlite-persistence.md +1 -1
  68. package/docs/supervisors.md +2 -0
  69. package/docs/work-artifacts-and-review.md +100 -0
  70. package/docs/work-connectors.md +32 -0
  71. package/docs/work-tools.md +117 -0
  72. package/docs/workflows.md +4 -0
  73. package/docs/working-and-semantic-memory.md +20 -5
  74. package/package.json +4 -1
  75. package/templates/init/providers.json +22 -0
@@ -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
@@ -4,12 +4,23 @@ 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;
@@ -120,6 +120,7 @@ export function createExtensionKernel(options = {}) {
120
120
  for (const extension of extensions) {
121
121
  try {
122
122
  await assertPermission(options.permission, { kind: "extension", action: "setup", target: extension.name });
123
+ await assertExtensionLoadPolicy(options.loadPolicy, extension);
123
124
  await extension.setup(api);
124
125
  }
125
126
  catch (error) {
@@ -131,4 +132,18 @@ export function createExtensionKernel(options = {}) {
131
132
  },
132
133
  };
133
134
  }
135
+ async function assertExtensionLoadPolicy(policy, extension) {
136
+ if (!policy)
137
+ return;
138
+ if (policy.allowList && !policy.allowList.includes(extension.name)) {
139
+ throw new Error(`Extension "${extension.name}" is not allow-listed`);
140
+ }
141
+ if (policy.verifySignature) {
142
+ if (!extension.signature)
143
+ throw new Error(`Extension "${extension.name}" is unsigned`);
144
+ const ok = await policy.verifySignature(extension);
145
+ if (!ok)
146
+ throw new Error(`Extension "${extension.name}" failed signature verification`);
147
+ }
148
+ }
134
149
  //# sourceMappingURL=extensions.js.map
@@ -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;
@@ -0,0 +1,257 @@
1
+ export const DEFAULT_IDENTITY_LIMITS = {
2
+ maxScopes: 64,
3
+ maxScopeBytes: 128,
4
+ maxMetadataBytes: 4 * 1024,
5
+ maxCredentialRefBytes: 256,
6
+ maxPrincipalIdBytes: 256,
7
+ };
8
+ export const HARD_IDENTITY_LIMITS = {
9
+ maxScopes: 256,
10
+ maxScopeBytes: 512,
11
+ maxMetadataBytes: 16 * 1024,
12
+ maxCredentialRefBytes: 2 * 1024,
13
+ maxPrincipalIdBytes: 2 * 1024,
14
+ };
15
+ export class IdentityError extends Error {
16
+ reason;
17
+ code = "ERR_PRISM_IDENTITY";
18
+ constructor(message, reason) {
19
+ super(message);
20
+ this.reason = reason;
21
+ this.name = "IdentityError";
22
+ }
23
+ }
24
+ export function resolveIdentityLimits(input = {}) {
25
+ return {
26
+ maxScopes: bound("maxScopes", input.maxScopes, DEFAULT_IDENTITY_LIMITS.maxScopes, HARD_IDENTITY_LIMITS.maxScopes),
27
+ maxScopeBytes: bound("maxScopeBytes", input.maxScopeBytes, DEFAULT_IDENTITY_LIMITS.maxScopeBytes, HARD_IDENTITY_LIMITS.maxScopeBytes),
28
+ maxMetadataBytes: bound("maxMetadataBytes", input.maxMetadataBytes, DEFAULT_IDENTITY_LIMITS.maxMetadataBytes, HARD_IDENTITY_LIMITS.maxMetadataBytes),
29
+ maxCredentialRefBytes: bound("maxCredentialRefBytes", input.maxCredentialRefBytes, DEFAULT_IDENTITY_LIMITS.maxCredentialRefBytes, HARD_IDENTITY_LIMITS.maxCredentialRefBytes),
30
+ maxPrincipalIdBytes: bound("maxPrincipalIdBytes", input.maxPrincipalIdBytes, DEFAULT_IDENTITY_LIMITS.maxPrincipalIdBytes, HARD_IDENTITY_LIMITS.maxPrincipalIdBytes),
31
+ };
32
+ }
33
+ /** Project verified identity onto ownership scope fields used by persistence/server. */
34
+ export function ownershipFromIdentity(identity) {
35
+ return {
36
+ tenantId: identity.tenantId,
37
+ ...(identity.accountId !== undefined ? { accountId: identity.accountId } : {}),
38
+ ...(identity.userId !== undefined ? { userId: identity.userId } : {}),
39
+ };
40
+ }
41
+ /**
42
+ * Fail closed on shape/limits/expiry/revocation/tenant mismatch.
43
+ * Sync and network-free — host {@link IdentityVerifier} owns remote checks.
44
+ */
45
+ export function assertIdentityActive(identity, options = {}) {
46
+ const limits = resolveIdentityLimits(options.limits);
47
+ assertIdentityShape(identity, limits);
48
+ if (identity.verified !== true)
49
+ throw new IdentityError("Identity is not host-verified", "unverified");
50
+ if (options.expectedTenantId !== undefined && identity.tenantId !== options.expectedTenantId) {
51
+ throw new IdentityError("Identity tenant mismatch", "wrong_tenant");
52
+ }
53
+ const now = options.now ?? Date.now();
54
+ if (identity.revokedAt !== undefined) {
55
+ const revokedAt = Date.parse(identity.revokedAt);
56
+ if (!Number.isFinite(revokedAt))
57
+ throw new IdentityError("Identity revokedAt is invalid", "invalid_revoked_at");
58
+ if (revokedAt <= now)
59
+ throw new IdentityError("Identity has been revoked", "revoked");
60
+ }
61
+ if (identity.expiresAt !== undefined) {
62
+ const expiresAt = Date.parse(identity.expiresAt);
63
+ if (!Number.isFinite(expiresAt))
64
+ throw new IdentityError("Identity expiresAt is invalid", "invalid_expires_at");
65
+ if (expiresAt <= now)
66
+ throw new IdentityError("Identity has expired", "expired");
67
+ }
68
+ const issuedAt = Date.parse(identity.issuedAt);
69
+ if (!Number.isFinite(issuedAt))
70
+ throw new IdentityError("Identity issuedAt is invalid", "invalid_issued_at");
71
+ if (issuedAt > now + 60_000)
72
+ throw new IdentityError("Identity issuedAt is in the future", "invalid_issued_at");
73
+ }
74
+ /** Delegation may only narrow scopes; tenant and ownership ids stay immutable. */
75
+ export function narrowIdentity(parent, options) {
76
+ assertIdentityActive(parent, { limits: options.limits });
77
+ const limits = resolveIdentityLimits(options.limits);
78
+ if (options.scopes.length === 0)
79
+ throw new IdentityError("Delegated scopes must be non-empty", "empty_scopes");
80
+ const parentScopes = new Set(parent.scopes);
81
+ for (const scope of options.scopes) {
82
+ if (!parentScopes.has(scope))
83
+ throw new IdentityError(`Delegation widens scope: ${scope}`, "scope_widen");
84
+ }
85
+ if (options.expiresAt !== undefined) {
86
+ const childExp = Date.parse(options.expiresAt);
87
+ if (!Number.isFinite(childExp))
88
+ throw new IdentityError("Delegated expiresAt is invalid", "invalid_expires_at");
89
+ if (parent.expiresAt !== undefined) {
90
+ const parentExp = Date.parse(parent.expiresAt);
91
+ if (Number.isFinite(parentExp) && childExp > parentExp) {
92
+ throw new IdentityError("Delegation cannot extend expiry", "expiry_widen");
93
+ }
94
+ }
95
+ }
96
+ const child = {
97
+ tenantId: parent.tenantId,
98
+ ...(parent.accountId !== undefined ? { accountId: parent.accountId } : {}),
99
+ ...(parent.userId !== undefined ? { userId: parent.userId } : {}),
100
+ principal: options.principal ?? parent.principal,
101
+ ...(parent.sponsor !== undefined ? { sponsor: parent.sponsor } : {}),
102
+ ...(parent.owner !== undefined ? { owner: parent.owner } : {}),
103
+ delegatedFrom: parent.principal,
104
+ scopes: Object.freeze([...options.scopes]),
105
+ credentialRefs: Object.freeze([...(options.credentialRefs ?? parent.credentialRefs ?? [])]),
106
+ issuedAt: parent.issuedAt,
107
+ ...(options.expiresAt !== undefined ? { expiresAt: options.expiresAt } : parent.expiresAt !== undefined ? { expiresAt: parent.expiresAt } : {}),
108
+ ...(parent.revokedAt !== undefined ? { revokedAt: parent.revokedAt } : {}),
109
+ ...(options.metadata !== undefined ? { metadata: options.metadata } : parent.metadata !== undefined ? { metadata: parent.metadata } : {}),
110
+ verified: true,
111
+ };
112
+ assertIdentityActive(child, { limits: options.limits });
113
+ assertPrincipalBounds(child.principal, limits);
114
+ return child;
115
+ }
116
+ /** Refuse ownership that widens or conflicts with a verified identity. */
117
+ export function assertIdentityMatchesOwnership(identity, ownership) {
118
+ assertIdentityActive(identity);
119
+ if (!ownership)
120
+ return;
121
+ if (ownership.tenantId !== undefined && ownership.tenantId !== identity.tenantId) {
122
+ throw new IdentityError("Ownership tenant conflicts with identity", "ownership_tenant");
123
+ }
124
+ if (ownership.accountId !== undefined) {
125
+ if (identity.accountId === undefined || ownership.accountId !== identity.accountId) {
126
+ throw new IdentityError("Ownership account conflicts with identity", "ownership_account");
127
+ }
128
+ }
129
+ if (ownership.userId !== undefined) {
130
+ if (identity.userId === undefined || ownership.userId !== identity.userId) {
131
+ throw new IdentityError("Ownership user conflicts with identity", "ownership_user");
132
+ }
133
+ }
134
+ }
135
+ /**
136
+ * Propagation check: child must share tenant, not widen scopes, and remain active.
137
+ * Used by supervisor/A2A/MCP/server when forwarding identity across a boundary.
138
+ */
139
+ export function assertIdentityPropagation(parent, child, options = {}) {
140
+ assertIdentityActive(parent, options);
141
+ assertIdentityActive(child, options);
142
+ if (child.tenantId !== parent.tenantId)
143
+ throw new IdentityError("Propagated identity changed tenant", "tenant_widen");
144
+ if (parent.accountId !== undefined && child.accountId !== parent.accountId) {
145
+ throw new IdentityError("Propagated identity changed account", "account_widen");
146
+ }
147
+ if (parent.userId !== undefined && child.userId !== parent.userId) {
148
+ throw new IdentityError("Propagated identity changed user", "user_widen");
149
+ }
150
+ const parentScopes = new Set(parent.scopes);
151
+ for (const scope of child.scopes) {
152
+ if (!parentScopes.has(scope))
153
+ throw new IdentityError(`Propagated identity widens scope: ${scope}`, "scope_widen");
154
+ }
155
+ }
156
+ /** Redacted identity refs safe for telemetry/ledger metadata (no secrets, no raw JWT). */
157
+ export function identityTelemetryAttributes(identity) {
158
+ assertIdentityActive(identity);
159
+ return {
160
+ "prism.identity.tenant_id": identity.tenantId,
161
+ "prism.identity.principal_kind": String(identity.principal.kind),
162
+ "prism.identity.principal_id": identity.principal.id,
163
+ "prism.identity.scope_count": String(identity.scopes.length),
164
+ ...(identity.accountId !== undefined ? { "prism.identity.account_id": identity.accountId } : {}),
165
+ ...(identity.userId !== undefined ? { "prism.identity.user_id": identity.userId } : {}),
166
+ ...(identity.sponsor !== undefined ? { "prism.identity.sponsor_id": identity.sponsor.id } : {}),
167
+ ...(identity.credentialRefs?.length ? { "prism.identity.credential_ref_count": String(identity.credentialRefs.length) } : {}),
168
+ };
169
+ }
170
+ /** Resolve run identity: explicit option wins; otherwise agent default. Assert when present. */
171
+ export function resolveRunIdentity(runIdentity, agentIdentity, ownership, options = {}) {
172
+ const identity = runIdentity ?? agentIdentity;
173
+ if (!identity)
174
+ return undefined;
175
+ assertIdentityActive(identity, options);
176
+ assertIdentityMatchesOwnership(identity, ownership ?? ownershipFromIdentity(identity));
177
+ return identity;
178
+ }
179
+ function assertIdentityShape(identity, limits) {
180
+ if (typeof identity.tenantId !== "string" || identity.tenantId.length === 0) {
181
+ throw new IdentityError("Identity tenantId is required", "missing_tenant");
182
+ }
183
+ if (byteLength(identity.tenantId) > limits.maxPrincipalIdBytes) {
184
+ throw new IdentityError("Identity tenantId exceeds byte limit", "tenant_too_large");
185
+ }
186
+ assertPrincipalBounds(identity.principal, limits);
187
+ if (identity.sponsor)
188
+ assertPrincipalBounds(identity.sponsor, limits);
189
+ if (identity.owner)
190
+ assertPrincipalBounds(identity.owner, limits);
191
+ if (identity.delegatedFrom)
192
+ assertPrincipalBounds(identity.delegatedFrom, limits);
193
+ if (!Array.isArray(identity.scopes) || identity.scopes.length === 0) {
194
+ throw new IdentityError("Identity scopes must be a non-empty array", "empty_scopes");
195
+ }
196
+ if (identity.scopes.length > limits.maxScopes) {
197
+ throw new IdentityError(`Identity exceeds maxScopes (${limits.maxScopes})`, "too_many_scopes");
198
+ }
199
+ for (const scope of identity.scopes) {
200
+ if (typeof scope !== "string" || scope.length === 0)
201
+ throw new IdentityError("Identity scope is invalid", "invalid_scope");
202
+ if (byteLength(scope) > limits.maxScopeBytes)
203
+ throw new IdentityError("Identity scope exceeds byte limit", "scope_too_large");
204
+ }
205
+ if (identity.credentialRefs) {
206
+ for (const ref of identity.credentialRefs) {
207
+ if (typeof ref !== "string" || ref.length === 0)
208
+ throw new IdentityError("Credential ref is invalid", "invalid_credential_ref");
209
+ if (byteLength(ref) > limits.maxCredentialRefBytes) {
210
+ throw new IdentityError("Credential ref exceeds byte limit", "credential_ref_too_large");
211
+ }
212
+ }
213
+ }
214
+ if (identity.metadata !== undefined) {
215
+ let encoded;
216
+ try {
217
+ encoded = JSON.stringify(identity.metadata);
218
+ }
219
+ catch {
220
+ throw new IdentityError("Identity metadata is not JSON-serializable", "invalid_metadata");
221
+ }
222
+ if (byteLength(encoded) > limits.maxMetadataBytes) {
223
+ throw new IdentityError("Identity metadata exceeds byte limit", "metadata_too_large");
224
+ }
225
+ }
226
+ if (identity.accountId !== undefined && byteLength(identity.accountId) > limits.maxPrincipalIdBytes) {
227
+ throw new IdentityError("Identity accountId exceeds byte limit", "account_too_large");
228
+ }
229
+ if (identity.userId !== undefined && byteLength(identity.userId) > limits.maxPrincipalIdBytes) {
230
+ throw new IdentityError("Identity userId exceeds byte limit", "user_too_large");
231
+ }
232
+ }
233
+ function assertPrincipalBounds(principal, limits) {
234
+ if (typeof principal.id !== "string" || principal.id.length === 0) {
235
+ throw new IdentityError("Principal id is required", "missing_principal");
236
+ }
237
+ if (byteLength(principal.id) > limits.maxPrincipalIdBytes) {
238
+ throw new IdentityError("Principal id exceeds byte limit", "principal_too_large");
239
+ }
240
+ if (typeof principal.kind !== "string" || principal.kind.length === 0 || byteLength(principal.kind) > 64) {
241
+ throw new IdentityError("Principal kind is invalid", "invalid_principal_kind");
242
+ }
243
+ if (principal.displayName !== undefined && byteLength(principal.displayName) > limits.maxPrincipalIdBytes) {
244
+ throw new IdentityError("Principal displayName exceeds byte limit", "display_name_too_large");
245
+ }
246
+ }
247
+ function bound(name, value, fallback, hard) {
248
+ const resolved = value ?? fallback;
249
+ if (!Number.isSafeInteger(resolved) || resolved < 1 || resolved > hard) {
250
+ throw new RangeError(`${name} must be a positive safe integer no greater than ${hard}`);
251
+ }
252
+ return resolved;
253
+ }
254
+ function byteLength(value) {
255
+ return Buffer.byteLength(value, "utf8");
256
+ }
257
+ //# sourceMappingURL=identity.js.map
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export type * from "./contracts.js";
2
2
  export type { RunLimitCounters, RunLimitName, SecureAgentOptions } from "./contracts.js";
3
- export { isSessionEntryKind, SESSION_APPEND_CONFLICT_CODE, SESSION_ENTRY_KINDS, SESSION_ENTRY_SCHEMA_VERSION, SessionAppendConflictError, isSessionAppendConflict, AgentRunError, AgentRunStateError, SESSION_SEARCH_WORKSPACE_METADATA_KEY, SESSION_SEARCH_UNSUPPORTED_CODE, SessionSearchUnsupportedError, isSessionSearchUnsupported, DEFAULT_SESSION_SEARCH_LIMIT, HARD_MAX_SESSION_SEARCH_LIMIT, DEFAULT_MAX_SESSION_SEARCH_QUERY_BYTES, HARD_MAX_SESSION_SEARCH_QUERY_BYTES, DEFAULT_MAX_SESSION_SEARCH_SNIPPET_BYTES, HARD_MAX_SESSION_SEARCH_SNIPPET_BYTES, DEFAULT_MAX_SESSION_SEARCH_CURSOR_BYTES, HARD_MAX_SESSION_SEARCH_CURSOR_BYTES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_SESSIONS, HARD_MAX_SESSION_SEARCH_LINEAR_SESSIONS, DEFAULT_MAX_SESSION_SEARCH_LINEAR_ENTRIES, HARD_MAX_SESSION_SEARCH_LINEAR_ENTRIES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_BYTES, HARD_MAX_SESSION_SEARCH_LINEAR_BYTES, DEFAULT_MAX_SESSION_SEARCH_FTS_CANDIDATES, HARD_MAX_SESSION_SEARCH_FTS_CANDIDATES, resolveSessionSearchQuery, DEFAULT_MAX_PENDING_STEERS, HARD_MAX_PENDING_STEERS, DEFAULT_MAX_PENDING_STEER_BYTES, HARD_MAX_PENDING_STEER_BYTES } from "./contracts.js";
3
+ export { isSessionEntryKind, SESSION_APPEND_CONFLICT_CODE, SESSION_ENTRY_KINDS, SESSION_ENTRY_SCHEMA_VERSION, SessionAppendConflictError, isSessionAppendConflict, AgentRunError, AgentRunStateError, assertSessionMetadataKey, SESSION_SEARCH_WORKSPACE_METADATA_KEY, SESSION_SEARCH_UNSUPPORTED_CODE, SessionSearchUnsupportedError, isSessionSearchUnsupported, DEFAULT_SESSION_SEARCH_LIMIT, HARD_MAX_SESSION_SEARCH_LIMIT, DEFAULT_MAX_SESSION_SEARCH_QUERY_BYTES, HARD_MAX_SESSION_SEARCH_QUERY_BYTES, DEFAULT_MAX_SESSION_SEARCH_SNIPPET_BYTES, HARD_MAX_SESSION_SEARCH_SNIPPET_BYTES, DEFAULT_MAX_SESSION_SEARCH_CURSOR_BYTES, HARD_MAX_SESSION_SEARCH_CURSOR_BYTES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_SESSIONS, HARD_MAX_SESSION_SEARCH_LINEAR_SESSIONS, DEFAULT_MAX_SESSION_SEARCH_LINEAR_ENTRIES, HARD_MAX_SESSION_SEARCH_LINEAR_ENTRIES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_BYTES, HARD_MAX_SESSION_SEARCH_LINEAR_BYTES, DEFAULT_MAX_SESSION_SEARCH_FTS_CANDIDATES, HARD_MAX_SESSION_SEARCH_FTS_CANDIDATES, resolveSessionSearchQuery, DEFAULT_MAX_PENDING_STEERS, HARD_MAX_PENDING_STEERS, DEFAULT_MAX_PENDING_STEER_BYTES, HARD_MAX_PENDING_STEER_BYTES } from "./contracts.js";
4
4
  export { createAgent, createAgentSession, resumeAgentRun, resumeAgentRunStream } from "./agents.js";
5
5
  export { createBatchedRunLedger, isFlushableRunLedger, DEFAULT_LEDGER_BATCH_ENTRIES, HARD_LEDGER_BATCH_ENTRIES, DEFAULT_LEDGER_BATCH_BYTES, HARD_LEDGER_BATCH_BYTES, DEFAULT_LEDGER_BATCH_DELAY_MS, HARD_LEDGER_BATCH_DELAY_MS, } from "./run-ledger.js";
6
6
  export type { BatchedRunLedgerOptions } from "./run-ledger.js";
@@ -14,6 +14,8 @@ export { createAgentRunLifecycle } from "./agent-run-lifecycle.js";
14
14
  export type { AgentRunLifecycle, AgentRunLifecycleAgent, AgentRunLifecycleOptions, AgentRunLifecycleRequest, AgentRunLifecycleStreamRequest } from "./agent-run-lifecycle.js";
15
15
  export type { MemoryCheckpointStoreOptions } from "./checkpoints.js";
16
16
  export { LEASE_CONFLICT_CODE, LeaseConflictError, createMemoryLeaseStore } from "./leases.js";
17
+ export { createMemoryPersistenceLifecycle, isResourceHeld, PersistenceLifecycleError, DEFAULT_LIFECYCLE_PAGE_SIZE, HARD_LIFECYCLE_PAGE_SIZE, DEFAULT_MAX_HOLD_REASON_BYTES, HARD_MAX_HOLD_REASON_BYTES, } from "./persistence-lifecycle.js";
18
+ export type { PersistenceResourceKind, LegalHoldRecord, PutLegalHoldInput, ReleaseLegalHoldInput, LegalHoldQuery, ApplyRetentionInput, ApplyRetentionResult, LegalHoldExportItem, ExportUnderHoldInput, TenantQuota, SetTenantQuotaInput, ConsumeTenantQuotaInput, PersistenceLifecycleStore, } from "./persistence-lifecycle.js";
17
19
  export { createEventMultiplexer } from "./event-multiplexer.js";
18
20
  export type { EventMultiplexer, EventMultiplexerOptions, EventOverflowInfo, EventOverflowPolicy } from "./event-multiplexer.js";
19
21
  export { applyCacheControl, cacheHitRate, cacheSavings, cacheUsageReport, mapCacheRetention, sanitizeCacheKey } from "./cache-helpers.js";
@@ -28,10 +30,10 @@ export { createDefaultRetryPolicy, isTransientErrorInfo, waitForRetry } from "./
28
30
  export type { DefaultRetryPolicyOptions } from "./retry.js";
29
31
  export { createContributionRegistries, createContributionRegistry, registerDiscoveredContributions } from "./contributions.js";
30
32
  export type { ContributionRegistries, ContributionRegistriesOptions, ContributionRegistry, ContributionRegistryOptions } from "./contributions.js";
31
- export { createChainedCredentialResolver, createEnvCredentialResolver, createExplicitCredentialResolver, createMemoryCredentialStore, refreshOAuthCredential, resolveCredentialValue } from "./credentials.js";
33
+ export { createChainedCredentialResolver, createEnvCredentialResolver, createExplicitCredentialResolver, createMemoryCredentialStore, refreshOAuthCredential, revokeOAuthCredential, resolveCredentialValue } from "./credentials.js";
32
34
  export { createExtensionEventBus, createExtensionKernel } from "./extensions.js";
33
- export type { ExtensionErrorPolicy, ExtensionEventBus, ExtensionEventHandler, ExtensionKernel, ExtensionKernelOptions } from "./extensions.js";
34
- export type { CredentialRecord, CredentialValueSource, MemoryCredentialStore } from "./credentials.js";
35
+ export type { ExtensionErrorPolicy, ExtensionEventBus, ExtensionEventHandler, ExtensionKernel, ExtensionKernelOptions, ExtensionLoadPolicy } from "./extensions.js";
36
+ export type { CredentialRecord, CredentialValueSource, MemoryCredentialStore, RevocableOAuthCredentialStore } from "./credentials.js";
35
37
  export { createModelRegistry } from "./models.js";
36
38
  export { authMethodKey, defineProviderPackage, systemPromptContributionKey } from "./provider-packages.js";
37
39
  export { assertStructuredOutputRequestSupported, artifactStructuredOutputRequest, DEFAULT_MAX_STRUCTURED_OUTPUT_NAME_LENGTH, DEFAULT_MAX_STRUCTURED_OUTPUT_SCHEMA_BYTES, modelSupportsStructuredOutput, resolveRunProviderOptions, StructuredOutputError, validateStructuredOutputOptions, withoutStructuredOutput, } from "./structured-output.js";
@@ -60,6 +62,12 @@ export type { ContextBudget, ContextBudgetMessageGroups, ContextBudgetOmission,
60
62
  export type { AgentInput, AssembleProviderInputOptions, DefaultInputBuilder, DefaultInputBuildContext, DefaultPromptBuilder, InputAttachment, PromptInstruction, PromptTemplateOptions, ResolveContextOptions } from "./input.js";
61
63
  export { createMockProvider } from "./mock-provider.js";
62
64
  export { createMemorySessionStore, createSessionEntry, getSessionBranchEntries, listSessionBranches, rebuildSessionContext } from "./session-stores.js";
65
+ export { CONVERSATION_METADATA_KEY, DEFAULT_MAX_CONVERSATION_CURSOR_BYTES, HARD_MAX_CONVERSATION_CURSOR_BYTES, ConversationError, conversationMarkerMetadata, conversationThreadFromRecord, decodeConversationReplayCursor, encodeConversationReplayCursor, } from "./conversations.js";
66
+ export type { ConversationBranchRef, ConversationReplayCursor, ConversationThread, ConversationThreadState, } from "./conversations.js";
67
+ export { ARTIFACT_CHECKPOINT_NAMESPACE, ArtifactError, artifactApprovalState, artifactCheckpointKey, } from "./artifacts.js";
68
+ export type { ArtifactApproval, ArtifactApprovalState, ArtifactCitation, ArtifactDecisionState, ArtifactDeliveryToken, ArtifactRecord, ArtifactRevision, } from "./artifacts.js";
69
+ export { DEFAULT_DEVICE_MAX_CHUNK_BYTES, DEFAULT_DEVICE_MAX_CONCURRENT_SESSIONS, HARD_DEVICE_MAX_CHUNK_BYTES, HARD_DEVICE_MAX_CONCURRENT_SESSIONS, DevicePolicyError, acceptDeviceChunk, assertDeviceAdmit, redactDeviceTelemetry, resolveDevicePolicy, runDevicePolicyConformance, } from "./devices.js";
70
+ export type { DeviceAdapter, DeviceAdmitRequest, DeviceChunkResult, DeviceConformanceResult, DeviceKind, DevicePolicyErrorCode, DevicePolicyOptions, DeviceStreamLimits, ResolvedDevicePolicy, } from "./devices.js";
63
71
  export type { CreateMemorySessionStoreOptions, CreateSessionEntryOptions, MemorySessionSearchMode, SessionBranch, SessionBranchOptions, SessionContextSnapshot } from "./session-stores.js";
64
72
  export type { MockProviderOptions } from "./mock-provider.js";
65
73
  export { providerContentDelta, providerDone, providerError, providerTextDelta, providerThinkingDelta, providerToolCall, providerToolCallDelta, providerUsage, toolCallContent, toolCallFromArgumentsText, } from "./provider-events.js";
@@ -70,6 +78,8 @@ export { createSecretRedactor, errorToErrorInfo, redactAgentEvent, redactMessage
70
78
  export type { SecretRedactor } from "./redaction.js";
71
79
  export { assertPermission, assertTrusted, checkPermission, createStaticPermissionPolicy, createStaticTrustPolicy, denialToErrorInfo, isTrusted, PermissionDeniedError, TrustDeniedError } from "./security.js";
72
80
  export type { PermissionDecision, PermissionPolicy, PermissionRequest, TrustDecision, TrustPolicy, TrustRequest } from "./security.js";
81
+ export { assertIdentityActive, assertIdentityMatchesOwnership, assertIdentityPropagation, DEFAULT_IDENTITY_LIMITS, HARD_IDENTITY_LIMITS, IdentityError, identityTelemetryAttributes, narrowIdentity, ownershipFromIdentity, resolveIdentityLimits, resolveRunIdentity, } from "./identity.js";
82
+ export type { AgentIdentity, AssertIdentityActiveOptions, IdentityLimits, IdentityVerifier, NarrowIdentityOptions, Principal, ResolvedIdentityLimits, } from "./identity.js";
73
83
  export { applyExecutionDecision, assertExecutionAllowed, checkExecution, ExecutionDeniedError } from "./execution-policy.js";
74
84
  export type { ExecutionAction, ExecutionDecision, ExecutionPolicy, ExecutionRisk } from "./execution-policy.js";
75
85
  export { createSkillRegistry, resolveActiveSkills } from "./skills.js";
@@ -85,5 +95,5 @@ export type { DispatchToolCallOptions, ToolArgumentValidationError, ToolArgument
85
95
  export type { DuplicateRegistrationOptions, DuplicateRegistrationPolicy } from "./registry-options.js";
86
96
  export { dispatchToolCallsInOrder, generateValidateReviseLoop, isAgentLoopOptions, resolveLoop, resolveToolConcurrency, singleShotLoop } from "./agent-loops.js";
87
97
  export declare const name = "prism";
88
- export declare const version = "0.0.12";
98
+ export declare const version = "0.0.14";
89
99
  export declare const description = "Agent harness for AI providers, agents, sessions, and tools.";