@ai-matrx/alchemy 0.12.0 → 0.12.2

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ports.ts"],"sourcesContent":["/**\n * `@ai-matrx/alchemy/ports` — the host ports (Matrx Alchemy CONTRACT §2.2).\n *\n * The app binds these ONCE, at its boundary (where the Alchemy host is\n * mounted). The package never imports the host.\n *\n * ── Ports arrive with the wave that needs them (chair ruling N8) ────────────\n *\n * This entry declares only the ports the current waves use:\n *\n * ALC-13 `kinds` (content-ir's validator, injected), `diagnostics` (required)\n * ALC-14 `identity`, `persistence`\n * ALC-15 `store`, `notify`, `overlay`, `icons`, `knobs`, `i18n`, `auth`, `dialogs`\n *\n * ALC-16 `transport`, `assets`, `server`\n * PP-01a `window` (host-rendered windows for a live preparation session)\n * PP-13a `transferKnobs` (organization default recipes)\n *\n * ALC-17 `door` (the one write door), `review` (queued writes)\n *\n * ── Absent-port semantics ───────────────────────────────────────────────────\n *\n * Every port except `diagnostics` is OPTIONAL. A port the host does not bind\n * means that capability is ABSENT: the actions, formats or writes that need\n * it are not offered at all. A missing port is never stubbed, never faked\n * with a default that pretends to work, and never shown as a dead control.\n *\n * kinds absent → kind validation is absent (no value can be checked,\n * so a kind-bearing write is not offered)\n * diagnostics REQUIRED → nothing fails silently; contract breaks land here\n * identity absent → organization-scoped declarations are absent\n * persistence absent → per-person settings are absent (organization and\n * platform defaults apply)\n * store absent → actions that read named host state or run named\n * host commands are absent\n * notify absent → outcomes are not announced (diagnostics still captures)\n * overlay absent → actions that open an overlay or navigate are absent\n * icons absent → rows render label-only (a key is never shown as text)\n * knobs absent → the platform default layout/density applies\n * i18n absent → labels as written\n * auth absent → a signed-out person does not see requiresAuth actions\n * (sign-in-then-resume is the capability that is missing)\n * dialogs absent → actions that need a host dialog are absent\n * window absent → the preparation workspace window is absent (its\n * Prepare entry is not offered; it never falls back\n * to an overlay carrying the session as data)\n * transferKnobs absent → no organization default recipe; a person's own\n * default (persistence) or the package's Full applies\n * serverActions absent → `runs: \"server\"` Actions refuse with \"not available here\"\n * mandates absent → `runs: \"mandate\"` Actions refuse the same way\n * pageActions absent → `runs: \"page\"` Actions refuse the same way\n * approvals absent → a policy \"ask\" Action started by an agent, applet or automation is refused\n * (\"needs a person's approval\"); a person's own click is the approval\n * (`runs: \"navigate\"` goes through `overlay.navigate`; overlay absent → refused)\n * door absent → write-target Actions dispatch to `pageActions` as before, and\n * `Delivery.send` is refused with a remedy; bound → every such write goes\n * through it and returns its receipt\n * review absent → a write to a `queued` target is refused (\"review isn't available here\")\n *\n * Types only: this entry has no runtime code.\n */\n\n/** JSON as it crosses a port. */\nexport type Json =\n | null\n | boolean\n | number\n | string\n | Json[]\n | { [key: string]: Json };\n\n/**\n * content-ir's kind validator, injected so Alchemy validates without\n * importing the app or the kind catalog. The host builds it from\n * `@ai-matrx/content-ir`'s `createKindValidator` over its own\n * `SchemaSourcePort` (which content-ir defines and exports; Alchemy never\n * declares it).\n *\n * `unverifiable: true` marks a verdict where the kind could not be checked\n * (not registered, no schema, uncompilable schema, catalog unreachable) — a\n * skip, which is never a pass.\n */\nexport interface KindValidatorPort {\n validate(\n kindKey: string,\n value: unknown,\n signal: AbortSignal,\n ): Promise<\n | { ok: true }\n | { ok: false; sentence: string; remedy: string; unverifiable?: true }\n >;\n}\n\nimport type { ClickTarget } from \"./actions/click-target\";\nimport type { WriteDoor, WriteReviewPort } from \"./operate/door\";\nexport type { WriteDoor, WriteReviewPort } from \"./operate/door\";\nimport type {\n ActionContext,\n ActionResult,\n MandateRunRef,\n MandateRunRequest,\n ServerActionRunRequest,\n ServerActionRunResponse,\n} from \"./actions/applet-types\";\n\n/** Where contract breaks are captured. Required: nothing fails silently. */\nexport interface DiagnosticsPort {\n capture(\n error: unknown,\n context: {\n area: \"declare\" | \"operate\" | \"write\" | \"actions\" | \"menu\";\n detail: Json;\n },\n ): void;\n}\n\n/** Who is using the app right now, and in which organization. */\nexport interface IdentityPort {\n /** `null` while the host has no resolved identity yet. */\n current(): {\n userId: string;\n organizationId: string | null;\n isAuthenticated: boolean;\n isAdmin: boolean;\n } | null;\n /** Called whenever `current()` may answer differently; returns unsubscribe. */\n onChange(listener: () => void): () => void;\n}\n\n/** Per-person settings (layout, density, transfer preferences), layered by the host's settings system. */\nexport interface PersistencePort {\n readSetting<T extends Json>(key: string): Promise<T | null>;\n writeSetting(key: string, value: Json): Promise<void>;\n}\n\n/** Neutral state access: named reads and named commands. No Redux type or dispatch crosses this line. */\nexport interface StorePort {\n /** A named, typed read the host registered (\"activeConversationId\", \"organizationName\"). */\n read<T extends Json>(key: string): T | null;\n /** A named command the host registered (\"tasks.setPendingSource\"); resolves with its result. */\n command(name: string, input: Json): Promise<Json | void>;\n subscribe(key: string, listener: () => void): () => void;\n}\n\nexport interface NotifyPort {\n success(sentence: string): void;\n info(sentence: string): void;\n /** Every failure names its remedy. */\n error(sentence: string, remedy: string): void;\n}\n\n/** Overlays are dispatched through the host; the menu never renders a modal. */\nexport interface OverlayHostPort {\n open(overlayId: string, data: Json, instanceId?: string): void;\n close(overlayId: string, instanceId?: string): void;\n navigate(href: string): void;\n}\n\n/** Icon key → the host's icon component. The model only ever holds keys. */\nexport interface IconResolverPort {\n resolve(key: string): unknown /* host component */ | null;\n}\n\nexport type ContextArrangementKnob = \"classic\" | \"tiered\" | \"command\";\nexport type DensityKnob = \"comfortable\" | \"compact\";\n\n/** ORGANIZATION defaults only; every method takes the organization. */\nexport interface MenuKnobsPort {\n /** Replaces the constant PLACEMENT_LABEL_OVERRIDE. */\n placementLabel(placement: string, organizationId: string | null): string | null;\n /** Replaces the constant DEFAULT_PLACEMENT_MODE. */\n placementMode(placement: string, organizationId: string | null): \"show\" | \"hide\";\n /** The organization's default layout and density (platform default when null: command / comfortable). */\n layout(organizationId: string | null): { context: ContextArrangementKnob; density: DensityKnob } | null;\n}\n\n/** Label translation. Absent = labels as written. */\nexport interface I18nPort {\n t(key: string, fallback: string): string;\n}\n\n/** Sign in, then resume the action the person picked. */\nexport interface AuthResumePort {\n isSignedIn(): boolean;\n /** Stash {actionId, target fingerprint} and open sign-in; after sign-in the next matching menu host resumes it. */\n signInThenResume(pending: { actionId: string; contentFingerprint: string }): void;\n takePending(contentFingerprint: string): { actionId: string } | null;\n}\n\n/** Per-instance host dialogs (today's RichDocumentActionContextCallbacks). */\nexport interface HostDialogsPort {\n requestDelete?(target: ClickTarget): void;\n requestConvert?(target: ClickTarget): void;\n requestEditHistory?(target: ClickTarget): void;\n requestFullPrint?(target: ClickTarget): void;\n requestSaveTable?(table: { headers: string[]; rows: string[][] }): void;\n requestFlashcard?(answer: string): void;\n}\n\n/** The ports a host binds. Optional = absent capability when unbound (see above). */\nexport interface AlchemyHostPorts {\n // ALC-13\n kinds?: KindValidatorPort;\n /** REQUIRED from ALC-13 (the only non-optional port): contract breaks are captured here. */\n diagnostics: DiagnosticsPort;\n // ALC-14\n identity?: IdentityPort;\n persistence?: PersistencePort;\n // ALC-15\n store?: StorePort;\n notify?: NotifyPort;\n overlay?: OverlayHostPort;\n icons?: IconResolverPort;\n /** Organization defaults for menu knobs; a person's own choice is layered over them via persistence (N9). */\n knobs?: MenuKnobsPort;\n i18n?: I18nPort;\n auth?: AuthResumePort;\n dialogs?: HostDialogsPort;\n // ALC-16\n /** Clipboard and download for this runtime (the browser default ships in /operate). */\n transport?: TransportPort;\n /** Private or signed images → share-safe URLs before HTML is sealed. Absent = images keep their URL. */\n assets?: AssetResolverPort;\n /** Present when the host can reach aidream server operations; absent = server formats are not offered. */\n server?: ServerPort;\n // Preparation parity (PP-01a, PP-13a)\n /** Host-rendered windows for a LIVE preparation session (functions, subscriptions, sealed artifacts). */\n window?: WindowPort;\n /** Organization default recipes, resolved by the host's Feature Knob ladder (platform → organization). */\n transferKnobs?: TransferKnobsPort;\n // Surface Actions (Applets CONTRACTS §6)\n /** aidream `POST /actions/run`: a registered server action by name, as the caller, under the apply-policy cascade. */\n serverActions?: ServerActionPort;\n /** Runs a mandate by alias or key (the host binds `@ai-matrx/agents`; alchemy never imports it). */\n mandates?: MandatePort;\n /** The page's own handlers for `runs: \"page\"` Actions (in an applet frame: routed as `{ action, input, runId }`). */\n pageActions?: PageActionPort;\n /** Asks a person, in place, to approve an `ask` Action that an agent, applet or automation started. */\n approvals?: ApprovalPort;\n // ALC-17 — the one write door\n /** `createWriteDoor(...)`: every write to a declared target lands here and returns a receipt. */\n door?: WriteDoor;\n /** Files a write to a `queued` target for an organization's review. */\n review?: WriteReviewPort;\n}\n\n// ── Surface Actions (Applets CONTRACTS §6) ──────────────────────────────────\n\nexport interface ServerActionPort {\n run(request: ServerActionRunRequest): Promise<ServerActionRunResponse>;\n}\nexport interface MandatePort {\n /**\n * STARTS the run and resolves with its `RunRef` (CONTRACTS §3/§6) — never the run's result, which\n * arrives later on `run:<requestId>` / `IntelligencePort.result(ref)`. Rejects with the system's sentence.\n */\n run(request: MandateRunRequest): Promise<MandateRunRef>;\n}\nexport interface ApprovalRequest {\n action: string;\n label: string;\n description: string;\n input: Json;\n by: \"agent\" | \"applet\" | \"automation\";\n surfaceName: string;\n}\nexport interface ApprovalPort {\n /** Resolves true when the person approves; false when they decline or dismiss. */\n ask(request: ApprovalRequest): Promise<boolean>;\n}\nexport interface PageActionPort {\n dispatch(name: string, input: unknown, context: ActionContext): Promise<ActionResult<unknown>>;\n}\n\n// ── PP-01a — WindowPort ─────────────────────────────────────────────────────\n\n/**\n * Why a port and not `OverlayHostPort`: an overlay's `data` is `Json`, and a live preparation\n * session is not — it holds functions, subscriptions and sealed-artifact handles. So the workspace\n * never travels as overlay data; the package hands the host a render function and the host draws it\n * inside its own window system, with the session still live in the closure.\n */\nexport interface WindowSize {\n width: number;\n height: number;\n}\nexport interface WindowOpenOptions {\n /** The window's title, as a person reads it. */\n title: string;\n /** Preferred size; `\"full\"` = near-full-screen. The host clamps to the viewport. */\n size?: WindowSize | \"full\";\n /**\n * Stable identity. Opening an id that is already open focuses that window and draws the new\n * render in it instead of opening a second one.\n */\n instanceId?: string;\n /** Called exactly once when the window closes, whoever closed it (the person, `close()`, the host). */\n onClose?: () => void;\n}\nexport interface WindowHandle {\n readonly instanceId: string;\n /** Close the window; idempotent. */\n close(): void;\n /** Bring the window to the front; a no-op once closed. */\n focus(): void;\n isOpen(): boolean;\n}\nexport interface WindowPort {\n /**\n * What the host really draws. `\"window\"`: non-blocking, movable, resizable. `\"dialog\"`: a blocking\n * full-screen dialog — a surface on a dialog host never says \"window\" and never offers a second one.\n */\n readonly presentation: \"window\" | \"dialog\";\n /** True only when several windows can be open and usable at once. False = never offer \"open another\". */\n readonly multiInstance: boolean;\n /**\n * Draw `render(handle)` inside a host window. `render` returns a host UI node (a React element in\n * a React host) and is called by the host while the window is open.\n */\n open(render: (window: WindowHandle) => unknown, options: WindowOpenOptions): WindowHandle;\n}\n\n// ── PP-13a — TransferKnobsPort ──────────────────────────────────────────────\n\n/**\n * ORGANIZATION defaults for transfer, resolved by the host's Feature Knob ladder: the platform\n * default an admin sets, overridden by the organization. Never per-person — a person's own choice\n * lives only in `PersistencePort` and is layered over this by the package (`resolveDefaultRecipe`).\n */\nexport interface TransferKnobsPort {\n /**\n * The organization's default recipe for one source kind (the platform default when the\n * organization set none), or null when neither names one. A read that fails rejects; it never\n * answers null for \"could not ask\".\n */\n defaultRecipe(sourceKind: string, organizationId: string | null): Promise<Json | null>;\n}\n\n// ── ALC-16 ports (Operate) ──────────────────────────────────────────────────\n\n/** A transferable artifact as the transport sees it (structurally `@ai-matrx/alchemy/operate`'s Artifact). */\nexport interface TransportArtifact {\n format: string;\n plainText: string;\n html?: string;\n file?: { filename: string; mime: string; bytes: Uint8Array };\n}\nexport type TransportOutcome =\n | { status: \"success\"; delivered: \"clipboard\" | \"download-started\"; mimeTypes: string[] }\n | { status: \"degraded\"; delivered: \"plain-text\"; reason: string }\n | { status: \"cancelled\" }\n | { status: \"error\"; code: string; message: string; retryable: boolean };\n/** Clipboard and download for a non-browser runtime (MV3 service worker, Tauri — CONTRACT R8). */\nexport interface TransportPort {\n copy(artifact: TransportArtifact, signal: AbortSignal): Promise<TransportOutcome>;\n download(artifact: TransportArtifact, signal: AbortSignal): Promise<TransportOutcome>;\n}\nexport interface AssetResolverPort {\n resolve(url: string, signal: AbortSignal): Promise<{ url: string } | null>;\n}\nexport type ServerResult =\n | { status: \"bytes\"; filename: string; mime: string; bytes: Uint8Array; notices: readonly { code: string; sentence: string; remedy: string }[] }\n | { status: \"receipt\"; receipt: { status: string; sentence: string; remedy?: string } };\nexport interface ServerPort {\n call(operationId: string, input: Json, signal: AbortSignal): Promise<ServerResult>;\n}\n"],"mappings":"sjBAAA","names":[]}
1
+ {"version":3,"sources":["../src/ports.ts"],"sourcesContent":["/**\n * `@ai-matrx/alchemy/ports` — the host ports (Matrx Alchemy CONTRACT §2.2).\n *\n * The app binds these ONCE, at its boundary (where the Alchemy host is\n * mounted). The package never imports the host.\n *\n * ── Ports arrive with the wave that needs them (chair ruling N8) ────────────\n *\n * This entry declares only the ports the current waves use:\n *\n * ALC-13 `kinds` (content-ir's validator, injected), `diagnostics` (required)\n * ALC-14 `identity`, `persistence`\n * ALC-15 `store`, `notify`, `overlay`, `icons`, `knobs`, `i18n`, `auth`, `dialogs`\n *\n * ALC-16 `transport`, `assets`, `server`\n * PP-01a `window` (host-rendered windows for a live preparation session)\n * PP-13a `transferKnobs` (organization default recipes)\n *\n * ALC-17 `door` (the one write door), `review` (queued writes)\n *\n * ── Absent-port semantics ───────────────────────────────────────────────────\n *\n * Every port except `diagnostics` is OPTIONAL. A port the host does not bind\n * means that capability is ABSENT: the actions, formats or writes that need\n * it are not offered at all. A missing port is never stubbed, never faked\n * with a default that pretends to work, and never shown as a dead control.\n *\n * kinds absent → kind validation is absent (no value can be checked,\n * so a kind-bearing write is not offered)\n * diagnostics REQUIRED → nothing fails silently; contract breaks land here\n * identity absent → organization-scoped declarations are absent\n * persistence absent → per-person settings are absent (organization and\n * platform defaults apply)\n * store absent → actions that read named host state or run named\n * host commands are absent\n * notify absent → outcomes are not announced (diagnostics still captures)\n * overlay absent → actions that open an overlay or navigate are absent\n * icons absent → rows render label-only (a key is never shown as text)\n * knobs absent → the platform default layout/density applies\n * i18n absent → labels as written\n * auth absent → a signed-out person does not see requiresAuth actions\n * (sign-in-then-resume is the capability that is missing)\n * dialogs absent → actions that need a host dialog are absent\n * window absent → the preparation workspace window is absent (its\n * Prepare entry is not offered; it never falls back\n * to an overlay carrying the session as data)\n * transferKnobs absent → no organization default recipe; a person's own\n * default (persistence) or the package's Full applies\n * serverActions absent → `runs: \"server\"` Actions refuse with \"not available here\"\n * mandates absent → `runs: \"mandate\"` Actions refuse the same way\n * pageActions absent → `runs: \"page\"` Actions refuse the same way\n * approvals absent → a policy \"ask\" Action started by an agent, applet or automation is refused\n * (\"needs a person's approval\"); a person's own click is the approval\n * (`runs: \"navigate\"` goes through `overlay.navigate`; overlay absent → refused)\n * door absent → write-target Actions dispatch to `pageActions` as before, and\n * `Delivery.send` is refused with a remedy; bound → every such write goes\n * through it and returns its receipt\n * review absent → a write to a `queued` target is refused (\"review isn't available here\")\n *\n * Types only: this entry has no runtime code.\n */\n\n/** JSON as it crosses a port. */\nexport type Json =\n | null\n | boolean\n | number\n | string\n | Json[]\n | { [key: string]: Json };\n\n/**\n * content-ir's kind validator, injected so Alchemy validates without\n * importing the app or the kind catalog. The host builds it from\n * `@ai-matrx/content-ir`'s `createKindValidator` over its own\n * `SchemaSourcePort` (which content-ir defines and exports; Alchemy never\n * declares it).\n *\n * `unverifiable: true` marks a verdict where the kind could not be checked\n * (not registered, no schema, uncompilable schema, catalog unreachable) — a\n * skip, which is never a pass.\n */\nexport interface KindValidatorPort {\n validate(\n kindKey: string,\n value: unknown,\n signal: AbortSignal,\n ): Promise<\n | { ok: true }\n | { ok: false; sentence: string; remedy: string; unverifiable?: true }\n >;\n}\n\nimport type { ClickTarget } from \"./actions/click-target\";\nimport type { WriteDoor, WriteReviewPort } from \"./operate/door\";\nexport type { WriteDoor, WriteReviewPort } from \"./operate/door\";\nimport type {\n ActionContext,\n ActionResult,\n MandateRunRef,\n MandateRunRequest,\n ServerActionRunRequest,\n ServerActionRunResponse,\n} from \"./actions/applet-types\";\n\n/** Where contract breaks are captured. Required: nothing fails silently. */\nexport interface DiagnosticsPort {\n capture(\n error: unknown,\n context: {\n area: \"declare\" | \"operate\" | \"write\" | \"actions\" | \"menu\";\n detail: Json;\n },\n ): void;\n}\n\n/** Who is using the app right now, and in which organization. */\nexport interface IdentityPort {\n /** `null` while the host has no resolved identity yet. */\n current(): {\n userId: string;\n organizationId: string | null;\n isAuthenticated: boolean;\n isAdmin: boolean;\n } | null;\n /** Called whenever `current()` may answer differently; returns unsubscribe. */\n onChange(listener: () => void): () => void;\n}\n\n/** Per-person settings (layout, density, transfer preferences), layered by the host's settings system. */\nexport interface PersistencePort {\n readSetting<T extends Json>(key: string): Promise<T | null>;\n writeSetting(key: string, value: Json): Promise<void>;\n}\n\n/** Neutral state access: named reads and named commands. No Redux type or dispatch crosses this line. */\nexport interface StorePort {\n /** A named, typed read the host registered (\"activeConversationId\", \"organizationName\"). */\n read<T extends Json>(key: string): T | null;\n /** A named command the host registered (\"tasks.setPendingSource\"); resolves with its result. */\n command(name: string, input: Json): Promise<Json | void>;\n subscribe(key: string, listener: () => void): () => void;\n}\n\nexport interface NotifyPort {\n success(sentence: string): void;\n info(sentence: string): void;\n /** Every failure names its remedy. */\n error(sentence: string, remedy: string): void;\n}\n\n/** Overlays are dispatched through the host; the menu never renders a modal. */\nexport interface OverlayHostPort {\n open(overlayId: string, data: Json, instanceId?: string): void;\n close(overlayId: string, instanceId?: string): void;\n navigate(href: string): void;\n}\n\n/** Icon key → the host's icon component. The model only ever holds keys. */\nexport interface IconResolverPort {\n resolve(key: string): unknown /* host component */ | null;\n}\n\nexport type ContextArrangementKnob = \"classic\" | \"tiered\" | \"command\";\nexport type DensityKnob = \"comfortable\" | \"compact\";\n\n/** ORGANIZATION defaults only; every method takes the organization. */\nexport interface MenuKnobsPort {\n /** Replaces the constant PLACEMENT_LABEL_OVERRIDE. */\n placementLabel(placement: string, organizationId: string | null): string | null;\n /** Replaces the constant DEFAULT_PLACEMENT_MODE. */\n placementMode(placement: string, organizationId: string | null): \"show\" | \"hide\";\n /** The organization's default layout and density (platform default when null: command / comfortable). */\n layout(organizationId: string | null): { context: ContextArrangementKnob; density: DensityKnob } | null;\n}\n\n/** Label translation. Absent = labels as written. */\nexport interface I18nPort {\n t(key: string, fallback: string): string;\n}\n\n/** Sign in, then resume the action the person picked. */\nexport interface AuthResumePort {\n isSignedIn(): boolean;\n /** Stash {actionId, target fingerprint} and open sign-in; after sign-in the next matching menu host resumes it. */\n signInThenResume(pending: { actionId: string; contentFingerprint: string }): void;\n takePending(contentFingerprint: string): { actionId: string } | null;\n}\n\n/** Per-instance host dialogs (today's RichDocumentActionContextCallbacks). */\nexport interface HostDialogsPort {\n requestDelete?(target: ClickTarget): void;\n requestConvert?(target: ClickTarget): void;\n requestEditHistory?(target: ClickTarget): void;\n requestFullPrint?(target: ClickTarget): void;\n requestSaveTable?(table: { headers: string[]; rows: string[][] }): void;\n requestFlashcard?(answer: string): void;\n}\n\n/** The ports a host binds. Optional = absent capability when unbound (see above). */\nexport interface AlchemyHostPorts {\n // ALC-13\n kinds?: KindValidatorPort;\n /** REQUIRED from ALC-13 (the only non-optional port): contract breaks are captured here. */\n diagnostics: DiagnosticsPort;\n // ALC-14\n identity?: IdentityPort;\n persistence?: PersistencePort;\n // ALC-15\n store?: StorePort;\n notify?: NotifyPort;\n overlay?: OverlayHostPort;\n icons?: IconResolverPort;\n /** Organization defaults for menu knobs; a person's own choice is layered over them via persistence (N9). */\n knobs?: MenuKnobsPort;\n i18n?: I18nPort;\n auth?: AuthResumePort;\n dialogs?: HostDialogsPort;\n // ALC-16\n /** Clipboard and download for this runtime (the browser default ships in /operate). */\n transport?: TransportPort;\n /** Private or signed images → share-safe URLs before HTML is sealed. Absent = images keep their URL. */\n assets?: AssetResolverPort;\n /** Present when the host can reach aidream server operations; absent = server formats are not offered. */\n server?: ServerPort;\n // Preparation parity (PP-01a, PP-13a)\n /** Host-rendered windows for a LIVE preparation session (functions, subscriptions, sealed artifacts). */\n window?: WindowPort;\n /** Organization default recipes, resolved by the host's Feature Knob ladder (platform → organization). */\n transferKnobs?: TransferKnobsPort;\n // Surface Actions (Applets CONTRACTS §6)\n /** aidream `POST /actions/run`: a registered server action by name, as the caller, under the apply-policy cascade. */\n serverActions?: ServerActionPort;\n /** Runs a mandate by alias or key (the host binds `@ai-matrx/agents`; alchemy never imports it). */\n mandates?: MandatePort;\n /** The page's own handlers for `runs: \"page\"` Actions (in an applet frame: routed as `{ action, input, runId }`). */\n pageActions?: PageActionPort;\n /** Asks a person, in place, to approve an `ask` Action that an agent, applet or automation started. */\n approvals?: ApprovalPort;\n // ALC-17 — the one write door\n /** `createWriteDoor(...)`: every write to a declared target lands here and returns a receipt. */\n door?: WriteDoor;\n /** Files a write to a `queued` target for an organization's review. */\n review?: WriteReviewPort;\n}\n\n// ── Surface Actions (Applets CONTRACTS §6) ──────────────────────────────────\n\nexport interface ServerActionPort {\n run(request: ServerActionRunRequest): Promise<ServerActionRunResponse>;\n}\nexport interface MandatePort {\n /**\n * STARTS the run and resolves with its `RunRef` (CONTRACTS §3/§6) — never the run's result, which\n * arrives later on `run:<requestId>` / `IntelligencePort.result(ref)`. Rejects with the system's sentence.\n */\n run(request: MandateRunRequest): Promise<MandateRunRef>;\n}\nexport interface ApprovalRequest {\n action: string;\n label: string;\n description: string;\n input: Json;\n by: \"agent\" | \"applet\" | \"automation\";\n surfaceName: string;\n /** The run this approval belongs to, when the caller named one — lets a host match the answer to its own waiting write or Action instead of guessing by value. */\n runId?: string;\n}\nexport interface ApprovalPort {\n /** Resolves true when the person approves; false when they decline or dismiss. */\n ask(request: ApprovalRequest): Promise<boolean>;\n}\nexport interface PageActionPort {\n dispatch(name: string, input: unknown, context: ActionContext): Promise<ActionResult<unknown>>;\n}\n\n// ── PP-01a — WindowPort ─────────────────────────────────────────────────────\n\n/**\n * Why a port and not `OverlayHostPort`: an overlay's `data` is `Json`, and a live preparation\n * session is not — it holds functions, subscriptions and sealed-artifact handles. So the workspace\n * never travels as overlay data; the package hands the host a render function and the host draws it\n * inside its own window system, with the session still live in the closure.\n */\nexport interface WindowSize {\n width: number;\n height: number;\n}\nexport interface WindowOpenOptions {\n /** The window's title, as a person reads it. */\n title: string;\n /** Preferred size; `\"full\"` = near-full-screen. The host clamps to the viewport. */\n size?: WindowSize | \"full\";\n /**\n * Stable identity. Opening an id that is already open focuses that window and draws the new\n * render in it instead of opening a second one.\n */\n instanceId?: string;\n /** Called exactly once when the window closes, whoever closed it (the person, `close()`, the host). */\n onClose?: () => void;\n}\nexport interface WindowHandle {\n readonly instanceId: string;\n /** Close the window; idempotent. */\n close(): void;\n /** Bring the window to the front; a no-op once closed. */\n focus(): void;\n isOpen(): boolean;\n}\nexport interface WindowPort {\n /**\n * What the host really draws. `\"window\"`: non-blocking, movable, resizable. `\"dialog\"`: a blocking\n * full-screen dialog — a surface on a dialog host never says \"window\" and never offers a second one.\n */\n readonly presentation: \"window\" | \"dialog\";\n /** True only when several windows can be open and usable at once. False = never offer \"open another\". */\n readonly multiInstance: boolean;\n /**\n * Draw `render(handle)` inside a host window. `render` returns a host UI node (a React element in\n * a React host) and is called by the host while the window is open.\n */\n open(render: (window: WindowHandle) => unknown, options: WindowOpenOptions): WindowHandle;\n}\n\n// ── PP-13a — TransferKnobsPort ──────────────────────────────────────────────\n\n/**\n * ORGANIZATION defaults for transfer, resolved by the host's Feature Knob ladder: the platform\n * default an admin sets, overridden by the organization. Never per-person — a person's own choice\n * lives only in `PersistencePort` and is layered over this by the package (`resolveDefaultRecipe`).\n */\nexport interface TransferKnobsPort {\n /**\n * The organization's default recipe for one source kind (the platform default when the\n * organization set none), or null when neither names one. A read that fails rejects; it never\n * answers null for \"could not ask\".\n */\n defaultRecipe(sourceKind: string, organizationId: string | null): Promise<Json | null>;\n}\n\n// ── ALC-16 ports (Operate) ──────────────────────────────────────────────────\n\n/** A transferable artifact as the transport sees it (structurally `@ai-matrx/alchemy/operate`'s Artifact). */\nexport interface TransportArtifact {\n format: string;\n plainText: string;\n html?: string;\n file?: { filename: string; mime: string; bytes: Uint8Array };\n}\nexport type TransportOutcome =\n | { status: \"success\"; delivered: \"clipboard\" | \"download-started\"; mimeTypes: string[] }\n | { status: \"degraded\"; delivered: \"plain-text\"; reason: string }\n | { status: \"cancelled\" }\n | { status: \"error\"; code: string; message: string; retryable: boolean };\n/** Clipboard and download for a non-browser runtime (MV3 service worker, Tauri — CONTRACT R8). */\nexport interface TransportPort {\n copy(artifact: TransportArtifact, signal: AbortSignal): Promise<TransportOutcome>;\n download(artifact: TransportArtifact, signal: AbortSignal): Promise<TransportOutcome>;\n}\nexport interface AssetResolverPort {\n resolve(url: string, signal: AbortSignal): Promise<{ url: string } | null>;\n}\nexport type ServerResult =\n | { status: \"bytes\"; filename: string; mime: string; bytes: Uint8Array; notices: readonly { code: string; sentence: string; remedy: string }[] }\n | { status: \"receipt\"; receipt: { status: string; sentence: string; remedy?: string } };\nexport interface ServerPort {\n call(operationId: string, input: Json, signal: AbortSignal): Promise<ServerResult>;\n}\n"],"mappings":"sjBAAA","names":[]}
package/dist/ports.d.cts CHANGED
@@ -679,6 +679,8 @@ interface ApprovalRequest {
679
679
  input: Json;
680
680
  by: "agent" | "applet" | "automation";
681
681
  surfaceName: string;
682
+ /** The run this approval belongs to, when the caller named one — lets a host match the answer to its own waiting write or Action instead of guessing by value. */
683
+ runId?: string;
682
684
  }
