@tanstack/ai 0.38.0 → 0.39.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.
- package/dist/esm/activities/chat/index.js +32 -0
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/middleware/compose.d.ts +7 -1
- package/dist/esm/activities/chat/middleware/compose.js +27 -0
- package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
- package/dist/esm/activities/chat/middleware/index.d.ts +1 -1
- package/dist/esm/activities/chat/middleware/sandbox-runtime.d.ts +8 -0
- package/dist/esm/activities/chat/middleware/sandbox-runtime.js +9 -0
- package/dist/esm/activities/chat/middleware/sandbox-runtime.js.map +1 -0
- package/dist/esm/activities/chat/middleware/types.d.ts +22 -0
- package/dist/esm/activities/chat/stream/processor.d.ts +18 -0
- package/dist/esm/activities/chat/stream/processor.js +86 -4
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/adapter-internals.d.ts +2 -0
- package/dist/esm/adapter-internals.js +4 -0
- package/dist/esm/adapter-internals.js.map +1 -1
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.js +3 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/logger/internal-logger.d.ts +2 -0
- package/dist/esm/logger/internal-logger.js +6 -1
- package/dist/esm/logger/internal-logger.js.map +1 -1
- package/dist/esm/logger/resolve.js +6 -3
- package/dist/esm/logger/resolve.js.map +1 -1
- package/dist/esm/logger/types.d.ts +5 -0
- package/dist/esm/types.d.ts +40 -1
- package/dist/esm/utilities/provider-executed.d.ts +18 -0
- package/dist/esm/utilities/provider-executed.js +15 -0
- package/dist/esm/utilities/provider-executed.js.map +1 -0
- package/package.json +1 -1
- package/skills/ai-core/adapter-configuration/SKILL.md +25 -12
- package/skills/ai-core/adapter-configuration/references/anthropic-adapter.md +43 -12
- package/skills/ai-core/media-generation/SKILL.md +1 -1
- package/src/activities/chat/index.ts +47 -0
- package/src/activities/chat/middleware/compose.ts +34 -0
- package/src/activities/chat/middleware/index.ts +2 -0
- package/src/activities/chat/middleware/sandbox-runtime.ts +21 -0
- package/src/activities/chat/middleware/types.ts +37 -0
- package/src/activities/chat/stream/processor.ts +141 -3
- package/src/adapter-internals.ts +6 -0
- package/src/index.ts +9 -0
- package/src/logger/internal-logger.ts +6 -0
- package/src/logger/resolve.ts +3 -0
- package/src/logger/types.ts +5 -0
- package/src/types.ts +43 -1
- package/src/utilities/provider-executed.ts +32 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal-logger.js","sources":["../../../src/logger/internal-logger.ts"],"sourcesContent":["import type { DebugCategories, Logger } from './types'\n\n/**\n * Fully-resolved categories map. Every flag is a definite boolean (never\n * undefined), produced by `resolveDebugOption` from a `DebugOption`.\n */\nexport type ResolvedCategories = Required<DebugCategories>\n\n/**\n * Package-internal logger wrapper used by every activity and adapter in\n * `@tanstack/ai`. Wraps a user-supplied (or default `ConsoleLogger`) `Logger`\n * plus a fully-resolved per-category map. Each category has a dedicated\n * method that no-ops when its flag is `false`, or prepends a\n * `[tanstack-ai:<category>] ` prefix and calls the underlying logger's\n * `error` (for the `errors` category) or `debug` (for everything else).\n *\n * Not exported from the package root. Adapter packages consume it via the\n * `@tanstack/ai/adapter-internals` subpath export.\n */\n/**\n * Emoji marker per category — bracketing the `[tanstack-ai:<cat>]` tag on\n * both sides makes it trivial to visually pick out a category when scanning\n * dense streaming logs.\n */\nconst CATEGORY_EMOJI: Record<keyof ResolvedCategories, string> = {\n request: '📤',\n provider: '📥',\n output: '📨',\n middleware: '🧩',\n tools: '🔧',\n agentLoop: '🔁',\n config: '⚙️',\n errors: '❌',\n}\n\nexport class InternalLogger {\n constructor(\n private readonly logger: Logger,\n private readonly categories: ResolvedCategories,\n ) {}\n\n /** Whether a category is enabled. Cheap, safe to call on hot paths. */\n isEnabled(category: keyof ResolvedCategories): boolean {\n return this.categories[category]\n }\n\n private emit(\n level: 'debug' | 'error',\n category: keyof ResolvedCategories,\n message: string,\n meta?: Record<string, unknown>,\n ): void {\n if (!this.categories[category]) return\n const emoji = CATEGORY_EMOJI[category]\n const prefixed = `${emoji} [tanstack-ai:${category}] ${emoji} ${message}`\n try {\n if (level === 'error') this.logger.error(prefixed, meta)\n else this.logger.debug(prefixed, meta)\n } catch {\n // User-supplied logger threw; swallow so we never mask the original\n // error that triggered this log call.\n }\n }\n\n /** Log a raw chunk/frame received from a provider SDK. */\n provider(message: string, meta?: Record<string, unknown>): void {\n this.emit('debug', 'provider', message, meta)\n }\n\n /** Log a chunk/result yielded to the consumer after middleware. */\n output(message: string, meta?: Record<string, unknown>): void {\n this.emit('debug', 'output', message, meta)\n }\n\n /** Log inputs/outputs around a middleware hook invocation. Chat-only. */\n middleware(message: string, meta?: Record<string, unknown>): void {\n this.emit('debug', 'middleware', message, meta)\n }\n\n /** Log before/after a tool-call execution. Chat-only. */\n tools(message: string, meta?: Record<string, unknown>): void {\n this.emit('debug', 'tools', message, meta)\n }\n\n /** Log an agent-loop iteration marker or phase transition. Chat-only. */\n agentLoop(message: string, meta?: Record<string, unknown>): void {\n this.emit('debug', 'agentLoop', message, meta)\n }\n\n /** Log a config transform returned by a middleware `onConfig` hook. Chat-only. */\n config(message: string, meta?: Record<string, unknown>): void {\n this.emit('debug', 'config', message, meta)\n }\n\n /**\n * Log a caught error. Defaults to on even when `debug` is unspecified.\n * Uses the underlying logger's `error` level.\n */\n errors(message: string, meta?: Record<string, unknown>): void {\n this.emit('error', 'errors', message, meta)\n }\n\n /** Log outgoing request metadata before an adapter SDK call. */\n request(message: string, meta?: Record<string, unknown>): void {\n this.emit('debug', 'request', message, meta)\n }\n\n /**\n * Log a non-fatal misconfiguration or recoverable anomaly. Gated by the\n * `errors` category — on by default (and when `debug` is unspecified), so\n * silent-drop conditions surface, but still silenced by `debug: false`,\n * which honors the \"disable everything including errors\" contract. Routes to\n * the underlying logger's `warn` level.\n */\n warn(message: string, meta?: Record<string, unknown>): void {\n if (!this.categories.errors) return\n const prefixed = `⚠️ [tanstack-ai:warn] ⚠️ ${message}`\n try {\n this.logger.warn(prefixed, meta)\n } catch {\n // User-supplied logger threw; swallow so a broken logger never masks the\n // condition we were trying to surface.\n }\n }\n}\n"],"names":[],"mappings":"AAwBA,MAAM,iBAA2D;AAAA,EAC/D,SAAS;AAAA,EACT,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,QAAQ;
|
|
1
|
+
{"version":3,"file":"internal-logger.js","sources":["../../../src/logger/internal-logger.ts"],"sourcesContent":["import type { DebugCategories, Logger } from './types'\n\n/**\n * Fully-resolved categories map. Every flag is a definite boolean (never\n * undefined), produced by `resolveDebugOption` from a `DebugOption`.\n */\nexport type ResolvedCategories = Required<DebugCategories>\n\n/**\n * Package-internal logger wrapper used by every activity and adapter in\n * `@tanstack/ai`. Wraps a user-supplied (or default `ConsoleLogger`) `Logger`\n * plus a fully-resolved per-category map. Each category has a dedicated\n * method that no-ops when its flag is `false`, or prepends a\n * `[tanstack-ai:<category>] ` prefix and calls the underlying logger's\n * `error` (for the `errors` category) or `debug` (for everything else).\n *\n * Not exported from the package root. Adapter packages consume it via the\n * `@tanstack/ai/adapter-internals` subpath export.\n */\n/**\n * Emoji marker per category — bracketing the `[tanstack-ai:<cat>]` tag on\n * both sides makes it trivial to visually pick out a category when scanning\n * dense streaming logs.\n */\nconst CATEGORY_EMOJI: Record<keyof ResolvedCategories, string> = {\n request: '📤',\n provider: '📥',\n output: '📨',\n middleware: '🧩',\n tools: '🔧',\n agentLoop: '🔁',\n config: '⚙️',\n errors: '❌',\n sandbox: '📦',\n}\n\nexport class InternalLogger {\n constructor(\n private readonly logger: Logger,\n private readonly categories: ResolvedCategories,\n ) {}\n\n /** Whether a category is enabled. Cheap, safe to call on hot paths. */\n isEnabled(category: keyof ResolvedCategories): boolean {\n return this.categories[category]\n }\n\n private emit(\n level: 'debug' | 'error',\n category: keyof ResolvedCategories,\n message: string,\n meta?: Record<string, unknown>,\n ): void {\n if (!this.categories[category]) return\n const emoji = CATEGORY_EMOJI[category]\n const prefixed = `${emoji} [tanstack-ai:${category}] ${emoji} ${message}`\n try {\n if (level === 'error') this.logger.error(prefixed, meta)\n else this.logger.debug(prefixed, meta)\n } catch {\n // User-supplied logger threw; swallow so we never mask the original\n // error that triggered this log call.\n }\n }\n\n /** Log a raw chunk/frame received from a provider SDK. */\n provider(message: string, meta?: Record<string, unknown>): void {\n this.emit('debug', 'provider', message, meta)\n }\n\n /** Log a chunk/result yielded to the consumer after middleware. */\n output(message: string, meta?: Record<string, unknown>): void {\n this.emit('debug', 'output', message, meta)\n }\n\n /** Log inputs/outputs around a middleware hook invocation. Chat-only. */\n middleware(message: string, meta?: Record<string, unknown>): void {\n this.emit('debug', 'middleware', message, meta)\n }\n\n /** Log before/after a tool-call execution. Chat-only. */\n tools(message: string, meta?: Record<string, unknown>): void {\n this.emit('debug', 'tools', message, meta)\n }\n\n /** Log sandbox internals (watcher, file events, hook dispatch). Chat-only. */\n sandbox(message: string, meta?: Record<string, unknown>): void {\n this.emit('debug', 'sandbox', message, meta)\n }\n\n /** Log an agent-loop iteration marker or phase transition. Chat-only. */\n agentLoop(message: string, meta?: Record<string, unknown>): void {\n this.emit('debug', 'agentLoop', message, meta)\n }\n\n /** Log a config transform returned by a middleware `onConfig` hook. Chat-only. */\n config(message: string, meta?: Record<string, unknown>): void {\n this.emit('debug', 'config', message, meta)\n }\n\n /**\n * Log a caught error. Defaults to on even when `debug` is unspecified.\n * Uses the underlying logger's `error` level.\n */\n errors(message: string, meta?: Record<string, unknown>): void {\n this.emit('error', 'errors', message, meta)\n }\n\n /** Log outgoing request metadata before an adapter SDK call. */\n request(message: string, meta?: Record<string, unknown>): void {\n this.emit('debug', 'request', message, meta)\n }\n\n /**\n * Log a non-fatal misconfiguration or recoverable anomaly. Gated by the\n * `errors` category — on by default (and when `debug` is unspecified), so\n * silent-drop conditions surface, but still silenced by `debug: false`,\n * which honors the \"disable everything including errors\" contract. Routes to\n * the underlying logger's `warn` level.\n */\n warn(message: string, meta?: Record<string, unknown>): void {\n if (!this.categories.errors) return\n const prefixed = `⚠️ [tanstack-ai:warn] ⚠️ ${message}`\n try {\n this.logger.warn(prefixed, meta)\n } catch {\n // User-supplied logger threw; swallow so a broken logger never masks the\n // condition we were trying to surface.\n }\n }\n}\n"],"names":[],"mappings":"AAwBA,MAAM,iBAA2D;AAAA,EAC/D,SAAS;AAAA,EACT,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AACX;AAEO,MAAM,eAAe;AAAA,EAC1B,YACmB,QACA,YACjB;AAFiB,SAAA,SAAA;AACA,SAAA,aAAA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA;AAAA,EAInB,UAAU,UAA6C;AACrD,WAAO,KAAK,WAAW,QAAQ;AAAA,EACjC;AAAA,EAEQ,KACN,OACA,UACA,SACA,MACM;AACN,QAAI,CAAC,KAAK,WAAW,QAAQ,EAAG;AAChC,UAAM,QAAQ,eAAe,QAAQ;AACrC,UAAM,WAAW,GAAG,KAAK,iBAAiB,QAAQ,KAAK,KAAK,IAAI,OAAO;AACvE,QAAI;AACF,UAAI,UAAU,QAAS,MAAK,OAAO,MAAM,UAAU,IAAI;AAAA,UAClD,MAAK,OAAO,MAAM,UAAU,IAAI;AAAA,IACvC,QAAQ;AAAA,IAGR;AAAA,EACF;AAAA;AAAA,EAGA,SAAS,SAAiB,MAAsC;AAC9D,SAAK,KAAK,SAAS,YAAY,SAAS,IAAI;AAAA,EAC9C;AAAA;AAAA,EAGA,OAAO,SAAiB,MAAsC;AAC5D,SAAK,KAAK,SAAS,UAAU,SAAS,IAAI;AAAA,EAC5C;AAAA;AAAA,EAGA,WAAW,SAAiB,MAAsC;AAChE,SAAK,KAAK,SAAS,cAAc,SAAS,IAAI;AAAA,EAChD;AAAA;AAAA,EAGA,MAAM,SAAiB,MAAsC;AAC3D,SAAK,KAAK,SAAS,SAAS,SAAS,IAAI;AAAA,EAC3C;AAAA;AAAA,EAGA,QAAQ,SAAiB,MAAsC;AAC7D,SAAK,KAAK,SAAS,WAAW,SAAS,IAAI;AAAA,EAC7C;AAAA;AAAA,EAGA,UAAU,SAAiB,MAAsC;AAC/D,SAAK,KAAK,SAAS,aAAa,SAAS,IAAI;AAAA,EAC/C;AAAA;AAAA,EAGA,OAAO,SAAiB,MAAsC;AAC5D,SAAK,KAAK,SAAS,UAAU,SAAS,IAAI;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,SAAiB,MAAsC;AAC5D,SAAK,KAAK,SAAS,UAAU,SAAS,IAAI;AAAA,EAC5C;AAAA;AAAA,EAGA,QAAQ,SAAiB,MAAsC;AAC7D,SAAK,KAAK,SAAS,WAAW,SAAS,IAAI;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,KAAK,SAAiB,MAAsC;AAC1D,QAAI,CAAC,KAAK,WAAW,OAAQ;AAC7B,UAAM,WAAW,4BAA4B,OAAO;AACpD,QAAI;AACF,WAAK,OAAO,KAAK,UAAU,IAAI;AAAA,IACjC,QAAQ;AAAA,IAGR;AAAA,EACF;AACF;"}
|
|
@@ -8,7 +8,8 @@ const ALL_OFF = {
|
|
|
8
8
|
agentLoop: false,
|
|
9
9
|
config: false,
|
|
10
10
|
errors: false,
|
|
11
|
-
request: false
|
|
11
|
+
request: false,
|
|
12
|
+
sandbox: false
|
|
12
13
|
};
|
|
13
14
|
const ALL_ON = {
|
|
14
15
|
provider: true,
|
|
@@ -18,7 +19,8 @@ const ALL_ON = {
|
|
|
18
19
|
agentLoop: true,
|
|
19
20
|
config: true,
|
|
20
21
|
errors: true,
|
|
21
|
-
request: true
|
|
22
|
+
request: true,
|
|
23
|
+
sandbox: true
|
|
22
24
|
};
|
|
23
25
|
const errorsOnlyCategories = () => ({
|
|
24
26
|
...ALL_OFF,
|
|
@@ -32,7 +34,8 @@ const resolveCategoriesFromPartial = (partial) => ({
|
|
|
32
34
|
agentLoop: partial.agentLoop ?? true,
|
|
33
35
|
config: partial.config ?? true,
|
|
34
36
|
errors: partial.errors ?? true,
|
|
35
|
-
request: partial.request ?? true
|
|
37
|
+
request: partial.request ?? true,
|
|
38
|
+
sandbox: partial.sandbox ?? true
|
|
36
39
|
});
|
|
37
40
|
function resolveDebugOption(debug) {
|
|
38
41
|
if (debug === void 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.js","sources":["../../../src/logger/resolve.ts"],"sourcesContent":["import { ConsoleLogger } from './console-logger'\nimport { InternalLogger } from './internal-logger'\nimport type { ResolvedCategories } from './internal-logger'\nimport type { DebugCategories, DebugConfig, DebugOption, Logger } from './types'\n\nconst ALL_OFF: ResolvedCategories = {\n provider: false,\n output: false,\n middleware: false,\n tools: false,\n agentLoop: false,\n config: false,\n errors: false,\n request: false,\n}\n\nconst ALL_ON: ResolvedCategories = {\n provider: true,\n output: true,\n middleware: true,\n tools: true,\n agentLoop: true,\n config: true,\n errors: true,\n request: true,\n}\n\nconst errorsOnlyCategories = (): ResolvedCategories => ({\n ...ALL_OFF,\n errors: true,\n})\n\nconst resolveCategoriesFromPartial = (\n partial: DebugCategories,\n): ResolvedCategories => ({\n provider: partial.provider ?? true,\n output: partial.output ?? true,\n middleware: partial.middleware ?? true,\n tools: partial.tools ?? true,\n agentLoop: partial.agentLoop ?? true,\n config: partial.config ?? true,\n errors: partial.errors ?? true,\n request: partial.request ?? true,\n})\n\n/**\n * Normalize a `DebugOption` into an `InternalLogger` ready to be threaded\n * through the library's activities and adapters. See the `DebugOption`\n * resolution table in the spec for the complete rules.\n *\n * - `undefined`: only the `errors` category is enabled; default `ConsoleLogger`.\n * - `true`: all categories enabled; default `ConsoleLogger`.\n * - `false`: all categories disabled (including `errors`); default `ConsoleLogger`.\n * - `DebugConfig`: each unspecified category defaults to `true`; an optional\n * `logger` replaces the default `ConsoleLogger`.\n */\nexport function resolveDebugOption(\n debug: DebugOption | undefined,\n): InternalLogger {\n if (debug === undefined) {\n return new InternalLogger(new ConsoleLogger(), errorsOnlyCategories())\n }\n if (debug === true) {\n return new InternalLogger(new ConsoleLogger(), ALL_ON)\n }\n if (debug === false) {\n return new InternalLogger(new ConsoleLogger(), ALL_OFF)\n }\n const { logger, ...cats }: DebugConfig = debug\n const userLogger: Logger = logger ?? new ConsoleLogger()\n return new InternalLogger(userLogger, resolveCategoriesFromPartial(cats))\n}\n"],"names":[],"mappings":";;AAKA,MAAM,UAA8B;AAAA,EAClC,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AACX;AAEA,MAAM,SAA6B;AAAA,EACjC,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AACX;AAEA,MAAM,uBAAuB,OAA2B;AAAA,EACtD,GAAG;AAAA,EACH,QAAQ;AACV;AAEA,MAAM,+BAA+B,CACnC,aACwB;AAAA,EACxB,UAAU,QAAQ,YAAY;AAAA,EAC9B,QAAQ,QAAQ,UAAU;AAAA,EAC1B,YAAY,QAAQ,cAAc;AAAA,EAClC,OAAO,QAAQ,SAAS;AAAA,EACxB,WAAW,QAAQ,aAAa;AAAA,EAChC,QAAQ,QAAQ,UAAU;AAAA,EAC1B,QAAQ,QAAQ,UAAU;AAAA,EAC1B,SAAS,QAAQ,WAAW;AAC9B;AAaO,SAAS,mBACd,OACgB;AAChB,MAAI,UAAU,QAAW;AACvB,WAAO,IAAI,eAAe,IAAI,cAAA,GAAiB,sBAAsB;AAAA,EACvE;AACA,MAAI,UAAU,MAAM;AAClB,WAAO,IAAI,eAAe,IAAI,cAAA,GAAiB,MAAM;AAAA,EACvD;AACA,MAAI,UAAU,OAAO;AACnB,WAAO,IAAI,eAAe,IAAI,cAAA,GAAiB,OAAO;AAAA,EACxD;AACA,QAAM,EAAE,QAAQ,GAAG,KAAA,IAAsB;AACzC,QAAM,aAAqB,UAAU,IAAI,cAAA;AACzC,SAAO,IAAI,eAAe,YAAY,6BAA6B,IAAI,CAAC;AAC1E;"}
|
|
1
|
+
{"version":3,"file":"resolve.js","sources":["../../../src/logger/resolve.ts"],"sourcesContent":["import { ConsoleLogger } from './console-logger'\nimport { InternalLogger } from './internal-logger'\nimport type { ResolvedCategories } from './internal-logger'\nimport type { DebugCategories, DebugConfig, DebugOption, Logger } from './types'\n\nconst ALL_OFF: ResolvedCategories = {\n provider: false,\n output: false,\n middleware: false,\n tools: false,\n agentLoop: false,\n config: false,\n errors: false,\n request: false,\n sandbox: false,\n}\n\nconst ALL_ON: ResolvedCategories = {\n provider: true,\n output: true,\n middleware: true,\n tools: true,\n agentLoop: true,\n config: true,\n errors: true,\n request: true,\n sandbox: true,\n}\n\nconst errorsOnlyCategories = (): ResolvedCategories => ({\n ...ALL_OFF,\n errors: true,\n})\n\nconst resolveCategoriesFromPartial = (\n partial: DebugCategories,\n): ResolvedCategories => ({\n provider: partial.provider ?? true,\n output: partial.output ?? true,\n middleware: partial.middleware ?? true,\n tools: partial.tools ?? true,\n agentLoop: partial.agentLoop ?? true,\n config: partial.config ?? true,\n errors: partial.errors ?? true,\n request: partial.request ?? true,\n sandbox: partial.sandbox ?? true,\n})\n\n/**\n * Normalize a `DebugOption` into an `InternalLogger` ready to be threaded\n * through the library's activities and adapters. See the `DebugOption`\n * resolution table in the spec for the complete rules.\n *\n * - `undefined`: only the `errors` category is enabled; default `ConsoleLogger`.\n * - `true`: all categories enabled; default `ConsoleLogger`.\n * - `false`: all categories disabled (including `errors`); default `ConsoleLogger`.\n * - `DebugConfig`: each unspecified category defaults to `true`; an optional\n * `logger` replaces the default `ConsoleLogger`.\n */\nexport function resolveDebugOption(\n debug: DebugOption | undefined,\n): InternalLogger {\n if (debug === undefined) {\n return new InternalLogger(new ConsoleLogger(), errorsOnlyCategories())\n }\n if (debug === true) {\n return new InternalLogger(new ConsoleLogger(), ALL_ON)\n }\n if (debug === false) {\n return new InternalLogger(new ConsoleLogger(), ALL_OFF)\n }\n const { logger, ...cats }: DebugConfig = debug\n const userLogger: Logger = logger ?? new ConsoleLogger()\n return new InternalLogger(userLogger, resolveCategoriesFromPartial(cats))\n}\n"],"names":[],"mappings":";;AAKA,MAAM,UAA8B;AAAA,EAClC,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AACX;AAEA,MAAM,SAA6B;AAAA,EACjC,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AACX;AAEA,MAAM,uBAAuB,OAA2B;AAAA,EACtD,GAAG;AAAA,EACH,QAAQ;AACV;AAEA,MAAM,+BAA+B,CACnC,aACwB;AAAA,EACxB,UAAU,QAAQ,YAAY;AAAA,EAC9B,QAAQ,QAAQ,UAAU;AAAA,EAC1B,YAAY,QAAQ,cAAc;AAAA,EAClC,OAAO,QAAQ,SAAS;AAAA,EACxB,WAAW,QAAQ,aAAa;AAAA,EAChC,QAAQ,QAAQ,UAAU;AAAA,EAC1B,QAAQ,QAAQ,UAAU;AAAA,EAC1B,SAAS,QAAQ,WAAW;AAAA,EAC5B,SAAS,QAAQ,WAAW;AAC9B;AAaO,SAAS,mBACd,OACgB;AAChB,MAAI,UAAU,QAAW;AACvB,WAAO,IAAI,eAAe,IAAI,cAAA,GAAiB,sBAAsB;AAAA,EACvE;AACA,MAAI,UAAU,MAAM;AAClB,WAAO,IAAI,eAAe,IAAI,cAAA,GAAiB,MAAM;AAAA,EACvD;AACA,MAAI,UAAU,OAAO;AACnB,WAAO,IAAI,eAAe,IAAI,cAAA,GAAiB,OAAO;AAAA,EACxD;AACA,QAAM,EAAE,QAAQ,GAAG,KAAA,IAAsB;AACzC,QAAM,aAAqB,UAAU,IAAI,cAAA;AACzC,SAAO,IAAI,eAAe,YAAY,6BAA6B,IAAI,CAAC;AAC1E;"}
|
|
@@ -59,6 +59,11 @@ export interface DebugCategories {
|
|
|
59
59
|
* Outgoing call metadata (provider, model, message/tool counts) emitted before each adapter SDK call.
|
|
60
60
|
*/
|
|
61
61
|
request?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Sandbox internals: watcher start/stop + mechanism, file events, sandbox
|
|
64
|
+
* hook dispatch, ensure/bootstrap and lifecycle transitions. Chat-only.
|
|
65
|
+
*/
|
|
66
|
+
sandbox?: boolean;
|
|
62
67
|
}
|
|
63
68
|
/**
|
|
64
69
|
* Granular debug configuration combining per-category toggles with an optional custom logger. Any unspecified category flag defaults to `true`.
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { StandardJSONSchemaV1, StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
2
|
import { InternalLogger } from './logger/internal-logger.js';
|
|
3
3
|
import { SystemPrompt } from './system-prompts.js';
|
|
4
|
+
import { CapabilityContext } from './activities/chat/middleware/capabilities.js';
|
|
4
5
|
import { CompletionTokensDetails, PromptTokensDetails, ProviderUsageDetails, TokenUsage, UsageCostBreakdown } from '@tanstack/ai-event-client';
|
|
5
6
|
import { BaseEvent as AGUIBaseEvent, CustomEvent as AGUICustomEvent, MessagesSnapshotEvent as AGUIMessagesSnapshotEvent, ReasoningEncryptedValueEvent as AGUIReasoningEncryptedValueEvent, ReasoningEndEvent as AGUIReasoningEndEvent, ReasoningMessageContentEvent as AGUIReasoningMessageContentEvent, ReasoningMessageEndEvent as AGUIReasoningMessageEndEvent, ReasoningMessageStartEvent as AGUIReasoningMessageStartEvent, ReasoningStartEvent as AGUIReasoningStartEvent, RunErrorEvent as AGUIRunErrorEvent, RunFinishedEvent as AGUIRunFinishedEvent, RunStartedEvent as AGUIRunStartedEvent, StateDeltaEvent as AGUIStateDeltaEvent, StateSnapshotEvent as AGUIStateSnapshotEvent, StepFinishedEvent as AGUIStepFinishedEvent, StepStartedEvent as AGUIStepStartedEvent, TextMessageContentEvent as AGUITextMessageContentEvent, TextMessageEndEvent as AGUITextMessageEndEvent, TextMessageStartEvent as AGUITextMessageStartEvent, ToolCallArgsEvent as AGUIToolCallArgsEvent, ToolCallEndEvent as AGUIToolCallEndEvent, ToolCallResultEvent as AGUIToolCallResultEvent, ToolCallStartEvent as AGUIToolCallStartEvent, EventType } from '@ag-ui/core';
|
|
6
7
|
/**
|
|
@@ -95,6 +96,25 @@ export interface ToolCall<TMetadata = unknown> {
|
|
|
95
96
|
* `@tanstack/ai-gemini` sets this to `{ thoughtSignature?: string }`. */
|
|
96
97
|
metadata?: TMetadata;
|
|
97
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Convention for tool-call `metadata` that marks a call as **provider-executed**
|
|
101
|
+
* — run by the provider's own infrastructure (e.g. Anthropic `web_search` /
|
|
102
|
+
* `web_fetch` server tools) rather than by the agent loop. Adapters set
|
|
103
|
+
* `providerExecuted: true` so that:
|
|
104
|
+
*
|
|
105
|
+
* 1. The agent loop never tries to execute the call client-side (see
|
|
106
|
+
* {@link isProviderExecutedToolCall} usage in the chat engine), and
|
|
107
|
+
* 2. The adapter can stash the raw provider result alongside it so the call —
|
|
108
|
+
* and its evidence — round-trips into the next turn's request.
|
|
109
|
+
*
|
|
110
|
+
* Provider-specific payloads live under a namespaced key (e.g. `anthropic`),
|
|
111
|
+
* keeping this convention opaque to the framework core. The index signature
|
|
112
|
+
* preserves those per-adapter fields.
|
|
113
|
+
*/
|
|
114
|
+
export interface ProviderExecutedToolMetadata {
|
|
115
|
+
providerExecuted?: boolean;
|
|
116
|
+
[key: string]: unknown;
|
|
117
|
+
}
|
|
98
118
|
/**
|
|
99
119
|
* Supported input modality types for multimodal content.
|
|
100
120
|
* - 'text': Plain text content
|
|
@@ -251,7 +271,9 @@ export interface ToolCallPart<TMetadata = unknown> {
|
|
|
251
271
|
/** Tool execution output (for client tools or after approval) */
|
|
252
272
|
output?: any;
|
|
253
273
|
/** Provider-specific metadata that round-trips with the tool call.
|
|
254
|
-
* Typed per-adapter via `TToolCallMetadata`.
|
|
274
|
+
* Typed per-adapter via `TToolCallMetadata`. May follow the
|
|
275
|
+
* {@link ProviderExecutedToolMetadata} convention to mark provider-executed
|
|
276
|
+
* server tools (e.g. Anthropic `web_search`). */
|
|
255
277
|
metadata?: TMetadata;
|
|
256
278
|
}
|
|
257
279
|
export interface ToolResultPart {
|
|
@@ -761,6 +783,23 @@ export interface TextOptions<TProviderOptionsSuperset extends Record<string, any
|
|
|
761
783
|
* Surfaced for observability/middleware; not consumed by the LLM call.
|
|
762
784
|
*/
|
|
763
785
|
parentRunId?: string;
|
|
786
|
+
/**
|
|
787
|
+
* Middleware capability context for this run. The engine populates it with
|
|
788
|
+
* the live middleware context so harness adapters that declare
|
|
789
|
+
* `requires: [SomeCapability]` can read provided capabilities from inside
|
|
790
|
+
* `chatStream` — e.g. `getSandbox(options.capabilities)`. Capabilities are
|
|
791
|
+
* provisioned by middleware `setup` before the adapter runs. Undefined for
|
|
792
|
+
* direct adapter usage outside the chat engine.
|
|
793
|
+
*/
|
|
794
|
+
capabilities?: CapabilityContext;
|
|
795
|
+
/**
|
|
796
|
+
* Client approval decisions for this run, keyed by approval id. The engine
|
|
797
|
+
* populates this from approvals carried on the incoming messages. Harness
|
|
798
|
+
* adapters consult it to resolve `ask`-policy permission requests (the agent
|
|
799
|
+
* pauses on a risky action; the client re-runs with a decision recorded
|
|
800
|
+
* here). Undefined for direct adapter usage outside the chat engine.
|
|
801
|
+
*/
|
|
802
|
+
approvals?: ReadonlyMap<string, boolean>;
|
|
764
803
|
}
|
|
765
804
|
/**
|
|
766
805
|
* Re-export EventType enum from @ag-ui/core for use in event creation.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ProviderExecutedToolMetadata } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Narrow a tool call's opaque `metadata` to the provider-executed convention.
|
|
4
|
+
* Returns the typed metadata when the call is provider-executed, else `null`.
|
|
5
|
+
*
|
|
6
|
+
* @see ProviderExecutedToolMetadata
|
|
7
|
+
*/
|
|
8
|
+
export declare function getProviderExecutedMetadata(toolCall: {
|
|
9
|
+
metadata?: unknown;
|
|
10
|
+
} | null | undefined): ProviderExecutedToolMetadata | null;
|
|
11
|
+
/**
|
|
12
|
+
* True when a tool call was executed by the provider (e.g. Anthropic
|
|
13
|
+
* `web_search` / `web_fetch` server tools) rather than the agent loop. Such
|
|
14
|
+
* calls must not be routed to client-side execution and are already "complete".
|
|
15
|
+
*/
|
|
16
|
+
export declare function isProviderExecutedToolCall(toolCall: {
|
|
17
|
+
metadata?: unknown;
|
|
18
|
+
} | null | undefined): boolean;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
function getProviderExecutedMetadata(toolCall) {
|
|
2
|
+
const metadata = toolCall?.metadata;
|
|
3
|
+
if (typeof metadata === "object" && metadata !== null && metadata.providerExecuted === true) {
|
|
4
|
+
return metadata;
|
|
5
|
+
}
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
function isProviderExecutedToolCall(toolCall) {
|
|
9
|
+
return getProviderExecutedMetadata(toolCall) !== null;
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
getProviderExecutedMetadata,
|
|
13
|
+
isProviderExecutedToolCall
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=provider-executed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-executed.js","sources":["../../../src/utilities/provider-executed.ts"],"sourcesContent":["import type { ProviderExecutedToolMetadata } from '../types'\n\n/**\n * Narrow a tool call's opaque `metadata` to the provider-executed convention.\n * Returns the typed metadata when the call is provider-executed, else `null`.\n *\n * @see ProviderExecutedToolMetadata\n */\nexport function getProviderExecutedMetadata(\n toolCall: { metadata?: unknown } | null | undefined,\n): ProviderExecutedToolMetadata | null {\n const metadata = toolCall?.metadata\n if (\n typeof metadata === 'object' &&\n metadata !== null &&\n (metadata as ProviderExecutedToolMetadata).providerExecuted === true\n ) {\n return metadata as ProviderExecutedToolMetadata\n }\n return null\n}\n\n/**\n * True when a tool call was executed by the provider (e.g. Anthropic\n * `web_search` / `web_fetch` server tools) rather than the agent loop. Such\n * calls must not be routed to client-side execution and are already \"complete\".\n */\nexport function isProviderExecutedToolCall(\n toolCall: { metadata?: unknown } | null | undefined,\n): boolean {\n return getProviderExecutedMetadata(toolCall) !== null\n}\n"],"names":[],"mappings":"AAQO,SAAS,4BACd,UACqC;AACrC,QAAM,WAAW,UAAU;AAC3B,MACE,OAAO,aAAa,YACpB,aAAa,QACZ,SAA0C,qBAAqB,MAChE;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAOO,SAAS,2BACd,UACS;AACT,SAAO,4BAA4B,QAAQ,MAAM;AACnD;"}
|
package/package.json
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
name: ai-core/adapter-configuration
|
|
3
3
|
description: >
|
|
4
4
|
Provider adapter selection and configuration: openaiText, anthropicText,
|
|
5
|
-
geminiText, ollamaText, grokText, groqText, openRouterText,
|
|
6
|
-
type safety with modelOptions, reasoning/thinking configuration,
|
|
5
|
+
geminiText, ollamaText, grokText, groqText, openRouterText, bedrockText,
|
|
6
|
+
openaiCompatible. Per-model type safety with modelOptions, reasoning/thinking configuration,
|
|
7
7
|
runtime adapter switching, extendAdapter() for custom models, createModel().
|
|
8
8
|
Generic OpenAI-compatible providers (DeepSeek, Together, Fireworks, etc.) via
|
|
9
9
|
openaiCompatible({ baseURL, apiKey, models }) from @tanstack/ai-openai/compatible.
|
|
10
10
|
API key env vars: OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_API_KEY/GEMINI_API_KEY,
|
|
11
|
-
XAI_API_KEY, GROQ_API_KEY, OPENROUTER_API_KEY, OLLAMA_HOST
|
|
11
|
+
XAI_API_KEY, GROQ_API_KEY, OPENROUTER_API_KEY, OLLAMA_HOST,
|
|
12
|
+
BEDROCK_API_KEY (or AWS_BEARER_TOKEN_BEDROCK).
|
|
12
13
|
type: sub-skill
|
|
13
14
|
library: tanstack-ai
|
|
14
15
|
library_version: '0.10.0'
|
|
@@ -78,6 +79,7 @@ The text adapter is the primary one for chat/completions:
|
|
|
78
79
|
| Groq | `@tanstack/ai-groq` | `groqText` | `GROQ_API_KEY` |
|
|
79
80
|
| OpenRouter | `@tanstack/ai-openrouter` | `openRouterText` | `OPENROUTER_API_KEY` |
|
|
80
81
|
| Ollama | `@tanstack/ai-ollama` | `ollamaText` | `OLLAMA_HOST` (default: `http://localhost:11434`) |
|
|
82
|
+
| Bedrock | `@tanstack/ai-bedrock` | `bedrockText` | `BEDROCK_API_KEY` or `AWS_BEARER_TOKEN_BEDROCK` |
|
|
81
83
|
| OpenAI-compatible | `@tanstack/ai-openai/compatible` | `openaiCompatible` / `openaiCompatibleText` | provider-specific (passed via `apiKey`) |
|
|
82
84
|
|
|
83
85
|
```typescript
|
|
@@ -89,6 +91,7 @@ import { grokText } from '@tanstack/ai-grok'
|
|
|
89
91
|
import { groqText } from '@tanstack/ai-groq'
|
|
90
92
|
import { openRouterText } from '@tanstack/ai-openrouter'
|
|
91
93
|
import { ollamaText } from '@tanstack/ai-ollama'
|
|
94
|
+
import { bedrockText } from '@tanstack/ai-bedrock'
|
|
92
95
|
|
|
93
96
|
// Model string is passed to the factory, NOT to chat()
|
|
94
97
|
const adapter = openaiText('gpt-5.2')
|
|
@@ -98,6 +101,7 @@ const adapter4 = grokText('grok-4')
|
|
|
98
101
|
const adapter5 = groqText('llama-3.3-70b-versatile')
|
|
99
102
|
const adapter6 = openRouterText('anthropic/claude-sonnet-4')
|
|
100
103
|
const adapter7 = ollamaText('llama3.3')
|
|
104
|
+
const adapter8 = bedrockText('us.anthropic.claude-3-7-sonnet-20250219-v1:0')
|
|
101
105
|
|
|
102
106
|
// Optional: pass explicit API key
|
|
103
107
|
const adapterWithKey = openaiText('gpt-5.2', {
|
|
@@ -105,6 +109,14 @@ const adapterWithKey = openaiText('gpt-5.2', {
|
|
|
105
109
|
})
|
|
106
110
|
```
|
|
107
111
|
|
|
112
|
+
`@tanstack/ai-bedrock` (Amazon Bedrock) branches on `config.api`:
|
|
113
|
+
|
|
114
|
+
- `bedrockText(model)` or `bedrockText(model, { api: 'converse' })` (the default) — Bedrock's native Converse API via `@aws-sdk/client-bedrock-runtime` (adapter name `bedrock-converse`). Reaches the broad catalog: Claude, Nova, Llama, Mistral, DeepSeek, and more.
|
|
115
|
+
- `bedrockText(model, { api: 'chat' })` — OpenAI-compatible Chat Completions endpoint (adapter name `bedrock`). Open-weight models only (gpt-oss, DeepSeek V3.x, Gemma, Qwen, etc.). Does NOT reach Claude, Nova, or Llama.
|
|
116
|
+
- `bedrockText(model, { api: 'responses' })` — OpenAI-compatible Responses API, mantle-only (adapter name `bedrock-responses`). Currently gpt-oss family.
|
|
117
|
+
|
|
118
|
+
Use `createBedrockText(model, apiKey, config?)` to pass the key explicitly. Auth resolves from `BEDROCK_API_KEY` / `AWS_BEARER_TOKEN_BEDROCK`, or SigV4 via the standard AWS credential chain (no extra packages needed — handled by `@aws-sdk/client-bedrock-runtime`).
|
|
119
|
+
|
|
108
120
|
### 2. Runtime Adapter Switching
|
|
109
121
|
|
|
110
122
|
Use an adapter factory map to switch providers dynamically based on user
|
|
@@ -397,15 +409,16 @@ Source: docs/migration/migration.md
|
|
|
397
409
|
Each provider uses a specific env var name. Using the wrong one causes a
|
|
398
410
|
runtime error:
|
|
399
411
|
|
|
400
|
-
| Provider | Correct Env Var
|
|
401
|
-
| ---------- |
|
|
402
|
-
| OpenAI | `OPENAI_API_KEY`
|
|
403
|
-
| Anthropic | `ANTHROPIC_API_KEY`
|
|
404
|
-
| Gemini | `GOOGLE_API_KEY` or `GEMINI_API_KEY`
|
|
405
|
-
| Grok (xAI) | `XAI_API_KEY`
|
|
406
|
-
| Groq | `GROQ_API_KEY`
|
|
407
|
-
| OpenRouter | `OPENROUTER_API_KEY`
|
|
408
|
-
| Ollama | `OLLAMA_HOST`
|
|
412
|
+
| Provider | Correct Env Var | Common Mistake |
|
|
413
|
+
| ---------- | ---------------------------------------------- | ------------------------------------------------------------------------ |
|
|
414
|
+
| OpenAI | `OPENAI_API_KEY` | |
|
|
415
|
+
| Anthropic | `ANTHROPIC_API_KEY` | |
|
|
416
|
+
| Gemini | `GOOGLE_API_KEY` or `GEMINI_API_KEY` | `GOOGLE_GENAI_API_KEY` (does not work) |
|
|
417
|
+
| Grok (xAI) | `XAI_API_KEY` | `GROK_API_KEY` (does not work) |
|
|
418
|
+
| Groq | `GROQ_API_KEY` | |
|
|
419
|
+
| OpenRouter | `OPENROUTER_API_KEY` | |
|
|
420
|
+
| Ollama | `OLLAMA_HOST` | No API key needed, just the host URL (default: `http://localhost:11434`) |
|
|
421
|
+
| Bedrock | `BEDROCK_API_KEY` / `AWS_BEARER_TOKEN_BEDROCK` | Falls back to SigV4 credentials when no API key is set |
|
|
409
422
|
|
|
410
423
|
Source: adapter source code (`utils/client.ts` in each adapter package).
|
|
411
424
|
|
|
@@ -21,17 +21,23 @@ import { anthropicText } from '@tanstack/ai-anthropic'
|
|
|
21
21
|
|
|
22
22
|
## Key Chat Models
|
|
23
23
|
|
|
24
|
-
| Model | Context Window | Max Output | Notes
|
|
25
|
-
| ------------------- | -------------- | ---------- |
|
|
26
|
-
| `claude-
|
|
27
|
-
| `claude-sonnet-
|
|
28
|
-
| `claude-
|
|
29
|
-
| `claude-opus-4-
|
|
30
|
-
| `claude-
|
|
31
|
-
| `claude-sonnet-4`
|
|
32
|
-
| `claude-
|
|
33
|
-
|
|
34
|
-
|
|
24
|
+
| Model | Context Window | Max Output | Notes |
|
|
25
|
+
| ------------------- | -------------- | ---------- | ------------------------------------------- |
|
|
26
|
+
| `claude-fable-5` | 1M | 128K | Most capable; thinking always on (adaptive) |
|
|
27
|
+
| `claude-sonnet-5` | 1M | 128K | Best balance; adaptive thinking by default |
|
|
28
|
+
| `claude-opus-4-8` | 1M | 128K | Opus tier; adaptive thinking, no sampling |
|
|
29
|
+
| `claude-opus-4-7` | 1M | 128K | Older Opus; adaptive thinking, no sampling |
|
|
30
|
+
| `claude-opus-4-6` | 200K | 128K | Older Opus, adaptive + budget thinking |
|
|
31
|
+
| `claude-sonnet-4-6` | 1M | 64K | Previous gen balanced, adaptive + budget |
|
|
32
|
+
| `claude-sonnet-4-5` | 200K | 64K | Previous gen balanced |
|
|
33
|
+
| `claude-opus-4-5` | 200K | 32K | Previous gen most capable |
|
|
34
|
+
| `claude-opus-4-1` | 200K | 64K | Deprecated (retires 2026-08-05) |
|
|
35
|
+
| `claude-haiku-4-5` | 200K | 64K | Fast and affordable |
|
|
36
|
+
|
|
37
|
+
Note: Model IDs use the format `claude-sonnet-5`, `claude-opus-4-8`, etc.
|
|
38
|
+
Retired models (Claude 3.x, Sonnet 3.7, Opus 4 / Sonnet 4) and the `-fast`
|
|
39
|
+
variant ids were removed — every registered id resolves against the
|
|
40
|
+
first-party Anthropic API.
|
|
35
41
|
|
|
36
42
|
## Provider-Specific modelOptions
|
|
37
43
|
|
|
@@ -90,11 +96,36 @@ chat({
|
|
|
90
96
|
ANTHROPIC_API_KEY
|
|
91
97
|
```
|
|
92
98
|
|
|
99
|
+
## Adaptive-era modelOptions (Sonnet 5, Fable 5, Opus 4.7/4.8)
|
|
100
|
+
|
|
101
|
+
The per-model types restrict `modelOptions` on the newest models:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
chat({
|
|
105
|
+
adapter: anthropicText('claude-sonnet-5'), // or 'claude-fable-5', 'claude-opus-4-8'
|
|
106
|
+
messages,
|
|
107
|
+
modelOptions: {
|
|
108
|
+
// Adaptive thinking only — budget_tokens is rejected (400).
|
|
109
|
+
// On claude-fable-5, { type: 'disabled' } is also rejected;
|
|
110
|
+
// elsewhere it opts out of thinking.
|
|
111
|
+
thinking: { type: 'adaptive', display: 'summarized' },
|
|
112
|
+
// Effort lives under output_config; 'xhigh' is available on
|
|
113
|
+
// Opus 4.7+, Sonnet 5, and Fable 5.
|
|
114
|
+
output_config: { effort: 'xhigh' },
|
|
115
|
+
max_tokens: 64_000,
|
|
116
|
+
// NO temperature / top_p / top_k — the API rejects them on these models
|
|
117
|
+
},
|
|
118
|
+
})
|
|
119
|
+
```
|
|
120
|
+
|
|
93
121
|
## Gotchas
|
|
94
122
|
|
|
95
123
|
- `thinking.budget_tokens` must be >= 1024 AND less than `modelOptions.max_tokens`.
|
|
96
124
|
Failing either check throws a validation error.
|
|
97
125
|
- Cannot set both `top_p` and `temperature` at the same time (throws error).
|
|
98
|
-
- `claude-
|
|
126
|
+
- `claude-sonnet-5`, `claude-fable-5`, `claude-opus-4-8`, and
|
|
127
|
+
`claude-opus-4-7` do NOT accept `temperature`, `top_p`, `top_k`, or
|
|
128
|
+
`thinking: { type: 'enabled', budget_tokens }` — adaptive thinking +
|
|
129
|
+
`output_config.effort` replace them (typed per model).
|
|
99
130
|
- System prompts support prompt caching via `cache_control` on `TextBlockParam[]`.
|
|
100
131
|
- All Claude models accept `text`, `image`, and `document` (PDF) input.
|
|
@@ -151,7 +151,7 @@ function ImageGenerator() {
|
|
|
151
151
|
|
|
152
152
|
Supported adapters: `openaiImage` (dall-e-2, dall-e-3, gpt-image-1,
|
|
153
153
|
gpt-image-1-mini, gpt-image-2) and `geminiImage` (gemini-3.1-flash-image-preview,
|
|
154
|
-
imagen-4.0-generate-001, etc.).
|
|
154
|
+
gemini-3.1-flash-lite-image, imagen-4.0-generate-001, etc.).
|
|
155
155
|
|
|
156
156
|
```typescript
|
|
157
157
|
import { generateImage } from '@tanstack/ai'
|
|
@@ -12,6 +12,7 @@ import { streamToText } from '../../stream-to-response.js'
|
|
|
12
12
|
import { resolveDebugOption } from '../../logger/resolve'
|
|
13
13
|
import { EventType } from '../../types'
|
|
14
14
|
import { normalizeToolResult } from '../../utilities/tool-result'
|
|
15
|
+
import { isProviderExecutedToolCall } from '../../utilities/provider-executed'
|
|
15
16
|
import { LazyToolManager } from './tools/lazy-tool-manager'
|
|
16
17
|
import {
|
|
17
18
|
MiddlewareAbortError,
|
|
@@ -27,6 +28,7 @@ import {
|
|
|
27
28
|
import { maxIterations as maxIterationsStrategy } from './agent-loop-strategies'
|
|
28
29
|
import { convertMessagesToModelMessages, generateMessageId } from './messages'
|
|
29
30
|
import { MiddlewareRunner } from './middleware/compose'
|
|
31
|
+
import { provideSandboxRuntime } from './middleware/sandbox-runtime'
|
|
30
32
|
import { CapabilityRegistry } from './middleware/capabilities'
|
|
31
33
|
import { validateCapabilities } from './middleware/validate'
|
|
32
34
|
import { MCPManager } from './mcp/manager'
|
|
@@ -67,6 +69,7 @@ import type {
|
|
|
67
69
|
ChatMiddleware,
|
|
68
70
|
ChatMiddlewareConfig,
|
|
69
71
|
ChatMiddlewareContext,
|
|
72
|
+
SandboxFileEvent,
|
|
70
73
|
StructuredOutputMiddlewareConfig,
|
|
71
74
|
} from './middleware/types'
|
|
72
75
|
import type { CheckCoverage } from './middleware/builder'
|
|
@@ -542,6 +545,7 @@ class TextEngine<
|
|
|
542
545
|
// Middleware support
|
|
543
546
|
private readonly middlewareRunner: MiddlewareRunner<TContext>
|
|
544
547
|
private readonly middlewareCtx: ChatMiddlewareContext<TContext>
|
|
548
|
+
private readonly sandboxFileQueue: Array<StreamChunk> = []
|
|
545
549
|
private readonly deferredPromises: Array<Promise<unknown>> = []
|
|
546
550
|
private abortReason?: string
|
|
547
551
|
private readonly middlewareAbortController?: AbortController
|
|
@@ -701,6 +705,21 @@ class TextEngine<
|
|
|
701
705
|
capability[0](this.middlewareCtx, { optional: true }),
|
|
702
706
|
provide: (capability, value) => capability[1](this.middlewareCtx, value),
|
|
703
707
|
}
|
|
708
|
+
|
|
709
|
+
// Provide the internal SandboxRuntime capability so harness adapters and
|
|
710
|
+
// sandbox middleware can emit file events. The sink logs, fans the event
|
|
711
|
+
// out through the middleware `onFile*` hooks (fire-and-forget), and queues
|
|
712
|
+
// a `sandbox.file` custom chunk to be drained into the public stream.
|
|
713
|
+
provideSandboxRuntime(this.middlewareCtx, {
|
|
714
|
+
logger: this.logger,
|
|
715
|
+
emit: (event: SandboxFileEvent) => {
|
|
716
|
+
this.logger.sandbox(`file ${event.type} ${event.path}`, { event })
|
|
717
|
+
void this.middlewareRunner.runSandboxFile(this.middlewareCtx, event)
|
|
718
|
+
this.sandboxFileQueue.push(
|
|
719
|
+
this.createCustomEventChunk('sandbox.file', { ...event }),
|
|
720
|
+
)
|
|
721
|
+
},
|
|
722
|
+
})
|
|
704
723
|
}
|
|
705
724
|
|
|
706
725
|
/** Get the accumulated content after the chat loop completes */
|
|
@@ -1021,6 +1040,10 @@ class TextEngine<
|
|
|
1021
1040
|
threadId: this.threadId,
|
|
1022
1041
|
runId: this.runIdOverride,
|
|
1023
1042
|
parentRunId: this.parentRunIdOverride,
|
|
1043
|
+
// Expose provided capabilities (e.g. sandbox) to harness adapters.
|
|
1044
|
+
capabilities: this.middlewareCtx,
|
|
1045
|
+
// Client approval decisions, for harness interactive-approval resolution.
|
|
1046
|
+
approvals: this.initialApprovals,
|
|
1024
1047
|
...(combinedSchema ? { outputSchema: combinedSchema } : {}),
|
|
1025
1048
|
})) {
|
|
1026
1049
|
if (this.isCancelled()) {
|
|
@@ -1105,10 +1128,16 @@ class TextEngine<
|
|
|
1105
1128
|
await this.middlewareRunner.runOnUsage(this.middlewareCtx, chunk.usage)
|
|
1106
1129
|
}
|
|
1107
1130
|
|
|
1131
|
+
// Drain any sandbox.file events emitted while processing this chunk.
|
|
1132
|
+
yield* this.drainSandboxFileQueue()
|
|
1133
|
+
|
|
1108
1134
|
if (this.earlyTermination) {
|
|
1109
1135
|
break
|
|
1110
1136
|
}
|
|
1111
1137
|
}
|
|
1138
|
+
|
|
1139
|
+
// Drain any remaining sandbox.file events emitted after the stream ended.
|
|
1140
|
+
yield* this.drainSandboxFileQueue()
|
|
1112
1141
|
}
|
|
1113
1142
|
|
|
1114
1143
|
private handleStreamChunk(chunk: StreamChunk): void {
|
|
@@ -1856,6 +1885,13 @@ class TextEngine<
|
|
|
1856
1885
|
for (const message of this.messages) {
|
|
1857
1886
|
if (message.role === 'assistant' && message.toolCalls) {
|
|
1858
1887
|
for (const toolCall of message.toolCalls) {
|
|
1888
|
+
// Provider-executed tool calls (e.g. Anthropic `web_search`) were
|
|
1889
|
+
// already run by the provider; they carry no client result, so they
|
|
1890
|
+
// would otherwise look "pending" forever and the loop would try (and
|
|
1891
|
+
// fail) to execute them client-side. Skip them.
|
|
1892
|
+
if (isProviderExecutedToolCall(toolCall)) {
|
|
1893
|
+
continue
|
|
1894
|
+
}
|
|
1859
1895
|
if (!completedToolIds.has(toolCall.id)) {
|
|
1860
1896
|
pending.push(toolCall)
|
|
1861
1897
|
}
|
|
@@ -2502,6 +2538,17 @@ class TextEngine<
|
|
|
2502
2538
|
}
|
|
2503
2539
|
}
|
|
2504
2540
|
|
|
2541
|
+
/**
|
|
2542
|
+
* Drain queued `sandbox.file` chunks (emitted via the SandboxRuntime sink)
|
|
2543
|
+
* through the middleware pipeline and into the public stream.
|
|
2544
|
+
*/
|
|
2545
|
+
private async *drainSandboxFileQueue(): AsyncGenerator<StreamChunk> {
|
|
2546
|
+
while (this.sandboxFileQueue.length > 0) {
|
|
2547
|
+
const chunk = this.sandboxFileQueue.shift()
|
|
2548
|
+
if (chunk) yield* this.pipeThroughMiddleware(chunk)
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2505
2552
|
/**
|
|
2506
2553
|
* Drain an executeToolCalls async generator, yielding any CustomEvent chunks
|
|
2507
2554
|
* through the middleware pipeline and returning the final ExecuteToolCallsResult.
|
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
ErrorInfo,
|
|
12
12
|
FinishInfo,
|
|
13
13
|
IterationInfo,
|
|
14
|
+
SandboxFileEvent,
|
|
14
15
|
StructuredOutputMiddlewareConfig,
|
|
15
16
|
ToolCallHookContext,
|
|
16
17
|
ToolPhaseCompleteInfo,
|
|
@@ -344,6 +345,39 @@ export class MiddlewareRunner<TContext = unknown> {
|
|
|
344
345
|
return chunks
|
|
345
346
|
}
|
|
346
347
|
|
|
348
|
+
/**
|
|
349
|
+
* Dispatch a sandbox file event to every middleware's `sandbox` hooks, in
|
|
350
|
+
* array order: the catch-all `onFile` then the type-specific hook. Errors are
|
|
351
|
+
* logged and swallowed so one bad hook can't break the run.
|
|
352
|
+
*/
|
|
353
|
+
async runSandboxFile(
|
|
354
|
+
ctx: ChatMiddlewareContext<TContext>,
|
|
355
|
+
event: SandboxFileEvent,
|
|
356
|
+
): Promise<void> {
|
|
357
|
+
const typed = (
|
|
358
|
+
{
|
|
359
|
+
create: 'onFileCreate',
|
|
360
|
+
change: 'onFileChange',
|
|
361
|
+
delete: 'onFileDelete',
|
|
362
|
+
} as const
|
|
363
|
+
)[event.type]
|
|
364
|
+
for (const mw of this.middlewares) {
|
|
365
|
+
const hooks = mw.sandbox
|
|
366
|
+
if (!hooks) continue
|
|
367
|
+
for (const fn of [hooks.onFile, hooks[typed]]) {
|
|
368
|
+
if (!fn) continue
|
|
369
|
+
try {
|
|
370
|
+
await fn(ctx, event)
|
|
371
|
+
} catch (error) {
|
|
372
|
+
this.logger.sandbox(
|
|
373
|
+
`hook=${typed} middleware=${mw.name ?? 'unnamed'} threw`,
|
|
374
|
+
{ middleware: mw.name ?? 'unnamed', error },
|
|
375
|
+
)
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
347
381
|
/**
|
|
348
382
|
* Run onBeforeToolCall through middleware in order.
|
|
349
383
|
* Returns the first non-void decision, or undefined to continue normally.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal runtime seam the chat engine PROVIDES so the sandbox middleware can
|
|
3
|
+
* surface file events without a public ctx method. `emit` runs every
|
|
4
|
+
* middleware's `sandbox` hooks AND emits a CUSTOM `sandbox.file` chunk into the
|
|
5
|
+
* stream; `logger` lets the sandbox layer log under the `sandbox` debug
|
|
6
|
+
* category. Consumed (optionally) by `withSandbox` in `@tanstack/ai-sandbox`.
|
|
7
|
+
*/
|
|
8
|
+
import { createCapability } from './capabilities'
|
|
9
|
+
import type { InternalLogger } from '../../../logger/internal-logger'
|
|
10
|
+
import type { SandboxFileEvent } from './types'
|
|
11
|
+
|
|
12
|
+
export interface SandboxRuntime {
|
|
13
|
+
emit: (event: SandboxFileEvent) => void
|
|
14
|
+
logger: InternalLogger
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const SandboxRuntimeCapability =
|
|
18
|
+
createCapability<SandboxRuntime>()('sandbox-runtime')
|
|
19
|
+
|
|
20
|
+
export const [getSandboxRuntime, provideSandboxRuntime] =
|
|
21
|
+
SandboxRuntimeCapability
|
|
@@ -13,6 +13,37 @@ import type {
|
|
|
13
13
|
CapabilityRegistry,
|
|
14
14
|
} from './capabilities'
|
|
15
15
|
|
|
16
|
+
/** A file change observed inside a sandbox during a chat run. */
|
|
17
|
+
export interface SandboxFileEvent {
|
|
18
|
+
type: 'create' | 'change' | 'delete'
|
|
19
|
+
/** Absolute path inside the sandbox (under the workspace root). */
|
|
20
|
+
path: string
|
|
21
|
+
timestamp: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Sandbox file-event hooks a chat middleware can declare. Fire server-side for
|
|
26
|
+
* every file create/change/delete observed in the sandbox during the run.
|
|
27
|
+
*/
|
|
28
|
+
export interface ChatSandboxHooks<TContext = unknown> {
|
|
29
|
+
onFile?: (
|
|
30
|
+
ctx: ChatMiddlewareContext<TContext>,
|
|
31
|
+
e: SandboxFileEvent,
|
|
32
|
+
) => void | Promise<void>
|
|
33
|
+
onFileCreate?: (
|
|
34
|
+
ctx: ChatMiddlewareContext<TContext>,
|
|
35
|
+
e: SandboxFileEvent,
|
|
36
|
+
) => void | Promise<void>
|
|
37
|
+
onFileChange?: (
|
|
38
|
+
ctx: ChatMiddlewareContext<TContext>,
|
|
39
|
+
e: SandboxFileEvent,
|
|
40
|
+
) => void | Promise<void>
|
|
41
|
+
onFileDelete?: (
|
|
42
|
+
ctx: ChatMiddlewareContext<TContext>,
|
|
43
|
+
e: SandboxFileEvent,
|
|
44
|
+
) => void | Promise<void>
|
|
45
|
+
}
|
|
46
|
+
|
|
16
47
|
// ===========================
|
|
17
48
|
// Middleware Context
|
|
18
49
|
// ===========================
|
|
@@ -539,6 +570,12 @@ export interface ChatMiddleware<TContext = unknown> {
|
|
|
539
570
|
ctx: ChatMiddlewareContext<TContext>,
|
|
540
571
|
info: ErrorInfo,
|
|
541
572
|
) => void | Promise<void>
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Sandbox file-event hooks. Fire when a sandbox provided by `withSandbox` is
|
|
576
|
+
* active during the run and a file is created/changed/deleted. Server-side.
|
|
577
|
+
*/
|
|
578
|
+
sandbox?: ChatSandboxHooks<TContext>
|
|
542
579
|
}
|
|
543
580
|
|
|
544
581
|
/** A `ChatMiddleware` with a permissive context — for use as a constraint. */
|