@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/src/policy.ts CHANGED
@@ -86,12 +86,15 @@ export interface ContextPolicy {
86
86
  * Axis-keyed primitives doc slice. When `"axis-keyed"`, the first-turn
87
87
  * system prompt injects only the allowlisted primitives derived from
88
88
  * the classification, shrinking the ~130 KB monolith to ~30–50 KB.
89
- * Default `"full"`.
89
+ * `"axis-keyed+contract"` adds the input primitives the contract's own
90
+ * fields imply (ggui#1324: the axis slice alone dropped `Input` /
91
+ * `TextArea` from a card that edits a string). Default `"full"`.
90
92
  */
91
- readonly primitiveDocSlice?: "full" | "axis-keyed";
93
+ readonly primitiveDocSlice?: "full" | "axis-keyed" | "axis-keyed+contract";
92
94
  /**
93
- * Primitives to exclude from the axis-keyed slice. Only meaningful
94
- * when `primitiveDocSlice === "axis-keyed"`.
95
+ * Primitives to exclude from the axis-keyed slice (applied after the
96
+ * contract additions under `"axis-keyed+contract"`). Only meaningful
97
+ * when `primitiveDocSlice` is one of the two slice modes.
95
98
  */
96
99
  readonly primitiveDocExcludes?: readonly string[];
97
100
  /**
@@ -27,8 +27,19 @@
27
27
  * mechanical compile+vm pipeline is the shared
28
28
  * `evaluateComponentSource` helper (also used by the in-loop probe's
29
29
  * `loadComponent`); only the resolution policy lives here.
30
+ *
31
+ * Shutdown (ggui#1380): the compile runs on esbuild's service, a child
32
+ * process esbuild keeps alive for the life of this worker. After the
33
+ * verdict is written the worker calls `esbuild.stop()` so that child is
34
+ * gone before the worker is. `workerMain` is exported for the
35
+ * process-shape pins; `main()` runs only when this file is the process
36
+ * entry, so importing it never reads stdin.
30
37
  */
31
38
  import { createRequire } from 'node:module';
39
+ import { realpathSync } from 'node:fs';
40
+ import { resolve as resolvePath } from 'node:path';
41
+ import { fileURLToPath } from 'node:url';
42
+ import * as esbuild from 'esbuild';
32
43
  import type { JsonObject } from '@ggui-ai/protocol';
33
44
  import { evaluateComponentSource } from '../internal/evaluate-component-source.js';
34
45
  import { hostGlobals } from '../internal/open-record.js';
@@ -42,28 +53,49 @@ type WorkerOutput =
42
53
  | { readonly ok: true }
43
54
  | { readonly ok: false; readonly error: string };
44
55
 
45
- async function main(): Promise<void> {
46
- const raw = await readAllStdin();
47
- let input: WorkerInput;
56
+ /**
57
+ * Parse one input document, render once, write the verdict. Whatever
58
+ * happens, esbuild's service is stopped before this returns (or
59
+ * rejects). If the service was never started, `stop()` is a no-op.
60
+ */
61
+ export async function workerMain(raw: string): Promise<void> {
48
62
  try {
49
- input = JSON.parse(raw) as WorkerInput;
50
- } catch (err) {
51
- emit({
52
- ok: false,
53
- error: `worker: malformed input JSON — ${
54
- err instanceof Error ? err.message : String(err)
55
- }`,
56
- });
57
- return;
58
- }
63
+ let input: WorkerInput;
64
+ try {
65
+ input = JSON.parse(raw) as WorkerInput;
66
+ } catch (err) {
67
+ emit({
68
+ ok: false,
69
+ error: `worker: malformed input JSON — ${
70
+ err instanceof Error ? err.message : String(err)
71
+ }`,
72
+ });
73
+ return;
74
+ }
75
+
76
+ if (typeof input.sourceCode !== 'string' || input.sourceCode.length === 0) {
77
+ emit({ ok: false, error: 'worker: sourceCode is required' });
78
+ return;
79
+ }
59
80
 
60
- if (typeof input.sourceCode !== 'string' || input.sourceCode.length === 0) {
61
- emit({ ok: false, error: 'worker: sourceCode is required' });
62
- return;
81
+ const result = await renderOnce(input);
82
+ emit(result);
83
+ } finally {
84
+ // Post-verdict work must never turn a written verdict into a failure:
85
+ // a rejecting `stop()` would otherwise become an unhandled rejection,
86
+ // exit code 1, and the host discarding the verdict on stdout.
87
+ try {
88
+ await esbuild.stop();
89
+ } catch (err) {
90
+ process.stderr.write(
91
+ `render-check worker: esbuild.stop() failed — ${err instanceof Error ? err.message : String(err)}\n`,
92
+ );
93
+ }
63
94
  }
95
+ }
64
96
 
65
- const result = await renderOnce(input);
66
- emit(result);
97
+ async function main(): Promise<void> {
98
+ await workerMain(await readAllStdin());
67
99
  }
68
100
 
69
101
  async function renderOnce(input: WorkerInput): Promise<WorkerOutput> {
@@ -192,9 +224,28 @@ async function readAllStdin(): Promise<string> {
192
224
  return buffer;
193
225
  }
194
226
 
195
- void main().catch((err: unknown) => {
196
- emit({
197
- ok: false,
198
- error: `worker crashed: ${err instanceof Error ? err.message : String(err)}`,
227
+ /** True when this file is the process entry (`node <this file>`), not an import. */
228
+ function isProcessEntry(): boolean {
229
+ const entry = process.argv[1];
230
+ if (entry === undefined) return false;
231
+ // Resolve BOTH sides through the filesystem: `import.meta.url` is this
232
+ // file's realpath while argv[1] is whatever the spawner passed — a
233
+ // symlinked spawn path must still read as the entry, or the worker would
234
+ // exit with no verdict and the host would read a silent `unverified`.
235
+ let entryReal: string;
236
+ try {
237
+ entryReal = realpathSync(resolvePath(entry));
238
+ } catch {
239
+ return false;
240
+ }
241
+ return entryReal === realpathSync(fileURLToPath(import.meta.url));
242
+ }
243
+
244
+ if (isProcessEntry()) {
245
+ void main().catch((err: unknown) => {
246
+ emit({
247
+ ok: false,
248
+ error: `worker crashed: ${err instanceof Error ? err.message : String(err)}`,
249
+ });
199
250
  });
200
- });
251
+ }
@@ -1,105 +0,0 @@
1
- import { JsonObject, DataContract } from '@ggui-ai/protocol';
2
- import { P as ProbeHostLoad } from './types-public-Ow-tNtFE.js';
3
-
4
- /**
5
- * 3-state outcome. Internal to runtime-render.
6
- * The adapter maps this to existing EvalIssue shapes:
7
- * verified → no issue
8
- * failed → fail
9
- * unverified → warn
10
- * skipped → no issue
11
- */
12
- type CheckOutcome = "verified" | "failed" | "unverified" | "skipped";
13
- interface RenderCheckIssue {
14
- readonly check: "render-no-throw" | "action-wiring" | "selection-identity" | "prop-coverage" | "prop-sensitivity" | "stream-rerender" | "optional-props-omitted";
15
- readonly outcome: CheckOutcome;
16
- readonly subject?: string;
17
- readonly reason: string;
18
- /** Optional element description ("button[aria-label='Save']") for action-wiring failures. */
19
- readonly elementHint?: string;
20
- /**
21
- * Diagnostic context from probe + AST analysis. Populated for richer feedback
22
- * to the coding agent (which native props the callback flows into, what the
23
- * probe observed firing, etc.).
24
- */
25
- readonly diagnostics?: {
26
- readonly observedJsxElements?: readonly string[];
27
- readonly observedNativeProps?: readonly string[];
28
- readonly observedCustomProps?: readonly string[];
29
- /** For action-wiring: which actions DID fire from probe clicks (helps surface mis-wiring). */
30
- readonly actionsFiredFromClicks?: readonly string[];
31
- /** Surfaced to the agent: prop sourceTool hints from the contract (only if present). */
32
- readonly sourceToolHints?: Readonly<Record<string, string>>;
33
- /**
34
- * For action-wiring (ggui#1187): what input priming did before the
35
- * synthetic dispatch — how many controls it filled, or why it could not
36
- * run. A "did not dispatch it" on an input-gated action reads differently
37
- * beside `{ primed: 0 }` or `{ error }` than beside `{ primed: 1 }`.
38
- */
39
- readonly inputPriming?: InputPrimingDiagnostic;
40
- };
41
- }
42
- /**
43
- * Outcome of `primeInputs` before the action-wiring probe (ggui#1187). A
44
- * priming failure never aborts the check — it is recorded here, on every
45
- * action-wiring issue, so it is visible in the feedback instead of vanishing.
46
- */
47
- type InputPrimingDiagnostic = {
48
- readonly primed: number;
49
- } | {
50
- readonly error: string;
51
- };
52
- /**
53
- * The check started but did not finish (ggui#1299). The only kind is a
54
- * wall-clock timeout in the isolated worker. It is not a verdict on the
55
- * component: `issues` is empty and `ok` is false because nothing was
56
- * verified, never because something failed.
57
- */
58
- interface RenderCheckIncomplete {
59
- readonly kind: "timeout";
60
- /** How long the worker ran before it was stopped. */
61
- readonly elapsedMs: number;
62
- /** The wall-clock bound it crossed. */
63
- readonly boundMs: number;
64
- }
65
- interface RenderCheckResult {
66
- readonly ok: boolean;
67
- readonly issues: readonly RenderCheckIssue[];
68
- /** Present only when the check did not finish; see {@link RenderCheckIncomplete}. */
69
- readonly incomplete?: RenderCheckIncomplete;
70
- readonly stats: {
71
- readonly actionsChecked: number;
72
- readonly streamsChecked: number;
73
- readonly renderMs: number;
74
- /** Host load around an isolated check (absent in-process). */
75
- readonly hostLoad?: ProbeHostLoad;
76
- };
77
- }
78
- interface RunRenderCheckInput {
79
- readonly sourceCode: string;
80
- readonly mockupProps: JsonObject;
81
- readonly contract?: DataContract;
82
- }
83
- /**
84
- * Run the in-loop runtime render check.
85
- *
86
- * Isolation dispatcher (#592): when the CALLING environment already
87
- * owns a DOM (`window` + `document` on `globalThis` — e.g. a vitest
88
- * `happy-dom` environment, where the environment owner installed the
89
- * globals deliberately), the check runs in-process exactly as before.
90
- * In a plain Node process it runs in an ISOLATED SUBPROCESS
91
- * (`render-check-host.ts` → `render-check-worker.ts`), because the
92
- * in-process path installs happy-dom globals on `globalThis`
93
- * process-wide — a TOCTOU hazard for anything that sniffs `window`
94
- * concurrently (provider SDKs throw "browser-like environment" at
95
- * client construction; two overlapping checks corrupt each other's
96
- * teardown). See #592 for the measured incident (4 bench cells hard-
97
- * failed on the 2026-08-19 run).
98
- *
99
- * The host is loaded lazily so the worker bundle (which imports this
100
- * module for `runRenderCheckInProcess`) never drags in the sandbox
101
- * spawn machinery.
102
- */
103
- declare function runRenderCheck(input: RunRenderCheckInput): Promise<RenderCheckResult>;
104
-
105
- export { type RenderCheckIssue as R, type RenderCheckResult as a, runRenderCheck as r };