@ggui-ai/ui-gen 0.24.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 (142) hide show
  1. package/README.md +2 -2
  2. package/dist/{adapter-Du7P_5Fx.d.ts → adapter-BJwn74rx.d.ts} +49 -1
  3. package/dist/adapters/generation-dispatch.d.ts +32 -7
  4. package/dist/adapters/generation-dispatch.js +1997 -216
  5. package/dist/adapters/generation-dispatch.js.map +1 -1
  6. package/dist/adapters/index.js +29 -6
  7. package/dist/adapters/index.js.map +1 -1
  8. package/dist/advanced/index.d.ts +15 -12
  9. package/dist/advanced/index.js +2013 -270
  10. package/dist/advanced/index.js.map +1 -1
  11. package/dist/{axes-CzLEMDeB.d.ts → axes-CKVCzR_x.d.ts} +1 -1
  12. package/dist/bank-Ctdp3FUA.d.ts +118 -0
  13. package/dist/blueprint-validator.js +416 -92
  14. package/dist/blueprint-validator.js.map +1 -1
  15. package/dist/boilerplate.d.ts +2 -2
  16. package/dist/boilerplate.js +34 -15
  17. package/dist/boilerplate.js.map +1 -1
  18. package/dist/check/index.d.ts +13 -5
  19. package/dist/check/index.js +56 -3
  20. package/dist/check/index.js.map +1 -1
  21. package/dist/classifier/index.d.ts +2 -2
  22. package/dist/compose.d.ts +2 -2
  23. package/dist/{create-ui-generator-GIhzx08x.d.ts → create-ui-generator-N4gRXpcL.d.ts} +11 -4
  24. package/dist/{design-mode-DLufhrNp.d.ts → design-mode-DGWN_Gkm.d.ts} +1 -1
  25. package/dist/{dispatch-BnAH56p4.d.ts → dispatch-BUoXRj_r.d.ts} +6 -4
  26. package/dist/evaluation/axis-checks/index.d.ts +7 -6
  27. package/dist/evaluation/axis-checks/index.js +90 -2
  28. package/dist/evaluation/axis-checks/index.js.map +1 -1
  29. package/dist/evaluation/axis-checks/registry.d.ts +6 -5
  30. package/dist/evaluation/axis-checks/registry.js +88 -1
  31. package/dist/evaluation/axis-checks/registry.js.map +1 -1
  32. package/dist/evaluation/index.d.ts +189 -344
  33. package/dist/evaluation/index.js +1272 -59
  34. package/dist/evaluation/index.js.map +1 -1
  35. package/dist/evaluation/types.d.ts +26 -1
  36. package/dist/evaluation/types.js.map +1 -1
  37. package/dist/fragments/index.d.ts +3 -3
  38. package/dist/{generate-9IeNT4ZY.d.ts → generate-tSO_shQ0.d.ts} +1 -1
  39. package/dist/harness/check/runtime-render/index.d.ts +7 -7
  40. package/dist/harness/check/runtime-render/index.js +416 -92
  41. package/dist/harness/check/runtime-render/index.js.map +1 -1
  42. package/dist/harness/check/runtime-render/render-check-worker.js +393 -88
  43. package/dist/harness/check/runtime-render/render-check-worker.js.map +1 -1
  44. package/dist/harness/index.d.ts +5 -6
  45. package/dist/harness/index.js +69 -20
  46. package/dist/harness/index.js.map +1 -1
  47. package/dist/harness/prompts.d.ts +2 -2
  48. package/dist/harness/prompts.js +10 -1
  49. package/dist/harness/prompts.js.map +1 -1
  50. package/dist/harness/result-types.d.ts +19 -5
  51. package/dist/harness/result-types.js.map +1 -1
  52. package/dist/harness/runtime.d.ts +21 -7
  53. package/dist/harness/runtime.js +36 -17
  54. package/dist/harness/runtime.js.map +1 -1
  55. package/dist/harness/types-public.d.ts +6 -4
  56. package/dist/harness/types-public.js +43 -18
  57. package/dist/harness/types-public.js.map +1 -1
  58. package/dist/harness/validator-trace-sink.d.ts +6 -5
  59. package/dist/hash.d.ts +1 -1
  60. package/dist/index.d.ts +64 -13
  61. package/dist/index.js +2023 -270
  62. package/dist/index.js.map +1 -1
  63. package/dist/patch.d.ts +5 -5
  64. package/dist/policy.d.ts +1 -1
  65. package/dist/{prompts-YU9piOgs.d.ts → prompts-BhNUuRoZ.d.ts} +1 -1
  66. package/dist/tools/get-primitives-ts.d.ts +1 -1
  67. package/dist/tools/get-primitives-ts.js +1 -1
  68. package/dist/tools/get-primitives-ts.js.map +1 -1
  69. package/dist/tools/render-check-worker.js +8 -1
  70. package/dist/tools/render-check-worker.js.map +1 -1
  71. package/dist/{types-BOvHNG7K.d.ts → types-Dh1s4K9j.d.ts} +1 -1
  72. package/dist/types-public-B7wn4ce5.d.ts +959 -0
  73. package/dist/validation/index.d.ts +1 -1
  74. package/dist/validation/index.js +1 -1
  75. package/dist/validation/index.js.map +1 -1
  76. package/dist/visual-evaluator-C9L-o6ap.d.ts +639 -0
  77. package/dist/workflows.d.ts +5 -5
  78. package/package.json +12 -12
  79. package/src/adapters/generation-dispatch.ts +38 -2
  80. package/src/adapters/tools.ts +2 -1
  81. package/src/boilerplate/free-design-prompt.ts +4 -4
  82. package/src/boilerplate/generate.ts +26 -1
  83. package/src/boilerplate/hard-sections.ts +14 -14
  84. package/src/boilerplate/system-prompt.ts +1 -1
  85. package/src/boilerplate/templates/layouts/chat-mobile.tsx.tmpl +3 -1
  86. package/src/boilerplate/templates/layouts/chat-universal.tsx.tmpl +3 -1
  87. package/src/boilerplate/templates/layouts/fullscreen-desktop.tsx.tmpl +3 -1
  88. package/src/boilerplate/templates/layouts/fullscreen-universal.tsx.tmpl +3 -1
  89. package/src/check/index.ts +1 -0
  90. package/src/check/run-tier0.ts +6 -3
  91. package/src/check/sample-props.ts +63 -0
  92. package/src/coding-agent/tools.ts +4 -1
  93. package/src/contract-context.ts +1 -1
  94. package/src/create-harness.ts +3 -3
  95. package/src/create-ui-generator.ts +20 -1
  96. package/src/evaluation/__fixtures__/png.ts +100 -0
  97. package/src/evaluation/axis-checks/checks/state-merge.ts +1 -1
  98. package/src/evaluation/axis-checks/checks/universal.ts +111 -0
  99. package/src/evaluation/axis-checks/dispatch.ts +4 -1
  100. package/src/evaluation/cost-tracker.ts +54 -6
  101. package/src/evaluation/criteria/bank.ts +85 -0
  102. package/src/evaluation/criteria/context.ts +52 -0
  103. package/src/evaluation/criteria/edge-probe.ts +286 -0
  104. package/src/evaluation/criteria/edge.ts +202 -0
  105. package/src/evaluation/criteria/resolve.ts +207 -0
  106. package/src/evaluation/criteria/select.ts +79 -0
  107. package/src/evaluation/index.ts +27 -0
  108. package/src/evaluation/ink-extent.ts +200 -0
  109. package/src/evaluation/judge-spend.ts +48 -0
  110. package/src/evaluation/theme-css.ts +12 -5
  111. package/src/evaluation/types-public.ts +221 -3
  112. package/src/evaluation/types.ts +19 -0
  113. package/src/evaluation/visual-evaluator.ts +853 -51
  114. package/src/harness/check/runtime-render/adapter.ts +18 -0
  115. package/src/harness/check/runtime-render/probe-pending.ts +111 -0
  116. package/src/harness/check/runtime-render/render-check-host.ts +10 -6
  117. package/src/harness/check/runtime-render/render-check-worker.ts +6 -3
  118. package/src/harness/check/runtime-render/render-check.ts +417 -133
  119. package/src/harness/check/runtime-render/walk.ts +200 -0
  120. package/src/harness/coding/assemble-result.ts +2 -0
  121. package/src/harness/coding/generate-task-runner.ts +30 -0
  122. package/src/harness/coding/init-session.ts +5 -3
  123. package/src/harness/coding/run-coding-turn.ts +12 -4
  124. package/src/harness/coding/run-eval-round.ts +82 -3
  125. package/src/harness/in-loop-evaluation.ts +39 -0
  126. package/src/harness/llm-router.ts +58 -1
  127. package/src/harness/result-types.ts +9 -0
  128. package/src/harness/runtime.ts +14 -0
  129. package/src/harness/types-public.ts +10 -1
  130. package/src/harness-tool-names.ts +38 -0
  131. package/src/index.ts +17 -0
  132. package/src/internal/verdict-line.ts +33 -0
  133. package/src/run-check.ts +2 -0
  134. package/src/taught-tokens.ts +32 -0
  135. package/src/tools/get-primitives-ts.ts +1 -1
  136. package/src/tools/get-wire.ts +1 -1
  137. package/src/tools/render-check-worker.ts +2 -1
  138. package/src/tools/render-check.ts +8 -67
  139. package/src/tools.ts +11 -1
  140. package/src/validation/primitives.ts +1 -1
  141. package/dist/llm-router-DwwUK1bz.d.ts +0 -265
  142. package/dist/types-public-Bk6d1WLN.d.ts +0 -451
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Which in-loop evaluation legs a generation runs (ggui#1513).
3
+ *
4
+ * The harness gates its evaluation rounds on this, and nothing else does:
5
+ * the text evaluator runs when `evaluation.enabled`, and the visual judge
6
+ * runs when `visualEvaluation.enabled` or when a quality config asks for it
7
+ * (`qualityConfig.visualEval`). A host that needs to know whether a
8
+ * generation's token counts (`GenerationMetadata`) can include evaluation
9
+ * calls reads it here, with the options it configured the generator with,
10
+ * instead of keeping its own copy of the rule.
11
+ */
12
+ import type { QualityConfig } from "../evaluation/types-public.js";
13
+
14
+ /** The switches that turn an in-loop evaluation leg on. */
15
+ export interface InLoopEvaluationSwitches {
16
+ readonly evaluation?: { readonly enabled: boolean };
17
+ readonly visualEvaluation?: { readonly enabled: boolean };
18
+ readonly qualityConfig?: Pick<QualityConfig, "visualEval">;
19
+ }
20
+
21
+ /** The in-loop evaluation legs these options turn on. */
22
+ export function inLoopEvaluation(options: InLoopEvaluationSwitches): {
23
+ readonly text: boolean;
24
+ readonly visual: boolean;
25
+ } {
26
+ return {
27
+ text: options.evaluation?.enabled === true,
28
+ visual: options.visualEvaluation?.enabled === true || options.qualityConfig?.visualEval === true,
29
+ };
30
+ }
31
+
32
+ /**
33
+ * True when these options turn either leg on: a generation built with them
34
+ * can spend tokens on evaluation calls as well as on its coding turns.
35
+ */
36
+ export function runsInLoopEvaluation(options: InLoopEvaluationSwitches): boolean {
37
+ const legs = inLoopEvaluation(options);
38
+ return legs.text || legs.visual;
39
+ }
@@ -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) */
@@ -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,19 @@ 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;
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";
106
120
  };
