@ggui-ai/ui-gen 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{adapter-Du7P_5Fx.d.ts → adapter-Cg_rkpGq.d.ts} +1 -1
- package/dist/adapters/generation-dispatch.d.ts +27 -5
- package/dist/adapters/generation-dispatch.js +909 -53
- package/dist/adapters/generation-dispatch.js.map +1 -1
- package/dist/adapters/index.js +3 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/advanced/index.d.ts +12 -10
- package/dist/advanced/index.js +917 -98
- package/dist/advanced/index.js.map +1 -1
- package/dist/{axes-CzLEMDeB.d.ts → axes-CKVCzR_x.d.ts} +1 -1
- package/dist/bank-CT4eEkta.d.ts +118 -0
- package/dist/boilerplate.js +24 -12
- package/dist/boilerplate.js.map +1 -1
- package/dist/check/index.d.ts +11 -4
- package/dist/check/index.js +47 -1
- package/dist/check/index.js.map +1 -1
- package/dist/classifier/index.d.ts +2 -2
- package/dist/compose.d.ts +2 -2
- package/dist/{create-ui-generator-GIhzx08x.d.ts → create-ui-generator-DFnnArrv.d.ts} +11 -3
- package/dist/{dispatch-BnAH56p4.d.ts → dispatch-CStl9eAw.d.ts} +5 -3
- package/dist/evaluation/axis-checks/index.d.ts +5 -5
- package/dist/evaluation/axis-checks/index.js +90 -2
- package/dist/evaluation/axis-checks/index.js.map +1 -1
- package/dist/evaluation/axis-checks/registry.d.ts +3 -3
- package/dist/evaluation/axis-checks/registry.js +88 -1
- package/dist/evaluation/axis-checks/registry.js.map +1 -1
- package/dist/evaluation/index.d.ts +300 -23
- package/dist/evaluation/index.js +746 -30
- package/dist/evaluation/index.js.map +1 -1
- package/dist/evaluation/types.d.ts +9 -1
- package/dist/evaluation/types.js.map +1 -1
- package/dist/fragments/index.d.ts +3 -3
- package/dist/harness/check/runtime-render/index.d.ts +5 -5
- package/dist/harness/index.d.ts +4 -4
- package/dist/harness/index.js +33 -15
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/result-types.d.ts +3 -3
- package/dist/harness/runtime.d.ts +10 -4
- package/dist/harness/runtime.js +26 -14
- package/dist/harness/runtime.js.map +1 -1
- package/dist/harness/types-public.d.ts +3 -3
- package/dist/harness/types-public.js +33 -15
- package/dist/harness/types-public.js.map +1 -1
- package/dist/harness/validator-trace-sink.d.ts +3 -3
- package/dist/hash.d.ts +1 -1
- package/dist/index.d.ts +25 -9
- package/dist/index.js +926 -101
- package/dist/index.js.map +1 -1
- package/dist/{llm-router-DwwUK1bz.d.ts → llm-router-nfO7wPec.d.ts} +3 -1
- package/dist/patch.d.ts +3 -3
- package/dist/policy.d.ts +1 -1
- package/dist/tools/get-primitives-ts.d.ts +1 -1
- package/dist/tools/get-primitives-ts.js +1 -1
- package/dist/tools/get-primitives-ts.js.map +1 -1
- package/dist/{types-BOvHNG7K.d.ts → types-Dh1s4K9j.d.ts} +1 -1
- package/dist/{types-public-Bk6d1WLN.d.ts → types-public-BJTLpzWn.d.ts} +134 -7
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +1 -1
- package/dist/validation/index.js.map +1 -1
- package/dist/workflows.d.ts +3 -3
- package/package.json +12 -12
- package/src/adapters/generation-dispatch.ts +36 -2
- package/src/adapters/tools.ts +2 -1
- package/src/boilerplate/free-design-prompt.ts +1 -1
- package/src/boilerplate/generate.ts +26 -1
- package/src/boilerplate/hard-sections.ts +13 -13
- package/src/boilerplate/system-prompt.ts +1 -1
- package/src/check/index.ts +1 -0
- package/src/check/sample-props.ts +63 -0
- package/src/coding-agent/tools.ts +4 -1
- package/src/create-harness.ts +3 -3
- package/src/create-ui-generator.ts +20 -1
- package/src/evaluation/__fixtures__/png.ts +100 -0
- package/src/evaluation/axis-checks/checks/state-merge.ts +1 -1
- package/src/evaluation/axis-checks/checks/universal.ts +111 -0
- package/src/evaluation/axis-checks/dispatch.ts +4 -1
- package/src/evaluation/criteria/bank.ts +85 -0
- package/src/evaluation/criteria/context.ts +52 -0
- package/src/evaluation/criteria/resolve.ts +159 -0
- package/src/evaluation/criteria/select.ts +79 -0
- package/src/evaluation/index.ts +23 -0
- package/src/evaluation/ink-extent.ts +200 -0
- package/src/evaluation/theme-css.ts +12 -5
- package/src/evaluation/types-public.ts +130 -3
- package/src/evaluation/types.ts +9 -0
- package/src/evaluation/visual-evaluator.ts +636 -30
- package/src/harness/coding/generate-task-runner.ts +3 -0
- package/src/harness/coding/run-coding-turn.ts +7 -3
- package/src/harness/coding/run-eval-round.ts +19 -0
- package/src/harness/runtime.ts +5 -0
- package/src/harness-tool-names.ts +38 -0
- package/src/index.ts +12 -0
- package/src/run-check.ts +2 -0
- package/src/tools/get-primitives-ts.ts +1 -1
- package/src/tools/get-wire.ts +1 -1
- package/src/tools/render-check.ts +0 -60
- package/src/tools.ts +11 -1
- package/src/validation/primitives.ts +1 -1
package/src/tools/get-wire.ts
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
* AUTO-GENERATED from @ggui-ai/wire JSDoc.
|
|
3
3
|
* Do not edit manually. Run: pnpm --filter @ggui-ai/wire generate:docs
|
|
4
4
|
*/
|
|
5
|
-
export const WIRE_DOCUMENTATION = "# ggui Wire Hooks Reference\n\n> Wire hooks connect generated UI components to agent communication.\n> They are pre-imported in the boilerplate — use them directly.\n> All hooks must be called inside a GguiWireProvider (handled automatically by the renderer).\n\nImport: `import { useAction, useStream } from '@ggui-ai/wire'`\n\n## Communication Hooks\n\n\n\nThese are the wire primitives for component-agent communication. `useWiredTool` retired 2026-05-11 — agentCapabilities.tools is a catalog the AGENT invokes, not a component hook surface; user gestures use `useAction(name)` and the optional `nextStep` field on the action entry names the tool the agent SHOULD invoke next.\n\n### useAction\n\nFire an action to the agent. Fire-and-forget — no response, no pending state.\n\nEvery action is an event: it lands on the GguiSession's consume buffer and\nthe agent receives it on its next turn (via `ggui_consume`). The action\nentry's optional `nextStep` names the tool the agent SHOULD invoke in\nresponse — advisory only; the agent owns the call decision. Component code\nis identical either way: call `useAction(name)(payload)` and treat\n`nextStep` as informational (use it to inform button labels, icons, copy).\n\nRUNTIME DEDUP (backstop, not a feature). Same-`(name, payload)` calls within\none event-loop task are coalesced — the first wins, subsequent duplicates\nare suppressed. This is the structural defense against LLM-generated\nnested-interactive components where a Checkbox `onChange` and an outer\n`Card as={Clickable}` `onClick` both wire to the same `useAction` binding;\nthe inner gesture bubbles to the outer handler, so without dedup one user\nclick would fire the action twice (a toggle would run back-to-back and the\nuser's change would disappear). **Do not rely on this as a feature** — wire\neach `useAction` callback to exactly ONE interactive surface; the dedup\nexists only because the LLM's source code can be wrong in subtle ways and\nthe runtime is the only place that can see the actual event-bubble path.\nSee `dispatch-dedup.ts` for the full failure-mode rationale.\n\nNEVER SILENT. When the dedup fires, a `console.warn` is emitted in BOTH dev\nand prod with the full diagnostic. The suppression is always visible in\nbrowser DevTools; operators investigating a \"the second click does nothing\"\nreport see the warning immediately. A structured observer also exists as\n`WireConfig.onDispatchSuppressed`, but the first-party renderers never set\nit — it is reachable only from a hand-built `WireConfig` (see its\ndocstring), so the console signal is what operators get in practice.\n\n**Signature:** `useAction<T = unknown>(actionName: string): (data: T) => void`\n\n**Parameters:**\n\n| Param | Type | Description |\n|-------|------|-------------|\n| actionName | `string` | Action name from the action contract |\n\n**Returns:** `(data: T) => void`\n\n**Example:**\n```tsx\nconst submitForm = useAction<{name: string; email: string}>('formSubmit');\n\n// In JSX:\n<Button onClick={() => submitForm({ name, email })}>Submit</Button>\n```\n\n\n### useStream\n\nSubscribe to deliveries on a named stream channel.\n\nHonors the channel's per-delivery `mode` ('append' vs 'replace')\nand the optional `complete` terminal marker. Channels declared\n`mode: 'replace'` on the spec typically emit every delivery with\n`mode: 'replace'` — this hook folds them into a single-latest\nvalue without accumulating history.\n\n**Signature:** `useStream<T = unknown>(channelName: string): StreamResult<T>`\n\n**Parameters:**\n\n| Param | Type | Description |\n|-------|------|-------------|\n| channelName | `string` | Channel name from the GguiSession's streamSpec |\n\n**Returns:** `StreamResult<T>`\n\n| Property | Type | Description |\n|----------|------|-------------|\n| latest | `T \\| null` | Most recent payload delivered on this channel, or null if none received yet. |\n| all | `T[]` | All payloads accumulated on this channel. - `mode: 'append'` deliveries are pushed to the tail (continuous stream). - `mode: 'replace'` deliveries collapse `all` to a single-element array containing the latest payload — matching the channel's \"full replacement\" semantics. |\n| isComplete | `boolean` | Truthy after the channel has delivered an envelope with `complete: true`. Subscribers flip into a \"channel closed\" rendering state based on this signal; further deliveries on a completed channel are still accumulated, since the underlying wire doesn't enforce quiescence. |\n\n**Example:**\n```tsx\nconst progress = useStream<{ percent: number; message: string }>('progress');\n\n// In JSX:\n{progress.latest && (\n <Progress value={progress.latest.percent} />\n)}\n<Text>{progress.all.length} updates received</Text>\n```\n\n\n## Context Hooks\n\n\n\nRead-only access to render, app, and auth context.\n\n### useAuth\n\nRead-only auth context. Token excluded — auth is added server-side.\n\n**Signature:** `useAuth(): AuthInfo`\n\n**Returns:** `AuthInfo`\n\n| Property | Type | Description |\n|----------|------|-------------|\n| userId | `string` | userId |\n| isAuthenticated | `boolean` | isAuthenticated |\n\n**Example:**\n```tsx\nconst auth = useAuth();\n\n// In JSX:\n{auth.isAuthenticated\n ? <Text>Welcome, user {auth.userId}</Text>\n : <Text>Please sign in</Text>\n}\n```\n\n\n### useApp\n\nRead-only app metadata.\n\n**Signature:** `useApp(): AppInfo`\n\n**Returns:** `AppInfo`\n\n| Property | Type | Description |\n|----------|------|-------------|\n| appId | `string` | appId |\n| appName | `string` | appName |\n| appDescription | `string` | appDescription |\n| appIcon | `string` | appIcon |\n\n**Example:**\n```tsx\nconst app = useApp();\n\n// In JSX:\n<Heading>{app.appName}</Heading>\n{app.appDescription && <Text>{app.appDescription}</Text>}\n```\n\n\n### useRender\n\nRead-only render context with connection status.\n\n**Signature:** `useRender(): GguiSessionInfo`\n\n**Returns:** `GguiSessionInfo`\n\n| Property | Type | Description |\n|----------|------|-------------|\n| sessionId | `string` | sessionId |\n| isConnected | `boolean` | isConnected |\n\n**Example:**\n```tsx\nconst render = useRender();\n\n// In JSX:\n<Badge variant={render.isConnected ? 'success' : 'error'}>\n {render.isConnected ? 'Connected' : 'Disconnected'}\n</Badge>\n```\n\n\n## Provider\n\n\n\nThe provider is set up automatically by the renderer. Generated components do not need to wrap themselves in a provider.\n\n### GguiWireProvider\n\nReact context provider that injects WireConfig for all wire hooks.\n\n**Props:**\n\n| Prop | Type | Description |\n|------|------|-------------|\n| config | `WireConfig` | config |\n| children | `ReactNode` | children |\n\n**Example:**\n```tsx\n<GguiWireProvider config={wireConfig}>\n <YourComponent />\n</GguiWireProvider>\n```\n\n\n## Internal: WireConfig\n\n\n\nThis is the configuration object passed to GguiWireProvider. Generated components do not interact with this directly — it is provided by the renderer.\n\n### WireConfig\n\nConfiguration injected by the provider — the renderer inside the\niframe.\n\nEvery method is typed against the contract generic `T` so typed\ncallers get compile-time enforcement:\n - `dispatch(name, data)` — `name` MUST be a declared actionSpec\n key; `data` MUST satisfy that action's schema.\n - `subscribe(channel, handler)` — same discipline for streamSpec.\n\nUntyped callers (`T = DataContract` default) degrade to the broad\nshape via the conditional `WireDispatchData` / `WireStreamPayload`\naliases — no call-site break.\n\nThe contract's `agentCapabilities.tools` catalog declares tools the AGENT\ninvokes (not the component); user gestures fire via\n`dispatch(name, data)` and the optional `nextStep` field on the\naction entry names the tool the agent SHOULD invoke next.\n\nThe renderer mounts exactly ONE GguiSession per iframe — `render.sessionId`\nis the stable identity the WireProvider was constructed with. No\nper-item scoping factory; with a one-GguiSession-per-mount lifecycle,\n\"scope\" collapses to identity.\n\n| Property | Type | Description |\n|----------|------|-------------|\n| app | `{ readonly appId: string; readonly appName: string; readonly appDescription?: string; readonly appIcon?: string; }` | app |\n| render | `{ readonly sessionId: string; readonly isConnected: boolean; }` | render |\n| auth | `{ readonly userId?: string; readonly isAuthenticated: boolean; }` | auth |\n| dispatch | `<N extends string>( actionName: N, data: WireDispatchData<T, N>, ) => void` | Fire an action to the agent (fire-and-forget over WS). Typed callers get compile-time checked `name` + `data`; untyped callers (`T = DataContract`) keep the broad shape. |\n| subscribe | `<N extends string>( channelName: N, handler: (delivery: StreamDelivery<WireStreamPayload<T, N>>) => void, ) => () => void` | Subscribe to deliveries on a named stream channel. |\n| onDispatchSuppressed | `(info: DispatchSuppressedInfo) => void` | Optional structured observability for the runtime's two dispatch- suppression invariants — the task-scoped duplicate backstop (`reason: 'duplicate-dispatch'`, fired by `useAction`) and the render-lifetime one-shot guard (`reason: 'one-shot-spent'`, fired by the config's own `dispatch` when a declared `oneShot` action is gestured a second time, ggui#1108). Both fire ALONGSIDE the always-on `console.warn`; neither reaches the agent. Reachability constraint: the first-party renderers do NOT set this field — `buildRootWireConfig` (`@ggui-ai/iframe-runtime`) and `<GguiRender>`'s internal config both omit it. It fires only when a host hand-builds a complete `WireConfig` (or passes `onDispatchSuppressed` to `buildWireConfig`). On every first-party render path the `console.warn` is the sole suppression signal — which is what the `oneShot` contract requires as its minimum trace. |\n";
|
|
5
|
+
export const WIRE_DOCUMENTATION = "# ggui Wire Hooks Reference\n\n> Wire hooks connect generated UI components to agent communication.\n> They are pre-imported in the boilerplate — use them directly.\n> All hooks must be called inside a GguiWireProvider (handled automatically by the renderer).\n\nImport: `import { useAction, useStream } from '@ggui-ai/wire'`\n\n## Communication Hooks\n\n\n\nThese are the wire primitives for component-agent communication. `useWiredTool` retired 2026-05-11 — agentCapabilities.tools is a catalog the AGENT invokes, not a component hook surface; user gestures use `useAction(name)` and the optional `nextStep` field on the action entry names the tool the agent SHOULD invoke next.\n\n### useAction\n\nFire an action to the agent. Fire-and-forget — no response, no pending state.\n\nEvery action is an event: it lands on the GguiSession's consume buffer and\nthe agent receives it on its next turn (via `ggui_consume`). The action\nentry's optional `nextStep` names the tool the agent SHOULD invoke in\nresponse — advisory only; the agent owns the call decision. Component code\nis identical either way: call `useAction(name)(payload)` and treat\n`nextStep` as informational (use it to inform button labels, icons, copy).\n\nRUNTIME DEDUP (backstop, not a feature). Same-`(name, payload)` calls within\none event-loop task are coalesced — the first wins, subsequent duplicates\nare suppressed. This is the structural defense against LLM-generated\nnested-interactive components where a Checkbox `onChange` and an outer\n`Card as={Clickable}` `onClick` both wire to the same `useAction` binding;\nthe inner gesture bubbles to the outer handler, so without dedup one user\nclick would fire the action twice (a toggle would run back-to-back and the\nuser's change would disappear). **Do not rely on this as a feature** — wire\neach `useAction` callback to exactly ONE interactive surface; the dedup\nexists only because the LLM's source code can be wrong in subtle ways and\nthe runtime is the only place that can see the actual event-bubble path.\nSee `dispatch-dedup.ts` for the full failure-mode rationale.\n\nNEVER SILENT. When the dedup fires, a `console.warn` is emitted in BOTH dev\nand prod with the full diagnostic. The suppression is always visible in\nbrowser DevTools; operators investigating a \"the second click does nothing\"\nreport see the warning immediately. A structured observer also exists as\n`WireConfig.onDispatchSuppressed`, but the first-party renderers never set\nit — it is reachable only from a hand-built `WireConfig` (see its\ndocstring), so the console signal is what operators get in practice.\n\n**Signature:** `useAction<T = unknown>(actionName: string): (data: T) => void`\n\n**Parameters:**\n\n| Param | Type | Description |\n|-------|------|-------------|\n| actionName | `string` | Action name from the action contract |\n\n**Returns:** `(data: T) => void`\n\n**Example:**\n```tsx\nconst submitForm = useAction<{name: string; email: string}>('formSubmit');\n\n// In JSX:\n<Button onClick={() => submitForm({ name, email })}>Submit</Button>\n```\n\n\n### useActionSpent\n\nWhether the card's `oneShot` action `actionName` is already spent, so a\ncomponent can paint its control as submitted on the first paint, with no\npress needed to find out.\n\n`true` iff the contract declares `actionName` `oneShot` and the card already\nfired it, either in this render or before a reload (the record the host\nre-serves). It is reactive, flipping to `true` when this card's own dispatch\ncommits. It returns data only: how a spent control looks (disabled,\n\"Submitted\", hidden) is the component's choice. Outside a runtime that\nprovides spent state, it is `false` and the dispatch guard still holds.\n\n**Signature:** `useActionSpent(actionName: string): boolean`\n\n**Parameters:**\n\n| Param | Type | Description |\n|-------|------|-------------|\n| actionName | `string` | actionName |\n\n**Returns:** `boolean`\n\n**Example:**\n```tsx\nconst confirm = useAction<ActionConfirmPayload>('confirm');\nconst spent = useActionSpent('confirm');\n\n// In JSX — the control paints spent on the first paint after a reload, with no press:\n<Button variant=\"primary\" disabled={spent} onClick={() => confirm({ bookingId })}>\n {spent ? 'Confirmed' : 'Confirm booking'}\n</Button>\n```\n\n\n### useActionPending\n\nWhether this card's action `actionName` was dispatched and is still waiting\nfor the agent's answer, so the control can show it is working (disabled,\n`aria-busy`, \"Sending…\") instead of looking untouched while the agent\nreacts.\n\n`true` from the committed dispatch until the session's next frame lands, or\nthe bound elapses. It is reactive. It returns data only: how a pending\ncontrol looks is the component's choice. Outside a runtime that provides\npending state, it is `false`.\n\n**Signature:** `useActionPending(actionName: string): boolean`\n\n**Parameters:**\n\n| Param | Type | Description |\n|-------|------|-------------|\n| actionName | `string` | actionName |\n\n**Returns:** `boolean`\n\n**Example:**\n```tsx\nconst send = useAction<ActionSendPayload>('send');\nconst sending = useActionPending('send');\n\n// In JSX — the control shows it is working until the agent's answer repaints the card:\n<Button variant=\"primary\" disabled={sending} aria-busy={sending} onClick={() => send({ text })}>\n {sending ? 'Sending…' : 'Send'}\n</Button>\n```\n\n\n### useStream\n\nSubscribe to deliveries on a named stream channel.\n\nHonors the channel's per-delivery `mode` ('append' vs 'replace')\nand the optional `complete` terminal marker. Channels declared\n`mode: 'replace'` on the spec typically emit every delivery with\n`mode: 'replace'` — this hook folds them into a single-latest\nvalue without accumulating history.\n\n**Signature:** `useStream<T = unknown>(channelName: string): StreamResult<T>`\n\n**Parameters:**\n\n| Param | Type | Description |\n|-------|------|-------------|\n| channelName | `string` | Channel name from the GguiSession's streamSpec |\n\n**Returns:** `StreamResult<T>`\n\n| Property | Type | Description |\n|----------|------|-------------|\n| latest | `T \\| null` | Most recent payload delivered on this channel, or null if none received yet. |\n| all | `T[]` | All payloads accumulated on this channel. - `mode: 'append'` deliveries are pushed to the tail (continuous stream). - `mode: 'replace'` deliveries collapse `all` to a single-element array containing the latest payload — matching the channel's \"full replacement\" semantics. |\n| isComplete | `boolean` | Truthy after the channel has delivered an envelope with `complete: true`. Subscribers flip into a \"channel closed\" rendering state based on this signal; further deliveries on a completed channel are still accumulated, since the underlying wire doesn't enforce quiescence. |\n\n**Example:**\n```tsx\nconst progress = useStream<{ percent: number; message: string }>('progress');\n\n// In JSX:\n{progress.latest && (\n <Progress value={progress.latest.percent} />\n)}\n<Text>{progress.all.length} updates received</Text>\n```\n\n\n## Context Hooks\n\n\n\nRead-only access to render, app, and auth context.\n\n### useAuth\n\nRead-only auth context. Token excluded — auth is added server-side.\n\n**Signature:** `useAuth(): AuthInfo`\n\n**Returns:** `AuthInfo`\n\n| Property | Type | Description |\n|----------|------|-------------|\n| userId | `string` | userId |\n| isAuthenticated | `boolean` | isAuthenticated |\n\n**Example:**\n```tsx\nconst auth = useAuth();\n\n// In JSX:\n{auth.isAuthenticated\n ? <Text>Welcome, user {auth.userId}</Text>\n : <Text>Please sign in</Text>\n}\n```\n\n\n### useApp\n\nRead-only app metadata.\n\n**Signature:** `useApp(): AppInfo`\n\n**Returns:** `AppInfo`\n\n| Property | Type | Description |\n|----------|------|-------------|\n| appId | `string` | appId |\n| appName | `string` | appName |\n| appDescription | `string` | appDescription |\n| appIcon | `string` | appIcon |\n\n**Example:**\n```tsx\nconst app = useApp();\n\n// In JSX:\n<Heading>{app.appName}</Heading>\n{app.appDescription && <Text>{app.appDescription}</Text>}\n```\n\n\n### useRender\n\nRead-only render context with connection status.\n\n**Signature:** `useRender(): GguiSessionInfo`\n\n**Returns:** `GguiSessionInfo`\n\n| Property | Type | Description |\n|----------|------|-------------|\n| sessionId | `string` | sessionId |\n| isConnected | `boolean` | isConnected |\n\n**Example:**\n```tsx\nconst render = useRender();\n\n// In JSX:\n<Badge variant={render.isConnected ? 'success' : 'error'}>\n {render.isConnected ? 'Connected' : 'Disconnected'}\n</Badge>\n```\n\n\n## Provider\n\n\n\nThe provider is set up automatically by the renderer. Generated components do not need to wrap themselves in a provider.\n\n### GguiWireProvider\n\nReact context provider that injects WireConfig for all wire hooks.\n\n**Props:**\n\n| Prop | Type | Description |\n|------|------|-------------|\n| config | `WireConfig` | config |\n| children | `ReactNode` | children |\n\n**Example:**\n```tsx\n<GguiWireProvider config={wireConfig}>\n <YourComponent />\n</GguiWireProvider>\n```\n\n\n## Internal: WireConfig\n\n\n\nThis is the configuration object passed to GguiWireProvider. Generated components do not interact with this directly — it is provided by the renderer.\n\n### WireConfig\n\nConfiguration injected by the provider — the renderer inside the\niframe.\n\nEvery method is typed against the contract generic `T` so typed\ncallers get compile-time enforcement:\n - `dispatch(name, data)` — `name` MUST be a declared actionSpec\n key; `data` MUST satisfy that action's schema.\n - `subscribe(channel, handler)` — same discipline for streamSpec.\n\nUntyped callers (`T = DataContract` default) degrade to the broad\nshape via the conditional `WireDispatchData` / `WireStreamPayload`\naliases — no call-site break.\n\nThe contract's `agentCapabilities.tools` catalog declares tools the AGENT\ninvokes (not the component); user gestures fire via\n`dispatch(name, data)` and the optional `nextStep` field on the\naction entry names the tool the agent SHOULD invoke next.\n\nThe renderer mounts exactly ONE GguiSession per iframe — `render.sessionId`\nis the stable identity the WireProvider was constructed with. No\nper-item scoping factory; with a one-GguiSession-per-mount lifecycle,\n\"scope\" collapses to identity.\n\n| Property | Type | Description |\n|----------|------|-------------|\n| app | `{ readonly appId: string; readonly appName: string; readonly appDescription?: string; readonly appIcon?: string; }` | app |\n| render | `{ readonly sessionId: string; readonly isConnected: boolean; }` | render |\n| auth | `{ readonly userId?: string; readonly isAuthenticated: boolean; }` | auth |\n| dispatch | `<N extends string>( actionName: N, data: WireDispatchData<T, N>, ) => void` | Fire an action to the agent (fire-and-forget over WS). Typed callers get compile-time checked `name` + `data`; untyped callers (`T = DataContract`) keep the broad shape. |\n| subscribe | `<N extends string>( channelName: N, handler: (delivery: StreamDelivery<WireStreamPayload<T, N>>) => void, ) => () => void` | Subscribe to deliveries on a named stream channel. |\n| onDispatchSuppressed | `(info: DispatchSuppressedInfo) => void` | Optional structured observability for the runtime's two dispatch- suppression invariants — the task-scoped duplicate backstop (`reason: 'duplicate-dispatch'`, fired by `useAction`) and the render-lifetime one-shot guard (`reason: 'one-shot-spent'`, fired by the config's own `dispatch` when a declared `oneShot` action is gestured a second time, ggui#1108). Both fire ALONGSIDE the always-on `console.warn`; neither reaches the agent. Reachability constraint: the first-party renderers do NOT set this field — `buildRootWireConfig` (`@ggui-ai/iframe-runtime`) and `<GguiRender>`'s internal config both omit it. It fires only when a host hand-builds a complete `WireConfig` (or passes `onDispatchSuppressed` to `buildWireConfig`). On every first-party render path the `console.warn` is the sole suppression signal — which is what the `oneShot` contract requires as its minimum trace. |\n";
|
|
@@ -30,68 +30,8 @@ import { pathToFileURL, fileURLToPath } from 'node:url';
|
|
|
30
30
|
import { runSandboxed } from '@ggui-ai/sandbox';
|
|
31
31
|
import type {
|
|
32
32
|
JsonObject,
|
|
33
|
-
JsonSchema,
|
|
34
|
-
JsonValue,
|
|
35
|
-
PropsSpec,
|
|
36
33
|
} from '@ggui-ai/protocol';
|
|
37
34
|
|
|
38
|
-
/**
|
|
39
|
-
* Generate sample props from a PropsSpec contract.
|
|
40
|
-
* Uses `example` values from each PropEntry when available,
|
|
41
|
-
* otherwise synthesizes plausible defaults from the JSON Schema.
|
|
42
|
-
*/
|
|
43
|
-
export function generateSampleProps(spec: PropsSpec): JsonObject {
|
|
44
|
-
const props: JsonObject = {};
|
|
45
|
-
|
|
46
|
-
for (const [name, entry] of Object.entries(spec.properties)) {
|
|
47
|
-
// Prefer explicit example value
|
|
48
|
-
if (entry.example !== undefined) {
|
|
49
|
-
props[name] = entry.example;
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Prefer explicit default value
|
|
54
|
-
if (entry.default !== undefined) {
|
|
55
|
-
props[name] = entry.default;
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Synthesize from JSON Schema
|
|
60
|
-
props[name] = synthesizeFromSchema(entry.schema);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return props;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function synthesizeFromSchema(schema: JsonSchema): JsonValue | undefined {
|
|
67
|
-
// Draft-07 type arrays (`['string','null']`) synthesize from the
|
|
68
|
-
// first non-null member — the canonical nullability form the
|
|
69
|
-
// enforced-props-schema emission produces.
|
|
70
|
-
const declared = Array.isArray(schema.type)
|
|
71
|
-
? schema.type.find((t) => t !== 'null')
|
|
72
|
-
: schema.type;
|
|
73
|
-
switch (declared) {
|
|
74
|
-
case 'string': return 'sample';
|
|
75
|
-
case 'number':
|
|
76
|
-
case 'integer': return 0;
|
|
77
|
-
case 'boolean': return false;
|
|
78
|
-
case 'null': return null;
|
|
79
|
-
case 'array': {
|
|
80
|
-
if (schema.items) return [synthesizeFromSchema(schema.items) ?? null];
|
|
81
|
-
return [];
|
|
82
|
-
}
|
|
83
|
-
case 'object': {
|
|
84
|
-
const obj: JsonObject = {};
|
|
85
|
-
if (schema.properties) {
|
|
86
|
-
for (const [k, v] of Object.entries(schema.properties)) {
|
|
87
|
-
obj[k] = synthesizeFromSchema(v);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return obj;
|
|
91
|
-
}
|
|
92
|
-
default: return undefined;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
35
|
|
|
96
36
|
/**
|
|
97
37
|
* Timeout for a single render smoke test. Generous — some LLM-
|
package/src/tools.ts
CHANGED
|
@@ -256,11 +256,21 @@ export const APPLY_CHANGES_HASHLINE_TOOL_FLAT: LLMToolDef = {
|
|
|
256
256
|
},
|
|
257
257
|
};
|
|
258
258
|
|
|
259
|
+
/**
|
|
260
|
+
* What to do when the name you wanted is not in the icon list (ggui#1183): the
|
|
261
|
+
* list is complete and constant, so a second call is the same exchange — and
|
|
262
|
+
* three of those end the run (`SAME_EXCHANGE_BREAK_AT`). Said ONCE, on the
|
|
263
|
+
* tool's description and appended to every result, because the model that
|
|
264
|
+
* looped had never been told the list would not change.
|
|
265
|
+
*/
|
|
266
|
+
export const ICON_LIST_NO_MATCH_GUIDANCE =
|
|
267
|
+
"This list is complete and constant — calling again returns exactly it. If the name you wanted is not here, pick the nearest listed name (or a neutral one such as `circle`) and continue with apply_changes; never call this tool twice.";
|
|
259
268
|
/** Helper icon-lookup tool — not a patch grammar, attached to the same LLM turn. */
|
|
260
269
|
export const GET_ICONS_TOOL: LLMToolDef = {
|
|
261
270
|
name: "get_available_icons",
|
|
262
271
|
description:
|
|
263
|
-
'List every Lucide icon name the <Icon name="..."> component renders (kebab-case). A name outside this list renders nothing — check before choosing.'
|
|
272
|
+
'List every Lucide icon name the <Icon name="..."> component renders (kebab-case). A name outside this list renders nothing — check before choosing. ' +
|
|
273
|
+
ICON_LIST_NO_MATCH_GUIDANCE,
|
|
264
274
|
parameters: { type: "object", properties: {} },
|
|
265
275
|
};
|
|
266
276
|
|