@ggui-ai/ui-gen 0.23.0 → 0.24.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 (67) hide show
  1. package/dist/adapter-Du7P_5Fx.d.ts +201 -0
  2. package/dist/adapters/generation-dispatch.d.ts +33 -12
  3. package/dist/adapters/generation-dispatch.js +510 -128
  4. package/dist/adapters/generation-dispatch.js.map +1 -1
  5. package/dist/adapters/index.js.map +1 -1
  6. package/dist/advanced/index.d.ts +5 -3
  7. package/dist/advanced/index.js +551 -142
  8. package/dist/advanced/index.js.map +1 -1
  9. package/dist/blueprint-validator.js +153 -59
  10. package/dist/blueprint-validator.js.map +1 -1
  11. package/dist/boilerplate.js.map +1 -1
  12. package/dist/check/index.d.ts +2 -1
  13. package/dist/check/index.js +47 -1
  14. package/dist/check/index.js.map +1 -1
  15. package/dist/{create-ui-generator-D0kbWwln.d.ts → create-ui-generator-GIhzx08x.d.ts} +34 -10
  16. package/dist/{dispatch-wxaIMwZt.d.ts → dispatch-BnAH56p4.d.ts} +1 -1
  17. package/dist/evaluation/axis-checks/index.d.ts +4 -3
  18. package/dist/evaluation/axis-checks/index.js.map +1 -1
  19. package/dist/evaluation/axis-checks/registry.d.ts +2 -1
  20. package/dist/evaluation/index.d.ts +4 -3
  21. package/dist/evaluation/index.js.map +1 -1
  22. package/dist/harness/check/runtime-render/index.d.ts +29 -10
  23. package/dist/harness/check/runtime-render/index.js +203 -60
  24. package/dist/harness/check/runtime-render/index.js.map +1 -1
  25. package/dist/harness/check/runtime-render/render-check-worker.d.ts +8 -1
  26. package/dist/harness/check/runtime-render/render-check-worker.js +72 -32
  27. package/dist/harness/check/runtime-render/render-check-worker.js.map +1 -1
  28. package/dist/harness/index.d.ts +2 -1
  29. package/dist/harness/index.js.map +1 -1
  30. package/dist/harness/prompts.js.map +1 -1
  31. package/dist/harness/result-types.d.ts +2 -1
  32. package/dist/harness/runtime.d.ts +2 -1
  33. package/dist/harness/runtime.js.map +1 -1
  34. package/dist/harness/types-public.d.ts +11 -2
  35. package/dist/harness/types-public.js.map +1 -1
  36. package/dist/harness/validator-trace-sink.d.ts +2 -1
  37. package/dist/index.d.ts +38 -3
  38. package/dist/index.js +553 -143
  39. package/dist/index.js.map +1 -1
  40. package/dist/patch.d.ts +2 -1
  41. package/dist/policy.d.ts +7 -4
  42. package/dist/policy.js.map +1 -1
  43. package/dist/tools/render-check-worker.d.ts +7 -1
  44. package/dist/tools/render-check-worker.js +55 -23
  45. package/dist/tools/render-check-worker.js.map +1 -1
  46. package/dist/{types-public-Ow-tNtFE.d.ts → types-public-Bk6d1WLN.d.ts} +77 -11
  47. package/dist/workflows.d.ts +2 -1
  48. package/package.json +12 -12
  49. package/src/adapters/generation-dispatch.ts +137 -96
  50. package/src/check/run-tier0.ts +80 -1
  51. package/src/create-ui-generator.ts +185 -95
  52. package/src/evaluation/types-public.ts +73 -11
  53. package/src/harness/check/runtime-render/adapter.ts +221 -75
  54. package/src/harness/check/runtime-render/index.ts +75 -2
  55. package/src/harness/check/runtime-render/render-check-host.ts +82 -19
  56. package/src/harness/check/runtime-render/render-check-worker.ts +83 -29
  57. package/src/harness/check/runtime-render/render-check.ts +154 -117
  58. package/src/harness/coding/generate-task-runner.ts +57 -4
  59. package/src/harness/coding/init-session.ts +13 -0
  60. package/src/harness/coding/run-eval-round.ts +238 -15
  61. package/src/harness/policy.ts +11 -0
  62. package/src/harness/primitive-slice.ts +104 -0
  63. package/src/harness/types-public.ts +9 -1
  64. package/src/index.ts +4 -0
  65. package/src/policy.ts +7 -4
  66. package/src/tools/render-check-worker.ts +74 -23
  67. package/dist/render-check-Blwj8gB6.d.ts +0 -105
package/dist/patch.d.ts CHANGED
@@ -2,7 +2,8 @@ import { PatchFn } from './harness/types-public.js';
2
2
  import '@ggui-ai/protocol';
3
3
  import './styling-profile-CxfvV48g.js';
4
4
  import './axes-CzLEMDeB.js';
5
- import './types-public-Ow-tNtFE.js';
5
+ import './types-public-Bk6d1WLN.js';
6
+ import '@ggui-ai/mcp-server-core';
6
7
  import './design-mode-DLufhrNp.js';
7
8
  import './types-BOvHNG7K.js';
8
9
  import './llm.js';
