Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/10859-ai-node-zod-arms.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,13 @@
- `@object-ui/types/zod` exports `AIFormAssistSchema`, `AIRecommendationsSchema`, `NLQuerySchema`, their sub-schemas `AIConfigSchema`, `AIFieldSuggestionSchema`, `AIRecommendationItemSchema` and `NLQueryResultSchema`, and the category union `AIComponentSchema`, which `AnyComponentSchema` now lists. The strict authoring face (`StrictAnyComponentSchema`) derives from `AnyComponentSchema` and accepts the same three types, closed to undeclared keys like every other arm.
- Each arm restates its TypeScript declaration member for member. The objectui#8178 retired members (`formId`, `objectName`, `fields`, `autoFill`, `maxResults`) and both content channels (objectui#9256) are refused by name, as they already were on the TypeScript face.
- ⚠️ The `on*` members are the one place the two faces differ. The TypeScript face still types `onApplySuggestion`, `onRejectSuggestion`, `onSelect`, `onDismiss` and `onSubmit` as `string`; the zod face refuses an authored value on each, by name, following objectui#6182 (the handler-expression string dialect is not an authoring form) and objectui#6124 (`onSelect` / `onDismiss` / `onSubmit` are runtime slots a React host fills; the other two have no reader). An authored string was never a working handler: `onSelect`, `onDismiss` and `onSubmit` are called only as function props a React host passes, and `onApplySuggestion` / `onRejectSuggestion` are read by nothing — delete the key from the document. The TypeScript face's `string` typing is objectui#10874's to correct.

⚠️ **Dated note, 2026-09-28 — the TypeScript face now types the `on*` members per key, and `config` / `context` are refused — objectui#10874.**
Later in this same release the TypeScript face settles the five `on*` members by the objectui#6124
rule, so the `string` typing described above is gone. `onSelect`, `onDismiss` and `onSubmit` are typed
as the callables their components invoke: the objectui#6124 shape, a callable twin beside the named
refusal this entry added. `onApplySuggestion` and `onRejectSuggestion` are `?: never`, so both faces
refuse those two. The same change retires `config` (all three types) and `context` (`ai-form-assist`,
`ai-recommendations`) on both faces, so `safeValidateSchema` — and `objectui validate` — refuses a
document that carries one of them, by name, where the arms this entry added accepted it. The rest of
this entry is kept as the reading of this change; the objectui#10874 entry states what ships.
52 changes: 52 additions & 0 deletions .changeset/10874-ai-node-faces-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
'@object-ui/types': minor
---

