@kohaku-ui/renderer-wc 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,141 @@
1
1
  # @kohaku-ui/renderer-wc
2
2
 
3
+ ## 0.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#64](https://github.com/yosuque/kohaku/pull/64) [`b1a7af7`](https://github.com/yosuque/kohaku/commit/b1a7af7f39f09f2f088298098eaae618b32e363e) Thanks [@yosuque](https://github.com/yosuque)! - `implement` and `implementWc` parse a node's props against the part's schema once per `node.props` object instead of on every render or rebuild, and emit the dev-mode schema-mismatch warning once per props object (design.md decision 68). Rendered output is unchanged.
8
+
9
+ - [#66](https://github.com/yosuque/kohaku/pull/66) [`0a2a43b`](https://github.com/yosuque/kohaku/commit/0a2a43b5ea0cbe90df1bbe53af9137452654e477) Thanks [@yosuque](https://github.com/yosuque)! - `action.button` and `presentForm` now show the governed-action phases that stop an action before it commits: a rejected payload (`invalid`, `role="alert"`) and a declined confirmation or pending approval (`awaitingApproval`, `role="status"`). renderer-core adds `actionPhaseNotice` and the `RendererMessages.actionInvalid` / `actionAwaiting` strings (design.md decisions 62-64); `<kohaku-surface>` also clears a form's previous submit message when a new submit starts, as the React form does.
10
+
11
+ - [#66](https://github.com/yosuque/kohaku/pull/66) [`4072ca8`](https://github.com/yosuque/kohaku/commit/4072ca8c86f28c2bb79abbfc0483b2e6242b7768) Thanks [@yosuque](https://github.com/yosuque)! - Editable `presentSpreadsheet` cells now run the governed-action pre-check in both renderers (a value the `actionManifest` rejects stays in edit mode with `aria-invalid`), and an optimistic cell edit is discarded again when its `cellEdit` invoke ends `invalid`, `awaitingApproval` or `failed`, so the grid shows the server value. renderer-core adds `revertCellEdit`; renderer-react's `useInvokeAction().invoke` takes an optional `onPhase` callback (design.md decisions 62-64).
12
+
13
+ - [#66](https://github.com/yosuque/kohaku/pull/66) [`d16f7b3`](https://github.com/yosuque/kohaku/commit/d16f7b367ec62be4875ac18b42fd8be4d9da5c33) Thanks [@yosuque](https://github.com/yosuque)! - `<kohaku-surface>` reads `actionManifest`, `confirm` and `requestApproval` live at invoke time instead of rebuilding the mounted tree when one is reassigned. Assigning a spec and its manifest back to back no longer tears down an L2 sandbox iframe twice, and an inline `confirm` arrow no longer causes a rebuild on every assignment.
14
+ - Updated dependencies [[`a80f17a`](https://github.com/yosuque/kohaku/commit/a80f17a8ac304def877d23df9e7b8e37ef5c7396), [`e250463`](https://github.com/yosuque/kohaku/commit/e2504639145c3baacd1843c72512e8abf4f21b08), [`cb6e91a`](https://github.com/yosuque/kohaku/commit/cb6e91afdbbc083587cd32941ebac07b0151753a), [`171103f`](https://github.com/yosuque/kohaku/commit/171103f58ab87ff98daad8cb12901d417079d76b), [`b712fef`](https://github.com/yosuque/kohaku/commit/b712fef7404c6af1ca6ff726890eb6ddfd44dbfc), [`003e360`](https://github.com/yosuque/kohaku/commit/003e3605159973f6837fccf87f83691b40839483), [`5bb982e`](https://github.com/yosuque/kohaku/commit/5bb982e2fca7d4c609d5b99b6dc71bac95dbfe55), [`f34568b`](https://github.com/yosuque/kohaku/commit/f34568bb40e3c136bfc74e9c7d10c50c812f7e18), [`06a724e`](https://github.com/yosuque/kohaku/commit/06a724e046a432e8c64af217911bb2683ab8ca9d), [`71e17f9`](https://github.com/yosuque/kohaku/commit/71e17f9d970e01abaa8ebdf967054ad468555b56), [`ab25ddc`](https://github.com/yosuque/kohaku/commit/ab25ddc5691e216e6d5d027920a0a9abbc8f4207), [`206b95b`](https://github.com/yosuque/kohaku/commit/206b95b425800ec5af8f7e9ac203a8be70fd8d0b), [`343ccd7`](https://github.com/yosuque/kohaku/commit/343ccd7a71d4370473640dce94f1e2e2a821b39d), [`7c92cc4`](https://github.com/yosuque/kohaku/commit/7c92cc433e40a88f3b43eaeba7fd0af1a8755cad), [`a936266`](https://github.com/yosuque/kohaku/commit/a9362668b281564bc09a3b4a20233f8e1294bf41), [`0a2a43b`](https://github.com/yosuque/kohaku/commit/0a2a43b5ea0cbe90df1bbe53af9137452654e477), [`f8ecb4c`](https://github.com/yosuque/kohaku/commit/f8ecb4c71ba780378849c27c8ccdc6a14b31dcdc), [`4072ca8`](https://github.com/yosuque/kohaku/commit/4072ca8c86f28c2bb79abbfc0483b2e6242b7768)]:
15
+ - @kohaku-ui/registry@0.4.1
16
+ - @kohaku-ui/spec-core@0.4.1
17
+ - @kohaku-ui/data-binding@0.4.1
18
+ - @kohaku-ui/renderer-core@0.4.1
19
+ - @kohaku-ui/sandbox@0.4.1
20
+
21
+ ## 0.4.0
22
+
23
+ ### Minor Changes
24
+
25
+ - [#60](https://github.com/yosuque/kohaku/pull/60) [`5f1bbbd`](https://github.com/yosuque/kohaku/commit/5f1bbbd09fe1edb984a7b0f5a0c5212c3da628ea) Thanks [@yosuque](https://github.com/yosuque)! - Adds a Compliance Evidence Pack export (`@kohaku-ui/lineage`'s new `evidence` module; `kohaku evidence
26
+ keygen`/`export`/`verify`) and opt-in AI-generation disclosure for both renderers (design.md [#66](https://github.com/yosuque/kohaku/issues/66)/[#67](https://github.com/yosuque/kohaku/issues/67)).
27
+
28
+ **Evidence Pack** (`@kohaku-ui/lineage`): `buildEvidencePack` assembles a normalized, Ed25519-signed
29
+ export of the lineage log (`events.jsonl`), a governance-decision index (`approvals.jsonl`:
30
+ `component.reviewed`/`published`/`withdrawn`, `intent.fixated`/`unfixated`), promotion/fixation
31
+ snapshots, and the referenced component HTML artifacts, plus `manifest.json` and a detached signature
32
+ (`manifest.sig`). `EvidenceManifestSchema` is new but deliberately not part of `spec/schemas` — it
33
+ describes an export format for auditors, not a wire type. Ed25519 signing uses `globalThis.crypto.subtle`
34
+ (no new runtime dependency for TS); an artifact whose recorded hash does not match its own content is
35
+ still exported, recorded as a non-fatal warning rather than aborting the export.
36
+
37
+ **CLI** (`@kohaku-ui/cli`): `kohaku evidence keygen --out-dir <dir>` generates an Ed25519 keypair (private
38
+ key file mode 0600). `kohaku evidence export (--data-dir <dir> | --rest <baseUrl> [--header k:v])
39
+ [--tenant <id>] --since --until --private-key <pem> --out <dir> [--allow-incomplete]` builds and signs a
40
+ pack from a local `StoragePort` data directory or, over REST, from the existing `KohakuClient` surface
41
+ (`lineagePages`/`promotions.list`/`fixations.list`) — the REST source leaves `fixations.jsonl` empty with
42
+ a recorded warning, since `GET /fixations` does not expose enough fields to reconstruct a full
43
+ `FixationRecord`. `kohaku evidence verify <dir> --public-key <pem>` checks the manifest schema, the
44
+ signature, and every file's hash/size, and reports an independent artifact-hash cross-check as non-fatal
45
+ `mismatches`; exit code 0 valid / 1 invalid / 2 usage error. `@kohaku-ui/lineage` and
46
+ `@kohaku-ui/storage-memory` move from `cli`'s devDependencies to dependencies.
47
+
48
+ **AI-generation disclosure** (`@kohaku-ui/renderer-core`, `@kohaku-ui/renderer-react`,
49
+ `@kohaku-ui/renderer-wc`): `deriveDisclosure(provenance)` (renderer-core) derives a disclosure level
50
+ (`"ai-generated"` for tier L1/L2, `"ai-assisted-reviewed"` for a fixated tier-L0 Spec, `"none"` otherwise
51
+ — never encoded on the wire) and the corresponding `data-kohaku-disclosure`/`data-kohaku-tier`/
52
+ `data-digital-source-type` (IPTC Digital Source Type) attributes. `SpecView` gains a `disclosure?: "off" |
53
+ "attributes" | "label"` prop (renderer-react; also exports `useDisclosure`/`KohakuDisclosureLabel`), and
54
+ `<kohaku-surface>` gains a matching `disclosure` attribute (renderer-wc, applied to the host element,
55
+ with the visible label — `"label"` mode only — inside the shadow root). Both default to `"off"`: existing
56
+ DOM output is unchanged unless a host opts in.
57
+
58
+ See [docs/user-guide.md](../docs/user-guide.md)'s "Compliance Evidence Pack and AI-generation disclosure"
59
+ section for usage, EU AI Act Article 50 context (not legal advice), and a PII caution for exported Intent
60
+ `params`/request text.
61
+
62
+ - [#62](https://github.com/yosuque/kohaku/pull/62) [`cc17b7b`](https://github.com/yosuque/kohaku/commit/cc17b7bc3c96e49b1b74197ac20cd7a3d8ee0b47) Thanks [@yosuque](https://github.com/yosuque)! - Add Governed Actions: Human-In-The-Loop tiers for `DomainPort` write operations (design.md [#62](https://github.com/yosuque/kohaku/issues/62)/[#63](https://github.com/yosuque/kohaku/issues/63)/[#64](https://github.com/yosuque/kohaku/issues/64);
63
+ SPEC §5's ACT-PRM-001/ACT-APR-001/ACT-CNF-001, LIN-ACT-001, §6.2's MCPAPP-ACT-001).
64
+
65
+ An operation may declare `tier` (`"auto"` (default) / `"confirm"` / `"approve"`) and `paramsSchema`
66
+ (kohaku's own closed JSON Schema subset — `type`, `properties`, `required`,
67
+ `additionalProperties: false`, `enum`, `minimum`/`maximum`, `minLength`/`maxLength`, `items`, `maxItems`,
68
+ `x-message`; deliberately no `pattern`, to avoid both ReDoS and a JS/Python regex-dialect mismatch).
69
+ `spec-core`'s `validateActionParams`/`assertValidActionParamsSchema` (env-neutral, dependency-free, pinned
70
+ byte-for-byte against the Python port via the cross-language golden) enforces the schema before
71
+ `DomainPort.invoke` ever runs, on every write surface alike: REST's `POST /binding/action`, MCP's
72
+ `${prefix}_action`, and the client-side `preflightAction` check `renderer-core` runs before either.
73
+
74
+ `"approve"`-tier actions are gated by a new stateless, short-lived HMAC-signed `ApprovalPort`
75
+ (`@kohaku-ui/authz-hmac`'s `createHmacApprovalPort`, `"kohaku-approval.v1."`-prefixed tokens, 300s default
76
+ TTL) bound to `(action, payloadHash, requesterId, tenant)`; self-approval is refused at issuance, and a
77
+ verification failure the host cannot classify is treated as a denial (fail-closed). An optional
78
+ `ApprovalStore` adds single-use enforcement. REST gains `POST /approvals` (mints a token as an authorized
79
+ approver, governance kind `action.approve`) and both the REST body and the MCP action tool's input gain
80
+ optional `confirmed`/`approval` fields; a gate failure is `422 ACTION_PARAMS_INVALID` / `403
81
+ APPROVAL_REQUIRED` on REST (with `error.issues`/`error.approval`) and the MCP structured-tool-error
82
+ equivalent.
83
+
84
+ A compose response optionally carries an **Action manifest** (REST's `actions?` on
85
+ `/compose`/`/events`/`event: spec`; MCP's `_meta["kohaku/actions"]`) mapping each governed action name to
86
+ `{tier, paramsSchema?, confirmMessage?}` — placed outside the `UISpec` itself, next to the capability, so
87
+ it never affects `specHash` or the cache key. `renderer-core`'s `preflightAction` consults it client-side
88
+ before a write round-trip; `renderer-react`/`renderer-wc`/`mcp-renderer` thread `confirm`/`requestApproval`
89
+ hooks through (`renderer-react` ships a `globalThis.confirm`-backed default for the `"confirm"` tier; there
90
+ is no framework-neutral default for `"approve"`, so that tier stays gated until a product wires its own
91
+ hook). `@kohaku-ui/client`/`@kohaku-ui/data-binding` gain typed `ACTION_PARAMS_INVALID`/`APPROVAL_REQUIRED`
92
+ error codes and `confirmed`/`approval` request options. A host that records action outcomes to lineage does
93
+ so under a distinct `action.*` event family (`action.invoked`/`action.denied`/`action.approvalRequested`/
94
+ `action.approved`), carrying `payloadHash` but never the payload's own field values.
95
+
96
+ The Python port (`python/kohaku`) mirrors the full surface (`kohaku.spec.action_params`,
97
+ `kohaku.host_core.action_gate`/`action_audit`, `POST /approvals`, the MCP action-tool gate), and
98
+ `apps/sample-api` / `python/examples/sales-api` demonstrate both tiers end to end (`annotate`: confirm,
99
+ `publish`: approve) — see the [user guide](../docs/user-guide.md)'s "Governed actions: tiers" section.
100
+
101
+ **Behavior changes to check when upgrading.** (1) `POST /binding/action` and the MCP action tool now
102
+ reject, before `DomainPort.invoke` runs, any action that is not in the DomainPort's own
103
+ `listOperations()` — previously such an action was invoked ungated. It is rejected with the same response
104
+ as a missing write scope and recorded as `action.denied`. A product whose `listOperations()` omits an
105
+ operation it still expects to be invoked must declare it. (2) `validateActionParams` rejects a payload
106
+ property named `__proto__`, `constructor` or `prototype` at any depth (issue code `unsafeKey`), whatever
107
+ the schema's `additionalProperties` says.
108
+
109
+ - [#54](https://github.com/yosuque/kohaku/pull/54) [`bd2d484`](https://github.com/yosuque/kohaku/commit/bd2d484826bec86c78dcb720502740494a672ea8) Thanks [@yosuque](https://github.com/yosuque)! - Adds a typed way to register a product-specific part, so its `{type, version, propsSchema}` can live in one
110
+ `ComponentDefinition` instead of being re-typed as string literals at the registration call site (design.md
111
+ [#68](https://github.com/yosuque/kohaku/issues/68)).
112
+
113
+ `@kohaku-ui/renderer-react`: `implement(def, Component)` wraps a component whose props are inferred from
114
+ `def.propsSchema` (`z.infer`) — no `node.props["x"] as T` cast needed — and returns an entry consumed by the
115
+ new `ImplRegistry.use(entry)`. The existing `register(type, version, component)` / `ImplProps` keep working
116
+ unchanged for parts that have no static `ComponentDefinition` (e.g. a promoted part's per-artifact schema).
117
+
118
+ `@kohaku-ui/renderer-wc`: `<kohaku-surface>` gains a public `registerPart(type, version, builder)` (it was
119
+ previously private with no way for a host to register anything beyond the core catalog), plus `getPartVersion`
120
+ for introspection, and `implementWc(def, builder)` is the typed counterpart of `implement` for a `PartBuilder`.
121
+
122
+ Both `implement` and `implementWc` parse a node's props against the schema **unconditionally, in every
123
+ environment** (`propsSchema.safeParse` is also what materializes a `.default()`-ed prop the Spec omits, not
124
+ just a validation nicety, so it never skips in production) — on success the component receives the parsed
125
+ value, on failure it receives the raw (unvalidated) props instead (fail-open: a malformed prop degrades the
126
+ part's own display rather than the whole surface). Only the diagnostic — a `console.warn` on a mismatch — is
127
+ gated by environment: by default it fires outside a `NODE_ENV=production` build; pass `{ validate }` to force
128
+ the warning on or off regardless of environment.
129
+
130
+ ### Patch Changes
131
+
132
+ - Updated dependencies [[`fcd4eb7`](https://github.com/yosuque/kohaku/commit/fcd4eb7c8c6608030d4f9045648a305fa2e5992f), [`5f1bbbd`](https://github.com/yosuque/kohaku/commit/5f1bbbd09fe1edb984a7b0f5a0c5212c3da628ea), [`cc17b7b`](https://github.com/yosuque/kohaku/commit/cc17b7bc3c96e49b1b74197ac20cd7a3d8ee0b47), [`730e625`](https://github.com/yosuque/kohaku/commit/730e62584b249056078792f6f646019cb225b049), [`730e625`](https://github.com/yosuque/kohaku/commit/730e62584b249056078792f6f646019cb225b049), [`730e625`](https://github.com/yosuque/kohaku/commit/730e62584b249056078792f6f646019cb225b049), [`5d167cb`](https://github.com/yosuque/kohaku/commit/5d167cb386cc1f91102644f8a99bd5b5c2949ce0)]:
133
+ - @kohaku-ui/registry@0.4.0
134
+ - @kohaku-ui/renderer-core@0.4.0
135
+ - @kohaku-ui/spec-core@0.4.0
136
+ - @kohaku-ui/data-binding@0.4.0
137
+ - @kohaku-ui/sandbox@0.4.0
138
+
3
139
  ## 0.3.0
4
140
 
5
141
  ### Patch Changes
@@ -0,0 +1,37 @@
1
+ import type { ComponentDefinition } from "@kohaku-ui/registry";
2
+ import type { ComponentNode, JsonObject } from "@kohaku-ui/spec-core";
3
+ import type { z } from "zod";
4
+ import type { PartBuilder, RenderRuntime, Teardown } from "./types.js";
5
+ /**
6
+ * A part builder with typed props, taken from a `ComponentDefinition`'s `propsSchema` (design.md #68).
7
+ * Same shape as `PartBuilder`, with `props` added between `node` and `row`, so a product's builder never
8
+ * has to read `node.props["x"] as T` itself — the WC counterpart of renderer-react's typed `Component`.
9
+ */
10
+ export type TypedPartBuilder<P> = (rt: RenderRuntime, parent: ParentNode, node: ComponentNode, props: P, row: JsonObject | null) => Teardown;
11
+ /** Registration triple returned by `implementWc`, passed to `<kohaku-surface>.registerPart`. */
12
+ export interface PartEntry {
13
+ type: string;
14
+ version: string;
15
+ builder: PartBuilder;
16
+ }
17
+ /**
18
+ * Wraps a typed `TypedPartBuilder` into a plain `PartBuilder` bound to `def`'s own type and version — the
19
+ * WC counterpart of renderer-react's `implement` (design.md #68). Usage:
20
+ * ```ts
21
+ * const entry = implementWc(myPartDef, (rt, parent, node, props, row) => { ... });
22
+ * surface.registerPart(entry.type, entry.version, entry.builder);
23
+ * ```
24
+ *
25
+ * `def.propsSchema.safeParse` always runs, in every environment — not just outside a `NODE_ENV=production`
26
+ * build — because it is also what materializes a `.default()`-ed prop (or any other Zod transform) the Spec
27
+ * omits; skipping it in production would silently make `builder` see `undefined` for a prop its type says
28
+ * is always present. On success, `builder` receives `parsed.data`; on failure it receives the raw
29
+ * (unvalidated) `node.props` instead, matching the renderer's general fail-open policy. The parse (and so
30
+ * the warning) runs once per `node.props` object rather than on every rebuild. Only the **diagnostic** — a `console.warn` on a mismatch — is gated by environment: by default it fires outside a
31
+ * `NODE_ENV=production` build (see `isDevEnvironment`); pass `{ validate }` to force it on or off
32
+ * regardless of environment.
33
+ */
34
+ export declare function implementWc<P extends z.ZodObject>(def: ComponentDefinition<P>, builder: TypedPartBuilder<z.infer<P>>, options?: {
35
+ validate?: boolean;
36
+ }): PartEntry;
37
+ //# sourceMappingURL=implement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"implement.d.ts","sourceRoot":"","sources":["../src/implement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEvE;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,CAChC,EAAE,EAAE,aAAa,EACjB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,aAAa,EACnB,KAAK,EAAE,CAAC,EACR,GAAG,EAAE,UAAU,GAAG,IAAI,KACnB,QAAQ,CAAC;AAEd,gGAAgG;AAChG,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,WAAW,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,EAC/C,GAAG,EAAE,mBAAmB,CAAC,CAAC,CAAC,EAC3B,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EACrC,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/B,SAAS,CAyBX"}
@@ -0,0 +1,44 @@
1
+ import { isDevEnvironment } from "@kohaku-ui/renderer-core";
2
+ /**
3
+ * Wraps a typed `TypedPartBuilder` into a plain `PartBuilder` bound to `def`'s own type and version — the
4
+ * WC counterpart of renderer-react's `implement` (design.md #68). Usage:
5
+ * ```ts
6
+ * const entry = implementWc(myPartDef, (rt, parent, node, props, row) => { ... });
7
+ * surface.registerPart(entry.type, entry.version, entry.builder);
8
+ * ```
9
+ *
10
+ * `def.propsSchema.safeParse` always runs, in every environment — not just outside a `NODE_ENV=production`
11
+ * build — because it is also what materializes a `.default()`-ed prop (or any other Zod transform) the Spec
12
+ * omits; skipping it in production would silently make `builder` see `undefined` for a prop its type says
13
+ * is always present. On success, `builder` receives `parsed.data`; on failure it receives the raw
14
+ * (unvalidated) `node.props` instead, matching the renderer's general fail-open policy. The parse (and so
15
+ * the warning) runs once per `node.props` object rather than on every rebuild. Only the **diagnostic** — a `console.warn` on a mismatch — is gated by environment: by default it fires outside a
16
+ * `NODE_ENV=production` build (see `isDevEnvironment`); pass `{ validate }` to force it on or off
17
+ * regardless of environment.
18
+ */
19
+ export function implementWc(def, builder, options) {
20
+ const shouldWarn = options?.validate ?? isDevEnvironment();
21
+ // Parse once per props object, not once per rebuild: `node.props` keeps its identity for as long as the
22
+ // Spec does, so a rebuild (and the mismatch warning) reuses the first result. Keyed weakly, so a replaced
23
+ // Spec's props are collected with it.
24
+ const resolved = new WeakMap();
25
+ const wrapped = (rt, parent, node, row) => {
26
+ let props = resolved.get(node.props);
27
+ if (props === undefined) {
28
+ const parsed = def.propsSchema.safeParse(node.props);
29
+ if (parsed.success) {
30
+ props = parsed.data;
31
+ }
32
+ else {
33
+ props = node.props;
34
+ if (shouldWarn) {
35
+ console.warn(`[kohaku] component "${def.type}" (node ${node.id}) received props that don't match its schema: ${parsed.error.message}`);
36
+ }
37
+ }
38
+ resolved.set(node.props, props);
39
+ }
40
+ return builder(rt, parent, node, props, row);
41
+ };
42
+ return { type: def.type, version: def.version, builder: wrapped };
43
+ }
44
+ //# sourceMappingURL=implement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"implement.js","sourceRoot":"","sources":["../src/implement.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAyB5D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,WAAW,CACzB,GAA2B,EAC3B,OAAqC,EACrC,OAAgC;IAEhC,MAAM,UAAU,GAAG,OAAO,EAAE,QAAQ,IAAI,gBAAgB,EAAE,CAAC;IAC3D,wGAAwG;IACxG,0GAA0G;IAC1G,sCAAsC;IACtC,MAAM,QAAQ,GAAG,IAAI,OAAO,EAAsB,CAAC;IACnD,MAAM,OAAO,GAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QACrD,IAAI,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,MAAM,GAAG,GAAG,CAAC,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACrD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,KAAK,GAAG,MAAM,CAAC,IAAkB,CAAC;YACpC,CAAC;iBAAM,CAAC;gBACN,KAAK,GAAG,IAAI,CAAC,KAAmB,CAAC;gBACjC,IAAI,UAAU,EAAE,CAAC;oBACf,OAAO,CAAC,IAAI,CACV,uBAAuB,GAAG,CAAC,IAAI,WAAW,IAAI,CAAC,EAAE,iDAAiD,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CACzH,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAClC,CAAC;QACD,OAAO,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IAC/C,CAAC,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AACpE,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- export { DEFAULT_MESSAGES, type RendererMessages } from "@kohaku-ui/renderer-core";
1
+ export { type ActionManifest, type ActionManifestEntry, DEFAULT_MESSAGES, type PreflightActionResult, preflightAction, type RendererMessages, summarizeActionForModel, } from "@kohaku-ui/renderer-core";
2
+ export { implementWc, type PartEntry, type TypedPartBuilder } from "./implement.js";
2
3
  export { defineKohakuSurface, KOHAKU_EVENT, KohakuSurface } from "./kohaku-surface.js";
3
4
  export { createCoreRenderRegistry } from "./registry.js";
4
5
  export type { ActionPhase, PartBuilder, RenderRuntime, SurfaceContext, SurfaceEvent, Teardown, } from "./types.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACnF,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACvF,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACzD,YAAY,EACV,WAAW,EACX,WAAW,EACX,aAAa,EACb,cAAc,EACd,YAAY,EACZ,QAAQ,GACT,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,gBAAgB,EAChB,KAAK,qBAAqB,EAC1B,eAAe,EACf,KAAK,gBAAgB,EACrB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACpF,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACvF,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACzD,YAAY,EACV,WAAW,EACX,WAAW,EACX,aAAa,EACb,cAAc,EACd,YAAY,EACZ,QAAQ,GACT,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -2,7 +2,10 @@
2
2
  // A single <kohaku-surface> builds the entire UI Spec tree into one shadow root. The shared core is renderer-core.
3
3
  // Dependency direction: spec-core → {registry, data-binding} → renderer-core → renderer-wc (sandbox is imported directly).
4
4
  // UI message type and defaults (the single source of truth is renderer-core). Used to override context.messages.
5
- export { DEFAULT_MESSAGES } from "@kohaku-ui/renderer-core";
5
+ // Governed actions (design.md #62/#63/#64): re-exported from renderer-core (the framework-free source of
6
+ // truth) so a consumer does not need to reach into @kohaku-ui/renderer-core directly for these.
7
+ export { DEFAULT_MESSAGES, preflightAction, summarizeActionForModel, } from "@kohaku-ui/renderer-core";
8
+ export { implementWc } from "./implement.js";
6
9
  export { defineKohakuSurface, KOHAKU_EVENT, KohakuSurface } from "./kohaku-surface.js";
7
10
  export { createCoreRenderRegistry } from "./registry.js";
8
11
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,mHAAmH;AACnH,2HAA2H;AAE3H,iHAAiH;AACjH,OAAO,EAAE,gBAAgB,EAAyB,MAAM,0BAA0B,CAAC;AACnF,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACvF,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,mHAAmH;AACnH,2HAA2H;AAE3H,iHAAiH;AACjH,yGAAyG;AACzG,gGAAgG;AAChG,OAAO,EAGL,gBAAgB,EAEhB,eAAe,EAEf,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,WAAW,EAAyC,MAAM,gBAAgB,CAAC;AACpF,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACvF,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC"}
@@ -1,8 +1,10 @@
1
1
  import { type RendererMessages } from "@kohaku-ui/renderer-core";
2
2
  import { type ThemeTokens, type UISpec } from "@kohaku-ui/spec-core";
3
- import type { SurfaceContext } from "./types.js";
3
+ import type { PartBuilder, SurfaceContext } from "./types.js";
4
4
  /** Name of the forward event dispatched by <kohaku-surface> (detail is a SurfaceEvent). */
5
5
  export declare const KOHAKU_EVENT = "kohaku-event";
6
+ /** `disclosure` attribute values (design.md #66); anything else (including absence) behaves as `"off"`. */
7
+ type DisclosureMode = "off" | "attributes" | "label";
6
8
  /**
7
9
  * The single host element <kohaku-surface> (Shadow DOM). It builds the entire UI Spec tree into one shadow root
8
10
  * (rather than a Custom Element per part type — a single-element surface). Spec / context are received as properties.
@@ -15,8 +17,13 @@ export declare const KOHAKU_EVENT = "kohaku-event";
15
17
  */
16
18
  export declare class KohakuSurface extends HTMLElement {
17
19
  #private;
20
+ static get observedAttributes(): string[];
18
21
  constructor();
19
22
  connectedCallback(): void;
23
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
24
+ /** `"off"` (default) | `"attributes"` | `"label"` (design.md #66). Reflects the `disclosure` attribute. */
25
+ get disclosure(): DisclosureMode;
26
+ set disclosure(v: DisclosureMode);
20
27
  disconnectedCallback(): void;
21
28
  /** UI Spec. Setting it triggers resetForIntent → rebuild the tree + reattach the controller. */
22
29
  get spec(): UISpec | null;
@@ -35,8 +42,38 @@ export declare class KohakuSurface extends HTMLElement {
35
42
  set onEvent(v: SurfaceContext["onEvent"]);
36
43
  set onNodeError(v: SurfaceContext["onNodeError"]);
37
44
  set onActionResult(v: SurfaceContext["onActionResult"]);
45
+ set actionManifest(v: SurfaceContext["actionManifest"]);
46
+ set confirm(v: SurfaceContext["confirm"]);
47
+ set requestApproval(v: SurfaceContext["requestApproval"]);
38
48
  set sandbox(v: SurfaceContext["sandbox"]);
49
+ /**
50
+ * Public registration API for product-specific parts (design.md #68). Previously `#registry` was private
51
+ * with no way for a host to add its own component types — a product could not register anything beyond
52
+ * the core catalog on the WC surface at all. Pair with `implementWc` for typed props:
53
+ * ```ts
54
+ * const entry = implementWc(myPartDef, builder);
55
+ * surface.registerPart(entry.type, entry.version, entry.builder);
56
+ * ```
57
+ * `version` is recorded for parity with renderer-react's `ImplRegistry.register`; unlike that registry,
58
+ * this surface does not (yet) feed a `SurfaceCapabilities` table into `@kohaku-ui/registry`'s
59
+ * `negotiate()` — no host wires WC capability negotiation today (AGENTS.md's renderer-wc section) — so
60
+ * `version` is not itself consulted by any negotiation path yet. Call before assigning `spec` when
61
+ * possible; if a Spec is already mounted, this rebuilds it so the newly registered part renders
62
+ * immediately instead of only taking effect on the next Spec swap.
63
+ *
64
+ * `#registry` starts seeded with the 16 core-catalog parts (`createCoreRenderRegistry`), so registering
65
+ * an already-taken `type` — most easily a core one, by typo or intentional override — silently replaces
66
+ * it. That is sometimes exactly what a product wants (replacing a core part with its own), but silent
67
+ * shadowing of a core part is also an easy way to lose one by accident, so replacing any existing entry
68
+ * warns via `console.warn` unless the caller opts in explicitly with `{ override: true }`.
69
+ */
70
+ registerPart(type: string, version: string, builder: PartBuilder, options?: {
71
+ override?: boolean;
72
+ }): void;
73
+ /** The version last passed to `registerPart` for `type`, if any (introspection only — see its doc comment). */
74
+ getPartVersion(type: string): string | undefined;
39
75
  }
40
76
  /** Registers <kohaku-surface> as a custom element (idempotent). */
41
77
  export declare function defineKohakuSurface(tagName?: string): void;
78
+ export {};
42
79
  //# sourceMappingURL=kohaku-surface.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"kohaku-surface.d.ts","sourceRoot":"","sources":["../src/kohaku-surface.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,gBAAgB,EAItB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAqB,KAAK,WAAW,EAAE,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAIxF,OAAO,KAAK,EAAe,cAAc,EAAY,MAAM,YAAY,CAAC;AAExE,2FAA2F;AAC3F,eAAO,MAAM,YAAY,iBAAiB,CAAC;AA0B3C;;;;;;;;;GASG;AACH,qBAAa,aAAc,SAAQ,WAAW;;IAW5C,cAYC;IAED,iBAAiB,IAAI,IAAI,CAUxB;IAED,oBAAoB,IAAI,IAAI,CAG3B;IAUD,gGAAgG;IAChG,IAAI,IAAI,IAAI,MAAM,GAAG,IAAI,CAExB;IACD,IAAI,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAK3B;IAED;;;OAGG;IACH,IAAI,OAAO,IAAI,cAAc,CAE5B;IACD,IAAI,OAAO,CAAC,IAAI,EAAE,cAAc,EAG/B;IAED,4EAA4E;IAC5E,IAAI,OAAO,CAAC,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,EAEvC;IACD,IAAI,KAAK,CAAC,CAAC,EAAE,WAAW,GAAG,SAAS,EAEnC;IACD,IAAI,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,EAE/B;IACD,IAAI,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,SAAS,EAEpD;IACD,IAAI,OAAO,CAAC,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,EAEvC;IACD,IAAI,WAAW,CAAC,CAAC,EAAE,cAAc,CAAC,aAAa,CAAC,EAE/C;IACD,IAAI,cAAc,CAAC,CAAC,EAAE,cAAc,CAAC,gBAAgB,CAAC,EAErD;IACD,IAAI,OAAO,CAAC,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,EAEvC;CA2EF;AAED,mEAAmE;AACnE,wBAAgB,mBAAmB,CAAC,OAAO,SAAmB,GAAG,IAAI,CAIpE"}
1
+ {"version":3,"file":"kohaku-surface.d.ts","sourceRoot":"","sources":["../src/kohaku-surface.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,gBAAgB,EAItB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAqB,KAAK,WAAW,EAAE,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAIxF,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAY,MAAM,YAAY,CAAC;AAExE,2FAA2F;AAC3F,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAiC3C,2GAA2G;AAC3G,KAAK,cAAc,GAAG,KAAK,GAAG,YAAY,GAAG,OAAO,CAAC;AAErD;;;;;;;;;GASG;AACH,qBAAa,aAAc,SAAQ,WAAW;;IAqB5C,MAAM,KAAK,kBAAkB,IAAI,MAAM,EAAE,CAExC;IAED,cAYC;IAED,iBAAiB,IAAI,IAAI,CAWxB;IAED,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAE7F;IAED,2GAA2G;IAC3G,IAAI,UAAU,IAAI,cAAc,CAG/B;IACD,IAAI,UAAU,CAAC,CAAC,EAAE,cAAc,EAG/B;IAED,oBAAoB,IAAI,IAAI,CAG3B;IAUD,gGAAgG;IAChG,IAAI,IAAI,IAAI,MAAM,GAAG,IAAI,CAExB;IACD,IAAI,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAK3B;IAED;;;OAGG;IACH,IAAI,OAAO,IAAI,cAAc,CAE5B;IACD,IAAI,OAAO,CAAC,IAAI,EAAE,cAAc,EAG/B;IAED,4EAA4E;IAC5E,IAAI,OAAO,CAAC,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,EAEvC;IACD,IAAI,KAAK,CAAC,CAAC,EAAE,WAAW,GAAG,SAAS,EAEnC;IACD,IAAI,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,EAE/B;IACD,IAAI,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,SAAS,EAEpD;IACD,IAAI,OAAO,CAAC,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,EAEvC;IACD,IAAI,WAAW,CAAC,CAAC,EAAE,cAAc,CAAC,aAAa,CAAC,EAE/C;IACD,IAAI,cAAc,CAAC,CAAC,EAAE,cAAc,CAAC,gBAAgB,CAAC,EAErD;IACD,IAAI,cAAc,CAAC,CAAC,EAAE,cAAc,CAAC,gBAAgB,CAAC,EAErD;IACD,IAAI,OAAO,CAAC,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,EAEvC;IACD,IAAI,eAAe,CAAC,CAAC,EAAE,cAAc,CAAC,iBAAiB,CAAC,EAEvD;IACD,IAAI,OAAO,CAAC,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,EAEvC;IAoBD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAUxG;IAED,+GAA+G;IAC/G,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE/C;CAuHF;AAED,mEAAmE;AACnE,wBAAgB,mBAAmB,CAAC,OAAO,SAAmB,GAAG,IAAI,CAIpE"}
@@ -1,4 +1,4 @@
1
- import { createDataInvalidationBus, createSpecStateStore, DEFAULT_LOCALE, DEFAULT_MESSAGES, PARTS_STATE_CSS, themeTokensToCssVars, } from "@kohaku-ui/renderer-core";
1
+ import { createDataInvalidationBus, createSpecStateStore, DEFAULT_LOCALE, DEFAULT_MESSAGES, deriveDisclosure, disclosureDomAttributes, PARTS_STATE_CSS, themeTokensToCssVars, } from "@kohaku-ui/renderer-core";
2
2
  import { ROOT_COMPONENT_ID } from "@kohaku-ui/spec-core";
3
3
  import { noop } from "./dom.js";
4
4
  import { createCoreRenderRegistry } from "./registry.js";
@@ -9,9 +9,12 @@ export const KOHAKU_EVENT = "kohaku-event";
9
9
  * Individual context properties whose change requires tearing down and rebuilding the mounted tree
10
10
  * (they are baked into the RenderRuntime at #render time — binding/theme/locale/messages feed the
11
11
  * BoundDataController / theme resolution / message lookup, and sandbox is read at mount time by the
12
- * sandbox.html part builder). `onEvent` / `onNodeError` / `onActionResult` are deliberately **not** in
13
- * this set: they are read live from `#context` at call time (see `#dispatchForward` and the wrapper
14
- * closures built in `#render`), so reassigning one of them mid-lifecycle must not force a full rebuild.
12
+ * sandbox.html part builder). `onEvent` / `onNodeError` / `onActionResult` and the action-governance
13
+ * hooks `actionManifest` / `confirm` / `requestApproval` are deliberately **not** in this set: they are
14
+ * read live from `#context` at call time (see `#dispatchForward` and the wrapper closures / getters built
15
+ * in `#render`), so reassigning one of them mid-lifecycle must not force a full rebuild -- which would
16
+ * tear down every mounted L2 sandbox iframe, and would fire twice when a host assigns the spec and its
17
+ * manifest back to back.
15
18
  */
16
19
  const REBUILD_KEYS = new Set(["binding", "theme", "locale", "messages", "sandbox"]);
17
20
  /** Every property <kohaku-surface> exposes as a plain instance accessor (used by #upgradeProperty). */
@@ -25,7 +28,11 @@ const UPGRADE_PROPS = [
25
28
  "onEvent",
26
29
  "onNodeError",
27
30
  "onActionResult",
31
+ "actionManifest",
32
+ "confirm",
33
+ "requestApproval",
28
34
  "sandbox",
35
+ "disclosure",
29
36
  ];
30
37
  /**
31
38
  * The single host element <kohaku-surface> (Shadow DOM). It builds the entire UI Spec tree into one shadow root
@@ -42,10 +49,22 @@ export class KohakuSurface extends HTMLElement {
42
49
  #spec = null;
43
50
  #context = {};
44
51
  #registry = createCoreRenderRegistry();
52
+ // version is not read by #render (RenderRuntime.registry only needs the builder); it is recorded purely
53
+ // for parity with renderer-react's ImplRegistry.register (see registerPart's doc comment).
54
+ #partVersions = new Map();
45
55
  // The store is retained by intent.hash; the bus stays the same for the surface's lifetime (equivalent to renderer-react's Provider useRef bus).
46
56
  #store = null;
47
57
  #bus = createDataInvalidationBus();
48
58
  #teardown = noop;
59
+ // AI-generation disclosure (design.md #66). Unlike the properties above, `disclosure` is attribute-backed
60
+ // (`static observedAttributes` + `attributeChangedCallback`) rather than a plain instance accessor: it is
61
+ // a simple serializable enum, well suited to `<kohaku-surface disclosure="label">` markup, unlike
62
+ // spec/context/binding/theme which need real object identity. #disclosureLabelEl is the shadow-root
63
+ // element `#applyDisclosure` creates on demand for `disclosure="label"`; null in every other mode.
64
+ #disclosureLabelEl = null;
65
+ static get observedAttributes() {
66
+ return ["disclosure"];
67
+ }
49
68
  constructor() {
50
69
  super();
51
70
  const shadow = this.attachShadow({ mode: "open" });
@@ -66,12 +85,28 @@ export class KohakuSurface extends HTMLElement {
66
85
  // forever, so its setter logic (patchContext / re-render) would never fire again once the element is
67
86
  // upgraded. Re-apply each such value through its accessor exactly once, here, at upgrade time
68
87
  // (connectedCallback always fires after the class is defined). A no-op for an element created after
69
- // define() (no own-property was ever set).
88
+ // define() (no own-property was ever set). "disclosure" is listed last so, if it was pre-assigned
89
+ // alongside "spec", #render() (triggered by spec's own upgrade) already sees the right value.
70
90
  for (const prop of UPGRADE_PROPS)
71
91
  this.#upgradeProperty(prop);
72
92
  if (this.#spec != null)
73
93
  this.#render();
74
94
  }
95
+ attributeChangedCallback(name, oldValue, newValue) {
96
+ if (name === "disclosure" && oldValue !== newValue)
97
+ this.#applyDisclosure();
98
+ }
99
+ /** `"off"` (default) | `"attributes"` | `"label"` (design.md #66). Reflects the `disclosure` attribute. */
100
+ get disclosure() {
101
+ const v = this.getAttribute("disclosure");
102
+ return v === "attributes" || v === "label" ? v : "off";
103
+ }
104
+ set disclosure(v) {
105
+ if (v === "off")
106
+ this.removeAttribute("disclosure");
107
+ else
108
+ this.setAttribute("disclosure", v);
109
+ }
75
110
  disconnectedCallback() {
76
111
  this.#teardown();
77
112
  this.#teardown = noop;
@@ -129,6 +164,15 @@ export class KohakuSurface extends HTMLElement {
129
164
  set onActionResult(v) {
130
165
  this.#patchContext({ onActionResult: v });
131
166
  }
167
+ set actionManifest(v) {
168
+ this.#patchContext({ actionManifest: v });
169
+ }
170
+ set confirm(v) {
171
+ this.#patchContext({ confirm: v });
172
+ }
173
+ set requestApproval(v) {
174
+ this.#patchContext({ requestApproval: v });
175
+ }
132
176
  set sandbox(v) {
133
177
  this.#patchContext({ sandbox: v });
134
178
  }
@@ -152,14 +196,51 @@ export class KohakuSurface extends HTMLElement {
152
196
  if (needsRebuild && this.#spec != null)
153
197
  this.#render();
154
198
  }
199
+ /**
200
+ * Public registration API for product-specific parts (design.md #68). Previously `#registry` was private
201
+ * with no way for a host to add its own component types — a product could not register anything beyond
202
+ * the core catalog on the WC surface at all. Pair with `implementWc` for typed props:
203
+ * ```ts
204
+ * const entry = implementWc(myPartDef, builder);
205
+ * surface.registerPart(entry.type, entry.version, entry.builder);
206
+ * ```
207
+ * `version` is recorded for parity with renderer-react's `ImplRegistry.register`; unlike that registry,
208
+ * this surface does not (yet) feed a `SurfaceCapabilities` table into `@kohaku-ui/registry`'s
209
+ * `negotiate()` — no host wires WC capability negotiation today (AGENTS.md's renderer-wc section) — so
210
+ * `version` is not itself consulted by any negotiation path yet. Call before assigning `spec` when
211
+ * possible; if a Spec is already mounted, this rebuilds it so the newly registered part renders
212
+ * immediately instead of only taking effect on the next Spec swap.
213
+ *
214
+ * `#registry` starts seeded with the 16 core-catalog parts (`createCoreRenderRegistry`), so registering
215
+ * an already-taken `type` — most easily a core one, by typo or intentional override — silently replaces
216
+ * it. That is sometimes exactly what a product wants (replacing a core part with its own), but silent
217
+ * shadowing of a core part is also an easy way to lose one by accident, so replacing any existing entry
218
+ * warns via `console.warn` unless the caller opts in explicitly with `{ override: true }`.
219
+ */
220
+ registerPart(type, version, builder, options) {
221
+ if (this.#registry.has(type) && options?.override !== true) {
222
+ console.warn(`[kohaku] registerPart("${type}", ...) replaces an existing part. ` +
223
+ "Pass { override: true } if that is intentional.");
224
+ }
225
+ this.#registry.set(type, builder);
226
+ this.#partVersions.set(type, version);
227
+ if (this.#spec != null)
228
+ this.#render();
229
+ }
230
+ /** The version last passed to `registerPart` for `type`, if any (introspection only — see its doc comment). */
231
+ getPartVersion(type) {
232
+ return this.#partVersions.get(type);
233
+ }
155
234
  #render() {
156
235
  // Tear down the previous tree (controller detach / unsubscribe / sandbox destroy / anchor removal).
157
236
  this.#teardown();
158
237
  this.#teardown = noop;
159
238
  this.#root.replaceChildren();
160
239
  const spec = this.#spec;
161
- if (spec == null)
240
+ if (spec == null) {
241
+ this.#applyDisclosure();
162
242
  return;
243
+ }
163
244
  const theme = this.#context.theme ?? {};
164
245
  const messages = { ...DEFAULT_MESSAGES, ...(this.#context.messages ?? {}) };
165
246
  const locale = this.#context.locale ?? DEFAULT_LOCALE;
@@ -175,6 +256,7 @@ export class KohakuSurface extends HTMLElement {
175
256
  for (const [name, value] of Object.entries(themeTokensToCssVars(theme))) {
176
257
  this.style.setProperty(name, value);
177
258
  }
259
+ const liveContext = () => this.#context;
178
260
  const rt = createRuntime({
179
261
  spec,
180
262
  store: this.#store,
@@ -187,21 +269,77 @@ export class KohakuSurface extends HTMLElement {
187
269
  // just one of these callbacks does not go through #render again, so RenderRuntime's own onNodeError/
188
270
  // onActionResult fields (baked in once here) must forward through a stable indirection instead of
189
271
  // holding a stale reference to whatever callback existed when the tree was last built.
272
+ // actionManifest / confirm / requestApproval are exposed as getters for the same reason: they are
273
+ // consulted per invoke, so a reassignment takes effect on the next action without a rebuild.
190
274
  ctx: {
191
275
  ...this.#context,
192
276
  onNodeError: (args) => this.#context.onNodeError?.(args),
193
277
  onActionResult: (args) => this.#context.onActionResult?.(args),
278
+ get actionManifest() {
279
+ return liveContext().actionManifest;
280
+ },
281
+ get confirm() {
282
+ return liveContext().confirm;
283
+ },
284
+ get requestApproval() {
285
+ return liveContext().requestApproval;
286
+ },
194
287
  },
195
288
  registry: this.#registry,
196
289
  dispatchForward: (event) => this.#dispatchForward(event),
197
290
  });
198
291
  this.#teardown = mountTree(rt, this.#root, ROOT_COMPONENT_ID);
292
+ this.#applyDisclosure();
199
293
  }
200
294
  #dispatchForward(event) {
201
295
  // composed so it crosses the shadow boundary and reaches the host page. Flows to both paths regardless of whether a sink exists.
202
296
  this.dispatchEvent(new CustomEvent(KOHAKU_EVENT, { detail: event, bubbles: true, composed: true }));
203
297
  this.#context.onEvent?.(event);
204
298
  }
299
+ /**
300
+ * Applies (or clears) AI-generation disclosure (design.md #66): the `data-kohaku-disclosure` /
301
+ * `data-kohaku-tier` / `data-digital-source-type` attributes go on the *host* element itself (`this`),
302
+ * not inside the shadow root -- unlike renderer-react's SpecView, which has no host element of its own
303
+ * to mark and so wraps the tree in an internal `<div>` instead. The visible label (`disclosure="label"`
304
+ * only) is a shadow-root element, since that is what is actually displayed. Called from `#render()`
305
+ * (spec/context changes) and from `attributeChangedCallback` (the `disclosure` attribute changing on
306
+ * its own, with no other change to re-render for).
307
+ */
308
+ #applyDisclosure() {
309
+ const mode = this.disclosure;
310
+ if (mode === "off" || this.#spec == null) {
311
+ this.removeAttribute("data-kohaku-disclosure");
312
+ this.removeAttribute("data-kohaku-tier");
313
+ this.removeAttribute("data-digital-source-type");
314
+ this.#disclosureLabelEl?.remove();
315
+ this.#disclosureLabelEl = null;
316
+ return;
317
+ }
318
+ const disclosure = deriveDisclosure(this.#spec.provenance);
319
+ const attrs = disclosureDomAttributes(disclosure);
320
+ this.setAttribute("data-kohaku-disclosure", attrs["data-kohaku-disclosure"]);
321
+ this.setAttribute("data-kohaku-tier", attrs["data-kohaku-tier"]);
322
+ if (attrs["data-digital-source-type"] != null) {
323
+ this.setAttribute("data-digital-source-type", attrs["data-digital-source-type"]);
324
+ }
325
+ else {
326
+ this.removeAttribute("data-digital-source-type");
327
+ }
328
+ if (mode === "label" && disclosure.level !== "none") {
329
+ const messages = { ...DEFAULT_MESSAGES, ...(this.#context.messages ?? {}) };
330
+ const text = disclosure.level === "ai-generated" ? messages.disclosureAiGenerated : messages.disclosureAiReviewed;
331
+ if (this.#disclosureLabelEl == null) {
332
+ this.#disclosureLabelEl = document.createElement("div");
333
+ this.#disclosureLabelEl.setAttribute("data-kohaku-disclosure-label", "");
334
+ this.shadowRoot.insertBefore(this.#disclosureLabelEl, this.#root);
335
+ }
336
+ this.#disclosureLabelEl.textContent = text;
337
+ }
338
+ else {
339
+ this.#disclosureLabelEl?.remove();
340
+ this.#disclosureLabelEl = null;
341
+ }
342
+ }
205
343
  }
206
344
  /** Registers <kohaku-surface> as a custom element (idempotent). */
207
345
  export function defineKohakuSurface(tagName = "kohaku-surface") {