@ggui-ai/ui-gen 0.20.0 → 0.22.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 (93) hide show
  1. package/dist/adapters/base.d.ts +7 -0
  2. package/dist/adapters/base.js +1 -0
  3. package/dist/adapters/base.js.map +1 -1
  4. package/dist/adapters/generation-dispatch.d.ts +17 -3
  5. package/dist/adapters/generation-dispatch.js +1078 -3464
  6. package/dist/adapters/generation-dispatch.js.map +1 -1
  7. package/dist/adapters/index.js +60 -3
  8. package/dist/adapters/index.js.map +1 -1
  9. package/dist/advanced/index.d.ts +4 -4
  10. package/dist/advanced/index.js +1346 -3557
  11. package/dist/advanced/index.js.map +1 -1
  12. package/dist/blueprint-validator.js +10 -3
  13. package/dist/blueprint-validator.js.map +1 -1
  14. package/dist/boilerplate.js +2 -2
  15. package/dist/boilerplate.js.map +1 -1
  16. package/dist/check/index.d.ts +1 -1
  17. package/dist/check/index.js.map +1 -1
  18. package/dist/{create-ui-generator-CxPvudbs.d.ts → create-ui-generator-BEkzEtNz.d.ts} +2 -2
  19. package/dist/{dispatch-N8wdNOX-.d.ts → dispatch-DJNpQ7DI.d.ts} +1 -1
  20. package/dist/evaluation/axis-checks/index.d.ts +3 -3
  21. package/dist/evaluation/axis-checks/index.js.map +1 -1
  22. package/dist/evaluation/axis-checks/registry.d.ts +1 -1
  23. package/dist/evaluation/index.d.ts +38 -6
  24. package/dist/evaluation/index.js +670 -3374
  25. package/dist/evaluation/index.js.map +1 -1
  26. package/dist/harness/check/runtime-render/index.d.ts +2 -2
  27. package/dist/harness/check/runtime-render/index.js +10 -3
  28. package/dist/harness/check/runtime-render/index.js.map +1 -1
  29. package/dist/harness/check/runtime-render/render-check-worker.js +5 -3
  30. package/dist/harness/check/runtime-render/render-check-worker.js.map +1 -1
  31. package/dist/harness/index.d.ts +2 -2
  32. package/dist/harness/index.js +587 -30
  33. package/dist/harness/index.js.map +1 -1
  34. package/dist/harness/llm-trace-sink.d.ts +7 -1
  35. package/dist/harness/llm-trace-sink.js.map +1 -1
  36. package/dist/harness/prompts.js +1 -1
  37. package/dist/harness/prompts.js.map +1 -1
  38. package/dist/harness/result-types.d.ts +15 -2
  39. package/dist/harness/result-types.js.map +1 -1
  40. package/dist/harness/runtime.d.ts +11 -2
  41. package/dist/harness/runtime.js +3 -3
  42. package/dist/harness/runtime.js.map +1 -1
  43. package/dist/harness/types-public.d.ts +1 -1
  44. package/dist/harness/types-public.js +2 -2
  45. package/dist/harness/types-public.js.map +1 -1
  46. package/dist/harness/validator-trace-sink.d.ts +1 -1
  47. package/dist/index.d.ts +23 -7
  48. package/dist/index.js +1346 -3557
  49. package/dist/index.js.map +1 -1
  50. package/dist/{llm-router-D7fjBhtr.d.ts → llm-router-DwwUK1bz.d.ts} +16 -3
  51. package/dist/patch.d.ts +1 -1
  52. package/dist/providers/index.js +5 -4
  53. package/dist/providers/index.js.map +1 -1
  54. package/dist/{render-check-WfOVO9kU.d.ts → render-check-C0CqVceF.d.ts} +17 -0
  55. package/dist/{types-public-DKR_h70H.d.ts → types-public-mM_-1Yk3.d.ts} +59 -1
  56. package/dist/validation/index.d.ts +1 -1
  57. package/dist/validation/index.js +1 -1
  58. package/dist/validation/index.js.map +1 -1
  59. package/dist/workflows.d.ts +1 -1
  60. package/package.json +16 -12
  61. package/src/adapters/base.ts +8 -0
  62. package/src/adapters/claude/claude-code-login.ts +128 -0
  63. package/src/adapters/claude/sdk.ts +9 -1
  64. package/src/adapters/generation-dispatch.ts +19 -2
  65. package/src/adapters/openai/tokens.ts +18 -0
  66. package/src/adapters/provider-router.ts +28 -0
  67. package/src/boilerplate/generate.ts +19 -4
  68. package/src/boilerplate/hard-sections.ts +1 -1
  69. package/src/boilerplate/templates/layouts/chat-mobile.tsx.tmpl +7 -3
  70. package/src/boilerplate/templates/layouts/chat-universal.tsx.tmpl +7 -3
  71. package/src/contract-context.ts +1 -1
  72. package/src/create-ui-generator.ts +49 -10
  73. package/src/evaluation/design-identity.ts +16 -5
  74. package/src/evaluation/index.ts +1 -0
  75. package/src/evaluation/llm-evaluator.ts +40 -13
  76. package/src/evaluation/types-public.ts +58 -0
  77. package/src/evaluation/visual-evaluator.ts +240 -30
  78. package/src/generator-build.ts +158 -0
  79. package/src/harness/check/runtime-render/adapter.ts +9 -0
  80. package/src/harness/check/runtime-render/render-check.ts +26 -5
  81. package/src/harness/claude-code-login-query.ts +529 -0
  82. package/src/harness/coding/assemble-result.ts +18 -2
  83. package/src/harness/coding/generate-task-runner.ts +75 -36
  84. package/src/harness/coding/run-coding-turn.ts +9 -0
  85. package/src/harness/coding/run-eval-round.ts +338 -38
  86. package/src/harness/enforced-coding.ts +20 -0
  87. package/src/harness/llm-router.ts +390 -55
  88. package/src/harness/llm-trace-sink.ts +7 -1
  89. package/src/harness/result-types.ts +14 -0
  90. package/src/harness/runtime.ts +6 -0
  91. package/src/index.ts +2 -1
  92. package/src/providers/openai.ts +31 -19
  93. package/src/validation/primitives.ts +1 -1