683
685
  interface ApprovalPort {
684
686
  /** Resolves true when the person approves; false when they decline or dismiss. */
package/dist/ports.d.ts CHANGED
@@ -679,6 +679,8 @@ interface ApprovalRequest {
679
679
  input: Json;
680
680
  by: "agent" | "applet" | "automation";
681
681
  surfaceName: string;
682
+ /** The run this approval belongs to, when the caller named one — lets a host match the answer to its own waiting write or Action instead of guessing by value. */
683
+ runId?: string;
682
684
  }
683
685
  interface ApprovalPort {
684
686
  /** Resolves true when the person approves; false when they decline or dismiss. */
@@ -716,6 +716,8 @@ interface ApprovalRequest {
716
716
  input: Json;
717
717
  by: "agent" | "applet" | "automation";
718
718
  surfaceName: string;
719
+ /** The run this approval belongs to, when the caller named one — lets a host match the answer to its own waiting write or Action instead of guessing by value. */
720
+ runId?: string;
719
721
  }
720
722
  interface ApprovalPort {
721
723
  /** Resolves true when the person approves; false when they decline or dismiss. */
@@ -716,6 +716,8 @@ interface ApprovalRequest {
716
716
  input: Json;
717
717
  by: "agent" | "applet" | "automation";
718
718
  surfaceName: string;
719
+ /** The run this approval belongs to, when the caller named one — lets a host match the answer to its own waiting write or Action instead of guessing by value. */
720
+ runId?: string;
719
721
  }
720
722
  interface ApprovalPort {
721
723
  /** Resolves true when the person approves; false when they decline or dismiss. */
@@ -716,6 +716,8 @@ interface ApprovalRequest {
716
716
  input: Json;
717
717
  by: "agent" | "applet" | "automation";
718
718
  surfaceName: string;
719
+ /** The run this approval belongs to, when the caller named one — lets a host match the answer to its own waiting write or Action instead of guessing by value. */
720
+ runId?: string;
719
721
  }
720
722
  interface ApprovalPort {
721
723
  /** Resolves true when the person approves; false when they decline or dismiss. */
@@ -716,6 +716,8 @@ interface ApprovalRequest {
716
716
  input: Json;
717
717
  by: "agent" | "applet" | "automation";
718
718
  surfaceName: string;
719
+ /** The run this approval belongs to, when the caller named one — lets a host match the answer to its own waiting write or Action instead of guessing by value. */
720
+ runId?: string;
719
721
  }
720
722
  interface ApprovalPort {
721
723
  /** Resolves true when the person approves; false when they decline or dismiss. */
package/dist/surface.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to};var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod);var surface_exports={};__export(surface_exports,{APPLET_SURFACE_PREFIX:()=>APPLET_SURFACE_PREFIX,PAGE_ACTION_STREAM:()=>PAGE_ACTION_STREAM,createSurfacePort:()=>createSurfacePort,readSurfaceChain:()=>readSurfaceChain,surfaceActionVisible:()=>surfaceActionVisible,valueStream:()=>valueStream});module.exports=__toCommonJS(surface_exports);var DEFAULT_MAX_INHERITANCE_DEPTH=3;var APPLET_CATEGORY="applet";function compact(value){return Object.fromEntries(Object.entries(value).filter(([,v])=>v!==void 0))}function surfaceActionRowToAction(row){return compact({name:row.name,category:row.category,label:row.label,description:row.description,input:row.input??void 0,output:row.output??void 0,runs:row.runs,target:row.target??void 0,triggeredBy:[...row.triggered_by],policy:row.policy,updatesValue:row.updates_value??void 0,itemType:row.item_type||void 0,sortOrder:row.sort_order,origin:compact({source:"surface-action",surfaceName:row.surface_name,surfaceId:row.surface_id,id:row.id,declaredBy:row.declared_by,codeRef:row.code_ref??void 0,organizationId:row.organization_id??void 0})})}function toActionInfo(declaration,declaredOn){return compact({name:declaration.name,category:declaration.category??APPLET_CATEGORY,label:declaration.label,description:declaration.description,runs:declaration.runs,policy:declaration.policy,triggeredBy:[...declaration.triggeredBy],input:declaration.input,output:declaration.output,declaredOn,target:declaration.target,updatesValue:declaration.updatesValue,itemType:declaration.itemType,source:declaration.origin?.source})}function resolveActionChain(chain,options={}){let maxDepth=options.maxDepth??DEFAULT_MAX_INHERITANCE_DEPTH,names=chain.map(link=>link.surfaceName),seen=new Set;for(let name of names){if(seen.has(name))throw new Error(`[actions] inheritance CYCLE detected at "${name}". Fix the surface chain: ${names.join(" \u2192 ")}`);seen.add(name)}if(chain.length-1>maxDepth)throw new Error(`[actions] inheritance chain for "${names[names.length-1]}" exceeds depth ${maxDepth}: ${names.join(" \u2192 ")}`);let byName=new Map;for(let link of chain){let own=new Set;for(let action of link.actions){if(own.has(action.name))throw new Error(`[actions] surface "${link.surfaceName}" declares Action "${action.name}" twice. Names are unique; nothing was overwritten.`);own.add(action.name),byName.set(action.name,toActionInfo(action,link.surfaceName))}}return[...byName.values()]}function asSurfaceClient(client){let c=client;if(typeof c.schema!="function")throw new Error("[alchemy/surface] the surface runtime needs a supabase-js v2 client carrying the viewer's session (it has no schema()).");return c}function readError(error,code="read_failed"){let out={code:error.code?`${code}:${error.code}`:code,message:error.message,retryable:!1};return error.details&&(out.diagnostic=error.details),out}async function read(query,shape){try{let answer=await query();return answer.error?{ok:!1,error:readError(answer.error)}:{ok:!0,data:shape(answer.data)}}catch(err){return{ok:!1,error:{code:"unreachable",message:err instanceof Error?err.message:String(err),retryable:!0}}}}function isRecord(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}var APPLET_SURFACE_PREFIX="applets/";function fail(code,message,retryable=!1){return{ok:!1,error:{code,message,retryable}}}function surfaceActionVisible(row,organizationIds){return row.declared_by==="code"||row.surface_name.startsWith(APPLET_SURFACE_PREFIX)?!0:typeof row.organization_id=="string"&&(organizationIds??[]).includes(row.organization_id)}async function readSurfaceChain(client,surfaceName,options={}){let db;try{db=asSurfaceClient(client)}catch(err){return fail("client_invalid",err instanceof Error?err.message:String(err))}let maxDepth=options.maxDepth??DEFAULT_MAX_INHERITANCE_DEPTH,names=[],at=surfaceName;for(;at;){if(names.includes(at))return fail("surface_cycle",`The surface chain loops at "${at}": ${[...names,at].join(" \u2192 ")}.`);if(names.length>maxDepth)return fail("surface_too_deep",`The surface chain for "${surfaceName}" is deeper than ${maxDepth}: ${[...names,at].join(" \u2192 ")}.`);let name=at,row=await read(()=>db.schema("ui").from("ui_surface").select("name, parent_surface_name").eq("name",name).is("deleted_at",null).maybeSingle(),d=>d);if(!row.ok)return row;if(!isRecord(row.data))return fail("surface_not_found",names.length===0?`The surface "${at}" does not exist yet; saving the Applet writes it (ui.save_applet_surface).`:`The surface "${names[names.length-1]}" names a parent "${at}" that does not exist.`);names.push(at);let parent=row.data.parent_surface_name;at=typeof parent=="string"&&parent?parent:null}let rows=await read(()=>db.schema("ui").from("ui_surface_action").select("*").in("surface_name",names).is("deleted_at",null).order("sort_order",{ascending:!0}),d=>Array.isArray(d)?d:[]);if(!rows.ok)return rows;let visible=rows.data.filter(r=>surfaceActionVisible(r,options.organizationIds)),chain=[...names].reverse().map(name=>({surfaceName:name,actions:visible.filter(r=>r.surface_name===name).map(surfaceActionRowToAction)}));try{return{ok:!0,data:resolveActionChain(chain,{maxDepth})}}catch(err){return fail("surface_chain",err instanceof Error?err.message:String(err))}}function fail2(code,message,retryable=!1,diagnostic){return{ok:!1,error:diagnostic?{code,message,retryable,diagnostic}:{code,message,retryable}}}function messageOf(error){return error instanceof Error&&error.message.trim()?error.message:typeof error=="string"&&error.trim()?error:"The run failed without saying why."}function newRunId(){let c=globalThis.crypto;return c?.randomUUID?c.randomUUID():`run-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,10)}`}function fillActionTarget(template,input){let source=input&&typeof input=="object"&&!Array.isArray(input)?input:{},missing=[];return{value:template.replace(/\{([A-Za-z_][A-Za-z0-9_]*)\}/g,(_,key)=>{let v=source[key];return v==null||v===""||typeof v=="object"?(missing.push(key),""):encodeURIComponent(String(v))}),missing}}function fillName(template,input){let filled=fillActionTarget(template,input);return{value:decodeURIComponent(filled.value),missing:filled.missing}}function toJson(value){return value===void 0?null:JSON.parse(JSON.stringify(value))}function refuseCaller(info,by){return info.triggeredBy.includes(by)?info.policy==="manual"&&by!=="person"?{code:"manual_only",message:`"${info.label}" (${info.name}) runs only when a person starts it.`,retryable:!1}:null:{code:"caller_not_allowed",message:`"${info.label}" (${info.name}) can't be started by ${CALLER_WORDS[by]}; it allows ${info.triggeredBy.join(", ")||"no one"}.`,retryable:!1}}var CALLER_WORDS={person:"a person",agent:"an agent",applet:"an applet",automation:"an automation"};function absent(info,capability){return fail2("capability_absent",`"${info.label}" can't run here: ${capability} isn't available on this page.`)}async function runSurfaceAction(info,input,by,ports,options={}){let refusal=refuseCaller(info,by);if(refusal)return{ok:!1,error:refusal};let context={runId:options.runId??newRunId(),by,viewerId:options.viewerId??null,surfaceName:info.declaredOn,policy:info.policy},capture=(error,stage)=>{try{ports.diagnostics.capture(error,{area:"actions",detail:{action:info.name,runs:info.runs,stage,runId:context.runId}})}catch{}};try{if(info.source==="write-target"&&ports.door){let receipt=await ports.door.write({surfaceName:info.declaredOn,target:info.name,...info.itemType?{itemType:info.itemType}:{},...options.itemIdentity?{itemIdentity:options.itemIdentity}:{},...options.ops?{ops:options.ops}:{value:input},by,runId:context.runId},options.signal);return receipt.status==="refused"?{ok:!1,error:{code:receipt.reason,message:receipt.sentence,retryable:receipt.reason==="failed",remedy:receipt.remedy}}:{ok:!0,data:receipt}}if(info.policy==="ask"&&by!=="person"&&info.runs!=="server"){if(!ports.approvals)return fail2("needs_approval",`"${info.label}" needs a person's approval, and there is no one to ask here.`);if(!await ports.approvals.ask({action:info.name,label:info.label,description:info.description,input:toJson(input),by,surfaceName:info.declaredOn}))return fail2("declined",`"${info.label}" was not approved, so it did not run.`)}switch(info.runs){case"server":{if(!ports.serverActions)return absent(info,"server actions");let filled=fillName(info.target??info.name,input);if(filled.missing.length)return fail2("input_missing",`"${info.label}" needs ${filled.missing.join(", ")}.`);let response=await ports.serverActions.run({name:filled.value,input:toJson(input),by,surface_name:info.declaredOn,policy:info.policy});return response.status==="refused"?fail2("refused",response.message):{ok:!0,data:response}}case"mandate":{if(!ports.mandates)return absent(info,"running a job");if(!info.target)return fail2("target_missing",`"${info.label}" names no job to run.`);let filled=fillName(info.target,input);if(filled.missing.length)return fail2("input_missing",`"${info.label}" needs ${filled.missing.join(", ")}.`);let record=input&&typeof input=="object"&&!Array.isArray(input)?input:{},variables=/\{[A-Za-z_]/.test(info.target)?record.variables&&typeof record.variables=="object"?toJson(record.variables):{}:toJson(record),userInput=typeof record.userInput=="string"?record.userInput:void 0;return{ok:!0,data:await ports.mandates.run({alias:filled.value,variables,...userInput!==void 0?{userInput}:{},by,policy:info.policy})}}case"navigate":{if(!ports.overlay)return absent(info,"navigation");let filled=fillActionTarget(info.target??"",input);if(filled.missing.length)return fail2("input_missing",`"${info.label}" needs ${filled.missing.join(", ")}.`);let to=info.target&&/^\{[A-Za-z_][A-Za-z0-9_]*\}$/.test(info.target)?decodeURIComponent(filled.value):filled.value;return to?(ports.overlay.navigate(to),{ok:!0,data:{to}}):fail2("target_missing",`"${info.label}" names no page to open.`)}case"page":return ports.pageActions?await ports.pageActions.dispatch(info.name,input,context):absent(info,"this page's actions");default:{let runs=info.runs;return fail2("runs_unknown",`"${info.label}" runs as "${String(runs)}", which nothing here knows how to run.`)}}}catch(error){return capture(error,"run"),fail2("run_failed",messageOf(error),!0)}}var PAGE_ACTION_STREAM="actions",valueStream=name=>`value:${name}`;function clone(value){if(value===void 0)return value;let sc=globalThis.structuredClone;return sc?sc(value):JSON.parse(JSON.stringify(value))}function errorOf(err,code="surface_failed"){return{ok:!1,error:{code,message:err instanceof Error&&err.message?err.message:String(err),retryable:!1}}}var sequence=0;function defaultMint(prefix){let c=globalThis.crypto;return c?.randomUUID?`${prefix}-${c.randomUUID()}`:`${prefix}-${Date.now().toString(36)}-${(sequence++).toString(36)}`}function createSurfacePort(opts){let mint=opts.mintId??defaultMint,pending=opts.pending??new Map,parked=new Set,values=new Map,mounted=null,disposed=!1,report=(error,stage,detail={})=>{try{opts.ports.diagnostics.capture(error,{area:"actions",detail:{stage:`surface.${stage}`,...detail}})}catch{}},memberships=async()=>{if(opts.organizationIds)try{return await opts.organizationIds()}catch(err){report(err,"organizationIds");return}},read2=async name=>{let organizationIds=await memberships();return readSurfaceChain(opts.client,name,{...opts.maxDepth!==void 0?{maxDepth:opts.maxDepth}:{},...organizationIds?{organizationIds}:{}})},currentChain=async fresh=>{if(mounted&&!fresh)return{ok:!0,data:mounted.chain};let name=mounted?.name??(opts.surfaceName?await opts.surfaceName():null);if(!name)return{ok:!1,error:{code:"not_mounted",message:"No surface is mounted here yet.",retryable:!1}};let chain=await read2(name);return chain.ok&&mounted?.name===name&&(mounted.chain=chain.data),chain},awaitPageAction=runId=>disposed?Promise.resolve({ok:!1,error:{code:"host_disposed",message:"This page has closed.",retryable:!1}}):new Promise(resolve=>{parked.add(runId),pending.set(runId,resolve)}),selfRouted={dispatch(name,input,context){let waiting=awaitPageAction(context.runId),event={action:name,input,runId:context.runId,by:context.by},forget=()=>{pending.delete(context.runId),parked.delete(context.runId)};if(disposed)return forget(),Promise.resolve({ok:!1,error:{code:"host_disposed",message:"This page closed, so the action did not run.",retryable:!1}});let heard;try{heard=opts.emit(PAGE_ACTION_STREAM,event)}catch(err){throw forget(),err}return heard!==!0?(forget(),Promise.resolve({ok:!1,error:{code:"action_unhandled",message:`No page is open to run "${name}".`,retryable:!1}})):waiting}},port={async mount(name){try{let chain=await read2(name);if(!chain.ok)return chain;if(opts.seedValues)try{let seeds=await opts.seedValues();for(let[key,v]of Object.entries(seeds))values.has(key)||values.set(key,clone(v))}catch(err){report(err,"seedValues",{surface:name})}return mounted={mountId:mint("mount"),name,chain:chain.data},{ok:!0,data:{mountId:mounted.mountId}}}catch(err){return errorOf(err)}},async unmount(mountId){mounted?.mountId===mountId&&(mounted=null)},async getValue(name){return clone(values.get(name))},async setValue(name,v){let copy;try{copy=clone(v)}catch(err){report(err,"setValue",{name});return}values.set(name,copy);try{opts.emit(valueStream(name),clone(v))}catch(err){report(err,"emit",{stream:valueStream(name)})}},async listActions(){try{let chain=await currentChain(!1);return chain.ok?clone(chain.data):(report(chain.error,"listActions"),[])}catch(err){return report(err,"listActions"),[]}},async runAction(name,input,by){let info;try{let known=await currentChain(!1);if(info=known.ok?known.data.find(a=>a.name===name):void 0,!info){let fresh=await currentChain(!0);if(!fresh.ok)return fresh;info=fresh.data.find(a=>a.name===name)}}catch(err){return errorOf(err)}if(!info)return{ok:!1,error:{code:"action_not_found",message:`This page has no action named "${name}".`,retryable:!1}};let viewerId=null;try{viewerId=opts.viewerId?await opts.viewerId():null}catch(err){report(err,"viewerId")}let ports={...opts.ports,pageActions:opts.pageActions??selfRouted},result=await runSurfaceAction(info,input,by,ports,{runId:mint("act"),viewerId});return result.ok&&info.updatesValue&&await port.setValue(info.updatesValue,result.data),result},async completeAction(runId,result){let resolve=pending.get(runId);if(!resolve){report(new Error(`completeAction for run "${runId}", which is not waiting (already answered, never routed here, or the page closed).`),"completeAction",{runId});return}pending.delete(runId),parked.delete(runId),resolve?.(result)},awaitPageAction,dispose(){disposed=!0;for(let runId of parked){let resolve=pending.get(runId);pending.delete(runId),resolve?.({ok:!1,error:{code:"host_disposed",message:"This page closed before the action finished.",retryable:!1}})}parked.clear(),mounted=null,values.clear()}};return port}
1
+ "use strict";var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to};var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod);var surface_exports={};__export(surface_exports,{APPLET_SURFACE_PREFIX:()=>APPLET_SURFACE_PREFIX,PAGE_ACTION_STREAM:()=>PAGE_ACTION_STREAM,createSurfacePort:()=>createSurfacePort,readSurfaceChain:()=>readSurfaceChain,surfaceActionVisible:()=>surfaceActionVisible,valueStream:()=>valueStream});module.exports=__toCommonJS(surface_exports);var DEFAULT_MAX_INHERITANCE_DEPTH=3;var APPLET_CATEGORY="applet";function compact(value){return Object.fromEntries(Object.entries(value).filter(([,v])=>v!==void 0))}function surfaceActionRowToAction(row){return compact({name:row.name,category:row.category,label:row.label,description:row.description,input:row.input??void 0,output:row.output??void 0,runs:row.runs,target:row.target??void 0,triggeredBy:[...row.triggered_by],policy:row.policy,updatesValue:row.updates_value??void 0,itemType:row.item_type||void 0,sortOrder:row.sort_order,origin:compact({source:"surface-action",surfaceName:row.surface_name,surfaceId:row.surface_id,id:row.id,declaredBy:row.declared_by,codeRef:row.code_ref??void 0,organizationId:row.organization_id??void 0})})}function toActionInfo(declaration,declaredOn){return compact({name:declaration.name,category:declaration.category??APPLET_CATEGORY,label:declaration.label,description:declaration.description,runs:declaration.runs,policy:declaration.policy,triggeredBy:[...declaration.triggeredBy],input:declaration.input,output:declaration.output,declaredOn,target:declaration.target,updatesValue:declaration.updatesValue,itemType:declaration.itemType,source:declaration.origin?.source})}function resolveActionChain(chain,options={}){let maxDepth=options.maxDepth??DEFAULT_MAX_INHERITANCE_DEPTH,names=chain.map(link=>link.surfaceName),seen=new Set;for(let name of names){if(seen.has(name))throw new Error(`[actions] inheritance CYCLE detected at "${name}". Fix the surface chain: ${names.join(" \u2192 ")}`);seen.add(name)}if(chain.length-1>maxDepth)throw new Error(`[actions] inheritance chain for "${names[names.length-1]}" exceeds depth ${maxDepth}: ${names.join(" \u2192 ")}`);let byName=new Map;for(let link of chain){let own=new Set;for(let action of link.actions){if(own.has(action.name))throw new Error(`[actions] surface "${link.surfaceName}" declares Action "${action.name}" twice. Names are unique; nothing was overwritten.`);own.add(action.name),byName.set(action.name,toActionInfo(action,link.surfaceName))}}return[...byName.values()]}function asSurfaceClient(client){let c=client;if(typeof c.schema!="function")throw new Error("[alchemy/surface] the surface runtime needs a supabase-js v2 client carrying the viewer's session (it has no schema()).");return c}function readError(error,code="read_failed"){let out={code:error.code?`${code}:${error.code}`:code,message:error.message,retryable:!1};return error.details&&(out.diagnostic=error.details),out}async function read(query,shape){try{let answer=await query();return answer.error?{ok:!1,error:readError(answer.error)}:{ok:!0,data:shape(answer.data)}}catch(err){return{ok:!1,error:{code:"unreachable",message:err instanceof Error?err.message:String(err),retryable:!0}}}}function isRecord(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}var APPLET_SURFACE_PREFIX="applets/";function fail(code,message,retryable=!1){return{ok:!1,error:{code,message,retryable}}}function surfaceActionVisible(row,organizationIds){return row.declared_by==="code"||row.surface_name.startsWith(APPLET_SURFACE_PREFIX)?!0:typeof row.organization_id=="string"&&(organizationIds??[]).includes(row.organization_id)}async function readSurfaceChain(client,surfaceName,options={}){let db;try{db=asSurfaceClient(client)}catch(err){return fail("client_invalid",err instanceof Error?err.message:String(err))}let maxDepth=options.maxDepth??DEFAULT_MAX_INHERITANCE_DEPTH,names=[],at=surfaceName;for(;at;){if(names.includes(at))return fail("surface_cycle",`The surface chain loops at "${at}": ${[...names,at].join(" \u2192 ")}.`);if(names.length>maxDepth)return fail("surface_too_deep",`The surface chain for "${surfaceName}" is deeper than ${maxDepth}: ${[...names,at].join(" \u2192 ")}.`);let name=at,row=await read(()=>db.schema("ui").from("ui_surface").select("name, parent_surface_name").eq("name",name).is("deleted_at",null).maybeSingle(),d=>d);if(!row.ok)return row;if(!isRecord(row.data))return fail("surface_not_found",names.length===0?`The surface "${at}" does not exist yet; saving the Applet writes it (ui.save_applet_surface).`:`The surface "${names[names.length-1]}" names a parent "${at}" that does not exist.`);names.push(at);let parent=row.data.parent_surface_name;at=typeof parent=="string"&&parent?parent:null}let rows=await read(()=>db.schema("ui").from("ui_surface_action").select("*").in("surface_name",names).is("deleted_at",null).order("sort_order",{ascending:!0}),d=>Array.isArray(d)?d:[]);if(!rows.ok)return rows;let visible=rows.data.filter(r=>surfaceActionVisible(r,options.organizationIds)),chain=[...names].reverse().map(name=>({surfaceName:name,actions:visible.filter(r=>r.surface_name===name).map(surfaceActionRowToAction)}));try{return{ok:!0,data:resolveActionChain(chain,{maxDepth})}}catch(err){return fail("surface_chain",err instanceof Error?err.message:String(err))}}function fail2(code,message,retryable=!1,diagnostic){return{ok:!1,error:diagnostic?{code,message,retryable,diagnostic}:{code,message,retryable}}}function messageOf(error){return error instanceof Error&&error.message.trim()?error.message:typeof error=="string"&&error.trim()?error:"The run failed without saying why."}function newRunId(){let c=globalThis.crypto;return c?.randomUUID?c.randomUUID():`run-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,10)}`}function fillActionTarget(template,input){let source=input&&typeof input=="object"&&!Array.isArray(input)?input:{},missing=[];return{value:template.replace(/\{([A-Za-z_][A-Za-z0-9_]*)\}/g,(_,key)=>{let v=source[key];return v==null||v===""||typeof v=="object"?(missing.push(key),""):encodeURIComponent(String(v))}),missing}}function fillName(template,input){let filled=fillActionTarget(template,input);return{value:decodeURIComponent(filled.value),missing:filled.missing}}function toJson(value){return value===void 0?null:JSON.parse(JSON.stringify(value))}function refuseCaller(info,by){return info.triggeredBy.includes(by)?info.policy==="manual"&&by!=="person"?{code:"manual_only",message:`"${info.label}" (${info.name}) runs only when a person starts it.`,retryable:!1}:null:{code:"caller_not_allowed",message:`"${info.label}" (${info.name}) can't be started by ${CALLER_WORDS[by]}; it allows ${info.triggeredBy.join(", ")||"no one"}.`,retryable:!1}}var CALLER_WORDS={person:"a person",agent:"an agent",applet:"an applet",automation:"an automation"};function absent(info,capability){return fail2("capability_absent",`"${info.label}" can't run here: ${capability} isn't available on this page.`)}async function runSurfaceAction(info,input,by,ports,options={}){let refusal=refuseCaller(info,by);if(refusal)return{ok:!1,error:refusal};let context={runId:options.runId??newRunId(),by,viewerId:options.viewerId??null,surfaceName:info.declaredOn,policy:info.policy},capture=(error,stage)=>{try{ports.diagnostics.capture(error,{area:"actions",detail:{action:info.name,runs:info.runs,stage,runId:context.runId}})}catch{}};try{if(info.source==="write-target"&&ports.door){let receipt=await ports.door.write({surfaceName:info.declaredOn,target:info.name,...info.itemType?{itemType:info.itemType}:{},...options.itemIdentity?{itemIdentity:options.itemIdentity}:{},...options.ops?{ops:options.ops}:{value:input},by,runId:context.runId},options.signal);return receipt.status==="refused"?{ok:!1,error:{code:receipt.reason,message:receipt.sentence,retryable:receipt.reason==="failed",remedy:receipt.remedy}}:{ok:!0,data:receipt}}if(info.policy==="ask"&&by!=="person"&&info.runs!=="server"){if(!ports.approvals)return fail2("needs_approval",`"${info.label}" needs a person's approval, and there is no one to ask here.`);if(!await ports.approvals.ask({action:info.name,label:info.label,description:info.description,input:toJson(input),by,surfaceName:info.declaredOn,runId:context.runId}))return fail2("declined",`"${info.label}" was not approved, so it did not run.`)}switch(info.runs){case"server":{if(!ports.serverActions)return absent(info,"server actions");let filled=fillName(info.target??info.name,input);if(filled.missing.length)return fail2("input_missing",`"${info.label}" needs ${filled.missing.join(", ")}.`);let response=await ports.serverActions.run({name:filled.value,input:toJson(input),by,surface_name:info.declaredOn,policy:info.policy});return response.status==="refused"?fail2("refused",response.message):{ok:!0,data:response}}case"mandate":{if(!ports.mandates)return absent(info,"running a job");if(!info.target)return fail2("target_missing",`"${info.label}" names no job to run.`);let filled=fillName(info.target,input);if(filled.missing.length)return fail2("input_missing",`"${info.label}" needs ${filled.missing.join(", ")}.`);let record=input&&typeof input=="object"&&!Array.isArray(input)?input:{},variables=/\{[A-Za-z_]/.test(info.target)?record.variables&&typeof record.variables=="object"?toJson(record.variables):{}:toJson(record),userInput=typeof record.userInput=="string"?record.userInput:void 0;return{ok:!0,data:await ports.mandates.run({alias:filled.value,variables,...userInput!==void 0?{userInput}:{},by,policy:info.policy})}}case"navigate":{if(!ports.overlay)return absent(info,"navigation");let filled=fillActionTarget(info.target??"",input);if(filled.missing.length)return fail2("input_missing",`"${info.label}" needs ${filled.missing.join(", ")}.`);let to=info.target&&/^\{[A-Za-z_][A-Za-z0-9_]*\}$/.test(info.target)?decodeURIComponent(filled.value):filled.value;return to?(ports.overlay.navigate(to),{ok:!0,data:{to}}):fail2("target_missing",`"${info.label}" names no page to open.`)}case"page":return ports.pageActions?await ports.pageActions.dispatch(info.name,input,context):absent(info,"this page's actions");default:{let runs=info.runs;return fail2("runs_unknown",`"${info.label}" runs as "${String(runs)}", which nothing here knows how to run.`)}}}catch(error){return capture(error,"run"),fail2("run_failed",messageOf(error),!0)}}var PAGE_ACTION_STREAM="actions",valueStream=name=>`value:${name}`;function clone(value){if(value===void 0)return value;let sc=globalThis.structuredClone;return sc?sc(value):JSON.parse(JSON.stringify(value))}function errorOf(err,code="surface_failed"){return{ok:!1,error:{code,message:err instanceof Error&&err.message?err.message:String(err),retryable:!1}}}var sequence=0;function defaultMint(prefix){let c=globalThis.crypto;return c?.randomUUID?`${prefix}-${c.randomUUID()}`:`${prefix}-${Date.now().toString(36)}-${(sequence++).toString(36)}`}function createSurfacePort(opts){let mint=opts.mintId??defaultMint,pending=opts.pending??new Map,parked=new Set,values=new Map,mounted=null,disposed=!1,report=(error,stage,detail={})=>{try{opts.ports.diagnostics.capture(error,{area:"actions",detail:{stage:`surface.${stage}`,...detail}})}catch{}},memberships=async()=>{if(opts.organizationIds)try{return await opts.organizationIds()}catch(err){report(err,"organizationIds");return}},read2=async name=>{let organizationIds=await memberships();return readSurfaceChain(opts.client,name,{...opts.maxDepth!==void 0?{maxDepth:opts.maxDepth}:{},...organizationIds?{organizationIds}:{}})},currentChain=async fresh=>{if(mounted&&!fresh)return{ok:!0,data:mounted.chain};let name=mounted?.name??(opts.surfaceName?await opts.surfaceName():null);if(!name)return{ok:!1,error:{code:"not_mounted",message:"No surface is mounted here yet.",retryable:!1}};let chain=await read2(name);return chain.ok&&mounted?.name===name&&(mounted.chain=chain.data),chain},awaitPageAction=runId=>disposed?Promise.resolve({ok:!1,error:{code:"host_disposed",message:"This page has closed.",retryable:!1}}):new Promise(resolve=>{parked.add(runId),pending.set(runId,resolve)}),selfRouted={dispatch(name,input,context){let waiting=awaitPageAction(context.runId),event={action:name,input,runId:context.runId,by:context.by},forget=()=>{pending.delete(context.runId),parked.delete(context.runId)};if(disposed)return forget(),Promise.resolve({ok:!1,error:{code:"host_disposed",message:"This page closed, so the action did not run.",retryable:!1}});let heard;try{heard=opts.emit(PAGE_ACTION_STREAM,event)}catch(err){throw forget(),err}return heard!==!0?(forget(),Promise.resolve({ok:!1,error:{code:"action_unhandled",message:`No page is open to run "${name}".`,retryable:!1}})):waiting}},port={async mount(name){try{let chain=await read2(name);if(!chain.ok)return chain;if(opts.seedValues)try{let seeds=await opts.seedValues();for(let[key,v]of Object.entries(seeds))values.has(key)||values.set(key,clone(v))}catch(err){report(err,"seedValues",{surface:name})}return mounted={mountId:mint("mount"),name,chain:chain.data},{ok:!0,data:{mountId:mounted.mountId}}}catch(err){return errorOf(err)}},async unmount(mountId){mounted?.mountId===mountId&&(mounted=null)},async getValue(name){return clone(values.get(name))},async setValue(name,v){let copy;try{copy=clone(v)}catch(err){report(err,"setValue",{name});return}values.set(name,copy);try{opts.emit(valueStream(name),clone(v))}catch(err){report(err,"emit",{stream:valueStream(name)})}},async listActions(){try{let chain=await currentChain(!1);return chain.ok?clone(chain.data):(report(chain.error,"listActions"),[])}catch(err){return report(err,"listActions"),[]}},async runAction(name,input,by){let info;try{let known=await currentChain(!1);if(info=known.ok?known.data.find(a=>a.name===name):void 0,!info){let fresh=await currentChain(!0);if(!fresh.ok)return fresh;info=fresh.data.find(a=>a.name===name)}}catch(err){return errorOf(err)}if(!info)return{ok:!1,error:{code:"action_not_found",message:`This page has no action named "${name}".`,retryable:!1}};let viewerId=null;try{viewerId=opts.viewerId?await opts.viewerId():null}catch(err){report(err,"viewerId")}let ports={...opts.ports,pageActions:opts.pageActions??selfRouted},result=await runSurfaceAction(info,input,by,ports,{runId:mint("act"),viewerId});return result.ok&&info.updatesValue&&await port.setValue(info.updatesValue,result.data),result},async completeAction(runId,result){let resolve=pending.get(runId);if(!resolve){report(new Error(`completeAction for run "${runId}", which is not waiting (already answered, never routed here, or the page closed).`),"completeAction",{runId});return}pending.delete(runId),parked.delete(runId),resolve?.(result)},awaitPageAction,dispose(){disposed=!0;for(let runId of parked){let resolve=pending.get(runId);pending.delete(runId),resolve?.({ok:!1,error:{code:"host_disposed",message:"This page closed before the action finished.",retryable:!1}})}parked.clear(),mounted=null,values.clear()}};return port}
2
2
  //# sourceMappingURL=surface.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/surface.ts","../src/declare/registry.ts","../src/actions/declarations.ts","../src/surface/client.ts","../src/surface/chain.ts","../src/actions/surface-run.ts","../src/surface/port.ts"],"sourcesContent":["/**\n * `@ai-matrx/alchemy/surface` — the one SurfacePort runtime (Applets CONTRACTS §6, owner AP-2).\n * Framework-neutral: zero React, zero `@ai-matrx/agents`, zero supabase import (the viewer's\n * client is typed structurally).\n *\n * readSurfaceChain(client, name, { maxDepth?, organizationIds? }) the chain's Actions, from the database\n * createSurfacePort(opts) mount / values / list / run / complete\n */\nexport {\n APPLET_SURFACE_PREFIX,\n readSurfaceChain,\n surfaceActionVisible,\n type ReadSurfaceChainOptions,\n} from \"./surface/chain\";\nexport {\n PAGE_ACTION_STREAM,\n createSurfacePort,\n valueStream,\n type CreateSurfacePortOptions,\n type PageActionEvent,\n type PendingPageActions,\n type SurfacePortHandle,\n type SurfacePortRunPorts,\n} from \"./surface/port\";\nexport type { SurfaceClient, SurfaceReadAnswer, SurfaceReadChain } from \"./surface/client\";\nexport type {\n ActionCaller,\n ActionContext,\n ActionInfo,\n ActionResult,\n MandateRunRef,\n SurfacePort,\n} from \"./actions/applet-types\";\n","/**\n * The declaration registry: inheritance, baseline injection, provenance and\n * group synthesis (CONTRACT §2.1 `DeclarationRegistry`). Ported verbatim in\n * behaviour from matrx-frontend `features/surfaces/manifests/registry.ts`\n * (ALC-14); the app now supplies its declarations and baselines.\n *\n * Guards are LOUD by design: an unknown parent, a cycle, a chain deeper than\n * the limit, a reserved or duplicate group key, a value naming an undeclared\n * group, or a reserved platform name throws on resolution — a build fails\n * instead of silently dropping values.\n */\nimport { declarationHash } from \"./hash\";\nimport type {\n DeclarationExtension,\n DeclarationIssue,\n DeclaredValue,\n ResolvedSurfaceDeclaration,\n ResolvedValue,\n Sensitivity,\n SurfaceDeclaration,\n ValueGroup,\n ValueProvenance,\n} from \"./types\";\nimport { validateDeclarations, type ValidateOptions } from \"./validate\";\n\nexport const RESERVED_GROUP_KEYS = {\n general: \"general\",\n baseline: \"baseline\",\n inheritedPrefix: \"inherited:\",\n} as const;\n\n/** Group sortOrder bands. Curated groups author 0–899; the rest is reserved. */\nexport const GENERAL_GROUP_ORDER = 850;\nexport const INHERITED_GROUP_ORDER_BASE = 9000;\nexport const BASELINE_GROUP_ORDER = 9900;\nexport const DEFAULT_MAX_INHERITANCE_DEPTH = 3;\n\nimport { DEFAULT_SENSITIVITY, resolveSensitivity } from \"./sensitivity\";\nexport { DEFAULT_SENSITIVITY, resolveSensitivity };\n\nexport type Resolved<D extends SurfaceDeclaration> = Omit<D, \"values\" | \"groups\"> & {\n values: readonly (D[\"values\"][number] & ResolvedValue)[];\n groups: readonly ValueGroup[];\n contentHash: string;\n};\n\nexport interface DeclarationRegistryOptions {\n /** Generic values injected into every declaration unless `skipBaselineValues`. */\n baselineValues?: readonly DeclaredValue[];\n /** Value names only the platform writes; a declaration claiming one is refused. */\n reservedValueNames?: readonly string[];\n /** Write-target names only the platform writes. */\n reservedWriteTargetNames?: readonly string[];\n maxInheritanceDepth?: number;\n}\n\nexport interface DeclarationRegistry<D extends SurfaceDeclaration> {\n /** Refuses a second declaration of the same surfaceName. */\n register(declaration: D): void;\n registerExtension(extension: DeclarationExtension<D>): void;\n extensions(): readonly DeclarationExtension<D>[];\n /** Declarations exactly as authored (inheritance unresolved, no baselines). */\n raw(): readonly D[];\n getRaw(surfaceName: string): D | undefined;\n /** Parent chain, ROOT FIRST. Unknown surfaces return []. */\n ancestry(surfaceName: string): string[];\n /** Direct children (declarations naming it in `inheritsFrom`), in declaration order. */\n children(surfaceName: string): string[];\n /** Inheritance + baseline + groups resolved; provenance kept. */\n all(): readonly Resolved<D>[];\n get(surfaceName: string): Resolved<D> | undefined;\n validate(options?: ValidateOptions): readonly DeclarationIssue[];\n}\n\nexport function createDeclarationRegistry<D extends SurfaceDeclaration>(\n options: DeclarationRegistryOptions = {},\n): DeclarationRegistry<D> {\n const baselineValues = options.baselineValues ?? [];\n const baselineNames = new Set(baselineValues.map((v) => v.name));\n const reservedValues = new Set(options.reservedValueNames ?? []);\n const reservedTargets = new Set(options.reservedWriteTargetNames ?? []);\n const maxDepth = options.maxInheritanceDepth ?? DEFAULT_MAX_INHERITANCE_DEPTH;\n const declarations: D[] = [];\n const index = new Map<string, D>();\n const extensionList: DeclarationExtension<D>[] = [];\n let resolvedCache: Resolved<D>[] | null = null;\n let resolvedIndex = new Map<string, Resolved<D>>();\n\n function ancestry(surfaceName: string): string[] {\n const chain: string[] = [];\n const seen = new Set<string>([surfaceName]);\n let current = index.get(surfaceName)?.inheritsFrom;\n while (current) {\n if (seen.has(current)) {\n throw new Error(\n `[surfaces] inheritsFrom CYCLE detected at \"${current}\" (from \"${surfaceName}\"). ` +\n `Fix the manifest chain: ${[...seen].join(\" → \")} → ${current}`,\n );\n }\n const parent = index.get(current);\n if (!parent) {\n throw new Error(\n `[surfaces] \"${surfaceName}\" inheritsFrom unknown surface \"${current}\" — ` +\n `the parent must be a registered declaration`,\n );\n }\n seen.add(current);\n chain.unshift(current);\n if (chain.length > maxDepth) {\n throw new Error(\n `[surfaces] inheritance chain for \"${surfaceName}\" exceeds depth ${maxDepth}: ` + chain.join(\" → \"),\n );\n }\n current = parent.inheritsFrom;\n }\n return chain;\n }\n\n function groupKeyFor(value: DeclaredValue, provenance: ValueProvenance): string {\n if (provenance.kind === \"own\" && value.group) return value.group;\n if (baselineNames.has(value.name)) return RESERVED_GROUP_KEYS.baseline;\n if (provenance.kind === \"inherited\") return `${RESERVED_GROUP_KEYS.inheritedPrefix}${provenance.from}`;\n if (provenance.kind === \"baseline\") return RESERVED_GROUP_KEYS.baseline;\n return RESERVED_GROUP_KEYS.general;\n }\n\n function resolve(declaration: D): Resolved<D> {\n const chain = ancestry(declaration.surfaceName);\n const lineage: { layer: D; from: string | null }[] = [\n ...chain.map((name) => {\n const layer = index.get(name);\n if (!layer) throw new Error(`[surfaces] missing inherited declaration \"${name}\"`);\n return { layer, from: name };\n }),\n { layer: declaration, from: null },\n ];\n\n const curatedKeys = new Set<string>();\n for (const group of declaration.groups ?? []) {\n if (\n group.key === RESERVED_GROUP_KEYS.general ||\n group.key === RESERVED_GROUP_KEYS.baseline ||\n group.key.startsWith(RESERVED_GROUP_KEYS.inheritedPrefix)\n ) {\n throw new Error(\n `[surfaces] \"${declaration.surfaceName}\" declares reserved group key \"${group.key}\" — ` +\n `general/baseline/inherited:* are synthesized by the registry`,\n );\n }\n if (curatedKeys.has(group.key)) {\n throw new Error(`[surfaces] \"${declaration.surfaceName}\" declares duplicate group key \"${group.key}\"`);\n }\n curatedKeys.add(group.key);\n }\n for (const value of declaration.values) {\n if (reservedValues.has(value.name)) {\n throw new Error(\n `[surfaces] \"${declaration.surfaceName}\" declares value \"${value.name}\" — that name is written by the platform; choose another name`,\n );\n }\n if (value.group && !curatedKeys.has(value.group)) {\n throw new Error(\n `[surfaces] \"${declaration.surfaceName}\" value \"${value.name}\" references undeclared ` +\n `group \"${value.group}\" — declare it in the manifest's \\`groups\\``,\n );\n }\n }\n for (const target of declaration.writeTargets ?? []) {\n if (reservedTargets.has(target.name)) {\n throw new Error(\n `[surfaces] \"${declaration.surfaceName}\" declares write target \"${target.name}\" — that name is written by the platform; choose another name`,\n );\n }\n }\n\n const valuesByName = new Map<string, D[\"values\"][number] & ResolvedValue>();\n const inheritedLists = new Map<string, Map<string, unknown>>();\n const coreLists: { field: string; identity(entry: unknown): string }[] = [\n { field: \"configNamespaces\", identity: (entry) => (entry as { namespace: string }).namespace },\n {\n field: \"evidenceSources\",\n identity: (entry) => `${(entry as { kind: string }).kind}:${(entry as { idValue: string }).idValue}`,\n },\n ];\n const lists = [...coreLists, ...extensionList.flatMap((extension) => extension.inheritedLists ?? [])];\n for (const { layer, from } of lineage) {\n for (const value of layer.values) {\n const provenance: ValueProvenance = from ? { kind: \"inherited\", from } : { kind: \"own\" };\n valuesByName.set(value.name, {\n ...value,\n provenance,\n groupKey: groupKeyFor(value, provenance),\n resolvedSensitivity: resolveSensitivity(value),\n });\n }\n for (const list of lists) {\n const entries = (layer as unknown as Record<string, unknown>)[list.field];\n if (!Array.isArray(entries)) continue;\n const byIdentity = inheritedLists.get(list.field) ?? new Map<string, unknown>();\n for (const entry of entries) byIdentity.set(list.identity(entry), entry);\n inheritedLists.set(list.field, byIdentity);\n }\n }\n\n if (!declaration.skipBaselineValues) {\n for (const baseline of baselineValues) {\n if (valuesByName.has(baseline.name)) continue;\n valuesByName.set(baseline.name, {\n ...baseline,\n provenance: { kind: \"baseline\" },\n groupKey: RESERVED_GROUP_KEYS.baseline,\n resolvedSensitivity: resolveSensitivity(baseline),\n } as D[\"values\"][number] & ResolvedValue);\n }\n }\n\n const usedGroupKeys = new Set(Array.from(valuesByName.values(), (value) => value.groupKey));\n const groups: ValueGroup[] = [...(declaration.groups ?? [])];\n if (usedGroupKeys.has(RESERVED_GROUP_KEYS.general)) {\n groups.push({ key: RESERVED_GROUP_KEYS.general, label: \"General\", sortOrder: GENERAL_GROUP_ORDER });\n }\n for (let i = chain.length - 1, distance = 0; i >= 0; i -= 1, distance += 1) {\n const parentName = chain[i]!;\n const key = `${RESERVED_GROUP_KEYS.inheritedPrefix}${parentName}`;\n if (!usedGroupKeys.has(key)) continue;\n const parentLabel = index.get(parentName)?.label ?? parentName;\n groups.push({\n key,\n label: `Inherited from ${parentLabel}`,\n sortOrder: INHERITED_GROUP_ORDER_BASE + distance * 10,\n });\n }\n if (usedGroupKeys.has(RESERVED_GROUP_KEYS.baseline)) {\n groups.push({ key: RESERVED_GROUP_KEYS.baseline, label: \"Generic baselines\", sortOrder: BASELINE_GROUP_ORDER });\n }\n groups.sort((a, b) => a.sortOrder - b.sortOrder);\n\n const groupOrder = new Map(groups.map((group) => [group.key, group.sortOrder] as const));\n const values = Array.from(valuesByName.values()).sort((a, b) => {\n const ga = groupOrder.get(a.groupKey) ?? GENERAL_GROUP_ORDER;\n const gb = groupOrder.get(b.groupKey) ?? GENERAL_GROUP_ORDER;\n if (ga !== gb) return ga - gb;\n return (a.sortOrder ?? 1000) - (b.sortOrder ?? 1000);\n });\n\n const merged: Record<string, unknown> = {};\n for (const [field, byIdentity] of inheritedLists) {\n if (byIdentity.size > 0) merged[field] = Array.from(byIdentity.values());\n }\n return {\n ...declaration,\n values,\n groups,\n ...merged,\n contentHash: declarationHash(declaration),\n } as Resolved<D>;\n }\n\n function resolvedAll(): Resolved<D>[] {\n if (!resolvedCache) {\n resolvedCache = declarations.map(resolve);\n resolvedIndex = new Map(resolvedCache.map((entry) => [entry.surfaceName, entry] as const));\n }\n return resolvedCache;\n }\n\n const registry: DeclarationRegistry<D> = {\n register(declaration) {\n if (index.has(declaration.surfaceName)) {\n throw new Error(`[surfaces] \"${declaration.surfaceName}\" is declared twice — one surface, one declaration`);\n }\n declarations.push(declaration);\n index.set(declaration.surfaceName, declaration);\n resolvedCache = null;\n },\n registerExtension(extension) {\n if (extensionList.some((existing) => existing.key === extension.key)) {\n throw new Error(`[surfaces] declaration extension \"${extension.key}\" is registered twice`);\n }\n extensionList.push(extension);\n resolvedCache = null;\n },\n extensions: () => extensionList,\n raw: () => declarations,\n getRaw: (surfaceName) => index.get(surfaceName),\n ancestry,\n children: (surfaceName) =>\n declarations.filter((d) => d.inheritsFrom === surfaceName).map((d) => d.surfaceName),\n all: () => resolvedAll(),\n get(surfaceName) {\n resolvedAll();\n return resolvedIndex.get(surfaceName);\n },\n validate: (validateOptions) =>\n validateDeclarations(resolvedAll() as unknown as readonly ResolvedSurfaceDeclaration[], {\n ...validateOptions,\n extensions: [...(validateOptions?.extensions ?? []), ...(extensionList as DeclarationExtension[])],\n baselineNames: validateOptions?.baselineNames ?? [...baselineNames],\n }),\n };\n return registry;\n}\n","/**\n * Every source of an Action projects onto ONE shape (Applets CONTRACTS §6):\n * a declared write target, a client tool row, a `ui.ui_surface_action` row.\n * Pure functions, zero loss (the census in `declarations.test.ts` proves every\n * source field lands). Then the chain: a child surface's Action wins by name.\n */\nimport { DEFAULT_MAX_INHERITANCE_DEPTH } from \"../declare/registry\";\nimport type { Json, WriteTarget } from \"../declare/types\";\nimport type {\n ActionCaller,\n ActionDeclaration,\n ActionDeclaredBy,\n ActionInfo,\n ActionInput,\n ActionOrigin,\n ActionPolicy,\n ActionRuns,\n ActionWriteMode,\n} from \"./applet-types\";\n\n/** Default `category` of an Action that names none. */\nexport const APPLET_CATEGORY = \"applet\";\n\n/** The DB default of `ui.ui_surface_action.triggered_by`, used where a source carries no list. */\nexport const DEFAULT_TRIGGERED_BY: readonly ActionCaller[] = [\"person\", \"agent\", \"applet\"];\n\nexport const ACTION_CALLERS: readonly ActionCaller[] = [\"person\", \"agent\", \"applet\", \"automation\"];\nexport const ACTION_RUNS: readonly ActionRuns[] = [\"page\", \"server\", \"mandate\", \"navigate\"];\nexport const ACTION_POLICIES: readonly ActionPolicy[] = [\"ask\", \"auto\", \"manual\"];\n\n/** A `ui.ui_surface_client_tool` row (live columns that carry meaning). */\nexport interface ClientToolRow {\n id?: string;\n surface_name: string;\n name: string;\n label: string;\n description: string;\n input_schema: Json;\n /** Live values: \"ui\" (applied directly) | \"draft\" (proposed for review). */\n mode: \"ui\" | \"draft\";\n declared_by?: ActionDeclaredBy;\n}\n\n/** A `ui.ui_surface_action` row. */\nexport interface SurfaceActionRow {\n id?: string;\n surface_id: string;\n surface_name: string;\n /** '' = the screen itself. */\n item_type: string;\n name: string;\n category: string;\n label: string;\n description: string;\n input: ActionInput | null;\n output: string | null;\n runs: ActionRuns;\n target: string | null;\n triggered_by: ActionCaller[];\n policy: ActionPolicy;\n updates_value: string | null;\n code_ref: string | null;\n declared_by: ActionDeclaredBy;\n sort_order: number;\n /**\n * The row's organization. A `declared_by='database'` row on a surface outside `applets/` is that\n * organization's EXTENSION and applies only to its members (`readSurfaceChain`, aidream `visible_actions`).\n */\n organization_id?: string | null;\n}\n\ntype Loose<T> = { [K in keyof T]: T[K] | undefined };\n\n/** Drop undefined keys (exactOptionalPropertyTypes: an absent field is absent, never `undefined`). */\nfunction compact<T extends object>(value: Loose<T>): T {\n return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== undefined)) as T;\n}\n\nconst POLICY_OF_APPLY: Record<NonNullable<WriteTarget[\"applyPolicy\"]>, ActionPolicy> = {\n manual: \"manual\",\n ask: \"ask\",\n auto: \"auto\",\n // Filed for an organization's review: never applied without someone saying yes.\n queued: \"ask\",\n};\n\n/** A declared write target → an Action that runs on the page. */\nexport function writeTargetToAction(target: WriteTarget): ActionDeclaration {\n const applyPolicy = target.applyPolicy ?? \"manual\";\n return compact<ActionDeclaration>({\n name: target.name,\n label: target.label,\n description: target.description,\n input: target.valueKind ?? { jsonSchema: target.valueType === \"document\" ? {} : { type: target.valueType } },\n runs: \"page\",\n triggeredBy: [...DEFAULT_TRIGGERED_BY],\n policy: POLICY_OF_APPLY[applyPolicy],\n updatesValue: target.updatesValue,\n mode: target.mode as ActionWriteMode,\n patchable: target.patchable,\n approvalComparison: target.approvalComparison,\n streamOps: target.streamOps ? [...target.streamOps] : undefined,\n destination: target.destination,\n groupKey: target.group,\n itemType: target.itemType,\n applyPolicy: target.applyPolicy,\n valueType: target.valueType,\n valueKind: target.valueKind,\n sortOrder: target.sortOrder,\n origin: { source: \"write-target\", declaredBy: \"code\" },\n });\n}\n\n/** A client tool row → an Action an agent runs on the page (\"draft\" tools are proposed first). */\nexport function clientToolToAction(row: ClientToolRow): ActionDeclaration {\n return compact<ActionDeclaration>({\n name: row.name,\n label: row.label,\n description: row.description,\n input: { jsonSchema: (row.input_schema ?? {}) as object },\n runs: \"page\",\n triggeredBy: [\"agent\"],\n policy: row.mode === \"draft\" ? \"ask\" : \"auto\",\n mode: row.mode,\n origin: compact<ActionOrigin>({ source: \"client-tool\", surfaceName: row.surface_name, id: row.id, declaredBy: row.declared_by }),\n });\n}\n\n/** A `ui.ui_surface_action` row → its Action. */\nexport function surfaceActionRowToAction(row: SurfaceActionRow): ActionDeclaration {\n return compact<ActionDeclaration>({\n name: row.name,\n category: row.category,\n label: row.label,\n description: row.description,\n input: row.input ?? undefined,\n output: row.output ?? undefined,\n runs: row.runs,\n target: row.target ?? undefined,\n triggeredBy: [...row.triggered_by],\n policy: row.policy,\n updatesValue: row.updates_value ?? undefined,\n itemType: row.item_type || undefined,\n sortOrder: row.sort_order,\n origin: compact<ActionOrigin>({\n source: \"surface-action\",\n surfaceName: row.surface_name,\n surfaceId: row.surface_id,\n id: row.id,\n declaredBy: row.declared_by,\n codeRef: row.code_ref ?? undefined,\n organizationId: row.organization_id ?? undefined,\n }),\n });\n}\n\n/** JSON-only view of an Action as declared on `declaredOn`. */\nexport function toActionInfo(declaration: ActionDeclaration, declaredOn: string): ActionInfo {\n return compact<ActionInfo>({\n name: declaration.name,\n category: declaration.category ?? APPLET_CATEGORY,\n label: declaration.label,\n description: declaration.description,\n runs: declaration.runs,\n policy: declaration.policy,\n triggeredBy: [...declaration.triggeredBy],\n input: declaration.input,\n output: declaration.output,\n declaredOn,\n target: declaration.target,\n updatesValue: declaration.updatesValue,\n itemType: declaration.itemType,\n source: declaration.origin?.source,\n });\n}\n\nexport interface ActionChainLink {\n surfaceName: string;\n actions: readonly ActionDeclaration[];\n}\n\n/**\n * The Actions a mount sees: its own plus every ancestor's, a child winning by\n * name. `chain` runs MOST GENERAL FIRST (root parent … the mounted surface),\n * as `DeclarationRegistry` resolves inheritance. Guards are loud, like the\n * declaration registry's: a surface twice in the chain is a cycle, more\n * ancestors than `maxDepth` throws, and one surface naming an Action twice throws.\n */\nexport function resolveActionChain(\n chain: readonly ActionChainLink[],\n options: { maxDepth?: number } = {},\n): ActionInfo[] {\n const maxDepth = options.maxDepth ?? DEFAULT_MAX_INHERITANCE_DEPTH;\n const names = chain.map((link) => link.surfaceName);\n const seen = new Set<string>();\n for (const name of names) {\n if (seen.has(name)) {\n throw new Error(`[actions] inheritance CYCLE detected at \"${name}\". Fix the surface chain: ${names.join(\" → \")}`);\n }\n seen.add(name);\n }\n if (chain.length - 1 > maxDepth) {\n throw new Error(\n `[actions] inheritance chain for \"${names[names.length - 1]}\" exceeds depth ${maxDepth}: ${names.join(\" → \")}`,\n );\n }\n const byName = new Map<string, ActionInfo>();\n for (const link of chain) {\n const own = new Set<string>();\n for (const action of link.actions) {\n if (own.has(action.name)) {\n throw new Error(\n `[actions] surface \"${link.surfaceName}\" declares Action \"${action.name}\" twice. Names are unique; nothing was overwritten.`,\n );\n }\n own.add(action.name);\n byName.set(action.name, toActionInfo(action, link.surfaceName));\n }\n }\n return [...byName.values()];\n}\n","/**\n * The few reads the surface runtime makes through the VIEWER's supabase client, typed STRUCTURALLY.\n *\n * Alchemy never imports supabase or the host (package rule 13). Deliberately not\n * `SupabaseClient<Database>` either: a generated `Database` type makes `schema().from().select()`\n * enormous and a host passing its typed client hits TS2589. The host hands its client in as an\n * `object`; the ONE cast lives in `asSurfaceClient`, and every read names its schema out loud.\n *\n * Holds no key and opens no connection: the client carries the person's session, so row security\n * decides every answer.\n */\nimport type { ActionError, ActionResult } from \"../actions/applet-types\";\n\n/** What a supabase-js read resolves with. */\nexport interface SurfaceReadAnswer {\n data: unknown;\n error: { message: string; code?: string; details?: string; hint?: string } | null;\n}\n\n/** The filter chain the reads use — a subset of supabase-js's `PostgrestFilterBuilder`. */\nexport interface SurfaceReadChain extends PromiseLike<SurfaceReadAnswer> {\n eq(column: string, value: unknown): SurfaceReadChain;\n in(column: string, values: readonly unknown[]): SurfaceReadChain;\n is(column: string, value: null): SurfaceReadChain;\n order(column: string, options?: { ascending?: boolean }): SurfaceReadChain;\n maybeSingle(): PromiseLike<SurfaceReadAnswer>;\n}\n\n/** The part of a supabase-js v2 client the surface runtime reads through. */\nexport interface SurfaceClient {\n schema(name: string): { from(table: string): { select(columns: string): SurfaceReadChain } };\n}\n\n/** Bind the host's client. A value without `schema()` is refused by name. */\nexport function asSurfaceClient(client: object): SurfaceClient {\n // The one cast: the host's typed client is passed as `object` (see above) and checked here.\n const c = client as Partial<SurfaceClient>;\n if (typeof c.schema !== \"function\") {\n throw new Error(\"[alchemy/surface] the surface runtime needs a supabase-js v2 client carrying the viewer's session (it has no schema()).\");\n }\n return c as SurfaceClient;\n}\n\n/** A read's error: the database's own sentence, never paraphrased. */\nexport function readError(error: NonNullable<SurfaceReadAnswer[\"error\"]>, code = \"read_failed\"): ActionError {\n const out: ActionError = { code: error.code ? `${code}:${error.code}` : code, message: error.message, retryable: false };\n if (error.details) out.diagnostic = error.details;\n return out;\n}\n\n/** Run a read (built inside the guard); a thrown transport error answers `unreachable` (retryable), never throws. */\nexport async function read<T>(query: () => PromiseLike<SurfaceReadAnswer>, shape: (data: unknown) => T): Promise<ActionResult<T>> {\n try {\n const answer = await query();\n if (answer.error) return { ok: false, error: readError(answer.error) };\n return { ok: true, data: shape(answer.data) };\n } catch (err) {\n return { ok: false, error: { code: \"unreachable\", message: err instanceof Error ? err.message : String(err), retryable: true } };\n }\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","/**\n * `readSurfaceChain` — the Actions a surface offers, read from the DATABASE (Applets CONTRACTS §6).\n *\n * Walks `ui.ui_surface` by `parent_surface_name` (live rows only), reads every surface's\n * `ui.ui_surface_action` rows for the whole chain in ONE query, projects each row with\n * `surfaceActionRowToAction` and resolves the chain with `resolveActionChain` (a child's Action\n * wins by name). Loud, named refusals for a cycle, a chain too deep, and a missing surface.\n *\n * Organization scoping — the same rule as aidream's `visible_actions`: a `declared_by='database'`\n * row on a surface OUTSIDE `applets/` is one organization's EXTENSION and is kept only when its\n * `organization_id` is one of the viewer's memberships (`organizationIds` = ALL of them; the active\n * organization is never a filter). No `organizationIds` = extensions are dropped (fail closed).\n * Code rows and `applets/*` rows are always kept. This scopes WHICH extension applies; row\n * security still decides what the client can read at all.\n *\n * The client is the viewer's supabase-js v2 client, typed STRUCTURALLY (`./client.ts`): the package\n * never imports supabase or the host. Never throws: a transport failure answers `unreachable`\n * (retryable); a database error keeps the database's own sentence.\n */\nimport { DEFAULT_MAX_INHERITANCE_DEPTH } from \"../declare/registry\";\nimport type { ActionInfo, ActionResult } from \"../actions/applet-types\";\nimport { resolveActionChain, surfaceActionRowToAction, type ActionChainLink, type SurfaceActionRow } from \"../actions/declarations\";\nimport { asSurfaceClient, isRecord, read, type SurfaceClient } from \"./client\";\n\n/** Surfaces under this prefix belong to one Applet; their database rows are never \"extensions\". */\nexport const APPLET_SURFACE_PREFIX = \"applets/\";\n\nexport interface ReadSurfaceChainOptions {\n /** Most ancestors a surface may have (alchemy's declaration default when absent). */\n maxDepth?: number;\n /** ALL of the viewer's organization memberships. Absent = organization extensions are dropped. */\n organizationIds?: readonly string[];\n}\n\nfunction fail(code: string, message: string, retryable = false): { ok: false; error: { code: string; message: string; retryable: boolean } } {\n return { ok: false, error: { code, message, retryable } };\n}\n\n/** Whether a row applies to a viewer with these memberships (aidream `visible_actions`). */\nexport function surfaceActionVisible(row: Pick<SurfaceActionRow, \"declared_by\" | \"surface_name\" | \"organization_id\">, organizationIds: readonly string[] | undefined): boolean {\n // Only a CODE row is everyone's; anything else (database, or a value nobody declared) is an extension\n // until proven otherwise — fail closed.\n if (row.declared_by === \"code\") return true;\n if (row.surface_name.startsWith(APPLET_SURFACE_PREFIX)) return true;\n return typeof row.organization_id === \"string\" && (organizationIds ?? []).includes(row.organization_id);\n}\n\nexport async function readSurfaceChain(\n client: object,\n surfaceName: string,\n options: ReadSurfaceChainOptions = {},\n): Promise<ActionResult<ActionInfo[]>> {\n let db: SurfaceClient;\n try {\n db = asSurfaceClient(client);\n } catch (err) {\n return fail(\"client_invalid\", err instanceof Error ? err.message : String(err));\n }\n const maxDepth = options.maxDepth ?? DEFAULT_MAX_INHERITANCE_DEPTH;\n const names: string[] = [];\n let at: string | null = surfaceName;\n while (at) {\n if (names.includes(at)) return fail(\"surface_cycle\", `The surface chain loops at \"${at}\": ${[...names, at].join(\" → \")}.`);\n if (names.length > maxDepth) {\n return fail(\"surface_too_deep\", `The surface chain for \"${surfaceName}\" is deeper than ${maxDepth}: ${[...names, at].join(\" → \")}.`);\n }\n const name: string = at;\n const row: ActionResult<unknown> = await read(\n () => db.schema(\"ui\").from(\"ui_surface\").select(\"name, parent_surface_name\").eq(\"name\", name).is(\"deleted_at\", null).maybeSingle(),\n (d) => d,\n );\n if (!row.ok) return row;\n if (!isRecord(row.data)) {\n return fail(\n \"surface_not_found\",\n names.length === 0\n ? `The surface \"${at}\" does not exist yet; saving the Applet writes it (ui.save_applet_surface).`\n : `The surface \"${names[names.length - 1]}\" names a parent \"${at}\" that does not exist.`,\n );\n }\n names.push(at);\n const parent: unknown = row.data.parent_surface_name;\n at = typeof parent === \"string\" && parent ? parent : null;\n }\n const rows = await read(\n () =>\n db\n .schema(\"ui\")\n .from(\"ui_surface_action\")\n .select(\"*\")\n .in(\"surface_name\", names)\n .is(\"deleted_at\", null)\n .order(\"sort_order\", { ascending: true }),\n (d) => (Array.isArray(d) ? (d as SurfaceActionRow[]) : []),\n );\n if (!rows.ok) return rows;\n const visible = rows.data.filter((r) => surfaceActionVisible(r, options.organizationIds));\n // Most general first (root parent … the surface itself), as resolveActionChain expects.\n const chain: ActionChainLink[] = [...names].reverse().map((name) => ({\n surfaceName: name,\n actions: visible.filter((r) => r.surface_name === name).map(surfaceActionRowToAction),\n }));\n try {\n return { ok: true, data: resolveActionChain(chain, { maxDepth }) };\n } catch (err) {\n return fail(\"surface_chain\", err instanceof Error ? err.message : String(err));\n }\n}\n","/**\n * THE ONE RUNNER for surface Actions (Applets CONTRACTS §6). Every caller — a\n * person's click, an agent, an applet, an automation — runs an Action through\n * here, so policy and caller rules live once.\n *\n * Policy \"ask\": a person's own click is the approval. Started by an agent, applet or automation,\n * a \"server\" run carries the policy to aidream (which answers with a proposal); every other run\n * asks the person through `ports.approvals` first, and is refused by name without that port.\n *\n * Dispatch by `runs`:\n * \"server\" → `ports.serverActions.run({ name: target, input, by, surface_name, policy })`\n * \"mandate\" → `ports.mandates.run({ alias: target, variables, by, policy })` — answers with the\n * started run's `MandateRunRef`; the result streams later on `run:<requestId>`\n * \"navigate\" → `ports.overlay.navigate(target)` with `{param}` filled from the input\n * \"page\" → `ports.pageActions.dispatch(name, input, context)`\n * A port the host did not bind means that capability is ABSENT: the run is\n * refused with a sentence, never stubbed (ports rule).\n *\n * Write targets (ALC-17): an Action projected from a declared write target (`source: \"write-target\"`)\n * goes through `ports.door.write` when a door is bound — the door applies the target's own policy\n * (manual / ask / queued / auto) and its receipt becomes the result (`applied` / `queued` → ok with\n * the receipt; refused → an error whose code is the refusal reason and whose `remedy` is the door's).\n * Without a door it dispatches to `pageActions` as before.\n *\n * Never throws: every outcome is a `Result` whose error message is the\n * system's own sentence.\n */\nimport type { AlchemyHostPorts, Json } from \"../ports\";\nimport type { WriteOp } from \"../operate/door\";\nimport type { ActionCaller, ActionContext, ActionError, ActionInfo, ActionResult } from \"./applet-types\";\n\nexport type SurfaceActionPorts = Pick<\n AlchemyHostPorts,\n \"diagnostics\" | \"serverActions\" | \"mandates\" | \"pageActions\" | \"overlay\" | \"approvals\" | \"door\"\n>;\n\nexport interface RunSurfaceActionOptions {\n runId?: string;\n viewerId?: string | null;\n signal?: AbortSignal;\n /** Which item a write-target Action writes into (by its declared identity values). */\n itemIdentity?: Record<string, string>;\n /** Text edits for a stream or patchable write target, instead of the input as the whole value. */\n ops?: readonly WriteOp[];\n}\n\nfunction fail(code: string, message: string, retryable = false, diagnostic?: string): { ok: false; error: ActionError } {\n return { ok: false, error: diagnostic ? { code, message, retryable, diagnostic } : { code, message, retryable } };\n}\n\nfunction messageOf(error: unknown): string {\n if (error instanceof Error && error.message.trim()) return error.message;\n if (typeof error === \"string\" && error.trim()) return error;\n return \"The run failed without saying why.\";\n}\n\nfunction newRunId(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (c?.randomUUID) return c.randomUUID();\n return `run-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/** Fill `{param}` placeholders from the input object. Returns the names it could not fill. */\nexport function fillActionTarget(template: string, input: unknown): { value: string; missing: string[] } {\n const source = input && typeof input === \"object\" && !Array.isArray(input) ? (input as Record<string, unknown>) : {};\n const missing: string[] = [];\n const value = template.replace(/\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g, (_, key: string) => {\n const v = source[key];\n if (v === undefined || v === null || v === \"\" || typeof v === \"object\") {\n missing.push(key);\n return \"\";\n }\n return encodeURIComponent(String(v));\n });\n return { value, missing };\n}\n\n/** Same as fillActionTarget, without URL-encoding (a mandate alias or server action name). */\nfunction fillName(template: string, input: unknown): { value: string; missing: string[] } {\n const filled = fillActionTarget(template, input);\n return { value: decodeURIComponent(filled.value), missing: filled.missing };\n}\n\nfunction toJson(value: unknown): Json {\n if (value === undefined) return null;\n return JSON.parse(JSON.stringify(value)) as Json;\n}\n\n/**\n * Who may run this Action, under its policy. Returns the refusal, or null.\n * \"manual\" = a person only. `triggeredBy` lists every caller allowed at all.\n */\nexport function refuseCaller(info: Pick<ActionInfo, \"name\" | \"label\" | \"policy\" | \"triggeredBy\">, by: ActionCaller): ActionError | null {\n if (!info.triggeredBy.includes(by)) {\n return {\n code: \"caller_not_allowed\",\n message: `\"${info.label}\" (${info.name}) can't be started by ${CALLER_WORDS[by]}; it allows ${info.triggeredBy.join(\", \") || \"no one\"}.`,\n retryable: false,\n };\n }\n if (info.policy === \"manual\" && by !== \"person\") {\n return {\n code: \"manual_only\",\n message: `\"${info.label}\" (${info.name}) runs only when a person starts it.`,\n retryable: false,\n };\n }\n return null;\n}\n\nconst CALLER_WORDS: Record<ActionCaller, string> = {\n person: \"a person\",\n agent: \"an agent\",\n applet: \"an applet\",\n automation: \"an automation\",\n};\n\nfunction absent(info: ActionInfo, capability: string): { ok: false; error: ActionError } {\n return fail(\"capability_absent\", `\"${info.label}\" can't run here: ${capability} isn't available on this page.`);\n}\n\nexport async function runSurfaceAction(\n info: ActionInfo,\n input: unknown,\n by: ActionCaller,\n ports: SurfaceActionPorts,\n options: RunSurfaceActionOptions = {},\n): Promise<ActionResult<unknown>> {\n const refusal = refuseCaller(info, by);\n if (refusal) return { ok: false, error: refusal };\n const context: ActionContext = {\n runId: options.runId ?? newRunId(),\n by,\n viewerId: options.viewerId ?? null,\n surfaceName: info.declaredOn,\n policy: info.policy,\n };\n const capture = (error: unknown, stage: string) => {\n try {\n ports.diagnostics.capture(error, { area: \"actions\", detail: { action: info.name, runs: info.runs, stage, runId: context.runId } });\n } catch {\n // A broken diagnostics port never turns a refusal into a throw.\n }\n };\n\n try {\n if (info.source === \"write-target\" && ports.door) {\n const receipt = await ports.door.write(\n {\n surfaceName: info.declaredOn,\n target: info.name,\n ...(info.itemType ? { itemType: info.itemType } : {}),\n ...(options.itemIdentity ? { itemIdentity: options.itemIdentity } : {}),\n ...(options.ops ? { ops: options.ops } : { value: input }),\n by,\n runId: context.runId,\n },\n options.signal,\n );\n if (receipt.status === \"refused\") {\n return { ok: false, error: { code: receipt.reason, message: receipt.sentence, retryable: receipt.reason === \"failed\", remedy: receipt.remedy } };\n }\n return { ok: true, data: receipt };\n }\n if (info.policy === \"ask\" && by !== \"person\" && info.runs !== \"server\") {\n if (!ports.approvals) {\n return fail(\"needs_approval\", `\"${info.label}\" needs a person's approval, and there is no one to ask here.`);\n }\n const approved = await ports.approvals.ask({\n action: info.name,\n label: info.label,\n description: info.description,\n input: toJson(input),\n by,\n surfaceName: info.declaredOn,\n });\n if (!approved) return fail(\"declined\", `\"${info.label}\" was not approved, so it did not run.`);\n }\n switch (info.runs) {\n case \"server\": {\n if (!ports.serverActions) return absent(info, \"server actions\");\n const filled = fillName(info.target ?? info.name, input);\n if (filled.missing.length) return fail(\"input_missing\", `\"${info.label}\" needs ${filled.missing.join(\", \")}.`);\n const response = await ports.serverActions.run({\n name: filled.value,\n input: toJson(input),\n by,\n surface_name: info.declaredOn,\n policy: info.policy,\n });\n if (response.status === \"refused\") return fail(\"refused\", response.message);\n return { ok: true, data: response };\n }\n case \"mandate\": {\n if (!ports.mandates) return absent(info, \"running a job\");\n if (!info.target) return fail(\"target_missing\", `\"${info.label}\" names no job to run.`);\n const filled = fillName(info.target, input);\n if (filled.missing.length) return fail(\"input_missing\", `\"${info.label}\" needs ${filled.missing.join(\", \")}.`);\n const record = input && typeof input === \"object\" && !Array.isArray(input) ? (input as Record<string, unknown>) : {};\n // A generic \"run a job\" Action carries { alias, variables, userInput }; a dedicated one's input IS the variables.\n const generic = /\\{[A-Za-z_]/.test(info.target);\n const variables = generic\n ? ((record.variables && typeof record.variables === \"object\" ? toJson(record.variables) : {}) as Record<string, Json>)\n : (toJson(record) as Record<string, Json>);\n const userInput = typeof record.userInput === \"string\" ? record.userInput : undefined;\n // The Action answers with the run's identity (RunRef); its result streams on `run:<requestId>`.\n const ref = await ports.mandates.run({\n alias: filled.value,\n variables,\n ...(userInput !== undefined ? { userInput } : {}),\n by,\n policy: info.policy,\n });\n return { ok: true, data: ref };\n }\n case \"navigate\": {\n if (!ports.overlay) return absent(info, \"navigation\");\n const filled = fillActionTarget(info.target ?? \"\", input);\n if (filled.missing.length) return fail(\"input_missing\", `\"${info.label}\" needs ${filled.missing.join(\", \")}.`);\n // `{to}` is a whole route: keep its slashes.\n const to = info.target && /^\\{[A-Za-z_][A-Za-z0-9_]*\\}$/.test(info.target) ? decodeURIComponent(filled.value) : filled.value;\n if (!to) return fail(\"target_missing\", `\"${info.label}\" names no page to open.`);\n ports.overlay.navigate(to);\n return { ok: true, data: { to } };\n }\n case \"page\": {\n if (!ports.pageActions) return absent(info, \"this page's actions\");\n return await ports.pageActions.dispatch(info.name, input, context);\n }\n default: {\n const runs: never = info.runs;\n return fail(\"runs_unknown\", `\"${info.label}\" runs as \"${String(runs)}\", which nothing here knows how to run.`);\n }\n }\n } catch (error) {\n capture(error, \"run\");\n return fail(\"run_failed\", messageOf(error), true);\n }\n}\n","/**\n * `createSurfacePort` — the one SurfacePort runtime (Applets CONTRACTS §6 + Amendment 2.1).\n *\n * A parent surface offers every Action and value it has; a user's or organization's surface\n * extends it. This is the runtime a host binds once and hands to a frame:\n *\n * mount reads the chain from the DATABASE (`readSurfaceChain`); the frame's `parent`\n * argument names the same thing and is not trusted over it\n * get/setValue the surface's named values, cloned both ways; `setValue` emits `value:<name>`\n * listActions never throws — a refusal goes to `ports.diagnostics` and answers `[]`\n * runAction found by name in the mounted chain (or a fresh read), run through\n * `runSurfaceAction` (the one runner); an Action that `updatesValue` sets it\n * completeAction settles a pending page-Action run by its runId\n *\n * Page Actions: with `opts.pageActions` the host routes them (e.g. to the frame that declared the\n * surface) and may park the run with `awaitPageAction(runId)`. Without it the port routes them\n * itself: it emits `\"actions\"` `{ action, input, runId, by }` and waits for `completeAction`.\n * `dispose()` settles every run this port parked with `host_disposed`.\n *\n * The package never imports the host or supabase: the client is structural (`./client.ts`), and\n * every capability arrives as a port. Nothing here picks an organization.\n */\nimport type { PageActionPort } from \"../ports\";\nimport type { ActionCaller, ActionContext, ActionInfo, ActionResult, SurfacePort } from \"../actions/applet-types\";\nimport { runSurfaceAction, type SurfaceActionPorts } from \"../actions/surface-run\";\nimport { readSurfaceChain } from \"./chain\";\n\n/** The stream a self-routed page Action travels on (CONTRACTS Amendment 2.1). */\nexport const PAGE_ACTION_STREAM = \"actions\";\n/** The stream a surface value travels on. */\nexport const valueStream = (name: string): string => `value:${name}`;\n\n/** What travels on `\"actions\"`; the frame answers with `surface.completeAction(runId, result)`. */\nexport interface PageActionEvent {\n action: string;\n input: unknown;\n runId: string;\n by?: ActionCaller;\n}\n\n/** The runner's ports the host binds; page Actions are routed by `pageActions` / the port itself. */\nexport type SurfacePortRunPorts = Omit<SurfaceActionPorts, \"pageActions\">;\n\n/** One settle function per parked page-Action run. */\nexport type PendingPageActions = Map<string, (result: ActionResult<unknown>) => void>;\n\nexport interface CreateSurfacePortOptions {\n /** A supabase-js v2 client carrying the VIEWER's session. Typed `object` on purpose (see ./client.ts). */\n client: object;\n /** ALL of the viewer's memberships, for organization extensions. Absent = extensions are dropped. */\n organizationIds?: () => Promise<readonly string[]>;\n /** The viewer's user id, stamped on every run's context. */\n viewerId?: () => Promise<string | null>;\n /** The runner's capabilities. `diagnostics` is required; each absent port = that capability is absent. */\n ports: SurfacePortRunPorts;\n /** The host's own page-Action routing. Absent = the port emits `\"actions\"` and waits for `completeAction`. */\n pageActions?: PageActionPort;\n /**\n * The host's event hub: `value:<name>` on `setValue`, and `\"actions\"` for self-routed page Actions.\n * Return `true` when someone is subscribed to the stream and `false` when nobody is. A self-routed\n * page Action waits for `completeAction` ONLY when `emit` answered `true`; anything else is refused\n * `action_unhandled`, so a page Action can never wait for an answer that cannot come.\n */\n emit(stream: string, payload: unknown): boolean;\n /** Values seeded at mount; a value already set is never overwritten. */\n seedValues?: () => Promise<Record<string, unknown>>;\n /** The surface to read when nothing is mounted yet (e.g. the Applet's own `applets/<id>`). */\n surfaceName?: () => Promise<string>;\n /** Most ancestors a surface may have. */\n maxDepth?: number;\n /** Ids for mounts (`\"mount\"`) and runs (`\"act\"`). */\n mintId?: (prefix: string) => string;\n /**\n * Share ONE map across the ports of one host tree, so a frame answering on its own host settles a\n * run another host routed to it. Absent = this port's own map.\n */\n pending?: PendingPageActions;\n}\n\nexport interface SurfacePortHandle extends SurfacePort {\n /** Park a page-Action run until `completeAction(runId, …)` (or `dispose()`) settles it. */\n awaitPageAction(runId: string): Promise<ActionResult<unknown>>;\n /** Settle every run this port parked with `host_disposed`, and forget the mount and values. */\n dispose(): void;\n}\n\nfunction clone<T>(value: T): T {\n if (value === undefined) return value;\n const sc = (globalThis as { structuredClone?: <V>(v: V) => V }).structuredClone;\n return sc ? sc(value) : (JSON.parse(JSON.stringify(value)) as T);\n}\n\nfunction errorOf(err: unknown, code = \"surface_failed\"): { ok: false; error: { code: string; message: string; retryable: boolean } } {\n return { ok: false, error: { code, message: err instanceof Error && err.message ? err.message : String(err), retryable: false } };\n}\n\nlet sequence = 0;\nfunction defaultMint(prefix: string): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n return c?.randomUUID ? `${prefix}-${c.randomUUID()}` : `${prefix}-${Date.now().toString(36)}-${(sequence++).toString(36)}`;\n}\n\nexport function createSurfacePort(opts: CreateSurfacePortOptions): SurfacePortHandle {\n const mint = opts.mintId ?? defaultMint;\n const pending: PendingPageActions = opts.pending ?? new Map();\n const parked = new Set<string>();\n const values = new Map<string, unknown>();\n let mounted: { mountId: string; name: string; chain: ActionInfo[] } | null = null;\n let disposed = false;\n\n const report = (error: unknown, stage: string, detail: Record<string, string> = {}) => {\n try {\n opts.ports.diagnostics.capture(error, { area: \"actions\", detail: { stage: `surface.${stage}`, ...detail } });\n } catch {\n // A broken diagnostics port never turns a refusal into a throw.\n }\n };\n\n const memberships = async (): Promise<readonly string[] | undefined> => {\n if (!opts.organizationIds) return undefined;\n try {\n return await opts.organizationIds();\n } catch (err) {\n // Fail closed: without memberships, organization extensions are dropped — and said so.\n report(err, \"organizationIds\");\n return undefined;\n }\n };\n\n const read = async (name: string): Promise<ActionResult<ActionInfo[]>> => {\n const organizationIds = await memberships();\n return readSurfaceChain(opts.client, name, {\n ...(opts.maxDepth !== undefined ? { maxDepth: opts.maxDepth } : {}),\n ...(organizationIds ? { organizationIds } : {}),\n });\n };\n\n /** The chain as it stands: the mount's, re-read when `fresh`; else the default surface's. */\n const currentChain = async (fresh: boolean): Promise<ActionResult<ActionInfo[]>> => {\n if (mounted && !fresh) return { ok: true, data: mounted.chain };\n const name = mounted?.name ?? (opts.surfaceName ? await opts.surfaceName() : null);\n if (!name) return { ok: false, error: { code: \"not_mounted\", message: \"No surface is mounted here yet.\", retryable: false } };\n const chain = await read(name);\n if (chain.ok && mounted?.name === name) mounted.chain = chain.data;\n return chain;\n };\n\n const awaitPageAction = (runId: string): Promise<ActionResult<unknown>> => {\n if (disposed) return Promise.resolve({ ok: false, error: { code: \"host_disposed\", message: \"This page has closed.\", retryable: false } });\n return new Promise((resolve) => {\n parked.add(runId);\n pending.set(runId, resolve);\n });\n };\n\n const selfRouted: PageActionPort = {\n dispatch(name: string, input: unknown, context: ActionContext): Promise<ActionResult<unknown>> {\n const waiting = awaitPageAction(context.runId);\n const event: PageActionEvent = { action: name, input, runId: context.runId, by: context.by };\n const forget = () => {\n pending.delete(context.runId);\n parked.delete(context.runId);\n };\n if (disposed) {\n forget();\n return Promise.resolve({ ok: false, error: { code: \"host_disposed\", message: \"This page closed, so the action did not run.\", retryable: false } });\n }\n let heard: boolean;\n try {\n heard = opts.emit(PAGE_ACTION_STREAM, event);\n } catch (err) {\n forget();\n throw err; // the runner captures it and answers run_failed\n }\n if (heard !== true) {\n forget();\n return Promise.resolve({ ok: false, error: { code: \"action_unhandled\", message: `No page is open to run \"${name}\".`, retryable: false } });\n }\n return waiting;\n },\n };\n\n const port: SurfacePortHandle = {\n async mount(name) {\n try {\n const chain = await read(name);\n if (!chain.ok) return chain;\n if (opts.seedValues) {\n try {\n const seeds = await opts.seedValues();\n for (const [key, v] of Object.entries(seeds)) if (!values.has(key)) values.set(key, clone(v));\n } catch (err) {\n report(err, \"seedValues\", { surface: name });\n }\n }\n mounted = { mountId: mint(\"mount\"), name, chain: chain.data };\n return { ok: true, data: { mountId: mounted.mountId } };\n } catch (err) {\n return errorOf(err);\n }\n },\n async unmount(mountId) {\n if (mounted?.mountId === mountId) mounted = null;\n },\n async getValue(name) {\n return clone(values.get(name));\n },\n async setValue(name, v) {\n let copy: unknown;\n try {\n copy = clone(v);\n } catch (err) {\n // A value that cannot be copied (a function, a live handle) is not stored, and never turns an\n // Action that already ran into a failure — it is reported by name instead.\n report(err, \"setValue\", { name });\n return;\n }\n values.set(name, copy);\n try {\n opts.emit(valueStream(name), clone(v));\n } catch (err) {\n report(err, \"emit\", { stream: valueStream(name) });\n }\n },\n async listActions() {\n try {\n const chain = await currentChain(false);\n if (!chain.ok) {\n report(chain.error, \"listActions\");\n return [];\n }\n return clone(chain.data);\n } catch (err) {\n report(err, \"listActions\");\n return [];\n }\n },\n async runAction(name, input, by) {\n let info: ActionInfo | undefined;\n try {\n const known = await currentChain(false);\n info = known.ok ? known.data.find((a) => a.name === name) : undefined;\n if (!info) {\n // Not in the mounted chain: the database may have gained it since the mount.\n const fresh = await currentChain(true);\n if (!fresh.ok) return fresh;\n info = fresh.data.find((a) => a.name === name);\n }\n } catch (err) {\n return errorOf(err);\n }\n if (!info) return { ok: false, error: { code: \"action_not_found\", message: `This page has no action named \"${name}\".`, retryable: false } };\n let viewerId: string | null = null;\n try {\n viewerId = opts.viewerId ? await opts.viewerId() : null;\n } catch (err) {\n report(err, \"viewerId\");\n }\n const ports: SurfaceActionPorts = { ...opts.ports, pageActions: opts.pageActions ?? selfRouted };\n const result = await runSurfaceAction(info, input, by, ports, { runId: mint(\"act\"), viewerId });\n if (result.ok && info.updatesValue) await port.setValue(info.updatesValue, result.data);\n return result;\n },\n async completeAction(runId, result) {\n const resolve = pending.get(runId);\n if (!resolve) {\n // A late, duplicate or misrouted answer leaves a trace instead of vanishing.\n report(new Error(`completeAction for run \"${runId}\", which is not waiting (already answered, never routed here, or the page closed).`), \"completeAction\", { runId });\n return;\n }\n pending.delete(runId);\n parked.delete(runId);\n resolve?.(result);\n },\n awaitPageAction,\n dispose() {\n disposed = true;\n for (const runId of parked) {\n const resolve = pending.get(runId);\n pending.delete(runId);\n resolve?.({ ok: false, error: { code: \"host_disposed\", message: \"This page closed before the action finished.\", retryable: false } });\n }\n parked.clear();\n mounted = null;\n values.clear();\n },\n };\n return port;\n}\n"],"mappings":"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","names":["fail","read"]}
1
+ {"version":3,"sources":["../src/surface.ts","../src/declare/registry.ts","../src/actions/declarations.ts","../src/surface/client.ts","../src/surface/chain.ts","../src/actions/surface-run.ts","../src/surface/port.ts"],"sourcesContent":["/**\n * `@ai-matrx/alchemy/surface` — the one SurfacePort runtime (Applets CONTRACTS §6, owner AP-2).\n * Framework-neutral: zero React, zero `@ai-matrx/agents`, zero supabase import (the viewer's\n * client is typed structurally).\n *\n * readSurfaceChain(client, name, { maxDepth?, organizationIds? }) the chain's Actions, from the database\n * createSurfacePort(opts) mount / values / list / run / complete\n */\nexport {\n APPLET_SURFACE_PREFIX,\n readSurfaceChain,\n surfaceActionVisible,\n type ReadSurfaceChainOptions,\n} from \"./surface/chain\";\nexport {\n PAGE_ACTION_STREAM,\n createSurfacePort,\n valueStream,\n type CreateSurfacePortOptions,\n type PageActionEvent,\n type PendingPageActions,\n type SurfacePortHandle,\n type SurfacePortRunPorts,\n} from \"./surface/port\";\nexport type { SurfaceClient, SurfaceReadAnswer, SurfaceReadChain } from \"./surface/client\";\nexport type {\n ActionCaller,\n ActionContext,\n ActionInfo,\n ActionResult,\n MandateRunRef,\n SurfacePort,\n} from \"./actions/applet-types\";\n","/**\n * The declaration registry: inheritance, baseline injection, provenance and\n * group synthesis (CONTRACT §2.1 `DeclarationRegistry`). Ported verbatim in\n * behaviour from matrx-frontend `features/surfaces/manifests/registry.ts`\n * (ALC-14); the app now supplies its declarations and baselines.\n *\n * Guards are LOUD by design: an unknown parent, a cycle, a chain deeper than\n * the limit, a reserved or duplicate group key, a value naming an undeclared\n * group, or a reserved platform name throws on resolution — a build fails\n * instead of silently dropping values.\n */\nimport { declarationHash } from \"./hash\";\nimport type {\n DeclarationExtension,\n DeclarationIssue,\n DeclaredValue,\n ResolvedSurfaceDeclaration,\n ResolvedValue,\n Sensitivity,\n SurfaceDeclaration,\n ValueGroup,\n ValueProvenance,\n} from \"./types\";\nimport { validateDeclarations, type ValidateOptions } from \"./validate\";\n\nexport const RESERVED_GROUP_KEYS = {\n general: \"general\",\n baseline: \"baseline\",\n inheritedPrefix: \"inherited:\",\n} as const;\n\n/** Group sortOrder bands. Curated groups author 0–899; the rest is reserved. */\nexport const GENERAL_GROUP_ORDER = 850;\nexport const INHERITED_GROUP_ORDER_BASE = 9000;\nexport const BASELINE_GROUP_ORDER = 9900;\nexport const DEFAULT_MAX_INHERITANCE_DEPTH = 3;\n\nimport { DEFAULT_SENSITIVITY, resolveSensitivity } from \"./sensitivity\";\nexport { DEFAULT_SENSITIVITY, resolveSensitivity };\n\nexport type Resolved<D extends SurfaceDeclaration> = Omit<D, \"values\" | \"groups\"> & {\n values: readonly (D[\"values\"][number] & ResolvedValue)[];\n groups: readonly ValueGroup[];\n contentHash: string;\n};\n\nexport interface DeclarationRegistryOptions {\n /** Generic values injected into every declaration unless `skipBaselineValues`. */\n baselineValues?: readonly DeclaredValue[];\n /** Value names only the platform writes; a declaration claiming one is refused. */\n reservedValueNames?: readonly string[];\n /** Write-target names only the platform writes. */\n reservedWriteTargetNames?: readonly string[];\n maxInheritanceDepth?: number;\n}\n\nexport interface DeclarationRegistry<D extends SurfaceDeclaration> {\n /** Refuses a second declaration of the same surfaceName. */\n register(declaration: D): void;\n registerExtension(extension: DeclarationExtension<D>): void;\n extensions(): readonly DeclarationExtension<D>[];\n /** Declarations exactly as authored (inheritance unresolved, no baselines). */\n raw(): readonly D[];\n getRaw(surfaceName: string): D | undefined;\n /** Parent chain, ROOT FIRST. Unknown surfaces return []. */\n ancestry(surfaceName: string): string[];\n /** Direct children (declarations naming it in `inheritsFrom`), in declaration order. */\n children(surfaceName: string): string[];\n /** Inheritance + baseline + groups resolved; provenance kept. */\n all(): readonly Resolved<D>[];\n get(surfaceName: string): Resolved<D> | undefined;\n validate(options?: ValidateOptions): readonly DeclarationIssue[];\n}\n\nexport function createDeclarationRegistry<D extends SurfaceDeclaration>(\n options: DeclarationRegistryOptions = {},\n): DeclarationRegistry<D> {\n const baselineValues = options.baselineValues ?? [];\n const baselineNames = new Set(baselineValues.map((v) => v.name));\n const reservedValues = new Set(options.reservedValueNames ?? []);\n const reservedTargets = new Set(options.reservedWriteTargetNames ?? []);\n const maxDepth = options.maxInheritanceDepth ?? DEFAULT_MAX_INHERITANCE_DEPTH;\n const declarations: D[] = [];\n const index = new Map<string, D>();\n const extensionList: DeclarationExtension<D>[] = [];\n let resolvedCache: Resolved<D>[] | null = null;\n let resolvedIndex = new Map<string, Resolved<D>>();\n\n function ancestry(surfaceName: string): string[] {\n const chain: string[] = [];\n const seen = new Set<string>([surfaceName]);\n let current = index.get(surfaceName)?.inheritsFrom;\n while (current) {\n if (seen.has(current)) {\n throw new Error(\n `[surfaces] inheritsFrom CYCLE detected at \"${current}\" (from \"${surfaceName}\"). ` +\n `Fix the manifest chain: ${[...seen].join(\" → \")} → ${current}`,\n );\n }\n const parent = index.get(current);\n if (!parent) {\n throw new Error(\n `[surfaces] \"${surfaceName}\" inheritsFrom unknown surface \"${current}\" — ` +\n `the parent must be a registered declaration`,\n );\n }\n seen.add(current);\n chain.unshift(current);\n if (chain.length > maxDepth) {\n throw new Error(\n `[surfaces] inheritance chain for \"${surfaceName}\" exceeds depth ${maxDepth}: ` + chain.join(\" → \"),\n );\n }\n current = parent.inheritsFrom;\n }\n return chain;\n }\n\n function groupKeyFor(value: DeclaredValue, provenance: ValueProvenance): string {\n if (provenance.kind === \"own\" && value.group) return value.group;\n if (baselineNames.has(value.name)) return RESERVED_GROUP_KEYS.baseline;\n if (provenance.kind === \"inherited\") return `${RESERVED_GROUP_KEYS.inheritedPrefix}${provenance.from}`;\n if (provenance.kind === \"baseline\") return RESERVED_GROUP_KEYS.baseline;\n return RESERVED_GROUP_KEYS.general;\n }\n\n function resolve(declaration: D): Resolved<D> {\n const chain = ancestry(declaration.surfaceName);\n const lineage: { layer: D; from: string | null }[] = [\n ...chain.map((name) => {\n const layer = index.get(name);\n if (!layer) throw new Error(`[surfaces] missing inherited declaration \"${name}\"`);\n return { layer, from: name };\n }),\n { layer: declaration, from: null },\n ];\n\n const curatedKeys = new Set<string>();\n for (const group of declaration.groups ?? []) {\n if (\n group.key === RESERVED_GROUP_KEYS.general ||\n group.key === RESERVED_GROUP_KEYS.baseline ||\n group.key.startsWith(RESERVED_GROUP_KEYS.inheritedPrefix)\n ) {\n throw new Error(\n `[surfaces] \"${declaration.surfaceName}\" declares reserved group key \"${group.key}\" — ` +\n `general/baseline/inherited:* are synthesized by the registry`,\n );\n }\n if (curatedKeys.has(group.key)) {\n throw new Error(`[surfaces] \"${declaration.surfaceName}\" declares duplicate group key \"${group.key}\"`);\n }\n curatedKeys.add(group.key);\n }\n for (const value of declaration.values) {\n if (reservedValues.has(value.name)) {\n throw new Error(\n `[surfaces] \"${declaration.surfaceName}\" declares value \"${value.name}\" — that name is written by the platform; choose another name`,\n );\n }\n if (value.group && !curatedKeys.has(value.group)) {\n throw new Error(\n `[surfaces] \"${declaration.surfaceName}\" value \"${value.name}\" references undeclared ` +\n `group \"${value.group}\" — declare it in the manifest's \\`groups\\``,\n );\n }\n }\n for (const target of declaration.writeTargets ?? []) {\n if (reservedTargets.has(target.name)) {\n throw new Error(\n `[surfaces] \"${declaration.surfaceName}\" declares write target \"${target.name}\" — that name is written by the platform; choose another name`,\n );\n }\n }\n\n const valuesByName = new Map<string, D[\"values\"][number] & ResolvedValue>();\n const inheritedLists = new Map<string, Map<string, unknown>>();\n const coreLists: { field: string; identity(entry: unknown): string }[] = [\n { field: \"configNamespaces\", identity: (entry) => (entry as { namespace: string }).namespace },\n {\n field: \"evidenceSources\",\n identity: (entry) => `${(entry as { kind: string }).kind}:${(entry as { idValue: string }).idValue}`,\n },\n ];\n const lists = [...coreLists, ...extensionList.flatMap((extension) => extension.inheritedLists ?? [])];\n for (const { layer, from } of lineage) {\n for (const value of layer.values) {\n const provenance: ValueProvenance = from ? { kind: \"inherited\", from } : { kind: \"own\" };\n valuesByName.set(value.name, {\n ...value,\n provenance,\n groupKey: groupKeyFor(value, provenance),\n resolvedSensitivity: resolveSensitivity(value),\n });\n }\n for (const list of lists) {\n const entries = (layer as unknown as Record<string, unknown>)[list.field];\n if (!Array.isArray(entries)) continue;\n const byIdentity = inheritedLists.get(list.field) ?? new Map<string, unknown>();\n for (const entry of entries) byIdentity.set(list.identity(entry), entry);\n inheritedLists.set(list.field, byIdentity);\n }\n }\n\n if (!declaration.skipBaselineValues) {\n for (const baseline of baselineValues) {\n if (valuesByName.has(baseline.name)) continue;\n valuesByName.set(baseline.name, {\n ...baseline,\n provenance: { kind: \"baseline\" },\n groupKey: RESERVED_GROUP_KEYS.baseline,\n resolvedSensitivity: resolveSensitivity(baseline),\n } as D[\"values\"][number] & ResolvedValue);\n }\n }\n\n const usedGroupKeys = new Set(Array.from(valuesByName.values(), (value) => value.groupKey));\n const groups: ValueGroup[] = [...(declaration.groups ?? [])];\n if (usedGroupKeys.has(RESERVED_GROUP_KEYS.general)) {\n groups.push({ key: RESERVED_GROUP_KEYS.general, label: \"General\", sortOrder: GENERAL_GROUP_ORDER });\n }\n for (let i = chain.length - 1, distance = 0; i >= 0; i -= 1, distance += 1) {\n const parentName = chain[i]!;\n const key = `${RESERVED_GROUP_KEYS.inheritedPrefix}${parentName}`;\n if (!usedGroupKeys.has(key)) continue;\n const parentLabel = index.get(parentName)?.label ?? parentName;\n groups.push({\n key,\n label: `Inherited from ${parentLabel}`,\n sortOrder: INHERITED_GROUP_ORDER_BASE + distance * 10,\n });\n }\n if (usedGroupKeys.has(RESERVED_GROUP_KEYS.baseline)) {\n groups.push({ key: RESERVED_GROUP_KEYS.baseline, label: \"Generic baselines\", sortOrder: BASELINE_GROUP_ORDER });\n }\n groups.sort((a, b) => a.sortOrder - b.sortOrder);\n\n const groupOrder = new Map(groups.map((group) => [group.key, group.sortOrder] as const));\n const values = Array.from(valuesByName.values()).sort((a, b) => {\n const ga = groupOrder.get(a.groupKey) ?? GENERAL_GROUP_ORDER;\n const gb = groupOrder.get(b.groupKey) ?? GENERAL_GROUP_ORDER;\n if (ga !== gb) return ga - gb;\n return (a.sortOrder ?? 1000) - (b.sortOrder ?? 1000);\n });\n\n const merged: Record<string, unknown> = {};\n for (const [field, byIdentity] of inheritedLists) {\n if (byIdentity.size > 0) merged[field] = Array.from(byIdentity.values());\n }\n return {\n ...declaration,\n values,\n groups,\n ...merged,\n contentHash: declarationHash(declaration),\n } as Resolved<D>;\n }\n\n function resolvedAll(): Resolved<D>[] {\n if (!resolvedCache) {\n resolvedCache = declarations.map(resolve);\n resolvedIndex = new Map(resolvedCache.map((entry) => [entry.surfaceName, entry] as const));\n }\n return resolvedCache;\n }\n\n const registry: DeclarationRegistry<D> = {\n register(declaration) {\n if (index.has(declaration.surfaceName)) {\n throw new Error(`[surfaces] \"${declaration.surfaceName}\" is declared twice — one surface, one declaration`);\n }\n declarations.push(declaration);\n index.set(declaration.surfaceName, declaration);\n resolvedCache = null;\n },\n registerExtension(extension) {\n if (extensionList.some((existing) => existing.key === extension.key)) {\n throw new Error(`[surfaces] declaration extension \"${extension.key}\" is registered twice`);\n }\n extensionList.push(extension);\n resolvedCache = null;\n },\n extensions: () => extensionList,\n raw: () => declarations,\n getRaw: (surfaceName) => index.get(surfaceName),\n ancestry,\n children: (surfaceName) =>\n declarations.filter((d) => d.inheritsFrom === surfaceName).map((d) => d.surfaceName),\n all: () => resolvedAll(),\n get(surfaceName) {\n resolvedAll();\n return resolvedIndex.get(surfaceName);\n },\n validate: (validateOptions) =>\n validateDeclarations(resolvedAll() as unknown as readonly ResolvedSurfaceDeclaration[], {\n ...validateOptions,\n extensions: [...(validateOptions?.extensions ?? []), ...(extensionList as DeclarationExtension[])],\n baselineNames: validateOptions?.baselineNames ?? [...baselineNames],\n }),\n };\n return registry;\n}\n","/**\n * Every source of an Action projects onto ONE shape (Applets CONTRACTS §6):\n * a declared write target, a client tool row, a `ui.ui_surface_action` row.\n * Pure functions, zero loss (the census in `declarations.test.ts` proves every\n * source field lands). Then the chain: a child surface's Action wins by name.\n */\nimport { DEFAULT_MAX_INHERITANCE_DEPTH } from \"../declare/registry\";\nimport type { Json, WriteTarget } from \"../declare/types\";\nimport type {\n ActionCaller,\n ActionDeclaration,\n ActionDeclaredBy,\n ActionInfo,\n ActionInput,\n ActionOrigin,\n ActionPolicy,\n ActionRuns,\n ActionWriteMode,\n} from \"./applet-types\";\n\n/** Default `category` of an Action that names none. */\nexport const APPLET_CATEGORY = \"applet\";\n\n/** The DB default of `ui.ui_surface_action.triggered_by`, used where a source carries no list. */\nexport const DEFAULT_TRIGGERED_BY: readonly ActionCaller[] = [\"person\", \"agent\", \"applet\"];\n\nexport const ACTION_CALLERS: readonly ActionCaller[] = [\"person\", \"agent\", \"applet\", \"automation\"];\nexport const ACTION_RUNS: readonly ActionRuns[] = [\"page\", \"server\", \"mandate\", \"navigate\"];\nexport const ACTION_POLICIES: readonly ActionPolicy[] = [\"ask\", \"auto\", \"manual\"];\n\n/** A `ui.ui_surface_client_tool` row (live columns that carry meaning). */\nexport interface ClientToolRow {\n id?: string;\n surface_name: string;\n name: string;\n label: string;\n description: string;\n input_schema: Json;\n /** Live values: \"ui\" (applied directly) | \"draft\" (proposed for review). */\n mode: \"ui\" | \"draft\";\n declared_by?: ActionDeclaredBy;\n}\n\n/** A `ui.ui_surface_action` row. */\nexport interface SurfaceActionRow {\n id?: string;\n surface_id: string;\n surface_name: string;\n /** '' = the screen itself. */\n item_type: string;\n name: string;\n category: string;\n label: string;\n description: string;\n input: ActionInput | null;\n output: string | null;\n runs: ActionRuns;\n target: string | null;\n triggered_by: ActionCaller[];\n policy: ActionPolicy;\n updates_value: string | null;\n code_ref: string | null;\n declared_by: ActionDeclaredBy;\n sort_order: number;\n /**\n * The row's organization. A `declared_by='database'` row on a surface outside `applets/` is that\n * organization's EXTENSION and applies only to its members (`readSurfaceChain`, aidream `visible_actions`).\n */\n organization_id?: string | null;\n}\n\ntype Loose<T> = { [K in keyof T]: T[K] | undefined };\n\n/** Drop undefined keys (exactOptionalPropertyTypes: an absent field is absent, never `undefined`). */\nfunction compact<T extends object>(value: Loose<T>): T {\n return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== undefined)) as T;\n}\n\nconst POLICY_OF_APPLY: Record<NonNullable<WriteTarget[\"applyPolicy\"]>, ActionPolicy> = {\n manual: \"manual\",\n ask: \"ask\",\n auto: \"auto\",\n // Filed for an organization's review: never applied without someone saying yes.\n queued: \"ask\",\n};\n\n/** A declared write target → an Action that runs on the page. */\nexport function writeTargetToAction(target: WriteTarget): ActionDeclaration {\n const applyPolicy = target.applyPolicy ?? \"manual\";\n return compact<ActionDeclaration>({\n name: target.name,\n label: target.label,\n description: target.description,\n input: target.valueKind ?? { jsonSchema: target.valueType === \"document\" ? {} : { type: target.valueType } },\n runs: \"page\",\n triggeredBy: [...DEFAULT_TRIGGERED_BY],\n policy: POLICY_OF_APPLY[applyPolicy],\n updatesValue: target.updatesValue,\n mode: target.mode as ActionWriteMode,\n patchable: target.patchable,\n approvalComparison: target.approvalComparison,\n streamOps: target.streamOps ? [...target.streamOps] : undefined,\n destination: target.destination,\n groupKey: target.group,\n itemType: target.itemType,\n applyPolicy: target.applyPolicy,\n valueType: target.valueType,\n valueKind: target.valueKind,\n sortOrder: target.sortOrder,\n origin: { source: \"write-target\", declaredBy: \"code\" },\n });\n}\n\n/** A client tool row → an Action an agent runs on the page (\"draft\" tools are proposed first). */\nexport function clientToolToAction(row: ClientToolRow): ActionDeclaration {\n return compact<ActionDeclaration>({\n name: row.name,\n label: row.label,\n description: row.description,\n input: { jsonSchema: (row.input_schema ?? {}) as object },\n runs: \"page\",\n triggeredBy: [\"agent\"],\n policy: row.mode === \"draft\" ? \"ask\" : \"auto\",\n mode: row.mode,\n origin: compact<ActionOrigin>({ source: \"client-tool\", surfaceName: row.surface_name, id: row.id, declaredBy: row.declared_by }),\n });\n}\n\n/** A `ui.ui_surface_action` row → its Action. */\nexport function surfaceActionRowToAction(row: SurfaceActionRow): ActionDeclaration {\n return compact<ActionDeclaration>({\n name: row.name,\n category: row.category,\n label: row.label,\n description: row.description,\n input: row.input ?? undefined,\n output: row.output ?? undefined,\n runs: row.runs,\n target: row.target ?? undefined,\n triggeredBy: [...row.triggered_by],\n policy: row.policy,\n updatesValue: row.updates_value ?? undefined,\n itemType: row.item_type || undefined,\n sortOrder: row.sort_order,\n origin: compact<ActionOrigin>({\n source: \"surface-action\",\n surfaceName: row.surface_name,\n surfaceId: row.surface_id,\n id: row.id,\n declaredBy: row.declared_by,\n codeRef: row.code_ref ?? undefined,\n organizationId: row.organization_id ?? undefined,\n }),\n });\n}\n\n/** JSON-only view of an Action as declared on `declaredOn`. */\nexport function toActionInfo(declaration: ActionDeclaration, declaredOn: string): ActionInfo {\n return compact<ActionInfo>({\n name: declaration.name,\n category: declaration.category ?? APPLET_CATEGORY,\n label: declaration.label,\n description: declaration.description,\n runs: declaration.runs,\n policy: declaration.policy,\n triggeredBy: [...declaration.triggeredBy],\n input: declaration.input,\n output: declaration.output,\n declaredOn,\n target: declaration.target,\n updatesValue: declaration.updatesValue,\n itemType: declaration.itemType,\n source: declaration.origin?.source,\n });\n}\n\nexport interface ActionChainLink {\n surfaceName: string;\n actions: readonly ActionDeclaration[];\n}\n\n/**\n * The Actions a mount sees: its own plus every ancestor's, a child winning by\n * name. `chain` runs MOST GENERAL FIRST (root parent … the mounted surface),\n * as `DeclarationRegistry` resolves inheritance. Guards are loud, like the\n * declaration registry's: a surface twice in the chain is a cycle, more\n * ancestors than `maxDepth` throws, and one surface naming an Action twice throws.\n */\nexport function resolveActionChain(\n chain: readonly ActionChainLink[],\n options: { maxDepth?: number } = {},\n): ActionInfo[] {\n const maxDepth = options.maxDepth ?? DEFAULT_MAX_INHERITANCE_DEPTH;\n const names = chain.map((link) => link.surfaceName);\n const seen = new Set<string>();\n for (const name of names) {\n if (seen.has(name)) {\n throw new Error(`[actions] inheritance CYCLE detected at \"${name}\". Fix the surface chain: ${names.join(\" → \")}`);\n }\n seen.add(name);\n }\n if (chain.length - 1 > maxDepth) {\n throw new Error(\n `[actions] inheritance chain for \"${names[names.length - 1]}\" exceeds depth ${maxDepth}: ${names.join(\" → \")}`,\n );\n }\n const byName = new Map<string, ActionInfo>();\n for (const link of chain) {\n const own = new Set<string>();\n for (const action of link.actions) {\n if (own.has(action.name)) {\n throw new Error(\n `[actions] surface \"${link.surfaceName}\" declares Action \"${action.name}\" twice. Names are unique; nothing was overwritten.`,\n );\n }\n own.add(action.name);\n byName.set(action.name, toActionInfo(action, link.surfaceName));\n }\n }\n return [...byName.values()];\n}\n","/**\n * The few reads the surface runtime makes through the VIEWER's supabase client, typed STRUCTURALLY.\n *\n * Alchemy never imports supabase or the host (package rule 13). Deliberately not\n * `SupabaseClient<Database>` either: a generated `Database` type makes `schema().from().select()`\n * enormous and a host passing its typed client hits TS2589. The host hands its client in as an\n * `object`; the ONE cast lives in `asSurfaceClient`, and every read names its schema out loud.\n *\n * Holds no key and opens no connection: the client carries the person's session, so row security\n * decides every answer.\n */\nimport type { ActionError, ActionResult } from \"../actions/applet-types\";\n\n/** What a supabase-js read resolves with. */\nexport interface SurfaceReadAnswer {\n data: unknown;\n error: { message: string; code?: string; details?: string; hint?: string } | null;\n}\n\n/** The filter chain the reads use — a subset of supabase-js's `PostgrestFilterBuilder`. */\nexport interface SurfaceReadChain extends PromiseLike<SurfaceReadAnswer> {\n eq(column: string, value: unknown): SurfaceReadChain;\n in(column: string, values: readonly unknown[]): SurfaceReadChain;\n is(column: string, value: null): SurfaceReadChain;\n order(column: string, options?: { ascending?: boolean }): SurfaceReadChain;\n maybeSingle(): PromiseLike<SurfaceReadAnswer>;\n}\n\n/** The part of a supabase-js v2 client the surface runtime reads through. */\nexport interface SurfaceClient {\n schema(name: string): { from(table: string): { select(columns: string): SurfaceReadChain } };\n}\n\n/** Bind the host's client. A value without `schema()` is refused by name. */\nexport function asSurfaceClient(client: object): SurfaceClient {\n // The one cast: the host's typed client is passed as `object` (see above) and checked here.\n const c = client as Partial<SurfaceClient>;\n if (typeof c.schema !== \"function\") {\n throw new Error(\"[alchemy/surface] the surface runtime needs a supabase-js v2 client carrying the viewer's session (it has no schema()).\");\n }\n return c as SurfaceClient;\n}\n\n/** A read's error: the database's own sentence, never paraphrased. */\nexport function readError(error: NonNullable<SurfaceReadAnswer[\"error\"]>, code = \"read_failed\"): ActionError {\n const out: ActionError = { code: error.code ? `${code}:${error.code}` : code, message: error.message, retryable: false };\n if (error.details) out.diagnostic = error.details;\n return out;\n}\n\n/** Run a read (built inside the guard); a thrown transport error answers `unreachable` (retryable), never throws. */\nexport async function read<T>(query: () => PromiseLike<SurfaceReadAnswer>, shape: (data: unknown) => T): Promise<ActionResult<T>> {\n try {\n const answer = await query();\n if (answer.error) return { ok: false, error: readError(answer.error) };\n return { ok: true, data: shape(answer.data) };\n } catch (err) {\n return { ok: false, error: { code: \"unreachable\", message: err instanceof Error ? err.message : String(err), retryable: true } };\n }\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","/**\n * `readSurfaceChain` — the Actions a surface offers, read from the DATABASE (Applets CONTRACTS §6).\n *\n * Walks `ui.ui_surface` by `parent_surface_name` (live rows only), reads every surface's\n * `ui.ui_surface_action` rows for the whole chain in ONE query, projects each row with\n * `surfaceActionRowToAction` and resolves the chain with `resolveActionChain` (a child's Action\n * wins by name). Loud, named refusals for a cycle, a chain too deep, and a missing surface.\n *\n * Organization scoping — the same rule as aidream's `visible_actions`: a `declared_by='database'`\n * row on a surface OUTSIDE `applets/` is one organization's EXTENSION and is kept only when its\n * `organization_id` is one of the viewer's memberships (`organizationIds` = ALL of them; the active\n * organization is never a filter). No `organizationIds` = extensions are dropped (fail closed).\n * Code rows and `applets/*` rows are always kept. This scopes WHICH extension applies; row\n * security still decides what the client can read at all.\n *\n * The client is the viewer's supabase-js v2 client, typed STRUCTURALLY (`./client.ts`): the package\n * never imports supabase or the host. Never throws: a transport failure answers `unreachable`\n * (retryable); a database error keeps the database's own sentence.\n */\nimport { DEFAULT_MAX_INHERITANCE_DEPTH } from \"../declare/registry\";\nimport type { ActionInfo, ActionResult } from \"../actions/applet-types\";\nimport { resolveActionChain, surfaceActionRowToAction, type ActionChainLink, type SurfaceActionRow } from \"../actions/declarations\";\nimport { asSurfaceClient, isRecord, read, type SurfaceClient } from \"./client\";\n\n/** Surfaces under this prefix belong to one Applet; their database rows are never \"extensions\". */\nexport const APPLET_SURFACE_PREFIX = \"applets/\";\n\nexport interface ReadSurfaceChainOptions {\n /** Most ancestors a surface may have (alchemy's declaration default when absent). */\n maxDepth?: number;\n /** ALL of the viewer's organization memberships. Absent = organization extensions are dropped. */\n organizationIds?: readonly string[];\n}\n\nfunction fail(code: string, message: string, retryable = false): { ok: false; error: { code: string; message: string; retryable: boolean } } {\n return { ok: false, error: { code, message, retryable } };\n}\n\n/** Whether a row applies to a viewer with these memberships (aidream `visible_actions`). */\nexport function surfaceActionVisible(row: Pick<SurfaceActionRow, \"declared_by\" | \"surface_name\" | \"organization_id\">, organizationIds: readonly string[] | undefined): boolean {\n // Only a CODE row is everyone's; anything else (database, or a value nobody declared) is an extension\n // until proven otherwise — fail closed.\n if (row.declared_by === \"code\") return true;\n if (row.surface_name.startsWith(APPLET_SURFACE_PREFIX)) return true;\n return typeof row.organization_id === \"string\" && (organizationIds ?? []).includes(row.organization_id);\n}\n\nexport async function readSurfaceChain(\n client: object,\n surfaceName: string,\n options: ReadSurfaceChainOptions = {},\n): Promise<ActionResult<ActionInfo[]>> {\n let db: SurfaceClient;\n try {\n db = asSurfaceClient(client);\n } catch (err) {\n return fail(\"client_invalid\", err instanceof Error ? err.message : String(err));\n }\n const maxDepth = options.maxDepth ?? DEFAULT_MAX_INHERITANCE_DEPTH;\n const names: string[] = [];\n let at: string | null = surfaceName;\n while (at) {\n if (names.includes(at)) return fail(\"surface_cycle\", `The surface chain loops at \"${at}\": ${[...names, at].join(\" → \")}.`);\n if (names.length > maxDepth) {\n return fail(\"surface_too_deep\", `The surface chain for \"${surfaceName}\" is deeper than ${maxDepth}: ${[...names, at].join(\" → \")}.`);\n }\n const name: string = at;\n const row: ActionResult<unknown> = await read(\n () => db.schema(\"ui\").from(\"ui_surface\").select(\"name, parent_surface_name\").eq(\"name\", name).is(\"deleted_at\", null).maybeSingle(),\n (d) => d,\n );\n if (!row.ok) return row;\n if (!isRecord(row.data)) {\n return fail(\n \"surface_not_found\",\n names.length === 0\n ? `The surface \"${at}\" does not exist yet; saving the Applet writes it (ui.save_applet_surface).`\n : `The surface \"${names[names.length - 1]}\" names a parent \"${at}\" that does not exist.`,\n );\n }\n names.push(at);\n const parent: unknown = row.data.parent_surface_name;\n at = typeof parent === \"string\" && parent ? parent : null;\n }\n const rows = await read(\n () =>\n db\n .schema(\"ui\")\n .from(\"ui_surface_action\")\n .select(\"*\")\n .in(\"surface_name\", names)\n .is(\"deleted_at\", null)\n .order(\"sort_order\", { ascending: true }),\n (d) => (Array.isArray(d) ? (d as SurfaceActionRow[]) : []),\n );\n if (!rows.ok) return rows;\n const visible = rows.data.filter((r) => surfaceActionVisible(r, options.organizationIds));\n // Most general first (root parent … the surface itself), as resolveActionChain expects.\n const chain: ActionChainLink[] = [...names].reverse().map((name) => ({\n surfaceName: name,\n actions: visible.filter((r) => r.surface_name === name).map(surfaceActionRowToAction),\n }));\n try {\n return { ok: true, data: resolveActionChain(chain, { maxDepth }) };\n } catch (err) {\n return fail(\"surface_chain\", err instanceof Error ? err.message : String(err));\n }\n}\n","/**\n * THE ONE RUNNER for surface Actions (Applets CONTRACTS §6). Every caller — a\n * person's click, an agent, an applet, an automation — runs an Action through\n * here, so policy and caller rules live once.\n *\n * Policy \"ask\": a person's own click is the approval. Started by an agent, applet or automation,\n * a \"server\" run carries the policy to aidream (which answers with a proposal); every other run\n * asks the person through `ports.approvals` first, and is refused by name without that port.\n *\n * Dispatch by `runs`:\n * \"server\" → `ports.serverActions.run({ name: target, input, by, surface_name, policy })`\n * \"mandate\" → `ports.mandates.run({ alias: target, variables, by, policy })` — answers with the\n * started run's `MandateRunRef`; the result streams later on `run:<requestId>`\n * \"navigate\" → `ports.overlay.navigate(target)` with `{param}` filled from the input\n * \"page\" → `ports.pageActions.dispatch(name, input, context)`\n * A port the host did not bind means that capability is ABSENT: the run is\n * refused with a sentence, never stubbed (ports rule).\n *\n * Write targets (ALC-17): an Action projected from a declared write target (`source: \"write-target\"`)\n * goes through `ports.door.write` when a door is bound — the door applies the target's own policy\n * (manual / ask / queued / auto) and its receipt becomes the result (`applied` / `queued` → ok with\n * the receipt; refused → an error whose code is the refusal reason and whose `remedy` is the door's).\n * Without a door it dispatches to `pageActions` as before.\n *\n * Never throws: every outcome is a `Result` whose error message is the\n * system's own sentence.\n */\nimport type { AlchemyHostPorts, Json } from \"../ports\";\nimport type { WriteOp } from \"../operate/door\";\nimport type { ActionCaller, ActionContext, ActionError, ActionInfo, ActionResult } from \"./applet-types\";\n\nexport type SurfaceActionPorts = Pick<\n AlchemyHostPorts,\n \"diagnostics\" | \"serverActions\" | \"mandates\" | \"pageActions\" | \"overlay\" | \"approvals\" | \"door\"\n>;\n\nexport interface RunSurfaceActionOptions {\n runId?: string;\n viewerId?: string | null;\n signal?: AbortSignal;\n /** Which item a write-target Action writes into (by its declared identity values). */\n itemIdentity?: Record<string, string>;\n /** Text edits for a stream or patchable write target, instead of the input as the whole value. */\n ops?: readonly WriteOp[];\n}\n\nfunction fail(code: string, message: string, retryable = false, diagnostic?: string): { ok: false; error: ActionError } {\n return { ok: false, error: diagnostic ? { code, message, retryable, diagnostic } : { code, message, retryable } };\n}\n\nfunction messageOf(error: unknown): string {\n if (error instanceof Error && error.message.trim()) return error.message;\n if (typeof error === \"string\" && error.trim()) return error;\n return \"The run failed without saying why.\";\n}\n\nfunction newRunId(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (c?.randomUUID) return c.randomUUID();\n return `run-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/** Fill `{param}` placeholders from the input object. Returns the names it could not fill. */\nexport function fillActionTarget(template: string, input: unknown): { value: string; missing: string[] } {\n const source = input && typeof input === \"object\" && !Array.isArray(input) ? (input as Record<string, unknown>) : {};\n const missing: string[] = [];\n const value = template.replace(/\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g, (_, key: string) => {\n const v = source[key];\n if (v === undefined || v === null || v === \"\" || typeof v === \"object\") {\n missing.push(key);\n return \"\";\n }\n return encodeURIComponent(String(v));\n });\n return { value, missing };\n}\n\n/** Same as fillActionTarget, without URL-encoding (a mandate alias or server action name). */\nfunction fillName(template: string, input: unknown): { value: string; missing: string[] } {\n const filled = fillActionTarget(template, input);\n return { value: decodeURIComponent(filled.value), missing: filled.missing };\n}\n\nfunction toJson(value: unknown): Json {\n if (value === undefined) return null;\n return JSON.parse(JSON.stringify(value)) as Json;\n}\n\n/**\n * Who may run this Action, under its policy. Returns the refusal, or null.\n * \"manual\" = a person only. `triggeredBy` lists every caller allowed at all.\n */\nexport function refuseCaller(info: Pick<ActionInfo, \"name\" | \"label\" | \"policy\" | \"triggeredBy\">, by: ActionCaller): ActionError | null {\n if (!info.triggeredBy.includes(by)) {\n return {\n code: \"caller_not_allowed\",\n message: `\"${info.label}\" (${info.name}) can't be started by ${CALLER_WORDS[by]}; it allows ${info.triggeredBy.join(\", \") || \"no one\"}.`,\n retryable: false,\n };\n }\n if (info.policy === \"manual\" && by !== \"person\") {\n return {\n code: \"manual_only\",\n message: `\"${info.label}\" (${info.name}) runs only when a person starts it.`,\n retryable: false,\n };\n }\n return null;\n}\n\nconst CALLER_WORDS: Record<ActionCaller, string> = {\n person: \"a person\",\n agent: \"an agent\",\n applet: \"an applet\",\n automation: \"an automation\",\n};\n\nfunction absent(info: ActionInfo, capability: string): { ok: false; error: ActionError } {\n return fail(\"capability_absent\", `\"${info.label}\" can't run here: ${capability} isn't available on this page.`);\n}\n\nexport async function runSurfaceAction(\n info: ActionInfo,\n input: unknown,\n by: ActionCaller,\n ports: SurfaceActionPorts,\n options: RunSurfaceActionOptions = {},\n): Promise<ActionResult<unknown>> {\n const refusal = refuseCaller(info, by);\n if (refusal) return { ok: false, error: refusal };\n const context: ActionContext = {\n runId: options.runId ?? newRunId(),\n by,\n viewerId: options.viewerId ?? null,\n surfaceName: info.declaredOn,\n policy: info.policy,\n };\n const capture = (error: unknown, stage: string) => {\n try {\n ports.diagnostics.capture(error, { area: \"actions\", detail: { action: info.name, runs: info.runs, stage, runId: context.runId } });\n } catch {\n // A broken diagnostics port never turns a refusal into a throw.\n }\n };\n\n try {\n if (info.source === \"write-target\" && ports.door) {\n const receipt = await ports.door.write(\n {\n surfaceName: info.declaredOn,\n target: info.name,\n ...(info.itemType ? { itemType: info.itemType } : {}),\n ...(options.itemIdentity ? { itemIdentity: options.itemIdentity } : {}),\n ...(options.ops ? { ops: options.ops } : { value: input }),\n by,\n runId: context.runId,\n },\n options.signal,\n );\n if (receipt.status === \"refused\") {\n return { ok: false, error: { code: receipt.reason, message: receipt.sentence, retryable: receipt.reason === \"failed\", remedy: receipt.remedy } };\n }\n return { ok: true, data: receipt };\n }\n if (info.policy === \"ask\" && by !== \"person\" && info.runs !== \"server\") {\n if (!ports.approvals) {\n return fail(\"needs_approval\", `\"${info.label}\" needs a person's approval, and there is no one to ask here.`);\n }\n const approved = await ports.approvals.ask({\n action: info.name,\n label: info.label,\n description: info.description,\n input: toJson(input),\n by,\n surfaceName: info.declaredOn,\n runId: context.runId,\n });\n if (!approved) return fail(\"declined\", `\"${info.label}\" was not approved, so it did not run.`);\n }\n switch (info.runs) {\n case \"server\": {\n if (!ports.serverActions) return absent(info, \"server actions\");\n const filled = fillName(info.target ?? info.name, input);\n if (filled.missing.length) return fail(\"input_missing\", `\"${info.label}\" needs ${filled.missing.join(\", \")}.`);\n const response = await ports.serverActions.run({\n name: filled.value,\n input: toJson(input),\n by,\n surface_name: info.declaredOn,\n policy: info.policy,\n });\n if (response.status === \"refused\") return fail(\"refused\", response.message);\n return { ok: true, data: response };\n }\n case \"mandate\": {\n if (!ports.mandates) return absent(info, \"running a job\");\n if (!info.target) return fail(\"target_missing\", `\"${info.label}\" names no job to run.`);\n const filled = fillName(info.target, input);\n if (filled.missing.length) return fail(\"input_missing\", `\"${info.label}\" needs ${filled.missing.join(\", \")}.`);\n const record = input && typeof input === \"object\" && !Array.isArray(input) ? (input as Record<string, unknown>) : {};\n // A generic \"run a job\" Action carries { alias, variables, userInput }; a dedicated one's input IS the variables.\n const generic = /\\{[A-Za-z_]/.test(info.target);\n const variables = generic\n ? ((record.variables && typeof record.variables === \"object\" ? toJson(record.variables) : {}) as Record<string, Json>)\n : (toJson(record) as Record<string, Json>);\n const userInput = typeof record.userInput === \"string\" ? record.userInput : undefined;\n // The Action answers with the run's identity (RunRef); its result streams on `run:<requestId>`.\n const ref = await ports.mandates.run({\n alias: filled.value,\n variables,\n ...(userInput !== undefined ? { userInput } : {}),\n by,\n policy: info.policy,\n });\n return { ok: true, data: ref };\n }\n case \"navigate\": {\n if (!ports.overlay) return absent(info, \"navigation\");\n const filled = fillActionTarget(info.target ?? \"\", input);\n if (filled.missing.length) return fail(\"input_missing\", `\"${info.label}\" needs ${filled.missing.join(\", \")}.`);\n // `{to}` is a whole route: keep its slashes.\n const to = info.target && /^\\{[A-Za-z_][A-Za-z0-9_]*\\}$/.test(info.target) ? decodeURIComponent(filled.value) : filled.value;\n if (!to) return fail(\"target_missing\", `\"${info.label}\" names no page to open.`);\n ports.overlay.navigate(to);\n return { ok: true, data: { to } };\n }\n case \"page\": {\n if (!ports.pageActions) return absent(info, \"this page's actions\");\n return await ports.pageActions.dispatch(info.name, input, context);\n }\n default: {\n const runs: never = info.runs;\n return fail(\"runs_unknown\", `\"${info.label}\" runs as \"${String(runs)}\", which nothing here knows how to run.`);\n }\n }\n } catch (error) {\n capture(error, \"run\");\n return fail(\"run_failed\", messageOf(error), true);\n }\n}\n","/**\n * `createSurfacePort` — the one SurfacePort runtime (Applets CONTRACTS §6 + Amendment 2.1).\n *\n * A parent surface offers every Action and value it has; a user's or organization's surface\n * extends it. This is the runtime a host binds once and hands to a frame:\n *\n * mount reads the chain from the DATABASE (`readSurfaceChain`); the frame's `parent`\n * argument names the same thing and is not trusted over it\n * get/setValue the surface's named values, cloned both ways; `setValue` emits `value:<name>`\n * listActions never throws — a refusal goes to `ports.diagnostics` and answers `[]`\n * runAction found by name in the mounted chain (or a fresh read), run through\n * `runSurfaceAction` (the one runner); an Action that `updatesValue` sets it\n * completeAction settles a pending page-Action run by its runId\n *\n * Page Actions: with `opts.pageActions` the host routes them (e.g. to the frame that declared the\n * surface) and may park the run with `awaitPageAction(runId)`. Without it the port routes them\n * itself: it emits `\"actions\"` `{ action, input, runId, by }` and waits for `completeAction`.\n * `dispose()` settles every run this port parked with `host_disposed`.\n *\n * The package never imports the host or supabase: the client is structural (`./client.ts`), and\n * every capability arrives as a port. Nothing here picks an organization.\n */\nimport type { PageActionPort } from \"../ports\";\nimport type { ActionCaller, ActionContext, ActionInfo, ActionResult, SurfacePort } from \"../actions/applet-types\";\nimport { runSurfaceAction, type SurfaceActionPorts } from \"../actions/surface-run\";\nimport { readSurfaceChain } from \"./chain\";\n\n/** The stream a self-routed page Action travels on (CONTRACTS Amendment 2.1). */\nexport const PAGE_ACTION_STREAM = \"actions\";\n/** The stream a surface value travels on. */\nexport const valueStream = (name: string): string => `value:${name}`;\n\n/** What travels on `\"actions\"`; the frame answers with `surface.completeAction(runId, result)`. */\nexport interface PageActionEvent {\n action: string;\n input: unknown;\n runId: string;\n by?: ActionCaller;\n}\n\n/** The runner's ports the host binds; page Actions are routed by `pageActions` / the port itself. */\nexport type SurfacePortRunPorts = Omit<SurfaceActionPorts, \"pageActions\">;\n\n/** One settle function per parked page-Action run. */\nexport type PendingPageActions = Map<string, (result: ActionResult<unknown>) => void>;\n\nexport interface CreateSurfacePortOptions {\n /** A supabase-js v2 client carrying the VIEWER's session. Typed `object` on purpose (see ./client.ts). */\n client: object;\n /** ALL of the viewer's memberships, for organization extensions. Absent = extensions are dropped. */\n organizationIds?: () => Promise<readonly string[]>;\n /** The viewer's user id, stamped on every run's context. */\n viewerId?: () => Promise<string | null>;\n /** The runner's capabilities. `diagnostics` is required; each absent port = that capability is absent. */\n ports: SurfacePortRunPorts;\n /** The host's own page-Action routing. Absent = the port emits `\"actions\"` and waits for `completeAction`. */\n pageActions?: PageActionPort;\n /**\n * The host's event hub: `value:<name>` on `setValue`, and `\"actions\"` for self-routed page Actions.\n * Return `true` when someone is subscribed to the stream and `false` when nobody is. A self-routed\n * page Action waits for `completeAction` ONLY when `emit` answered `true`; anything else is refused\n * `action_unhandled`, so a page Action can never wait for an answer that cannot come.\n */\n emit(stream: string, payload: unknown): boolean;\n /** Values seeded at mount; a value already set is never overwritten. */\n seedValues?: () => Promise<Record<string, unknown>>;\n /** The surface to read when nothing is mounted yet (e.g. the Applet's own `applets/<id>`). */\n surfaceName?: () => Promise<string>;\n /** Most ancestors a surface may have. */\n maxDepth?: number;\n /** Ids for mounts (`\"mount\"`) and runs (`\"act\"`). */\n mintId?: (prefix: string) => string;\n /**\n * Share ONE map across the ports of one host tree, so a frame answering on its own host settles a\n * run another host routed to it. Absent = this port's own map.\n */\n pending?: PendingPageActions;\n}\n\nexport interface SurfacePortHandle extends SurfacePort {\n /** Park a page-Action run until `completeAction(runId, …)` (or `dispose()`) settles it. */\n awaitPageAction(runId: string): Promise<ActionResult<unknown>>;\n /** Settle every run this port parked with `host_disposed`, and forget the mount and values. */\n dispose(): void;\n}\n\nfunction clone<T>(value: T): T {\n if (value === undefined) return value;\n const sc = (globalThis as { structuredClone?: <V>(v: V) => V }).structuredClone;\n return sc ? sc(value) : (JSON.parse(JSON.stringify(value)) as T);\n}\n\nfunction errorOf(err: unknown, code = \"surface_failed\"): { ok: false; error: { code: string; message: string; retryable: boolean } } {\n return { ok: false, error: { code, message: err instanceof Error && err.message ? err.message : String(err), retryable: false } };\n}\n\nlet sequence = 0;\nfunction defaultMint(prefix: string): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n return c?.randomUUID ? `${prefix}-${c.randomUUID()}` : `${prefix}-${Date.now().toString(36)}-${(sequence++).toString(36)}`;\n}\n\nexport function createSurfacePort(opts: CreateSurfacePortOptions): SurfacePortHandle {\n const mint = opts.mintId ?? defaultMint;\n const pending: PendingPageActions = opts.pending ?? new Map();\n const parked = new Set<string>();\n const values = new Map<string, unknown>();\n let mounted: { mountId: string; name: string; chain: ActionInfo[] } | null = null;\n let disposed = false;\n\n const report = (error: unknown, stage: string, detail: Record<string, string> = {}) => {\n try {\n opts.ports.diagnostics.capture(error, { area: \"actions\", detail: { stage: `surface.${stage}`, ...detail } });\n } catch {\n // A broken diagnostics port never turns a refusal into a throw.\n }\n };\n\n const memberships = async (): Promise<readonly string[] | undefined> => {\n if (!opts.organizationIds) return undefined;\n try {\n return await opts.organizationIds();\n } catch (err) {\n // Fail closed: without memberships, organization extensions are dropped — and said so.\n report(err, \"organizationIds\");\n return undefined;\n }\n };\n\n const read = async (name: string): Promise<ActionResult<ActionInfo[]>> => {\n const organizationIds = await memberships();\n return readSurfaceChain(opts.client, name, {\n ...(opts.maxDepth !== undefined ? { maxDepth: opts.maxDepth } : {}),\n ...(organizationIds ? { organizationIds } : {}),\n });\n };\n\n /** The chain as it stands: the mount's, re-read when `fresh`; else the default surface's. */\n const currentChain = async (fresh: boolean): Promise<ActionResult<ActionInfo[]>> => {\n if (mounted && !fresh) return { ok: true, data: mounted.chain };\n const name = mounted?.name ?? (opts.surfaceName ? await opts.surfaceName() : null);\n if (!name) return { ok: false, error: { code: \"not_mounted\", message: \"No surface is mounted here yet.\", retryable: false } };\n const chain = await read(name);\n if (chain.ok && mounted?.name === name) mounted.chain = chain.data;\n return chain;\n };\n\n const awaitPageAction = (runId: string): Promise<ActionResult<unknown>> => {\n if (disposed) return Promise.resolve({ ok: false, error: { code: \"host_disposed\", message: \"This page has closed.\", retryable: false } });\n return new Promise((resolve) => {\n parked.add(runId);\n pending.set(runId, resolve);\n });\n };\n\n const selfRouted: PageActionPort = {\n dispatch(name: string, input: unknown, context: ActionContext): Promise<ActionResult<unknown>> {\n const waiting = awaitPageAction(context.runId);\n const event: PageActionEvent = { action: name, input, runId: context.runId, by: context.by };\n const forget = () => {\n pending.delete(context.runId);\n parked.delete(context.runId);\n };\n if (disposed) {\n forget();\n return Promise.resolve({ ok: false, error: { code: \"host_disposed\", message: \"This page closed, so the action did not run.\", retryable: false } });\n }\n let heard: boolean;\n try {\n heard = opts.emit(PAGE_ACTION_STREAM, event);\n } catch (err) {\n forget();\n throw err; // the runner captures it and answers run_failed\n }\n if (heard !== true) {\n forget();\n return Promise.resolve({ ok: false, error: { code: \"action_unhandled\", message: `No page is open to run \"${name}\".`, retryable: false } });\n }\n return waiting;\n },\n };\n\n const port: SurfacePortHandle = {\n async mount(name) {\n try {\n const chain = await read(name);\n if (!chain.ok) return chain;\n if (opts.seedValues) {\n try {\n const seeds = await opts.seedValues();\n for (const [key, v] of Object.entries(seeds)) if (!values.has(key)) values.set(key, clone(v));\n } catch (err) {\n report(err, \"seedValues\", { surface: name });\n }\n }\n mounted = { mountId: mint(\"mount\"), name, chain: chain.data };\n return { ok: true, data: { mountId: mounted.mountId } };\n } catch (err) {\n return errorOf(err);\n }\n },\n async unmount(mountId) {\n if (mounted?.mountId === mountId) mounted = null;\n },\n async getValue(name) {\n return clone(values.get(name));\n },\n async setValue(name, v) {\n let copy: unknown;\n try {\n copy = clone(v);\n } catch (err) {\n // A value that cannot be copied (a function, a live handle) is not stored, and never turns an\n // Action that already ran into a failure — it is reported by name instead.\n report(err, \"setValue\", { name });\n return;\n }\n values.set(name, copy);\n try {\n opts.emit(valueStream(name), clone(v));\n } catch (err) {\n report(err, \"emit\", { stream: valueStream(name) });\n }\n },\n async listActions() {\n try {\n const chain = await currentChain(false);\n if (!chain.ok) {\n report(chain.error, \"listActions\");\n return [];\n }\n return clone(chain.data);\n } catch (err) {\n report(err, \"listActions\");\n return [];\n }\n },\n async runAction(name, input, by) {\n let info: ActionInfo | undefined;\n try {\n const known = await currentChain(false);\n info = known.ok ? known.data.find((a) => a.name === name) : undefined;\n if (!info) {\n // Not in the mounted chain: the database may have gained it since the mount.\n const fresh = await currentChain(true);\n if (!fresh.ok) return fresh;\n info = fresh.data.find((a) => a.name === name);\n }\n } catch (err) {\n return errorOf(err);\n }\n if (!info) return { ok: false, error: { code: \"action_not_found\", message: `This page has no action named \"${name}\".`, retryable: false } };\n let viewerId: string | null = null;\n try {\n viewerId = opts.viewerId ? await opts.viewerId() : null;\n } catch (err) {\n report(err, \"viewerId\");\n }\n const ports: SurfaceActionPorts = { ...opts.ports, pageActions: opts.pageActions ?? selfRouted };\n const result = await runSurfaceAction(info, input, by, ports, { runId: mint(\"act\"), viewerId });\n if (result.ok && info.updatesValue) await port.setValue(info.updatesValue, result.data);\n return result;\n },\n async completeAction(runId, result) {\n const resolve = pending.get(runId);\n if (!resolve) {\n // A late, duplicate or misrouted answer leaves a trace instead of vanishing.\n report(new Error(`completeAction for run \"${runId}\", which is not waiting (already answered, never routed here, or the page closed).`), \"completeAction\", { runId });\n return;\n }\n pending.delete(runId);\n parked.delete(runId);\n resolve?.(result);\n },\n awaitPageAction,\n dispose() {\n disposed = true;\n for (const runId of parked) {\n const resolve = pending.get(runId);\n pending.delete(runId);\n resolve?.({ ok: false, error: { code: \"host_disposed\", message: \"This page closed before the action finished.\", retryable: false } });\n }\n parked.clear();\n mounted = null;\n values.clear();\n },\n };\n return port;\n}\n"],"mappings":"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","names":["fail","read"]}
@@ -777,6 +777,8 @@ interface ApprovalRequest {
777
777
  input: Json;
778
778
  by: "agent" | "applet" | "automation";
779
779
  surfaceName: string;
780
+ /** The run this approval belongs to, when the caller named one — lets a host match the answer to its own waiting write or Action instead of guessing by value. */
781
+ runId?: string;
780
782
  }
781
783
  interface ApprovalPort {
782
784
  /** Resolves true when the person approves; false when they decline or dismiss. */
package/dist/surface.d.ts CHANGED
@@ -777,6 +777,8 @@ interface ApprovalRequest {
777
777
  input: Json;
778
778
  by: "agent" | "applet" | "automation";
779
779
  surfaceName: string;
780
+ /** The run this approval belongs to, when the caller named one — lets a host match the answer to its own waiting write or Action instead of guessing by value. */
781
+ runId?: string;
780
782
  }
781
783
  interface ApprovalPort {
782
784
  /** Resolves true when the person approves; false when they decline or dismiss. */