107
121
  onProgress?: (event: unknown) => void;
108
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 {
@@ -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
@@ -23,6 +23,16 @@ export type { CreateUiGeneratorOptions } from './create-ui-generator.js';
23
23
  export type { RuntimeRenderProbeConfig } from './harness/check/runtime-render/adapter.js';
24
24
  export type { ProviderRetryInfo } from './harness/llm-router.js';
25
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';
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';
26
36
  export type { UiGenBuild } from './generator-build.js';
27
37
 
28
38
  // Generator registry seam re-export. The interface + slug helpers live
@@ -54,6 +64,12 @@ export {
54
64
  export type { RenderingContext } from './contract-context.js';
55
65
  export { canvasForRenderingContext } from './contract-context.js';
56
66
 
67
+ // ── The coding loop's tool set ───────────────────────────────────────
68
+ //
69
+ // The tools the harness declares, by name — the one list `createHarness`
70
+ // builds from, so a build stamp names the same set that runs.
71
+ export { harnessToolEntry, harnessToolNames, HARNESS_CODING_TOOLS, HARNESS_SCOPED_TOOLS } from './harness-tool-names.js';
72
+
57
73
  // ── Design mode ──────────────────────────────────────────────────────
58
74
  //
59
75
  // `createUiGenerator({ designMode })` — `constrained` (default) or
@@ -88,4 +104,5 @@ export { createAnthropicClient } from './adapters/claude/client.js';
88
104
  // login path (`ggui serve --local-cli-login`); never a real key.
89
105
  export { CLAUDE_CODE_LOGIN_CREDENTIAL } from './adapters/claude/claude-code-login.js';
90
106
  export { AESTHETIC_BRIEF_MAX_CHARS, type GenerationProfileInput } from './boilerplate/styling-profile.js';
107
+ export type { VisualEvalAgentSpec } from './adapters/generation-dispatch.js';
91
108
  export { EFFORT_DIALS, EFFORT_TABLE_VERSION, effortDials, type EffortDials } from './effort.js';
@@ -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
+ }
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);
@@ -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
+ );