@tanstack/ai 0.20.0 → 0.20.1

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 (87) hide show
  1. package/dist/esm/activities/chat/adapter.js +3 -1
  2. package/dist/esm/activities/chat/adapter.js.map +1 -1
  3. package/dist/esm/activities/chat/index.d.ts +3 -3
  4. package/dist/esm/activities/chat/index.js +46 -18
  5. package/dist/esm/activities/chat/index.js.map +1 -1
  6. package/dist/esm/activities/chat/messages.js +1 -1
  7. package/dist/esm/activities/chat/messages.js.map +1 -1
  8. package/dist/esm/activities/chat/middleware/compose.js +2 -0
  9. package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
  10. package/dist/esm/activities/chat/middleware/types.d.ts +5 -5
  11. package/dist/esm/activities/chat/stream/processor.d.ts +8 -8
  12. package/dist/esm/activities/chat/stream/processor.js +29 -21
  13. package/dist/esm/activities/chat/stream/processor.js.map +1 -1
  14. package/dist/esm/activities/chat/stream/strategies.d.ts +3 -3
  15. package/dist/esm/activities/chat/stream/strategies.js +4 -4
  16. package/dist/esm/activities/chat/stream/strategies.js.map +1 -1
  17. package/dist/esm/activities/chat/tools/lazy-tool-manager.js +5 -0
  18. package/dist/esm/activities/chat/tools/lazy-tool-manager.js.map +1 -1
  19. package/dist/esm/activities/chat/tools/schema-converter.d.ts +1 -1
  20. package/dist/esm/activities/chat/tools/schema-converter.js +36 -35
  21. package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -1
  22. package/dist/esm/activities/chat/tools/tool-calls.d.ts +2 -2
  23. package/dist/esm/activities/chat/tools/tool-calls.js +17 -9
  24. package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
  25. package/dist/esm/activities/chat/tools/tool-definition.js +1 -1
  26. package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
  27. package/dist/esm/activities/generateAudio/adapter.js +3 -1
  28. package/dist/esm/activities/generateAudio/adapter.js.map +1 -1
  29. package/dist/esm/activities/generateAudio/index.d.ts +5 -1
  30. package/dist/esm/activities/generateAudio/index.js.map +1 -1
  31. package/dist/esm/activities/generateImage/adapter.js +3 -1
  32. package/dist/esm/activities/generateImage/adapter.js.map +1 -1
  33. package/dist/esm/activities/generateImage/index.js +5 -0
  34. package/dist/esm/activities/generateImage/index.js.map +1 -1
  35. package/dist/esm/activities/generateSpeech/adapter.js +3 -1
  36. package/dist/esm/activities/generateSpeech/adapter.js.map +1 -1
  37. package/dist/esm/activities/generateTranscription/adapter.js +3 -1
  38. package/dist/esm/activities/generateTranscription/adapter.js.map +1 -1
  39. package/dist/esm/activities/generateVideo/adapter.js +3 -1
  40. package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
  41. package/dist/esm/activities/stream-generation-result.js +6 -2
  42. package/dist/esm/activities/stream-generation-result.js.map +1 -1
  43. package/dist/esm/activities/summarize/adapter.js +3 -1
  44. package/dist/esm/activities/summarize/adapter.js.map +1 -1
  45. package/dist/esm/activities/summarize/chat-stream-summarize.d.ts +1 -1
  46. package/dist/esm/activities/summarize/chat-stream-summarize.js +5 -0
  47. package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
  48. package/dist/esm/activities/summarize/index.js.map +1 -1
  49. package/dist/esm/index.d.ts +1 -0
  50. package/dist/esm/index.js +2 -0
  51. package/dist/esm/index.js.map +1 -1
  52. package/dist/esm/logger/internal-logger.js +2 -0
  53. package/dist/esm/logger/internal-logger.js.map +1 -1
  54. package/dist/esm/middlewares/content-guard.js +5 -4
  55. package/dist/esm/middlewares/content-guard.js.map +1 -1
  56. package/dist/esm/middlewares/otel.js +24 -14
  57. package/dist/esm/middlewares/otel.js.map +1 -1
  58. package/dist/esm/realtime/index.d.ts +1 -1
  59. package/dist/esm/realtime/index.js.map +1 -1
  60. package/dist/esm/tools/provider-tool.d.ts +9 -0
  61. package/dist/esm/tools/provider-tool.js +7 -0
  62. package/dist/esm/tools/provider-tool.js.map +1 -0
  63. package/dist/esm/types.d.ts +6 -6
  64. package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
  65. package/dist/esm/utilities/chat-params.js.map +1 -1
  66. package/package.json +2 -2
  67. package/src/activities/chat/index.ts +34 -26
  68. package/src/activities/chat/messages.ts +2 -1
  69. package/src/activities/chat/middleware/types.ts +11 -9
  70. package/src/activities/chat/stream/processor.ts +19 -19
  71. package/src/activities/chat/stream/strategies.ts +3 -3
  72. package/src/activities/chat/tools/schema-converter.ts +77 -52
  73. package/src/activities/chat/tools/tool-calls.ts +18 -11
  74. package/src/activities/chat/tools/tool-definition.ts +6 -1
  75. package/src/activities/generateAudio/index.ts +5 -4
  76. package/src/activities/generateImage/index.ts +5 -0
  77. package/src/activities/stream-generation-result.ts +11 -2
  78. package/src/activities/summarize/chat-stream-summarize.ts +5 -2
  79. package/src/activities/summarize/index.ts +2 -2
  80. package/src/index.ts +1 -0
  81. package/src/middlewares/content-guard.ts +11 -3
  82. package/src/middlewares/otel.ts +25 -14
  83. package/src/realtime/index.ts +1 -1
  84. package/src/tools/provider-tool.ts +14 -0
  85. package/src/types.ts +12 -8
  86. package/src/utilities/ag-ui-wire.ts +2 -2
  87. package/src/utilities/chat-params.ts +2 -2
