@ggui-ai/ui-gen 0.25.0 → 0.26.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 (106) hide show
  1. package/README.md +2 -2
  2. package/dist/{adapter-Cg_rkpGq.d.ts → adapter-BJwn74rx.d.ts} +49 -1
  3. package/dist/adapters/generation-dispatch.d.ts +8 -5
  4. package/dist/adapters/generation-dispatch.js +1118 -193
  5. package/dist/adapters/generation-dispatch.js.map +1 -1
  6. package/dist/adapters/index.js +27 -6
  7. package/dist/adapters/index.js.map +1 -1
  8. package/dist/advanced/index.d.ts +10 -9
  9. package/dist/advanced/index.js +1126 -202
  10. package/dist/advanced/index.js.map +1 -1
  11. package/dist/blueprint-validator.js +416 -92
  12. package/dist/blueprint-validator.js.map +1 -1
  13. package/dist/boilerplate.d.ts +2 -2
  14. package/dist/boilerplate.js +10 -3
  15. package/dist/boilerplate.js.map +1 -1
  16. package/dist/check/index.d.ts +3 -2
  17. package/dist/check/index.js +9 -2
  18. package/dist/check/index.js.map +1 -1
  19. package/dist/{create-ui-generator-DFnnArrv.d.ts → create-ui-generator-N4gRXpcL.d.ts} +3 -4
  20. package/dist/{design-mode-DLufhrNp.d.ts → design-mode-DGWN_Gkm.d.ts} +1 -1
  21. package/dist/{dispatch-CStl9eAw.d.ts → dispatch-BUoXRj_r.d.ts} +2 -2
  22. package/dist/evaluation/axis-checks/index.d.ts +5 -4
  23. package/dist/evaluation/axis-checks/index.js.map +1 -1
  24. package/dist/evaluation/axis-checks/registry.d.ts +4 -3
  25. package/dist/evaluation/index.d.ts +107 -539
  26. package/dist/evaluation/index.js +552 -55
  27. package/dist/evaluation/index.js.map +1 -1
  28. package/dist/evaluation/types.d.ts +17 -0
  29. package/dist/evaluation/types.js.map +1 -1
  30. package/dist/{generate-9IeNT4ZY.d.ts → generate-tSO_shQ0.d.ts} +1 -1
  31. package/dist/harness/check/runtime-render/index.d.ts +5 -5
  32. package/dist/harness/check/runtime-render/index.js +416 -92
  33. package/dist/harness/check/runtime-render/index.js.map +1 -1
  34. package/dist/harness/check/runtime-render/render-check-worker.js +393 -88
  35. package/dist/harness/check/runtime-render/render-check-worker.js.map +1 -1
  36. package/dist/harness/index.d.ts +4 -5
  37. package/dist/harness/index.js +36 -5
  38. package/dist/harness/index.js.map +1 -1
  39. package/dist/harness/prompts.d.ts +2 -2
  40. package/dist/harness/prompts.js +10 -1
  41. package/dist/harness/prompts.js.map +1 -1
  42. package/dist/harness/result-types.d.ts +17 -3
  43. package/dist/harness/result-types.js.map +1 -1
  44. package/dist/harness/runtime.d.ts +14 -6
  45. package/dist/harness/runtime.js +11 -4
  46. package/dist/harness/runtime.js.map +1 -1
  47. package/dist/harness/types-public.d.ts +4 -2
  48. package/dist/harness/types-public.js +10 -3
  49. package/dist/harness/types-public.js.map +1 -1
  50. package/dist/harness/validator-trace-sink.d.ts +4 -3
  51. package/dist/index.d.ts +46 -11
  52. package/dist/index.js +1128 -200
  53. package/dist/index.js.map +1 -1
  54. package/dist/patch.d.ts +3 -3
  55. package/dist/{prompts-YU9piOgs.d.ts → prompts-BhNUuRoZ.d.ts} +1 -1
  56. package/dist/tools/render-check-worker.js +8 -1
  57. package/dist/tools/render-check-worker.js.map +1 -1
  58. package/dist/{types-public-BJTLpzWn.d.ts → types-public-B7wn4ce5.d.ts} +384 -3
  59. package/dist/validation/index.d.ts +1 -1
  60. package/dist/validation/index.js +1 -1
  61. package/dist/validation/index.js.map +1 -1
  62. package/dist/visual-evaluator-C9L-o6ap.d.ts +639 -0
  63. package/dist/workflows.d.ts +3 -3
  64. package/package.json +12 -12
  65. package/src/adapters/generation-dispatch.ts +2 -0
  66. package/src/boilerplate/free-design-prompt.ts +3 -3
  67. package/src/boilerplate/hard-sections.ts +1 -1
  68. package/src/boilerplate/templates/layouts/chat-mobile.tsx.tmpl +3 -1
  69. package/src/boilerplate/templates/layouts/chat-universal.tsx.tmpl +3 -1
  70. package/src/boilerplate/templates/layouts/fullscreen-desktop.tsx.tmpl +3 -1
  71. package/src/boilerplate/templates/layouts/fullscreen-universal.tsx.tmpl +3 -1
  72. package/src/check/run-tier0.ts +6 -3
  73. package/src/contract-context.ts +1 -1
  74. package/src/evaluation/cost-tracker.ts +54 -6
  75. package/src/evaluation/criteria/edge-probe.ts +286 -0
  76. package/src/evaluation/criteria/edge.ts +202 -0
  77. package/src/evaluation/criteria/resolve.ts +55 -7
  78. package/src/evaluation/index.ts +5 -1
  79. package/src/evaluation/judge-spend.ts +48 -0
  80. package/src/evaluation/types-public.ts +91 -0
  81. package/src/evaluation/types.ts +10 -0
  82. package/src/evaluation/visual-evaluator.ts +236 -40
  83. package/src/harness/check/runtime-render/adapter.ts +18 -0
  84. package/src/harness/check/runtime-render/probe-pending.ts +111 -0
  85. package/src/harness/check/runtime-render/render-check-host.ts +10 -6
  86. package/src/harness/check/runtime-render/render-check-worker.ts +6 -3
  87. package/src/harness/check/runtime-render/render-check.ts +417 -133
  88. package/src/harness/check/runtime-render/walk.ts +200 -0
  89. package/src/harness/coding/assemble-result.ts +2 -0
  90. package/src/harness/coding/generate-task-runner.ts +27 -0
  91. package/src/harness/coding/init-session.ts +5 -3
  92. package/src/harness/coding/run-coding-turn.ts +5 -1
  93. package/src/harness/coding/run-eval-round.ts +63 -3
  94. package/src/harness/in-loop-evaluation.ts +39 -0
  95. package/src/harness/llm-router.ts +58 -1
  96. package/src/harness/result-types.ts +9 -0
  97. package/src/harness/runtime.ts +9 -0
  98. package/src/harness/types-public.ts +10 -1
  99. package/src/index.ts +5 -0
  100. package/src/internal/verdict-line.ts +33 -0
  101. package/src/taught-tokens.ts +32 -0
  102. package/src/tools/render-check-worker.ts +2 -1
  103. package/src/tools/render-check.ts +8 -7
  104. package/src/validation/primitives.ts +1 -1
  105. package/dist/llm-router-nfO7wPec.d.ts +0 -267
  106. package/dist/{bank-CT4eEkta.d.ts → bank-Ctdp3FUA.d.ts} +3 -3
