@agent-surface/core 0.6.0 → 0.8.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/policy.ts","../src/ids.ts","../src/utils.ts","../src/internal.ts","../src/snapshot.ts"],"sourcesContent":["import type { JsonValue } from \"./types.js\";\n\n/** The closed agent-facing error enum — one runtime source, cross-validated\n * against spec/error-matrix.json (docs/07 §principles, AS-ERR-001). */\nexport const AGENT_CAPABILITY_ERROR_CODES = [\n \"CAPABILITY_NOT_FOUND\",\n \"CAPABILITY_NOT_AVAILABLE\",\n \"AMBIGUOUS_INSTANCE\",\n \"COMPONENT_UNMOUNTED\",\n \"STALE_CAPABILITY\",\n \"INVOCATION_CONFLICT\",\n \"INVALID_INPUT\",\n \"NOT_AUTHENTICATED\",\n \"NOT_AUTHORIZED\",\n \"PRECONDITION_FAILED\",\n \"CONFIRMATION_REQUIRED\",\n \"CONFIRMATION_INVALID\",\n \"RATE_LIMITED\",\n \"TIMEOUT\",\n \"CANCELLED\",\n \"EXECUTION_FAILED\",\n] as const;\n\nexport type AgentCapabilityErrorCode = (typeof AGENT_CAPABILITY_ERROR_CODES)[number];\n\nexport type AgentErrorRetry =\n | \"no\"\n | \"yes\"\n | \"after-refresh\"\n | \"after-delay\"\n | \"with-confirmation\"\n | \"with-changes\";\n\nexport interface AgentCapabilityErrorPayload {\n code: AgentCapabilityErrorCode;\n /** Agent-safe, imperative, ≤ 300 chars. */\n message: string;\n retry: AgentErrorRetry;\n /** Code-specific, agent-safe, JsonValue only. */\n details?: Record<string, JsonValue>;\n}\n\n/** Thrown form used inside policies/handlers; serialized at the boundary. */\nexport class AgentSurfaceError extends Error {\n readonly payload: AgentCapabilityErrorPayload;\n constructor(payload: AgentCapabilityErrorPayload, opts?: { cause?: unknown }) {\n super(payload.message, opts);\n this.name = \"AgentSurfaceError\";\n this.payload = payload;\n }\n}\n\nexport function isAgentSurfaceError(e: unknown): e is AgentSurfaceError {\n return (\n e instanceof AgentSurfaceError ||\n (typeof e === \"object\" &&\n e !== null &&\n (e as { name?: unknown }).name === \"AgentSurfaceError\" &&\n typeof (e as { payload?: unknown }).payload === \"object\")\n );\n}\n\nexport type AgentSurfaceDefinitionErrorCode =\n | \"INVALID_ID\"\n | \"INVALID_DEFINITION\"\n | \"UNSUPPORTED_SCHEMA\"\n | \"PLANE_VIOLATION\"\n | \"DUPLICATE_CAPABILITY\"\n | \"LIMIT_EXCEEDED\";\n\n/** Structural defects at registration time. Always thrown, never agent-facing. */\nexport class AgentSurfaceDefinitionError extends Error {\n readonly code: AgentSurfaceDefinitionErrorCode;\n constructor(code: AgentSurfaceDefinitionErrorCode, message: string) {\n super(`[${code}] ${message}`);\n this.name = \"AgentSurfaceDefinitionError\";\n this.code = code;\n }\n}\n","import type {\n AgentConsumer,\n AgentEffect,\n AgentEnvironment,\n JsonValue,\n} from \"./types.js\";\nimport { AgentSurfaceError } from \"./errors.js\";\nimport type { AgentInvocationResult } from \"./invocation-types.js\";\nimport type { AuditSink } from \"./audit.js\";\n\nexport type DiscoveryDecision =\n | { decision: \"expose\" }\n | { decision: \"disable\"; reason: string } // visible-disabled\n | { decision: \"hide\" }; // absent from snapshot\n\nexport interface AgentPolicyContext {\n capabilityId: string;\n plane: \"view\" | \"domain\";\n kind: \"observation\" | \"action\" | \"procedure\";\n effect: AgentEffect;\n registrationId: string;\n consumer: AgentConsumer;\n host: Readonly<Record<string, unknown>>; // RegistryOptions.context()\n meta: {\n component?: Record<string, JsonValue>;\n capability?: Record<string, JsonValue>;\n };\n internal: Readonly<Record<string, unknown>>; // never serialized\n /** Registry environment (additive convenience for built-ins). */\n environment: AgentEnvironment;\n /** Registry's injectable clock — built-ins MUST use this, never Date.now(). */\n now(): number;\n}\n\n/** Phase-4 context: no agent input is available here, by construction (D21). */\nexport type AgentAuthorizationContext = AgentPolicyContext;\n\n/** Phase-6 context: only the validated effective input is visible (D21). */\nexport interface AgentInvocationPolicyContext extends AgentAuthorizationContext {\n invocationId: string;\n effectiveInput: JsonValue;\n}\n\nexport interface AgentPolicy {\n name: string;\n /**\n * Discovery-time filter. MUST be synchronous, cheap, side-effect free.\n * Advisory: hides/disables in catalogs. Default when absent: expose.\n */\n onDiscovery?(ctx: AgentPolicyContext): DiscoveryDecision;\n /**\n * Pre-input authority gate (pipeline phase 4): authn/authz/tenant/\n * environment/input-independent rate. MAY be async. Onion order; call\n * next() to proceed. Throw AgentSurfaceError to deny.\n */\n onAuthorize?(\n ctx: AgentAuthorizationContext,\n next: () => Promise<AgentInvocationResult>,\n ): Promise<AgentInvocationResult>;\n /**\n * Post-input invocation gate (pipeline phase 6). Receives ONLY the\n * validated effective input — never raw agent input (D21).\n */\n onInvoke?(\n ctx: AgentInvocationPolicyContext,\n next: () => Promise<AgentInvocationResult>,\n ): Promise<AgentInvocationResult>;\n}\n\n/**\n * Marker read by the invocation pipeline: policies carrying it escalate the\n * capability's confirmation requirement (docs/06 requireConfirmation).\n */\nexport const CONFIRMATION_ESCALATION: unique symbol = Symbol(\"agent-surface.confirmation-escalation\");\n\nexport interface ConfirmationEscalation {\n /** Evaluated at phase 6 over the validated effective input (D21). */\n if?: (ctx: AgentPolicyContext & { effectiveInput: JsonValue }) => boolean;\n summary?: (effectiveInput: JsonValue) => string;\n}\n\nexport type AgentPolicyWithEscalation = AgentPolicy & {\n [CONFIRMATION_ESCALATION]?: ConfirmationEscalation;\n};\n\n/** Most-restrictive-wins composition of discovery decisions (docs/06). */\nexport function evaluateDiscovery(\n policies: ReadonlyArray<AgentPolicy>,\n ctx: AgentPolicyContext,\n): DiscoveryDecision {\n let disable: { decision: \"disable\"; reason: string } | undefined;\n for (const policy of policies) {\n if (!policy.onDiscovery) continue;\n let decision: DiscoveryDecision;\n try {\n decision = policy.onDiscovery(ctx);\n } catch {\n // A throwing discovery policy is a defect; fail closed.\n return { decision: \"hide\" };\n }\n if (decision.decision === \"hide\") return decision;\n if (decision.decision === \"disable\" && !disable) disable = decision;\n }\n return disable ?? { decision: \"expose\" };\n}\n\n/** Onion composition of onAuthorize handlers (registry outermost, phase 4). */\nexport function composeAuthorizeChain(\n policies: ReadonlyArray<AgentPolicy>,\n ctx: AgentAuthorizationContext,\n core: () => Promise<AgentInvocationResult>,\n): Promise<AgentInvocationResult> {\n let index = -1;\n const dispatch = (i: number): Promise<AgentInvocationResult> => {\n if (i <= index) {\n return Promise.reject(new Error(\"policy next() called multiple times\"));\n }\n index = i;\n const policy = policies[i];\n if (!policy) return core();\n if (!policy.onAuthorize) return dispatch(i + 1);\n return policy.onAuthorize(ctx, () => dispatch(i + 1));\n };\n return dispatch(0);\n}\n\n/** Onion composition of onInvoke handlers (registry outermost, phase 6). */\nexport function composeInvokeChain(\n policies: ReadonlyArray<AgentPolicy>,\n ctx: AgentInvocationPolicyContext,\n core: () => Promise<AgentInvocationResult>,\n): Promise<AgentInvocationResult> {\n let index = -1;\n const dispatch = (i: number): Promise<AgentInvocationResult> => {\n if (i <= index) {\n return Promise.reject(new Error(\"policy next() called multiple times\"));\n }\n index = i;\n const policy = policies[i];\n if (!policy) return core();\n if (!policy.onInvoke) return dispatch(i + 1);\n return policy.onInvoke(ctx, () => dispatch(i + 1));\n };\n return dispatch(0);\n}\n\n/* ───────────────────────── built-in policies ─────────────────────────\n * Authority hides, state discloses (D11/D12): authz-style built-ins hide at\n * discovery and fail typed at invocation.\n */\n\n/** Requires ctx.host[key] (default \"user\"). Fails NOT_AUTHENTICATED; hides. */\nexport function authenticated(opts?: { key?: string }): AgentPolicy {\n const key = opts?.key ?? \"user\";\n return {\n name: \"authenticated\",\n onDiscovery(ctx) {\n return ctx.host[key] ? { decision: \"expose\" } : { decision: \"hide\" };\n },\n async onAuthorize(ctx, next) {\n if (!ctx.host[key]) {\n throw new AgentSurfaceError({\n code: \"NOT_AUTHENTICATED\",\n message: \"Sign-in is required before this capability can be used. Ask the user to sign in.\",\n retry: \"no\",\n });\n }\n return next();\n },\n };\n}\n\n/** Delegates to a host authorizer. Fails NOT_AUTHORIZED; hides at discovery. */\nexport function hasPermission(\n permission: string,\n check: (host: Record<string, unknown>, permission: string) => boolean,\n): AgentPolicy {\n return {\n name: `has-permission(${permission})`,\n onDiscovery(ctx) {\n return check({ ...ctx.host }, permission) ? { decision: \"expose\" } : { decision: \"hide\" };\n },\n async onAuthorize(ctx, next) {\n if (!check({ ...ctx.host }, permission)) {\n throw new AgentSurfaceError({\n code: \"NOT_AUTHORIZED\",\n message: \"The current user is not authorized to use this capability.\",\n retry: \"no\",\n details: { origin: \"client\" },\n });\n }\n return next();\n },\n };\n}\n\n/** Tenant boundary: hides unless current(host) === expected(ctx). */\nexport function tenantBoundary(opts: {\n current: (host: Record<string, unknown>) => string | undefined;\n expected: (ctx: AgentPolicyContext) => string | undefined;\n}): AgentPolicy {\n const matches = (ctx: AgentPolicyContext): boolean => {\n const current = opts.current({ ...ctx.host });\n const expected = opts.expected(ctx);\n return expected === undefined || current === expected;\n };\n return {\n name: \"tenant-boundary\",\n onDiscovery(ctx) {\n return matches(ctx) ? { decision: \"expose\" } : { decision: \"hide\" };\n },\n async onAuthorize(ctx, next) {\n if (!matches(ctx)) {\n throw new AgentSurfaceError({\n code: \"NOT_AUTHORIZED\",\n message: \"This capability belongs to a different tenant.\",\n retry: \"no\",\n details: { origin: \"client\" },\n });\n }\n return next();\n },\n };\n}\n\n/** Restricts to environments. Others: hidden. */\nexport function environment(allowed: AgentEnvironment[]): AgentPolicy {\n return {\n name: \"environment\",\n onDiscovery(ctx) {\n return allowed.includes(ctx.environment) ? { decision: \"expose\" } : { decision: \"hide\" };\n },\n async onAuthorize(ctx, next) {\n if (!allowed.includes(ctx.environment)) {\n throw new AgentSurfaceError({\n code: \"CAPABILITY_NOT_FOUND\",\n message: \"This capability does not exist in the current surface.\",\n retry: \"after-refresh\",\n });\n }\n return next();\n },\n };\n}\n\n/**\n * Token bucket per (consumer, capability). Advisory, input-independent —\n * runs pre-input (phase 4). Author input-aware rate policies as onInvoke.\n * Fails RATE_LIMITED. Uses the injectable clock (AS-POLICY-001).\n */\nexport function rateLimit(opts: { limit: number; windowMs: number }): AgentPolicy {\n const hits = new Map<string, number[]>();\n return {\n name: \"rate-limit\",\n async onAuthorize(ctx, next) {\n const key = `${ctx.consumer.kind}:${ctx.consumer.id} ${ctx.capabilityId}`;\n const now = ctx.now();\n const windowStart = now - opts.windowMs;\n const list = (hits.get(key) ?? []).filter((t) => t > windowStart);\n if (list.length >= opts.limit) {\n const retryAfterMs = Math.max(0, (list[0] ?? now) + opts.windowMs - now);\n throw new AgentSurfaceError({\n code: \"RATE_LIMITED\",\n message: \"Too many calls to this capability. Wait before retrying.\",\n retry: \"after-delay\",\n details: { reason: \"rate\", retryAfterMs },\n });\n }\n list.push(now);\n hits.set(key, list);\n return next();\n },\n };\n}\n\n/** Escalates confirmation to \"required\" (optionally conditionally).\n * Predicates run at phase 6 over the validated effective input (D21). */\nexport function requireConfirmation(opts?: {\n if?: (ctx: AgentPolicyContext & { effectiveInput: JsonValue }) => boolean;\n summary?: (effectiveInput: JsonValue) => string;\n}): AgentPolicy {\n const policy: AgentPolicyWithEscalation = {\n name: \"require-confirmation\",\n };\n policy[CONFIRMATION_ESCALATION] = { if: opts?.if, summary: opts?.summary };\n return policy;\n}\n\n/** Forwards invocation events to a sink at the given detail level.\n * Runs at phase 6 (onInvoke): its enrichment sees the effective input. */\nexport function audit(sink?: AuditSink, level: \"metadata\" | \"full\" = \"metadata\"): AgentPolicy {\n return {\n name: \"audit\",\n async onInvoke(ctx, next) {\n const startedAt = ctx.now();\n sink?.record({\n at: new Date(startedAt).toISOString(),\n type: \"invocation-started\",\n capabilityId: ctx.capabilityId,\n registrationId: ctx.registrationId,\n invocationId: ctx.invocationId,\n consumerId: ctx.consumer.id,\n ...(level === \"full\" ? { payload: { input: ctx.effectiveInput } } : {}),\n });\n const result = await next();\n sink?.record({\n at: new Date(ctx.now()).toISOString(),\n type: \"invocation-settled\",\n capabilityId: ctx.capabilityId,\n registrationId: ctx.registrationId,\n invocationId: ctx.invocationId,\n consumerId: ctx.consumer.id,\n status: result.status,\n ...(result.status === \"error\" ? { code: result.error.code } : {}),\n durationMs: ctx.now() - startedAt,\n ...(level === \"full\" && result.status === \"ok\" && result.output !== undefined\n ? { payload: { output: result.output } }\n : {}),\n });\n return result;\n },\n };\n}\n","/**\n * Canonical ID grammar (docs/01 §identity):\n *\n * capability-id = plane \":\" component-type \".\" capability-name\n * plane = \"view\" | \"domain\"\n * component-type = segment *( \".\" segment )\n * segment = lowercase-letter *( lowercase-letter / digit / \"-\" )\n * capability-name = lowercase-letter *( letter / digit ) ; camelCase, no dots\n * instance-id = 1*( letter / digit / \"-\" / \"_\" )\n *\n * Underscores are reserved for the wire-name encoding (docs/09).\n */\n\nexport const MAX_ID_LENGTH = 128;\n\nconst SEGMENT_RE = /^[a-z][a-z0-9-]*$/;\nconst CAPABILITY_NAME_RE = /^[a-z][A-Za-z0-9]*$/;\nconst INSTANCE_ID_RE = /^[A-Za-z0-9_-]+$/;\n\nexport type AgentPlane = \"view\" | \"domain\";\n\nexport function isValidComponentType(type: string): boolean {\n if (type.length === 0 || type.length > MAX_ID_LENGTH) return false;\n return type.split(\".\").every((seg) => SEGMENT_RE.test(seg));\n}\n\nexport function isValidCapabilityName(name: string): boolean {\n return CAPABILITY_NAME_RE.test(name);\n}\n\nexport function isValidInstanceId(id: string): boolean {\n return id.length > 0 && id.length <= MAX_ID_LENGTH && INSTANCE_ID_RE.test(id);\n}\n\nexport function formatViewCapabilityId(componentType: string, name: string): string {\n return `view:${componentType}.${name}`;\n}\n\nexport function formatDomainCapabilityId(path: string): string {\n return `domain:${path}`;\n}\n\nexport interface ParsedViewCapabilityId {\n plane: \"view\";\n componentType: string;\n name: string;\n}\nexport interface ParsedDomainCapabilityId {\n plane: \"domain\";\n /** Canonical oRPC procedure path — treated as opaque (docs/01). */\n path: string;\n}\nexport type ParsedCapabilityId = ParsedViewCapabilityId | ParsedDomainCapabilityId;\n\n/** Parses a capability id; returns undefined when the grammar is violated. */\nexport function parseCapabilityId(id: string): ParsedCapabilityId | undefined {\n // The signature says `string`, and this is the boundary where that assumption\n // is load-bearing: a caller relaying a malformed request (an adapter whose\n // envelope carried no `capabilityId`) must get a grammar rejection —\n // CAPABILITY_NOT_FOUND — not a TypeError the pipeline reports as an internal\n // defect with retry:\"no\".\n if (typeof id !== \"string\" || id.length > MAX_ID_LENGTH) return undefined;\n if (id.startsWith(\"view:\")) {\n const rest = id.slice(\"view:\".length);\n // The capability name is everything after the LAST dot (docs/01).\n const lastDot = rest.lastIndexOf(\".\");\n if (lastDot <= 0) return undefined;\n const componentType = rest.slice(0, lastDot);\n const name = rest.slice(lastDot + 1);\n if (!isValidComponentType(componentType) || !isValidCapabilityName(name)) {\n return undefined;\n }\n return { plane: \"view\", componentType, name };\n }\n if (id.startsWith(\"domain:\")) {\n const path = id.slice(\"domain:\".length);\n if (path.length === 0) return undefined;\n return { plane: \"domain\", path };\n }\n return undefined;\n}\n\n/* ───────────────────────────── wire names ─────────────────────────────\n * encode(id): \":\" → \"_\" \".\" → \"__\"\n * decode(name): first \"_\" splits the plane (the grammar forbids \"_\" in ids);\n * \"__\" → \".\"\n * Names that would exceed 64 chars are SHORTENED: kept prefix + \"_0_\" + hash\n * of the full id. The marker makes shortening self-evident, so `decodeWireName`\n * can refuse rather than return a plausible wrong id (D30).\n */\n\nexport const MAX_WIRE_NAME_LENGTH = 64;\n\n/**\n * Marks a shortened name. Unreachable in a faithful encoding of a `view:` id\n * (the grammar forbids \"_\", and no segment or capability name may start with a\n * digit). A `domain:` path with a bare \"0\" segment would produce it — decoding\n * such a name is refused rather than guessed, which is the safe direction.\n */\nconst SHORTENED_MARKER = \"_0_\";\n/** Instance disambiguator (docs/09 rule 7); also not decodable by inspection. */\nconst INSTANCE_MARKER = \"_at_\";\n\n/** FNV-1a, base36, extended by re-seeding when more characters are asked for. */\nfunction hash36(input: string, length: number): string {\n let out = \"\";\n for (let round = 0; out.length < length; round++) {\n let hash = (0x811c9dc5 ^ round) >>> 0;\n for (let i = 0; i < input.length; i++) {\n hash ^= input.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193) >>> 0;\n }\n out += hash.toString(36).padStart(7, \"0\");\n }\n return out.slice(0, length);\n}\n\nfunction rawWireName(id: string, instanceId?: string): string {\n const encoded = id.replace(\":\", \"_\").replaceAll(\".\", \"__\");\n return instanceId ? `${encoded}${INSTANCE_MARKER}${instanceId}` : encoded;\n}\n\nexport function encodeWireName(id: string): string {\n return encodeWireNameForInstance(id);\n}\n\n/**\n * Wire name disambiguated per instance: providers require UNIQUE tool names,\n * so when several live instances expose the same capability the adapter\n * appends `_at_<instanceId>` (docs/09 rule 7 — the id↔name map stays\n * authoritative).\n *\n * The result is ALWAYS ≤ 64 characters (`AS-WIRE-004`) and deterministic for a\n * given `(id, instanceId, level)` (`AS-WIRE-005`). `level` escalates the hash\n * when a catalog would otherwise emit the same name twice — see\n * {@link assignWireNames}, which owns that check; callers with a whole catalog\n * in hand should use it rather than this function directly.\n */\nexport function encodeWireNameForInstance(\n id: string,\n instanceId?: string,\n level = 0,\n): string {\n const raw = rawWireName(id, instanceId);\n // An id carrying its own \"_\" has no faithful encoding — `domain:readState__0`\n // and `domain:readState.0` both produce `domain_readState__0`, and no decoder\n // can separate them. Only reachable on the opaque `domain:` plane (the view\n // grammar forbids \"_\"), and it is what made the codec non-injective. Such ids\n // take the hashed path, which is the existing \"not decodable — consult\n // wireNameMap()\" contract rather than a new one.\n if (level === 0 && raw.length <= MAX_WIRE_NAME_LENGTH && !id.includes(\"_\")) return raw;\n const hashLength = 7 + level * 2;\n const keep = MAX_WIRE_NAME_LENGTH - SHORTENED_MARKER.length - hashLength;\n const hash = hash36(`${id}#${instanceId ?? \"\"}#${level}`, hashLength);\n return `${raw.slice(0, keep)}${SHORTENED_MARKER}${hash}`;\n}\n\nexport interface WireNameEntry {\n /** Canonical capability id. */\n id: string;\n /** Instance disambiguator, when several live instances share the id. */\n instanceId?: string;\n}\n\nexport interface WireNameAssignment {\n /** Emitted names, positionally aligned with the input entries. */\n names: string[];\n /** wireName → canonical id. Authoritative; shortened names are not decodable. */\n byName: ReadonlyMap<string, string>;\n}\n\n/**\n * Assigns wire names to a whole catalog, guaranteeing uniqueness within it\n * (`AS-WIRE-006`). Two distinct entries that collide are BOTH re-encoded at the\n * next hash level, so the outcome depends on the set of entries and not on\n * their order (`AS-WIRE-005`). Escalation is bounded; the last level appends\n * the entry's rank among the colliding keys, which terminates by construction.\n */\nexport function assignWireNames(entries: readonly WireNameEntry[]): WireNameAssignment {\n const keyOf = (e: WireNameEntry): string => `${e.id}#${e.instanceId ?? \"\"}`;\n const level = new Map<string, number>();\n const MAX_LEVEL = 3;\n\n let names = entries.map((e) => encodeWireNameForInstance(e.id, e.instanceId));\n for (let round = 0; round <= MAX_LEVEL; round++) {\n const byName = new Map<string, Set<string>>();\n entries.forEach((entry, i) => {\n const set = byName.get(names[i]!) ?? new Set<string>();\n set.add(keyOf(entry));\n byName.set(names[i]!, set);\n });\n const colliding = new Set<string>();\n for (const [, keys] of byName) {\n if (keys.size > 1) for (const key of keys) colliding.add(key);\n }\n if (colliding.size === 0) break;\n if (round === MAX_LEVEL) {\n // Terminal tie-break: rank within the sorted colliding keys is unique by\n // definition and stable for a given set.\n const ranked = [...colliding].sort();\n names = entries.map((entry, i) => {\n const rank = ranked.indexOf(keyOf(entry));\n if (rank < 0) return names[i]!;\n const suffix = `${SHORTENED_MARKER}${rank}`;\n const base = encodeWireNameForInstance(entry.id, entry.instanceId, MAX_LEVEL);\n return `${base.slice(0, MAX_WIRE_NAME_LENGTH - suffix.length)}${suffix}`;\n });\n break;\n }\n for (const key of colliding) level.set(key, (level.get(key) ?? 0) + 1);\n names = entries.map((entry) =>\n encodeWireNameForInstance(entry.id, entry.instanceId, level.get(keyOf(entry)) ?? 0),\n );\n }\n\n const byName = new Map<string, string>();\n entries.forEach((entry, i) => byName.set(names[i]!, entry.id));\n return { names, byName };\n}\n\n/**\n * Reverses `encodeWireName` for names that were encoded faithfully, and returns\n * `undefined` for every name that was not — shortened names and per-instance\n * names among them (`AS-WIRE-007`: consult `toolset.wireNameMap()` instead).\n * Returning a plausible-but-wrong canonical id would take the audit identity\n * with it, so this refuses anything it cannot re-encode byte-identically.\n *\n * Refusal is decided by what a name *is*, never by a substring it happens to\n * contain. `view:at.a.a` encodes to `view_at__a__a`, where `_at_` is the plane\n * separator meeting a segment named \"at\" — screening for the marker text cost\n * every id with an `at` or `0` segment its own faithful encoding (`AS-ID-004`).\n * Marker-bearing names are still refused, by the two checks that can tell:\n * every underscore run must be exactly two (one \".\"), and the id must re-encode\n * byte-identically.\n */\nexport function decodeWireName(name: string): string | undefined {\n const planeEnd = name.indexOf(\"_\");\n if (planeEnd <= 0) return undefined;\n const plane = name.slice(0, planeEnd);\n if (plane !== \"view\" && plane !== \"domain\") return undefined;\n const rest = name.slice(planeEnd + 1);\n // A longer run is ambiguous, not merely odd: `domain_at____x__a` is the\n // faithful encoding of BOTH `domain:at_._x.a` and `domain:at..x.a`, and\n // re-encoding cannot separate them because both produce it. Domain paths are\n // opaque, so they are the plane where a literal \"_\" can reach the codec.\n if (/_{3,}/.test(rest)) return undefined;\n const path = rest.replaceAll(\"__\", \".\");\n // The same collision in its other shape — an empty segment. `domain:at.at.`\n // shares `domain_at__at__` with `domain:at_` carrying instance `_`. The view\n // grammar already forbids empty segments; domain paths are opaque, so this is\n // where that is caught.\n if (path.split(\".\").some((segment) => segment === \"\")) return undefined;\n const id = `${plane}:${path}`;\n // The codec is injective only for grammar-valid ids with no \"_\" of their own.\n if (id.includes(\"_\") || !parseCapabilityId(id) || encodeWireName(id) !== name) return undefined;\n return id;\n}\n","import type { JsonValue } from \"./types.js\";\n\n/** Deep equality over JsonValue (order-sensitive for arrays, docs/06 rule 2). */\nexport function jsonDeepEqual(a: JsonValue | undefined, b: JsonValue | undefined): boolean {\n if (a === b) return true;\n if (a === undefined || b === undefined) return false;\n if (typeof a !== typeof b || a === null || b === null) return false;\n if (Array.isArray(a) || Array.isArray(b)) {\n return (\n Array.isArray(a) &&\n Array.isArray(b) &&\n a.length === b.length &&\n a.every((v, i) => jsonDeepEqual(v, b[i] as JsonValue))\n );\n }\n if (typeof a === \"object\" && typeof b === \"object\") {\n const ka = Object.keys(a).sort();\n const kb = Object.keys(b).sort();\n return (\n ka.length === kb.length &&\n ka.every(\n (k, i) =>\n k === kb[i] &&\n jsonDeepEqual(\n (a as Record<string, JsonValue>)[k],\n (b as Record<string, JsonValue>)[k],\n ),\n )\n );\n }\n return false;\n}\n\n/** Recursive freeze of plain data (descriptors are deep-frozen JSON). */\nexport function deepFreeze<T>(value: T): T {\n if (value !== null && typeof value === \"object\" && !Object.isFrozen(value)) {\n Object.freeze(value);\n for (const key of Object.keys(value as object)) {\n deepFreeze((value as Record<string, unknown>)[key]);\n }\n }\n return value;\n}\n\n/** Structured clone via JSON semantics (strips undefined, rejects non-JSON). */\nexport function jsonClone<T>(value: T): T {\n return value === undefined ? value : (JSON.parse(JSON.stringify(value)) as T);\n}\n\nexport function isJsonValue(value: unknown, depth = 0): value is JsonValue {\n if (depth > 64) return false;\n if (value === null) return true;\n const t = typeof value;\n if (t === \"string\" || t === \"boolean\") return true;\n if (t === \"number\") return Number.isFinite(value as number);\n if (Array.isArray(value)) return value.every((v) => isJsonValue(v, depth + 1));\n if (t === \"object\") {\n const proto = Object.getPrototypeOf(value);\n if (proto !== Object.prototype && proto !== null) return false;\n return Object.values(value as object).every(\n (v) => v === undefined || isJsonValue(v, depth + 1),\n );\n }\n return false;\n}\n\nexport function byteLength(value: unknown): number {\n const s = JSON.stringify(value);\n return s === undefined ? 0 : s.length;\n}\n\nconst ALPHABET = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\n\nexport function randomBase62(length: number): string {\n let out = \"\";\n for (let i = 0; i < length; i++) {\n out += ALPHABET[Math.floor(Math.random() * ALPHABET.length)];\n }\n return out;\n}\n\n/** Truncate a string for agent-safe messages/summaries. */\nexport function truncate(s: string, max: number): string {\n return s.length <= max ? s : s.slice(0, Math.max(0, max - 1)) + \"…\";\n}\n\n/**\n * Canonical JSON encoding (docs/18 §correction 1, D21/D22): object keys\n * sorted by UTF-16 code unit, array order significant, `undefined`\n * properties omitted, `-0` encodes as `0`, non-finite numbers are defects.\n */\nexport function canonicalJson(value: JsonValue | undefined): string {\n if (value === undefined || value === null) return \"null\";\n const t = typeof value;\n if (t === \"number\") {\n if (!Number.isFinite(value)) {\n throw new Error(\"canonicalJson: non-finite numbers are not JsonValues\");\n }\n return JSON.stringify(Object.is(value, -0) ? 0 : value);\n }\n if (t === \"string\" || t === \"boolean\") return JSON.stringify(value);\n if (Array.isArray(value)) {\n return `[${value.map((v) => canonicalJson(v ?? null)).join(\",\")}]`;\n }\n const entries = Object.keys(value as Record<string, JsonValue>)\n .sort()\n .filter((k) => (value as Record<string, JsonValue | undefined>)[k] !== undefined)\n .map((k) => `${JSON.stringify(k)}:${canonicalJson((value as Record<string, JsonValue>)[k])}`);\n return `{${entries.join(\",\")}}`;\n}\n\n/** FNV-1a 64-bit over a string, hex-encoded. Non-cryptographic: used for\n * dedupe fingerprints only — confirmation evidence matches exact values,\n * never hash-only (docs/06 rule 2). */\nexport function fnv1a64(input: string): string {\n let hash = 0xcbf29ce484222325n;\n const prime = 0x100000001b3n;\n for (let i = 0; i < input.length; i++) {\n hash ^= BigInt(input.charCodeAt(i));\n hash = (hash * prime) & 0xffffffffffffffffn;\n }\n return hash.toString(16).padStart(16, \"0\");\n}\n","import type {\n AgentConcurrency,\n AgentConsumer,\n AgentEnvironment,\n AgentProcedureEffect,\n AgentRouteInfo,\n AgentSurfaceLimits,\n JsonSchema,\n JsonValue,\n} from \"./types.js\";\nimport type {\n AgentComponentDefinition,\n AgentProcedureBinding,\n AgentProcedureExecutor,\n} from \"./definition.js\";\nimport type { AgentSchema } from \"./schema.js\";\nimport type { AgentPolicy, AgentPolicyContext } from \"./policy.js\";\nimport type { AgentCapabilityErrorPayload } from \"./errors.js\";\nimport type { AgentSurfaceEvent, EventDispatcher } from \"./events.js\";\nimport type { AuditEvent, AuditSink } from \"./audit.js\";\nimport type { ConfirmationStore } from \"./confirmation.js\";\nimport type { AgentInvocationResult } from \"./invocation-types.js\";\nimport { formatViewCapabilityId } from \"./ids.js\";\nimport { jsonClone } from \"./utils.js\";\n\nexport type ConfirmationLevel = \"never\" | \"optional\" | \"required\";\nexport type AuditLevel = \"none\" | \"metadata\" | \"full\";\n\nexport interface ObservationRuntime {\n kind: \"observation\";\n name: string;\n capabilityId: string;\n description: string;\n outputSchema: AgentSchema<any>;\n jsonSchema: JsonSchema;\n meta?: Record<string, JsonValue>;\n timeoutMs?: number;\n policies: AgentPolicy[];\n auditLevel: AuditLevel;\n}\n\nexport interface ActionRuntime {\n kind: \"action\";\n name: string;\n capabilityId: string;\n description: string;\n inputSchema: AgentSchema<any>;\n inputJsonSchema: JsonSchema;\n outputSchema?: AgentSchema<any>;\n outputJsonSchema?: JsonSchema;\n effect: \"local-state\" | \"navigation\";\n idempotent: boolean;\n reversible: boolean;\n confirmation: ConfirmationLevel;\n auditLevel: AuditLevel;\n meta?: Record<string, JsonValue>;\n timeoutMs?: number;\n policies: AgentPolicy[];\n concurrency?: AgentConcurrency;\n}\n\nexport interface ProcedureRuntime {\n kind: \"procedure\";\n binding: AgentProcedureBinding;\n capabilityId: string; // \"domain:\" + path\n path: string;\n effect: AgentProcedureEffect;\n requiresApproval: boolean;\n baseDescription: string;\n fullInputSchema: JsonSchema;\n reducedInputSchema: JsonSchema;\n outputJsonSchema?: JsonSchema;\n boundKeys: string[];\n lockedKeys: string[];\n overridableKeys: Set<string>;\n confirmationFloor: ConfirmationLevel;\n idempotent: boolean;\n auditLevel: AuditLevel;\n meta?: Record<string, JsonValue>;\n policies: AgentPolicy[];\n contextLink?: { type: string; instanceId: string };\n concurrency?: AgentConcurrency;\n}\n\nexport interface InFlightEntry {\n /**\n * Owner unregistered. Default: aborts the handler signal and settles\n * COMPONENT_UNMOUNTED (first settle wins, D16). Navigation-effect entries\n * only abort the signal — the invocation settles on handler settlement,\n * timeout, or external cancel (D23).\n */\n onUnregister(): void;\n /** Registry disposed: always aborts and settles CANCELLED. */\n onDispose(): void;\n}\n\nexport interface InternalRegistration {\n id: string;\n key: string; // `${type}\\u0000${instanceId}`\n type: string;\n instanceId: string;\n description: string;\n parent?: { type: string; instanceId: string };\n meta?: Record<string, JsonValue>;\n internal: Readonly<Record<string, unknown>>;\n origin: string;\n priority: number;\n /** Live definition object: handlers/when are read through it (D3). */\n definition: AgentComponentDefinition;\n componentPolicies: AgentPolicy[];\n observations: Map<string, ObservationRuntime>;\n actions: Map<string, ActionRuntime>;\n procedures: ProcedureRuntime[];\n /** Procedure-only registrations never appear in snapshot.components. */\n procedureOnly: boolean;\n status: \"active\" | \"unregistered\";\n enabled: boolean;\n availabilityOverrides: Map<string, { available: boolean; reason?: string }>;\n inFlight: Set<InFlightEntry>;\n /**\n * Concurrency groups (D25), created lazily and deleted when they fall idle,\n * so the map is bounded by the number of *currently contended* groups rather\n * than by the number of capabilities ever invoked.\n */\n concurrencyGroups: Map<string, ConcurrencyGroup>;\n}\n\n/** One FIFO admission group. `max` is 1 for every mode except `parallel`. */\nexport interface ConcurrencyGroup {\n running: number;\n max: number;\n depth: number;\n waiting: Array<() => void>;\n}\n\nexport interface Tombstone {\n registrationId: string;\n type: string;\n instanceId: string;\n capabilityIds: Set<string>;\n expiresAt: number;\n}\n\n/** Dedupe entries carry the request fingerprint (D22): join/return only on\n * a match; a mismatch fails INVOCATION_CONFLICT without touching the entry. */\nexport type DedupeEntry =\n | { kind: \"inflight\"; fingerprint: string; promise: Promise<AgentInvocationResult> }\n | { kind: \"terminal\"; fingerprint: string; result: AgentInvocationResult; expiresAt: number };\n\n/** Bounded observation admission state (D24). `waiting` is arrival-ordered;\n * a release wakes the first waiter whose consumer is under its cap. */\nexport interface ObservationAdmission {\n total: number;\n perConsumer: Map<string, number>;\n waiting: Array<{ consumerKey: string; admit: (admitted: boolean) => void }>;\n}\n\nexport interface RegistryInternals {\n environment: AgentEnvironment;\n limits: AgentSurfaceLimits;\n surfaceId: string;\n version: number;\n registrations: Map<string, InternalRegistration>;\n byKey: Map<string, string>;\n tombstones: Map<string, Tombstone>;\n /** Keyed by `${consumerKey} ${invocationId}` (D22). */\n dedupe: Map<string, DedupeEntry>;\n observationAdmission: ObservationAdmission;\n dispatcher: EventDispatcher;\n confirmations: ConfirmationStore;\n executor: AgentProcedureExecutor | undefined;\n disposed: boolean;\n registryPolicies: AgentPolicy[];\n auditSink: AuditSink | undefined;\n contextFn: (() => Record<string, unknown>) | undefined;\n routeFn: (() => AgentRouteInfo | undefined) | undefined;\n now: () => number;\n bumpVersion(): void;\n emit(event: AgentSurfaceEvent): void;\n recordAudit(event: Omit<AuditEvent, \"at\">): void;\n host(): Record<string, unknown>;\n devWarn(...args: unknown[]): void;\n devError(...args: unknown[]): void;\n}\n\n/**\n * Internal seam, deliberately not part of `AgentSurfaceRegistry`: the registry\n * attaches its environment-gated dev logger here so a same-package adapter can\n * report a repair it performed without either widening the public interface or\n * inventing a second environment gate. Symbol-keyed and non-enumerable, so it\n * stays invisible to spreads, `Object.keys`, and serialization.\n */\nexport const DEV_WARN: unique symbol = Symbol(\"agent-surface.dev-warn\");\n\nexport interface DevWarnCarrier {\n [DEV_WARN]?: (...args: unknown[]) => void;\n}\n\n/**\n * Internal seam for the developer-tool projection in `./explain.ts`, attached\n * the same way and for the same reason as `DEV_WARN`: `explainSurface()` needs\n * the policy chains and availability hooks that `snapshot()` reads, and neither\n * belongs on the public `AgentSurfaceRegistry`.\n *\n * Symbol-keyed and non-enumerable, so it stays invisible to spreads,\n * `Object.keys`, and serialization. Nothing agent-facing reads it: the only\n * consumer is `@agent-surface/core/explain`, which is a separate entry point\n * precisely so no adapter can reach it by importing the package root\n * (AS-EXPLAIN-004).\n */\nexport const INTERNALS: unique symbol = Symbol(\"agent-surface.internals\");\n\nexport interface InternalsCarrier {\n [INTERNALS]?: RegistryInternals;\n}\n\n/** Marker for defects that must throw out of invoke() in development. */\nexport class DevDefectError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"AgentSurfaceDevDefectError\";\n }\n}\n\nexport function componentKey(type: string, instanceId: string): string {\n return `${type}\\u0000${instanceId}`;\n}\n\nconst CONFIRMATION_RANK: Record<ConfirmationLevel, number> = {\n never: 0,\n optional: 1,\n required: 2,\n};\n\nexport function maxConfirmation(...levels: ConfirmationLevel[]): ConfirmationLevel {\n return levels.reduce((acc, l) => (CONFIRMATION_RANK[l] > CONFIRMATION_RANK[acc] ? l : acc), \"never\");\n}\n\nexport function defaultConfirmationFor(effect: AgentProcedureEffect): ConfirmationLevel {\n switch (effect) {\n case \"server-query\":\n return \"never\";\n case \"server-mutation\":\n return \"optional\";\n case \"external-side-effect\":\n case \"destructive\":\n return \"required\";\n }\n}\n\nexport function defaultAuditFor(effect: AgentProcedureEffect): AuditLevel {\n return effect === \"external-side-effect\" || effect === \"destructive\" ? \"full\" : \"metadata\";\n}\n\nlet registrationCounter = 0;\nexport function nextRegistrationId(random: () => string): string {\n registrationCounter += 1;\n return `reg_${registrationCounter.toString(36).padStart(4, \"0\")}${random()}`;\n}\n\n/** Copies the structural descriptor out of a definition (frozen per D2). */\nexport function normalizeRegistration(\n def: AgentComponentDefinition,\n id: string,\n): InternalRegistration {\n const instanceId = def.instanceId ?? \"default\";\n const observations = new Map<string, ObservationRuntime>();\n for (const [name, obs] of Object.entries(def.observations ?? {})) {\n observations.set(name, {\n kind: \"observation\",\n name,\n capabilityId: formatViewCapabilityId(def.type, name),\n description: obs.description,\n outputSchema: obs.output,\n jsonSchema: jsonClone(obs.output.jsonSchema),\n meta: obs.meta ? jsonClone(obs.meta) : undefined,\n timeoutMs: obs.timeoutMs,\n policies: [...(obs.policies ?? [])],\n auditLevel: \"none\",\n });\n }\n const actions = new Map<string, ActionRuntime>();\n for (const [name, act] of Object.entries(def.actions ?? {})) {\n actions.set(name, {\n kind: \"action\",\n name,\n capabilityId: formatViewCapabilityId(def.type, name),\n description: act.description,\n inputSchema: act.input,\n inputJsonSchema: jsonClone(act.input.jsonSchema),\n outputSchema: act.output,\n outputJsonSchema: act.output ? jsonClone(act.output.jsonSchema) : undefined,\n effect: act.effect,\n idempotent: act.idempotent ?? false,\n reversible: act.reversible ?? true,\n confirmation: act.confirmation ?? \"never\",\n auditLevel: act.audit ?? \"metadata\",\n meta: act.meta ? jsonClone(act.meta) : undefined,\n timeoutMs: act.timeoutMs,\n policies: [...(act.policies ?? [])],\n concurrency: act.concurrency,\n });\n }\n const hasView = observations.size > 0 || actions.size > 0;\n const procedures: ProcedureRuntime[] = (def.procedures ?? []).map((binding) => {\n const boundKeys = [...binding.boundKeys];\n const overridable = new Set(binding.config.overridableFields ?? []);\n const lockedKeys = binding.lockedKeys\n ? [...binding.lockedKeys]\n : boundKeys.filter((k) => !overridable.has(k));\n const effect = binding.ref.effect;\n return {\n kind: \"procedure\",\n binding,\n capabilityId: binding.ref.id,\n path: binding.ref.path,\n effect,\n requiresApproval: binding.ref.requiresApproval === true,\n baseDescription: binding.ref.description,\n fullInputSchema: jsonClone(binding.ref.inputSchema),\n reducedInputSchema: jsonClone(binding.reducedInputSchema),\n outputJsonSchema: binding.ref.outputSchema ? jsonClone(binding.ref.outputSchema) : undefined,\n boundKeys,\n lockedKeys,\n overridableKeys: overridable,\n confirmationFloor: maxConfirmation(\n defaultConfirmationFor(effect),\n binding.config.confirmation ?? \"never\",\n binding.ref.requiresApproval === true ? \"required\" : \"never\",\n ),\n idempotent: effect === \"server-query\",\n auditLevel: defaultAuditFor(effect),\n meta: binding.config.meta ? jsonClone(binding.config.meta) : undefined,\n policies: [...(binding.config.policies ?? [])],\n contextLink:\n binding.contextLink ?? (hasView ? { type: def.type, instanceId } : undefined),\n concurrency: binding.config.concurrency,\n };\n });\n\n return {\n id,\n key: componentKey(def.type, instanceId),\n type: def.type,\n instanceId,\n description: def.description,\n parent: def.parent\n ? { type: def.parent.type, instanceId: def.parent.instanceId ?? \"default\" }\n : undefined,\n meta: def.meta ? jsonClone(def.meta) : undefined,\n internal: Object.freeze({ ...(def.internal ?? {}) }),\n origin: def.origin ?? \"first-party\",\n priority: def.priority ?? 0,\n definition: def,\n componentPolicies: [...(def.policies ?? [])],\n observations,\n actions,\n procedures,\n procedureOnly: !hasView && procedures.length > 0,\n status: \"active\",\n enabled: def.enabled !== false,\n availabilityOverrides: new Map(),\n inFlight: new Set(),\n concurrencyGroups: new Map(),\n };\n}\n\n/**\n * Resolve the concurrency group for a capability (D25). Actions default to\n * `{mode:\"instance\"}` — one queue for the whole registration, so unrelated\n * actions on one component cannot interleave. Procedure references default to\n * one group per procedure identity per referencing registration: conservative\n * for repeat calls of the same domain operation, and never coupled to view\n * actions that happen to live on the same component.\n */\nexport function concurrencyGroupFor(\n cap: ActionRuntime | ProcedureRuntime,\n limits: AgentSurfaceLimits,\n): { key: string; max: number; depth: number } {\n const declared: AgentConcurrency | undefined =\n cap.kind === \"action\" ? cap.concurrency : cap.concurrency;\n const fallbackDepth = limits.actionQueueDepth;\n if (declared === undefined) {\n return cap.kind === \"action\"\n ? { key: \"instance\", max: 1, depth: fallbackDepth }\n : { key: `proc:${cap.capabilityId}`, max: 1, depth: fallbackDepth };\n }\n const depth = declared.queueDepth ?? fallbackDepth;\n switch (declared.mode) {\n case \"instance\":\n return { key: \"instance\", max: 1, depth };\n case \"capability\":\n return { key: `cap:${cap.capabilityId}`, max: 1, depth };\n case \"key\":\n return { key: `key:${declared.key}`, max: 1, depth };\n case \"parallel\":\n return { key: `par:${cap.capabilityId}`, max: declared.max, depth };\n }\n}\n\nexport type CapabilityRuntime = ObservationRuntime | ActionRuntime | ProcedureRuntime;\n\n/** Live `when`/`unavailableReason` lookup through the definition (D3). */\nfunction liveAvailabilityHooks(\n reg: InternalRegistration,\n cap: CapabilityRuntime,\n): { when?: () => boolean; unavailableReason?: string | (() => string) } {\n if (cap.kind === \"observation\") {\n const live = reg.definition.observations?.[cap.name];\n return { when: live?.when, unavailableReason: live?.unavailableReason };\n }\n if (cap.kind === \"action\") {\n const live = reg.definition.actions?.[cap.name];\n return { when: live?.when, unavailableReason: live?.unavailableReason };\n }\n return { when: cap.binding.config.when, unavailableReason: cap.binding.config.unavailableReason };\n}\n\nexport interface Availability {\n available: boolean;\n reason?: string;\n}\n\n/** Availability formula from docs/03 §availability (policies applied separately). */\nexport function computeAvailability(\n internals: RegistryInternals,\n reg: InternalRegistration,\n cap: CapabilityRuntime,\n): Availability {\n if (reg.status !== \"active\") {\n return { available: false, reason: \"component-unregistered\" };\n }\n if (!reg.enabled) {\n return { available: false, reason: \"component-disabled\" };\n }\n const overrideKey = cap.kind === \"procedure\" ? cap.path : cap.name;\n const override =\n reg.availabilityOverrides.get(overrideKey) ?? reg.availabilityOverrides.get(cap.capabilityId);\n if (override && override.available === false) {\n return { available: false, reason: override.reason ?? \"unavailable\" };\n }\n const hooks = liveAvailabilityHooks(reg, cap);\n if (hooks.when) {\n let result: boolean;\n try {\n result = hooks.when() !== false;\n } catch (err) {\n internals.devWarn(\n `[agent-surface] when() threw for ${cap.capabilityId}; treating as unavailable`,\n err,\n );\n return { available: false, reason: \"when-error\" };\n }\n if (!result) {\n let reason = \"Currently unavailable\";\n const ur = hooks.unavailableReason;\n try {\n if (typeof ur === \"function\") reason = ur();\n else if (typeof ur === \"string\") reason = ur;\n } catch {\n /* keep fallback reason */\n }\n return { available: false, reason };\n }\n }\n return { available: true };\n}\n\nexport function policiesFor(\n internals: RegistryInternals,\n reg: InternalRegistration,\n cap: CapabilityRuntime,\n): AgentPolicy[] {\n return [...internals.registryPolicies, ...reg.componentPolicies, ...cap.policies];\n}\n\nexport function buildPolicyContext(\n internals: RegistryInternals,\n reg: InternalRegistration,\n cap: CapabilityRuntime,\n consumer: AgentConsumer,\n host: Record<string, unknown>,\n): AgentPolicyContext {\n return {\n capabilityId: cap.capabilityId,\n plane: cap.kind === \"procedure\" ? \"domain\" : \"view\",\n kind: cap.kind,\n effect: cap.kind === \"observation\" ? \"read\" : cap.effect,\n registrationId: reg.id,\n consumer,\n host,\n meta: { component: reg.meta, capability: cap.meta },\n internal: reg.internal,\n environment: internals.environment,\n now: () => internals.now(),\n };\n}\n\n/** Runtime-normalized consumer identity (D22): the invocation namespace. */\nexport function consumerKeyOf(consumer: AgentConsumer): string {\n return `${consumer.kind}:${consumer.id}`;\n}\n\nexport function pruneTombstones(internals: RegistryInternals): void {\n const now = internals.now();\n for (const [id, tomb] of internals.tombstones) {\n if (tomb.expiresAt <= now) internals.tombstones.delete(id);\n }\n while (internals.tombstones.size > internals.limits.tombstoneSize) {\n const oldest = internals.tombstones.keys().next().value;\n if (oldest === undefined) break;\n internals.tombstones.delete(oldest);\n }\n}\n\nexport function addTombstone(internals: RegistryInternals, reg: InternalRegistration): void {\n const capabilityIds = new Set<string>();\n for (const obs of reg.observations.values()) capabilityIds.add(obs.capabilityId);\n for (const act of reg.actions.values()) capabilityIds.add(act.capabilityId);\n for (const proc of reg.procedures) capabilityIds.add(proc.capabilityId);\n internals.tombstones.set(reg.id, {\n registrationId: reg.id,\n type: reg.type,\n instanceId: reg.instanceId,\n capabilityIds,\n expiresAt: internals.now() + internals.limits.tombstoneTtlMs,\n });\n pruneTombstones(internals);\n}\n","import type {\n AgentConsumer,\n AgentProcedureEffect,\n AgentRouteInfo,\n JsonSchema,\n JsonValue,\n} from \"./types.js\";\nimport type { RegistryInternals, InternalRegistration, ConfirmationLevel } from \"./internal.js\";\nimport {\n buildPolicyContext,\n computeAvailability,\n policiesFor,\n} from \"./internal.js\";\nimport { evaluateDiscovery } from \"./policy.js\";\nimport { byteLength, deepFreeze } from \"./utils.js\";\n\nexport interface SnapshotContext {\n consumer?: AgentConsumer; // default: {\"id\":\"anonymous\",\"kind\":\"embedded\"}\n /** Component-type prefixes to include, e.g. [\"devices\"]. Default: all. */\n scope?: string[];\n /** Include visible-disabled capabilities. Default true. */\n includeUnavailable?: boolean;\n /** [Experimental] Truncation budget. */\n budget?: { maxComponents?: number; maxBytes?: number };\n}\n\nexport interface AgentSurfaceSnapshot {\n surfaceId: string;\n surfaceVersion: string;\n capturedAt: string; // ISO-8601\n route?: AgentRouteInfo;\n components: AgentComponentDescriptor[];\n /** Domain references, top-level (planes are not nested into each other). */\n procedures: AgentProcedureDescriptor[];\n /** [Experimental] Present iff a budget truncated the snapshot. */\n truncated?: { droppedComponents: number };\n /**\n * [Experimental] Present iff a configured scope floor refused part of a\n * requested scope (D27) — set by the adapter, never by `snapshot()`, which\n * has no floor to intersect against. Empty `components` alongside this marker\n * means the request fell outside the floor, not that the surface is empty.\n */\n scopeRejected?: { prefixes: string[] };\n}\n\nexport interface AgentComponentDescriptor {\n type: string;\n instanceId: string;\n registrationId: string;\n description: string;\n parent?: { type: string; instanceId: string };\n meta?: Record<string, JsonValue>;\n observations: AgentObservationDescriptor[];\n actions: AgentActionDescriptor[];\n}\n\nexport interface AgentObservationDescriptor {\n capabilityId: string; // \"view:devices.table.readState\"\n name: string; // \"readState\"\n description: string;\n outputSchema: JsonSchema;\n available: boolean;\n unavailableReason?: string;\n meta?: Record<string, JsonValue>;\n}\n\nexport interface AgentActionDescriptor {\n capabilityId: string;\n name: string;\n description: string;\n inputSchema: JsonSchema;\n outputSchema?: JsonSchema;\n effect: \"local-state\" | \"navigation\";\n idempotent: boolean;\n reversible: boolean;\n confirmation: \"never\" | \"optional\" | \"required\";\n available: boolean;\n unavailableReason?: string;\n meta?: Record<string, JsonValue>;\n}\n\nexport interface AgentProcedureDescriptor {\n procedureId: string; // \"domain:devices.disable\"\n /**\n * The manifest description. Stable across snapshots — the contextual\n * `describe()` output is `contextualNote` and is never folded in here (D28).\n */\n description: string;\n /** Volatile: this snapshot's contextual `describe()` output, if any. */\n contextualNote?: string;\n /** Agent-facing (reduced) input schema per binding rule 1 (docs/05). */\n inputSchema: JsonSchema;\n outputSchema?: JsonSchema;\n effect: AgentProcedureEffect;\n confirmation: ConfirmationLevel; // max(manifest, reference)\n available: boolean;\n unavailableReason?: string;\n boundFields: Array<{ path: string; locked: boolean; source: \"ui-state\" }>;\n /** The registration that contributed this reference (staleness token). */\n registrationId: string;\n /** Optional link to the owning view component. */\n context?: { type: string; instanceId: string };\n meta?: Record<string, JsonValue>;\n}\n\nexport type AgentCapabilityDescriptorUnion =\n | AgentObservationDescriptor\n | AgentActionDescriptor\n | AgentProcedureDescriptor;\n\n/* The three helpers below are shared with `./explain.ts` so the developer\n * projection traverses the surface in exactly the same order, with exactly the\n * same scope and consumer defaults, as the agent-facing snapshot. They are not\n * re-exported from the package root. */\n\nexport const DEFAULT_CONSUMER: AgentConsumer = { id: \"anonymous\", kind: \"embedded\" };\n\nexport function matchesScope(type: string, scope: string[] | undefined): boolean {\n if (!scope || scope.length === 0) return true;\n return scope.some((prefix) => type === prefix || type.startsWith(`${prefix}.`));\n}\n\nexport function sortRegistrations(regs: InternalRegistration[]): InternalRegistration[] {\n return regs.sort((a, b) => {\n if (a.priority !== b.priority) return b.priority - a.priority;\n if (a.type !== b.type) return a.type < b.type ? -1 : 1;\n return a.instanceId < b.instanceId ? -1 : a.instanceId > b.instanceId ? 1 : 0;\n });\n}\n\n/**\n * Synchronous, side-effect-free catalog projection (docs/03 §snapshot, D5):\n * never runs read() handlers, never awaits, never serializes `internal`.\n */\nexport function createSnapshot(\n internals: RegistryInternals,\n ctx?: SnapshotContext,\n): AgentSurfaceSnapshot {\n const consumer = ctx?.consumer ?? DEFAULT_CONSUMER;\n const includeUnavailable = ctx?.includeUnavailable ?? true;\n const host = internals.host();\n\n const regs = sortRegistrations(\n [...internals.registrations.values()].filter((r) => r.status === \"active\"),\n );\n\n const components: AgentComponentDescriptor[] = [];\n const componentPriority: number[] = [];\n const procedures: AgentProcedureDescriptor[] = [];\n\n for (const reg of regs) {\n const inScopeForComponents = matchesScope(reg.type, ctx?.scope);\n\n if (!reg.procedureOnly && inScopeForComponents) {\n const observations: AgentObservationDescriptor[] = [];\n const actions: AgentActionDescriptor[] = [];\n let definedCount = 0;\n let hiddenCount = 0;\n\n for (const obs of reg.observations.values()) {\n definedCount += 1;\n const chain = policiesFor(internals, reg, obs);\n const policyCtx = buildPolicyContext(internals, reg, obs, consumer, host);\n const decision = evaluateDiscovery(chain, policyCtx);\n if (decision.decision === \"hide\") {\n hiddenCount += 1;\n continue;\n }\n const availability = computeAvailability(internals, reg, obs);\n const available = availability.available && decision.decision === \"expose\";\n const reason =\n decision.decision === \"disable\" ? decision.reason : availability.reason;\n if (!available && !includeUnavailable) continue;\n observations.push({\n capabilityId: obs.capabilityId,\n name: obs.name,\n description: obs.description,\n outputSchema: obs.jsonSchema,\n available,\n ...(available ? {} : { unavailableReason: reason }),\n ...(obs.meta ? { meta: obs.meta } : {}),\n });\n }\n\n for (const act of reg.actions.values()) {\n definedCount += 1;\n const chain = policiesFor(internals, reg, act);\n const policyCtx = buildPolicyContext(internals, reg, act, consumer, host);\n const decision = evaluateDiscovery(chain, policyCtx);\n if (decision.decision === \"hide\") {\n hiddenCount += 1;\n continue;\n }\n const availability = computeAvailability(internals, reg, act);\n const available = availability.available && decision.decision === \"expose\";\n const reason =\n decision.decision === \"disable\" ? decision.reason : availability.reason;\n if (!available && !includeUnavailable) continue;\n actions.push({\n capabilityId: act.capabilityId,\n name: act.name,\n description: act.description,\n inputSchema: act.inputJsonSchema,\n ...(act.outputJsonSchema ? { outputSchema: act.outputJsonSchema } : {}),\n effect: act.effect,\n idempotent: act.idempotent,\n reversible: act.reversible,\n confirmation: act.confirmation,\n available,\n ...(available ? {} : { unavailableReason: reason }),\n ...(act.meta ? { meta: act.meta } : {}),\n });\n }\n\n // Deny-by-default: a component whose every capability is policy-hidden\n // is itself hidden (existence is information, docs/06).\n const allHidden = definedCount > 0 && hiddenCount === definedCount;\n if (!allHidden) {\n components.push({\n type: reg.type,\n instanceId: reg.instanceId,\n registrationId: reg.id,\n description: reg.description,\n ...(reg.parent ? { parent: reg.parent } : {}),\n ...(reg.meta ? { meta: reg.meta } : {}),\n observations,\n actions,\n });\n componentPriority.push(reg.priority);\n }\n }\n\n for (const proc of reg.procedures) {\n const scopeMatch = proc.contextLink\n ? matchesScope(proc.contextLink.type, ctx?.scope)\n : matchesScope(proc.path, ctx?.scope);\n if (!scopeMatch) continue;\n const chain = policiesFor(internals, reg, proc);\n const policyCtx = buildPolicyContext(internals, reg, proc, consumer, host);\n const decision = evaluateDiscovery(chain, policyCtx);\n if (decision.decision === \"hide\") continue;\n const availability = computeAvailability(internals, reg, proc);\n const available = availability.available && decision.decision === \"expose\";\n const reason = decision.decision === \"disable\" ? decision.reason : availability.reason;\n if (!available && !includeUnavailable) continue;\n // D28: the stable description and the volatile note are kept apart here,\n // and merged back only for hosts that have not migrated yet.\n let contextualNote: string | undefined;\n const describe = proc.binding.config.describe;\n if (describe) {\n try {\n const contextual = describe();\n if (contextual) contextualNote = contextual;\n } catch {\n /* describe() must not break the snapshot */\n }\n }\n procedures.push({\n procedureId: proc.capabilityId,\n // Never merged with `contextualNote` (D28): the manifest text is the\n // stable half, and folding volatile text in is what churned the\n // provider's cached prompt prefix.\n description: proc.baseDescription,\n ...(contextualNote !== undefined ? { contextualNote } : {}),\n inputSchema: proc.reducedInputSchema,\n ...(proc.outputJsonSchema ? { outputSchema: proc.outputJsonSchema } : {}),\n effect: proc.effect,\n confirmation: proc.confirmationFloor,\n available,\n ...(available ? {} : { unavailableReason: reason }),\n boundFields: proc.boundKeys.map((path) => ({\n path,\n locked: proc.lockedKeys.includes(path),\n source: \"ui-state\" as const,\n })),\n registrationId: reg.id,\n ...(proc.contextLink ? { context: proc.contextLink } : {}),\n ...(proc.meta ? { meta: proc.meta } : {}),\n });\n }\n }\n\n // Budgets (Experimental): drop lowest-priority components first, loudly.\n let dropped = 0;\n const budget = ctx?.budget;\n if (budget?.maxComponents !== undefined && components.length > budget.maxComponents) {\n dropped += components.length - budget.maxComponents;\n dropLowestPriority(components, componentPriority, components.length - budget.maxComponents);\n }\n if (budget?.maxBytes !== undefined) {\n while (components.length > 0 && byteLength(components) > budget.maxBytes) {\n dropLowestPriority(components, componentPriority, 1);\n dropped += 1;\n }\n }\n\n const snapshot: AgentSurfaceSnapshot = {\n surfaceId: internals.surfaceId,\n surfaceVersion: String(internals.version),\n capturedAt: new Date(internals.now()).toISOString(),\n ...(internals.routeFn?.() ? { route: internals.routeFn() } : {}),\n components,\n procedures,\n ...(dropped > 0 ? { truncated: { droppedComponents: dropped } } : {}),\n };\n return deepFreeze(snapshot);\n}\n\nfunction dropLowestPriority(\n components: AgentComponentDescriptor[],\n priorities: number[],\n count: number,\n): void {\n for (let n = 0; n < count && components.length > 0; n++) {\n let lowestIndex = 0;\n for (let i = 1; i < priorities.length; i++) {\n if ((priorities[i] ?? 0) <= (priorities[lowestIndex] ?? 0)) lowestIndex = i;\n }\n components.splice(lowestIndex, 1);\n priorities.splice(lowestIndex, 1);\n }\n}\n"],"mappings":"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","names":["byName"]}
@@ -0,0 +1,103 @@
1
+ import { D as DiscoveryDecision, A as AgentRouteInfo, a as AgentConsumer, b as AgentSurfaceRegistry, S as SnapshotContext } from './registry-DmWUlnta.js';
2
+
3
+ /**
4
+ * `explainSurface()` — the developer projection.
5
+ *
6
+ * `snapshot()` answers "what may this agent call right now". It bakes policy
7
+ * *outcomes*: a `hide` decision deletes the capability outright, leaving no
8
+ * trace of which policy did it. That is correct for the agent boundary — the
9
+ * existence of a hidden capability is itself information (docs/06) — and it is
10
+ * exactly wrong for the developer staring at a surface that is missing a
11
+ * capability they know they registered.
12
+ *
13
+ * This module answers the other question: *why*. It reports every capability
14
+ * the registry holds, including the ones the snapshot omits, each with the
15
+ * policy chain that judged it and that chain's per-policy votes.
16
+ *
17
+ * ## This is never agent-facing
18
+ *
19
+ * It lives behind its own entry point (`@agent-surface/core/explain`) and is
20
+ * deliberately absent from the package root, so no adapter can reach it by
21
+ * importing `@agent-surface/core` (AS-EXPLAIN-004). Nothing here may be piped
22
+ * into a toolset, a transport, or a model prompt: doing so re-leaks precisely
23
+ * the existence that `hide` exists to withhold. Developer tools, tests, and
24
+ * CLIs only.
25
+ */
26
+
27
+ /** Which layer of the chain contributed a policy (docs/06 §composition). */
28
+ type PolicyScope = "registry" | "component" | "capability";
29
+ interface PolicyAttribution {
30
+ /** `AgentPolicy.name` — built-ins are `authenticated`, `rate-limit`, … */
31
+ name: string;
32
+ scope: PolicyScope;
33
+ /** Which pipeline hooks this policy implements. */
34
+ phases: Array<"discovery" | "authorize" | "invoke">;
35
+ /** This policy's own vote. Absent when it has no `onDiscovery`. */
36
+ discovery?: DiscoveryDecision;
37
+ /**
38
+ * `onDiscovery` threw. `evaluateDiscovery` fails closed, so the vote is
39
+ * recorded as `hide` — but a throwing discovery policy is a defect, and the
40
+ * snapshot alone cannot tell you it happened.
41
+ */
42
+ threw?: boolean;
43
+ /** Carries the `requireConfirmation` escalation marker. */
44
+ confirmationEscalation?: boolean;
45
+ }
46
+ interface CapabilityExplanation {
47
+ capabilityId: string;
48
+ kind: "observation" | "action" | "procedure";
49
+ plane: "view" | "domain";
50
+ /**
51
+ * The manifest description. Carried here because a hidden capability has no
52
+ * snapshot entry to read it from, and an id alone does not tell a developer
53
+ * which of their capabilities went missing.
54
+ */
55
+ description: string;
56
+ registrationId: string;
57
+ component: {
58
+ type: string;
59
+ instanceId: string;
60
+ };
61
+ /**
62
+ * What `snapshot()` did with this capability for the same context:
63
+ * `hide` means absent from the snapshot entirely.
64
+ */
65
+ outcome: "expose" | "disable" | "hide";
66
+ /** The reason a non-exposed capability carries, matching the snapshot's. */
67
+ reason?: string;
68
+ /** The full chain, registry-outermost first — the order policies run in. */
69
+ policies: PolicyAttribution[];
70
+ /**
71
+ * The `when()`/override verdict on its own. Authority hides, state discloses
72
+ * (D11/D12): keeping these apart is what lets you tell "a policy removed it"
73
+ * from "the UI says not right now".
74
+ */
75
+ availability: {
76
+ available: boolean;
77
+ reason?: string;
78
+ };
79
+ }
80
+ interface SurfaceExplanation {
81
+ surfaceId: string;
82
+ surfaceVersion: string;
83
+ capturedAt: string;
84
+ route?: AgentRouteInfo;
85
+ /** The consumer this explanation was computed for. */
86
+ consumer: AgentConsumer;
87
+ /** Every capability held by the registry, hidden ones included. */
88
+ capabilities: CapabilityExplanation[];
89
+ }
90
+ /**
91
+ * Developer projection of the surface: every capability, hidden included, with
92
+ * the policy chain that judged it.
93
+ *
94
+ * Honours `ctx.scope` and `ctx.consumer` so it lines up with the snapshot you
95
+ * are debugging. `includeUnavailable` and `budget` are ignored by design —
96
+ * withholding from an explanation is the one thing it must never do.
97
+ *
98
+ * @throws if `registry` was not produced by `createAgentSurfaceRegistry`, or
99
+ * has been disposed.
100
+ */
101
+ declare function explainSurface(registry: AgentSurfaceRegistry, ctx?: SnapshotContext): SurfaceExplanation;
102
+
103
+ export { type CapabilityExplanation, type PolicyAttribution, type PolicyScope, type SurfaceExplanation, explainSurface };
@@ -0,0 +1,123 @@
1
+ import {
2
+ CONFIRMATION_ESCALATION,
3
+ DEFAULT_CONSUMER,
4
+ INTERNALS,
5
+ buildPolicyContext,
6
+ computeAvailability,
7
+ deepFreeze,
8
+ matchesScope,
9
+ sortRegistrations
10
+ } from "./chunk-77YRWAXY.js";
11
+
12
+ // src/explain.ts
13
+ function phasesOf(policy) {
14
+ const phases = [];
15
+ if (policy.onDiscovery) phases.push("discovery");
16
+ if (policy.onAuthorize) phases.push("authorize");
17
+ if (policy.onInvoke) phases.push("invoke");
18
+ return phases;
19
+ }
20
+ function attribute(chain, boundaries, ctx) {
21
+ const policies = [];
22
+ let hidden = false;
23
+ let disable;
24
+ chain.forEach((policy, index) => {
25
+ const scope = index < boundaries.registry ? "registry" : index < boundaries.registry + boundaries.component ? "component" : "capability";
26
+ const attribution = {
27
+ name: policy.name,
28
+ scope,
29
+ phases: phasesOf(policy)
30
+ };
31
+ if (policy[CONFIRMATION_ESCALATION]) {
32
+ attribution.confirmationEscalation = true;
33
+ }
34
+ if (policy.onDiscovery) {
35
+ let decision;
36
+ try {
37
+ decision = policy.onDiscovery(ctx);
38
+ } catch {
39
+ decision = { decision: "hide" };
40
+ attribution.threw = true;
41
+ }
42
+ attribution.discovery = decision;
43
+ if (decision.decision === "hide") hidden = true;
44
+ else if (decision.decision === "disable" && !disable) disable = decision;
45
+ }
46
+ policies.push(attribution);
47
+ });
48
+ return {
49
+ policies,
50
+ decision: hidden ? { decision: "hide" } : disable ?? { decision: "expose" }
51
+ };
52
+ }
53
+ function explainCapability(internals, reg, cap, consumer, host) {
54
+ const chain = [...internals.registryPolicies, ...reg.componentPolicies, ...cap.policies];
55
+ const ctx = buildPolicyContext(internals, reg, cap, consumer, host);
56
+ const { policies, decision } = attribute(
57
+ chain,
58
+ { registry: internals.registryPolicies.length, component: reg.componentPolicies.length },
59
+ ctx
60
+ );
61
+ const availability = computeAvailability(internals, reg, cap);
62
+ const available = availability.available && decision.decision === "expose";
63
+ const reason = decision.decision === "disable" ? decision.reason : availability.reason;
64
+ const outcome = decision.decision === "hide" ? "hide" : available ? "expose" : "disable";
65
+ return {
66
+ capabilityId: cap.capabilityId,
67
+ kind: cap.kind,
68
+ plane: cap.kind === "procedure" ? "domain" : "view",
69
+ description: cap.kind === "procedure" ? cap.baseDescription : cap.description,
70
+ registrationId: reg.id,
71
+ component: { type: reg.type, instanceId: reg.instanceId },
72
+ outcome,
73
+ ...outcome === "expose" ? {} : reason !== void 0 ? { reason } : {},
74
+ policies,
75
+ availability: {
76
+ available: availability.available,
77
+ ...availability.reason !== void 0 ? { reason: availability.reason } : {}
78
+ }
79
+ };
80
+ }
81
+ function explainSurface(registry, ctx) {
82
+ const internals = registry[INTERNALS];
83
+ if (!internals) {
84
+ throw new Error(
85
+ "explainSurface() requires a registry created by createAgentSurfaceRegistry()"
86
+ );
87
+ }
88
+ if (internals.disposed) throw new Error("explainSurface() called on a disposed registry");
89
+ const consumer = ctx?.consumer ?? DEFAULT_CONSUMER;
90
+ const host = internals.host();
91
+ const regs = sortRegistrations(
92
+ [...internals.registrations.values()].filter((r) => r.status === "active")
93
+ );
94
+ const capabilities = [];
95
+ for (const reg of regs) {
96
+ if (!reg.procedureOnly && matchesScope(reg.type, ctx?.scope)) {
97
+ for (const obs of reg.observations.values()) {
98
+ capabilities.push(explainCapability(internals, reg, obs, consumer, host));
99
+ }
100
+ for (const act of reg.actions.values()) {
101
+ capabilities.push(explainCapability(internals, reg, act, consumer, host));
102
+ }
103
+ }
104
+ for (const proc of reg.procedures) {
105
+ const inScope = proc.contextLink ? matchesScope(proc.contextLink.type, ctx?.scope) : matchesScope(proc.path, ctx?.scope);
106
+ if (!inScope) continue;
107
+ capabilities.push(explainCapability(internals, reg, proc, consumer, host));
108
+ }
109
+ }
110
+ const route = internals.routeFn?.();
111
+ return deepFreeze({
112
+ surfaceId: internals.surfaceId,
113
+ surfaceVersion: String(internals.version),
114
+ capturedAt: new Date(internals.now()).toISOString(),
115
+ ...route ? { route } : {},
116
+ consumer,
117
+ capabilities
118
+ });
119
+ }
120
+ export {
121
+ explainSurface
122
+ };
123
+ //# sourceMappingURL=explain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/explain.ts"],"sourcesContent":["/**\n * `explainSurface()` — the developer projection.\n *\n * `snapshot()` answers \"what may this agent call right now\". It bakes policy\n * *outcomes*: a `hide` decision deletes the capability outright, leaving no\n * trace of which policy did it. That is correct for the agent boundary — the\n * existence of a hidden capability is itself information (docs/06) — and it is\n * exactly wrong for the developer staring at a surface that is missing a\n * capability they know they registered.\n *\n * This module answers the other question: *why*. It reports every capability\n * the registry holds, including the ones the snapshot omits, each with the\n * policy chain that judged it and that chain's per-policy votes.\n *\n * ## This is never agent-facing\n *\n * It lives behind its own entry point (`@agent-surface/core/explain`) and is\n * deliberately absent from the package root, so no adapter can reach it by\n * importing `@agent-surface/core` (AS-EXPLAIN-004). Nothing here may be piped\n * into a toolset, a transport, or a model prompt: doing so re-leaks precisely\n * the existence that `hide` exists to withhold. Developer tools, tests, and\n * CLIs only.\n */\nimport type { AgentConsumer, AgentRouteInfo } from \"./types.js\";\nimport type { AgentSurfaceRegistry } from \"./registry.js\";\nimport type { DiscoveryDecision, AgentPolicy, AgentPolicyContext } from \"./policy.js\";\nimport { CONFIRMATION_ESCALATION, type AgentPolicyWithEscalation } from \"./policy.js\";\nimport {\n INTERNALS,\n buildPolicyContext,\n computeAvailability,\n type CapabilityRuntime,\n type InternalRegistration,\n type InternalsCarrier,\n type RegistryInternals,\n} from \"./internal.js\";\nimport { DEFAULT_CONSUMER, matchesScope, sortRegistrations, type SnapshotContext } from \"./snapshot.js\";\nimport { deepFreeze } from \"./utils.js\";\n\n/** Which layer of the chain contributed a policy (docs/06 §composition). */\nexport type PolicyScope = \"registry\" | \"component\" | \"capability\";\n\nexport interface PolicyAttribution {\n /** `AgentPolicy.name` — built-ins are `authenticated`, `rate-limit`, … */\n name: string;\n scope: PolicyScope;\n /** Which pipeline hooks this policy implements. */\n phases: Array<\"discovery\" | \"authorize\" | \"invoke\">;\n /** This policy's own vote. Absent when it has no `onDiscovery`. */\n discovery?: DiscoveryDecision;\n /**\n * `onDiscovery` threw. `evaluateDiscovery` fails closed, so the vote is\n * recorded as `hide` — but a throwing discovery policy is a defect, and the\n * snapshot alone cannot tell you it happened.\n */\n threw?: boolean;\n /** Carries the `requireConfirmation` escalation marker. */\n confirmationEscalation?: boolean;\n}\n\nexport interface CapabilityExplanation {\n capabilityId: string;\n kind: \"observation\" | \"action\" | \"procedure\";\n plane: \"view\" | \"domain\";\n /**\n * The manifest description. Carried here because a hidden capability has no\n * snapshot entry to read it from, and an id alone does not tell a developer\n * which of their capabilities went missing.\n */\n description: string;\n registrationId: string;\n component: { type: string; instanceId: string };\n /**\n * What `snapshot()` did with this capability for the same context:\n * `hide` means absent from the snapshot entirely.\n */\n outcome: \"expose\" | \"disable\" | \"hide\";\n /** The reason a non-exposed capability carries, matching the snapshot's. */\n reason?: string;\n /** The full chain, registry-outermost first — the order policies run in. */\n policies: PolicyAttribution[];\n /**\n * The `when()`/override verdict on its own. Authority hides, state discloses\n * (D11/D12): keeping these apart is what lets you tell \"a policy removed it\"\n * from \"the UI says not right now\".\n */\n availability: { available: boolean; reason?: string };\n}\n\nexport interface SurfaceExplanation {\n surfaceId: string;\n surfaceVersion: string;\n capturedAt: string; // ISO-8601\n route?: AgentRouteInfo;\n /** The consumer this explanation was computed for. */\n consumer: AgentConsumer;\n /** Every capability held by the registry, hidden ones included. */\n capabilities: CapabilityExplanation[];\n}\n\nfunction phasesOf(policy: AgentPolicy): Array<\"discovery\" | \"authorize\" | \"invoke\"> {\n const phases: Array<\"discovery\" | \"authorize\" | \"invoke\"> = [];\n if (policy.onDiscovery) phases.push(\"discovery\");\n if (policy.onAuthorize) phases.push(\"authorize\");\n if (policy.onInvoke) phases.push(\"invoke\");\n return phases;\n}\n\n/**\n * Per-policy attribution plus the composed decision.\n *\n * `evaluateDiscovery` short-circuits on the first `hide`, so it cannot be\n * reused here — we need every vote, not the verdict. The composition below is\n * a faithful restatement of it (first `hide` wins; otherwise the *first*\n * `disable` is kept; otherwise `expose`), and AS-EXPLAIN-003 pins the two\n * together against a real snapshot. Re-running `onDiscovery` is safe by\n * contract: it MUST be synchronous, cheap, and side-effect free (docs/06).\n */\nfunction attribute(\n chain: AgentPolicy[],\n boundaries: { registry: number; component: number },\n ctx: AgentPolicyContext,\n): { policies: PolicyAttribution[]; decision: DiscoveryDecision } {\n const policies: PolicyAttribution[] = [];\n let hidden = false;\n let disable: { decision: \"disable\"; reason: string } | undefined;\n\n chain.forEach((policy, index) => {\n const scope: PolicyScope =\n index < boundaries.registry\n ? \"registry\"\n : index < boundaries.registry + boundaries.component\n ? \"component\"\n : \"capability\";\n\n const attribution: PolicyAttribution = {\n name: policy.name,\n scope,\n phases: phasesOf(policy),\n };\n if ((policy as AgentPolicyWithEscalation)[CONFIRMATION_ESCALATION]) {\n attribution.confirmationEscalation = true;\n }\n\n if (policy.onDiscovery) {\n let decision: DiscoveryDecision;\n try {\n decision = policy.onDiscovery(ctx);\n } catch {\n decision = { decision: \"hide\" }; // fail closed, exactly as evaluateDiscovery does\n attribution.threw = true;\n }\n attribution.discovery = decision;\n if (decision.decision === \"hide\") hidden = true;\n else if (decision.decision === \"disable\" && !disable) disable = decision;\n }\n\n policies.push(attribution);\n });\n\n return {\n policies,\n decision: hidden ? { decision: \"hide\" } : (disable ?? { decision: \"expose\" }),\n };\n}\n\nfunction explainCapability(\n internals: RegistryInternals,\n reg: InternalRegistration,\n cap: CapabilityRuntime,\n consumer: AgentConsumer,\n host: Record<string, unknown>,\n): CapabilityExplanation {\n const chain = [...internals.registryPolicies, ...reg.componentPolicies, ...cap.policies];\n const ctx = buildPolicyContext(internals, reg, cap, consumer, host);\n const { policies, decision } = attribute(\n chain,\n { registry: internals.registryPolicies.length, component: reg.componentPolicies.length },\n ctx,\n );\n const availability = computeAvailability(internals, reg, cap);\n\n // Mirrors createSnapshot exactly: a policy `disable` reason wins over the\n // availability reason, and availability only matters once discovery exposed.\n const available = availability.available && decision.decision === \"expose\";\n const reason = decision.decision === \"disable\" ? decision.reason : availability.reason;\n const outcome: CapabilityExplanation[\"outcome\"] =\n decision.decision === \"hide\" ? \"hide\" : available ? \"expose\" : \"disable\";\n\n return {\n capabilityId: cap.capabilityId,\n kind: cap.kind,\n plane: cap.kind === \"procedure\" ? \"domain\" : \"view\",\n description: cap.kind === \"procedure\" ? cap.baseDescription : cap.description,\n registrationId: reg.id,\n component: { type: reg.type, instanceId: reg.instanceId },\n outcome,\n ...(outcome === \"expose\" ? {} : reason !== undefined ? { reason } : {}),\n policies,\n availability: {\n available: availability.available,\n ...(availability.reason !== undefined ? { reason: availability.reason } : {}),\n },\n };\n}\n\n/**\n * Developer projection of the surface: every capability, hidden included, with\n * the policy chain that judged it.\n *\n * Honours `ctx.scope` and `ctx.consumer` so it lines up with the snapshot you\n * are debugging. `includeUnavailable` and `budget` are ignored by design —\n * withholding from an explanation is the one thing it must never do.\n *\n * @throws if `registry` was not produced by `createAgentSurfaceRegistry`, or\n * has been disposed.\n */\nexport function explainSurface(\n registry: AgentSurfaceRegistry,\n ctx?: SnapshotContext,\n): SurfaceExplanation {\n const internals = (registry as unknown as InternalsCarrier)[INTERNALS];\n if (!internals) {\n throw new Error(\n \"explainSurface() requires a registry created by createAgentSurfaceRegistry()\",\n );\n }\n if (internals.disposed) throw new Error(\"explainSurface() called on a disposed registry\");\n\n const consumer = ctx?.consumer ?? DEFAULT_CONSUMER;\n const host = internals.host();\n const regs = sortRegistrations(\n [...internals.registrations.values()].filter((r) => r.status === \"active\"),\n );\n\n const capabilities: CapabilityExplanation[] = [];\n for (const reg of regs) {\n if (!reg.procedureOnly && matchesScope(reg.type, ctx?.scope)) {\n for (const obs of reg.observations.values()) {\n capabilities.push(explainCapability(internals, reg, obs, consumer, host));\n }\n for (const act of reg.actions.values()) {\n capabilities.push(explainCapability(internals, reg, act, consumer, host));\n }\n }\n for (const proc of reg.procedures) {\n const inScope = proc.contextLink\n ? matchesScope(proc.contextLink.type, ctx?.scope)\n : matchesScope(proc.path, ctx?.scope);\n if (!inScope) continue;\n capabilities.push(explainCapability(internals, reg, proc, consumer, host));\n }\n }\n\n const route = internals.routeFn?.();\n return deepFreeze({\n surfaceId: internals.surfaceId,\n surfaceVersion: String(internals.version),\n capturedAt: new Date(internals.now()).toISOString(),\n ...(route ? { route } : {}),\n consumer,\n capabilities,\n });\n}\n"],"mappings":"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","names":[]}