@ggui-ai/ui-gen 0.7.0 → 0.9.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 (52) hide show
  1. package/dist/adapters/generation-dispatch.d.ts +10 -0
  2. package/dist/adapters/generation-dispatch.js +172 -1392
  3. package/dist/adapters/generation-dispatch.js.map +1 -1
  4. package/dist/advanced/index.d.ts +1 -1
  5. package/dist/advanced/index.js +191 -1403
  6. package/dist/advanced/index.js.map +1 -1
  7. package/dist/boilerplate.js +3 -2
  8. package/dist/boilerplate.js.map +1 -1
  9. package/dist/compose.js +5 -5
  10. package/dist/compose.js.map +1 -1
  11. package/dist/{create-ui-generator-GX7tvbyH.d.ts → create-ui-generator-B9kV4U9h.d.ts} +13 -0
  12. package/dist/evaluation/axis-checks/index.js +32 -1
  13. package/dist/evaluation/axis-checks/index.js.map +1 -1
  14. package/dist/evaluation/axis-checks/registry.js +32 -1
  15. package/dist/evaluation/axis-checks/registry.js.map +1 -1
  16. package/dist/evaluation/index.js +57 -12
  17. package/dist/evaluation/index.js.map +1 -1
  18. package/dist/fragments/index.js +5 -5
  19. package/dist/fragments/index.js.map +1 -1
  20. package/dist/harness/index.d.ts +4 -125
  21. package/dist/harness/index.js +34 -19
  22. package/dist/harness/index.js.map +1 -1
  23. package/dist/harness/runtime.d.ts +9 -0
  24. package/dist/harness/runtime.js +3 -2
  25. package/dist/harness/runtime.js.map +1 -1
  26. package/dist/harness/types-public.js +8 -7
  27. package/dist/harness/types-public.js.map +1 -1
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.js +191 -1403
  30. package/dist/index.js.map +1 -1
  31. package/dist/llm-router-1TyI0_TV.d.ts +156 -0
  32. package/dist/providers/index.d.ts +35 -93
  33. package/dist/providers/index.js +13 -8
  34. package/dist/providers/index.js.map +1 -1
  35. package/dist/validation/index.js +2 -2
  36. package/dist/validation/index.js.map +1 -1
  37. package/package.json +18 -18
  38. package/src/adapters/generation-dispatch.ts +19 -2
  39. package/src/boilerplate/system-prompt.ts +3 -2
  40. package/src/create-ui-generator.ts +44 -11
  41. package/src/evaluation/axis-checks/checks/state-merge.ts +1 -1
  42. package/src/evaluation/axis-checks/checks/universal.ts +39 -0
  43. package/src/evaluation/llm-evaluator.ts +13 -3
  44. package/src/evaluation/visual-evaluator.ts +5 -1
  45. package/src/fragments/realtime.ts +3 -3
  46. package/src/fragments/state.ts +2 -2
  47. package/src/harness/coding/init-session.ts +4 -1
  48. package/src/harness/coding/run-eval-round.ts +6 -0
  49. package/src/harness/llm-router.ts +59 -14
  50. package/src/harness/runtime.ts +8 -0
  51. package/src/providers/bedrock.ts +84 -22
  52. package/src/validation/ui-compiler.ts +2 -2
