@ggui-ai/protocol 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/dist/envelopes/render-refusal.d.ts +49 -0
  2. package/dist/envelopes/render-refusal.d.ts.map +1 -0
  3. package/dist/envelopes/render-refusal.js +12 -0
  4. package/dist/errors/domain-error.d.ts +70 -0
  5. package/dist/errors/domain-error.d.ts.map +1 -0
  6. package/dist/errors/domain-error.js +118 -0
  7. package/dist/gadgets/resolve-app-gadgets.d.ts +1 -1
  8. package/dist/gadgets/stdlib-gadgets.d.ts +2 -2
  9. package/dist/gadgets/stdlib-gadgets.js +1 -1
  10. package/dist/index.d.ts +69 -63
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +8 -2
  13. package/dist/integrations/mcp-apps.d.ts +35 -9
  14. package/dist/integrations/mcp-apps.d.ts.map +1 -1
  15. package/dist/integrations/mcp-apps.js +13 -13
  16. package/dist/integrations/overlay-hash.d.ts +19 -0
  17. package/dist/integrations/overlay-hash.d.ts.map +1 -0
  18. package/dist/integrations/overlay-hash.js +29 -0
  19. package/dist/integrations/theme-binding.d.ts +14 -12
  20. package/dist/integrations/theme-binding.d.ts.map +1 -1
  21. package/dist/integrations/theme-binding.js +10 -6
  22. package/dist/registry/blueprint-key.d.ts.map +1 -1
  23. package/dist/registry/blueprint-key.js +4 -3
  24. package/dist/schemas/app-theme.d.ts +46 -10
  25. package/dist/schemas/app-theme.d.ts.map +1 -1
  26. package/dist/schemas/app-theme.js +108 -40
  27. package/dist/schemas/blueprint.d.ts +4 -3
  28. package/dist/schemas/blueprint.d.ts.map +1 -1
  29. package/dist/schemas/blueprint.js +15 -3
  30. package/dist/schemas/data-contract.d.ts +11 -31
  31. package/dist/schemas/data-contract.d.ts.map +1 -1
  32. package/dist/schemas/data-contract.js +5 -28
  33. package/dist/schemas/handshake-suggestion.d.ts.map +1 -1
  34. package/dist/schemas/handshake-suggestion.js +8 -1
  35. package/dist/schemas/interface-context.d.ts +44 -0
  36. package/dist/schemas/interface-context.d.ts.map +1 -0
  37. package/dist/schemas/interface-context.js +22 -0
  38. package/dist/schemas/invoke.d.ts +3 -3
  39. package/dist/schemas/invoke.js +1 -1
  40. package/dist/schemas/mcp.d.ts +246 -111
  41. package/dist/schemas/mcp.d.ts.map +1 -1
  42. package/dist/schemas/mcp.js +149 -42
  43. package/dist/schemas/ops-blueprint.d.ts +4 -4
  44. package/dist/schemas/ops-blueprint.d.ts.map +1 -1
  45. package/dist/schemas/ops-blueprint.js +13 -8
  46. package/dist/schemas/public-env-key.d.ts +26 -0
  47. package/dist/schemas/public-env-key.d.ts.map +1 -0
  48. package/dist/schemas/public-env-key.js +25 -0
  49. package/dist/schemas/render-input-envelope.d.ts +2 -2
  50. package/dist/schemas/render-input-envelope.js +1 -1
  51. package/dist/schemas/runtime-telemetry-limits.d.ts +11 -0
  52. package/dist/schemas/runtime-telemetry-limits.d.ts.map +1 -0
  53. package/dist/schemas/runtime-telemetry-limits.js +10 -0
  54. package/dist/transport/websocket.d.ts +5 -5
  55. package/dist/types/auth.d.ts +1 -1
  56. package/dist/types/blueprint-source.d.ts +27 -7
  57. package/dist/types/blueprint-source.d.ts.map +1 -1
  58. package/dist/types/blueprint-source.js +19 -3
  59. package/dist/types/blueprint.d.ts +2 -2
  60. package/dist/types/blueprint.d.ts.map +1 -1
  61. package/dist/types/contract-inference.d.ts +1 -1
  62. package/dist/types/data-contract.d.ts +3 -3
  63. package/dist/types/domain-error-codes.d.ts +167 -0
  64. package/dist/types/domain-error-codes.d.ts.map +1 -0
  65. package/dist/types/domain-error-codes.js +170 -0
  66. package/dist/types/events.d.ts +1 -1
  67. package/dist/types/ggui-session-event.d.ts +2 -2
  68. package/dist/types/handshake-suggestion.d.ts +2 -2
  69. package/dist/types/host-context.d.ts +3 -3
  70. package/dist/types/interface-context.d.ts +1 -1
  71. package/dist/types/invoke.d.ts +1 -1
  72. package/dist/types/lifecycle.d.ts +1 -1
  73. package/dist/types/live-channel.d.ts +3 -3
  74. package/dist/types/llm-route.d.ts +59 -2
  75. package/dist/types/llm-route.d.ts.map +1 -1
  76. package/dist/types/llm-route.js +62 -16
  77. package/dist/types/llm.d.ts +22 -2
  78. package/dist/types/llm.d.ts.map +1 -1
  79. package/dist/types/llm.js +29 -2
  80. package/dist/types/mcp.d.ts +49 -100
  81. package/dist/types/mcp.d.ts.map +1 -1
  82. package/dist/types/mcp.js +28 -9
  83. package/dist/types/refusal-codes.d.ts +54 -95
  84. package/dist/types/refusal-codes.d.ts.map +1 -1
  85. package/dist/types/refusal-codes.js +67 -105
  86. package/dist/types/render.d.ts +12 -11
  87. package/dist/types/render.d.ts.map +1 -1
  88. package/dist/types/ui-generator.d.ts +1 -1
  89. package/dist/validation/ajv-runtime.d.ts +2 -2
  90. package/dist/validation/contract-validator.d.ts +14 -8
  91. package/dist/validation/contract-validator.d.ts.map +1 -1
  92. package/dist/validation/contract-validator.js +12 -4
  93. package/dist/validation/cross-references.d.ts +2 -2
  94. package/dist/validation/hygiene-rules.d.ts +1 -1
  95. package/dist/validation/lint-contract.d.ts +2 -2
  96. package/dist/validation/lint-contract.d.ts.map +1 -1
  97. package/dist/validation/lint-contract.js +6 -4
  98. package/dist/validation/name-invariants.d.ts +2 -2
  99. package/dist/validation/reserved-channels.d.ts +1 -1
  100. package/dist/validation/schema-compat-invariants.d.ts +2 -2
  101. package/dist/validation/schema-meta-validation.d.ts +1 -1
  102. package/dist/validation/schema-subset.d.ts +3 -3
  103. package/dist/version.d.ts +485 -12
  104. package/dist/version.d.ts.map +1 -1
  105. package/dist/version.js +484 -11
  106. package/dist/wire.d.ts +45 -0
  107. package/dist/wire.d.ts.map +1 -0
  108. package/dist/wire.js +44 -0
  109. package/package.json +7 -1
  110. package/dist/envelope-adapters.d.ts +0 -25
  111. package/dist/envelope-adapters.d.ts.map +0 -1
  112. package/dist/envelope-adapters.js +0 -14