@@ -161,6 +161,50 @@ export interface LLMResponse {
161
161
  * requested".
162
162
  */
163
163
  sampling?: AppliedSampling;
164
+ /**
165
+ * Why the provider stopped, normalized (ggui#1127): `length` = it hit the output cap, `stop` = it ended its
166
+ * answer, `content-filter` = a safety stop, `other` = anything else it reported. Set by the vision calls of
167
+ * providers that report one (Anthropic `stop_reason`, Google `finishReason`); absent when the provider or route
168
+ * said nothing — read absence as unknown, never as `stop`.
169
+ */
170
+ finishReason?: VisionFinishReason;
171
+ /**
172
+ * The model that answered, in the provider's own words (ggui#1687): Anthropic's `model`, Google's `modelVersion`.
173
+ * The request names an alias or a resolved id; this is what the provider says served it. Absent when it said
174
+ * nothing — read absence as unknown, never as the requested model.
175
+ */
176
+ servedModel?: string;
177
+ }
178
+
179
+ /** The `servedModel` field to spread into an {@link LLMResponse}: present only when the provider named one. */
180
+ export function servedModelOf(name: string | null | undefined): { servedModel?: string } {
181
+ return typeof name === 'string' && name.length > 0 ? { servedModel: name } : {};
182
+ }
183
+
184
+ /** The normalized stop reason a vision call reports (ggui#1127). */
185
+ export type VisionFinishReason = 'stop' | 'length' | 'content-filter' | 'other';
186
+
187
+ /** Anthropic's `stop_reason`, normalized; absent stays absent. */
188
+ export function anthropicFinishReason(stopReason: string | null | undefined): VisionFinishReason | undefined {
189
+ if (stopReason === null || stopReason === undefined) return undefined;
190
+ if (stopReason === 'end_turn' || stopReason === 'stop_sequence') return 'stop';
191
+ if (stopReason === 'max_tokens') return 'length';
192
+ if (stopReason === 'refusal') return 'content-filter';
193
+ return 'other';
194
+ }
195
+
196
+ /** Gemini's `finishReason`, normalized; absent stays absent. */
197
+ export function googleFinishReason(finishReason: string | null | undefined): VisionFinishReason | undefined {
198
+ if (finishReason === null || finishReason === undefined) return undefined;
199
+ if (finishReason === 'STOP') return 'stop';
200
+ if (finishReason === 'MAX_TOKENS') return 'length';
201
+ if (['SAFETY', 'RECITATION', 'BLOCKLIST', 'PROHIBITED_CONTENT', 'SPII', 'IMAGE_SAFETY'].includes(finishReason)) return 'content-filter';
202
+ return 'other';
203
+ }
204
+
205
+ /** The `finishReason` field to spread into an {@link LLMResponse}: present only when the provider said. */
206
+ function visionFinish(finishReason: VisionFinishReason | undefined): { finishReason?: VisionFinishReason } {
207
+ return finishReason !== undefined ? { finishReason } : {};
164
208
  }