@@ -345,11 +345,11 @@ var stateFragments = {
345
345
  axis: "state",
346
346
  value: "merge",
347
347
  cacheTier: "axisDelta",
348
- promptText: "## State: merge (live entity reconciliation)\n1. Seed `const [items, setItems] = useState(props.items ?? [])`.\n2. On stream updates: `setItems(prev => prev.map(it => it.id === event.id ? { ...it, ...event } : it))` \u2014 merge by id, do NOT append.\n3. Memoize derived views (grouping/sorting/filtering) with useMemo.\n4. Per-item actions pass `{ id, ... }` in the payload.\n5. Never push the stream payload into an append-only list unless realtime=append.",
348
+ promptText: "## State: merge (live entity reconciliation)\n1. Seed `const [items, setItems] = useState(props.items ?? [])` AND re-seed on prop change: `useEffect(() => setItems(props.items ?? []), [props.items])` \u2014 a `ggui_amend` repaint must move the list.\n2. On stream updates: `setItems(prev => prev.map(it => it.id === event.id ? { ...it, ...event } : it))` \u2014 merge by id, do NOT append.\n3. Memoize derived views (grouping/sorting/filtering) with useMemo.\n4. Per-item actions pass `{ id, ... }` in the payload.\n5. Never push the stream payload into an append-only list unless realtime=append.",
349
349
  boilerplateMarker: [
350
350
  "",
351
351
  " // \u2500\u2500 Live entity state (merge-by-id) \u2500\u2500",
352
- " // useState(props.items ?? []); merge stream events by item.id; never append.",
352
+ " // useState(props.items ?? []); re-seed via useEffect on [props.items]; merge stream events by item.id; never append.",
353
353
  ""
354
354
  ].join("\n")
355
355
  },
@@ -467,13 +467,13 @@ var realtimeFragments = {
467
467
  axis: "realtime",
468
468
  value: "merge",
469
469
  cacheTier: "axisDelta",
470
- promptText: "## Realtime: merge\nStream events carry an id; merge into LOCAL STATE. Subscribing alone is not enough \u2014 the local list must update on each event for the DOM to re-render.\n\n```tsx\nconst stream = useStream<UpdateT>('streamName');\nconst [items, setItems] = useState(props.items);\nuseEffect(() => {\n if (!stream.latest) return;\n setItems((prev) => prev.map((it) => it.id === stream.latest!.id ? { ...it, ...stream.latest } : it));\n}, [stream.latest]);\n// Render `items.map(...)` \u2014 NOT props.items, NOT stream.all.\n```\n\nNever append \u2014 stream=merge means the entity already exists locally."
470
+ promptText: "## Realtime: merge\nStream events carry an id; merge into LOCAL STATE. Subscribing alone is not enough \u2014 the local list must update on each event for the DOM to re-render.\n\n```tsx\nconst stream = useStream<UpdateT>('streamName');\nconst [items, setItems] = useState(props.items);\nuseEffect(() => setItems(props.items), [props.items]); // re-seed on ggui_amend repaint \u2014 props stay the source of truth\nuseEffect(() => {\n if (!stream.latest) return;\n setItems((prev) => prev.map((it) => it.id === stream.latest!.id ? { ...it, ...stream.latest } : it));\n}, [stream.latest]);\n// Render `items.map(...)` \u2014 NOT props.items, NOT stream.all.\n```\n\nNever append \u2014 stream=merge means the entity already exists locally."
471
471
  },
472
472
  append: {
473
473
  axis: "realtime",
474
474
  value: "append",
475
475
  cacheTier: "axisDelta",
476
- promptText: "## Realtime: append\nStream events are new entities; APPEND to local state. Subscribing alone is not enough \u2014 the local list must update for the DOM to re-render.\n\n```tsx\nconst stream = useStream<EventT>('streamName');\nconst [items, setItems] = useState(props.items);\nuseEffect(() => {\n if (!stream.latest) return;\n setItems((prev) => [...prev, stream.latest!]);\n}, [stream.latest]);\n// Render `items.map(...)` \u2014 NOT props.items, NOT stream.all (which accumulates only post-mount).\n```\n\nUse head for newest-first, tail for chat-style. Cap list length if needed. Do not dedupe unless the contract guarantees at-most-once."
476
+ promptText: "## Realtime: append\nStream events are new entities; APPEND to local state. Subscribing alone is not enough \u2014 the local list must update for the DOM to re-render.\n\n```tsx\nconst stream = useStream<EventT>('streamName');\nconst [items, setItems] = useState(props.items);\nuseEffect(() => setItems(props.items), [props.items]); // re-seed on ggui_amend repaint \u2014 props stay the source of truth\nuseEffect(() => {\n if (!stream.latest) return;\n setItems((prev) => [...prev, stream.latest!]);\n}, [stream.latest]);\n// Render `items.map(...)` \u2014 NOT props.items, NOT stream.all (which accumulates only post-mount).\n```\n\nUse head for newest-first, tail for chat-style. Cap list length if needed. Do not dedupe unless the contract guarantees at-most-once."
477
477
  },