@@ -0,0 +1,158 @@
1
+ /**
2
+ * The generator's BUILD identity (ggui#1280): values that change when the
3
+ * triad's templates change and never with the request, so generations group
4
+ * by the build that produced them.
5
+ *
6
+ * - `promptTemplateSha256` / `boilerplateTemplateSha256` are sha256 over the
7
+ * production system prompt and the boilerplate rendered on the design-mode
8
+ * pin's FIXED fixtures (`PIN_FIXTURES`), in the live env, so an env-gated
9
+ * block (the pitfalls) counts as it really is. The design-mode pin renders
10
+ * the same functions and asserts these values equal its recorded constants
11
+ * under the default env: the runtime key and the pin cannot drift.
12
+ * - These are BUILD keys, not instance fingerprints. A hash of the prompt one
13
+ * generation actually sent embeds the request and differs every time, so it
14
+ * could never group two generations by build.
15
+ * - `version` is the package's own version, read once from the nearest
16
+ * `package.json` named `@ggui-ai/ui-gen` above this module. It is absent
17
+ * when none is found (e.g. a consumer bundled this package into one file),
18
+ * never guessed.
19
+ */
20
+ import { createHash } from 'node:crypto';
21
+ import { existsSync, readFileSync } from 'node:fs';
22
+ import { dirname, join } from 'node:path';
23
+ import { fileURLToPath } from 'node:url';
24
+ import type { GeneratorBuild } from '@ggui-ai/mcp-server-core';
25
+ import { generateBoilerplate } from './boilerplate/generate.js';
26
+ import type { DesignMode } from './design-mode.js';
27
+ import { buildSystemPrompt } from './harness/runtime.js';
28
+ import { PIN_FIXTURES } from './pin-fixtures.js';
29
+
30
+ /** The separator the rendered fixtures are joined with before hashing. */
31
+ export const TEMPLATE_FIXTURE_BOUNDARY = '\n<<<pin-fixture-boundary>>>\n';
32
+
33
+ /** sha256 hex of a rendered template. */
34
+ export function templateSha256(text: string): string {
35
+ return createHash('sha256').update(text, 'utf8').digest('hex');
36
+ }
37
+
38
+ /**
39
+ * The production system prompt for every pin fixture, joined. The constrained
40
+ * arm uses the positional default-path signature (no `designMode` argument),
41
+ * which is what the pin has always recorded.
42
+ */
43
+ export function renderPromptTemplates(designMode: DesignMode): string {
44
+ return PIN_FIXTURES.map((f) => {
45
+ switch (designMode) {
46
+ case 'constrained':
47
+ return buildSystemPrompt(f.userRequest, f.shellType, f.screen);
48
+ case 'free':
49
+ return buildSystemPrompt(f.userRequest, f.shellType, f.screen, undefined, undefined, undefined, 'free');
50
+ }
51
+ }).join(TEMPLATE_FIXTURE_BOUNDARY);
52
+ }
53
+
54
+ /** The boilerplate for every pin fixture, joined — same signature split as the prompt. */
55
+ export function renderBoilerplateTemplates(designMode: DesignMode): string {
56
+ return PIN_FIXTURES.map((f) => {
57
+ switch (designMode) {
58
+ case 'constrained':
59
+ return generateBoilerplate(f.userRequest, f.contract, f.shellType, f.screen);
60
+ case 'free':
61
+ return generateBoilerplate(f.userRequest, f.contract, f.shellType, f.screen, undefined, undefined, 'free');
62
+ }
63
+ }).join(TEMPLATE_FIXTURE_BOUNDARY);
64
+ }
65
+
66
+ /**
67
+ * ui-gen's precise {@link GeneratorBuild}: the core port's generic shape with
68
+ * ui-gen's own keys — `mode` is the design mode, and `digests` names the two
69
+ * template digests (the keys a blueprint's build stamp copies).
70
+ */
71
+ export interface UiGenBuild extends GeneratorBuild {
72
+ /** This package's version; absent when its `package.json` cannot be found at runtime. */
73
+ readonly version?: string;
74
+ readonly mode: DesignMode;
75
+ readonly digests: {
76
+ readonly promptTemplateSha256: string;
77
+ readonly boilerplateTemplateSha256: string;
78
+ };
79
+ }
80
+
81
+ const PACKAGE_NAME = '@ggui-ai/ui-gen';
82
+ /** How far up from this module the package root can sit (`src/`, `dist/`, a nested entry). */
83
+ const MAX_PACKAGE_DEPTH = 6;
84
+
85
+ /**
86
+ * A `package.json` the walk meets may not be ours, and in a consumer's bundle
87
+ * it may not even parse. A candidate that does not parse is skipped (as if
88
+ * absent), so reading this package's version can never fail a generation —
89
+ * the version is then absent, never guessed. Only a JSON `SyntaxError` is
90
+ * handled here; any other error is a real fault and propagates.
91
+ */
92
+ function parsePackageJson(text: string): unknown {
93
+ try {
94
+ const parsed: unknown = JSON.parse(text);
95
+ return parsed;
96
+ } catch (err) {
97
+ if (err instanceof SyntaxError) return undefined;
98
+ throw err;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Walk up from `startDir` (this module's own directory by default) to the
104
+ * nearest `package.json` named `@ggui-ai/ui-gen` and return its version;
105
+ * `undefined` when none is found within reach.
106
+ */
107
+ export function findPackageVersion(startDir: string = dirname(fileURLToPath(import.meta.url))): string | undefined {
108
+ let dir = startDir;
109
+ for (let i = 0; i < MAX_PACKAGE_DEPTH; i++) {
110
+ const candidate = join(dir, 'package.json');
111
+ const pkg = existsSync(candidate) ? parsePackageJson(readFileSync(candidate, 'utf8')) : undefined;
112
+ if (pkg !== undefined) {
113
+ if (
114
+ typeof pkg === 'object' &&
115
+ pkg !== null &&
116
+ 'name' in pkg &&
117
+ pkg.name === PACKAGE_NAME &&
118
+ 'version' in pkg &&
119
+ typeof pkg.version === 'string'
120
+ ) {
121
+ return pkg.version;
122
+ }
123
+ }
124
+ const parent = dirname(dir);
125
+ if (parent === dir) break;
126
+ dir = parent;
127
+ }
128
+ return undefined;
129
+ }
130
+
131
+ let versionRead = false;
132
+ let packageVersion: string | undefined;
133
+ const identities = new Map<string, UiGenBuild>();
134
+
135
+ /**
136
+ * The build identity for `designMode`, computed once per process per design
137
+ * mode and pitfalls env gate (the only env the templates read), so a changed
138
+ * gate is never answered from a stale memo.
139
+ */
140
+ export function generatorBuild(designMode: DesignMode): UiGenBuild {
141
+ const key = `${designMode}|${process.env.GGUI_PITFALLS ?? ''}|${process.env.GGUI_NEW_PITFALLS ?? ''}`;
142
+ const known = identities.get(key);
143
+ if (known !== undefined) return known;
144
+ if (!versionRead) {
145
+ packageVersion = findPackageVersion();
146
+ versionRead = true;
147
+ }
148
+ const identity: UiGenBuild = {
149
+ ...(packageVersion !== undefined ? { version: packageVersion } : {}),
150
+ mode: designMode,
151
+ digests: {
152
+ promptTemplateSha256: templateSha256(renderPromptTemplates(designMode)),
153
+ boilerplateTemplateSha256: templateSha256(renderBoilerplateTemplates(designMode)),
154
+ },
155
+ };
156
+ identities.set(key, identity);
157
+ return identity;
158
+ }
@@ -203,6 +203,15 @@ function toEvalIssue(issue: RenderCheckIssue): EvalIssue | null {
203
203
  if (diag?.actionsFiredFromClicks?.length) {
204
204
  diagParts.push(`other actions fired from clicks: ${diag.actionsFiredFromClicks.join(", ")}`);
205
205
  }
206
+ if (diag?.inputPriming) {
207
+ // ggui#1187: an input-gated action that "did not dispatch" reads
208
+ // differently beside "primed 0" / a priming error than beside "primed 1".
209
+ diagParts.push(
210
+ "primed" in diag.inputPriming
211
+ ? `input priming: ${diag.inputPriming.primed} control(s) filled before the click`
212
+ : `input priming failed: ${diag.inputPriming.error}`,
213
+ );
214
+ }
206
215
  const diagSuffix = diagParts.length ? ` [observed: ${diagParts.join("; ")}]` : "";
207
216
 
208
217
  switch (issue.check) {
@@ -99,9 +99,25 @@ export interface RenderCheckIssue {
99
99
  readonly actionsFiredFromClicks?: readonly string[];
100
100
  /** Surfaced to the agent: prop sourceTool hints from the contract (only if present). */
101
101
  readonly sourceToolHints?: Readonly<Record<string, string>>;
102
+ /**
103
+ * For action-wiring (ggui#1187): what input priming did before the
104
+ * synthetic dispatch — how many controls it filled, or why it could not
105
+ * run. A "did not dispatch it" on an input-gated action reads differently
106
+ * beside `{ primed: 0 }` or `{ error }` than beside `{ primed: 1 }`.
107
+ */
108
+ readonly inputPriming?: InputPrimingDiagnostic;
102
109
  };
103
110
  }
104
111
 
112
+ /**
113
+ * Outcome of `primeInputs` before the action-wiring probe (ggui#1187). A
114
+ * priming failure never aborts the check — it is recorded here, on every
115
+ * action-wiring issue, so it is visible in the feedback instead of vanishing.
116
+ */
117
+ export type InputPrimingDiagnostic =
118
+ | { readonly primed: number }
119
+ | { readonly error: string };
120
+
105
121
  export interface RenderCheckResult {
106
122
  readonly ok: boolean;
107
123
  readonly issues: readonly RenderCheckIssue[];
@@ -513,11 +529,16 @@ export async function runRenderCheckInProcess(
513
529
  // sets input VALUES (not textContent) and fires input/change before the
514
530
  // per-candidate fire-log window is opened, so it neither perturbs the
515
531
  // prop-sensitivity text baseline nor false-positives the wiring probe.
516
- // Best-effort: a priming failure must never abort the check.
532
+ // A priming failure never aborts the check — it is RECORDED on every
533
+ // action-wiring issue as `diagnostics.inputPriming`. (The first cut
534
+ // swallowed it, and a `ReferenceError` in the check worker's realm —
535
+ // no `HTMLInputElement` on `globalThis` — hid behind that catch while
536
+ // every chat cell kept warning.)
537
+ let inputPriming: InputPrimingDiagnostic;
517
538
  try {
518
- primeInputs(renderResult.container);
519
- } catch {
520
- // Un-primeable DOM falls back to the prior (un-primed) behavior.
539
+ inputPriming = { primed: primeInputs(renderResult.container) };
540
+ } catch (err) {
541
+ inputPriming = { error: err instanceof Error ? err.message : String(err) };
521
542
  }
522
543
  // ── Check 2: Action wiring (BLOCK / unverified-as-warn) ───────────
523
544
  if (input.contract?.actionSpec) {
@@ -537,7 +558,7 @@ export async function runRenderCheckInProcess(
537
558
  probe,
538
559
  user,
539
560
  });
540
- if (issue) issues.push(issue);
561
+ if (issue) issues.push({ ...issue, diagnostics: { ...issue.diagnostics, inputPriming } });
541
562
  }
542
563
  }
543
564