@ggui-ai/ui-gen 0.23.0 → 0.25.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 (123) hide show
  1. package/dist/adapter-Cg_rkpGq.d.ts +201 -0
  2. package/dist/adapters/generation-dispatch.d.ts +58 -15
  3. package/dist/adapters/generation-dispatch.js +1418 -180
  4. package/dist/adapters/generation-dispatch.js.map +1 -1
  5. package/dist/adapters/index.js +3 -1
  6. package/dist/adapters/index.js.map +1 -1
  7. package/dist/advanced/index.d.ts +12 -8
  8. package/dist/advanced/index.js +1467 -239
  9. package/dist/advanced/index.js.map +1 -1
  10. package/dist/{axes-CzLEMDeB.d.ts → axes-CKVCzR_x.d.ts} +1 -1
  11. package/dist/bank-CT4eEkta.d.ts +118 -0
  12. package/dist/blueprint-validator.js +153 -59
  13. package/dist/blueprint-validator.js.map +1 -1
  14. package/dist/boilerplate.js +24 -12
  15. package/dist/boilerplate.js.map +1 -1
  16. package/dist/check/index.d.ts +12 -4
  17. package/dist/check/index.js +94 -2
  18. package/dist/check/index.js.map +1 -1
  19. package/dist/classifier/index.d.ts +2 -2
  20. package/dist/compose.d.ts +2 -2
  21. package/dist/{create-ui-generator-D0kbWwln.d.ts → create-ui-generator-DFnnArrv.d.ts} +43 -11
  22. package/dist/{dispatch-wxaIMwZt.d.ts → dispatch-CStl9eAw.d.ts} +5 -3
  23. package/dist/evaluation/axis-checks/index.d.ts +6 -5
  24. package/dist/evaluation/axis-checks/index.js +90 -2
  25. package/dist/evaluation/axis-checks/index.js.map +1 -1
  26. package/dist/evaluation/axis-checks/registry.d.ts +4 -3
  27. package/dist/evaluation/axis-checks/registry.js +88 -1
  28. package/dist/evaluation/axis-checks/registry.js.map +1 -1
  29. package/dist/evaluation/index.d.ts +301 -23
  30. package/dist/evaluation/index.js +746 -30
  31. package/dist/evaluation/index.js.map +1 -1
  32. package/dist/evaluation/types.d.ts +9 -1
  33. package/dist/evaluation/types.js.map +1 -1
  34. package/dist/fragments/index.d.ts +3 -3
  35. package/dist/harness/check/runtime-render/index.d.ts +30 -11
  36. package/dist/harness/check/runtime-render/index.js +203 -60
  37. package/dist/harness/check/runtime-render/index.js.map +1 -1
  38. package/dist/harness/check/runtime-render/render-check-worker.d.ts +8 -1
  39. package/dist/harness/check/runtime-render/render-check-worker.js +72 -32
  40. package/dist/harness/check/runtime-render/render-check-worker.js.map +1 -1
  41. package/dist/harness/index.d.ts +5 -4
  42. package/dist/harness/index.js +33 -15
  43. package/dist/harness/index.js.map +1 -1
  44. package/dist/harness/prompts.js.map +1 -1
  45. package/dist/harness/result-types.d.ts +4 -3
  46. package/dist/harness/runtime.d.ts +11 -4
  47. package/dist/harness/runtime.js +26 -14
  48. package/dist/harness/runtime.js.map +1 -1
  49. package/dist/harness/types-public.d.ts +13 -4
  50. package/dist/harness/types-public.js +33 -15
  51. package/dist/harness/types-public.js.map +1 -1
  52. package/dist/harness/validator-trace-sink.d.ts +4 -3
  53. package/dist/hash.d.ts +1 -1
  54. package/dist/index.d.ts +59 -8
  55. package/dist/index.js +1476 -241
  56. package/dist/index.js.map +1 -1
  57. package/dist/{llm-router-DwwUK1bz.d.ts → llm-router-nfO7wPec.d.ts} +3 -1
  58. package/dist/patch.d.ts +4 -3
  59. package/dist/policy.d.ts +8 -5
  60. package/dist/policy.js.map +1 -1
  61. package/dist/tools/get-primitives-ts.d.ts +1 -1
  62. package/dist/tools/get-primitives-ts.js +1 -1
  63. package/dist/tools/get-primitives-ts.js.map +1 -1
  64. package/dist/tools/render-check-worker.d.ts +7 -1
  65. package/dist/tools/render-check-worker.js +55 -23
  66. package/dist/tools/render-check-worker.js.map +1 -1
  67. package/dist/{types-BOvHNG7K.d.ts → types-Dh1s4K9j.d.ts} +1 -1
  68. package/dist/{types-public-Ow-tNtFE.d.ts → types-public-BJTLpzWn.d.ts} +208 -15
  69. package/dist/validation/index.d.ts +1 -1
  70. package/dist/validation/index.js +1 -1
  71. package/dist/validation/index.js.map +1 -1
  72. package/dist/workflows.d.ts +4 -3
  73. package/package.json +12 -12
  74. package/src/adapters/generation-dispatch.ts +173 -98
  75. package/src/adapters/tools.ts +2 -1
  76. package/src/boilerplate/free-design-prompt.ts +1 -1
  77. package/src/boilerplate/generate.ts +26 -1
  78. package/src/boilerplate/hard-sections.ts +13 -13
  79. package/src/boilerplate/system-prompt.ts +1 -1
  80. package/src/check/index.ts +1 -0
  81. package/src/check/run-tier0.ts +80 -1
  82. package/src/check/sample-props.ts +63 -0
  83. package/src/coding-agent/tools.ts +4 -1
  84. package/src/create-harness.ts +3 -3
  85. package/src/create-ui-generator.ts +204 -95
  86. package/src/evaluation/__fixtures__/png.ts +100 -0
  87. package/src/evaluation/axis-checks/checks/state-merge.ts +1 -1
  88. package/src/evaluation/axis-checks/checks/universal.ts +111 -0
  89. package/src/evaluation/axis-checks/dispatch.ts +4 -1
  90. package/src/evaluation/criteria/bank.ts +85 -0
  91. package/src/evaluation/criteria/context.ts +52 -0
  92. package/src/evaluation/criteria/resolve.ts +159 -0
  93. package/src/evaluation/criteria/select.ts +79 -0
  94. package/src/evaluation/index.ts +23 -0
  95. package/src/evaluation/ink-extent.ts +200 -0
  96. package/src/evaluation/theme-css.ts +12 -5
  97. package/src/evaluation/types-public.ts +200 -11
  98. package/src/evaluation/types.ts +9 -0
  99. package/src/evaluation/visual-evaluator.ts +636 -30
  100. package/src/harness/check/runtime-render/adapter.ts +221 -75
  101. package/src/harness/check/runtime-render/index.ts +75 -2
  102. package/src/harness/check/runtime-render/render-check-host.ts +82 -19
  103. package/src/harness/check/runtime-render/render-check-worker.ts +83 -29
  104. package/src/harness/check/runtime-render/render-check.ts +154 -117
  105. package/src/harness/coding/generate-task-runner.ts +60 -4
  106. package/src/harness/coding/init-session.ts +13 -0
  107. package/src/harness/coding/run-coding-turn.ts +7 -3
  108. package/src/harness/coding/run-eval-round.ts +257 -15
  109. package/src/harness/policy.ts +11 -0
  110. package/src/harness/primitive-slice.ts +104 -0
  111. package/src/harness/runtime.ts +5 -0
  112. package/src/harness/types-public.ts +9 -1
  113. package/src/harness-tool-names.ts +38 -0
  114. package/src/index.ts +16 -0
  115. package/src/policy.ts +7 -4
  116. package/src/run-check.ts +2 -0
  117. package/src/tools/get-primitives-ts.ts +1 -1
  118. package/src/tools/get-wire.ts +1 -1
  119. package/src/tools/render-check-worker.ts +74 -23
  120. package/src/tools/render-check.ts +0 -60
  121. package/src/tools.ts +11 -1
  122. package/src/validation/primitives.ts +1 -1
  123. package/dist/render-check-Blwj8gB6.d.ts +0 -105
