@tanstack/ai 0.19.1 → 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 (93) hide show
  1. package/dist/esm/activities/chat/adapter.d.ts +9 -3
  2. package/dist/esm/activities/chat/adapter.js +3 -1
  3. package/dist/esm/activities/chat/adapter.js.map +1 -1
  4. package/dist/esm/activities/chat/index.d.ts +13 -4
  5. package/dist/esm/activities/chat/index.js +46 -18
  6. package/dist/esm/activities/chat/index.js.map +1 -1
  7. package/dist/esm/activities/chat/messages.js +1 -1
  8. package/dist/esm/activities/chat/messages.js.map +1 -1
  9. package/dist/esm/activities/chat/middleware/compose.js +2 -0
  10. package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
  11. package/dist/esm/activities/chat/middleware/types.d.ts +8 -7
  12. package/dist/esm/activities/chat/stream/processor.d.ts +8 -8
  13. package/dist/esm/activities/chat/stream/processor.js +29 -21
  14. package/dist/esm/activities/chat/stream/processor.js.map +1 -1
  15. package/dist/esm/activities/chat/stream/strategies.d.ts +3 -3
  16. package/dist/esm/activities/chat/stream/strategies.js +4 -4
  17. package/dist/esm/activities/chat/stream/strategies.js.map +1 -1
  18. package/dist/esm/activities/chat/tools/lazy-tool-manager.js +5 -0
  19. package/dist/esm/activities/chat/tools/lazy-tool-manager.js.map +1 -1
  20. package/dist/esm/activities/chat/tools/schema-converter.d.ts +1 -1
  21. package/dist/esm/activities/chat/tools/schema-converter.js +36 -35
  22. package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -1
  23. package/dist/esm/activities/chat/tools/tool-calls.d.ts +2 -2
  24. package/dist/esm/activities/chat/tools/tool-calls.js +17 -9
  25. package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
  26. package/dist/esm/activities/chat/tools/tool-definition.js +1 -1
  27. package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
  28. package/dist/esm/activities/generateAudio/adapter.js +3 -1
  29. package/dist/esm/activities/generateAudio/adapter.js.map +1 -1
  30. package/dist/esm/activities/generateAudio/index.d.ts +5 -1
  31. package/dist/esm/activities/generateAudio/index.js.map +1 -1
  32. package/dist/esm/activities/generateImage/adapter.js +3 -1
  33. package/dist/esm/activities/generateImage/adapter.js.map +1 -1
  34. package/dist/esm/activities/generateImage/index.js +5 -0
  35. package/dist/esm/activities/generateImage/index.js.map +1 -1
  36. package/dist/esm/activities/generateSpeech/adapter.js +3 -1
  37. package/dist/esm/activities/generateSpeech/adapter.js.map +1 -1
  38. package/dist/esm/activities/generateTranscription/adapter.js +3 -1
  39. package/dist/esm/activities/generateTranscription/adapter.js.map +1 -1
  40. package/dist/esm/activities/generateVideo/adapter.js +3 -1
  41. package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
  42. package/dist/esm/activities/stream-generation-result.js +6 -2
  43. package/dist/esm/activities/stream-generation-result.js.map +1 -1
  44. package/dist/esm/activities/summarize/adapter.js +3 -1
  45. package/dist/esm/activities/summarize/adapter.js.map +1 -1
  46. package/dist/esm/activities/summarize/chat-stream-summarize.d.ts +1 -1
  47. package/dist/esm/activities/summarize/chat-stream-summarize.js +5 -0
  48. package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
  49. package/dist/esm/activities/summarize/index.js.map +1 -1
  50. package/dist/esm/index.d.ts +3 -0
  51. package/dist/esm/index.js +4 -0
  52. package/dist/esm/index.js.map +1 -1
  53. package/dist/esm/logger/internal-logger.js +2 -0
  54. package/dist/esm/logger/internal-logger.js.map +1 -1
  55. package/dist/esm/middlewares/content-guard.js +5 -4
  56. package/dist/esm/middlewares/content-guard.js.map +1 -1
  57. package/dist/esm/middlewares/otel.js +38 -16
  58. package/dist/esm/middlewares/otel.js.map +1 -1
  59. package/dist/esm/realtime/index.d.ts +1 -1
  60. package/dist/esm/realtime/index.js.map +1 -1
  61. package/dist/esm/system-prompts.d.ts +66 -0
  62. package/dist/esm/system-prompts.js +23 -0
  63. package/dist/esm/system-prompts.js.map +1 -0
  64. package/dist/esm/tools/provider-tool.d.ts +9 -0
  65. package/dist/esm/tools/provider-tool.js +7 -0
  66. package/dist/esm/tools/provider-tool.js.map +1 -0
  67. package/dist/esm/types.d.ts +22 -7
  68. package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
  69. package/dist/esm/utilities/chat-params.js.map +1 -1
  70. package/package.json +2 -2
  71. package/src/activities/chat/adapter.ts +11 -2
  72. package/src/activities/chat/index.ts +46 -27
  73. package/src/activities/chat/messages.ts +2 -1
  74. package/src/activities/chat/middleware/types.ts +14 -11
  75. package/src/activities/chat/stream/processor.ts +19 -19
  76. package/src/activities/chat/stream/strategies.ts +3 -3
  77. package/src/activities/chat/tools/schema-converter.ts +77 -52
  78. package/src/activities/chat/tools/tool-calls.ts +18 -11
  79. package/src/activities/chat/tools/tool-definition.ts +6 -1
  80. package/src/activities/generateAudio/index.ts +5 -4
  81. package/src/activities/generateImage/index.ts +5 -0
  82. package/src/activities/stream-generation-result.ts +11 -2
  83. package/src/activities/summarize/chat-stream-summarize.ts +5 -2
  84. package/src/activities/summarize/index.ts +2 -2
  85. package/src/index.ts +5 -0
  86. package/src/middlewares/content-guard.ts +11 -3
  87. package/src/middlewares/otel.ts +47 -16
  88. package/src/realtime/index.ts +1 -1
  89. package/src/system-prompts.ts +98 -0
  90. package/src/tools/provider-tool.ts +14 -0
  91. package/src/types.ts +28 -9
  92. package/src/utilities/ag-ui-wire.ts +2 -2
  93. package/src/utilities/chat-params.ts +2 -2