package/dist/policy.d.ts CHANGED
@@ -47,12 +47,15 @@ interface ContextPolicy {
47
47
  * Axis-keyed primitives doc slice. When `"axis-keyed"`, the first-turn
48
48
  * system prompt injects only the allowlisted primitives derived from
49
49
  * the classification, shrinking the ~130 KB monolith to ~30–50 KB.
50
- * Default `"full"`.
50
+ * `"axis-keyed+contract"` adds the input primitives the contract's own
51
+ * fields imply (ggui#1324: the axis slice alone dropped `Input` /
52
+ * `TextArea` from a card that edits a string). Default `"full"`.
51
53
  */
52
- readonly primitiveDocSlice?: "full" | "axis-keyed";
54
+ readonly primitiveDocSlice?: "full" | "axis-keyed" | "axis-keyed+contract";
53
55
  /**
54
- * Primitives to exclude from the axis-keyed slice. Only meaningful
55
- * when `primitiveDocSlice === "axis-keyed"`.
56
+ * Primitives to exclude from the axis-keyed slice (applied after the
57
+ * contract additions under `"axis-keyed+contract"`). Only meaningful
58
+ * when `primitiveDocSlice` is one of the two slice modes.
56
59
  */
57
60
  readonly primitiveDocExcludes?: readonly string[];
58
61
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/policy.ts"],"names":[],"mappings":";AAuLO,IAAM,sBAAA,GAAwC,OAAO,MAAA,CAAO;AAAA,EACjE,gBAAA,EAAkB,KAAA;AAAA,EAClB,YAAA,EAAc,KAAA;AAAA,EACd,mBAAA,EAAqB,KAAA;AAAA,EACrB,eAAA,EAAiB,QAAA;AAAA,EACjB,iBAAA,EAAmB,MAAA;AAAA,EACnB,QAAA,EAAU,KAAA;AAAA,EACV,cAAA,EAAgB,KAAA;AAAA,EAChB,wBAAA,EAA0B,KAAA;AAAA,EAC1B,sBAAA,EAAwB,KAAA;AAAA;AAAA;AAAA;AAAA,EAIxB,kBAAA,EAAoB,IAAA;AAAA,EACpB,aAAA,EAAe,KAAA;AAAA;AAAA;AAAA;AAAA,EAIf,UAAA,EAAY;AACd,CAAC;AAEM,IAAM,sBAAA,GAAwC,OAAO,MAAA,CAAO;AAAA,EACjE,OAAA,EAAS;AACX,CAAC;AAiBM,SAAS,qBAAqB,eAAA,EAAgD;AACnF,EAAA,OAAO,sBAAA;AACT;AAUO,SAAS,gBAAA,CACd,SACA,WAAA,EACmB;AACnB,EAAA,OAAO,OAAA,CAAQ,MAAA;AACjB;AAcO,SAAS,uBAAuB,MAAA,EAAgC;AACrE,EAAA,OACE,OAAO,OAAA,CAAQ,gBAAA,KAAqB,uBAAuB,gBAAA,IAC3D,MAAA,CAAO,QAAQ,YAAA,KAAiB,sBAAA,CAAuB,YAAA,IACvD,MAAA,CAAO,QAAQ,mBAAA,KAAwB,sBAAA,CAAuB,wBAC7D,MAAA,CAAO,OAAA,CAAQ,mBAAmB,QAAA,OAChC,sBAAA,CAAuB,eAAA,IAAmB,QAAA,CAAA,IAAA,CAC5C,OAAO,OAAA,CAAQ,iBAAA,IAAqB,aAClC,sBAAA,CAAuB,iBAAA,IAAqB,YAC9C,MAAA,CAAO,OAAA,CAAQ,oBAAA,EAAsB,MAAA,IAAU,OAAO,CAAA,IAAA,CACtD,MAAA,CAAO,QAAQ,QAAA,IAAY,KAAA,OAAY,uBAAuB,QAAA,IAAY,KAAA,CAAA,IAAA,CAC1E,MAAA,CAAO,OAAA,CAAQ,kBAAkB,KAAA,OAC/B,sBAAA,CAAuB,kBAAkB,KAAA,CAAA,IAAA,CAC3C,MAAA,CAAO,QAAQ,wBAAA,IAA4B,KAAA,OACzC,sBAAA,CAAuB,wBAAA,IAA4B,WACrD,MAAA,CAAO,OAAA,CAAQ,0BAA0B,KAAA,OACvC,sBAAA,CAAuB,0BAA0B,KAAA,CAAA,IAAA,CACnD,MAAA,CAAO,OAAA,CAAQ,kBAAA,IAAsB,iBACnC,sBAAA,CAAuB,kBAAA,IAAsB,gBAC/C,MAAA,CAAO,OAAA,CAAQ,iBAAiB,KAAA,OAC9B,sBAAA,CAAuB,iBAAiB,KAAA,CAAA,IAAA,CAC1C,MAAA,CAAO,QAAQ,UAAA,IAAc,OAAA,OAC3B,uBAAuB,UAAA,IAAc,OAAA,CAAA,IACxC,OAAO,WAAA,KAAgB,MAAA;AAE3B","file":"policy.js","sourcesContent":["// packages/ui-gen/src/policy.ts\n//\n// Harness policy — validated defaults + identity resolvers.\n//\n// What lives here is the CONTRACT layer + the SHIPPING DEFAULTS:\n// - Type vocabulary every harness is built against\n// (ContextPolicy, HarnessPolicy, RuntimeCtx, ResolvedRunPolicy,\n// ProcessMode).\n// - The default values that reflect the current best-known harness\n// (DEFAULT_CONTEXT_POLICY, DEFAULT_HARNESS_POLICY), updated as new\n// configurations ship as new defaults.\n// - The structural-equality check `isDefaultHarnessPolicy`, used by\n// createHarness to decide whether policy participates in the\n// harness id (a harness running the vanilla default policy gets a\n// byte-identical id regardless of how the policy was constructed).\n// - The default `resolveHarnessPolicy(classification)` (returns\n// `DEFAULT_HARNESS_POLICY` for every classification — no ENV\n// profile branches here).\n// - The identity `resolveRunPolicy(harness, runtimeCtx)` (returns\n// `harness.policy` unchanged — no per-model registry lookup here).\n//\n// What does NOT live here (it lives in `./harness/policy.ts`, the\n// experiment layer that wraps this module):\n// - `GGUI_POLICY_PROFILE` environment-variable reading.\n// - The named experimental profile branches\n// (narrow-merge-v1, hashline-v2, tool-driven-primitives-*,\n// primitives-ts-format, tsformat-plan-impl, numberline-flat,\n// legacy-code-array, hashline-v2-flat).\n// - `resolveRunPolicyForProfile`.\n// - The per-model `MODEL_HARNESS_REGISTRY` consultation in\n// `resolveRunPolicy`.\n// Those are experiment plumbing — a new arm lives in the wrapper layer\n// until it is validated, then graduates into the defaults here.\n\nimport type { Classification } from \"./classifier/axes.js\";\n\n// ─── Execution topology ────────────────────────────────────────────────────\n\n/**\n * Coarse execution-topology label attached to `ProcessLeg`. The workflow\n * DAG itself is the source of truth; this is a label on it, used by filters\n * and by `HarnessPolicy.processMode` to override the default `\"single_pass\"`.\n */\nexport type ProcessMode = \"single_pass\" | \"staged\" | \"staged-concurrent\";\n\n// ─── ContextPolicy ─────────────────────────────────────────────────────────\n\n/**\n * Context-leg policy. Controls the SHAPE of feedback the LLM sees on retry /\n * patch surfaces, the format of the primitives reference, and the tool-grammar\n * schema of `apply_changes`. Does NOT change what errors get caught or how\n * the code executes — only how errors get rendered back to the LLM and how\n * the primitive documentation is staged on turn 1.\n *\n * Every field is documented against the experiment that justified it. The\n * defaults in `DEFAULT_CONTEXT_POLICY` reflect the current best-known\n * harness; changing a default here is a semver-major-level event for\n * a hosted closed runtime (which shifts every benchmark cell) and\n * deserves its own commit with bench evidence.\n */\nexport interface ContextPolicy {\n /**\n * Prefix the preflight `PATCH_INVALID` retry message with `[P0-compile]`\n * so the LLM can rank it against the prompt's P0/P1/P2 priority schema.\n * Default `false`.\n */\n readonly labeledPreflight: boolean;\n /**\n * Reserved for future plumbing. When true, would prefix tier-0 self-check\n * retry violations with `[P0-*]` / `[P1-*]` priority tags. Default false.\n */\n readonly labeledTier0: boolean;\n /**\n * When true, the coding loop detects duplicate patch/error-class\n * fingerprints across consecutive patch turns and, on match, runs the\n * action chosen by {@link dupeBreakAction}. Default false.\n */\n readonly breakDuplicatePatch: boolean;\n /**\n * When `breakDuplicatePatch` fires, choose the intervention. Default\n * `\"escape\"` for back-compat with the dormant scoped-escape plumbing —\n * has no effect unless `breakDuplicatePatch` is also true.\n */\n readonly dupeBreakAction?: \"escape\" | \"diagnostic\" | \"diagnostic-noforce\";\n /**\n * Axis-keyed primitives doc slice. When `\"axis-keyed\"`, the first-turn\n * system prompt injects only the allowlisted primitives derived from\n * the classification, shrinking the ~130 KB monolith to ~30–50 KB.\n * Default `\"full\"`.\n */\n readonly primitiveDocSlice?: \"full\" | \"axis-keyed\";\n /**\n * Primitives to exclude from the axis-keyed slice. Only meaningful\n * when `primitiveDocSlice === \"axis-keyed\"`.\n */\n readonly primitiveDocExcludes?: readonly string[];\n /**\n * Hashline view format on `## Current File` + hash-verified\n * apply_changes line refs. Default `\"off\"`.\n */\n readonly hashline?: \"off\" | \"v2\";\n /**\n * Tool-driven primitive docs. When `\"names-only\"` or `\"with-props\"`,\n * the ~130 KB primitives doc is replaced with a compact ~7–9 KB index\n * and `get_components_info` is advertised alongside the authoring tool.\n * Default `\"off\"`.\n */\n readonly primitiveIndex?: \"off\" | \"names-only\" | \"with-props\";\n /**\n * Force turn 1 to advertise ONLY `get_components_info`. Requires\n * `primitiveIndex !== \"off\"`. Default `false`.\n */\n readonly primitiveIndexForceFetch?: boolean;\n /**\n * Force turn 2 to advertise ONLY `write_plan`. Requires\n * `primitiveIndex !== \"off\"` and typically `primitiveIndexForceFetch`.\n * Default `false`.\n */\n readonly primitiveIndexPlanTurn?: boolean;\n /**\n * Format of the primitive component reference. `\"markdown\"` = verbose\n * markdown tables (~128 KB); `\"ts\"` = compact TS-interface format\n * (~59 KB, same enum coverage). The TS format is the shipped default.\n */\n readonly primitiveDocFormat?: \"markdown\" | \"ts\";\n /**\n * Force turn 1 to advertise ONLY `write_plan`. Orthogonal to\n * `primitiveIndex`. Default `false`.\n */\n readonly planFirstTurn?: boolean;\n /**\n * apply_changes `code` field schema. `\"array\"` = `string[]` (one line\n * per element); `\"flat\"` = single string with `\\n` separators (3 JSON\n * nesting levels instead of 4). The flat format is the shipped\n * default.\n */\n readonly codeFormat?: \"array\" | \"flat\";\n}\n\n// ─── HarnessPolicy + runtime context ───────────────────────────────────────\n\n/**\n * Top-level policy attached to a harness. v1 defines only the context\n * sub-policy + an optional process-mode override. More sub-policies\n * (eval, retry, process-specific) will land when they have a first real\n * field with clean semantics.\n */\nexport interface HarnessPolicy {\n readonly context: ContextPolicy;\n /**\n * Process-leg mode override. When set, `createHarness` uses this value\n * instead of the default `\"single_pass\"`. Null/undefined → default.\n */\n readonly processMode?: ProcessMode;\n}\n\n/**\n * Runtime context visible at dispatch time. v1 carries `provider` +\n * optional `modelId`. Future fields (observed axis values, capability\n * flags) may appear as warranted.\n */\nexport interface RuntimeCtx {\n readonly provider: \"anthropic\" | \"openai\" | \"google\" | \"openrouter\";\n /**\n * Model id as passed to the provider SDK. Optional — when absent,\n * runtime policy resolution falls through to provider-level and\n * default resolution only.\n */\n readonly modelId?: string;\n}\n\n/**\n * Runtime-resolved policy. Identical shape to {@link HarnessPolicy} for\n * v1 — the default {@link resolveRunPolicy} is an identity. The separate\n * type exists as a parking spot for future provider-aware overrides so\n * callers can start typing `ResolvedRunPolicy` today and get new fields\n * automatically when they land.\n */\nexport type ResolvedRunPolicy = HarnessPolicy;\n\n// ─── Defaults ──────────────────────────────────────────────────────────────\n\n/** Validated shipping defaults for the generation harness. */\nexport const DEFAULT_CONTEXT_POLICY: ContextPolicy = Object.freeze({\n labeledPreflight: false,\n labeledTier0: false,\n breakDuplicatePatch: false,\n dupeBreakAction: \"escape\",\n primitiveDocSlice: \"full\",\n hashline: \"off\",\n primitiveIndex: \"off\",\n primitiveIndexForceFetch: false,\n primitiveIndexPlanTurn: false,\n // A processed TypeScript-interface format for the primitive docs,\n // chosen over verbose markdown tables: roughly half the size with no\n // loss of enum-value information.\n primitiveDocFormat: \"ts\",\n planFirstTurn: false,\n // A flat `code: string` patch payload instead of `code: string[]`.\n // The shallower JSON nesting is more reliably decoded by tool-calling\n // models.\n codeFormat: \"flat\",\n});\n\nexport const DEFAULT_HARNESS_POLICY: HarnessPolicy = Object.freeze({\n context: DEFAULT_CONTEXT_POLICY,\n});\n\n// ─── Resolvers (defaults + identity) ───────────────────────────────────────\n\n/**\n * Default harness-policy resolver — v1 returns `DEFAULT_HARNESS_POLICY`\n * for every classification. A hosted closed runtime can overlay its own\n * funnel profile-branching wrapper around this; OSS consumers get this\n * single default path and can wrap it themselves if they need\n * classification-aware overrides.\n *\n * Preserves reference equality: every call on every classification\n * returns the same frozen singleton object, so callers can use\n * `resolved === DEFAULT_HARNESS_POLICY` to detect the vanilla path\n * (used by `createHarness` via `isDefaultHarnessPolicy` to keep\n * `harness.id` byte-identical on the default path).\n */\nexport function resolveHarnessPolicy(_classification: Classification): HarnessPolicy {\n return DEFAULT_HARNESS_POLICY;\n}\n\n/**\n * Identity run-time resolver — returns `harness.policy` unchanged for\n * any runtime context. A hosted closed runtime can overlay a per-model\n * registry lookup on top (graduating bench-validated overrides per\n * model id); OSS consumers get the identity, preserving reference\n * equality so `resolved === harness.policy` detects \"no override\n * applied.\"\n */\nexport function resolveRunPolicy(\n harness: { readonly policy: HarnessPolicy },\n _runtimeCtx: RuntimeCtx,\n): ResolvedRunPolicy {\n return harness.policy;\n}\n\n// ─── Structural-equality check ─────────────────────────────────────────────\n\n/**\n * Structural equality against `DEFAULT_HARNESS_POLICY`. Used by\n * `createHarness` to skip hashing the policy for the vanilla case —\n * keeps `harness.id` byte-identical for every harness running the\n * default policy.\n *\n * Checks each field explicitly (including the unset-means-default\n * shorthand) instead of `policy === DEFAULT_HARNESS_POLICY` so callers\n * can pass a structurally-equivalent object they built themselves.\n */\nexport function isDefaultHarnessPolicy(policy: HarnessPolicy): boolean {\n return (\n policy.context.labeledPreflight === DEFAULT_CONTEXT_POLICY.labeledPreflight &&\n policy.context.labeledTier0 === DEFAULT_CONTEXT_POLICY.labeledTier0 &&\n policy.context.breakDuplicatePatch === DEFAULT_CONTEXT_POLICY.breakDuplicatePatch &&\n (policy.context.dupeBreakAction ?? \"escape\") ===\n (DEFAULT_CONTEXT_POLICY.dupeBreakAction ?? \"escape\") &&\n (policy.context.primitiveDocSlice ?? \"full\") ===\n (DEFAULT_CONTEXT_POLICY.primitiveDocSlice ?? \"full\") &&\n (policy.context.primitiveDocExcludes?.length ?? 0) === 0 &&\n (policy.context.hashline ?? \"off\") === (DEFAULT_CONTEXT_POLICY.hashline ?? \"off\") &&\n (policy.context.primitiveIndex ?? \"off\") ===\n (DEFAULT_CONTEXT_POLICY.primitiveIndex ?? \"off\") &&\n (policy.context.primitiveIndexForceFetch ?? false) ===\n (DEFAULT_CONTEXT_POLICY.primitiveIndexForceFetch ?? false) &&\n (policy.context.primitiveIndexPlanTurn ?? false) ===\n (DEFAULT_CONTEXT_POLICY.primitiveIndexPlanTurn ?? false) &&\n (policy.context.primitiveDocFormat ?? \"markdown\") ===\n (DEFAULT_CONTEXT_POLICY.primitiveDocFormat ?? \"markdown\") &&\n (policy.context.planFirstTurn ?? false) ===\n (DEFAULT_CONTEXT_POLICY.planFirstTurn ?? false) &&\n (policy.context.codeFormat ?? \"array\") ===\n (DEFAULT_CONTEXT_POLICY.codeFormat ?? \"array\") &&\n policy.processMode === undefined\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/policy.ts"],"names":[],"mappings":";AA0LO,IAAM,sBAAA,GAAwC,OAAO,MAAA,CAAO;AAAA,EACjE,gBAAA,EAAkB,KAAA;AAAA,EAClB,YAAA,EAAc,KAAA;AAAA,EACd,mBAAA,EAAqB,KAAA;AAAA,EACrB,eAAA,EAAiB,QAAA;AAAA,EACjB,iBAAA,EAAmB,MAAA;AAAA,EACnB,QAAA,EAAU,KAAA;AAAA,EACV,cAAA,EAAgB,KAAA;AAAA,EAChB,wBAAA,EAA0B,KAAA;AAAA,EAC1B,sBAAA,EAAwB,KAAA;AAAA;AAAA;AAAA;AAAA,EAIxB,kBAAA,EAAoB,IAAA;AAAA,EACpB,aAAA,EAAe,KAAA;AAAA;AAAA;AAAA;AAAA,EAIf,UAAA,EAAY;AACd,CAAC;AAEM,IAAM,sBAAA,GAAwC,OAAO,MAAA,CAAO;AAAA,EACjE,OAAA,EAAS;AACX,CAAC;AAiBM,SAAS,qBAAqB,eAAA,EAAgD;AACnF,EAAA,OAAO,sBAAA;AACT;AAUO,SAAS,gBAAA,CACd,SACA,WAAA,EACmB;AACnB,EAAA,OAAO,OAAA,CAAQ,MAAA;AACjB;AAcO,SAAS,uBAAuB,MAAA,EAAgC;AACrE,EAAA,OACE,OAAO,OAAA,CAAQ,gBAAA,KAAqB,uBAAuB,gBAAA,IAC3D,MAAA,CAAO,QAAQ,YAAA,KAAiB,sBAAA,CAAuB,YAAA,IACvD,MAAA,CAAO,QAAQ,mBAAA,KAAwB,sBAAA,CAAuB,wBAC7D,MAAA,CAAO,OAAA,CAAQ,mBAAmB,QAAA,OAChC,sBAAA,CAAuB,eAAA,IAAmB,QAAA,CAAA,IAAA,CAC5C,OAAO,OAAA,CAAQ,iBAAA,IAAqB,aAClC,sBAAA,CAAuB,iBAAA,IAAqB,YAC9C,MAAA,CAAO,OAAA,CAAQ,oBAAA,EAAsB,MAAA,IAAU,OAAO,CAAA,IAAA,CACtD,MAAA,CAAO,QAAQ,QAAA,IAAY,KAAA,OAAY,uBAAuB,QAAA,IAAY,KAAA,CAAA,IAAA,CAC1E,MAAA,CAAO,OAAA,CAAQ,kBAAkB,KAAA,OAC/B,sBAAA,CAAuB,kBAAkB,KAAA,CAAA,IAAA,CAC3C,MAAA,CAAO,QAAQ,wBAAA,IAA4B,KAAA,OACzC,sBAAA,CAAuB,wBAAA,IAA4B,WACrD,MAAA,CAAO,OAAA,CAAQ,0BAA0B,KAAA,OACvC,sBAAA,CAAuB,0BAA0B,KAAA,CAAA,IAAA,CACnD,MAAA,CAAO,OAAA,CAAQ,kBAAA,IAAsB,iBACnC,sBAAA,CAAuB,kBAAA,IAAsB,gBAC/C,MAAA,CAAO,OAAA,CAAQ,iBAAiB,KAAA,OAC9B,sBAAA,CAAuB,iBAAiB,KAAA,CAAA,IAAA,CAC1C,MAAA,CAAO,QAAQ,UAAA,IAAc,OAAA,OAC3B,uBAAuB,UAAA,IAAc,OAAA,CAAA,IACxC,OAAO,WAAA,KAAgB,MAAA;AAE3B","file":"policy.js","sourcesContent":["// packages/ui-gen/src/policy.ts\n//\n// Harness policy — validated defaults + identity resolvers.\n//\n// What lives here is the CONTRACT layer + the SHIPPING DEFAULTS:\n// - Type vocabulary every harness is built against\n// (ContextPolicy, HarnessPolicy, RuntimeCtx, ResolvedRunPolicy,\n// ProcessMode).\n// - The default values that reflect the current best-known harness\n// (DEFAULT_CONTEXT_POLICY, DEFAULT_HARNESS_POLICY), updated as new\n// configurations ship as new defaults.\n// - The structural-equality check `isDefaultHarnessPolicy`, used by\n// createHarness to decide whether policy participates in the\n// harness id (a harness running the vanilla default policy gets a\n// byte-identical id regardless of how the policy was constructed).\n// - The default `resolveHarnessPolicy(classification)` (returns\n// `DEFAULT_HARNESS_POLICY` for every classification — no ENV\n// profile branches here).\n// - The identity `resolveRunPolicy(harness, runtimeCtx)` (returns\n// `harness.policy` unchanged — no per-model registry lookup here).\n//\n// What does NOT live here (it lives in `./harness/policy.ts`, the\n// experiment layer that wraps this module):\n// - `GGUI_POLICY_PROFILE` environment-variable reading.\n// - The named experimental profile branches\n// (narrow-merge-v1, hashline-v2, tool-driven-primitives-*,\n// primitives-ts-format, tsformat-plan-impl, numberline-flat,\n// legacy-code-array, hashline-v2-flat).\n// - `resolveRunPolicyForProfile`.\n// - The per-model `MODEL_HARNESS_REGISTRY` consultation in\n// `resolveRunPolicy`.\n// Those are experiment plumbing — a new arm lives in the wrapper layer\n// until it is validated, then graduates into the defaults here.\n\nimport type { Classification } from \"./classifier/axes.js\";\n\n// ─── Execution topology ────────────────────────────────────────────────────\n\n/**\n * Coarse execution-topology label attached to `ProcessLeg`. The workflow\n * DAG itself is the source of truth; this is a label on it, used by filters\n * and by `HarnessPolicy.processMode` to override the default `\"single_pass\"`.\n */\nexport type ProcessMode = \"single_pass\" | \"staged\" | \"staged-concurrent\";\n\n// ─── ContextPolicy ─────────────────────────────────────────────────────────\n\n/**\n * Context-leg policy. Controls the SHAPE of feedback the LLM sees on retry /\n * patch surfaces, the format of the primitives reference, and the tool-grammar\n * schema of `apply_changes`. Does NOT change what errors get caught or how\n * the code executes — only how errors get rendered back to the LLM and how\n * the primitive documentation is staged on turn 1.\n *\n * Every field is documented against the experiment that justified it. The\n * defaults in `DEFAULT_CONTEXT_POLICY` reflect the current best-known\n * harness; changing a default here is a semver-major-level event for\n * a hosted closed runtime (which shifts every benchmark cell) and\n * deserves its own commit with bench evidence.\n */\nexport interface ContextPolicy {\n /**\n * Prefix the preflight `PATCH_INVALID` retry message with `[P0-compile]`\n * so the LLM can rank it against the prompt's P0/P1/P2 priority schema.\n * Default `false`.\n */\n readonly labeledPreflight: boolean;\n /**\n * Reserved for future plumbing. When true, would prefix tier-0 self-check\n * retry violations with `[P0-*]` / `[P1-*]` priority tags. Default false.\n */\n readonly labeledTier0: boolean;\n /**\n * When true, the coding loop detects duplicate patch/error-class\n * fingerprints across consecutive patch turns and, on match, runs the\n * action chosen by {@link dupeBreakAction}. Default false.\n */\n readonly breakDuplicatePatch: boolean;\n /**\n * When `breakDuplicatePatch` fires, choose the intervention. Default\n * `\"escape\"` for back-compat with the dormant scoped-escape plumbing —\n * has no effect unless `breakDuplicatePatch` is also true.\n */\n readonly dupeBreakAction?: \"escape\" | \"diagnostic\" | \"diagnostic-noforce\";\n /**\n * Axis-keyed primitives doc slice. When `\"axis-keyed\"`, the first-turn\n * system prompt injects only the allowlisted primitives derived from\n * the classification, shrinking the ~130 KB monolith to ~30–50 KB.\n * `\"axis-keyed+contract\"` adds the input primitives the contract's own\n * fields imply (ggui#1324: the axis slice alone dropped `Input` /\n * `TextArea` from a card that edits a string). Default `\"full\"`.\n */\n readonly primitiveDocSlice?: \"full\" | \"axis-keyed\" | \"axis-keyed+contract\";\n /**\n * Primitives to exclude from the axis-keyed slice (applied after the\n * contract additions under `\"axis-keyed+contract\"`). Only meaningful\n * when `primitiveDocSlice` is one of the two slice modes.\n */\n readonly primitiveDocExcludes?: readonly string[];\n /**\n * Hashline view format on `## Current File` + hash-verified\n * apply_changes line refs. Default `\"off\"`.\n */\n readonly hashline?: \"off\" | \"v2\";\n /**\n * Tool-driven primitive docs. When `\"names-only\"` or `\"with-props\"`,\n * the ~130 KB primitives doc is replaced with a compact ~7–9 KB index\n * and `get_components_info` is advertised alongside the authoring tool.\n * Default `\"off\"`.\n */\n readonly primitiveIndex?: \"off\" | \"names-only\" | \"with-props\";\n /**\n * Force turn 1 to advertise ONLY `get_components_info`. Requires\n * `primitiveIndex !== \"off\"`. Default `false`.\n */\n readonly primitiveIndexForceFetch?: boolean;\n /**\n * Force turn 2 to advertise ONLY `write_plan`. Requires\n * `primitiveIndex !== \"off\"` and typically `primitiveIndexForceFetch`.\n * Default `false`.\n */\n readonly primitiveIndexPlanTurn?: boolean;\n /**\n * Format of the primitive component reference. `\"markdown\"` = verbose\n * markdown tables (~128 KB); `\"ts\"` = compact TS-interface format\n * (~59 KB, same enum coverage). The TS format is the shipped default.\n */\n readonly primitiveDocFormat?: \"markdown\" | \"ts\";\n /**\n * Force turn 1 to advertise ONLY `write_plan`. Orthogonal to\n * `primitiveIndex`. Default `false`.\n */\n readonly planFirstTurn?: boolean;\n /**\n * apply_changes `code` field schema. `\"array\"` = `string[]` (one line\n * per element); `\"flat\"` = single string with `\\n` separators (3 JSON\n * nesting levels instead of 4). The flat format is the shipped\n * default.\n */\n readonly codeFormat?: \"array\" | \"flat\";\n}\n\n// ─── HarnessPolicy + runtime context ───────────────────────────────────────\n\n/**\n * Top-level policy attached to a harness. v1 defines only the context\n * sub-policy + an optional process-mode override. More sub-policies\n * (eval, retry, process-specific) will land when they have a first real\n * field with clean semantics.\n */\nexport interface HarnessPolicy {\n readonly context: ContextPolicy;\n /**\n * Process-leg mode override. When set, `createHarness` uses this value\n * instead of the default `\"single_pass\"`. Null/undefined → default.\n */\n readonly processMode?: ProcessMode;\n}\n\n/**\n * Runtime context visible at dispatch time. v1 carries `provider` +\n * optional `modelId`. Future fields (observed axis values, capability\n * flags) may appear as warranted.\n */\nexport interface RuntimeCtx {\n readonly provider: \"anthropic\" | \"openai\" | \"google\" | \"openrouter\";\n /**\n * Model id as passed to the provider SDK. Optional — when absent,\n * runtime policy resolution falls through to provider-level and\n * default resolution only.\n */\n readonly modelId?: string;\n}\n\n/**\n * Runtime-resolved policy. Identical shape to {@link HarnessPolicy} for\n * v1 — the default {@link resolveRunPolicy} is an identity. The separate\n * type exists as a parking spot for future provider-aware overrides so\n * callers can start typing `ResolvedRunPolicy` today and get new fields\n * automatically when they land.\n */\nexport type ResolvedRunPolicy = HarnessPolicy;\n\n// ─── Defaults ──────────────────────────────────────────────────────────────\n\n/** Validated shipping defaults for the generation harness. */\nexport const DEFAULT_CONTEXT_POLICY: ContextPolicy = Object.freeze({\n labeledPreflight: false,\n labeledTier0: false,\n breakDuplicatePatch: false,\n dupeBreakAction: \"escape\",\n primitiveDocSlice: \"full\",\n hashline: \"off\",\n primitiveIndex: \"off\",\n primitiveIndexForceFetch: false,\n primitiveIndexPlanTurn: false,\n // A processed TypeScript-interface format for the primitive docs,\n // chosen over verbose markdown tables: roughly half the size with no\n // loss of enum-value information.\n primitiveDocFormat: \"ts\",\n planFirstTurn: false,\n // A flat `code: string` patch payload instead of `code: string[]`.\n // The shallower JSON nesting is more reliably decoded by tool-calling\n // models.\n codeFormat: \"flat\",\n});\n\nexport const DEFAULT_HARNESS_POLICY: HarnessPolicy = Object.freeze({\n context: DEFAULT_CONTEXT_POLICY,\n});\n\n// ─── Resolvers (defaults + identity) ───────────────────────────────────────\n\n/**\n * Default harness-policy resolver — v1 returns `DEFAULT_HARNESS_POLICY`\n * for every classification. A hosted closed runtime can overlay its own\n * funnel profile-branching wrapper around this; OSS consumers get this\n * single default path and can wrap it themselves if they need\n * classification-aware overrides.\n *\n * Preserves reference equality: every call on every classification\n * returns the same frozen singleton object, so callers can use\n * `resolved === DEFAULT_HARNESS_POLICY` to detect the vanilla path\n * (used by `createHarness` via `isDefaultHarnessPolicy` to keep\n * `harness.id` byte-identical on the default path).\n */\nexport function resolveHarnessPolicy(_classification: Classification): HarnessPolicy {\n return DEFAULT_HARNESS_POLICY;\n}\n\n/**\n * Identity run-time resolver — returns `harness.policy` unchanged for\n * any runtime context. A hosted closed runtime can overlay a per-model\n * registry lookup on top (graduating bench-validated overrides per\n * model id); OSS consumers get the identity, preserving reference\n * equality so `resolved === harness.policy` detects \"no override\n * applied.\"\n */\nexport function resolveRunPolicy(\n harness: { readonly policy: HarnessPolicy },\n _runtimeCtx: RuntimeCtx,\n): ResolvedRunPolicy {\n return harness.policy;\n}\n\n// ─── Structural-equality check ─────────────────────────────────────────────\n\n/**\n * Structural equality against `DEFAULT_HARNESS_POLICY`. Used by\n * `createHarness` to skip hashing the policy for the vanilla case —\n * keeps `harness.id` byte-identical for every harness running the\n * default policy.\n *\n * Checks each field explicitly (including the unset-means-default\n * shorthand) instead of `policy === DEFAULT_HARNESS_POLICY` so callers\n * can pass a structurally-equivalent object they built themselves.\n */\nexport function isDefaultHarnessPolicy(policy: HarnessPolicy): boolean {\n return (\n policy.context.labeledPreflight === DEFAULT_CONTEXT_POLICY.labeledPreflight &&\n policy.context.labeledTier0 === DEFAULT_CONTEXT_POLICY.labeledTier0 &&\n policy.context.breakDuplicatePatch === DEFAULT_CONTEXT_POLICY.breakDuplicatePatch &&\n (policy.context.dupeBreakAction ?? \"escape\") ===\n (DEFAULT_CONTEXT_POLICY.dupeBreakAction ?? \"escape\") &&\n (policy.context.primitiveDocSlice ?? \"full\") ===\n (DEFAULT_CONTEXT_POLICY.primitiveDocSlice ?? \"full\") &&\n (policy.context.primitiveDocExcludes?.length ?? 0) === 0 &&\n (policy.context.hashline ?? \"off\") === (DEFAULT_CONTEXT_POLICY.hashline ?? \"off\") &&\n (policy.context.primitiveIndex ?? \"off\") ===\n (DEFAULT_CONTEXT_POLICY.primitiveIndex ?? \"off\") &&\n (policy.context.primitiveIndexForceFetch ?? false) ===\n (DEFAULT_CONTEXT_POLICY.primitiveIndexForceFetch ?? false) &&\n (policy.context.primitiveIndexPlanTurn ?? false) ===\n (DEFAULT_CONTEXT_POLICY.primitiveIndexPlanTurn ?? false) &&\n (policy.context.primitiveDocFormat ?? \"markdown\") ===\n (DEFAULT_CONTEXT_POLICY.primitiveDocFormat ?? \"markdown\") &&\n (policy.context.planFirstTurn ?? false) ===\n (DEFAULT_CONTEXT_POLICY.planFirstTurn ?? false) &&\n (policy.context.codeFormat ?? \"array\") ===\n (DEFAULT_CONTEXT_POLICY.codeFormat ?? \"array\") &&\n policy.processMode === undefined\n );\n}\n"]}
@@ -1,2 +1,8 @@
1
+ /**
2
+ * Parse one input document, render once, write the verdict. Whatever
3
+ * happens, esbuild's service is stopped before this returns (or
4
+ * rejects). If the service was never started, `stop()` is a no-op.
5
+ */
6
+ declare function workerMain(raw: string): Promise<void>;
1
7
 
2
- export { }
8
+ export { workerMain };
@@ -1,12 +1,16 @@
1
1
  import { createRequire } from 'module';
2
+ import { realpathSync } from 'fs';
3
+ import { resolve } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import * as esbuild from 'esbuild';
2
6
 
3
7
  // src/tools/render-check-worker.ts
4
8
 
5
9
  // src/internal/evaluate-component-source.ts
6
10
  async function evaluateComponentSource(sourceCode, sandboxRequire) {
7
- const esbuild = await import('esbuild');
11
+ const esbuild2 = await import('esbuild');
8
12
  const { Script } = await import('vm');
9
- const cjsResult = await esbuild.transform(sourceCode, {
13
+ const cjsResult = await esbuild2.transform(sourceCode, {
10
14
  loader: "tsx",
11
15
  target: "es2020",
12
16
  format: "cjs",
@@ -33,24 +37,37 @@ function hostGlobals() {
33
37
  }
34
38
 
35
39
  // src/tools/render-check-worker.ts
36
- async function main() {
37
- const raw = await readAllStdin();
38
- let input;
40
+ async function workerMain(raw) {
39
41
  try {
40
- input = JSON.parse(raw);
41
- } catch (err) {
42
- emit({
43
- ok: false,
44
- error: `worker: malformed input JSON \u2014 ${err instanceof Error ? err.message : String(err)}`
45
- });
46
- return;
47
- }
48
- if (typeof input.sourceCode !== "string" || input.sourceCode.length === 0) {
49
- emit({ ok: false, error: "worker: sourceCode is required" });
50
- return;
42
+ let input;
43
+ try {
44
+ input = JSON.parse(raw);
45
+ } catch (err) {
46
+ emit({
47
+ ok: false,
48
+ error: `worker: malformed input JSON \u2014 ${err instanceof Error ? err.message : String(err)}`
49
+ });
50
+ return;
51
+ }
52
+ if (typeof input.sourceCode !== "string" || input.sourceCode.length === 0) {
53
+ emit({ ok: false, error: "worker: sourceCode is required" });
54
+ return;
55
+ }
56
+ const result = await renderOnce(input);
57
+ emit(result);
58
+ } finally {
59
+ try {
60
+ await esbuild.stop();
61
+ } catch (err) {
62
+ process.stderr.write(
63
+ `render-check worker: esbuild.stop() failed \u2014 ${err instanceof Error ? err.message : String(err)}
64
+ `
65
+ );
66
+ }
51
67
  }
52
- const result = await renderOnce(input);
53
- emit(result);
68
+ }
69
+ async function main() {
70
+ await workerMain(await readAllStdin());
54
71
  }
55
72
  async function renderOnce(input) {
56
73
  const g = hostGlobals();
@@ -158,11 +175,26 @@ async function readAllStdin() {
158
175
  }
159
176
  return buffer;
160
177
  }
161
- void main().catch((err) => {
162
- emit({
163
- ok: false,
164
- error: `worker crashed: ${err instanceof Error ? err.message : String(err)}`
178
+ function isProcessEntry() {
179
+ const entry = process.argv[1];
180
+ if (entry === void 0) return false;
181
+ let entryReal;
182
+ try {
183
+ entryReal = realpathSync(resolve(entry));
184
+ } catch {
185
+ return false;
186
+ }
187
+ return entryReal === realpathSync(fileURLToPath(import.meta.url));
188
+ }
189
+ if (isProcessEntry()) {
190
+ void main().catch((err) => {
191
+ emit({
192
+ ok: false,
193
+ error: `worker crashed: ${err instanceof Error ? err.message : String(err)}`
194
+ });
165
195
  });
166
- });
196
+ }
197
+
198
+ export { workerMain };
167
199
  //# sourceMappingURL=render-check-worker.js.map
168
200
  //# sourceMappingURL=render-check-worker.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/internal/evaluate-component-source.ts","../../src/internal/open-record.ts","../../src/tools/render-check-worker.ts"],"names":[],"mappings":"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","file":"render-check-worker.js","sourcesContent":["// packages/ui-gen/src/internal/evaluate-component-source.ts\n//\n// The ONE compile-and-evaluate helper for component source: esbuild\n// TSX→CJS transform + vm.Script in-context execution behind a\n// caller-supplied `require`. Consumed by:\n//\n// - `tools/render-check-worker.ts` — the subprocess render smoke\n// test (host isolation lives at the spawn layer, in the parent's\n// `tryRender` via `@ggui-ai/sandbox`).\n// - `harness/check/runtime-render/load-component.ts` — the in-loop\n// probe (single-realm React; caller injects module resolutions).\n//\n// The module-resolution POLICY is deliberately not shared — each\n// caller owns its own `require` shim (allowed specifiers, fallbacks,\n// error wording). What is shared is the mechanical eval pipeline:\n//\n// 1. esbuild.transform(source) → CJS\n// 2. wrap in `(function(require, exports, module) { ... })`\n// 3. vm.Script(...).runInThisContext()\n// 4. call with the caller's require + a fresh module object\n// 5. return `module.exports` read AFTER execution — esbuild's CJS\n// output REPLACES `module.exports` (`module.exports =\n// __toCommonJS(...)`), so the initial reference can't be trusted.\n\nexport async function evaluateComponentSource(\n sourceCode: string,\n sandboxRequire: (id: string) => unknown,\n): Promise<Record<string, unknown>> {\n const esbuild = await import(\"esbuild\");\n const { Script } = await import(\"node:vm\");\n\n const cjsResult = await esbuild.transform(sourceCode, {\n loader: \"tsx\",\n target: \"es2020\",\n format: \"cjs\",\n jsx: \"automatic\",\n jsxImportSource: \"react\",\n sourcefile: \"Component.tsx\",\n });\n\n const sandboxModule: { exports: Record<string, unknown> } = { exports: {} };\n\n const wrappedCode = `(function(require, exports, module) {\\n${cjsResult.code}\\n})`;\n const script = new Script(wrappedCode, { filename: \"Component.cjs\" });\n const fn = script.runInThisContext() as (\n require: (id: string) => unknown,\n exports: Record<string, unknown>,\n module: { exports: Record<string, unknown> },\n ) => void;\n\n fn(sandboxRequire, sandboxModule.exports, sandboxModule);\n\n return sandboxModule.exports;\n}\n","// packages/ui-gen/src/internal/open-record.ts\n//\n// Single documented narrowing seam for \"object used as an open\n// string-keyed property bag\" — the host global object and happy-dom's\n// `Window` both genuinely ARE open records at runtime (we install /\n// restore named globals like `window` / `document` on them by key),\n// but TypeScript cannot express that without polluting the global type\n// space via declaration merging. Centralizing the one structural\n// widening here keeps every call site cast-free.\n\n/**\n * View an object as an open string-keyed record.\n *\n * Single structural widening (`Record<string, unknown>` is assignable\n * to `object`, so the cast is checked in one direction by tsc — no\n * `unknown` erasure). Returns the SAME reference: property writes land\n * on the live object.\n */\nexport function openRecord(o: object): Record<string, unknown> {\n return o as Record<string, unknown>;\n}\n\n/**\n * The host global object viewed as an open property bag.\n *\n * Used by the render-check pipeline (and its subprocess worker) to\n * install and restore DOM globals by name.\n */\nexport function hostGlobals(): Record<string, unknown> {\n return openRecord(globalThis);\n}\n","/**\n * Child worker for `tryRender`. Runs the render smoke test in a\n * subprocess so the LLM-generated TSX executes outside the parent\n * Node process.\n *\n * Wire protocol (newline-delimited JSON):\n *\n * stdin: { sourceCode: string, sampleProps?: JsonObject }\n * stdout: { ok: true } | { ok: false, error: string }\n * exit: 0 on either outcome (errors are data, not status).\n *\n * Why a separate worker instead of `vm.Script.runInThisContext` in\n * the parent:\n *\n * - `runInThisContext` runs in the parent's V8 context. A\n * pathological component that attaches to a global, recurses\n * into the event loop, or allocates a large buffer poisons the\n * parent.\n * - A subprocess gives us real kill semantics on runaway code +\n * output caps + env isolation — delegated to\n * `@ggui-ai/sandbox`.\n *\n * The worker still uses `vm.Script` to run compiled CJS because the\n * component needs a tailored `require` (react / jsx-runtime /\n * @ggui-ai/design only). Host-level process isolation is the\n * sandbox; module-scoped `require` is the resolution policy. The\n * mechanical compile+vm pipeline is the shared\n * `evaluateComponentSource` helper (also used by the in-loop probe's\n * `loadComponent`); only the resolution policy lives here.\n */\nimport { createRequire } from 'node:module';\nimport type { JsonObject } from '@ggui-ai/protocol';\nimport { evaluateComponentSource } from '../internal/evaluate-component-source.js';\nimport { hostGlobals } from '../internal/open-record.js';\n\ninterface WorkerInput {\n readonly sourceCode: string;\n readonly sampleProps?: JsonObject;\n}\n\ntype WorkerOutput =\n | { readonly ok: true }\n | { readonly ok: false; readonly error: string };\n\nasync function main(): Promise<void> {\n const raw = await readAllStdin();\n let input: WorkerInput;\n try {\n input = JSON.parse(raw) as WorkerInput;\n } catch (err) {\n emit({\n ok: false,\n error: `worker: malformed input JSON — ${\n err instanceof Error ? err.message : String(err)\n }`,\n });\n return;\n }\n\n if (typeof input.sourceCode !== 'string' || input.sourceCode.length === 0) {\n emit({ ok: false, error: 'worker: sourceCode is required' });\n return;\n }\n\n const result = await renderOnce(input);\n emit(result);\n}\n\nasync function renderOnce(input: WorkerInput): Promise<WorkerOutput> {\n // Minimal window/document shims — mirror the in-process impl so\n // components that touch either global don't crash on import. The\n // shims only need to exist for the render; they're torn down by\n // process exit.\n const g = hostGlobals();\n if (!('window' in globalThis)) {\n g.window = {\n addEventListener: () => {},\n removeEventListener: () => {},\n };\n }\n if (!('document' in globalThis)) {\n g.document = {\n getElementById: () => null,\n createElement: () => ({}),\n addEventListener: () => {},\n };\n }\n\n try {\n const React = await import('react');\n const ReactDOMServer = await import('react-dom/server');\n\n const require_ = createRequire(import.meta.url);\n\n const sandboxRequire = (id: string): unknown => {\n if (id === 'react/jsx-runtime' || id === 'react/jsx-dev-runtime') {\n return require_('react/jsx-runtime');\n }\n if (id === 'react') return require_('react');\n if (id.startsWith('@ggui-ai/design')) {\n try {\n return require_(id);\n } catch {\n // Fallback proxy — used in environments where the real\n // design package isn't resolvable. Each primitive becomes\n // a pass-through `div` so the render still exercises the\n // component's own logic.\n return new Proxy(\n {},\n {\n get: (_target, prop) => {\n if (prop === '__esModule') return true;\n return ({\n children,\n ...props\n }: {\n children?: React.ReactNode;\n [key: string]: unknown;\n }) =>\n React.createElement(\n 'div',\n { 'data-component': String(prop), ...props },\n children,\n );\n },\n },\n );\n }\n }\n throw new Error(`Import not allowed: ${id}`);\n };\n\n const exportsRecord = await evaluateComponentSource(\n input.sourceCode,\n sandboxRequire,\n );\n\n const Component = exportsRecord.default;\n if (typeof Component !== 'function') {\n return {\n ok: false,\n error: 'No default export function found in compiled code',\n };\n }\n\n const html = ReactDOMServer.renderToString(\n React.createElement(\n Component as React.ComponentType<JsonObject>,\n input.sampleProps ?? {},\n ),\n );\n\n if (!html || html.length < 5) {\n return { ok: false, error: 'Component rendered empty output' };\n }\n\n return { ok: true };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (message.includes('Cannot read properties of undefined')) {\n return {\n ok: false,\n error: `Render error: ${message}. A prop is likely undefined — add a default value or null check.`,\n };\n }\n if (message.includes('Cannot read properties of null')) {\n return {\n ok: false,\n error: `Render error: ${message}. A value is null — add a null check or fallback.`,\n };\n }\n if (message.includes('is not a function')) {\n return {\n ok: false,\n error: `Render error: ${message}. Check that all imported functions exist and are called correctly.`,\n };\n }\n return { ok: false, error: `Render error: ${message}` };\n }\n}\n\nfunction emit(output: WorkerOutput): void {\n process.stdout.write(JSON.stringify(output));\n}\n\nasync function readAllStdin(): Promise<string> {\n let buffer = '';\n process.stdin.setEncoding('utf-8');\n for await (const chunk of process.stdin) {\n buffer += chunk as string;\n }\n return buffer;\n}\n\nvoid main().catch((err: unknown) => {\n emit({\n ok: false,\n error: `worker crashed: ${err instanceof Error ? err.message : String(err)}`,\n });\n});\n"]}
1
+ {"version":3,"sources":["../../src/internal/evaluate-component-source.ts","../../src/internal/open-record.ts","../../src/tools/render-check-worker.ts"],"names":["esbuild","resolvePath"],"mappings":"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","file":"render-check-worker.js","sourcesContent":["// packages/ui-gen/src/internal/evaluate-component-source.ts\n//\n// The ONE compile-and-evaluate helper for component source: esbuild\n// TSX→CJS transform + vm.Script in-context execution behind a\n// caller-supplied `require`. Consumed by:\n//\n// - `tools/render-check-worker.ts` — the subprocess render smoke\n// test (host isolation lives at the spawn layer, in the parent's\n// `tryRender` via `@ggui-ai/sandbox`).\n// - `harness/check/runtime-render/load-component.ts` — the in-loop\n// probe (single-realm React; caller injects module resolutions).\n//\n// The module-resolution POLICY is deliberately not shared — each\n// caller owns its own `require` shim (allowed specifiers, fallbacks,\n// error wording). What is shared is the mechanical eval pipeline:\n//\n// 1. esbuild.transform(source) → CJS\n// 2. wrap in `(function(require, exports, module) { ... })`\n// 3. vm.Script(...).runInThisContext()\n// 4. call with the caller's require + a fresh module object\n// 5. return `module.exports` read AFTER execution — esbuild's CJS\n// output REPLACES `module.exports` (`module.exports =\n// __toCommonJS(...)`), so the initial reference can't be trusted.\n\nexport async function evaluateComponentSource(\n sourceCode: string,\n sandboxRequire: (id: string) => unknown,\n): Promise<Record<string, unknown>> {\n const esbuild = await import(\"esbuild\");\n const { Script } = await import(\"node:vm\");\n\n const cjsResult = await esbuild.transform(sourceCode, {\n loader: \"tsx\",\n target: \"es2020\",\n format: \"cjs\",\n jsx: \"automatic\",\n jsxImportSource: \"react\",\n sourcefile: \"Component.tsx\",\n });\n\n const sandboxModule: { exports: Record<string, unknown> } = { exports: {} };\n\n const wrappedCode = `(function(require, exports, module) {\\n${cjsResult.code}\\n})`;\n const script = new Script(wrappedCode, { filename: \"Component.cjs\" });\n const fn = script.runInThisContext() as (\n require: (id: string) => unknown,\n exports: Record<string, unknown>,\n module: { exports: Record<string, unknown> },\n ) => void;\n\n fn(sandboxRequire, sandboxModule.exports, sandboxModule);\n\n return sandboxModule.exports;\n}\n","// packages/ui-gen/src/internal/open-record.ts\n//\n// Single documented narrowing seam for \"object used as an open\n// string-keyed property bag\" — the host global object and happy-dom's\n// `Window` both genuinely ARE open records at runtime (we install /\n// restore named globals like `window` / `document` on them by key),\n// but TypeScript cannot express that without polluting the global type\n// space via declaration merging. Centralizing the one structural\n// widening here keeps every call site cast-free.\n\n/**\n * View an object as an open string-keyed record.\n *\n * Single structural widening (`Record<string, unknown>` is assignable\n * to `object`, so the cast is checked in one direction by tsc — no\n * `unknown` erasure). Returns the SAME reference: property writes land\n * on the live object.\n */\nexport function openRecord(o: object): Record<string, unknown> {\n return o as Record<string, unknown>;\n}\n\n/**\n * The host global object viewed as an open property bag.\n *\n * Used by the render-check pipeline (and its subprocess worker) to\n * install and restore DOM globals by name.\n */\nexport function hostGlobals(): Record<string, unknown> {\n return openRecord(globalThis);\n}\n","/**\n * Child worker for `tryRender`. Runs the render smoke test in a\n * subprocess so the LLM-generated TSX executes outside the parent\n * Node process.\n *\n * Wire protocol (newline-delimited JSON):\n *\n * stdin: { sourceCode: string, sampleProps?: JsonObject }\n * stdout: { ok: true } | { ok: false, error: string }\n * exit: 0 on either outcome (errors are data, not status).\n *\n * Why a separate worker instead of `vm.Script.runInThisContext` in\n * the parent:\n *\n * - `runInThisContext` runs in the parent's V8 context. A\n * pathological component that attaches to a global, recurses\n * into the event loop, or allocates a large buffer poisons the\n * parent.\n * - A subprocess gives us real kill semantics on runaway code +\n * output caps + env isolation — delegated to\n * `@ggui-ai/sandbox`.\n *\n * The worker still uses `vm.Script` to run compiled CJS because the\n * component needs a tailored `require` (react / jsx-runtime /\n * @ggui-ai/design only). Host-level process isolation is the\n * sandbox; module-scoped `require` is the resolution policy. The\n * mechanical compile+vm pipeline is the shared\n * `evaluateComponentSource` helper (also used by the in-loop probe's\n * `loadComponent`); only the resolution policy lives here.\n *\n * Shutdown (ggui#1380): the compile runs on esbuild's service, a child\n * process esbuild keeps alive for the life of this worker. After the\n * verdict is written the worker calls `esbuild.stop()` so that child is\n * gone before the worker is. `workerMain` is exported for the\n * process-shape pins; `main()` runs only when this file is the process\n * entry, so importing it never reads stdin.\n */\nimport { createRequire } from 'node:module';\nimport { realpathSync } from 'node:fs';\nimport { resolve as resolvePath } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport * as esbuild from 'esbuild';\nimport type { JsonObject } from '@ggui-ai/protocol';\nimport { evaluateComponentSource } from '../internal/evaluate-component-source.js';\nimport { hostGlobals } from '../internal/open-record.js';\n\ninterface WorkerInput {\n readonly sourceCode: string;\n readonly sampleProps?: JsonObject;\n}\n\ntype WorkerOutput =\n | { readonly ok: true }\n | { readonly ok: false; readonly error: string };\n\n/**\n * Parse one input document, render once, write the verdict. Whatever\n * happens, esbuild's service is stopped before this returns (or\n * rejects). If the service was never started, `stop()` is a no-op.\n */\nexport async function workerMain(raw: string): Promise<void> {\n try {\n let input: WorkerInput;\n try {\n input = JSON.parse(raw) as WorkerInput;\n } catch (err) {\n emit({\n ok: false,\n error: `worker: malformed input JSON — ${\n err instanceof Error ? err.message : String(err)\n }`,\n });\n return;\n }\n\n if (typeof input.sourceCode !== 'string' || input.sourceCode.length === 0) {\n emit({ ok: false, error: 'worker: sourceCode is required' });\n return;\n }\n\n const result = await renderOnce(input);\n emit(result);\n } finally {\n // Post-verdict work must never turn a written verdict into a failure:\n // a rejecting `stop()` would otherwise become an unhandled rejection,\n // exit code 1, and the host discarding the verdict on stdout.\n try {\n await esbuild.stop();\n } catch (err) {\n process.stderr.write(\n `render-check worker: esbuild.stop() failed — ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n }\n }\n}\n\nasync function main(): Promise<void> {\n await workerMain(await readAllStdin());\n}\n\nasync function renderOnce(input: WorkerInput): Promise<WorkerOutput> {\n // Minimal window/document shims — mirror the in-process impl so\n // components that touch either global don't crash on import. The\n // shims only need to exist for the render; they're torn down by\n // process exit.\n const g = hostGlobals();\n if (!('window' in globalThis)) {\n g.window = {\n addEventListener: () => {},\n removeEventListener: () => {},\n };\n }\n if (!('document' in globalThis)) {\n g.document = {\n getElementById: () => null,\n createElement: () => ({}),\n addEventListener: () => {},\n };\n }\n\n try {\n const React = await import('react');\n const ReactDOMServer = await import('react-dom/server');\n\n const require_ = createRequire(import.meta.url);\n\n const sandboxRequire = (id: string): unknown => {\n if (id === 'react/jsx-runtime' || id === 'react/jsx-dev-runtime') {\n return require_('react/jsx-runtime');\n }\n if (id === 'react') return require_('react');\n if (id.startsWith('@ggui-ai/design')) {\n try {\n return require_(id);\n } catch {\n // Fallback proxy — used in environments where the real\n // design package isn't resolvable. Each primitive becomes\n // a pass-through `div` so the render still exercises the\n // component's own logic.\n return new Proxy(\n {},\n {\n get: (_target, prop) => {\n if (prop === '__esModule') return true;\n return ({\n children,\n ...props\n }: {\n children?: React.ReactNode;\n [key: string]: unknown;\n }) =>\n React.createElement(\n 'div',\n { 'data-component': String(prop), ...props },\n children,\n );\n },\n },\n );\n }\n }\n throw new Error(`Import not allowed: ${id}`);\n };\n\n const exportsRecord = await evaluateComponentSource(\n input.sourceCode,\n sandboxRequire,\n );\n\n const Component = exportsRecord.default;\n if (typeof Component !== 'function') {\n return {\n ok: false,\n error: 'No default export function found in compiled code',\n };\n }\n\n const html = ReactDOMServer.renderToString(\n React.createElement(\n Component as React.ComponentType<JsonObject>,\n input.sampleProps ?? {},\n ),\n );\n\n if (!html || html.length < 5) {\n return { ok: false, error: 'Component rendered empty output' };\n }\n\n return { ok: true };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (message.includes('Cannot read properties of undefined')) {\n return {\n ok: false,\n error: `Render error: ${message}. A prop is likely undefined — add a default value or null check.`,\n };\n }\n if (message.includes('Cannot read properties of null')) {\n return {\n ok: false,\n error: `Render error: ${message}. A value is null — add a null check or fallback.`,\n };\n }\n if (message.includes('is not a function')) {\n return {\n ok: false,\n error: `Render error: ${message}. Check that all imported functions exist and are called correctly.`,\n };\n }\n return { ok: false, error: `Render error: ${message}` };\n }\n}\n\nfunction emit(output: WorkerOutput): void {\n process.stdout.write(JSON.stringify(output));\n}\n\nasync function readAllStdin(): Promise<string> {\n let buffer = '';\n process.stdin.setEncoding('utf-8');\n for await (const chunk of process.stdin) {\n buffer += chunk as string;\n }\n return buffer;\n}\n\n/** True when this file is the process entry (`node <this file>`), not an import. */\nfunction isProcessEntry(): boolean {\n const entry = process.argv[1];\n if (entry === undefined) return false;\n // Resolve BOTH sides through the filesystem: `import.meta.url` is this\n // file's realpath while argv[1] is whatever the spawner passed — a\n // symlinked spawn path must still read as the entry, or the worker would\n // exit with no verdict and the host would read a silent `unverified`.\n let entryReal: string;\n try {\n entryReal = realpathSync(resolvePath(entry));\n } catch {\n return false;\n }\n return entryReal === realpathSync(fileURLToPath(import.meta.url));\n}\n\nif (isProcessEntry()) {\n void main().catch((err: unknown) => {\n emit({\n ok: false,\n error: `worker crashed: ${err instanceof Error ? err.message : String(err)}`,\n });\n });\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import { GenerationRuntimeProbeStatus, GenerationRuntimeProbeOutcome } from '@ggui-ai/mcp-server-core';
1
2
  import { C as CanvasClass, D as DesignMode } from './design-mode-DLufhrNp.js';
2
3
  import { DataContract } from '@ggui-ai/protocol';
3
4
  import { C as Classification, A as AxisVector } from './axes-CzLEMDeB.js';
@@ -27,8 +28,10 @@ interface EvalIssue {
27
28
  line?: number;
28
29
  }
29
30
  /**
30
- * Runtime-render probe execution status — three-valued so "the probe
31
- * never ran" is never conflated with "the probe ran clean".
31
+ * Runtime-render probe execution status — four-valued so "the probe
32
+ * never ran" is never conflated with "the probe ran clean". ONE union: the
33
+ * same values a generation's metadata reports (`GenerationRuntimeProbeStatus`
34
+ * in `@ggui-ai/mcp-server-core`), declared there and named here.
32
35
  *
33
36
  * - `ran` — probe executed; its findings (possibly none) are
34
37
  * in the issue stream under `runtime:*` subcategories.
@@ -47,7 +50,7 @@ interface EvalIssue {
47
50
  * `hostLoad` say how long it ran and how busy the
48
51
  * host was, so a reader can tell the two cases apart.
49
52
  */
50
- type RuntimeProbeStatus = "ran" | "infra-skipped" | "not-applicable" | "timed-out";
53
+ type RuntimeProbeStatus = GenerationRuntimeProbeStatus;
51
54
  /**
52
55
  * The host's 1-minute load average at a probe's start and end, beside the
53
56
  * number of CPUs the process can use (ggui#1299). A load well above
@@ -60,20 +63,76 @@ interface ProbeHostLoad {
60
63
  readonly cores: number;
61
64
  }
62
65
  /**
63
- * Probe execution meta stamped onto an `EvalResult` at the exit-decision
64
- * points that invoke the probe. Absence of this field means the eval
65
- * path never reached a probe invocation at all — consumers treat that
66
- * the same as not-run, never as a pass.
66
+ * The engine's own fields on a probe's meta, beside the outcome a
67
+ * generation's metadata reports: why a probe did not finish, its render
68
+ * time, the host's load around it. Present on every arm of
69
+ * {@link RuntimeProbeMeta}; each is absent when the probe had nothing to
70
+ * say about it.
67
71
  */
68
- interface RuntimeProbeMeta {
69
- readonly status: RuntimeProbeStatus;
72
+ interface RuntimeProbeMetaDetail {
70
73
  /** Populated for `infra-skipped` / `not-applicable` / `timed-out` — why the probe didn't finish. */
71
74
  readonly reason?: string;
72
- /** `timed-out` only: how long the probe ran before it was stopped. */
73
- readonly elapsedMs?: number;
75
+ /** `ran` only: the check's own render time, inside `elapsedMs`. */
76
+ readonly renderMs?: number;
74
77
  /** Host load around the probe, when it ran isolated (absent for an in-process probe). */
75
78
  readonly hostLoad?: ProbeHostLoad;
79
+ /** Time the check waited for a probe slot before it started, ms; present only when > 0 (ggui#1380 C2b). */
80
+ readonly queuedMs?: number;
76
81
  }
82
+ /**
83
+ * Probe execution meta stamped onto an `EvalResult` at the exit-decision
84
+ * points that invoke the probe. Absence of this field means the eval
85
+ * path never reached a probe invocation at all — consumers treat that
86
+ * the same as not-run, never as a pass.
87
+ *
88
+ * ONE union with the generation metadata's probe outcome
89
+ * (`GenerationRuntimeProbeOutcome` in `@ggui-ai/mcp-server-core`, declared
90
+ * there and carried here with {@link RuntimeProbeMetaDetail} on every arm),
91
+ * so the round's stamp and the metadata's record cannot say different
92
+ * things (ggui#1380):
93
+ *
94
+ * - `ran` + `verdict: 'pass'` — the probe executed and no check failed;
95
+ * - `ran` + `verdict: 'fail'` — ANY check failed; `failChecks` lists the
96
+ * distinct check kinds that did (`RenderCheckKind`, the engine's name
97
+ * for the same union), never empty, in the order `RENDER_CHECK_KINDS`
98
+ * declares them, parsed from the probe's own
99
+ * `runtime:<check>[:<subject>]` issues;
100
+ * - `timed-out` / `infra-skipped` / `not-applicable` — no verdict, and the
101
+ * type carries none. `elapsedMs` (on every status that reached the
102
+ * check) and `hostLoad` say how long it ran and how busy the host was.
103
+ *
104
+ * The verdict and the repair trigger are two different questions. The
105
+ * verdict is `fail` on any failing check. The one repair turn a probe-only
106
+ * round buys fires on a `render-no-throw` fail the engine recognises
107
+ * (`isRecoverableRenderCrash`) and on nothing else: a `fail` verdict whose
108
+ * checks are `prop-sensitivity`, `action-wiring` or any other kind is
109
+ * recorded here and never fed back.
110
+ */
111
+ type RuntimeProbeMeta = GenerationRuntimeProbeOutcome & RuntimeProbeMetaDetail;
112
+ /**
113
+ * The one repair turn a probe-only round buys (ggui#1380). A serving
114
+ * deployment that wires the runtime probe and configures no evaluator runs
115
+ * the probe once after the coding turns; a render crash the engine
116
+ * recognises buys exactly one repair turn, then one re-probe. This records
117
+ * what came of it:
118
+ * - `compiled: false` — the repair turn's code did not pass self-check, so
119
+ * no re-probe ran (there is no `after`) and the pre-repair card is the
120
+ * one served;
121
+ * - `compiled: true` — the re-probe ran and `after` is its whole meta:
122
+ * its status, its verdict when it ran, and the checks that still fail.
123
+ * Whether the crash class the turn was bought for is still there is
124
+ * `after.verdict === 'fail' && after.failChecks.includes('render-no-throw')`.
125
+ * The two arms are exclusive by type.
126
+ */
127
+ type RuntimeProbeRepair = {
128
+ readonly attempted: true;
129
+ readonly compiled: false;
130
+ readonly trigger?: never;
131
+ } | {
132
+ readonly attempted: true;
133
+ readonly compiled: true;
134
+ readonly trigger: RuntimeProbeMeta;
135
+ };
77
136
  /**
78
137
  * Per-criterion execution status — three-valued so "the criterion
79
138
  * silently produced no verdict" is never conflated with "the criterion
@@ -169,6 +228,13 @@ interface EvalResult {
169
228
  * probe runner; absent on eval paths that never invoke the probe.
170
229
  */
171
230
  runtimeProbe?: RuntimeProbeMeta;
231
+ /**
232
+ * The probe-only round's repair record (ggui#1380) — present only when a
233
+ * recoverable render crash bought the one repair turn. See
234
+ * {@link RuntimeProbeRepair}. `runtimeProbe` beside it is the re-probe's
235
+ * verdict when the repair compiled, else the pre-repair probe's.
236
+ */
237
+ runtimeProbeRepair?: RuntimeProbeRepair;
172
238
  /**
173
239
  * The contract-feedback round, when one fired this generation (ggui#1261):
174
240
  * which exit-probe findings bought it and the source as it stood when the
@@ -2,7 +2,8 @@ import { C as Classification } from './axes-CzLEMDeB.js';
2
2
  import { Workflow } from './harness/types-public.js';
3
3
  import '@ggui-ai/protocol';
4
4
  import './styling-profile-CxfvV48g.js';
5
- import './types-public-Ow-tNtFE.js';
5
+ import './types-public-Bk6d1WLN.js';
6
+ import '@ggui-ai/mcp-server-core';
6
7
  import './design-mode-DLufhrNp.js';
7
8
  import './types-BOvHNG7K.js';
8
9
  import './llm.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ggui-ai/ui-gen",
3
- "version": "0.23.0",
3
+ "version": "0.24.0",
4
4
  "description": "UI generation harness for ggui — the LLM system prompt, multi-turn coding loop, and evaluation engine that turn a DataContract into a React component. Implements the UiGenerator contract from @ggui-ai/mcp-server-core; consumed by @ggui-ai/mcp-server.",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -189,9 +189,9 @@
189
189
  "puppeteer-core": "^24.40.0 || ^25.0.0",
190
190
  "react": "^18.0.0 || ^19.0.0",
191
191
  "react-dom": "^18.0.0 || ^19.0.0",
192
- "@ggui-ai/mcp-server-core": "0.23.0",
193
- "@ggui-ai/protocol": "0.23.0",
194
- "@ggui-ai/project-config": "0.23.0"
192
+ "@ggui-ai/project-config": "0.24.0",
193
+ "@ggui-ai/protocol": "0.24.0",
194
+ "@ggui-ai/mcp-server-core": "0.24.0"
195
195
  },
196
196
  "peerDependenciesMeta": {
197
197
  "@google/adk": {
@@ -233,11 +233,11 @@
233
233
  "openai": "^7.9.0",
234
234
  "typescript": "^5.0.0",
235
235
  "zod": "^4.3.6",
236
- "@ggui-ai/design": "0.23.0",
237
- "@ggui-ai/gadgets": "0.23.0",
238
- "@ggui-ai/sandbox": "0.23.0",
239
- "@ggui-ai/wire": "0.23.0",
240
- "@ggui-ai/ui-visual-tester": "0.23.0"
236
+ "@ggui-ai/design": "0.24.0",
237
+ "@ggui-ai/gadgets": "0.24.0",
238
+ "@ggui-ai/sandbox": "0.24.0",
239
+ "@ggui-ai/ui-visual-tester": "0.24.0",
240
+ "@ggui-ai/wire": "0.24.0"
241
241
  },
242
242
  "devDependencies": {
243
243
  "@google/adk": "^2.0.0",
@@ -252,9 +252,9 @@
252
252
  "react-dom": "^19.0.0",
253
253
  "tsup": "^8.5.0",
254
254
  "vitest": "^3.2.6",
255
- "@ggui-ai/mcp-server-core": "0.23.0",
256
- "@ggui-ai/project-config": "0.23.0",
257
- "@ggui-ai/protocol": "0.23.0"
255
+ "@ggui-ai/mcp-server-core": "0.24.0",
256
+ "@ggui-ai/project-config": "0.24.0",
257
+ "@ggui-ai/protocol": "0.24.0"
258
258
  },
259
259
  "engines": {
260
260
  "node": ">=22.0.0"