@@ -23,6 +23,7 @@
23
23
  // prompt-build time and modifies the LLM's *initial* conditioning
24
24
  // uniformly — it cannot contaminate a retry trajectory mid-run.
25
25
 
26
+ import type { DataContract, JsonSchema } from "@ggui-ai/protocol";
26
27
  import type { Classification } from "../classifier/index.js";
27
28
 
28
29
  /** Primitives every fixture needs — layout + typography + core interaction.
@@ -162,6 +163,62 @@ export function computePrimitiveAllowlist(
162
163
  return [...set].sort();
163
164
  }
164
165
 
166
+ /**
167
+ * The input primitives a contract's own fields imply (ggui#1324).
168
+ *
169
+ * The axis-keyed allowlist is derived from the classification alone, so a card
170
+ * whose axes do not say "form" still loses the inputs its contract needs: the
171
+ * #45 kanban canary edits a task's title and priority through an action
172
+ * payload, and the axis slice dropped `Input` / `TextArea` / `Select`.
173
+ *
174
+ * Walks every context slot's schema and every action payload's schema down to
175
+ * its leaves (object `properties`, array `items`) and maps each leaf type:
176
+ * an `enum` → `Select`, `RadioGroup`; `string` → `Input`, `TextArea`,
177
+ * `FormField`; `number` / `integer` → `Input`, `Slider`; `boolean` →
178
+ * `Checkbox`, `Toggle`. A declared stream adds `Spinner` and `Badge`. Props are
179
+ * display data and add nothing (the classification covers display). Returns a
180
+ * sorted array.
181
+ */
182
+ export function computeContractPrimitives(
183
+ contract: DataContract | undefined,
184
+ ): readonly string[] {
185
+ const out = new Set<string>();
186
+ if (!contract) return [];
187
+ for (const slot of Object.values(contract.contextSpec ?? {})) addLeaves(slot.schema, out);
188
+ for (const action of Object.values(contract.actionSpec ?? {})) {
189
+ if (action.schema) addLeaves(action.schema, out);
190
+ }
191
+ if (Object.keys(contract.streamSpec ?? {}).length > 0) {
192
+ out.add("Spinner");
193
+ out.add("Badge");
194
+ }
195
+ return [...out].sort();
196
+ }
197
+
198
+ function addLeaves(schema: JsonSchema, out: Set<string>): void {
199
+ for (const child of Object.values(schema.properties ?? {})) addLeaves(child, out);
200
+ if (schema.items) addLeaves(schema.items, out);
201
+ if (schema.enum && schema.enum.length > 0) {
202
+ out.add("Select");
203
+ out.add("RadioGroup");
204
+ return;
205
+ }
206
+ const types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
207
+ for (const t of types) {
208
+ if (t === "string") {
209
+ out.add("Input");
210
+ out.add("TextArea");
211
+ out.add("FormField");
212
+ } else if (t === "number" || t === "integer") {
213
+ out.add("Input");
214
+ out.add("Slider");
215
+ } else if (t === "boolean") {
216
+ out.add("Checkbox");
217
+ out.add("Toggle");
218
+ }
219
+ }
220
+ }
221
+
165
222
  /**
166
223
  * Slice `PRIMITIVES_DOCUMENTATION` to include only the allowlisted primitive
167
224
  * sections. The doc structure is:
@@ -222,3 +279,50 @@ export function slicePrimitiveDocumentation(
222
279
 
223
280
  return preamble + kept.join("");
224
281
  }
282
+
283
+ /**
284
+ * Slice the TypeScript-interface primitives reference
285
+ * (`PRIMITIVES_DOCUMENTATION_TS`) by the same allowlist the markdown
286
+ * slicer takes (ggui#1324, speed/002).
287
+ *
288
+ * The TS doc is a sequence of SECTIONS, each opened by a header line
289
+ * `// <Name> — <description>` and carrying that name's `interface`, its
290
+ * `// Example:` and the usage snippet that follows; category banners
291
+ * (`// ═══…`) sit between sections. A section whose `<Name>` is a KNOWN
292
+ * primitive is kept iff `<Name>` is in the allowlist. Every other line is
293
+ * kept: the preamble, the banners, the `CRITICAL` guidance sections, the
294
+ * support-type sections (`SelectOption`, `TableColumn`, …) and any section
295
+ * the slicer does not know — the same guidance rule as
296
+ * {@link slicePrimitiveDocumentation}. Allowing every known name returns the
297
+ * doc byte-identical.
298
+ *
299
+ * With no section header found the doc is returned whole (defensive, as the
300
+ * markdown slicer does when it finds no `### ` sections).
301
+ */
302
+ export function slicePrimitiveDocumentationTs(
303
+ fullDoc: string,
304
+ allowlist: readonly string[],
305
+ ): string {
306
+ const allow = new Set(allowlist);
307
+ const lines = fullDoc.split("\n");
308
+ let sawHeader = false;
309
+ let keep = true;
310
+ const kept: string[] = [];
311
+ for (const line of lines) {
312
+ const header = TS_SECTION_HEADER.exec(line);
313
+ if (header !== null) {
314
+ sawHeader = true;
315
+ const name = header[1]!;
316
+ keep = !KNOWN_PRIMITIVE_SECTIONS.has(name) || allow.has(name);
317
+ } else if (TS_SECTION_BANNER.test(line)) {
318
+ keep = true;
319
+ }
320
+ if (keep) kept.push(line);
321
+ }
322
+ return sawHeader ? kept.join("\n") : fullDoc;
323
+ }
324
+
325
+ /** `// Button — A clickable button …`: the line that opens a TS-doc section. */
326
+ const TS_SECTION_HEADER = /^\/\/ ([A-Za-z]+) — /;
327
+ /** `// ═══…`: a category banner between TS-doc sections; always kept. */
328
+ const TS_SECTION_BANNER = /^\/\/ ═/;
@@ -33,6 +33,7 @@ import { renderPitfallsBlock } from "./pitfalls.js";
33
33
  import type { GadgetDescriptor, DataContract, JsonObject } from "@ggui-ai/protocol";
