@arnilo/prism 0.0.12 → 0.0.13

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 (51) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/agents.js +21 -2
  3. package/dist/contracts.d.ts +25 -1
  4. package/dist/extensions.d.ts +11 -0
  5. package/dist/extensions.js +15 -0
  6. package/dist/identity.d.ts +92 -0
  7. package/dist/identity.js +257 -0
  8. package/dist/index.d.ts +6 -2
  9. package/dist/index.js +3 -1
  10. package/dist/persistence-lifecycle.d.ts +103 -0
  11. package/dist/persistence-lifecycle.js +204 -0
  12. package/dist/providers/openai-compatible.d.ts +5 -1
  13. package/dist/providers/openai-compatible.js +15 -6
  14. package/dist/secure-agent.js +7 -1
  15. package/dist/testing/persistence-schema.d.ts +2 -2
  16. package/dist/testing/persistence-schema.js +35 -2
  17. package/dist/tools.d.ts +2 -0
  18. package/dist/tools.js +6 -0
  19. package/docs/a2a.md +2 -0
  20. package/docs/agent-identity.md +111 -0
  21. package/docs/credential-storage.md +3 -0
  22. package/docs/database-persistence.md +18 -7
  23. package/docs/extensions.md +1 -0
  24. package/docs/guardrails.md +3 -0
  25. package/docs/host-security.md +5 -1
  26. package/docs/index.md +17 -8
  27. package/docs/mcp-tools.md +2 -0
  28. package/docs/migration.md +27 -0
  29. package/docs/model-routing.md +102 -0
  30. package/docs/observability.md +2 -0
  31. package/docs/performance.md +19 -0
  32. package/docs/policy-and-audit.md +127 -0
  33. package/docs/postgres-persistence.md +1 -1
  34. package/docs/provider-packages.md +8 -1
  35. package/docs/provider-request-policies.md +2 -0
  36. package/docs/providers/azure.md +74 -0
  37. package/docs/providers/bedrock.md +72 -0
  38. package/docs/providers/google.md +1 -0
  39. package/docs/providers/openai-compatible.md +3 -1
  40. package/docs/providers/openrouter.md +2 -0
  41. package/docs/providers/vertex.md +71 -0
  42. package/docs/public-contracts.md +3 -1
  43. package/docs/release-and-install.md +78 -6
  44. package/docs/review-coverage-2026-07-23-phase-8.md +245 -0
  45. package/docs/runs-and-usage.md +2 -0
  46. package/docs/server.md +33 -4
  47. package/docs/sqlite-persistence.md +1 -1
  48. package/docs/supervisors.md +2 -0
  49. package/docs/work-connectors.md +28 -0
  50. package/docs/work-tools.md +114 -0
  51. package/package.json +4 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.0.13] - 2026-07-24
4
+
5
+ ### Added
6
+
7
+ - Enterprise identity (`Principal` / `AgentIdentity`), optional `@arnilo/prism-policy`, `@arnilo/prism-model-router`, enterprise cloud providers (Azure/Bedrock/Vertex), server deployment seams, persistence schema v5 lifecycle hooks, and `@arnilo/prism-work-tools` (M365 + GWS).
8
+
9
+ ### Changed
10
+
11
+ - Versioned all **41** first-party manifests and exact internal ranges to `0.0.13`; Phase 8 optional packages enroll in `@arnilo/prism-all` only.
12
+ - Network-free enterprise evidence: `scripts/benchmark-0.0.13.mjs`.
13
+
3
14
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
4
15
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
16
 
package/dist/agents.js CHANGED
@@ -17,6 +17,7 @@ import { createToolRegistry, dispatchToolCall } from "./tools.js";
17
17
  import { RunLimitError, RunLimitTracker, resolveRunLimits } from "./run-limits.js";
18
18
  import { agentFingerprint, initialAgentRunState, loadAgentRunState, publicState, saveAgentRunState, validateRunStateOptions } from "./agent-run-state.js";
19
19
  import { resolveActiveSkills } from "./skills.js";