@@ -0,0 +1,98 @@
1
+ /**
2
+ * A single entry in `chat({ systemPrompts: [...] })`.
3
+ *
4
+ * Accepts a plain string (the common case) or a structured object that lets
5
+ * providers attach typed metadata to the prompt — e.g. Anthropic
6
+ * `cache_control` for prompt caching, future per-prompt safety overrides for
7
+ * Gemini, etc.
8
+ *
9
+ * At the chat call site, `metadata` is narrowed by the adapter via
10
+ * `~types['systemPromptMetadata']`. Providers that don't declare one inherit
11
+ * the default `never`, which makes the field carry no meaningful value: TS
12
+ * only accepts `undefined` there, and provider-foreign metadata that reaches
13
+ * an adapter via JS / `as any` is silently dropped, never written to the
14
+ * wire. For type-safe per-provider metadata, refer to the provider's
15
+ * `<Provider>SystemPromptMetadata` interface (e.g. `AnthropicSystemPromptMetadata`).
16
+ *
17
+ * @example
18
+ * // The 90% case — plain strings work everywhere.
19
+ * systemPrompts: ['Be concise.', 'Cite sources.']
20
+ *
21
+ * @example
22
+ * // Provider-specific metadata via the object form. No `satisfies` cast
23
+ * // is needed — the adapter narrows the `metadata` field's type at the
24
+ * // call site so users get autocomplete and structural checking
25
+ * // automatically.
26
+ * import { anthropicText } from '@tanstack/ai-anthropic'
27
+ *
28
+ * chat({
29
+ * adapter: anthropicText(),
30
+ * systemPrompts: [
31
+ * {
32
+ * content: 'Stable instructions — cache me.',
33
+ * metadata: { cache_control: { type: 'ephemeral' } },
34
+ * },
35
+ * 'Volatile per-request instruction.',
36
+ * ],
37
+ * })
38
+ */
39
+ export type SystemPrompt<TMetadata = unknown> =
40
+ | string
41
+ | {
42
+ content: string
43
+ metadata?: TMetadata
44
+ }
45
+
46
+ /**
47
+ * Normalised shape adapters see after the chat layer turns string entries
48
+ * into `{ content }` objects. Adapters call `normalizeSystemPrompts` once at
49
+ * the top of their option-mapping pipeline so the rest of the code only has
50
+ * to handle one shape.
51
+ */
52
+ export interface NormalizedSystemPrompt<TMetadata = unknown> {
53
+ content: string
54
+ metadata?: TMetadata
55
+ }
56
+
57
+ /**
58
+ * Normalise the public `systemPrompts` shape (`Array<string | { content, metadata? }>`)
59
+ * to a homogenous `Array<{ content, metadata? }>`. Adapters use this so they
60
+ * don't have to type-narrow string vs object inline.
61
+ *
62
+ * Returns an empty array (never `undefined`) so callers can chain `.map` /
63
+ * `.join` without an extra null check.
64
+ *
65
+ * Throws a `TypeError` (naming the offending index) if an object-form entry's
66
+ * `content` isn't a string. Public API boundary — callers reaching this
67
+ * function through `as any` / external JS would otherwise stream a literal
68
+ * `"undefined"` into the model's system prompt with no signal.
69
+ */
70
+ export function normalizeSystemPrompts<TMetadata = unknown>(
71
+ // Accept the wide public shape (`SystemPrompt<unknown>`) regardless of the
72
+ // caller's `TMetadata`. Adapters know their own metadata shape; the
73
+ // generic narrows the *output* so adapter code can read `p.metadata.X`
74
+ // without an additional cast.
75
+ prompts: ReadonlyArray<SystemPrompt> | undefined,
76
+ ): Array<NormalizedSystemPrompt<TMetadata>> {
77
+ if (!prompts || prompts.length === 0) return []
78
+ return prompts.map((p, i) => {
79
+ if (typeof p === 'string') return { content: p }
80
+ // Defence in depth: TypeScript narrows `p` to the object arm here, but
81
+ // this function is a public API boundary that callers can reach via
82
+ // plain JS or `as any`. Re-validate at runtime so we never stream a
83
+ // literal `"undefined"` into the model.
84
+ const candidate = p as unknown
85
+ if (candidate === null || typeof candidate !== 'object') {
86
+ throw new TypeError(
87
+ `systemPrompts[${i}]: expected a string or { content, metadata? }, got ${candidate === null ? 'null' : typeof candidate}`,
88
+ )
89
+ }
90
+ const { content } = candidate as { content?: unknown }
91
+ if (typeof content !== 'string') {
92
+ throw new TypeError(
93
+ `systemPrompts[${i}]: content must be a string, got ${typeof content}`,
94
+ )
95
+ }
96
+ return p as NormalizedSystemPrompt<TMetadata>
97
+ })
98
+ }
@@ -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
@@ -3,6 +3,7 @@ import type {
3
3
  StandardSchemaV1,
4
4
  } from '@standard-schema/spec'