@@ -131,18 +131,7 @@ export function deriveContextName(slotKey) {
131
131
  * @public
132
132
  */
133
133
  export const MCP_APP_AI_GGUI_RENDER_META_KEY = 'ai.ggui/render';
134
- /**
135
- * Read the `ai.ggui/render` slice off a parsed JSON-RPC `_meta` object.
136
- *
137
- * Structural validation only. Missing key returns `{ok: true, meta: undefined}`
138
- * — not a failure. Required-fields gate (sessionId / appId / runtimeUrl)
139
- * fires only when the key is present. Field-level optional-field
140
- * defensive parsing (e.g. context-slot schema narrowing, expiresAt date
141
- * parse) lives downstream in the iframe-runtime's `validateMeta`.
142
- *
143
- * @public
144
- */
145
- export function parseMcpAppAiGguiRenderMeta(meta) {
134
+ export function parseMcpAppAiGguiRenderMeta(meta, options = {}) {
146
135
  if (!isRecord(meta)) {
147
136
  return { ok: true };
148
137
  }
@@ -241,6 +230,9 @@ export function parseMcpAppAiGguiRenderMeta(meta) {
241
230
  // slice (tolerant degrade, consistent with the other optional fields).
242
231
  const themeParse = s.theme !== undefined ? appThemeSchema.safeParse(s.theme) : undefined;
243
232
  const parsedTheme = themeParse?.success === true ? themeParse.data : undefined;
233
+ if (themeParse !== undefined && !themeParse.success) {
234
+ options.onInvalidTheme?.(themeParse.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`));
235
+ }
244
236
  const slice = {
245
237
  sessionId: s.sessionId,
246
238
  appId: s.appId,
@@ -601,6 +593,14 @@ export function isGguiSubmitActionInput(value) {
601
593
  return true;
602
594
  }
603
595
  }
596
+ /**
597
+ * Narrow a guarded envelope to its `dispatch` member. Sound because
598
+ * {@link isGguiSubmitActionInput}'s closed-set `switch` validates the
599
+ * `'dispatch'` payload before any extension kind can carry that literal.
600
+ */
601
+ export function isGguiSubmitDispatchInput(env) {
602
+ return env.kind === 'dispatch';
603
+ }
604
604
  /**
605
605
  * Does this slice carry at least one MOUNT MODE discriminator?
606
606
  * Mirrors the iframe-runtime's `validateMeta`: the runtime needs
@@ -686,7 +686,7 @@ export function toolResultGguiRender(result) {
686
686
  *
687
687
  * @public
688
688
  */
689
- export const GGUI_RENDER_SHELL_SURFACE = 'var(--ggui-shell-background, var(--ggui-color-surface, var(--ggui-shell-scheme-surface, #f9fafb)))';
689
+ export const GGUI_RENDER_SHELL_SURFACE = 'var(--ggui-shell-background, var(--ggui-color-ground, var(--ggui-shell-scheme-surface, #f9fafb)))';
690
690
  /**
691
691
  * Inline `<style>` block that gives the shell's pre-render placeholder
692
692
  * a SCHEME-AWARE neutral ground (#662). Before the runtime injects
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Overlay attestation (ggui#987 §6.3): ONE canonical serialization and ONE
3
+ * hash for the per-app theme overlay, so every minter (a platform's
4
+ * projector, a console, a CLI) and every write door compute the same bytes.
5
+ *
6
+ * Canonical JSON: object keys sorted recursively, no whitespace, `undefined`
7
+ * members omitted, strings/numbers as JSON emits them. Hash: SHA-256 over the
8
+ * UTF-8 bytes, lowercase hex. Async because it uses Web Crypto
9
+ * (`globalThis.crypto.subtle`), available in every runtime the protocol
10
+ * targets.
11
+ */
12
+ import type { AppTheme } from '../schemas/app-theme.js';
13
+ /** The attested part of an overlay — never `mode`, `name` or `frameless`. */
14
+ export type OverlayHashInput = Pick<AppTheme, 'overlays' | 'cssVariables' | 'keyframes'>;
15
+ /** The canonical JSON the hash is taken over — exported so tests and other minters can pin it. */
16
+ export declare function canonicalOverlayJson(input: OverlayHashInput): string;
17
+ /** `sha256(canonicalOverlayJson(input))` as lowercase hex — the value `AppTheme.overlayHash` carries. */
18
+ export declare function canonicalOverlayHash(input: OverlayHashInput): Promise<string>;
19
+ //# sourceMappingURL=overlay-hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlay-hash.d.ts","sourceRoot":"","sources":["../../src/integrations/overlay-hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAExD,6EAA6E;AAC7E,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,QAAQ,EAAE,UAAU,GAAG,cAAc,GAAG,WAAW,CAAC,CAAC;AAczF,kGAAkG;AAClG,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAOpE;AAMD,yGAAyG;AACzG,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAGnF"}
@@ -0,0 +1,29 @@
1
+ function canonicalize(v) {
2
+ if (v === undefined)
3
+ return 'null';
4
+ if (v === null || typeof v !== 'object')
5
+ return JSON.stringify(v);
6
+ if (Array.isArray(v))
7
+ return `[${v.map((x) => canonicalize(x)).join(',')}]`;
8
+ const keys = Object.keys(v)
9
+ .filter((k) => v[k] !== undefined)
10
+ .sort();
11
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalize(v[k])}`).join(',')}}`;
12
+ }
13
+ /** The canonical JSON the hash is taken over — exported so tests and other minters can pin it. */
14
+ export function canonicalOverlayJson(input) {
15
+ const doc = {
16
+ overlays: input.overlays,
17
+ cssVariables: input.cssVariables,
18
+ keyframes: input.keyframes,
19
+ };
20
+ return canonicalize(doc);
21
+ }
22
+ function toHex(bytes) {
23
+ return Array.from(new Uint8Array(bytes), (b) => b.toString(16).padStart(2, '0')).join('');
24
+ }
25
+ /** `sha256(canonicalOverlayJson(input))` as lowercase hex — the value `AppTheme.overlayHash` carries. */
26
+ export async function canonicalOverlayHash(input) {
27
+ const bytes = new TextEncoder().encode(canonicalOverlayJson(input));
28
+ return toHex(await globalThis.crypto.subtle.digest('SHA-256', bytes));
29
+ }
@@ -7,8 +7,8 @@
7
7
  * ## The normative total order
8
8
  *
9
9
  * ```
10
- * themeMode: consolePick > staticConfig > sessionSidecar > hostAnnounced
11
- * themeId: consolePick > renderOverride > staticConfig > sidecarName
10
+ * themeMode: hostAnnounced > consolePick > staticConfig > sessionSidecar
11
+ * themeId: consolePick > renderOverride > staticConfig
12
12
  * ```
13
13
  *
14
14
  * - `consolePick` — the live operator pick (console theme provider).
@@ -17,8 +17,12 @@
17
17
  * - `sessionSidecar` — the per-app `App.theme` sidecar snapshotted at
18
18
  * render-commit: its `mode` (for `themeMode`) / its `name` (for
19
19
  * `themeId`, the registered-theme base-ladder binding).
20
- * - `hostAnnounced` — the embedding host's `hostContext.theme`
21
- * (mode only; hosts announce no theme id).
20
+ * - `hostAnnounced` — the embedding host's `hostContext.theme` (mode
21
+ * only; hosts announce no theme id). HIGHEST rank since ggui#987 (D4):
22
+ * the embedding host owns runtime mode; every other layer is a default
23
+ * the host's announce overrides. A one-palette sidecar can no longer
24
+ * exist (`appThemeSchema` requires both projections), so following the
25
+ * host never paints the wrong palette.
22
26
  *
23
27
  * An unresolved fact is `undefined` — NEVER defaulted to `'light'`.
24
28
  * Absence is load-bearing: it is the signal that lets the next layer
@@ -72,7 +76,7 @@ export interface ThemeModeEffectiveSources {
72
76
  readonly stamped?: ThemeModeOpinion | undefined;
73
77
  /** The slice theme OBJECT's own `mode` (the sidecar, read directly). */
74
78
  readonly sessionSidecar?: ThemeModeOpinion | undefined;
75
- /** The embedding host's `hostContext.theme` announce — final fallback. */
79
+ /** The embedding host's `hostContext.theme` announce — HIGHEST rank (ggui#987). */
76
80
  readonly hostAnnounced?: ThemeModeOpinion | undefined;
77
81
  }
78
82
  /** Server projection of the `themeMode` total order → the stamped field. */
@@ -88,16 +92,14 @@ export interface ThemeIdStampSources {
88
92
  /** Static `ggui.json#theme` deps. */
89
93
  readonly staticConfig?: string | undefined;
90
94
  }
91
- /** Client-visible `themeId` layers, highest rank first. */
95
+ /**
96
+ * Client-visible `themeId` layers. Since ggui#987 (D2 = B, the registration
97
+ * tier is gone) the sidecar's `name` is a label and never a binding: the
98
+ * only client-visible layer is the stamp.
99
+ */
92
100
  export interface ThemeIdEffectiveSources {
93
101
  /** The slice's stamped top-level `themeId`. */
94
102
  readonly stamped?: string | undefined;
95
- /**
96
- * The slice theme OBJECT's `name` — the registered-theme base-ladder
97
- * binding (ggui#589 ask 3): an unregistered name is harmless by
98
- * construction (renderer falls back to the default ladder).
99
- */
100
- readonly sidecarName?: string | undefined;
101
103
  }
102
104
  /** Server projection of the `themeId` total order → the stamped field. */
103
105
  export declare function stampThemeId(s: ThemeIdStampSources): string | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"theme-binding.d.ts","sourceRoot":"","sources":["../../src/integrations/theme-binding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AAEH,4EAA4E;AAC5E,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,MAAM,CAAC;AAEhD,6DAA6D;AAC7D,MAAM,WAAW,qBAAqB;IACpC,kEAAkE;IAClE,QAAQ,CAAC,WAAW,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACpD,qCAAqC;IACrC,QAAQ,CAAC,YAAY,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACrD,mEAAmE;IACnE,QAAQ,CAAC,cAAc,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;CACxD;AAED,6DAA6D;AAC7D,MAAM,WAAW,yBAAyB;IACxC,kFAAkF;IAClF,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAChD,wEAAwE;IACxE,QAAQ,CAAC,cAAc,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACvD,0EAA0E;IAC1E,QAAQ,CAAC,aAAa,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;CACvD;AAED,4EAA4E;AAC5E,wBAAgB,cAAc,CAC5B,CAAC,EAAE,qBAAqB,GACvB,gBAAgB,GAAG,SAAS,CAE9B;AAED,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAChC,CAAC,EAAE,yBAAyB,GAC3B,gBAAgB,GAAG,SAAS,CAE9B;AAED,2DAA2D;AAC3D,MAAM,WAAW,mBAAmB;IAClC,kEAAkE;IAClE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,kEAAkE;IAClE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7C,qCAAqC;IACrC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5C;AAED,2DAA2D;AAC3D,MAAM,WAAW,uBAAuB;IACtC,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3C;AAED,0EAA0E;AAC1E,wBAAgB,YAAY,CAAC,CAAC,EAAE,mBAAmB,GAAG,MAAM,GAAG,SAAS,CAEvE;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,uBAAuB,GAAG,MAAM,GAAG,SAAS,CAE/E"}
1
+ {"version":3,"file":"theme-binding.d.ts","sourceRoot":"","sources":["../../src/integrations/theme-binding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AAEH,4EAA4E;AAC5E,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,MAAM,CAAC;AAEhD,6DAA6D;AAC7D,MAAM,WAAW,qBAAqB;IACpC,kEAAkE;IAClE,QAAQ,CAAC,WAAW,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACpD,qCAAqC;IACrC,QAAQ,CAAC,YAAY,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACrD,mEAAmE;IACnE,QAAQ,CAAC,cAAc,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;CACxD;AAED,6DAA6D;AAC7D,MAAM,WAAW,yBAAyB;IACxC,kFAAkF;IAClF,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAChD,wEAAwE;IACxE,QAAQ,CAAC,cAAc,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACvD,mFAAmF;IACnF,QAAQ,CAAC,aAAa,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;CACvD;AAED,4EAA4E;AAC5E,wBAAgB,cAAc,CAC5B,CAAC,EAAE,qBAAqB,GACvB,gBAAgB,GAAG,SAAS,CAE9B;AAED,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAChC,CAAC,EAAE,yBAAyB,GAC3B,gBAAgB,GAAG,SAAS,CAE9B;AAED,2DAA2D;AAC3D,MAAM,WAAW,mBAAmB;IAClC,kEAAkE;IAClE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,kEAAkE;IAClE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7C,qCAAqC;IACrC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5C;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAED,0EAA0E;AAC1E,wBAAgB,YAAY,CAAC,CAAC,EAAE,mBAAmB,GAAG,MAAM,GAAG,SAAS,CAEvE;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,uBAAuB,GAAG,MAAM,GAAG,SAAS,CAE/E"}
@@ -7,8 +7,8 @@
7
7
  * ## The normative total order
8
8
  *
9
9
  * ```
10
- * themeMode: consolePick > staticConfig > sessionSidecar > hostAnnounced
11
- * themeId: consolePick > renderOverride > staticConfig > sidecarName
10
+ * themeMode: hostAnnounced > consolePick > staticConfig > sessionSidecar
11
+ * themeId: consolePick > renderOverride > staticConfig
12
12
  * ```
13
13
  *
14
14
  * - `consolePick` — the live operator pick (console theme provider).
@@ -17,8 +17,12 @@
17
17
  * - `sessionSidecar` — the per-app `App.theme` sidecar snapshotted at
18
18
  * render-commit: its `mode` (for `themeMode`) / its `name` (for
19
19
  * `themeId`, the registered-theme base-ladder binding).
20
- * - `hostAnnounced` — the embedding host's `hostContext.theme`
21
- * (mode only; hosts announce no theme id).
20
+ * - `hostAnnounced` — the embedding host's `hostContext.theme` (mode
21
+ * only; hosts announce no theme id). HIGHEST rank since ggui#987 (D4):
22
+ * the embedding host owns runtime mode; every other layer is a default
23
+ * the host's announce overrides. A one-palette sidecar can no longer
24
+ * exist (`appThemeSchema` requires both projections), so following the
25
+ * host never paints the wrong palette.
22
26
  *
23
27
  * An unresolved fact is `undefined` — NEVER defaulted to `'light'`.
24
28
  * Absence is load-bearing: it is the signal that lets the next layer
@@ -61,7 +65,7 @@ export function stampThemeMode(s) {
61
65
  }
62
66
  /** Client projection of the `themeMode` total order → the effective mode. */
63
67
  export function effectiveThemeMode(s) {
64
- return s.stamped ?? s.sessionSidecar ?? s.hostAnnounced;
68
+ return s.hostAnnounced ?? s.stamped ?? s.sessionSidecar;
65
69
  }
66
70
  /** Server projection of the `themeId` total order → the stamped field. */
67
71
  export function stampThemeId(s) {
@@ -69,5 +73,5 @@ export function stampThemeId(s) {
69
73
  }
70
74
  /** Client projection of the `themeId` total order → the effective id. */
71
75
  export function effectiveThemeId(s) {
72
- return s.stamped ?? s.sidecarName;
76
+ return s.stamped;
73
77
  }
@@ -1 +1 @@
1
- {"version":3,"file":"blueprint-key.d.ts","sourceRoot":"","sources":["../../src/registry/blueprint-key.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAG9D;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,YAAY,GAAG,SAAS,GAAG,MAAM,CAGvE;AAMD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,iCAAiC,EACjC,+BAA+B,EAC/B,KAAK,uBAAuB,EAC5B,KAAK,6BAA6B,GACnC,MAAM,yBAAyB,CAAC;AAIjC,cAAc,sBAAsB,CAAC"}
1
+ {"version":3,"file":"blueprint-key.d.ts","sourceRoot":"","sources":["../../src/registry/blueprint-key.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAG9D;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,YAAY,GAAG,SAAS,GAAG,MAAM,CAGvE;AAMD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,iCAAiC,EACjC,+BAA+B,EAC/B,KAAK,uBAAuB,EAC5B,KAAK,6BAA6B,GACnC,MAAM,yBAAyB,CAAC;AAIjC,cAAc,sBAAsB,CAAC"}
@@ -10,9 +10,10 @@
10
10
  * 16-character sha256 prefix — matches the existing `blueprintHash`
11
11
  * shape in `cache-trace-sink` and `generation-cache.ts`. Collision
12
12
  * probability for 100s-of-thousands of distinct contract is ~10^-6
13
- * (birthday-bound on 2^64), well below the budget for the OSS
14
- * single-tenant scope. Hosted multi-tenant deployments scope keys
15
- * by appId so the bound is per-tenant, never global.
13
+ * (birthday-bound on 2^64), well below the budget for a single
14
+ * deployment's key space. A deployment that serves many apps scopes
15
+ * keys by `appId`, so the bound holds per app, never across the whole
16
+ * store.
16
17
  */
17
18
  import { createHash } from 'node:crypto';
18
19
  import { canonicalizeContracts } from './canonicalize-contract.js';
@@ -26,23 +26,59 @@ export declare const GGUI_CSS_VAR_KEY_RE: RegExp;
26
26
  * bare regex on its own.
27
27
  */
28
28
  export declare const CSS_VALUE_SAFE_RE: RegExp;
29
+ /**
30
+ * The grammar a per-mode `keyframes` string MUST satisfy (ggui#987 §3.4,
31
+ * follower D). The renderer injects the text verbatim into the card's
32
+ * `<style>` after the variables, so the text is bounded to what that slot
33
+ * is FOR: zero or more `@keyframes <ident> { <frame-selectors> { <declarations> } … }`
34
+ * blocks and nothing else — no other at-rule (`@import`, `@font-face`,
35
+ * `@media`, …), no `<` / `>`, braces balanced and nested exactly one level
36
+ * inside a block. Comments (`/* … *\/`) are stripped before the walk and are
37
+ * therefore allowed, as `cssVariableMap` values allow `url(`; the deny-list
38
+ * on values (`;{}<>@`) cannot apply verbatim here because `;`, `{`, `}` and
39
+ * the one `@` ARE the grammar. Same principal, same slot, one rule.
40
+ */
41
+ export declare const KEYFRAMES_NAME_RE: RegExp;
42
+ export declare function isKeyframesText(text: string): boolean;
29
43
  export declare const appThemeSchema: z.ZodObject<{
30
- mode: z.ZodEnum<{
44
+ mode: z.ZodOptional<z.ZodEnum<{
31
45
  light: "light";
32
46
  dark: "dark";
33
- }>;
34
- cssVariables: z.ZodRecord<z.ZodString, z.ZodString>;
47
+ }>>;
35
48
  name: z.ZodOptional<z.ZodString>;
36
- base: z.ZodOptional<z.ZodObject<{
37
- documentHash: z.ZodString;
49
+ overlayHash: z.ZodString;
50
+ overlays: z.ZodObject<{
38
51
  light: z.ZodRecord<z.ZodString, z.ZodString>;
39
52
  dark: z.ZodRecord<z.ZodString, z.ZodString>;
40
- keyframes: z.ZodOptional<z.ZodObject<{
41
- light: z.ZodOptional<z.ZodString>;
42
- dark: z.ZodOptional<z.ZodString>;
43
- }, z.core.$strict>>;
44
- frameless: z.ZodOptional<z.ZodBoolean>;
53
+ }, z.core.$strict>;
54
+ cssVariables: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
55
+ keyframes: z.ZodOptional<z.ZodObject<{
56
+ light: z.ZodOptional<z.ZodString>;
57
+ dark: z.ZodOptional<z.ZodString>;
45
58
  }, z.core.$strict>>;
59
+ frameless: z.ZodOptional<z.ZodBoolean>;
46
60
  }, z.core.$strict>;
47
61
  export type AppTheme = z.infer<typeof appThemeSchema>;
62
+ /**
63
+ * The ONE refusal body every write door returns for an overlay it will not
64
+ * store (ggui#987 §3.4): REST 422 `invalid_app_config`, the AppSync
65
+ * `errorInfo`, the MCP ops door's `{ ok: false }` structured content. A
66
+ * discriminated union by key — exactly one of the four.
67
+ */
68
+ export declare const appThemeRefusalBodySchema: z.ZodUnion<readonly [z.ZodObject<{
69
+ uncovered: z.ZodObject<{
70
+ light: z.ZodArray<z.ZodString>;
71
+ dark: z.ZodArray<z.ZodString>;
72
+ }, z.core.$strict>;
73
+ }, z.core.$strict>, z.ZodObject<{
74
+ unknown: z.ZodObject<{
75
+ light: z.ZodArray<z.ZodString>;
76
+ dark: z.ZodArray<z.ZodString>;
77
+ }, z.core.$strict>;
78
+ }, z.core.$strict>, z.ZodObject<{
79
+ overlayHash: z.ZodLiteral<"mismatch">;
80
+ }, z.core.$strict>, z.ZodObject<{
81
+ refused: z.ZodLiteral<"v1 shape">;
82
+ }, z.core.$strict>]>;
83
+ export type AppThemeRefusalBody = z.infer<typeof appThemeRefusalBodySchema>;
48
84
  //# sourceMappingURL=app-theme.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-theme.d.ts","sourceRoot":"","sources":["../../src/schemas/app-theme.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,QAA2B,CAAC;AAE5D;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,QAAiB,CAAC;AAahD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;kBAkDhB,CAAC;AAEZ,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC"}
1
+ {"version":3,"file":"app-theme.d.ts","sourceRoot":"","sources":["../../src/schemas/app-theme.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,QAA2B,CAAC;AAE5D;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,QAAiB,CAAC;AAahD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB,QAA+B,CAAC;AAE9D,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAoCrD;AAQD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;kBAuChB,CAAC;AAEZ,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;oBAKpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
@@ -35,54 +35,122 @@ const cssValue = z
35
35
  const cssVariableMap = z
36
36
  .record(z.string().regex(GGUI_CSS_VAR_KEY_RE, 'css var key must be --ggui-*'), cssValue)
37
37
  .refine((m) => Object.keys(m).length <= 200, 'too many css variables (max 200)');
38
+ /**
39
+ * The grammar a per-mode `keyframes` string MUST satisfy (ggui#987 §3.4,
40
+ * follower D). The renderer injects the text verbatim into the card's
41
+ * `<style>` after the variables, so the text is bounded to what that slot
42
+ * is FOR: zero or more `@keyframes <ident> { <frame-selectors> { <declarations> } … }`
43
+ * blocks and nothing else — no other at-rule (`@import`, `@font-face`,
44
+ * `@media`, …), no `<` / `>`, braces balanced and nested exactly one level
45
+ * inside a block. Comments (`/* … *\/`) are stripped before the walk and are
46
+ * therefore allowed, as `cssVariableMap` values allow `url(`; the deny-list
47
+ * on values (`;{}<>@`) cannot apply verbatim here because `;`, `{`, `}` and
48
+ * the one `@` ARE the grammar. Same principal, same slot, one rule.
49
+ */
50
+ export const KEYFRAMES_NAME_RE = /^[A-Za-z_-][A-Za-z0-9_-]*$/;
51
+ export function isKeyframesText(text) {
52
+ const s = text.replace(/\/\*[\s\S]*?\*\//g, ' ');
53
+ if (/[<>]/.test(s))
54
+ return false;
55
+ let i = 0;
56
+ const n = s.length;
57
+ const skipWs = () => {
58
+ while (i < n && /\s/.test(s[i]))
59
+ i += 1;
60
+ };
61
+ for (;;) {
62
+ skipWs();
63
+ if (i >= n)
64
+ return true;
65
+ if (!s.startsWith('@keyframes', i))
66
+ return false;
67
+ i += '@keyframes'.length;
68
+ if (i >= n || !/\s/.test(s[i]))
69
+ return false;
70
+ skipWs();
71
+ const nameStart = i;
72
+ while (i < n && /[A-Za-z0-9_-]/.test(s[i]))
73
+ i += 1;
74
+ if (!KEYFRAMES_NAME_RE.test(s.slice(nameStart, i)))
75
+ return false;
76
+ skipWs();
77
+ if (s[i] !== '{')
78
+ return false;
79
+ i += 1;
80
+ // Inside the block: frame rules only — `selectors { declarations }` at depth 1, no `@`.
81
+ let depth = 1;
82
+ while (i < n && depth > 0) {
83
+ const ch = s[i];
84
+ if (ch === '@')
85
+ return false;
86
+ if (ch === '{') {
87
+ depth += 1;
88
+ if (depth > 2)
89
+ return false;
90
+ }
91
+ else if (ch === '}') {
92
+ depth -= 1;
93
+ }
94
+ i += 1;
95
+ }
96
+ if (depth !== 0)
97
+ return false;
98
+ }
99
+ }
100
+ /** A per-mode keyframes string: `@keyframes` blocks only (see {@link isKeyframesText}), 8 KiB cap unchanged. */
101
+ const keyframesText = z
102
+ .string()
103
+ .max(8192)
104
+ .refine(isKeyframesText, 'keyframes must be `@keyframes <name> { … }` blocks and nothing else');
38
105
  export const appThemeSchema = z
39
106
  .object({
40
- mode: z.enum(['light', 'dark']),
41
- cssVariables: cssVariableMap,
107
+ /**
108
+ * Default appearance: the mode painted when NO embedding host announces
109
+ * one. Never a pin — the host owns runtime mode (ggui#987 D4; see
110
+ * `integrations/theme-binding.ts`). Absent ⇒ mode-neutral.
111
+ */
112
+ mode: z.enum(['light', 'dark']).optional(),
113
+ /** A label the author's tooling reads back. Never resolved by a renderer. */
42
114
  name: z.string().min(1).max(64).optional(),
43
115
  /**
44
- * The REGISTERED base ladder, delivered (runtime theme
45
- * registration): both modes' resolved variable sets ride the
46
- * envelope so a mid-session mode switch is a local operation and
47
- * delivery never depends on the iframe being able to fetch. The
48
- * renderer injects the mode-selected set BELOW `cssVariables` (the
49
- * per-app overlay) in the documented precedence. `documentHash` is
50
- * the registration's identity: it joins painted ladders to
51
- * registration records, and a receiver holding the hash may be
52
- * served a future envelope without the variable sets.
116
+ * Delivery attestation: `canonicalOverlayHash({ overlays, cssVariables,
117
+ * keyframes })` (`integrations/overlay-hash.ts`). Every write door
118
+ * recomputes it and refuses a mismatch; read doors pass it through.
53
119
  */
54
- base: z
120
+ overlayHash: z.string().regex(/^[0-9a-f]{64}$/, 'overlayHash must be sha256 lowercase hex'),
121
+ /**
122
+ * The projection, both modes — the derived `--ggui-*` sets a card
123
+ * injects for the effective mode (ggui#987 §2.4: produced by ONE
124
+ * function on every path). REQUIRED, both: an overlay that could not
125
+ * follow the host's mode is not accepted.
126
+ */
127
+ overlays: z.object({ light: cssVariableMap, dark: cssVariableMap }).strict(),
128
+ /** Mode-agnostic per-app overrides, injected above both projections. */
129
+ cssVariables: cssVariableMap.optional(),
130
+ /** Per-mode `@keyframes` blocks, injected verbatim after the variables — bounded to that grammar by {@link isKeyframesText}. */
131
+ keyframes: z
55
132
  .object({
56
- documentHash: z
57
- .string()
58
- .regex(/^[0-9a-f]{64}$/, 'documentHash must be sha256 lowercase hex'),
59
- light: cssVariableMap,
60
- dark: cssVariableMap,
61
- /**
62
- * Per-mode `@keyframes` blocks from the registration document
63
- * (ggui#613 residual 2). A mode key is absent when its
64
- * document declares no motion keyframes; the whole field is
65
- * absent on pre-#613 registrations (additive optional — old
66
- * bases stay valid). Injected verbatim after the variable
67
- * ladder, same position the compiled path gives
68
- * `theme.cssKeyframes`.
69
- */
70
- keyframes: z
71
- .object({
72
- light: z.string().max(8192).optional(),
73
- dark: z.string().max(8192).optional(),
74
- })
75
- .strict()
76
- .optional(),
77
- /**
78
- * `$metadata.frameless` delivered (OR of both modes): the
79
- * embedding host owns the card silhouette, so the renderer
80
- * appends the root-children border-suppression rule — the
81
- * SAME rule the compiled path emits.
82
- */
83
- frameless: z.boolean().optional(),
133
+ light: keyframesText.optional(),
134
+ dark: keyframesText.optional(),
84
135
  })
85
136
  .strict()
86
137
  .optional(),
138
+ /**
139
+ * The embedding host owns the card silhouette: the renderer appends the
140
+ * root-children border-suppression rule.
141
+ */
142
+ frameless: z.boolean().optional(),
87
143
  })
88
144
  .strict();
145
+ /**
146
+ * The ONE refusal body every write door returns for an overlay it will not
147
+ * store (ggui#987 §3.4): REST 422 `invalid_app_config`, the AppSync
148
+ * `errorInfo`, the MCP ops door's `{ ok: false }` structured content. A
149
+ * discriminated union by key — exactly one of the four.
150
+ */
151
+ export const appThemeRefusalBodySchema = z.union([
152
+ z.object({ uncovered: z.object({ light: z.array(z.string()), dark: z.array(z.string()) }).strict() }).strict(),
153
+ z.object({ unknown: z.object({ light: z.array(z.string()), dark: z.array(z.string()) }).strict() }).strict(),
154
+ z.object({ overlayHash: z.literal('mismatch') }).strict(),
155
+ z.object({ refused: z.literal('v1 shape') }).strict(),
156
+ ]);
@@ -5,13 +5,14 @@
5
5
  * here is `z.ZodType<Blueprint>` so any drift fails compile.
6
6
  */
7
7
  import { z } from 'zod';
8
- import type { AppBlueprintSearchConfig, Blueprint, BlueprintVariance } from '../types/blueprint';
9
- import type { BlueprintSource, CuratedBlueprintSource, LlmBlueprintSource, UserBlueprintSource } from '../types/blueprint-source';
8
+ import type { AppBlueprintSearchConfig, Blueprint, BlueprintVariance } from '../types/blueprint.js';
9
+ import type { BlueprintSource, CuratedBlueprintSource, LlmBlueprintSource, UserBlueprintSource } from '../types/blueprint-source.js';
10
10
  /**
11
11
  * Zod mirror of {@link LlmBlueprintSource} — the engine-generated arm.
12
12
  * Both provenance fields are REQUIRED: every generation mint site has
13
13
  * them in scope, and an engine-generated artifact that cannot name its
14
- * engine + model is not a real state.
14
+ * engine + model is not a real state. Since ggui#924 the identity is
15
+ * de-modeled (`ui-gen-<tier>`) and the model is the run's `ModelRef`.
15
16
  */
16
17
  export declare const llmBlueprintSourceSchema: z.ZodType<LlmBlueprintSource>;
17
18
  /** Zod mirror of {@link UserBlueprintSource} — no engine claim exists. */
@@ -1 +1 @@
1
- {"version":3,"file":"blueprint.d.ts","sourceRoot":"","sources":["../../src/schemas/blueprint.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EACV,wBAAwB,EACxB,SAAS,EACT,iBAAiB,EAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EACV,eAAe,EACf,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,2BAA2B,CAAC;AAGnC;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,EAAE,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAMvB,CAAC;AAE7C,0EAA0E;AAC1E,eAAO,MAAM,yBAAyB,EAAE,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAExB,CAAC;AAE9C,oDAAoD;AACpD,eAAO,MAAM,4BAA4B,EAAE,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAE3B,CAAC;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CAI7B,CAAC;AAEjC;;;;;;;;;GASG;AACH,sFAAsF;AACtF,eAAO,MAAM,uBAAuB,4DAA6D,CAAC;AAElG,eAAO,MAAM,uBAAuB,EAAE,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAgCtB,CAAC;AAE5C;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAwBd,CAAC;AAmBpC;;;GAGG;AACH,eAAO,MAAM,8BAA8B,EAAE,CAAC,CAAC,OAAO,CAAC,wBAAwB,CAM7B,CAAC"}
1
+ {"version":3,"file":"blueprint.d.ts","sourceRoot":"","sources":["../../src/schemas/blueprint.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EACV,wBAAwB,EACxB,SAAS,EACT,iBAAiB,EAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EACV,eAAe,EACf,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,2BAA2B,CAAC;AAKnC;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,EAAE,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAexD,CAAC;AAEZ,0EAA0E;AAC1E,eAAO,MAAM,yBAAyB,EAAE,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAExB,CAAC;AAE9C,oDAAoD;AACpD,eAAO,MAAM,4BAA4B,EAAE,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAE3B,CAAC;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CAI7B,CAAC;AAEjC;;;;;;;;;GASG;AACH,sFAAsF;AACtF,eAAO,MAAM,uBAAuB,4DAA6D,CAAC;AAElG,eAAO,MAAM,uBAAuB,EAAE,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAgCtB,CAAC;AAE5C;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAwBd,CAAC;AAmBpC;;;GAGG;AACH,eAAO,MAAM,8BAA8B,EAAE,CAAC,CAAC,OAAO,CAAC,wBAAwB,CAM7B,CAAC"}
@@ -5,18 +5,30 @@
5
5
  * here is `z.ZodType<Blueprint>` so any drift fails compile.
6
6
  */
7
7
  import { z } from 'zod';
8
+ import { isGeneratorId } from '../types/blueprint-source.js';
9
+ import { MODEL_REF_PREFIXES, isModelRef } from '../types/llm-route.js';
8
10
  import { dataContractSchema, jsonValueSchema } from './data-contract.js';
9
11
  /**
10
12
  * Zod mirror of {@link LlmBlueprintSource} — the engine-generated arm.
11
13
  * Both provenance fields are REQUIRED: every generation mint site has
12
14
  * them in scope, and an engine-generated artifact that cannot name its
13
- * engine + model is not a real state.
15
+ * engine + model is not a real state. Since ggui#924 the identity is
16
+ * de-modeled (`ui-gen-<tier>`) and the model is the run's `ModelRef`.
14
17
  */
15
18
  export const llmBlueprintSourceSchema = z
16
19
  .object({
17
20
  kind: z.literal('llm'),
18
- generator: z.string().min(1),
19
- model: z.string().min(1),
21
+ // ggui#924: the de-modeled identity and the run's `ModelRef` — a
22
+ // modeled id, a bare model name or a second spelling is refused here,
23
+ // naming the field. Template literals (not `z.custom`) so the fields
24
+ // keep their literal types AND render to JSON Schema as `pattern` —
25
+ // this schema is embedded in tool inputs that `tools/list` renders.
26
+ generator: z.templateLiteral(['ui-gen-', z.string()]).refine(isGeneratorId, {
27
+ error: 'generator is the de-modeled identity `ui-gen-<tier>` — one tier token, no model segment (e.g. `ui-gen-default`)',
28
+ }),
29
+ model: z.templateLiteral([z.enum(MODEL_REF_PREFIXES), '/', z.string()]).refine(isModelRef, {
30
+ error: 'model is the run route in the registry spelling `<prefix>/<model>` (e.g. `anthropic/claude-haiku-4-5`)',
31
+ }),
20
32
  })
21
33
  .strict();
22
34
  /** Zod mirror of {@link UserBlueprintSource} — no engine claim exists. */
@@ -7,7 +7,7 @@
7
7
  * Output-side seams that type-narrow contract on the wire —
8
8
  * `renderOutputSchema.contract` and the various `decision` echoes — use
9
9
  * `z.custom<DataContract>()` because they trust the shape (it
10
- * originates from internal pod state).
10
+ * originates from internal server state).
11
11
  *
12
12
  * The input seam is different: agents author contract on
13
13
  * `story.contract` and the handler MUST plumb them to the generator.
@@ -51,7 +51,7 @@
51
51
  * just to accept the contract and pass it to the generator.
52
52
  */
53
53
  import { z } from 'zod';
54
- import type { DataContract, JsonValue, JsonSchema, JsonObject } from '../types/data-contract';
54
+ import type { DataContract, JsonValue, JsonSchema, JsonObject } from '../types/data-contract.js';
55
55
  /**
56
56
  * Recursive {@link JsonValue} — string | number | boolean | null |
57
57
  * array | object. All fields on contract entries that carry default
@@ -211,26 +211,6 @@ export declare const agentCapabilitiesSpecSchema: z.ZodObject<{
211
211
  }, z.core.$strict>>;
212
212
  }, z.core.$strict>>;
213
213
  }, z.core.$loose>;
214
- /**
215
- * `App.publicEnv` key regex.
216
- *
217
- * Each key in `App.publicEnv` MUST match this pattern. The prefix is
218
- * the **security boundary** — operators can't accidentally stash
219
- * sensitive credentials under arbitrary names, and downstream consumers
220
- * (render gate, bootstrap projection, iframe shim) can rely on the
221
- * naming convention to mean "public-by-design".
222
- *
223
- * Rule: `GGUI_PUBLIC_APP_` prefix, then uppercase letters / digits /
224
- * underscores, at least one char after the prefix.
225
- *
226
- * `GGUI_PUBLIC_USER_*` keys are RESERVED for a future per-user
227
- * channel. The current regex rejects them so App-side config can't
228
- * pre-emptively use the namespace.
229
- *
230
- * Hoisted above `gadgetDescriptorSchema` so the wrapper's `requires`
231
- * array can reference it at schema-construction time (TDZ-safe).
232
- */
233
- export declare const PUBLIC_ENV_APP_KEY_RE: RegExp;
234
214
  /**
235
215
  * Single source of truth for the `requires[]` field shape on gadget
236
216
  * descriptors. The wire-permissive `gadgetDescriptorSchema`, the
@@ -244,7 +224,7 @@ export declare const PUBLIC_ENV_APP_KEY_RE: RegExp;
244
224
  * that declare a `requires` key must have a corresponding App-side
245
225
  * publicEnv value at render time (gate: `assertPublicEnvSatisfied`).
246
226
  */
247
- export declare const gadgetRequiresSchema: z.ZodReadonly<z.ZodArray<z.ZodString>>;
227
+ export declare const gadgetRequiresSchema: z.ZodArray<z.ZodString>;
248
228
  /**
249
229
  * SRI hash format for gadget bundles. Registry install writes
250
230
  * `bundleSri` in this shape; iframe-runtime emits it verbatim into
@@ -367,7 +347,7 @@ export declare function bundleHostScheme(host: string): 'http' | 'https';
367
347
  * needs the grammars and which this module imports
368
348
  * `KNOWN_PERMISSION_NAMES` from).
369
349
  */
370
- export { HOOK_NAME_RE, COMPONENT_NAME_RE } from './gadget-name-grammar';
350
+ export { HOOK_NAME_RE, COMPONENT_NAME_RE } from './gadget-name-grammar.js';
371
351
  /**
372
352
  * Wire-permissive {@link GadgetExport} schema — a union of a
373
353
  * hook-export shape (carries `hook`) and a component-export shape
@@ -475,8 +455,8 @@ export declare const gadgetDescriptorSchema: z.ZodObject<{
475
455
  bundleHost: z.ZodOptional<z.ZodString>;
476
456
  bundleSri: z.ZodOptional<z.ZodString>;
477
457
  styleUrl: z.ZodOptional<z.ZodURL>;
478
- connect: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodURL>>>;
479
- requires: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
458
+ connect: z.ZodOptional<z.ZodArray<z.ZodURL>>;
459
+ requires: z.ZodOptional<z.ZodArray<z.ZodString>>;
480
460
  typesUrl: z.ZodOptional<z.ZodURL>;
481
461
  typesSri: z.ZodOptional<z.ZodString>;
482
462
  }, z.core.$strict>;
@@ -550,8 +530,8 @@ export declare const strictGadgetDescriptorSchema: z.ZodObject<{
550
530
  bundleHost: z.ZodOptional<z.ZodString>;
551
531
  bundleSri: z.ZodOptional<z.ZodString>;
552
532
  styleUrl: z.ZodOptional<z.ZodURL>;
553
- connect: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodURL>>>;
554
- requires: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
533
+ connect: z.ZodOptional<z.ZodArray<z.ZodURL>>;
534
+ requires: z.ZodOptional<z.ZodArray<z.ZodString>>;
555
535
  typesUrl: z.ZodOptional<z.ZodURL>;
556
536
  typesSri: z.ZodOptional<z.ZodString>;
557
537
  }, z.core.$strict>;
@@ -640,8 +620,8 @@ export declare const registeredGadgetDescriptorSchema: z.ZodObject<{
640
620
  bundleHost: z.ZodOptional<z.ZodString>;
641
621
  bundleSri: z.ZodOptional<z.ZodString>;
642
622
  styleUrl: z.ZodOptional<z.ZodURL>;
643
- connect: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodURL>>>;
644
- requires: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
623
+ connect: z.ZodOptional<z.ZodArray<z.ZodURL>>;
624
+ requires: z.ZodOptional<z.ZodArray<z.ZodString>>;
645
625
  typesUrl: z.ZodOptional<z.ZodURL>;
646
626
  typesSri: z.ZodOptional<z.ZodString>;
647
627
  }, z.core.$strict>;
@@ -718,7 +698,7 @@ export declare const clientCapabilitiesSpecSchema: z.ZodObject<{
718
698
  * - Defensive re-validation in the iframe-runtime slice-meta
719
699
  * extractors (`parseMetaFromGlobal`, `parseMetaFromToolResult`).
720
700
  */
721
- export declare const appPublicEnvSchema: z.ZodReadonly<z.ZodRecord<z.ZodString, z.ZodString>>;
701
+ export declare const appPublicEnvSchema: z.ZodRecord<z.ZodString, z.ZodString>;
722
702
  /**
723
703
  * {@link DataContract} — the unified four-spec contract surface
724
704
  * agents author on `story.contract` and that the generator