@@ -55,12 +55,21 @@ export async function* streamGenerationResult<TResult>(
55
55
  }
56
56
  } catch (error: unknown) {
57
57
  const payload = toRunErrorPayload(error, 'Generation failed')
58
+ // `code` is omitted entirely when undefined so the event matches the
59
+ // AG-UI `code?: string` shape under `exactOptionalPropertyTypes`. The
60
+ // deprecated nested `error` form preserves the same conditional
61
+ // structure for backward compatibility.
62
+ const codeFields =
63
+ payload.code !== undefined ? { code: payload.code } : undefined
58
64
  yield {
59
65
  type: EventType.RUN_ERROR,
60
66
  message: payload.message,
61
- code: payload.code,
67
+ ...codeFields,
62
68
  // Deprecated nested form for backward compatibility
63
- error: payload,
69
+ error: {
70
+ message: payload.message,
71
+ ...codeFields,
72
+ },
64
73
  timestamp: Date.now(),
65
74
  }
66
75
  }
@@ -46,7 +46,7 @@ export class ChatStreamSummarizeAdapter<
46
46
  > extends BaseSummarizeAdapter<TModel, TProviderOptions> {
47
47
  readonly name: string
48
48
 
49
- private textAdapter: ChatStreamCapable
49
+ private readonly textAdapter: ChatStreamCapable
50
50
 
51
51
  constructor(
52
52
  textAdapter: ChatStreamCapable,
@@ -124,7 +124,7 @@ export class ChatStreamSummarizeAdapter<
124
124
  return { id, model, summary, usage }
125
125
  }
126
126
 
127
- async *summarizeStream(
127
+ override async *summarizeStream(
128
128
  options: SummarizationOptions<TProviderOptions>,
129
129
  ): AsyncIterable<StreamChunk> {
130
130
  const systemPrompt = this.buildSummarizationPrompt(options)
@@ -221,6 +221,9 @@ export class ChatStreamSummarizeAdapter<
221
221
  case 'concise':
222
222
  prompt += 'Provide a very concise summary in 1-2 sentences. '
223
223
  break
224
+ case undefined:
225
+ prompt += 'Provide a clear and concise summary. '
226
+ break
224
227
  default:
225
228
  prompt += 'Provide a clear and concise summary. '
226
229
  }
@@ -159,7 +159,7 @@ export function summarize<
159
159
 
160
160
  if (stream) {
161
161
  return runStreamingSummarize(
162
- options as unknown as SummarizeActivityOptions<
162
+ options as SummarizeActivityOptions<
163
163
  SummarizeAdapter<string, object>,
164
164
  true
165
165
  >,
@@ -167,7 +167,7 @@ export function summarize<
167
167
  }
168
168
 
169
169
  return runSummarize(
170
- options as unknown as SummarizeActivityOptions<
170
+ options as SummarizeActivityOptions<
171
171
  SummarizeAdapter<string, object>,
172
172
  false
173
173
  >,
package/src/index.ts CHANGED
@@ -69,6 +69,7 @@ export { ToolCallManager } from './activities/chat/tools/tool-calls'
69
69
 
70
70
  // Provider tool type
71
71
  export type { ProviderTool } from './tools/provider-tool'
72
+ export { brandProviderTool } from './tools/provider-tool'
72
73
 
73
74
  // Agent loop strategies
74
75
  export {
@@ -142,10 +142,15 @@ function createDeltaStrategy(
142
142
 
143
143
  if (blockOnMatch) return null // drop chunk
144
144
 
145
+ // Strip out the previous `content` field by destructuring it away — with
146
+ // `exactOptionalPropertyTypes` we can't assign `content: undefined`
147
+ // against `content?: string`. The replacement event carries only the
148
+ // filtered delta.
149
+ const { content: _strippedContent, ...rest } = chunk
150
+ void _strippedContent
145
151
  return {
146
- ...chunk,
152
+ ...rest,
147
153
  delta: filtered,
148
- content: undefined,
149
154
  } as StreamChunk
150
155
  },
151
156
  }
@@ -278,8 +283,11 @@ function createBufferedStrategy(
278
283
  content: filtered.slice(0, safeFilteredEnd),
279
284
  } as StreamChunk
280
285
 
286
+ // `pending` was empty before this push iff `emitChunk` is now the only
287
+ // entry — return it directly without re-indexing through `pending[0]`.
288
+ const wasEmpty = pending.length === 0
281
289
  pending.push(emitChunk)
282
- return pending.length === 1 ? pending[0]! : pending
290
+ return wasEmpty ? emitChunk : pending
283
291
  },
284
292
  }
285
293
  }
@@ -137,8 +137,11 @@ function serializeContent(content: unknown): string {
137
137
  case 'document':
138
138
  parts.push('[document]')
139
139
  break
140
+ case undefined:
141
+ parts.push('[unknown]')
142
+ break
140
143
  default:
141
- parts.push(`[${type ?? 'unknown'}]`)
144
+ parts.push(`[${type}]`)
142
145
  }
143
146
  }