165
209
 
166
210
  /**
@@ -806,6 +850,8 @@ export class AnthropicAgent extends LLMAgent {
806
850
  text,
807
851
  inputTokens: response.usage.input_tokens,
808
852
  outputTokens: response.usage.output_tokens,
853
+ ...visionFinish(anthropicFinishReason(response.stop_reason)),
854
+ ...servedModelOf(response.model),
809
855
  };
810
856
  } catch (e) {
811
857
  const endedAt = Date.now();
@@ -1575,6 +1621,15 @@ export class OpenAIAgent extends LLMAgent {
1575
1621
  }
1576
1622
 
1577
1623
  // =============================================================================
1624
+ /**
1625
+ * `Interactions.GenerationConfig` as the Interactions API accepts it (ggui#1579). The SDK's type (@google/genai 2.15.0)
1626
+ * omits `temperature`, but the API takes `generation_config.temperature` and forwards it to the request's own: it
1627
+ * range-checks the value (2.5 is refused with "GenerateContentRequest.generation_config.temperature: temperature must be
1628
+ * in the range [0.0, 2.0]", 0 is accepted), while a key the API does not know is refused as "Unknown parameter". The
1629
+ * field is declared here so the call is built against a type that carries it, not left to a spread of an undeclared key.
1630
+ */
1631
+ type InteractionsGenerationConfig = Interactions.GenerationConfig & { readonly temperature?: number };
1632
+
1578
1633
  // GoogleAgent — uses Interactions API (server-side state, automatic caching)
1579
1634
  // =============================================================================
1580
1635
 
