@tanstack/ai 0.12.0 → 0.13.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.
Files changed (58) hide show
  1. package/dist/esm/activities/chat/adapter.d.ts +6 -1
  2. package/dist/esm/activities/chat/adapter.js.map +1 -1
  3. package/dist/esm/activities/chat/index.d.ts +8 -0
  4. package/dist/esm/activities/chat/index.js +78 -17
  5. package/dist/esm/activities/chat/index.js.map +1 -1
  6. package/dist/esm/activities/chat/middleware/compose.d.ts +3 -1
  7. package/dist/esm/activities/chat/middleware/compose.js +79 -1
  8. package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
  9. package/dist/esm/activities/generateImage/index.d.ts +7 -0
  10. package/dist/esm/activities/generateImage/index.js +19 -3
  11. package/dist/esm/activities/generateImage/index.js.map +1 -1
  12. package/dist/esm/activities/generateSpeech/index.d.ts +7 -0
  13. package/dist/esm/activities/generateSpeech/index.js +21 -3
  14. package/dist/esm/activities/generateSpeech/index.js.map +1 -1
  15. package/dist/esm/activities/generateTranscription/index.d.ts +7 -0
  16. package/dist/esm/activities/generateTranscription/index.js +32 -13
  17. package/dist/esm/activities/generateTranscription/index.js.map +1 -1
  18. package/dist/esm/activities/generateVideo/index.d.ts +7 -0
  19. package/dist/esm/activities/generateVideo/index.js +49 -7
  20. package/dist/esm/activities/generateVideo/index.js.map +1 -1
  21. package/dist/esm/activities/summarize/index.d.ts +7 -0
  22. package/dist/esm/activities/summarize/index.js +54 -19
  23. package/dist/esm/activities/summarize/index.js.map +1 -1
  24. package/dist/esm/adapter-internals.d.ts +3 -0
  25. package/dist/esm/adapter-internals.js +7 -0
  26. package/dist/esm/adapter-internals.js.map +1 -0
  27. package/dist/esm/index.d.ts +2 -0
  28. package/dist/esm/index.js +2 -0
  29. package/dist/esm/index.js.map +1 -1
  30. package/dist/esm/logger/console-logger.d.ts +11 -0
  31. package/dist/esm/logger/console-logger.js +27 -0
  32. package/dist/esm/logger/console-logger.js.map +1 -0
  33. package/dist/esm/logger/internal-logger.d.ts +33 -0
  34. package/dist/esm/logger/internal-logger.js +69 -0
  35. package/dist/esm/logger/internal-logger.js.map +1 -0
  36. package/dist/esm/logger/resolve.d.ts +14 -0
  37. package/dist/esm/logger/resolve.js +54 -0
  38. package/dist/esm/logger/resolve.js.map +1 -0
  39. package/dist/esm/logger/types.d.ts +75 -0
  40. package/dist/esm/types.d.ts +34 -0
  41. package/package.json +6 -2
  42. package/skills/ai-core/SKILL.md +5 -3
  43. package/skills/ai-core/debug-logging/SKILL.md +263 -0
  44. package/src/activities/chat/adapter.ts +6 -1
  45. package/src/activities/chat/index.ts +104 -22
  46. package/src/activities/chat/middleware/compose.ts +84 -1
  47. package/src/activities/generateImage/index.ts +29 -3
  48. package/src/activities/generateSpeech/index.ts +35 -3
  49. package/src/activities/generateTranscription/index.ts +45 -13
  50. package/src/activities/generateVideo/index.ts +65 -6
  51. package/src/activities/summarize/index.ts +66 -20
  52. package/src/adapter-internals.ts +7 -0
  53. package/src/index.ts +9 -0
  54. package/src/logger/console-logger.ts +49 -0
  55. package/src/logger/internal-logger.ts +107 -0
  56. package/src/logger/resolve.ts +72 -0
  57. package/src/logger/types.ts +78 -0
  58. package/src/types.ts +36 -0
@@ -8,6 +8,9 @@
8
8
  */
9
9
 
10
10
  import { aiEventClient } from '@tanstack/ai-event-client'