144
147
  return parts.join(' ')
@@ -630,9 +633,10 @@ export function otelMiddleware(options: OtelMiddlewareOptions): ChatMiddleware {
630
633
 
631
634
  if (!info.ok && info.error !== undefined) {
632
635
  toolSpan.recordException(info.error as Exception)
636
+ const msg = errorMessage(info.error)
633
637
  toolSpan.setStatus({
634
638
  code: SpanStatusCode.ERROR,
635
- message: errorMessage(info.error),
639
+ ...(msg !== undefined && { message: msg }),
636
640
  })
637
641
  }
638
642
 
@@ -688,13 +692,16 @@ export function otelMiddleware(options: OtelMiddlewareOptions): ChatMiddleware {
688
692
 
689
693
  const errType = errorTypeName(info.error)
690
694
  const message = errorMessage(info.error)
695
+ const statusMessage =
696
+ message !== undefined ? { message } : ({} as const)
691
697
  const exception = info.error as Exception
692
698
 
693
- if (state.currentIterationSpan) {
694
- state.currentIterationSpan.recordException(exception)
695
- state.currentIterationSpan.setStatus({
699
+ const iterationSpan = state.currentIterationSpan
700
+ if (iterationSpan) {
701
+ iterationSpan.recordException(exception)
702
+ iterationSpan.setStatus({
696
703
  code: SpanStatusCode.ERROR,
697
- message,
704
+ ...statusMessage,
698
705
  })
699
706
  safeCall('otel.onSpanEnd', () =>
700
707
  onSpanEnd?.(
@@ -703,17 +710,17 @@ export function otelMiddleware(options: OtelMiddlewareOptions): ChatMiddleware {
703
710
  ctx,
704
711
  iteration: state.iterationCount - 1,
705
712
  } as OtelSpanInfo<'iteration'>,
706
- state.currentIterationSpan!,
713
+ iterationSpan,
707
714
  ),
708
715
  )
709
- state.currentIterationSpan.end()
716
+ iterationSpan.end()
710
717
  state.currentIterationSpan = null
711
718
  }
712
719
 
713
720
  for (const [id, entry] of state.toolSpans) {
714
721
  const { span, toolName } = entry
715
722
  span.recordException(exception)
716
- span.setStatus({ code: SpanStatusCode.ERROR, message })
723
+ span.setStatus({ code: SpanStatusCode.ERROR, ...statusMessage })
717
724
  safeCall('otel.onSpanEnd', () =>
718
725
  onSpanEnd?.(
719
726
  {
@@ -731,7 +738,10 @@ export function otelMiddleware(options: OtelMiddlewareOptions): ChatMiddleware {
731
738
  }
732
739
 
733
740
  state.rootSpan.recordException(exception)
734
- state.rootSpan.setStatus({ code: SpanStatusCode.ERROR, message })
741
+ state.rootSpan.setStatus({
742
+ code: SpanStatusCode.ERROR,
743
+ ...statusMessage,
744
+ })
735
745
 
736
746
  if (durationHistogram) {
737
747
  durationHistogram.record(info.duration / 1000, {
@@ -763,8 +773,9 @@ export function otelMiddleware(options: OtelMiddlewareOptions): ChatMiddleware {
763
773
  span.setStatus({ code: SpanStatusCode.ERROR, message: 'cancelled' })
764
774
  }
765
775
 
766
- if (state.currentIterationSpan) {
767
- closeCancelled(state.currentIterationSpan)
776
+ const iterationSpan = state.currentIterationSpan
777
+ if (iterationSpan) {
778
+ closeCancelled(iterationSpan)
768
779
  safeCall('otel.onSpanEnd', () =>
769
780
  onSpanEnd?.(
770
781
  {
@@ -772,10 +783,10 @@ export function otelMiddleware(options: OtelMiddlewareOptions): ChatMiddleware {
772
783
  ctx,
773
784
  iteration: state.iterationCount - 1,
774
785
  } as OtelSpanInfo<'iteration'>,
775
- state.currentIterationSpan!,
786
+ iterationSpan,
776
787
  ),
777
788
  )
778
- state.currentIterationSpan.end()
789
+ iterationSpan.end()
779
790
  state.currentIterationSpan = null
780
791
  }
781
792
  for (const [id, entry] of state.toolSpans) {
@@ -1,7 +1,7 @@
1
1
  import type { RealtimeToken, RealtimeTokenOptions } from './types'
2
2
 
3
3
  // Re-export all types
4
- export * from './types'
4
+ export type * from './types'
5
5
 
6
6
  /**
7
7
  * Generate a realtime token using the provided adapter.
@@ -23,3 +23,17 @@ export interface ProviderTool<
23
23
  readonly '~provider': TProvider
24
24
  readonly '~toolKind': TKind
25
25
  }
26
+
27
+ /**
28
+ * Attach the `ProviderTool` phantom brand to a plain `Tool`-shaped object.
29
+ *
30
+ * The brand fields (`'~provider'`, `'~toolKind'`) exist only in the type
31
+ * system and are never assigned at runtime, so this is a single audited
32
+ * type-only assertion. Use it inside adapter `xxxTool()` factories instead
33
+ * of `as unknown as` — the cast collapses to one named site.
34
+ */
35
+ export function brandProviderTool<T extends ProviderTool<string, string>>(
36
+ tool: Omit<T, '~provider' | '~toolKind'>,
37
+ ): T {
38
+ return tool as T
39
+ }
package/src/types.ts CHANGED
@@ -586,7 +586,9 @@ export interface Tool<
586
586
  * return weather; // Can return object or string
587
587
  * }
588
588
  */
589
- execute?: (args: any, context?: ToolExecutionContext) => Promise<any> | any
589
+ execute?:
590
+ | ((args: any, context?: ToolExecutionContext) => Promise<any> | any)
591
+ | undefined
590
592
 
591
593
  /** If true, tool execution requires user approval before running. Works with both server and client tools. */
592
594
  needsApproval?: boolean
@@ -595,7 +597,7 @@ export interface Tool<
595
597
  lazy?: boolean
596
598
 
597
599
  /** Additional metadata for adapters or custom extensions */
598
- metadata?: Record<string, any>
600
+ metadata?: Record<string, any> | undefined
599
601
  }
600
602
 
601
603
  export interface ToolConfig {
@@ -729,7 +731,7 @@ export interface TextOptions<
729
731
  > {
730
732
  model: string
731
733
  messages: Array<ModelMessage>
732
- tools?: Array<Tool<any, any, any>>
734
+ tools?: Array<Tool<any, any, any>> | undefined
733
735
  /**
734
736
  * System prompts to include with the request.
735
737
  *
@@ -791,7 +793,7 @@ export interface TextOptions<
791
793
  * - Anthropic: `metadata` (Record<string, any>) - includes optional user_id (max 256 chars)
792
794
  * - Gemini: Not directly available in TextProviderOptions
793
795
  */
794
- metadata?: Record<string, any>
796
+ metadata?: Record<string, any> | undefined
795
797
  modelOptions?: TProviderOptionsForModel
796
798
  request?: Request | RequestInit
797
799
 
@@ -938,10 +940,12 @@ export interface RunErrorEvent extends AGUIRunErrorEvent {
938
940
  * @deprecated Use top-level `message` and `code` fields instead.
939
941
  * Kept for backward compatibility.
940
942
  */
941
- error?: {
942
- message: string
943
- code?: string
944
- }
943
+ error?:
944
+ | {
945
+ message: string
946
+ code?: string | undefined
947
+ }
948
+ | undefined
945
949
  }
946
950
 
947
951
  /**
@@ -61,7 +61,7 @@ export function uiMessagesToWire(
61
61
  content:
62
62
  parts.length > 0
63
63
  ? collectText(parts)
64
- : ((msg as unknown as { content?: string }).content ?? ''),
64
+ : ((msg as { content?: string }).content ?? ''),
65
65
  })
66
66
  continue
67
67
  }
@@ -72,7 +72,7 @@ export function uiMessagesToWire(
72
72
  content:
73
73
  parts.length > 0
74
74
  ? collectUserContent(parts)
75
- : ((msg as unknown as { content?: string }).content ?? ''),
75
+ : ((msg as { content?: string }).content ?? ''),
76
76
  })
77
77
  continue
78
78
  }
@@ -131,7 +131,7 @@ export async function chatParamsFromRequest(
131
131
  'Invalid AG-UI request body. See docs/migration/ag-ui-compliance.md.',
132
132
  { status: 400 },
133
133
  )
134
- ;(res as unknown as { cause?: unknown }).cause = cause
134
+ ;(res as { cause?: unknown }).cause = cause
135
135
  throw res
136
136
  }
137
137
  try {
@@ -145,7 +145,7 @@ export async function chatParamsFromRequest(
145
145
  'Invalid AG-UI request body. See docs/migration/ag-ui-compliance.md.',
146
146
  { status: 400 },
147
147
  )
148
- ;(res as unknown as { cause?: unknown }).cause = cause
148
+ ;(res as { cause?: unknown }).cause = cause
149
149
  throw res
150
150
  }
151
151
  }