Commit f830d9e
feat: type-safe tool-call parts in useChat (typed chunks, parsed input, gated approval) (#918)
* feat(react,vue,solid,svelte): infer typed chunks from a bare tools array
Add the `const` modifier to the `TTools` type param of useChat/createChat so a
plain inline `tools: [a, b]` array captures the tuple + literal tool names and
yields a typed, discriminated tool-call part union — no `clientTools(...)`
wrapper or `as const` needed. clientTools() still works.
* feat(ai): populate parsed input on tool-call parts
ToolCallPart already declared a typed `input?` field but it was never written
at runtime — only the raw `arguments` string and `output` were set. Populate
`input` from the parsed arguments once complete (in the stream processor's
completeToolCall, the TOOL_CALL_END parsed-input path, and history hydration in
modelMessagesToUIMessages), carrying it forward through the part updaters.
`arguments` is unchanged and not deprecated.
* feat(ai,ai-client): gate tool-call `approval` on needsApproval
Capture `needsApproval` as a literal type param (TNeedsApproval) on the client
tool types, and include the `approval` field on a tool-call part only when the
tool was defined with `needsApproval: true`. Non-approval tools have no
`approval` field (reading it is a compile error). Generic handlers still work
via an `'approval' in part` guard or the base ToolCallPart type; untyped
useChat() is unaffected.
* docs,examples,e2e: cover typed tool-call parts + AG-UI tool-forwarding safety
- Document parsed `input`, approval gating (+ generic-handler escape hatches),
and the AG-UI client-tool forwarding security tradeoff (manual registration
is the safe default; mergeAgentTools trusts the client) in tool-approval,
client-tools, ag-ui-compliance docs and the tool-calling skill.
- Drop the redundant Object.values() around mergeAgentTools (it already returns
an array) in the react/vue examples.
- Add the /typesafe-tools demo route and e2e assertions for input population.
* docs: prefer plain `tools` arrays, document clientTools as an optional escape hatch
A plain `tools: [a, b]` array (inline or a separate const) now narrows tool
names, inputs and outputs on its own, so drop `clientTools(...)` from the
framework/API/tool docs and demote it to an optional identity helper documented
once in client-tools + the ai-client reference. Adds a regression test proving a
separately-declared const array narrows without clientTools or `as const`.
* fix(examples,e2e): resolve type errors surfaced by precise tool inference
- ts-solid-chat: pass a real `fetchServerSentEvents` connection to useChat
instead of re-passing `chatOptions.connection` (typed `| undefined`, which
no longer satisfies the connection/fetcher transport XOR now that the tools
tuple is precise); guard `part.approval` with `'approval' in part`.
- e2e tools-test: cast the untyped history-fixture `initialMessages` to the
typed message shape so they don't conflict with the concrete tools tuple.
* fix: address PR review — sync TOOL_CALL_END.input override + doc nits
- processor: when TOOL_CALL_END provides a parsed `input` that diverges from
the accumulated args parse, refresh the rendered tool-call part so
`part.input` reflects the canonical override (not the stale parse). Adds a
divergent-input test.
- docs/skill: render `part.input` directly in approval examples (parts have
parsed input by approval time); note the approval gate in the ai-client
ToolCallPart reference; use gpt-5.5 across all ag-ui-compliance tiers.
* ci: apply automated fixes
* fix(e2e): type-check Gemini video mock config
`Omit<GeminiVideoConfig, 'apiKey'>` drops the inherited `GoogleGenAIOptions`
keys (a tsc quirk from the config's own `allowUrlFetch` member — the empty
image/audio configs are unaffected), so the `httpOptions` mock-routing literal
was rejected only here. Cast to the param type; the mock base URL is required
at runtime. Pre-existing on main, surfaced now that `test:pr` type-checks
examples/testing and this PR makes the e2e app affected.
* ci: apply automated fixes
* fix: address PR review follow-ups — rebase, parity, tests, safety
- Rebase onto main (resolve solid-chat + e2e merge conflicts)
- Extend `const TTools` to Preact `useChat` and Angular `injectChat`
- Add message-updaters tests for `input` carry-forward through approval
- Guard `JSON.stringify(chunk.input)` in stream processor
- Strengthen approval-gating changeset migration notes; include angular/preact
in the const-tools inference changeset
* ci: apply automated fixes
* fix(examples): link Type-Safe Tools demo from home page and nav
The /typesafe-tools route was added in PR #918 but had no discoverability
entry points — add it to the empty-state grid on the home page and to the
Examples section in the header drawer.
* fix(examples): remove duplicate connection in ts-solid-chat useChat
Spreading chatOptions already supplies the SSE connection. Re-specifying
connection conflicted with the ChatTransport XOR and broke test:types.
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com>1 parent e0bbbdd commit f830d9e
48 files changed
Lines changed: 1059 additions & 207 deletions
File tree
- .changeset
- docs
- advanced
- api
- migration
- structured-outputs
- tools
- examples
- ts-react-chat/src
- components
- routes
- ts-solid-chat/src/routes
- ts-vue-chat
- packages
- ai-angular/src
- ai-client
- src
- tests
- ai-preact/src
- ai-react
- src
- tests
- ai-solid/src
- ai-svelte/src
- ai-vue/src
- ai
- skills/ai-core/tool-calling
- src
- activities/chat
- stream
- tools
- tests
- testing/e2e
- src
- components
- lib
- routes
- tests
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
79 | 79 | | |
80 | 80 | | |
81 | 81 | | |
82 | | - | |
83 | 82 | | |
84 | 83 | | |
85 | 84 | | |
| |||
99 | 98 | | |
100 | 99 | | |
101 | 100 | | |
102 | | - | |
| 101 | + | |
103 | 102 | | |
104 | 103 | | |
105 | 104 | | |
| |||
184 | 183 | | |
185 | 184 | | |
186 | 185 | | |
187 | | - | |
| 186 | + | |
188 | 187 | | |
189 | 188 | | |
190 | 189 | | |
| |||
203 | 202 | | |
204 | 203 | | |
205 | 204 | | |
206 | | - | |
| 205 | + | |
207 | 206 | | |
208 | 207 | | |
209 | 208 | | |
| |||
221 | 220 | | |
222 | 221 | | |
223 | 222 | | |
224 | | - | |
225 | 223 | | |
226 | 224 | | |
227 | 225 | | |
| |||
240 | 238 | | |
241 | 239 | | |
242 | 240 | | |
243 | | - | |
| 241 | + | |
244 | 242 | | |
245 | 243 | | |
246 | 244 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
234 | 234 | | |
235 | 235 | | |
236 | 236 | | |
237 | | - | |
238 | 237 | | |
239 | 238 | | |
240 | 239 | | |
| |||
280 | 279 | | |
281 | 280 | | |
282 | 281 | | |
283 | | - | |
| 282 | + | |
284 | 283 | | |
285 | 284 | | |
286 | 285 | | |
| |||
526 | 525 | | |
527 | 526 | | |
528 | 527 | | |
529 | | - | |
530 | 528 | | |
531 | 529 | | |
532 | 530 | | |
533 | 531 | | |
534 | 532 | | |
535 | 533 | | |
536 | 534 | | |
537 | | - | |
| 535 | + | |
538 | 536 | | |
539 | 537 | | |
540 | 538 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
31 | | - | |
32 | 31 | | |
33 | 32 | | |
34 | 33 | | |
| |||
37 | 36 | | |
38 | 37 | | |
39 | 38 | | |
40 | | - | |
| 39 | + | |
41 | 40 | | |
42 | 41 | | |
43 | 42 | | |
| |||
281 | 280 | | |
282 | 281 | | |
283 | 282 | | |
284 | | - | |
| 283 | + | |
285 | 284 | | |
286 | 285 | | |
287 | 286 | | |
| |||
320 | 319 | | |
321 | 320 | | |
322 | 321 | | |
323 | | - | |
| 322 | + | |
324 | 323 | | |
325 | 324 | | |
326 | 325 | | |
| |||
348 | 347 | | |
349 | 348 | | |
350 | 349 | | |
351 | | - | |
352 | 350 | | |
353 | 351 | | |
354 | 352 | | |
355 | 353 | | |
356 | 354 | | |
357 | | - | |
| 355 | + | |
358 | 356 | | |
359 | 357 | | |
360 | 358 | | |
| |||
370 | 368 | | |
371 | 369 | | |
372 | 370 | | |
373 | | - | |
374 | 371 | | |
375 | 372 | | |
376 | 373 | | |
| |||
394 | 391 | | |
395 | 392 | | |
396 | 393 | | |
397 | | - | |
| 394 | + | |
398 | 395 | | |
399 | 396 | | |
400 | 397 | | |
| |||
454 | 451 | | |
455 | 452 | | |
456 | 453 | | |
457 | | - | |
| 454 | + | |
458 | 455 | | |
459 | 456 | | |
460 | 457 | | |
461 | 458 | | |
462 | | - | |
| 459 | + | |
463 | 460 | | |
464 | 461 | | |
465 | 462 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
30 | | - | |
31 | 30 | | |
32 | 31 | | |
33 | 32 | | |
| |||
50 | 49 | | |
51 | 50 | | |
52 | 51 | | |
53 | | - | |
| 52 | + | |
54 | 53 | | |
55 | 54 | | |
56 | 55 | | |
| |||
246 | 245 | | |
247 | 246 | | |
248 | 247 | | |
249 | | - | |
250 | 248 | | |
251 | 249 | | |
252 | 250 | | |
| |||
282 | 280 | | |
283 | 281 | | |
284 | 282 | | |
285 | | - | |
| 283 | + | |
286 | 284 | | |
287 | 285 | | |
288 | 286 | | |
| |||
311 | 309 | | |
312 | 310 | | |
313 | 311 | | |
314 | | - | |
315 | 312 | | |
316 | 313 | | |
317 | 314 | | |
318 | 315 | | |
319 | 316 | | |
320 | 317 | | |
321 | 318 | | |
322 | | - | |
| 319 | + | |
323 | 320 | | |
324 | 321 | | |
325 | 322 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
36 | | - | |
37 | 36 | | |
38 | 37 | | |
39 | 38 | | |
| |||
60 | 59 | | |
61 | 60 | | |
62 | 61 | | |
63 | | - | |
| 62 | + | |
64 | 63 | | |
65 | 64 | | |
66 | 65 | | |
| |||
275 | 274 | | |
276 | 275 | | |
277 | 276 | | |
278 | | - | |
279 | 277 | | |
280 | 278 | | |
281 | 279 | | |
| |||
319 | 317 | | |
320 | 318 | | |
321 | 319 | | |
322 | | - | |
| 320 | + | |
323 | 321 | | |
324 | 322 | | |
325 | 323 | | |
| |||
348 | 346 | | |
349 | 347 | | |
350 | 348 | | |
351 | | - | |
352 | 349 | | |
353 | 350 | | |
354 | 351 | | |
355 | 352 | | |
356 | 353 | | |
357 | 354 | | |
358 | 355 | | |
359 | | - | |
| 356 | + | |
360 | 357 | | |
361 | 358 | | |
362 | 359 | | |
| |||
0 commit comments