5
5
  import type { InternalLogger } from './logger/internal-logger'
6
+ import type { SystemPrompt } from './system-prompts'
6
7
  import type {
7
8
  BaseEvent as AGUIBaseEvent,
8
9
  CustomEvent as AGUICustomEvent,
@@ -585,7 +586,9 @@ export interface Tool<
585
586
  * return weather; // Can return object or string
586
587
  * }
587
588
  */
588
- execute?: (args: any, context?: ToolExecutionContext) => Promise<any> | any
589
+ execute?:
590
+ | ((args: any, context?: ToolExecutionContext) => Promise<any> | any)
591
+ | undefined
589
592
 
590
593
  /** If true, tool execution requires user approval before running. Works with both server and client tools. */
591
594
  needsApproval?: boolean
@@ -594,7 +597,7 @@ export interface Tool<
594
597
  lazy?: boolean
595
598
 
596
599
  /** Additional metadata for adapters or custom extensions */
597
- metadata?: Record<string, any>
600
+ metadata?: Record<string, any> | undefined
598
601
  }
599
602
 
600
603
  export interface ToolConfig {
@@ -728,8 +731,22 @@ export interface TextOptions<
728
731
  > {
729
732
  model: string
730
733
  messages: Array<ModelMessage>
731
- tools?: Array<Tool<any, any, any>>
732
- systemPrompts?: Array<string>
734
+ tools?: Array<Tool<any, any, any>> | undefined
735
+ /**
736
+ * System prompts to include with the request.
737
+ *
738
+ * Accepts plain strings (the common case) or `{ content, metadata }`
739
+ * objects that let providers attach typed metadata (e.g. Anthropic
740
+ * `cache_control` for prompt caching) per prompt. At the chat call site
741
+ * the adapter narrows `metadata`'s type via `~types['systemPromptMetadata']`
742
+ * — providers that don't declare one default to `never`, which makes the
743
+ * field carry no meaningful value (TypeScript will only accept
744
+ * `undefined` there). Provider-foreign metadata that reaches an adapter
745
+ * via JS / `as any` is silently dropped, never written to the wire.
746
+ *
747
+ * @see SystemPrompt
748
+ */
749
+ systemPrompts?: Array<SystemPrompt>
733
750
  agentLoopStrategy?: AgentLoopStrategy
734
751
  /**
735
752
  * Controls the randomness of the output.
@@ -776,7 +793,7 @@ export interface TextOptions<
776
793
  * - Anthropic: `metadata` (Record<string, any>) - includes optional user_id (max 256 chars)
777
794
  * - Gemini: Not directly available in TextProviderOptions
778
795
  */
779
- metadata?: Record<string, any>
796
+ metadata?: Record<string, any> | undefined
780
797
  modelOptions?: TProviderOptionsForModel
781
798
  request?: Request | RequestInit
782
799
 
@@ -923,10 +940,12 @@ export interface RunErrorEvent extends AGUIRunErrorEvent {
923
940
  * @deprecated Use top-level `message` and `code` fields instead.
924
941
  * Kept for backward compatibility.
925
942
  */
926
- error?: {
927
- message: string
928
- code?: string
929
- }
943
+ error?:
944
+ | {
945
+ message: string
946
+ code?: string | undefined
947
+ }
948
+ | undefined
930
949
  }
931
950
 
932
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
  }