11
+ import { resolveDebugOption } from '../../logger/resolve'
12
+ import type { InternalLogger } from '../../logger/internal-logger'
13
+ import type { DebugOption } from '../../logger/types'
11
14
  import type { VideoAdapter } from './adapter'
12
15
  import type {
13
16
  StreamChunk,
@@ -100,6 +103,12 @@ export type VideoCreateOptions<
100
103
  maxDuration?: number
101
104
  /** Custom run ID (stream mode only) */
102
105
  runId?: string
106
+ /**
107
+ * Enable debug logging. Pass `true` to enable all categories, `false` to
108
+ * silence everything including errors, or a `DebugConfig` object for granular
109
+ * control and/or a custom `Logger`.
110
+ */
111
+ debug?: DebugOption
103
112
  } & ({} extends VideoProviderOptions<TAdapter>
104
113
  ? {
105
114
  /** Provider-specific options for video generation */ modelOptions?: VideoProviderOptions<TAdapter>
@@ -241,14 +250,38 @@ async function runCreateVideoJob<
241
250
  >(options: VideoCreateOptions<TAdapter, boolean>): Promise<VideoJobResult> {
242
251
  const { adapter, prompt, size, duration, modelOptions } = options
243
252
  const model = adapter.model
244
-
245
- return adapter.createVideoJob({
253
+ const logger: InternalLogger = resolveDebugOption(options.debug)
254
+ const providerName =
255
+ (adapter as { name?: string; provider?: string }).provider ??
256
+ (adapter as { name?: string }).name ??
257
+ 'unknown'
258
+
259
+ logger.request(`activity=generateVideo provider=${providerName}`, {
260
+ provider: providerName,
246
261
  model,
247
- prompt,
248
- size,
249
- duration,
250
- modelOptions,
251
262
  })
263
+
264
+ try {
265
+ const result = await adapter.createVideoJob({
266
+ model,
267
+ prompt,
268
+ size,
269
+ duration,
270
+ modelOptions,
271
+ logger,
272
+ })
273
+ logger.output(`activity=generateVideo jobId=${result.jobId}`, {
274
+ jobId: result.jobId,
275
+ model: result.model,
276
+ })
277
+ return result
278
+ } catch (error) {
279
+ logger.errors('generateVideo activity failed', {
280
+ error,
281
+ source: 'generateVideo',
282
+ })
283
+ throw error
284
+ }
252
285
  }
253
286
 
254
287
  function sleep(ms: number): Promise<void> {
@@ -267,6 +300,11 @@ async function* runStreamingVideoGeneration<
267
300
  const runId = options.runId ?? createId('run')
268
301
  const pollingInterval = options.pollingInterval ?? 2000
269
302
  const maxDuration = options.maxDuration ?? 600_000
303
+ const logger: InternalLogger = resolveDebugOption(options.debug)
304
+ const providerName =
305
+ (adapter as { name?: string; provider?: string }).provider ??
306
+ (adapter as { name?: string }).name ??
307
+ 'unknown'
270
308
 
271
309
  const threadId = createId('thread')
272
310
 
@@ -277,6 +315,14 @@ async function* runStreamingVideoGeneration<
277
315
  timestamp: Date.now(),
278
316
  } as StreamChunk
279
317
 
318
+ logger.request(
319
+ `activity=generateVideo provider=${providerName} stream=true`,
320
+ {
321
+ provider: providerName,
322
+ model,
323
+ },
324
+ )
325
+
280
326
  try {
281
327
  // Create the video generation job
282
328
  const jobResult = await adapter.createVideoJob({
@@ -285,6 +331,7 @@ async function* runStreamingVideoGeneration<
285
331
  size,
286
332
  duration,
287
333
  modelOptions,
334
+ logger,
288
335
  })
289
336
 
290
337
  yield {
@@ -316,6 +363,14 @@ async function* runStreamingVideoGeneration<
316
363
  if (statusResult.status === 'completed') {
317
364
  const urlResult = await adapter.getVideoUrl(jobResult.jobId)
318
365
 
366
+ logger.output(
367
+ `activity=generateVideo jobId=${jobResult.jobId} status=completed`,
368
+ {
369
+ jobId: jobResult.jobId,
370
+ url: urlResult.url,
371
+ },
372
+ )
373
+
319
374
  yield {
320
375
  type: 'CUSTOM',
321
376
  name: 'generation:result',
@@ -345,6 +400,10 @@ async function* runStreamingVideoGeneration<
345
400
 
346
401
  throw new Error('Video generation timed out')
347
402
  } catch (error: any) {
403
+ logger.errors('generateVideo activity failed', {
404
+ error,
405
+ source: 'generateVideo',
406
+ })
348
407
  yield {
349
408
  type: 'RUN_ERROR',
350
409
  runId,
@@ -7,6 +7,9 @@
7
7
 
8
8
  import { aiEventClient } from '@tanstack/ai-event-client'
9
9
  import { streamGenerationResult } from '../stream-generation-result.js'
10
+ import { resolveDebugOption } from '../../logger/resolve'
11
+ import type { InternalLogger } from '../../logger/internal-logger'
12
+ import type { DebugOption } from '../../logger/types'
10
13
  import type { SummarizeAdapter } from './adapter'
11
14
  import type {
12
15
  StreamChunk,
@@ -66,6 +69,12 @@ export interface SummarizeActivityOptions<
66
69
  * @default false
67
70
  */
68
71
  stream?: TStream
72
+ /**
73
+ * Enable debug logging. Pass `true` to enable all categories, `false` to
74
+ * silence everything including errors, or a `DebugConfig` object for granular
75
+ * control and/or a custom `Logger`.
76
+ */
77
+ debug?: DebugOption
69
78
  }
70
79
 
71
80
  // ===========================
@@ -180,6 +189,7 @@ async function runSummarize(
180
189
  const requestId = createId('summarize')
181
190
  const inputLength = text.length
182
191
  const startTime = Date.now()
192
+ const logger: InternalLogger = resolveDebugOption(options.debug)
183
193
 
184
194
  aiEventClient.emit('summarize:request:started', {
185
195
  requestId,
@@ -189,30 +199,50 @@ async function runSummarize(
189
199
  timestamp: startTime,
190
200
  })
191
201
 
202
+ logger.request(`activity=summarize provider=${adapter.name}`, {
203
+ provider: adapter.name,
204
+ model,
205
+ inputLength,
206
+ })
207
+
192
208
  const summarizeOptions: SummarizationOptions = {
193
209
  model,
194
210
  text,
195
211
  maxLength,
196
212
  style,
197
213
  focus,
214
+ logger,
198
215
  }
199
216
 
200
- const result = await adapter.summarize(summarizeOptions)
217
+ try {
218
+ const result = await adapter.summarize(summarizeOptions)
201
219
 
202
- const duration = Date.now() - startTime
203
- const outputLength = result.summary.length
220
+ const duration = Date.now() - startTime
221
+ const outputLength = result.summary.length
204
222
 
205
- aiEventClient.emit('summarize:request:completed', {
206
- requestId,
207
- provider: adapter.name,
208
- model,
209
- inputLength,
210
- outputLength,
211
- duration,
212
- timestamp: Date.now(),
213
- })
223
+ aiEventClient.emit('summarize:request:completed', {
224
+ requestId,
225
+ provider: adapter.name,
226
+ model,
227
+ inputLength,
228
+ outputLength,
229
+ duration,
230
+ timestamp: Date.now(),
231
+ })
232
+
233
+ logger.output(`activity=summarize length=${outputLength}`, {
234
+ hasSummary: !!result.summary,
235
+ outputLength,
236
+ })
214
237
 
215
- return result
238
+ return result
239
+ } catch (error) {
240
+ logger.errors('summarize activity failed', {
241
+ error,
242
+ source: 'summarize',
243
+ })
244
+ throw error
245
+ }
216
246
  }
217
247
 
218
248
  /**
@@ -225,6 +255,13 @@ async function* runStreamingSummarize(
225
255
  ): AsyncIterable<StreamChunk> {
226
256
  const { adapter, text, maxLength, style, focus } = options
227
257
  const model = adapter.model
258
+ const logger: InternalLogger = resolveDebugOption(options.debug)
259
+
260
+ logger.request(`activity=summarize provider=${adapter.name}`, {
261
+ provider: adapter.name,
262
+ model,
263
+ stream: true,
264
+ })
228
265
 
229
266
  const summarizeOptions: SummarizationOptions = {
230
267
  model,
@@ -232,16 +269,25 @@ async function* runStreamingSummarize(
232
269
  maxLength,
233
270
  style,
234
271
  focus,
272
+ logger,
235
273
  }
236
274
 
237
- // Use real streaming if the adapter supports it
238
- if (adapter.summarizeStream) {
239
- yield* adapter.summarizeStream(summarizeOptions)
240
- return
241
- }
275
+ try {
276
+ // Use real streaming if the adapter supports it
277
+ if (adapter.summarizeStream) {
278
+ yield* adapter.summarizeStream(summarizeOptions)
279
+ return
280
+ }
242
281
 
243
- // Fall back to non-streaming — wrap result with streamGenerationResult
244
- yield* streamGenerationResult(() => adapter.summarize(summarizeOptions))
282
+ // Fall back to non-streaming — wrap result with streamGenerationResult
283
+ yield* streamGenerationResult(() => adapter.summarize(summarizeOptions))
284
+ } catch (error) {
285
+ logger.errors('summarize activity failed', {
286
+ error,
287
+ source: 'summarize',
288
+ })
289
+ throw error
290
+ }
245
291
  }
246
292
 
247
293
  // ===========================
@@ -0,0 +1,7 @@
1
+ // NOTE: This module is exposed ONLY via the `@tanstack/ai/adapter-internals`
2
+ // subpath export. It gives provider adapter packages access to the internal
3
+ // logger plumbing without leaking those symbols to end users.
4
+
5
+ export type { ResolvedCategories } from './logger/internal-logger'
6
+ export { InternalLogger } from './logger/internal-logger'
7
+ export { resolveDebugOption } from './logger/resolve'
package/src/index.ts CHANGED
@@ -167,3 +167,12 @@ export type {
167
167
  // Adapter extension utilities
168
168
  export { createModel, extendAdapter } from './extend-adapter'
169
169
  export type { ExtendedModelDef } from './extend-adapter'
170
+
171
+ // Logger
172
+ export type {
173
+ Logger,
174
+ DebugCategories,
175
+ DebugConfig,
176
+ DebugOption,
177
+ } from './logger/types'
178
+ export { ConsoleLogger } from './logger/console-logger'
@@ -0,0 +1,49 @@
1
+ import type { Logger } from './types'
2
+
3
+ /**
4
+ * Default `Logger` implementation that routes each level to the matching
5
+ * `console` method:
6
+ *
7
+ * - `debug` → `console.debug`
8
+ * - `info` → `console.info`
9
+ * - `warn` → `console.warn`
10
+ * - `error` → `console.error`
11
+ *
12
+ * When a `meta` object is supplied, the message is logged first and the meta
13
+ * object is then printed via `console.dir(meta, { depth: null, colors: true })`
14
+ * so deeply nested structures (e.g. provider chunk payloads with `usage`,
15
+ * `output`, `reasoning`, `tools`) render in full instead of truncating to
16
+ * `[Object]` / `[Array]`. On Node this produces a depth-unlimited inspect
17
+ * dump; browsers present the object as an interactive tree (extra options
18
+ * are ignored).
19
+ *
20
+ * This is the logger used when `debug` is enabled on any activity and no
21
+ * custom `logger` is supplied via `debug: { logger }`.
22
+ */
23
+ const DIR_OPTIONS = { depth: null, colors: true } as const
24
+
25
+ export class ConsoleLogger implements Logger {
26
+ /** Log a debug-level message; forwards to `console.debug`. */
27
+ debug(message: string, meta?: Record<string, unknown>): void {
28
+ console.debug(message)
29
+ if (meta !== undefined) console.dir(meta, DIR_OPTIONS)
30
+ }
31
+
32
+ /** Log an info-level message; forwards to `console.info`. */
33
+ info(message: string, meta?: Record<string, unknown>): void {
34
+ console.info(message)
35
+ if (meta !== undefined) console.dir(meta, DIR_OPTIONS)
36
+ }
37
+
38
+ /** Log a warning-level message; forwards to `console.warn`. */
39
+ warn(message: string, meta?: Record<string, unknown>): void {
40
+ console.warn(message)
41
+ if (meta !== undefined) console.dir(meta, DIR_OPTIONS)
42
+ }
43
+
44
+ /** Log an error-level message; forwards to `console.error`. */
45
+ error(message: string, meta?: Record<string, unknown>): void {
46
+ console.error(message)
47
+ if (meta !== undefined) console.dir(meta, DIR_OPTIONS)
48
+ }
49
+ }
@@ -0,0 +1,107 @@
1
+ import type { DebugCategories, Logger } from './types'
2
+
3
+ /**
4
+ * Fully-resolved categories map. Every flag is a definite boolean (never
5
+ * undefined), produced by `resolveDebugOption` from a `DebugOption`.
6
+ */
7
+ export type ResolvedCategories = Required<DebugCategories>
8
+
9
+ /**
10
+ * Package-internal logger wrapper used by every activity and adapter in
11
+ * `@tanstack/ai`. Wraps a user-supplied (or default `ConsoleLogger`) `Logger`
12
+ * plus a fully-resolved per-category map. Each category has a dedicated
13
+ * method that no-ops when its flag is `false`, or prepends a
14
+ * `[tanstack-ai:<category>] ` prefix and calls the underlying logger's
15
+ * `error` (for the `errors` category) or `debug` (for everything else).
16
+ *
17
+ * Not exported from the package root. Adapter packages consume it via the
18
+ * `@tanstack/ai/adapter-internals` subpath export.
19
+ */
20
+ /**
21
+ * Emoji marker per category — bracketing the `[tanstack-ai:<cat>]` tag on
22
+ * both sides makes it trivial to visually pick out a category when scanning
23
+ * dense streaming logs.
24
+ */
25
+ const CATEGORY_EMOJI: Record<keyof ResolvedCategories, string> = {
26
+ request: '📤',
27
+ provider: '📥',
28
+ output: '📨',
29
+ middleware: '🧩',
30
+ tools: '🔧',
31
+ agentLoop: '🔁',
32
+ config: '⚙️',
33
+ errors: '❌',
34
+ }
35
+
36
+ export class InternalLogger {
37
+ constructor(
38
+ private readonly logger: Logger,
39
+ private readonly categories: ResolvedCategories,
40
+ ) {}
41
+
42
+ /** Whether a category is enabled. Cheap, safe to call on hot paths. */
43
+ isEnabled(category: keyof ResolvedCategories): boolean {
44
+ return this.categories[category]
45
+ }
46
+
47
+ private emit(
48
+ level: 'debug' | 'error',
49
+ category: keyof ResolvedCategories,
50
+ message: string,
51
+ meta?: Record<string, unknown>,
52
+ ): void {
53
+ if (!this.categories[category]) return
54
+ const emoji = CATEGORY_EMOJI[category]
55
+ const prefixed = `${emoji} [tanstack-ai:${category}] ${emoji} ${message}`
56
+ try {
57
+ if (level === 'error') this.logger.error(prefixed, meta)
58
+ else this.logger.debug(prefixed, meta)
59
+ } catch {
60
+ // User-supplied logger threw; swallow so we never mask the original
61
+ // error that triggered this log call.
62
+ }
63
+ }
64
+
65
+ /** Log a raw chunk/frame received from a provider SDK. */
66
+ provider(message: string, meta?: Record<string, unknown>): void {
67
+ this.emit('debug', 'provider', message, meta)
68
+ }
69
+
70
+ /** Log a chunk/result yielded to the consumer after middleware. */
71
+ output(message: string, meta?: Record<string, unknown>): void {
72
+ this.emit('debug', 'output', message, meta)
73
+ }
74
+
75
+ /** Log inputs/outputs around a middleware hook invocation. Chat-only. */
76
+ middleware(message: string, meta?: Record<string, unknown>): void {
77
+ this.emit('debug', 'middleware', message, meta)
78
+ }
79
+
80
+ /** Log before/after a tool-call execution. Chat-only. */
81
+ tools(message: string, meta?: Record<string, unknown>): void {
82
+ this.emit('debug', 'tools', message, meta)
83
+ }
84
+
85
+ /** Log an agent-loop iteration marker or phase transition. Chat-only. */
86
+ agentLoop(message: string, meta?: Record<string, unknown>): void {
87
+ this.emit('debug', 'agentLoop', message, meta)
88
+ }
89
+
90
+ /** Log a config transform returned by a middleware `onConfig` hook. Chat-only. */
91
+ config(message: string, meta?: Record<string, unknown>): void {
92
+ this.emit('debug', 'config', message, meta)
93
+ }
94
+
95
+ /**
96
+ * Log a caught error. Defaults to on even when `debug` is unspecified.
97
+ * Uses the underlying logger's `error` level.
98
+ */
99
+ errors(message: string, meta?: Record<string, unknown>): void {
100
+ this.emit('error', 'errors', message, meta)
101
+ }
102
+
103
+ /** Log outgoing request metadata before an adapter SDK call. */
104
+ request(message: string, meta?: Record<string, unknown>): void {
105
+ this.emit('debug', 'request', message, meta)
106
+ }
107
+ }
@@ -0,0 +1,72 @@
1
+ import { ConsoleLogger } from './console-logger'
2
+ import { InternalLogger } from './internal-logger'
3
+ import type { ResolvedCategories } from './internal-logger'
4
+ import type { DebugCategories, DebugConfig, DebugOption, Logger } from './types'
5
+
6
+ const ALL_OFF: ResolvedCategories = {
7
+ provider: false,
8
+ output: false,
9
+ middleware: false,
10
+ tools: false,
11
+ agentLoop: false,
12
+ config: false,
13
+ errors: false,
14
+ request: false,
15
+ }
16
+
17
+ const ALL_ON: ResolvedCategories = {
18
+ provider: true,
19
+ output: true,
20
+ middleware: true,
21
+ tools: true,
22
+ agentLoop: true,
23
+ config: true,
24
+ errors: true,
25
+ request: true,
26
+ }
27
+
28
+ const errorsOnlyCategories = (): ResolvedCategories => ({
29
+ ...ALL_OFF,
30
+ errors: true,
31
+ })
32
+
33
+ const resolveCategoriesFromPartial = (
34
+ partial: DebugCategories,
35
+ ): ResolvedCategories => ({
36
+ provider: partial.provider ?? true,
37
+ output: partial.output ?? true,
38
+ middleware: partial.middleware ?? true,
39
+ tools: partial.tools ?? true,
40
+ agentLoop: partial.agentLoop ?? true,
41
+ config: partial.config ?? true,
42
+ errors: partial.errors ?? true,
43
+ request: partial.request ?? true,
44
+ })
45
+
46
+ /**
47
+ * Normalize a `DebugOption` into an `InternalLogger` ready to be threaded
48
+ * through the library's activities and adapters. See the `DebugOption`
49
+ * resolution table in the spec for the complete rules.
50
+ *
51
+ * - `undefined`: only the `errors` category is enabled; default `ConsoleLogger`.
52
+ * - `true`: all categories enabled; default `ConsoleLogger`.
53
+ * - `false`: all categories disabled (including `errors`); default `ConsoleLogger`.
54
+ * - `DebugConfig`: each unspecified category defaults to `true`; an optional
55
+ * `logger` replaces the default `ConsoleLogger`.
56
+ */
57
+ export function resolveDebugOption(
58
+ debug: DebugOption | undefined,
59
+ ): InternalLogger {
60
+ if (debug === undefined) {
61
+ return new InternalLogger(new ConsoleLogger(), errorsOnlyCategories())
62
+ }
63
+ if (debug === true) {
64
+ return new InternalLogger(new ConsoleLogger(), ALL_ON)
65
+ }
66
+ if (debug === false) {
67
+ return new InternalLogger(new ConsoleLogger(), ALL_OFF)
68
+ }
69
+ const { logger, ...cats }: DebugConfig = debug
70
+ const userLogger: Logger = logger ?? new ConsoleLogger()
71
+ return new InternalLogger(userLogger, resolveCategoriesFromPartial(cats))
72
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Pluggable logger interface consumed by every `@tanstack/ai` activity when `debug` is enabled. Supply a custom implementation via `debug: { logger }` on `chat()`, `summarize()`, `generateImage()`, etc. The four methods correspond to log levels: use `debug` for chunk-level diagnostic output, `info`/`warn` for notable events, `error` for caught exceptions.
3
+ */
4
+ export interface Logger {
5
+ /**
6
+ * Called for chunk-level diagnostic output (raw provider chunks, per-chunk output, agent-loop iteration markers).
7
+ * @param meta Structured data forwarded to the underlying logger. Loggers like pino will preserve this as a structured record; console-based loggers pass it as the second argument to `console.<level>`.
8
+ */
9
+ debug: (message: string, meta?: Record<string, unknown>) => void
10
+ /**
11
+ * Called for notable informational events (outgoing requests, tool invocations, middleware transitions).
12
+ * @param meta Structured data forwarded to the underlying logger. Loggers like pino will preserve this as a structured record; console-based loggers pass it as the second argument to `console.<level>`.
13
+ */
14
+ info: (message: string, meta?: Record<string, unknown>) => void
15
+ /**
16
+ * Called for notable warnings that don't halt execution (deprecations, recoverable anomalies).
17
+ * @param meta Structured data forwarded to the underlying logger. Loggers like pino will preserve this as a structured record; console-based loggers pass it as the second argument to `console.<level>`.
18
+ */
19
+ warn: (message: string, meta?: Record<string, unknown>) => void
20
+ /**
21
+ * Called for caught exceptions throughout the pipeline.
22
+ * @param meta Structured data forwarded to the underlying logger. Loggers like pino will preserve this as a structured record; console-based loggers pass it as the second argument to `console.<level>`.
23
+ */
24
+ error: (message: string, meta?: Record<string, unknown>) => void
25
+ }
26
+
27
+ /**
28
+ * Per-category toggles for debug logging. Each flag enables or disables one class of log message. Unspecified flags default to `true` when `DebugConfig` is partially specified; `undefined` on the `debug` option defaults all flags to `false` except `errors`.
29
+ */
30
+ export interface DebugCategories {
31
+ /**
32
+ * Raw chunks/frames received from a provider SDK (OpenAI, Anthropic, Gemini, Ollama, Grok, Groq, OpenRouter, fal, ElevenLabs). Emitted inside every streaming adapter's chunk loop.
33
+ */
34
+ provider?: boolean
35
+ /**
36
+ * Chunks/results yielded to the consumer after all middleware. For streaming activities this fires per chunk; for non-streaming activities it fires once per result.
37
+ */
38
+ output?: boolean
39
+ /**
40
+ * Inputs and outputs around each middleware hook invocation. Chat-only.
41
+ */
42
+ middleware?: boolean
43
+ /**
44
+ * Before/after tool-call execution in the chat agent loop. Chat-only.
45
+ */
46
+ tools?: boolean
47
+ /**
48
+ * Iteration markers and phase transitions in the chat agent loop. Chat-only.
49
+ */
50
+ agentLoop?: boolean
51
+ /**
52
+ * Config transforms returned by middleware `onConfig` hooks. Chat-only.
53
+ */
54
+ config?: boolean
55
+ /**
56
+ * Caught errors throughout the pipeline. Unlike other categories, defaults to `true` even when `debug` is unspecified. Explicitly set `errors: false` or `debug: false` to silence.
57
+ */
58
+ errors?: boolean
59
+ /**
60
+ * Outgoing call metadata (provider, model, message/tool counts) emitted before each adapter SDK call.
61
+ */
62
+ request?: boolean
63
+ }
64
+
65
+ /**
66
+ * Granular debug configuration combining per-category toggles with an optional custom logger. Any unspecified category flag defaults to `true`.
67
+ */
68
+ export interface DebugConfig extends DebugCategories {
69
+ /**
70
+ * Custom `Logger` implementation. When omitted, a default `ConsoleLogger` routes output to `console.debug`/`info`/`warn`/`error`.
71
+ */
72
+ logger?: Logger
73
+ }
74
+
75
+ /**
76
+ * The shape accepted by the `debug` option on every `@tanstack/ai` activity. Pass `true` to enable all categories with the default console logger; `false` to silence everything including errors; an object for granular control.
77
+ */
78
+ export type DebugOption = boolean | DebugConfig
package/src/types.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { StandardJSONSchemaV1 } from '@standard-schema/spec'
2
+ import type { InternalLogger } from './logger/internal-logger'
2
3
  import type {
3
4
  BaseEvent as AGUIBaseEvent,
4
5
  CustomEvent as AGUICustomEvent,
@@ -738,6 +739,14 @@ export interface TextOptions<
738
739
  * @see https://developer.mozilla.org/en-US/docs/Web/API/AbortController
739
740
  */
740
741
  abortController?: AbortController
742
+
743
+ /**
744
+ * Internal logger threaded from the chat entry point. Adapter implementations
745
+ * must call `logger.request()` before SDK calls, `logger.provider()` for each
746
+ * chunk received, and `logger.errors()` in catch blocks.
747
+ */
748
+ logger: InternalLogger
749
+
741
750
  /**
742
751
  * Thread ID for AG-UI protocol run correlation.
743
752
  * When provided, this will be used in RunStartedEvent and RunFinishedEvent.
@@ -1163,6 +1172,11 @@ export interface SummarizationOptions {
1163
1172
  maxLength?: number
1164
1173
  style?: 'bullet-points' | 'paragraph' | 'concise'
1165
1174
  focus?: Array<string>
1175
+ /**
1176
+ * Internal logger threaded from the summarize() entry point. Adapters must
1177
+ * call logger.request() before the SDK call and logger.errors() in catch blocks.
1178
+ */
1179
+ logger: InternalLogger
1166
1180
  }
1167
1181
 
1168
1182
  export interface SummarizationResult {
@@ -1198,6 +1212,11 @@ export interface ImageGenerationOptions<
1198
1212
  size?: TSize
1199
1213
  /** Model-specific options for image generation */
1200
1214
  modelOptions?: TProviderOptions
1215
+ /**
1216
+ * Internal logger threaded from the generateImage() entry point. Adapters must
1217
+ * call logger.request() before the SDK call and logger.errors() in catch blocks.
1218
+ */
1219
+ logger: InternalLogger
1201
1220
  }
1202
1221
 
1203
1222
  /**
@@ -1254,6 +1273,11 @@ export interface VideoGenerationOptions<
1254
1273
  duration?: number
1255
1274
  /** Model-specific options for video generation */
1256
1275
  modelOptions?: TProviderOptions
1276
+ /**
1277
+ * Internal logger threaded from the generateVideo() entry point. Adapters must
1278
+ * call logger.request() before the SDK call and logger.errors() in catch blocks.
1279
+ */
1280
+ logger: InternalLogger
1257
1281
  }
1258
1282
 
1259
1283
  /**
@@ -1319,6 +1343,12 @@ export interface TTSOptions<TProviderOptions extends object = object> {
1319
1343
  speed?: number
1320
1344
  /** Model-specific options for TTS generation */
1321
1345
  modelOptions?: TProviderOptions
1346
+ /**
1347
+ * Internal logger threaded from the generateSpeech() entry point. Adapters
1348
+ * must call logger.request() before the SDK call and logger.errors() in
1349
+ * catch blocks.
1350
+ */
1351
+ logger: InternalLogger
1322
1352
  }
1323
1353
 
1324
1354
  /**
@@ -1362,6 +1392,12 @@ export interface TranscriptionOptions<
1362
1392
  responseFormat?: 'json' | 'text' | 'srt' | 'verbose_json' | 'vtt'
1363
1393
  /** Model-specific options for transcription */
1364
1394
  modelOptions?: TProviderOptions
1395
+ /**
1396
+ * Internal logger threaded from the generateTranscription() entry point.
1397
+ * Adapters must call logger.request() before the SDK call and logger.errors()
1398
+ * in catch blocks.
1399
+ */
1400
+ logger: InternalLogger
1365
1401
  }
1366
1402
 
1367
1403
  /**