@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
@@ -1,12 +1,12 @@
1
1
  import * as _ggui_ai_protocol from '@ggui-ai/protocol';
2
2
  import { JsonValue, DataContract, JsonObject, GadgetDescriptor } from '@ggui-ai/protocol';
3
3
  import { G as GenerationProfileInput } from '../styling-profile-CxfvV48g.js';
4
- import { C as Classification } from '../axes-CzLEMDeB.js';
5
- import { E as EvalIssue, R as RuntimeProbeStatus, P as ProbeHostLoad, A as AxisCheck, c as EvalTier } from '../types-public-Bk6d1WLN.js';
4
+ import { C as Classification } from '../axes-CKVCzR_x.js';
5
+ import { E as EvalIssue, R as RuntimeProbeStatus, f as ProbeHostLoad, g as ProbePendingAffordance, A as AxisCheck, q as EvalTier } from '../types-public-B7wn4ce5.js';
6
6
  import { LLMToolDef } from '../llm.js';
7
- import { H as HarnessFragment, a as CacheTier } from '../types-BOvHNG7K.js';
7
+ import { H as HarnessFragment, a as CacheTier } from '../types-Dh1s4K9j.js';
8
8
  import { ProcessMode, HarnessPolicy } from '../policy.js';
9
- import { D as DesignMode, C as CanvasClass } from '../design-mode-DLufhrNp.js';
9
+ import { D as DesignMode, C as CanvasClass } from '../design-mode-DGWN_Gkm.js';
10
10
  import '@ggui-ai/mcp-server-core';
11
11
 
12
12
  /** Called by the executor to run a single task. One of these per task.id. */
@@ -251,6 +251,8 @@ interface RuntimeRenderOutcome {
251
251
  readonly hostLoad?: ProbeHostLoad;
252
252
  /** Time the check waited for a probe slot before it started, ms; present only when > 0 (ggui#1380 C2b). */
253
253
  readonly queuedMs?: number;
254
+ /** `ran` only: the pending-affordance walk (ggui#1398), report-only; absent when no action walk ran. */
255
+ readonly pendingAffordance?: ProbePendingAffordance;
254
256
  }