478
478
  status: {
479
479
  axis: "realtime",
@@ -491,7 +491,7 @@ var realtimeFragments = {
491
491
  axis: "realtime",
492
492
  value: "mixed",
493
493
  cacheTier: "axisDelta",
494
- promptText: "## Realtime: mixed\nMultiple stream channels with DIFFERENT semantics. Each `streamKinds` entry in the contract maps to one of: merge, append, status, presence. Subscribe + sync to local state PER CHANNEL \u2014 subscribing alone never re-renders the DOM.\n\nExample (chat-interface \u2014 message=append, typing=presence):\n\n```tsx\nconst messages = useStream<MessageT>('message');\nconst typing = useStream<TypingT>('typing');\nconst [messageList, setMessageList] = useState(props.messages);\nconst [typingUsers, setTypingUsers] = useState<Set<string>>(new Set());\n\nuseEffect(() => {\n if (!messages.latest) return;\n setMessageList((prev) => [...prev, messages.latest!]);\n}, [messages.latest]);\n\nuseEffect(() => {\n if (!typing.latest) return;\n setTypingUsers((prev) => {\n const next = new Set(prev);\n if (typing.latest!.active) next.add(typing.latest!.sender);\n else next.delete(typing.latest!.sender);\n return next;\n });\n}, [typing.latest]);\n```\n\nDo NOT treat all events the same. Do NOT skip the local-state sync \u2014 `useStream` alone won't trigger DOM updates of your derived view.",
494
+ promptText: "## Realtime: mixed\nMultiple stream channels with DIFFERENT semantics. Each `streamKinds` entry in the contract maps to one of: merge, append, status, presence. Subscribe + sync to local state PER CHANNEL \u2014 subscribing alone never re-renders the DOM.\n\nExample (chat-interface \u2014 message=append, typing=presence):\n\n```tsx\nconst messages = useStream<MessageT>('message');\nconst typing = useStream<TypingT>('typing');\nconst [messageList, setMessageList] = useState(props.messages);\nconst [typingUsers, setTypingUsers] = useState<Set<string>>(new Set());\n\nuseEffect(() => setMessageList(props.messages), [props.messages]); // re-seed on ggui_amend repaint\n\nuseEffect(() => {\n if (!messages.latest) return;\n setMessageList((prev) => [...prev, messages.latest!]);\n}, [messages.latest]);\n\nuseEffect(() => {\n if (!typing.latest) return;\n setTypingUsers((prev) => {\n const next = new Set(prev);\n if (typing.latest!.active) next.add(typing.latest!.sender);\n else next.delete(typing.latest!.sender);\n return next;\n });\n}, [typing.latest]);\n```\n\nDo NOT treat all events the same. Do NOT skip the local-state sync \u2014 `useStream` alone won't trigger DOM updates of your derived view.",
495
495
  boilerplateMarker: [
496
496
  "",
497
497
  " // \u2500\u2500 Mixed stream handlers \u2500\u2500",
@@ -1613,13 +1613,14 @@ A \`DataContract\` declares everything a GguiSession exchanges with the outside
1613
1613
 
1614
1614
  ## Defensive coding for absent / late-arriving data
1615
1615
 
1616
- Props arrive via \`ggui_update\` and may be partial on first render. Stream channels start empty and fill over time. Context slots start at their declared default (often \`null\`). **Never assume a field exists before you read it.**
1616
+ Props arrive at mount and may be partial on first render; later prop changes reach the SAME mounted component via \`ggui_amend\` (in-place repaint) on the live channel. Stream channels start empty and fill over time. Context slots start at their declared default (often \`null\`). **Never assume a field exists before you read it.**
1617
1617
 
1618
1618
  - **Array iteration**: always default to \`[]\` before \`.map\`/\`.filter\`/\`.length\`. Use \`(props.items ?? []).map(...)\` not \`props.items.map(...)\`. Same for stream.all, stream.latest, etc.
1619
1619
  - **Object access**: optional-chain through nested fields. \`props.user?.name ?? 'Anonymous'\` not \`props.user.name\`.
1620
1620
  - **Number ops**: default before arithmetic. \`(props.count ?? 0) + 1\` not \`props.count + 1\`.
1621
1621
  - **Stream latest**: \`useStream\` returns \`{latest: T | null, all: T[], isComplete: boolean}\`. The default \`all\` is \`[]\` so it's safe to map; \`latest\` is null until the first frame arrives \u2014 guard before reading \`.foo\`.
1622
1622
  - **Stream reconciliation**: when a stream event carries an \`action\` discriminant (e.g. \`create | move | edit | delete\`), the channel is a CRUD feed \u2014 your handler MUST branch on EVERY value: append on \`create\`, drop on \`delete\`, replace-by-id on \`move\` / \`edit\`. Merging only the "edit" case silently loses created and deleted items. Reconcile into the SAME state that seeds from \`props\` (e.g. \`useState(() => props.tasks ?? [])\`) so the seed data and the live feed render as one list \u2014 and handle an event whose id is not yet present (a \`create\` for an unknown item) by inserting it, not ignoring it.
1623
+ - **Props re-sync on repaint**: props the agent owns are the source of truth for display \u2014 prefer rendering DIRECTLY from \`props\` (controlled, no local copy). When local optimistic state genuinely needs a prop seed (a click bumps a count before the agent confirms), it MUST re-sync on prop change \u2014 \`useEffect(() => setCount(props.count ?? 0), [props.count])\` \u2014 or a \`ggui_amend\` repaint applies the new props while the display keeps the stale local value. BAD: \`const [count, setCount] = useState(props.count ?? 0)\` alone. GOOD: the same seed + the one-line re-sync effect.
1623
1624
  - **Loading state**: while data is still absent, render \`<Skeleton>\` placeholders \u2014 never a blank screen. \`<Skeleton variant="text" />\` for a text line, \`variant="circle"\` for an avatar slot, default \`rect\` for a block.
1624
1625
  - **Empty state**: when a list or results array is empty, render \`<EmptyState title="\u2026" description="\u2026" />\` \u2014 a region that renders nothing when empty looks broken to the user.
1625
1626
 
@@ -1747,7 +1748,7 @@ Every value change is mirrored to the host LLM's context automatically (debounce
1747
1748
 
1748
1749
  **When NOT to use it.** Local UI state the contract did NOT declare \u2014 \`isDropdownOpen\`, hover flags, animation phase, ephemeral toggles. For those, use a plain \`useState\` directly. The runtime ignores undeclared state.
1749
1750
 
1750
- **\`contextSpec\` direction is one-way: client \u2192 agent.** The agent uses \`propsSpec\` (via \`ggui_update\`) and \`streamSpec\` (via the live channel) to push state TO the client. Don't try to write to the agent via \`contextSpec\` \u2014 there is no return path.
1751
+ **\`contextSpec\` direction is one-way: client \u2192 agent.** The agent uses \`propsSpec\` (via \`ggui_amend\` for in-place repaints of this mount, or \`ggui_update\` when it mints a new history card) and \`streamSpec\` (via the live channel) to push state TO the client. Don't try to write to the agent via \`contextSpec\` \u2014 there is no return path.
1751
1752
 
1752
1753
  **Schema mismatches drop silently.** If you set a value that doesn't match the slot's schema (e.g. a string into a \`{type: 'number'}\` slot), the runtime logs a dev \`console.warn\` and skips the post. Make sure your setter calls produce values that match the declared shape.
1753
1754