34
34
  import { buildSystemPrompt as buildSystemPromptSkeleton } from "../boilerplate.js";
35
35
  import type { AgentConfig } from "./llm-router.js";
36
+ import type { CriteriaBank } from "../evaluation/criteria/bank.js";
36
37
  import type { CanvasClass, DesignMode } from "../design-mode.js";
37
38
 
38
39
  // Re-export the boilerplate generator so existing internal importers
@@ -103,6 +104,10 @@ export interface SingleComponentParams {
103
104
  * default tokens. Never part of the prompt or the identity.
104
105
  */
105
106
  cssTokens?: string;
107
+ /** ggui#1436 — the visual criteria bank (data, parsed by `parseCriteriaBank`); absent = no block, today's judge. */
108
+ criteriaBank?: CriteriaBank;
109
+ /** ggui#1436 — the caller's item kind for the selector (a bootstrap mint's `hello`); absent = not gated by kind. */
110
+ criteriaKind?: string;
106
111
  };
107
112
  onProgress?: (event: unknown) => void;
108
113
  onInitialResult?: (result: {
@@ -148,10 +148,18 @@ export interface RuntimeRenderOutcome {
148
148
  readonly issues: readonly EvalIssue[];
149
149
  /** Populated for `infra-skipped` / `not-applicable` / `timed-out` — why the probe didn't finish. */
150
150
  readonly reason?: string;
151
- /** `timed-out` only: how long the probe ran before it was stopped. */
151
+ /**
152
+ * How long the check took, wall-clock, measured by the adapter around the
153
+ * check — on every status that reached it (`ran`, `timed-out`,
154
+ * `infra-skipped`). Absent on `not-applicable`: nothing ran.
155
+ */
152
156
  readonly elapsedMs?: number;
157
+ /** `ran` only: the check's own render time, inside `elapsedMs`. */
158
+ readonly renderMs?: number;
153
159
  /** Host load around the probe, when it ran isolated (absent for an in-process probe). */
154
160
  readonly hostLoad?: ProbeHostLoad;
161
+ /** Time the check waited for a probe slot before it started, ms; present only when > 0 (ggui#1380 C2b). */
162
+ readonly queuedMs?: number;
155
163
  }
156
164
 
157
165
  export interface RuntimeRenderCheck {
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The coding loop's tool set — the ONE list `createHarness` builds its
3
+ * coding and scoped tools from, and the identity a build stamp names it
4
+ * by.
5
+ *
6
+ * `harnessToolNames(mode)` returns one entry per tool definition,
7
+ * `<name>@<digest>`: the tool's own (unprefixed) name and the first 8 hex
8
+ * of sha256 over `JSON.stringify` of its definition — so a change to a
9
+ * tool's schema OR its description (prompt text, as output-changing as a
10
+ * template) moves the entry, and two definitions that share a name (a
11
+ * scoped variant keeps the name and narrows the grammar) are two entries.
12
+ * Coding and scoped alike, since the scoped set runs on the
13
+ * duplicate-fingerprint escape; sorted, de-duplicated, a fresh array. It
14
+ * reads the same constants the harness is assembled from, so the list can
15
+ * never drift from what runs; the pin beside it holds the two equal for
16
+ * every design mode. The design mode is part of the signature because the
17
+ * tool set is keyed by the harness a mode builds, even while both modes
18
+ * declare the same tools.
19
+ */
20
+ import { createHash } from 'node:crypto';
21
+ import type { LLMToolDef } from './llm.js';
22
+ import type { DesignMode } from './design-mode.js';
23
+ import { APPLY_CHANGES_TOOL, APPLY_CHANGES_TOOL_SCOPED } from './tools.js';
24
+
25
+ /** The coding tools every harness declares (`what.codingTools`). */
26
+ export const HARNESS_CODING_TOOLS: readonly LLMToolDef[] = [APPLY_CHANGES_TOOL];
27
+ /** The scoped fallback the runtime promotes into `codingTools` on a duplicate-fingerprint escape. */
28
+ export const HARNESS_SCOPED_TOOLS: readonly LLMToolDef[] = [APPLY_CHANGES_TOOL_SCOPED];
29
+
30
+ /** One tool's entry: `<name>@<first 8 hex of sha256(JSON.stringify(def))>`. */
31
+ export function harnessToolEntry(def: LLMToolDef): string {
32
+ return `${def.name}@${createHash('sha256').update(JSON.stringify(def), 'utf8').digest('hex').slice(0, 8)}`;
33
+ }
34
+
35
+ export function harnessToolNames(designMode: DesignMode): string[] {
36
+ void designMode; // one tool set for every mode today; the mode keys the harness, so it stays on the signature
37
+ return [...new Set([...HARNESS_CODING_TOOLS, ...HARNESS_SCOPED_TOOLS].map(harnessToolEntry))].sort();
38
+ }
package/src/index.ts CHANGED
@@ -20,8 +20,14 @@ export {
20
20
  extractComponentCode,
21
21
  } from './create-ui-generator.js';
22
22
  export type { CreateUiGeneratorOptions } from './create-ui-generator.js';
23
+ export type { RuntimeRenderProbeConfig } from './harness/check/runtime-render/adapter.js';
23
24
  export type { ProviderRetryInfo } from './harness/llm-router.js';
24
25
  export type { GenerationResult, SameExchangeBreak } from './harness/result-types.js';
26
+ // The build identity a stamp outside this package can name: the same
27
+ // digests `createUiGenerator` puts on every result's `metadata.build`, for
28
+ // either design mode — so a harness definition can carry both modes' digests
29
+ // while a run's metadata carries only the mode that ran.
30
+ export { generatorBuild } from './generator-build.js';
25
31
  export type { UiGenBuild } from './generator-build.js';
26
32
 
27
33
  // Generator registry seam re-export. The interface + slug helpers live
@@ -53,6 +59,12 @@ export {
53
59
  export type { RenderingContext } from './contract-context.js';
54
60
  export { canvasForRenderingContext } from './contract-context.js';
55
61
 
62
+ // ── The coding loop's tool set ───────────────────────────────────────
63
+ //
64
+ // The tools the harness declares, by name — the one list `createHarness`
65
+ // builds from, so a build stamp names the same set that runs.
66
+ export { harnessToolEntry, harnessToolNames, HARNESS_CODING_TOOLS, HARNESS_SCOPED_TOOLS } from './harness-tool-names.js';
67
+
56
68
  // ── Design mode ──────────────────────────────────────────────────────
57
69
  //
58
70
  // `createUiGenerator({ designMode })` — `constrained` (default) or
@@ -83,5 +95,9 @@ export type {
83
95
  // go through this helper instead of `new Anthropic(...)` so future
84
96
  // header / baseURL tweaks land in one place.
85
97
  export { createAnthropicClient } from './adapters/claude/client.js';
98
+ // ggui#1185: the credential a key resolver returns to select the local CLI
99
+ // login path (`ggui serve --local-cli-login`); never a real key.
100
+ export { CLAUDE_CODE_LOGIN_CREDENTIAL } from './adapters/claude/claude-code-login.js';
86
101
  export { AESTHETIC_BRIEF_MAX_CHARS, type GenerationProfileInput } from './boilerplate/styling-profile.js';
102
+ export type { VisualEvalAgentSpec } from './adapters/generation-dispatch.js';
87
103
  export { EFFORT_DIALS, EFFORT_TABLE_VERSION, effortDials, type EffortDials } from './effort.js';
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
  /**
package/src/run-check.ts CHANGED
@@ -121,6 +121,8 @@ export async function runCheck(input: RunCheckInput): Promise<CheckResult> {
121
121
  designMode: harness.designMode,
122
122
  // ggui#1117: the canvas the harness was built for, when it has one — never a default.
123
123
  ...(harness.canvas !== undefined ? { canvas: harness.canvas } : {}),
124
+ // ggui#1285: the props the probe and judge rendered with — the fixture-echo check reads them.
125
+ ...(input.fixtureProps !== undefined ? { fixtureProps: input.fixtureProps } : {}),
124
126
  };
125
127
  // The one runner `runAxisChecks` shares (ggui#1046 trace prints here, on the served path).
126
128
  const axisRun = runGatedAxisChecks(check.axisChecks, axisInput);