**BREAKING (authoring)** — the three `@object-ui/plugin-ai` node declarations stop offering members that no runtime honours, and type the handler slots their components call (objectui#10874, ADR-0049, the objectui#6124 per-key rule).

**Clause-②: yes** — authorable members of published `@object-ui/types` declarations narrow. Three members become callable: `string` to a function type (`onSelect`, `onDismiss`, `onSubmit`). `config` and `context` become `never` on both faces, five members across the three declarations. `onApplySuggestion` and `onRejectSuggestion` become `never` on the TypeScript face, and the zod face already refused them. Scored `minor`, not `major`: this repository scores its own breaking changes `minor` and spells the breaking semantics out in the body (`check:changeset-no-major`).

**What changes, on which face.**

| Member | Was on | TypeScript face | zod face |
|---|---|---|---|
| `onSelect`, `onDismiss` | `AIRecommendationsSchema` | CALLABLE: `(item: AIRecommendationItem) => void` (was `string`) | refused by name, unchanged |
| `onSubmit` | `NLQuerySchema` | CALLABLE: `(query: string) => void` (was `string`) | refused by name, unchanged |
| `onApplySuggestion`, `onRejectSuggestion` | `AIFormAssistSchema` | `?: never` (was `string`) | refused by name, unchanged |
| `config` | `AIFormAssistSchema`, `AIRecommendationsSchema`, `NLQuerySchema` | `?: never` (was `AIConfig`) | refused by name (was accepted) |
| `context` | `AIFormAssistSchema`, `AIRecommendationsSchema` | `?: never` (was a string-keyed record) | refused by name (was accepted) |

**Why.**

- `onSelect`, `onDismiss` and `onSubmit` are RUNTIME SLOTS. Each node type is registered to its raw component, `SchemaRenderer` spreads the node's own keys onto the component's props, and `AIRecommendations` and `NLQueryInput` call these keys as functions. So a function on the node was always called, and a string there was called as a function and threw at click. The TypeScript face now declares the callable the component invokes, as objectui#6124 rules for a key a runtime consumer calls. The zod face keeps refusing an authored value, because JSON has no function value.
- `onApplySuggestion` and `onRejectSuggestion` were read by nothing: `AIFormAssist` takes `onApply` and `onRefresh`, so a string there did nothing.
- `config` and `context` were read by nothing in `@object-ui/plugin-ai`. The three components are presentation only and call no model, so a `provider`, `model`, `systemPrompt` or context written on the node did nothing and changed nothing on screen.
- Before this change the zod face refused all five `on*` strings (objectui#10859) while the TypeScript face offered them, so `onSelect: 'x'` passed `tsc` and was then refused by the validator. Now `tsc` refuses the string too.

**Migration.**

- `onSelect`, `onDismiss`, `onSubmit`: pass a function, never a string — either as the component prop of the same name, or on a node you build in TypeScript. `AIRecommendations` and `NLQueryInput` take the same signatures as props, unchanged.
- `onApplySuggestion`: delete it, and use the `onApply` prop of the `AIFormAssist` component. `onRejectSuggestion`: delete it; the component has no reject callback.
- `config`: delete it. Configure the model in your host's own AI service (ObjectUI has no AI-provider API; these components call no model), and hand its output to the node as `suggestions`, `recommendations` or `result`.
- `context`: delete it, and pass the context to your host's own AI service when you ask it for suggestions or recommendations.

```tsx
// before: compiled; `config` did nothing, and the string was called as a function and threw at click
const picks: AIRecommendationsSchema = {
type: 'ai-recommendations',
recommendations,
config: { model: 'gpt-4' },
onSelect: 'openItem',
};

// after
const picks: AIRecommendationsSchema = {
type: 'ai-recommendations',
recommendations,
onSelect: (item) => openItem(item),
};
```

**What does not change.** The renderers are untouched, so a stored document renders exactly as it did: `config` and `context` were ignored and still are, and a function reaching `onSelect`, `onDismiss` or `onSubmit` is still called. What moves is the verdict. `tsc` refuses the four retired keys, on all seven member slots that carried them, and a string on the three slots. The validator refuses `config` and `context` by name, where the arms objectui#10859 added in this same release accepted them. The exported `AIConfig` type and `AIConfigSchema` stay; no node member takes them any more.

⚠️ **The census behind "read by nothing" is the in-repo half.** Every package, app, example, doc and skill in this repository was searched: none reads `config` or `context` off an AI node, and none authors a changed member. Customer applications and published documents outside it were not enumerated. A TypeScript consumer that authored one of these keys gets a compile error naming it.
6 changes: 6 additions & 0 deletions .changeset/8800-retire-ai-insights.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ support types — each has a reader among those three, so none is orphaned. The
seven `?: never` member tombstones on those three schemas are objectui#8178's
retirement and do not move with this one.

⚠️ **Dated note, 2026-09-28 — `AIConfig` no longer has a reader among those three — objectui#10874.**
Later in this same release the `config` members of `AIFormAssistSchema`,
`AIRecommendationsSchema` and `NLQuerySchema` retire as `?: never`, so no node member takes the
shared `AIConfig` type any more; the type itself stays exported. The paragraph above is kept as the
reading of this change; the objectui#10874 entry states what ships.

⚠️ **The zero this rests on is the IN-REPO HALF, and the ruling was taken with
that limit attached.** This repository's tracked files were enumerated and the
spelling occurred exactly once — inside the declaration itself — against lit
Expand Down
21 changes: 21 additions & 0 deletions packages/plugin-ai/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,27 @@ is a compile error rather than a silent no-op:
| `autoFill` | `AIFormAssistSchema` | apply the suggestions you want from `onApply` |
| `maxResults` | `AIRecommendationsSchema` | slice `recommendations` before handing it over — **every item is rendered** |

Four more keys went the same way (objectui#10874), on seven member slots. `config` and `context`
were read by nothing: none of these components calls a model, so neither
configured anything. `onApplySuggestion` and `onRejectSuggestion` were typed
`string` and read by nothing either. All four are now refused by the schema
types and by the validator alike:

| Key | Was on | Instead |
|---|---|---|
| `config` | all three schemas | configure the model in your host's own AI service (ObjectUI has no AI-provider API; these components call no model), and hand its output to the node as `suggestions`, `recommendations` or `result` |
| `context` | `AIFormAssistSchema`, `AIRecommendationsSchema` | pass it to your host's own AI service when you ask it for suggestions or recommendations |
| `onApplySuggestion` | `AIFormAssistSchema` | the `onApply` component prop |
| `onRejectSuggestion` | `AIFormAssistSchema` | delete it — the component has no reject callback |

`onSelect` and `onDismiss` (`AIRecommendationsSchema`) and `onSubmit`
(`NLQuerySchema`) were typed `string` too, but the components DO call them:
a string there was called as a function and threw at click. They are now typed
as the callbacks the components invoke — `(item) => void` and
`(query) => void` — so pass a function, either as the component prop of the same
name or on a node you build in TypeScript. The validator still refuses them in
a JSON document, because JSON has no function value.

## Links

- 📚 [Documentation](https://www.objectui.org/docs/plugins/plugin-ai)
Expand Down
Loading
Loading