20
+ import { identityTelemetryAttributes, ownershipFromIdentity, resolveRunIdentity, } from "./identity.js";
20
21
  export function createAgent(config) {
21
22
  return {
22
23
  config,
@@ -154,6 +155,7 @@ class RuntimeAgentSession {
154
155
  activeProvider;
155
156
  activeLedger;
156
157
  activeOwnership;
158
+ activeIdentity;
157
159
  activeIdempotencyKey;
158
160
  activeGuardrails;
159
161
  activeMetadata;
@@ -261,6 +263,9 @@ class RuntimeAgentSession {
261
263
  this.activeRedactor = options.redactor ?? this.agent.config.redactor;
262
264
  this.activeLedger = options.runLedger ?? this.agent.config.runLedger;
263
265
  this.activeOwnership = options.ownership ?? this.agent.config.ownership;
266
+ this.activeIdentity = resolveRunIdentity(options.identity, this.agent.config.identity, this.activeOwnership);
267
+ if (this.activeIdentity && !this.activeOwnership)
268
+ this.activeOwnership = ownershipFromIdentity(this.activeIdentity);
264
269
  this.activeIdempotencyKey = options.idempotencyKey ?? this.agent.config.idempotencyKey;
265
270
  this.activeGuardrails = mergeGuardrails(this.agent.config.guardrails, options.guardrails);
266
271
  this.activeDurable = resumed ?? (durableOptions ? { options: durableOptions, version: 0 } : undefined);
@@ -272,7 +277,12 @@ class RuntimeAgentSession {
272
277
  let runStatus = "succeeded";
273
278
  const runUsage = createUsageAccumulator();
274
279
  let usage;
275
- const metadata = { ...this.agent.config.metadata, ...this.metadata, ...options.metadata };
280
+ const metadata = {
281
+ ...this.agent.config.metadata,
282
+ ...this.metadata,
283
+ ...options.metadata,
284
+ ...(this.activeIdentity ? identityTelemetryAttributes(this.activeIdentity) : {}),
285
+ };
276
286
  this.activeMetadata = metadata;
277
287
  const limits = new RunLimitTracker(resolvedLimits, {
278
288
  onExceeded: (breach) => {
@@ -434,7 +444,14 @@ class RuntimeAgentSession {
434
444
  dispatchToolCall: (call) => dispatchToolCall({
435
445
  call,
436
446
  registry,
437
- context: { sessionId: this.id, runId, toolCallId: call.id, signal: controller.signal, metadata },
447
+ context: {
448
+ sessionId: this.id,
449
+ runId,
450
+ toolCallId: call.id,
451
+ signal: controller.signal,
452
+ metadata,
453
+ identity: this.activeIdentity,
454
+ },
438
455
  middleware: this.agent.config.middleware,
439
456
  emit: (event) => this.emit(event),
440
457
  permission: this.agent.config.permission,
@@ -442,6 +459,7 @@ class RuntimeAgentSession {
442
459
  redactor: this.activeRedactor,
443
460
  ledger: this.activeLedger,
444
461
  ownership: this.activeOwnership,
462
+ identity: this.activeIdentity,
445
463
  guardrails: this.activeGuardrails,
446
464
  limitTracker: limits,
447
465
  beforeExecute: async (mediatedCall) => {
@@ -577,6 +595,7 @@ class RuntimeAgentSession {
577
595
  finally {
578
596
  this.activeLedger = undefined;
579
597
  this.activeOwnership = undefined;
598
+ this.activeIdentity = undefined;
580
599
  this.activeIdempotencyKey = undefined;
581
600
  this.activeGuardrails = undefined;
582
601
  this.activeMetadata = undefined;
@@ -287,6 +287,8 @@ export interface RunOptions {
287
287
  readonly redactor?: SecretRedactor;
288
288
  readonly runLedger?: RunLedger;
289
289
  readonly ownership?: OwnershipScope;
290
+ /** Host-verified identity; when set, must project onto `ownership` without widening. */
291
+ readonly identity?: import("./identity.js").AgentIdentity;
290
292
  readonly idempotencyKey?: string;
291
293
  readonly validate?: ToolValidator;
292
294
  readonly activeSkills?: readonly string[];
@@ -352,6 +354,8 @@ export interface AgentConfig {
352
354
  readonly redactor?: SecretRedactor;
353
355
  readonly runLedger?: RunLedger;
354
356
  readonly ownership?: OwnershipScope;
357
+ /** Host-verified identity default for sessions created from this agent. */
358
+ readonly identity?: import("./identity.js").AgentIdentity;
355
359
  readonly idempotencyKey?: string;
356
360
  readonly compaction?: false | CompactionOptions;
357
361
  readonly retry?: false | RetryOptions;
@@ -369,7 +373,7 @@ export interface AgentConfig {
369
373
  readonly secure?: true;
370
374
  }
371
375
  /** Opt-in fail-closed composition over the normal explicit AgentConfig API. */
372
- export interface SecureAgentOptions extends Omit<AgentConfig, "tools" | "validator" | "redactor" | "permission" | "trust" | "ownership" | "limits" | "runState" | "secure"> {
376
+ export interface SecureAgentOptions extends Omit<AgentConfig, "tools" | "validator" | "redactor" | "permission" | "trust" | "ownership" | "identity" | "limits" | "runState" | "secure"> {
373
377
  readonly id: string;
374
378
  readonly tools: readonly ToolDefinition[];
375
379
  readonly toolArgumentValidator: import("./tools.js").ToolArgumentValidator;
@@ -377,6 +381,8 @@ export interface SecureAgentOptions extends Omit<AgentConfig, "tools" | "validat
377
381
  readonly permission: PermissionPolicy;
378
382
  readonly trust: TrustPolicy;
379
383
  readonly ownership: OwnershipScope;
384
+ /** Optional host-verified identity; when set must match `ownership`. */
385
+ readonly identity?: import("./identity.js").AgentIdentity;
380
386
  readonly limits: RunLimits;
381
387
  readonly definitionRevision: string;
382
388
  readonly runState: Omit<AgentRunStateOptions, "definitionRevision" | "interruptBeforeTool">;
@@ -750,6 +756,8 @@ export interface ToolExecutionContext {
750
756
  readonly toolCallId: string;
751
757
  readonly signal?: AbortSignal;
752
758
  readonly metadata?: Readonly<Record<string, unknown>>;
759
+ /** Host-verified identity for this tool invocation, when enterprise identity is active. */
760
+ readonly identity?: import("./identity.js").AgentIdentity;
753
761
  progress?(progress?: unknown, metadata?: Readonly<Record<string, unknown>>): void | Promise<void>;
754
762
  }
755
763
  export interface ToolResult {
@@ -900,6 +908,9 @@ export interface ExtensionEvent {
900
908
  export interface Extension {
901
909
  readonly name: string;
902
910
  setup(api: ExtensionAPI): void | Promise<void>;
911
+ /** Host-attested signature/digest for `ExtensionLoadPolicy.verifySignature`. */
912
+ readonly signature?: string;
913
+ readonly metadata?: Readonly<Record<string, unknown>>;
903
914
  }
904
915
  export interface ProviderPackage {
905
916
  readonly name: string;
@@ -1605,6 +1616,11 @@ export interface ProductionPersistenceStore {
1605
1616
  /** DB-friendly branch read (mirrors `SessionStore.readBranchPath`): one ancestor-chain
1606
1617
  * query instead of `queryEntries({ sessionId })` + in-memory walk. Optional. */
1607
1618
  readBranchPath?(query: SessionBranchRead): Promise<PersistencePage<SessionEntry>>;
1619
+ /**
1620
+ * Optional Phase 8 retention / legal-hold / export / tenant-quota lifecycle.
1621
+ * Prefer attaching `createMemoryPersistenceLifecycle()` or adapter-native methods.
1622
+ */
1623
+ readonly lifecycle?: import("./persistence-lifecycle.js").PersistenceLifecycleStore;
1608
1624
  readonly metadata?: Readonly<Record<string, unknown>>;
1609
1625
  }
1610
1626
  export interface CompactionStrategy {
@@ -1796,3 +1812,11 @@ export interface ArtifactParseResult<T> {
1796
1812
  export type ArtifactParser<T> = (text: string, ctx: ArtifactContext) => ArtifactParseResult<T> | Promise<ArtifactParseResult<T>>;
1797
1813
  export type ArtifactValidator<T> = (value: T, ctx: ArtifactContext) => ArtifactValidation | Promise<ArtifactValidation>;
1798
1814
  export type ArtifactRepairer<T> = (value: T | undefined, failure: ArtifactValidation, ctx: ArtifactContext) => AgentInput | Promise<AgentInput>;
1815
+ /** Alias re-exports so `dist/contracts.d.ts` exposes implementer contract names. */
1816
+ export type AgentIdentity = import("./identity.js").AgentIdentity;
1817
+ export type Principal = import("./identity.js").Principal;
1818
+ export type IdentityVerifier = import("./identity.js").IdentityVerifier;
1819
+ export type PersistenceLifecycleStore = import("./persistence-lifecycle.js").PersistenceLifecycleStore;
1820
+ export type LegalHoldRecord = import("./persistence-lifecycle.js").LegalHoldRecord;
1821
+ export type TenantQuota = import("./persistence-lifecycle.js").TenantQuota;
1822
+ export type PersistenceResourceKind = import("./persistence-lifecycle.js").PersistenceResourceKind;
@@ -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
@@ -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";
@@ -30,7 +32,7 @@ export { createContributionRegistries, createContributionRegistry, registerDisco
30
32
  export type { ContributionRegistries, ContributionRegistriesOptions, ContributionRegistry, ContributionRegistryOptions } from "./contributions.js";
31
33
  export { createChainedCredentialResolver, createEnvCredentialResolver, createExplicitCredentialResolver, createMemoryCredentialStore, refreshOAuthCredential, resolveCredentialValue } from "./credentials.js";
32
34
  export { createExtensionEventBus, createExtensionKernel } from "./extensions.js";
33
- export type { ExtensionErrorPolicy, ExtensionEventBus, ExtensionEventHandler, ExtensionKernel, ExtensionKernelOptions } from "./extensions.js";
35
+ export type { ExtensionErrorPolicy, ExtensionEventBus, ExtensionEventHandler, ExtensionKernel, ExtensionKernelOptions, ExtensionLoadPolicy } from "./extensions.js";
34
36
  export type { CredentialRecord, CredentialValueSource, MemoryCredentialStore } from "./credentials.js";
35
37
  export { createModelRegistry } from "./models.js";
36
38
  export { authMethodKey, defineProviderPackage, systemPromptContributionKey } from "./provider-packages.js";
@@ -70,6 +72,8 @@ export { createSecretRedactor, errorToErrorInfo, redactAgentEvent, redactMessage
70
72
  export type { SecretRedactor } from "./redaction.js";
71
73
  export { assertPermission, assertTrusted, checkPermission, createStaticPermissionPolicy, createStaticTrustPolicy, denialToErrorInfo, isTrusted, PermissionDeniedError, TrustDeniedError } from "./security.js";
72
74
  export type { PermissionDecision, PermissionPolicy, PermissionRequest, TrustDecision, TrustPolicy, TrustRequest } from "./security.js";
75
+ export { assertIdentityActive, assertIdentityMatchesOwnership, assertIdentityPropagation, DEFAULT_IDENTITY_LIMITS, HARD_IDENTITY_LIMITS, IdentityError, identityTelemetryAttributes, narrowIdentity, ownershipFromIdentity, resolveIdentityLimits, resolveRunIdentity, } from "./identity.js";
76
+ export type { AgentIdentity, AssertIdentityActiveOptions, IdentityLimits, IdentityVerifier, NarrowIdentityOptions, Principal, ResolvedIdentityLimits, } from "./identity.js";
73
77
  export { applyExecutionDecision, assertExecutionAllowed, checkExecution, ExecutionDeniedError } from "./execution-policy.js";
74
78
  export type { ExecutionAction, ExecutionDecision, ExecutionPolicy, ExecutionRisk } from "./execution-policy.js";
75
79
  export { createSkillRegistry, resolveActiveSkills } from "./skills.js";
@@ -85,5 +89,5 @@ export type { DispatchToolCallOptions, ToolArgumentValidationError, ToolArgument
85
89
  export type { DuplicateRegistrationOptions, DuplicateRegistrationPolicy } from "./registry-options.js";
86
90
  export { dispatchToolCallsInOrder, generateValidateReviseLoop, isAgentLoopOptions, resolveLoop, resolveToolConcurrency, singleShotLoop } from "./agent-loops.js";
87
91
  export declare const name = "prism";
88
- export declare const version = "0.0.12";
92
+ export declare const version = "0.0.13";
89
93
  export declare const description = "Agent harness for AI providers, agents, sessions, and tools.";
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ export { CHECKPOINT_CONFLICT_CODE, CheckpointConflictError, createMemoryCheckpoi
7
7
  export { AGENT_RUN_STATE_NAMESPACE, AGENT_RUN_STATE_SCHEMA_VERSION, DEFAULT_MAX_AGENT_RUN_STATE_BYTES, HARD_MAX_AGENT_RUN_STATE_BYTES, agentFingerprint, loadAgentRunState } from "./agent-run-state.js";
8
8
  export { createAgentRunLifecycle } from "./agent-run-lifecycle.js";
9
9
  export { LEASE_CONFLICT_CODE, LeaseConflictError, createMemoryLeaseStore } from "./leases.js";
10
+ 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";
10
11
  export { createEventMultiplexer } from "./event-multiplexer.js";
11
12
  export { applyCacheControl, cacheHitRate, cacheSavings, cacheUsageReport, mapCacheRetention, sanitizeCacheKey } from "./cache-helpers.js";
12
13
  export { resolveAgentDefinition } from "./agent-definitions.js";
@@ -38,6 +39,7 @@ export { providerContentDelta, providerDone, providerError, providerTextDelta, p
38
39
  export { createProviderRegistry, createProviderResolver } from "./providers.js";
39
40
  export { createSecretRedactor, errorToErrorInfo, redactAgentEvent, redactMessage, redactProviderRequest, redactRunLedgerRecord, redactSecrets, redactSessionEntry } from "./redaction.js";
40
41
  export { assertPermission, assertTrusted, checkPermission, createStaticPermissionPolicy, createStaticTrustPolicy, denialToErrorInfo, isTrusted, PermissionDeniedError, TrustDeniedError } from "./security.js";
42
+ export { assertIdentityActive, assertIdentityMatchesOwnership, assertIdentityPropagation, DEFAULT_IDENTITY_LIMITS, HARD_IDENTITY_LIMITS, IdentityError, identityTelemetryAttributes, narrowIdentity, ownershipFromIdentity, resolveIdentityLimits, resolveRunIdentity, } from "./identity.js";
41
43
  export { applyExecutionDecision, assertExecutionAllowed, checkExecution, ExecutionDeniedError } from "./execution-policy.js";
42
44
  export { createSkillRegistry, resolveActiveSkills } from "./skills.js";
43
45
  export { resolveInstructionInjectors, runInstructionInjectors } from "./instruction-injection.js";
@@ -46,6 +48,6 @@ export { assertGuardrailsAllowed, GuardrailError, MAX_GUARDRAIL_CONCURRENCY, run
46
48
  export { createRunLimitTracker, DEFAULT_RUN_LIMITS, HARD_MAX_RUN_COST, HARD_RUN_LIMITS, RunLimitError, RunLimitTracker, resolveRunLimits } from "./run-limits.js";
47
49
  export { dispatchToolCallsInOrder, generateValidateReviseLoop, isAgentLoopOptions, resolveLoop, resolveToolConcurrency, singleShotLoop } from "./agent-loops.js";
48
50
  export const name = "prism";
49
- export const version = "0.0.12";
51
+ export const version = "0.0.13";
50
52
  export const description = "Agent harness for AI providers, agents, sessions, and tools.";
51
53
  //# sourceMappingURL=index.js.map