@@ -1666,7 +1721,7 @@ export class GoogleAgent extends LLMAgent {
1666
1721
  ): Promise<LLMResponse> {
1667
1722
  const client = await this.getClient<GoogleGenAI>();
1668
1723
 
1669
- const generationConfig: Interactions.GenerationConfig = {
1724
+ const generationConfig: InteractionsGenerationConfig = {
1670
1725
  ...(maxTokens ? { max_output_tokens: maxTokens } : {}),
1671
1726
  ...(temperature !== undefined ? { temperature } : {}),
1672
1727
  };
@@ -1730,6 +1785,8 @@ export class GoogleAgent extends LLMAgent {
1730
1785
  // ggui#1186 (third site): `promptTokenCount` includes `cachedContentTokenCount` — report the non-cached input.
1731
1786
  inputTokens: splitCacheInclusiveUsage(usage?.promptTokenCount ?? 0, usage?.candidatesTokenCount ?? 0, usage?.cachedContentTokenCount ?? 0).tokens.input,
1732
1787
  outputTokens: usage?.candidatesTokenCount ?? 0,
1788
+ ...visionFinish(googleFinishReason(response.candidates?.[0]?.finishReason)),
1789
+ ...servedModelOf(response.modelVersion),
1733
1790
  };
1734
1791
 
1735
1792
  const endedAt = Date.now();
@@ -13,6 +13,7 @@ import type { DataContract, JsonObject } from "@ggui-ai/protocol";
13
13
  import type { AdapterResult } from "../adapters/types.js";
14
14
  import type { RenderingContext } from "../contract-context.js";
15
15
  import type { EvalResult } from "../evaluation/types-public.js";
16
+ import type { VisualLegTokens } from "../evaluation/visual-evaluator.js";
16
17
  import type { CanvasClass, DesignMode } from "../design-mode.js";
17
18
 
18
19
  // =============================================================================
@@ -66,6 +67,14 @@ export interface GenerationResult extends AdapterResult {
66
67
  passesUsed: number;
67
68
  /** Three-tier evaluation result (tier 0 + LLM tier 1+2 + visual) */
68
69
  evalResult?: EvalResult;
70
+ /**
71
+ * ggui#1522 — what the IN-LOOP visual judge spent during this generation (its scoring calls, and its report-only
72
+ * criteria calls apart), priced at the visual agent's model. NOT inside `tokens`, which covers the coding turns
73
+ * and the text evaluator. And NOT the same calls as a post-generation judge's own visual pass (a benchmark
74
+ * report's `meta.costs.visualUsd`): a cost rule that adds both is correct, one read as the other double-counts.
75
+ * Absent when no in-loop visual leg ran or it reported no tokens.
76
+ */
77
+ inLoopVisualTokens?: VisualLegTokens;
69
78
  /** Set only when the same-exchange guard ended the run (ggui#404); no key otherwise. */
70
79
  sameExchangeBreak?: SameExchangeBreak;
71
80
  /** Whether background improvement should be spawned (auto-improve mode) */
@@ -108,6 +108,15 @@ export interface SingleComponentParams {
108
108
  criteriaBank?: CriteriaBank;
109
109
  /** ggui#1436 — the caller's item kind for the selector (a bootstrap mint's `hello`); absent = not gated by kind. */
110
110
  criteriaKind?: string;
111
+ /**
112
+ * ggui#1542 — which of the VISUAL leg's own findings may start another eval round when the quality mode is `fast`.
113
+ * `'critical'` (the default, today's behaviour): only a critical finding, which the harness makes a blocking fail.
114
+ * `'major'`: the visual JUDGE's major findings (tier-2 warns) too. The fit measurement's own warn-level overflow is
115
+ * not among them (fit runs with or without a judge, so it is its own feedback source), and neither are the text
116
+ * evaluator's warns. The other quality modes already act on every warn, so the setting changes nothing there. The
117
+ * fix turn is fed as any continuing round is (fails first, then warns).
118
+ */
119
+ actOn?: "critical" | "major";
111
120
  };
112
121
  onProgress?: (event: unknown) => void;
113
122
  onInitialResult?: (result: {
@@ -21,7 +21,14 @@
21
21
  import type { GenerationProfileInput } from "../boilerplate/styling-profile.js";
22
22
  import type { GadgetDescriptor, DataContract, JsonValue } from "@ggui-ai/protocol";
23
23
  import type { Classification } from "../classifier/axes.js";
24
- import type { EvalIssue, EvalTier, AxisCheck, ProbeHostLoad, RuntimeProbeStatus } from "../evaluation/types-public.js";
24
+ import type {
25
+ EvalIssue,
26
+ EvalTier,
27
+ AxisCheck,
28
+ ProbeHostLoad,
29
+ ProbePendingAffordance,
30
+ RuntimeProbeStatus,
31
+ } from "../evaluation/types-public.js";
25
32
  import type { LLMToolDef } from "../llm.js";
26
33
  import type { CacheTier, HarnessFragment } from "../fragments/index.js";
27
34
  import type { HarnessPolicy, ProcessMode } from "../policy.js";
@@ -160,6 +167,8 @@ export interface RuntimeRenderOutcome {
160
167
  readonly hostLoad?: ProbeHostLoad;
161
168
  /** Time the check waited for a probe slot before it started, ms; present only when > 0 (ggui#1380 C2b). */
162
169
  readonly queuedMs?: number;
170
+ /** `ran` only: the pending-affordance walk (ggui#1398), report-only; absent when no action walk ran. */
171
+ readonly pendingAffordance?: ProbePendingAffordance;
163
172
  }
164
173
 
165
174
  export interface RuntimeRenderCheck {
package/src/index.ts CHANGED
@@ -28,6 +28,11 @@ export type { GenerationResult, SameExchangeBreak } from './harness/result-types
28
28
  // either design mode — so a harness definition can carry both modes' digests
29
29
  // while a run's metadata carries only the mode that ran.
30
30
  export { generatorBuild } from './generator-build.js';
31
+ // ggui#1513 — which in-loop evaluation legs a generation runs: the harness's
32
+ // own gate, for a host that needs to know whether a generation's token
33
+ // counts can include evaluation calls.
34
+ export { inLoopEvaluation, runsInLoopEvaluation } from './harness/in-loop-evaluation.js';
35
+ export type { InLoopEvaluationSwitches } from './harness/in-loop-evaluation.js';
31
36
  export type { UiGenBuild } from './generator-build.js';
32
37
 
33
38
  // Generator registry seam re-export. The interface + slug helpers live
@@ -0,0 +1,33 @@
1
+ /**
2
+ * How an isolated worker that runs generated component code (the
3
+ * runtime-render check's, the render smoke test's) hands its verdict to its
4
+ * host.
5
+ *
6
+ * The worker's stdout is shared with the component: card code runs in the
7
+ * worker's own realm with Node's `console`, so a card that logs even one line
8
+ * would corrupt a verdict read as "the whole of stdout". The verdict
9
+ * therefore goes out on one line carrying {@link VERDICT_PREFIX}, and the
10
+ * host reads the LAST such line, ignoring everything else.
11
+ */
12
+
13
+ /** The prefix of the verdict line. Not a string a card would print by accident. */
14
+ export const VERDICT_PREFIX = '@@ggui-render-check-verdict@@';
15
+
16
+ /** The worker's verdict line, newline-terminated. */
17
+ export function formatVerdictLine(value: unknown): string {
18
+ return `${VERDICT_PREFIX} ${JSON.stringify(value)}\n`;
19
+ }
20
+
21
+ /**
22
+ * The value on the last verdict line in `stdout`, or `undefined` when no
23
+ * line carries the prefix. A prefixed line that is not JSON throws, so the
24
+ * host can report it as such.
25
+ */
26
+ export function parseVerdictLine<Verdict>(stdout: string): Verdict | undefined {
27
+ const lines = stdout.split('\n');
28
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
29
+ const line = lines[i]!;
30
+ if (line.startsWith(`${VERDICT_PREFIX} `)) return JSON.parse(line.slice(VERDICT_PREFIX.length + 1)) as Verdict;
31
+ }
32
+ return undefined;
33
+ }
@@ -0,0 +1,32 @@
1
+ // The consumed-token names the generator may TEACH — the manifest minus the names a card may not
2
+ // reference yet.
3
+ //
4
+ // A card is minted by one release and rendered by whichever runtime serves it: during a rolling
5
+ // deploy, or after a rollback, that runtime can be the previous release, which neither declares
6
+ // nor completes a key derived for the first time in this one. A bare `var(--new-key)` then
7
+ // resolves to nothing — a `border` shorthand drops whole — and the no-fallback rule leaves the
8
+ // card nothing to fall back on. So a new derived key ships in two steps: the renderers that
9
+ // declare and complete it serve first (the design package's own primitives read it behind a
10
+ // fallback), and the release AFTER takes it out of this set, in the same change as the prompt
11
+ // guidance that teaches it.
12
+
13
+ import { consumedTokenManifest } from "@ggui-ai/design/themes";
14
+
15
+ /**
16
+ * Manifest names held back from every surface that teaches the model a name — the free prompt's
17
+ * colour vocabulary and token reference, and the self-check's nearest-name suggestions. The
18
+ * closed-set check still admits them: a card that references one is valid wherever this release
19
+ * renders it.
20
+ *
21
+ * - ggui#1494 — the control edges (`controlOutline`, `controlAccentOutline`): derived and completed
22
+ * from this release; taught from the next.
23
+ */
24
+ export const NOT_YET_TAUGHT_TOKENS: ReadonlySet<string> = new Set([
25
+ "--ggui-color-controlAccentOutline",
26
+ "--ggui-color-controlOutline",
27
+ ]);
28
+
29
+ /** The manifest as the model is taught it. */
30
+ export const taughtTokenManifest: readonly string[] = consumedTokenManifest.filter(
31
+ (token) => !NOT_YET_TAUGHT_TOKENS.has(token),
32
+ );
@@ -43,6 +43,7 @@ import * as esbuild from 'esbuild';
43
43
  import type { JsonObject } from '@ggui-ai/protocol';
44
44
  import { evaluateComponentSource } from '../internal/evaluate-component-source.js';
45
45
  import { hostGlobals } from '../internal/open-record.js';
46
+ import { formatVerdictLine } from '../internal/verdict-line.js';
46
47
 
47
48
  interface WorkerInput {
48
49
  readonly sourceCode: string;
@@ -212,7 +213,7 @@ async function renderOnce(input: WorkerInput): Promise<WorkerOutput> {
212
213
  }
213
214
 
214
215
  function emit(output: WorkerOutput): void {
215
- process.stdout.write(JSON.stringify(output));
216
+ process.stdout.write(formatVerdictLine(output));
216
217
  }
217
218
 
218
219
  async function readAllStdin(): Promise<string> {
@@ -28,6 +28,7 @@ import { existsSync } from 'node:fs';
28
28
  import { createRequire } from 'node:module';
29
29
  import { pathToFileURL, fileURLToPath } from 'node:url';
30
30
  import { runSandboxed } from '@ggui-ai/sandbox';
31
+ import { parseVerdictLine } from '../internal/verdict-line.js';
31
32
  import type {
32
33
  JsonObject,
33
34
  } from '@ggui-ai/protocol';
@@ -168,20 +169,20 @@ export async function tryRender(
168
169
  return `Render error: worker exited ${result.exitCode}${tail ? ` — ${tail}` : ''}`;
169
170
  }
170
171
 
171
- const stdout = result.stdout.trim();
172
- if (stdout.length === 0) {
173
- return 'Render error: worker exited without producing a verdict.';
174
- }
175
-
176
172
  type Verdict = { ok: true } | { ok: false; error: string };
177
- let verdict: Verdict;
173
+ let verdict: Verdict | undefined;
178
174
  try {
179
- verdict = JSON.parse(stdout) as Verdict;
175
+ // The verdict rides its own prefixed line, so a component that logs to
176
+ // stdout while it renders cannot corrupt it.
177
+ verdict = parseVerdictLine<Verdict>(result.stdout);
180
178
  } catch (err) {
181
179
  return `Render error: malformed worker verdict — ${
182
180
  err instanceof Error ? err.message : String(err)
183
181
  }`;
184
182
  }
183
+ if (verdict === undefined) {
184
+ return 'Render error: worker exited without producing a verdict.';
185
+ }
185
186
 
186
187
  return verdict.ok ? null : verdict.error;
187
188
  }