255
257
  interface RuntimeRenderCheck {
256
258
  readonly id: string;
@@ -427,7 +427,9 @@ async function runCheck(input) {
427
427
  classification: harness.classification,
428
428
  designMode: harness.designMode,
429
429
  // ggui#1117: the canvas the harness was built for, when it has one — never a default.
430
- ...harness.canvas !== void 0 ? { canvas: harness.canvas } : {}
430
+ ...harness.canvas !== void 0 ? { canvas: harness.canvas } : {},
431
+ // ggui#1285: the props the probe and judge rendered with — the fixture-echo check reads them.
432
+ ...input.fixtureProps !== void 0 ? { fixtureProps: input.fixtureProps } : {}
431
433
  };
432
434
  const axisRun = runGatedAxisChecks(check.axisChecks, axisInput);
433
435
  issues.push(...axisRun.issues);
@@ -1295,6 +1297,7 @@ function generateBoilerplate(_userPrompt, contract, shellType, screen, composedS
1295
1297
  }
1296
1298
  const actionTypeAliases = [];
1297
1299
  const actionHookCalls = [];
1300
+ let hasOneShotAction = false;
1298
1301
  const actionsMap = contract?.actionSpec ?? {};
1299
1302
  for (const [key, entry] of Object.entries(actionsMap)) {
1300
1303
  const label = entry.label ?? key;
@@ -1315,7 +1318,20 @@ type ${typeName} = ${tsType};`
1315
1318
  const callSig = tsType === "void" ? "() => void \u2014 fire and forget" : `(data: ${tsType}) => void`;
1316
1319
  const toolHint = tool ? ` \u2192 nextStep: ${tool}` : "";
1317
1320
  const onceHint = " \u2014 if this is meant ONCE, disable its control after it fires";
1318
- actionHookCalls.push(` const ${key} = useAction<${typeName}>('${key}'); // ${callSig}${toolHint}${onceHint}`);
1321
+ if (entry.oneShot === true) {
1322
+ hasOneShotAction = true;
1323
+ actionHookCalls.push(
1324
+ ` const ${key} = useAction<${typeName}>('${key}'); // ${callSig}${toolHint} \u2014 declared oneShot: the runtime drops a second dispatch`
1325
+ );
1326
+ actionHookCalls.push(
1327
+ ` const ${key}Spent = useActionSpent('${key}'); // true once fired, also after a reload \u2014 render the control from THIS: disabled={${key}Spent} and a spent label`
1328
+ );
1329
+ } else {
1330
+ actionHookCalls.push(` const ${key} = useAction<${typeName}>('${key}'); // ${callSig}${toolHint}${onceHint}`);
1331
+ }
1332
+ actionHookCalls.push(
1333
+ ` const ${key}Pending = useActionPending('${key}'); // true from the tap until the agent's answer repaints the card \u2014 render EVERY control that dispatches '${key}' (a Button, a Select's onChange, an Input's Enter) from THIS: disabled={${key}Pending} aria-busy={${key}Pending}, and a working label on a button; no other control carries its aria-busy`
1334
+ );
1319
1335
  }
1320
1336
  const streamChannels = contract?.streamSpec ?? {};
1321
1337
  const streamChannelEntries = Object.entries(streamChannels);
@@ -1413,6 +1429,8 @@ ${hookLines.join("\n")}
1413
1429
  const hasAnyWireFromWire = hasActions || hasStream || hasContext;
1414
1430
  const wireHooks = [];
1415
1431
  if (hasActions) wireHooks.push("useAction");
1432
+ if (hasOneShotAction) wireHooks.push("useActionSpent");
1433
+ if (hasActions) wireHooks.push("useActionPending");
1416
1434
  if (hasStream) wireHooks.push("useStream");
1417
1435
  if (hasContext) wireHooks.push("useGguiContext");
1418
1436
  const wireImport = hasAnyWireFromWire ? `import { ${wireHooks.join(", ")} } from '@ggui-ai/wire';
@@ -1463,6 +1481,13 @@ ${body}
1463
1481
  AXIS_SECTIONS: composedSections ?? ""
1464
1482
  }, designMode);
1465
1483
  }
1484
+ var NOT_YET_TAUGHT_TOKENS = /* @__PURE__ */ new Set([
1485
+ "--ggui-color-controlAccentOutline",
1486
+ "--ggui-color-controlOutline"
1487
+ ]);
1488
+ var taughtTokenManifest = consumedTokenManifest.filter(
1489
+ (token) => !NOT_YET_TAUGHT_TOKENS.has(token)
1490
+ );
1466
1491
 
1467
1492
  // src/boilerplate/hard-sections.ts
1468
1493
  var PROTOCOL_NOTES = `## Protocol Notes
@@ -1649,21 +1674,17 @@ Rules:
1649
1674
  2. Layout and styling are universal. Colors, spacing, structure \u2014 these are the reusable part.
1650
1675
  3. Default values come from the current request \u2014 so the component renders correctly standalone.
1651
1676
  4. Copy comes from the props. NEVER invent display text the contract does not carry \u2014 no eyebrow / kicker / overline (a \`<Text caps>\` label), no helper line, no status text, no tagline. **Do NOT add a caps label above the heading** \u2014 not \`<Text caps>LET'S BEGIN</Text>\`, not \`WELCOME\`, not a section kicker like \`GETTING STARTED\` or \`OVERVIEW\` \u2014 UNLESS a prop supplies that exact text (\`<Text caps>{props.section}</Text>\`). When the request asks for a heading and a line, render the heading and the line and NOTHING above the heading. A heading's fallback is the request's own words, never a slogan of your own. Text the agent cannot change is chatter on every render, and a caps eyebrow you wrote is the most common form of it. An action's control shows the contract's \`label\` VERBATIM \u2014 every character, including \`&\`, \`/\`, \`\u2013\` and quotes \u2014 never paraphrased, shortened or re-cased (\`"Confirm & Schedule"\` is not \`"Confirm Schedule"\`).
1652
- 5. Props interface must be typed and exported.`;
1677
+ 5. Props interface must be typed and exported.
1678
+ 6. Keys on a mapped list come from the item's own identity: \`key={item.id}\` when the contract gives an id; when the items carry none (KPI tiles \`{label, value}\`, forecast days \`{day, high}\`), key by the field that names each one \u2014 \`key={item.label}\`, \`key={day.day}\` \u2014 never by the map index: a reorder or a stream merge re-keys by index and re-mounts the wrong rows, and the self-check refuses an index key.`;
1653
1679
  var TERMINAL_ACTIONS = `## Actions the user means once
1654
1680
 
1655
1681
  A second press is a mistake only when it would make a SECOND THING HAPPEN IN THE WORLD \u2014 a second booking, a second charge, a second order, a second application sent. If pressing again would only write the SAME STATE AGAIN \u2014 saving, updating, renaming, setting a preference, toggling \u2014 nothing is duplicated and the control stays armed.
1656
1682
 
1657
- Guard a control only where you have evidence, not a feeling about the verb: the request itself names a one-time commitment \u2014 "submit the application", "place the order", "confirm the booking".
1683
+ **The contract says which actions are once.** An action the contract declares \`oneShot: true\` (in the Contract block above) is guarded by the runtime \u2014 a second dispatch is dropped \u2014 and its spent state is DATA you read, never state you hold: render its control from \`const spent = useActionSpent('<name>')\`, \`disabled={spent}\`, and say so in the label or beside it ("Submitted", "Request sent") \u2014 a control that is disabled without explanation reads as broken. The hook's answer survives a reload and a second tab; \`useState(false)\` does not, so a reloaded card built on local state re-arms a control that already fired.
1658
1684
 
1659
- **When in doubt, leave it armed.** A control the user cannot press a second time has lost them something that worked \u2014 save, edit, save again is ordinary. A control left armed is only what every interface does by default.
1685
+ **An action the contract does not declare \`oneShot\` stays armed.** Never guard one from a feeling about the verb \u2014 "confirm", "submit", "place" alone are not evidence. Guard it only where the request itself names a one-time commitment the contract failed to declare ("submit the application", "place the order"), and then with local state as the fallback: \`const [submitted, setSubmitted] = useState(false)\` set in the handler, \`disabled={submitted}\` with a label \u2014 knowing that this state lives with the component, not with the request, and starts fresh on a re-render with new props.
1660
1686
 
1661
- When you do guard one:
1662
- - Hold a small piece of state for it and set it in the handler: \`const [submitted, setSubmitted] = useState(false)\`.
1663
- - \`disabled={submitted}\` on the control that fires it, and say so in the label or beside it ("Submitted", "Request sent") \u2014 a control that is disabled without explanation reads as broken.
1664
- - A card re-rendered with new props starts fresh; the state above lives with the component, not with the request.
1665
-
1666
- Nothing in the wire DECLARES one-shot-ness yet, which is why the test is what a second press would DO \u2014 not how final the word sounds.`;
1687
+ **When in doubt, leave it armed.** A control the user cannot press a second time has lost them something that worked \u2014 save, edit, save again is ordinary. A control left armed is only what every interface does by default.`;
1667
1688
  var FRAME_SIZING = `## Sizing \u2014 the frame owns the height and the width
1668
1689
 
1669
1690
  The host sizes the iframe; the component never sizes itself to the viewport. NEVER write \`100vh\` / \`100dvh\` / \`100svh\` (or an \`h-screen\` / \`min-h-screen\` class) as a height on any element:
@@ -1676,7 +1697,7 @@ The width works the same way, and the mistake is the mirror image: **your outerm
1676
1697
 
1677
1698
  - Never put \`maxWidth\` (or a \`max-w-*\` class) on the element you return at the top. On a wide canvas that turns your card into a narrow strip with the frame's ground on either side of it, and on a narrow one it does nothing \u2014 the same composition reading differently at two sizes.
1678
1699
  - DO cap the reading measure inside: a column of prose is easiest to read at 60\u201370 characters, so wrap the TEXT in a \`<Container maxWidth="sm">\` (or your own \`max-width\`) and leave the root filling. A hero band, a toolbar, a table or an image then still uses the whole width, which is what makes a wide card look composed rather than padded.
1679
- - Centre that inner column with the container's own centring, not by shrinking the root.
1700
+ - Centre that inner column with the container's own centring, not by shrinking the root \u2014 and give the card ONE column: every block of the reading flow (a heading, its text, and the chips or buttons that answer it) sits in a \`<Container>\` of the same \`maxWidth\`, so their left edges line up even where a full-width band runs between them.
1680
1701
  - **They are two different elements.** Even when the whole card is one column of text, the root still fills and the column sits INSIDE it \u2014 \`<Box padding="lg"><Container maxWidth="sm">\u2026</Container></Box>\`, never a root that is itself the container. A centred card is centring inside a filled frame, not a shrunken frame.`;
1681
1702
  var COMPONENT_STRUCTURE = `## Component Structure
1682
1703
 
@@ -1753,7 +1774,7 @@ function colorVocabularyFromManifest(manifest) {
1753
1774
  function renderRamp(r) {
1754
1775
  return `\`${r.family}-${r.steps.join("/")}\`${r.bare ? ` (plus bare \`${r.family}\`)` : ""}`;
1755
1776
  }
1756
- function renderFreeColorRule(manifest = consumedTokenManifest) {
1777
+ function renderFreeColorRule(manifest = taughtTokenManifest) {
1757
1778
  const { ramps, roles } = colorVocabularyFromManifest(manifest);
1758
1779
  const brand = ramps.filter((r) => r.family === "primary" || r.family === "neutral");
1759
1780
  const state = ramps.filter((r) => r.family !== "primary" && r.family !== "neutral");
@@ -1819,7 +1840,7 @@ With raw elements YOU own the semantics \u2014 nothing is accessible by construc
1819
1840
  3. **Images have \`alt\`** (\`alt=""\` for decorative). Icon-only buttons need \`aria-label\`; decorative icons and emoji are \`aria-hidden="true"\`.
1820
1841
  4. **Live & streaming data** (a \`useStream\` \`.latest\` value, counters, clocks, flashing prices) sits in an \`aria-live="polite"\` (or \`role="status"\`) region so screen readers announce the change.
1821
1842
  5. **Headings nest**: one \`<h1>\` per screen, \`<h2>\` for sections, \`<h3>\` below \u2014 never skipped or inverted.
1822
- 6. **Busy state is announced**: \`<button disabled={isLoading} aria-busy={isLoading}>{isLoading ? 'Submitting\u2026' : 'Submit'}</button>\`.
1843
+ 6. **Busy state is announced**: every control that dispatches an action (a button, a select whose change dispatches, an input that sends on Enter) stays pending from \`useActionPending('<action>')\` until the agent's answer repaints the card \u2014 \`<button disabled={sendPending} aria-busy={sendPending}>{sendPending ? 'Sending\u2026' : 'Send'}</button>\`. Only a control that dispatches the action carries its busy look; while it is pending, other controls of the same record may be disabled \u2014 nothing more (controls of other records, creation controls and the action's own cancel stay enabled); busy state the card owns (\`isLoading\`) the same way.
1823
1844
  7. **Stateful controls announce state**: \`role="checkbox"\` + \`aria-checked\` on a toggleable row, \`aria-pressed\` on a toggle button, \`aria-selected\` on the chosen item, \`aria-expanded\` on an open/close affordance. Styling (strikethrough, color, a check mark) is never the only carrier of state.
1824
1845
  8. **Focus is visible** \u2014 style \`:focus-visible\` with a token color; never \`outline: none\` without a replacement.`;
1825
1846
  var FREE_QUALITY_CHECKLIST = `## Quality Checklist (verify before returning)
@@ -1843,7 +1864,7 @@ var TOKEN_GROUPS = [
1843
1864
  { label: "Radius (optional)", prefixes: ["--ggui-shape-radius-"], note: "" },
1844
1865
  { label: "Shadow (optional)", prefixes: ["--ggui-shape-shadow-"], note: "" }
1845
1866
  ];
1846
- function renderTokenVocabulary(manifest = consumedTokenManifest) {
1867
+ function renderTokenVocabulary(manifest = taughtTokenManifest) {
1847
1868
  const sorted = [...manifest].sort();
1848
1869
  const claimed = /* @__PURE__ */ new Set();
1849
1870
  const lines = [
@@ -2363,7 +2384,7 @@ The design-system primitives are accessible by construction \u2014 they emit the
2363
2384
  3. **Icons are decorative by default** \u2014 \`<Icon name="check" />\` is hidden from screen readers, which is correct for an icon sitting next to text. Add \`aria-label\` ONLY for a standalone, meaning-bearing icon with no adjacent text. Icon-only \`Button\`s still need \`aria-label\` on the **Button** itself.
2364
2385
  4. **Live & streaming data** \u2014 wrap any region whose content updates on its own (a \`useStream\` \`.latest\` value, a live clock, an "N new" counter, a flashing price) in an element with \`aria-live="polite"\` so screen readers announce the change.
2365
2386
  5. **Headings nest** \u2014 one \`<Heading level={1}>\` per screen, \`level={2}\` for sections, \`level={3}\` for subsections. Never skip or invert levels.
2366
- 6. **Buttons** \u2014 descriptive text content; icon-only buttons need \`aria-label\`. Announce busy state: \`<Button disabled={isLoading} aria-busy={isLoading}>{isLoading ? 'Submitting\u2026' : 'Submit'}</Button>\`.
2387
+ 6. **Buttons** \u2014 descriptive text content; icon-only buttons need \`aria-label\`. Announce busy state. EVERY control that dispatches an action \u2014 a Button, a Select whose change dispatches, an Input that sends on Enter \u2014 stays pending from that action's \`useActionPending\` until the agent's answer repaints the card: with \`const sendPending = useActionPending('send');\`, write \`<Button disabled={sendPending} aria-busy={sendPending} onClick={() => send({ text })}>{sendPending ? 'Sending\u2026' : 'Send'}</Button>\`, and give a dispatching Select or Input the same \`disabled={sendPending} aria-busy={sendPending}\`. Only a control that dispatches the action carries its \`aria-busy\` and a working label. While it is pending, other controls of the SAME record may be \`disabled\` \u2014 nothing more; controls of other records, creation controls and the action's own cancel stay enabled. Busy state the card owns (a local \`isLoading\`) is announced the same way.
2367
2388
  7. **Stateful controls announce their state.** Toggleable / selectable / expandable things use the primitive that carries the semantics (\`Checkbox\`, \`Toggle\`, \`Tabs\`, \`Accordion\`) \u2014 never a styled \`Box\`/\`Icon\` that only *looks* checked. When you hand-roll one with \`as={Clickable}\`, \`Clickable\` adds button + keyboard semantics but NOT state \u2014 add it yourself: \`role="checkbox"\` + \`aria-checked={done}\` on a toggleable row, \`aria-pressed\` on a toggle button, \`aria-selected\` on the chosen item, \`aria-expanded\` on an open/close affordance. Styling (strikethrough, color, a check icon) must never be the only carrier of state.
2368
2389
 
2369
2390
  ## Design System Usage (CRITICAL)
@@ -2606,6 +2627,10 @@ var APPLY_CHANGES_TOOL_SCOPED = {
2606
2627
  }
2607
2628
  };
2608
2629
 
2630
+ // src/harness-tool-names.ts
2631
+ var HARNESS_CODING_TOOLS = [APPLY_CHANGES_TOOL];
2632
+ var HARNESS_SCOPED_TOOLS = [APPLY_CHANGES_TOOL_SCOPED];
2633
+
2609
2634
  // src/patch.ts
2610
2635
  function applyLineRanges(sourceBefore, rawChanges) {
2611
2636
  if (rawChanges.length === 0) {
@@ -2810,8 +2835,8 @@ function createHarness(input) {
2810
2835
  const baseWhat = {
2811
2836
  boilerplate,
2812
2837
  fragments: whatFragments,
2813
- codingTools: [APPLY_CHANGES_TOOL],
2814
- scopedTools: [APPLY_CHANGES_TOOL_SCOPED],
2838
+ codingTools: [...HARNESS_CODING_TOOLS],
2839
+ scopedTools: [...HARNESS_SCOPED_TOOLS],
2815
2840
  applyPatch: defaultPatchFn,
2816
2841
  // Registered gadget catalog — drives the system prompt's gadget
2817
2842
  // table + the boilerplate's direct-import emission. Omitted when no