agents 0.17.3 → 0.18.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.
- package/dist/{agent-tool-types-CNyE1iz_.d.ts → agent-tool-types-BNUGGBzQ.d.ts} +191 -61
- package/dist/agent-tool-types.d.ts +1 -1
- package/dist/{agent-tools-BeOheFBK.d.ts → agent-tools-BFbzVLFc.d.ts} +2 -2
- package/dist/agent-tools.d.ts +1 -1
- package/dist/chat/index.d.ts +29 -9
- package/dist/chat/index.js +31 -41
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/react.d.ts +25 -1
- package/dist/chat/react.js +249 -93
- package/dist/chat/react.js.map +1 -1
- package/dist/chat-sdk/index.d.ts +1 -1
- package/dist/{client-BZ-B3NhC.js → client-CcjiFpTf.js} +352 -81
- package/dist/client-CcjiFpTf.js.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/cloudflare-BldFV0Pa.js +117 -0
- package/dist/cloudflare-BldFV0Pa.js.map +1 -0
- package/dist/index.d.ts +13 -11
- package/dist/index.js +153 -48
- package/dist/index.js.map +1 -1
- package/dist/mcp/client.d.ts +18 -14
- package/dist/mcp/client.js +1 -1
- package/dist/mcp/index.d.ts +38 -30
- package/dist/mcp/index.js +1 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/observability/ai/index.d.ts +155 -0
- package/dist/observability/ai/index.js +1845 -0
- package/dist/observability/ai/index.js.map +1 -0
- package/dist/react.d.ts +1 -1
- package/dist/{retries-CvHJwSuh.d.ts → retries-CAvxtG9d.d.ts} +16 -7
- package/dist/retries.d.ts +8 -6
- package/dist/retries.js +20 -2
- package/dist/retries.js.map +1 -1
- package/dist/serializable.d.ts +1 -1
- package/dist/sub-routing.d.ts +6 -6
- package/dist/vite.d.ts +4 -4
- package/dist/vite.js +4 -2
- package/dist/vite.js.map +1 -1
- package/dist/{wire-types-nflOzNuU.js → wire-types-CU9rLoeS.js} +33 -2
- package/dist/wire-types-CU9rLoeS.js.map +1 -0
- package/dist/workflows.d.ts +1 -1
- package/docs/agent-class.md +9 -1
- package/docs/configuration.md +20 -0
- package/docs/mcp-client.md +52 -4
- package/docs/observability.md +282 -0
- package/package.json +7 -2
- package/dist/client-BZ-B3NhC.js.map +0 -1
- package/dist/wire-types-nflOzNuU.js.map +0 -1
package/dist/sub-routing.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
-
an as
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "./agent-tool-types-
|
|
2
|
+
an as SUB_PREFIX,
|
|
3
|
+
cn as parseSubAgentPath,
|
|
4
|
+
ln as routeSubAgentRequest,
|
|
5
|
+
on as SubAgentPathMatch,
|
|
6
|
+
sn as getSubAgentByName
|
|
7
|
+
} from "./agent-tool-types-BNUGGBzQ.js";
|
|
8
8
|
export {
|
|
9
9
|
SUB_PREFIX,
|
|
10
10
|
SubAgentPathMatch,
|
package/dist/vite.d.ts
CHANGED
|
@@ -3,10 +3,10 @@ import { Plugin } from "vite";
|
|
|
3
3
|
//#region src/vite.d.ts
|
|
4
4
|
interface AgentsPluginOptions {
|
|
5
5
|
/**
|
|
6
|
-
* Replace `turndown` with
|
|
7
|
-
* skill runner) doesn't drag turndown's `require()`-using DOM fallback
|
|
8
|
-
* the Worker's module-init path and break deploys. Enabled by default.
|
|
9
|
-
* `false` if your app uses turndown directly and needs the real
|
|
6
|
+
* Replace `turndown` with a diagnostic stub so `just-bash` (workspace bash
|
|
7
|
+
* tool / skill runner) doesn't drag turndown's `require()`-using DOM fallback
|
|
8
|
+
* into the Worker's module-init path and break deploys. Enabled by default.
|
|
9
|
+
* Set to `false` if your app uses turndown directly and needs the real
|
|
10
10
|
* implementation.
|
|
11
11
|
*/
|
|
12
12
|
stubTurndown?: boolean;
|
package/dist/vite.js
CHANGED
|
@@ -244,6 +244,7 @@ async function buildSkillsModule(dir, warn) {
|
|
|
244
244
|
return `const manifest = ${JSON.stringify(manifest)};\nexport default {\n id: manifest.id,\n fingerprint: manifest.fingerprint,\n async list() {\n return manifest.skills.map(({ body, rawContent, resources, ...skill }) => skill);\n },\n async load(name) {\n const skill = manifest.skills.find((entry) => entry.name === name);\n if (!skill) return null;\n return {\n ...skill,\n resources: skill.resources.map(({ content, ...resource }) => resource)\n };\n },\n async readResource(name, path) {\n const skill = manifest.skills.find((entry) => entry.name === name);\n const resource = skill?.resources.find((entry) => entry.path === path);\n return resource ? { ...resource } : null;\n }\n};\n`;
|
|
245
245
|
}
|
|
246
246
|
const TURNDOWN_STUB_ID = "\0agents:turndown-stub";
|
|
247
|
+
const TURNDOWN_STUB_MESSAGE = "The Agents Vite plugin stubbed the 'turndown' package to keep Workers bundles compatible with just-bash. If your app uses turndown directly, configure the plugin with agents({ stubTurndown: false }).";
|
|
247
248
|
function turndownStubPlugin() {
|
|
248
249
|
return {
|
|
249
250
|
name: "agents-turndown-stub",
|
|
@@ -254,13 +255,14 @@ function turndownStubPlugin() {
|
|
|
254
255
|
},
|
|
255
256
|
load(id) {
|
|
256
257
|
if (id !== TURNDOWN_STUB_ID) return null;
|
|
257
|
-
return `
|
|
258
|
+
return `const message = ${JSON.stringify(TURNDOWN_STUB_MESSAGE)};
|
|
259
|
+
class TurndownService {
|
|
258
260
|
constructor() {}
|
|
259
261
|
use() { return this; }
|
|
260
262
|
addRule() { return this; }
|
|
261
263
|
keep() { return this; }
|
|
262
264
|
remove() { return this; }
|
|
263
|
-
turndown() {
|
|
265
|
+
turndown() { throw new Error(message); }
|
|
264
266
|
}
|
|
265
267
|
export default TurndownService;
|
|
266
268
|
`;
|
package/dist/vite.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.js","names":["parseYaml"],"sources":["../src/vite.ts"],"sourcesContent":["import babel from \"@rolldown/plugin-babel\";\nimport { createHash } from \"node:crypto\";\nimport { readdir, readFile, stat } from \"node:fs/promises\";\nimport { basename, join, resolve } from \"node:path\";\nimport { parse as parseYaml } from \"yaml\";\nimport type { Plugin } from \"vite\";\nimport { compileSkillScript, isCompilableSkillScript } from \"./skills/compile\";\n\nconst SKILLS_SPECIFIER = \"agents:skills\";\nconst SKILLS_VIRTUAL_PREFIX = \"\\0agents:skills:\";\nconst SKILL_RESOURCE_ROOTS = new Set([\n \"references\",\n \"scripts\",\n \"assets\",\n \"graphics\",\n \"fonts\",\n \"templates\",\n \"rendered-files\",\n \"illustrations\"\n]);\nconst SKILL_IGNORED_ROOTS = new Set([\n \".git\",\n \".hg\",\n \".svn\",\n \".DS_Store\",\n \".idea\",\n \".vscode\",\n \"dist\",\n \"build\",\n \"coverage\",\n \"node_modules\"\n]);\nconst SPEC_RESOURCE_ROOTS = new Set([\"references\", \"scripts\", \"assets\"]);\n\n// Bundled skill resources are base64-embedded into the Worker bundle, which\n// inflates them by ~1.33x and competes with app code for the bundle-size\n// budget. These heuristic thresholds (raw bytes) trigger a recommendation to\n// move large assets to an R2-backed skill source instead.\nconst SKILL_ASSET_WARN_BYTES = 256 * 1024;\nconst SKILL_BUNDLE_WARN_BYTES = 1024 * 1024;\n\nfunction formatBytes(bytes: number): string {\n if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(0)}KB`;\n return `${bytes}B`;\n}\n\ninterface SkillFile {\n name: string;\n description: string;\n body: string;\n rawContent: string;\n compatibility?: string;\n license?: string;\n allowedTools?: string;\n metadata?: Record<string, unknown>;\n resources: Array<{\n path: string;\n kind: \"reference\" | \"script\" | \"asset\" | \"file\";\n size: number;\n encoding: \"text\" | \"base64\";\n mimeType?: string;\n content: string;\n precompiled?: boolean;\n }>;\n}\n\nfunction parseFrontmatter(raw: string): {\n data: Record<string, unknown>;\n body: string;\n} {\n const match = raw.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/);\n if (!match) return { data: {}, body: raw };\n const parsed = parseYaml(match[1] ?? \"\");\n const data =\n parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n return { data, body: match[2] ?? \"\" };\n}\n\nfunction stringField(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n\nfunction recordField(value: unknown): Record<string, unknown> | undefined {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction resourceKind(path: string): \"reference\" | \"script\" | \"asset\" | \"file\" {\n if (path.startsWith(\"references/\")) return \"reference\";\n if (path.startsWith(\"scripts/\")) return \"script\";\n if (\n path.startsWith(\"assets/\") ||\n path.startsWith(\"graphics/\") ||\n path.startsWith(\"fonts/\") ||\n path.startsWith(\"templates/\") ||\n path.startsWith(\"rendered-files/\") ||\n path.startsWith(\"illustrations/\")\n ) {\n return \"asset\";\n }\n return \"file\";\n}\n\nconst TEXT_EXTENSIONS = new Set([\n \".bash\",\n \".css\",\n \".csv\",\n \".html\",\n \".js\",\n \".json\",\n \".jsx\",\n \".md\",\n \".mjs\",\n \".py\",\n \".sh\",\n \".svg\",\n \".ts\",\n \".tsx\",\n \".txt\",\n \".xml\",\n \".yaml\",\n \".yml\"\n]);\n\nconst MIME_TYPES = new Map([\n [\".css\", \"text/css\"],\n [\".gif\", \"image/gif\"],\n [\".html\", \"text/html\"],\n [\".jpg\", \"image/jpeg\"],\n [\".jpeg\", \"image/jpeg\"],\n [\".js\", \"text/javascript\"],\n [\".json\", \"application/json\"],\n [\".md\", \"text/markdown\"],\n [\".mjs\", \"text/javascript\"],\n [\".pdf\", \"application/pdf\"],\n [\".png\", \"image/png\"],\n [\".py\", \"text/x-python\"],\n [\".sh\", \"text/x-shellscript\"],\n [\".svg\", \"image/svg+xml\"],\n [\".ts\", \"text/typescript\"],\n [\".tsx\", \"text/typescript\"],\n [\".txt\", \"text/plain\"],\n [\".webp\", \"image/webp\"],\n [\".woff\", \"font/woff\"],\n [\".woff2\", \"font/woff2\"],\n [\".xml\", \"application/xml\"],\n [\".yaml\", \"application/yaml\"],\n [\".yml\", \"application/yaml\"]\n]);\n\nfunction extensionOf(path: string): string {\n const file = path.split(\"/\").at(-1) ?? path;\n const index = file.lastIndexOf(\".\");\n return index === -1 ? \"\" : file.slice(index).toLowerCase();\n}\n\nfunction resourceEncoding(path: string): \"text\" | \"base64\" {\n return TEXT_EXTENSIONS.has(extensionOf(path)) ? \"text\" : \"base64\";\n}\n\nfunction resourceMimeType(path: string): string | undefined {\n return MIME_TYPES.get(extensionOf(path));\n}\n\nasync function collectFiles(\n root: string,\n relativeRoot = \"\",\n warn?: (message: string) => void\n): Promise<Array<{ path: string; absolutePath: string; size: number }>> {\n const entries = await readdir(join(root, relativeRoot), {\n withFileTypes: true\n }).catch(() => []);\n const files: Array<{ path: string; absolutePath: string; size: number }> = [];\n\n for (const entry of entries) {\n if (SKILL_IGNORED_ROOTS.has(entry.name)) continue;\n const relativePath = relativeRoot\n ? `${relativeRoot}/${entry.name}`\n : entry.name;\n const absolutePath = join(root, relativePath);\n if (entry.isDirectory()) {\n const resourceRoot = relativePath.split(\"/\")[0];\n if (!resourceRoot || SKILL_IGNORED_ROOTS.has(resourceRoot)) continue;\n if (!SKILL_RESOURCE_ROOTS.has(resourceRoot)) {\n if (!relativeRoot) {\n warn?.(\n `Ignoring skill directory \"${relativePath}\". Bundled skill resources should live under references/, scripts/, assets/, or a known asset root.`\n );\n }\n continue;\n }\n if (!SPEC_RESOURCE_ROOTS.has(resourceRoot) && !relativeRoot) {\n warn?.(\n `Bundling non-standard skill resource root \"${resourceRoot}/\". Prefer assets/ for portable Agent Skills when possible.`\n );\n }\n files.push(...(await collectFiles(root, relativePath, warn)));\n } else if (entry.isFile() && relativePath !== \"SKILL.md\") {\n const resourceRoot = relativePath.split(\"/\")[0];\n if (!resourceRoot || SKILL_IGNORED_ROOTS.has(resourceRoot)) continue;\n if (!SKILL_RESOURCE_ROOTS.has(resourceRoot)) {\n warn?.(\n `Ignoring skill file \"${relativePath}\". Bundled skill resources should live under references/, scripts/, assets/, or a known asset root.`\n );\n continue;\n }\n const info = await stat(absolutePath);\n files.push({ path: relativePath, absolutePath, size: info.size });\n }\n }\n\n return files.sort((a, b) => a.path.localeCompare(b.path));\n}\n\nasync function collectWatchTargets(\n root: string,\n relativeRoot = \"\"\n): Promise<string[]> {\n const directory = join(root, relativeRoot);\n const entries = await readdir(directory, { withFileTypes: true }).catch(\n () => []\n );\n const targets = [directory];\n\n for (const entry of entries) {\n if (SKILL_IGNORED_ROOTS.has(entry.name)) continue;\n const relativePath = relativeRoot\n ? `${relativeRoot}/${entry.name}`\n : entry.name;\n const resourceRoot = relativePath.split(\"/\")[0];\n if (!resourceRoot || SKILL_IGNORED_ROOTS.has(resourceRoot)) continue;\n\n const absolutePath = join(root, relativePath);\n targets.push(absolutePath);\n\n if (entry.isDirectory()) {\n targets.push(...(await collectWatchTargets(root, relativePath)));\n }\n }\n\n return targets;\n}\n\nasync function readSkill(\n skillDir: string,\n warn?: (message: string) => void\n): Promise<SkillFile | null> {\n const skillPath = join(skillDir, \"SKILL.md\");\n const rawContent = await readFile(skillPath, \"utf8\").catch(() => null);\n if (rawContent === null) return null;\n\n const { data, body } = parseFrontmatter(rawContent);\n const name = stringField(data.name);\n const description = stringField(data.description);\n if (!name || !description) return null;\n\n const resources = await Promise.all(\n (await collectFiles(skillDir, \"\", warn)).map(async (file) => {\n const encoding = resourceEncoding(file.path);\n const bytes = await readFile(file.absolutePath);\n const kind = resourceKind(file.path);\n let content =\n encoding === \"base64\" ? bytes.toString(\"base64\") : bytes.toString();\n let precompiled = false;\n\n // Compile text script resources to self-contained JS at build time so the\n // runtime never needs an in-Worker bundler to run them.\n let size = file.size;\n if (\n kind === \"script\" &&\n encoding === \"text\" &&\n isCompilableSkillScript(file.path)\n ) {\n try {\n content = (await compileSkillScript(file.absolutePath)).content;\n precompiled = true;\n // The compiled bundle (which may inline sibling files and bundled\n // dependencies) is what actually ships, so report its size for the\n // bundle-size warnings rather than the raw source size.\n size = Buffer.byteLength(content);\n } catch (error) {\n warn?.(\n `Failed to compile skill script \"${file.path}\": ${\n error instanceof Error ? error.message : String(error)\n }. Skill scripts must be self-contained, compilable modules to run at runtime.`\n );\n }\n }\n\n return {\n path: file.path,\n kind,\n size,\n encoding,\n mimeType: resourceMimeType(file.path),\n content,\n precompiled\n };\n })\n );\n\n return {\n name,\n description,\n body,\n rawContent,\n compatibility: stringField(data.compatibility),\n license: stringField(data.license),\n allowedTools: stringField(data[\"allowed-tools\"]),\n metadata: recordField(data.metadata),\n resources\n };\n}\n\nasync function buildSkillsModule(\n dir: string,\n warn?: (message: string) => void\n): Promise<string> {\n const entries = await readdir(dir, { withFileTypes: true });\n const skills: SkillFile[] = [];\n const seen = new Set<string>();\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const skill = await readSkill(join(dir, entry.name), warn);\n if (!skill) continue;\n if (seen.has(skill.name)) {\n warn?.(\n `Duplicate bundled skill name \"${skill.name}\" in \"${entry.name}/\"; keeping the first occurrence and ignoring this one.`\n );\n continue;\n }\n seen.add(skill.name);\n skills.push(skill);\n }\n\n skills.sort((a, b) => a.name.localeCompare(b.name));\n\n let totalBytes = 0;\n for (const skill of skills) {\n for (const resource of skill.resources) {\n totalBytes += resource.size;\n if (resource.size > SKILL_ASSET_WARN_BYTES) {\n warn?.(\n `Bundled skill resource \"${skill.name}/${resource.path}\" is ${formatBytes(resource.size)}; large assets bloat the Worker bundle (base64, ~1.33x). Prefer an R2-backed source via skills.r2().`\n );\n }\n }\n }\n if (totalBytes > SKILL_BUNDLE_WARN_BYTES) {\n warn?.(\n `Bundled skills total ${formatBytes(totalBytes)} of embedded resources; this competes with the Worker bundle-size budget. Consider serving large skills from R2 via skills.r2().`\n );\n }\n\n const hash = createHash(\"sha256\");\n hash.update(JSON.stringify(skills));\n\n const manifest = {\n id: `bundle:${basename(dir)}`,\n fingerprint: hash.digest(\"hex\"),\n skills\n };\n\n return `const manifest = ${JSON.stringify(manifest)};\\nexport default {\\n id: manifest.id,\\n fingerprint: manifest.fingerprint,\\n async list() {\\n return manifest.skills.map(({ body, rawContent, resources, ...skill }) => skill);\\n },\\n async load(name) {\\n const skill = manifest.skills.find((entry) => entry.name === name);\\n if (!skill) return null;\\n return {\\n ...skill,\\n resources: skill.resources.map(({ content, ...resource }) => resource)\\n };\\n },\\n async readResource(name, path) {\\n const skill = manifest.skills.find((entry) => entry.name === name);\\n const resource = skill?.resources.find((entry) => entry.path === path);\\n return resource ? { ...resource } : null;\\n }\\n};\\n`;\n}\n\nconst TURNDOWN_STUB_ID = \"\\0agents:turndown-stub\";\n\n// `just-bash` (pulled in by the workspace bash tool / skill runner) statically\n// depends on `turndown`, whose ESM build runs a top-level `require()` on its\n// Node DOM fallback. Workers is ESM with no global `require`, so the module\n// throws at startup — even when the bash tool is never used. turndown is only\n// needed by just-bash's niche `html-to-markdown` command, so we replace it with\n// an inert stub by default to keep Workers deploys clean. Opt out with\n// `agents({ stubTurndown: false })` if you rely on turndown elsewhere.\nfunction turndownStubPlugin(): Plugin {\n return {\n name: \"agents-turndown-stub\",\n enforce: \"pre\",\n resolveId(source) {\n if (source === \"turndown\") return TURNDOWN_STUB_ID;\n return null;\n },\n load(id) {\n if (id !== TURNDOWN_STUB_ID) return null;\n return `class TurndownService {\n constructor() {}\n use() { return this; }\n addRule() { return this; }\n keep() { return this; }\n remove() { return this; }\n turndown() { return \"\"; }\n}\nexport default TurndownService;\n`;\n }\n };\n}\n\nfunction skillsImportPlugin(): Plugin {\n return {\n name: \"agents-skills-import\",\n async resolveId(source, importer) {\n // `agents:skills` resolves to a `./skills` directory next to the\n // importer; `agents:skills/<dir>` points at a sibling directory.\n if (\n source !== SKILLS_SPECIFIER &&\n !source.startsWith(`${SKILLS_SPECIFIER}/`)\n ) {\n return null;\n }\n if (!importer) return null;\n const relative =\n source === SKILLS_SPECIFIER\n ? \"skills\"\n : source.slice(SKILLS_SPECIFIER.length + 1);\n const resolved = resolve(importer, \"..\", relative);\n return `${SKILLS_VIRTUAL_PREFIX}${resolved}`;\n },\n async load(id) {\n if (!id.startsWith(SKILLS_VIRTUAL_PREFIX)) return null;\n const dir = id.slice(SKILLS_VIRTUAL_PREFIX.length);\n for (const target of await collectWatchTargets(dir)) {\n this.addWatchFile(target);\n }\n return buildSkillsModule(dir, (message) => this.warn(message));\n }\n };\n}\n\nexport interface AgentsPluginOptions {\n /**\n * Replace `turndown` with an inert stub so `just-bash` (workspace bash tool /\n * skill runner) doesn't drag turndown's `require()`-using DOM fallback into\n * the Worker's module-init path and break deploys. Enabled by default. Set to\n * `false` if your app uses turndown directly and needs the real\n * implementation.\n */\n stubTurndown?: boolean;\n}\n\n/**\n * Vite plugin for Agents SDK projects.\n *\n * Handles TC39 decorator transforms (Oxc doesn't support them yet, oxc#9170) so\n * `@callable()` works at runtime, the `agents:skills` import transform, and\n * stubbing `turndown` to keep Workers deploys clean. Will grow to cover other\n * Agents-specific build concerns as needed.\n */\nexport default function agents(options: AgentsPluginOptions = {}): Plugin[] {\n const { stubTurndown = true } = options;\n return [\n ...(stubTurndown ? [turndownStubPlugin()] : []),\n skillsImportPlugin(),\n babel({\n presets: [\n {\n preset: () => ({\n plugins: [\n [\"@babel/plugin-proposal-decorators\", { version: \"2023-11\" }]\n ]\n }),\n rolldown: { filter: { code: \"@\" } }\n }\n ]\n }) as unknown as Plugin\n ];\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vite.js","names":["parseYaml"],"sources":["../src/vite.ts"],"sourcesContent":["import babel from \"@rolldown/plugin-babel\";\nimport { createHash } from \"node:crypto\";\nimport { readdir, readFile, stat } from \"node:fs/promises\";\nimport { basename, join, resolve } from \"node:path\";\nimport { parse as parseYaml } from \"yaml\";\nimport type { Plugin } from \"vite\";\nimport { compileSkillScript, isCompilableSkillScript } from \"./skills/compile\";\n\nconst SKILLS_SPECIFIER = \"agents:skills\";\nconst SKILLS_VIRTUAL_PREFIX = \"\\0agents:skills:\";\nconst SKILL_RESOURCE_ROOTS = new Set([\n \"references\",\n \"scripts\",\n \"assets\",\n \"graphics\",\n \"fonts\",\n \"templates\",\n \"rendered-files\",\n \"illustrations\"\n]);\nconst SKILL_IGNORED_ROOTS = new Set([\n \".git\",\n \".hg\",\n \".svn\",\n \".DS_Store\",\n \".idea\",\n \".vscode\",\n \"dist\",\n \"build\",\n \"coverage\",\n \"node_modules\"\n]);\nconst SPEC_RESOURCE_ROOTS = new Set([\"references\", \"scripts\", \"assets\"]);\n\n// Bundled skill resources are base64-embedded into the Worker bundle, which\n// inflates them by ~1.33x and competes with app code for the bundle-size\n// budget. These heuristic thresholds (raw bytes) trigger a recommendation to\n// move large assets to an R2-backed skill source instead.\nconst SKILL_ASSET_WARN_BYTES = 256 * 1024;\nconst SKILL_BUNDLE_WARN_BYTES = 1024 * 1024;\n\nfunction formatBytes(bytes: number): string {\n if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(0)}KB`;\n return `${bytes}B`;\n}\n\ninterface SkillFile {\n name: string;\n description: string;\n body: string;\n rawContent: string;\n compatibility?: string;\n license?: string;\n allowedTools?: string;\n metadata?: Record<string, unknown>;\n resources: Array<{\n path: string;\n kind: \"reference\" | \"script\" | \"asset\" | \"file\";\n size: number;\n encoding: \"text\" | \"base64\";\n mimeType?: string;\n content: string;\n precompiled?: boolean;\n }>;\n}\n\nfunction parseFrontmatter(raw: string): {\n data: Record<string, unknown>;\n body: string;\n} {\n const match = raw.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/);\n if (!match) return { data: {}, body: raw };\n const parsed = parseYaml(match[1] ?? \"\");\n const data =\n parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n return { data, body: match[2] ?? \"\" };\n}\n\nfunction stringField(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n\nfunction recordField(value: unknown): Record<string, unknown> | undefined {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction resourceKind(path: string): \"reference\" | \"script\" | \"asset\" | \"file\" {\n if (path.startsWith(\"references/\")) return \"reference\";\n if (path.startsWith(\"scripts/\")) return \"script\";\n if (\n path.startsWith(\"assets/\") ||\n path.startsWith(\"graphics/\") ||\n path.startsWith(\"fonts/\") ||\n path.startsWith(\"templates/\") ||\n path.startsWith(\"rendered-files/\") ||\n path.startsWith(\"illustrations/\")\n ) {\n return \"asset\";\n }\n return \"file\";\n}\n\nconst TEXT_EXTENSIONS = new Set([\n \".bash\",\n \".css\",\n \".csv\",\n \".html\",\n \".js\",\n \".json\",\n \".jsx\",\n \".md\",\n \".mjs\",\n \".py\",\n \".sh\",\n \".svg\",\n \".ts\",\n \".tsx\",\n \".txt\",\n \".xml\",\n \".yaml\",\n \".yml\"\n]);\n\nconst MIME_TYPES = new Map([\n [\".css\", \"text/css\"],\n [\".gif\", \"image/gif\"],\n [\".html\", \"text/html\"],\n [\".jpg\", \"image/jpeg\"],\n [\".jpeg\", \"image/jpeg\"],\n [\".js\", \"text/javascript\"],\n [\".json\", \"application/json\"],\n [\".md\", \"text/markdown\"],\n [\".mjs\", \"text/javascript\"],\n [\".pdf\", \"application/pdf\"],\n [\".png\", \"image/png\"],\n [\".py\", \"text/x-python\"],\n [\".sh\", \"text/x-shellscript\"],\n [\".svg\", \"image/svg+xml\"],\n [\".ts\", \"text/typescript\"],\n [\".tsx\", \"text/typescript\"],\n [\".txt\", \"text/plain\"],\n [\".webp\", \"image/webp\"],\n [\".woff\", \"font/woff\"],\n [\".woff2\", \"font/woff2\"],\n [\".xml\", \"application/xml\"],\n [\".yaml\", \"application/yaml\"],\n [\".yml\", \"application/yaml\"]\n]);\n\nfunction extensionOf(path: string): string {\n const file = path.split(\"/\").at(-1) ?? path;\n const index = file.lastIndexOf(\".\");\n return index === -1 ? \"\" : file.slice(index).toLowerCase();\n}\n\nfunction resourceEncoding(path: string): \"text\" | \"base64\" {\n return TEXT_EXTENSIONS.has(extensionOf(path)) ? \"text\" : \"base64\";\n}\n\nfunction resourceMimeType(path: string): string | undefined {\n return MIME_TYPES.get(extensionOf(path));\n}\n\nasync function collectFiles(\n root: string,\n relativeRoot = \"\",\n warn?: (message: string) => void\n): Promise<Array<{ path: string; absolutePath: string; size: number }>> {\n const entries = await readdir(join(root, relativeRoot), {\n withFileTypes: true\n }).catch(() => []);\n const files: Array<{ path: string; absolutePath: string; size: number }> = [];\n\n for (const entry of entries) {\n if (SKILL_IGNORED_ROOTS.has(entry.name)) continue;\n const relativePath = relativeRoot\n ? `${relativeRoot}/${entry.name}`\n : entry.name;\n const absolutePath = join(root, relativePath);\n if (entry.isDirectory()) {\n const resourceRoot = relativePath.split(\"/\")[0];\n if (!resourceRoot || SKILL_IGNORED_ROOTS.has(resourceRoot)) continue;\n if (!SKILL_RESOURCE_ROOTS.has(resourceRoot)) {\n if (!relativeRoot) {\n warn?.(\n `Ignoring skill directory \"${relativePath}\". Bundled skill resources should live under references/, scripts/, assets/, or a known asset root.`\n );\n }\n continue;\n }\n if (!SPEC_RESOURCE_ROOTS.has(resourceRoot) && !relativeRoot) {\n warn?.(\n `Bundling non-standard skill resource root \"${resourceRoot}/\". Prefer assets/ for portable Agent Skills when possible.`\n );\n }\n files.push(...(await collectFiles(root, relativePath, warn)));\n } else if (entry.isFile() && relativePath !== \"SKILL.md\") {\n const resourceRoot = relativePath.split(\"/\")[0];\n if (!resourceRoot || SKILL_IGNORED_ROOTS.has(resourceRoot)) continue;\n if (!SKILL_RESOURCE_ROOTS.has(resourceRoot)) {\n warn?.(\n `Ignoring skill file \"${relativePath}\". Bundled skill resources should live under references/, scripts/, assets/, or a known asset root.`\n );\n continue;\n }\n const info = await stat(absolutePath);\n files.push({ path: relativePath, absolutePath, size: info.size });\n }\n }\n\n return files.sort((a, b) => a.path.localeCompare(b.path));\n}\n\nasync function collectWatchTargets(\n root: string,\n relativeRoot = \"\"\n): Promise<string[]> {\n const directory = join(root, relativeRoot);\n const entries = await readdir(directory, { withFileTypes: true }).catch(\n () => []\n );\n const targets = [directory];\n\n for (const entry of entries) {\n if (SKILL_IGNORED_ROOTS.has(entry.name)) continue;\n const relativePath = relativeRoot\n ? `${relativeRoot}/${entry.name}`\n : entry.name;\n const resourceRoot = relativePath.split(\"/\")[0];\n if (!resourceRoot || SKILL_IGNORED_ROOTS.has(resourceRoot)) continue;\n\n const absolutePath = join(root, relativePath);\n targets.push(absolutePath);\n\n if (entry.isDirectory()) {\n targets.push(...(await collectWatchTargets(root, relativePath)));\n }\n }\n\n return targets;\n}\n\nasync function readSkill(\n skillDir: string,\n warn?: (message: string) => void\n): Promise<SkillFile | null> {\n const skillPath = join(skillDir, \"SKILL.md\");\n const rawContent = await readFile(skillPath, \"utf8\").catch(() => null);\n if (rawContent === null) return null;\n\n const { data, body } = parseFrontmatter(rawContent);\n const name = stringField(data.name);\n const description = stringField(data.description);\n if (!name || !description) return null;\n\n const resources = await Promise.all(\n (await collectFiles(skillDir, \"\", warn)).map(async (file) => {\n const encoding = resourceEncoding(file.path);\n const bytes = await readFile(file.absolutePath);\n const kind = resourceKind(file.path);\n let content =\n encoding === \"base64\" ? bytes.toString(\"base64\") : bytes.toString();\n let precompiled = false;\n\n // Compile text script resources to self-contained JS at build time so the\n // runtime never needs an in-Worker bundler to run them.\n let size = file.size;\n if (\n kind === \"script\" &&\n encoding === \"text\" &&\n isCompilableSkillScript(file.path)\n ) {\n try {\n content = (await compileSkillScript(file.absolutePath)).content;\n precompiled = true;\n // The compiled bundle (which may inline sibling files and bundled\n // dependencies) is what actually ships, so report its size for the\n // bundle-size warnings rather than the raw source size.\n size = Buffer.byteLength(content);\n } catch (error) {\n warn?.(\n `Failed to compile skill script \"${file.path}\": ${\n error instanceof Error ? error.message : String(error)\n }. Skill scripts must be self-contained, compilable modules to run at runtime.`\n );\n }\n }\n\n return {\n path: file.path,\n kind,\n size,\n encoding,\n mimeType: resourceMimeType(file.path),\n content,\n precompiled\n };\n })\n );\n\n return {\n name,\n description,\n body,\n rawContent,\n compatibility: stringField(data.compatibility),\n license: stringField(data.license),\n allowedTools: stringField(data[\"allowed-tools\"]),\n metadata: recordField(data.metadata),\n resources\n };\n}\n\nasync function buildSkillsModule(\n dir: string,\n warn?: (message: string) => void\n): Promise<string> {\n const entries = await readdir(dir, { withFileTypes: true });\n const skills: SkillFile[] = [];\n const seen = new Set<string>();\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const skill = await readSkill(join(dir, entry.name), warn);\n if (!skill) continue;\n if (seen.has(skill.name)) {\n warn?.(\n `Duplicate bundled skill name \"${skill.name}\" in \"${entry.name}/\"; keeping the first occurrence and ignoring this one.`\n );\n continue;\n }\n seen.add(skill.name);\n skills.push(skill);\n }\n\n skills.sort((a, b) => a.name.localeCompare(b.name));\n\n let totalBytes = 0;\n for (const skill of skills) {\n for (const resource of skill.resources) {\n totalBytes += resource.size;\n if (resource.size > SKILL_ASSET_WARN_BYTES) {\n warn?.(\n `Bundled skill resource \"${skill.name}/${resource.path}\" is ${formatBytes(resource.size)}; large assets bloat the Worker bundle (base64, ~1.33x). Prefer an R2-backed source via skills.r2().`\n );\n }\n }\n }\n if (totalBytes > SKILL_BUNDLE_WARN_BYTES) {\n warn?.(\n `Bundled skills total ${formatBytes(totalBytes)} of embedded resources; this competes with the Worker bundle-size budget. Consider serving large skills from R2 via skills.r2().`\n );\n }\n\n const hash = createHash(\"sha256\");\n hash.update(JSON.stringify(skills));\n\n const manifest = {\n id: `bundle:${basename(dir)}`,\n fingerprint: hash.digest(\"hex\"),\n skills\n };\n\n return `const manifest = ${JSON.stringify(manifest)};\\nexport default {\\n id: manifest.id,\\n fingerprint: manifest.fingerprint,\\n async list() {\\n return manifest.skills.map(({ body, rawContent, resources, ...skill }) => skill);\\n },\\n async load(name) {\\n const skill = manifest.skills.find((entry) => entry.name === name);\\n if (!skill) return null;\\n return {\\n ...skill,\\n resources: skill.resources.map(({ content, ...resource }) => resource)\\n };\\n },\\n async readResource(name, path) {\\n const skill = manifest.skills.find((entry) => entry.name === name);\\n const resource = skill?.resources.find((entry) => entry.path === path);\\n return resource ? { ...resource } : null;\\n }\\n};\\n`;\n}\n\nconst TURNDOWN_STUB_ID = \"\\0agents:turndown-stub\";\nconst TURNDOWN_STUB_MESSAGE =\n \"The Agents Vite plugin stubbed the 'turndown' package to keep Workers \" +\n \"bundles compatible with just-bash. If your app uses turndown directly, \" +\n \"configure the plugin with agents({ stubTurndown: false }).\";\n\n// `just-bash` (pulled in by the workspace bash tool / skill runner) statically\n// depends on `turndown`, whose ESM build runs a top-level `require()` on its\n// Node DOM fallback. Workers is ESM with no global `require`, so the module\n// throws at startup — even when the bash tool is never used. turndown is only\n// needed by just-bash's niche `html-to-markdown` command, so we replace it with\n// a diagnostic stub by default to keep Workers deploys clean. Opt out with\n// `agents({ stubTurndown: false })` if you rely on turndown elsewhere.\nfunction turndownStubPlugin(): Plugin {\n return {\n name: \"agents-turndown-stub\",\n enforce: \"pre\",\n resolveId(source) {\n if (source === \"turndown\") return TURNDOWN_STUB_ID;\n return null;\n },\n load(id) {\n if (id !== TURNDOWN_STUB_ID) return null;\n return `const message = ${JSON.stringify(TURNDOWN_STUB_MESSAGE)};\nclass TurndownService {\n constructor() {}\n use() { return this; }\n addRule() { return this; }\n keep() { return this; }\n remove() { return this; }\n turndown() { throw new Error(message); }\n}\nexport default TurndownService;\n`;\n }\n };\n}\n\nfunction skillsImportPlugin(): Plugin {\n return {\n name: \"agents-skills-import\",\n async resolveId(source, importer) {\n // `agents:skills` resolves to a `./skills` directory next to the\n // importer; `agents:skills/<dir>` points at a sibling directory.\n if (\n source !== SKILLS_SPECIFIER &&\n !source.startsWith(`${SKILLS_SPECIFIER}/`)\n ) {\n return null;\n }\n if (!importer) return null;\n const relative =\n source === SKILLS_SPECIFIER\n ? \"skills\"\n : source.slice(SKILLS_SPECIFIER.length + 1);\n const resolved = resolve(importer, \"..\", relative);\n return `${SKILLS_VIRTUAL_PREFIX}${resolved}`;\n },\n async load(id) {\n if (!id.startsWith(SKILLS_VIRTUAL_PREFIX)) return null;\n const dir = id.slice(SKILLS_VIRTUAL_PREFIX.length);\n for (const target of await collectWatchTargets(dir)) {\n this.addWatchFile(target);\n }\n return buildSkillsModule(dir, (message) => this.warn(message));\n }\n };\n}\n\nexport interface AgentsPluginOptions {\n /**\n * Replace `turndown` with a diagnostic stub so `just-bash` (workspace bash\n * tool / skill runner) doesn't drag turndown's `require()`-using DOM fallback\n * into the Worker's module-init path and break deploys. Enabled by default.\n * Set to `false` if your app uses turndown directly and needs the real\n * implementation.\n */\n stubTurndown?: boolean;\n}\n\n/**\n * Vite plugin for Agents SDK projects.\n *\n * Handles TC39 decorator transforms (Oxc doesn't support them yet, oxc#9170) so\n * `@callable()` works at runtime, the `agents:skills` import transform, and\n * stubbing `turndown` to keep Workers deploys clean. Will grow to cover other\n * Agents-specific build concerns as needed.\n */\nexport default function agents(options: AgentsPluginOptions = {}): Plugin[] {\n const { stubTurndown = true } = options;\n return [\n ...(stubTurndown ? [turndownStubPlugin()] : []),\n skillsImportPlugin(),\n babel({\n presets: [\n {\n preset: () => ({\n plugins: [\n [\"@babel/plugin-proposal-decorators\", { version: \"2023-11\" }]\n ]\n }),\n rolldown: { filter: { code: \"@\" } }\n }\n ]\n }) as unknown as Plugin\n ];\n}\n"],"mappings":"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"}
|
|
@@ -191,6 +191,37 @@ function transition(state, event) {
|
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
//#endregion
|
|
194
|
+
//#region src/chat/protocol.ts
|
|
195
|
+
/**
|
|
196
|
+
* Wire protocol message type constants for the cf_agent_chat_* protocol.
|
|
197
|
+
*
|
|
198
|
+
* These are the string values used on the wire between agent servers and
|
|
199
|
+
* clients. Both @cloudflare/ai-chat (via its MessageType enum) and
|
|
200
|
+
* @cloudflare/think use these values.
|
|
201
|
+
*/
|
|
202
|
+
const STREAM_RESUME_NONE_REASONS = {
|
|
203
|
+
/** No active, pending, or terminal stream exists for this agent. */
|
|
204
|
+
IDLE: "idle",
|
|
205
|
+
/** An active tool continuation is owned by another live connection. */
|
|
206
|
+
CONTINUATION_OWNED: "continuation-owned"
|
|
207
|
+
};
|
|
208
|
+
const CHAT_MESSAGE_TYPES = {
|
|
209
|
+
CHAT_MESSAGES: "cf_agent_chat_messages",
|
|
210
|
+
USE_CHAT_REQUEST: "cf_agent_use_chat_request",
|
|
211
|
+
USE_CHAT_RESPONSE: "cf_agent_use_chat_response",
|
|
212
|
+
CHAT_CLEAR: "cf_agent_chat_clear",
|
|
213
|
+
CHAT_REQUEST_CANCEL: "cf_agent_chat_request_cancel",
|
|
214
|
+
STREAM_RESUMING: "cf_agent_stream_resuming",
|
|
215
|
+
STREAM_RESUME_ACK: "cf_agent_stream_resume_ack",
|
|
216
|
+
STREAM_RESUME_REQUEST: "cf_agent_stream_resume_request",
|
|
217
|
+
STREAM_RESUME_NONE: "cf_agent_stream_resume_none",
|
|
218
|
+
STREAM_PENDING: "cf_agent_stream_pending",
|
|
219
|
+
TOOL_RESULT: "cf_agent_tool_result",
|
|
220
|
+
TOOL_APPROVAL: "cf_agent_tool_approval",
|
|
221
|
+
MESSAGE_UPDATED: "cf_agent_message_updated",
|
|
222
|
+
CHAT_RECOVERING: "cf_agent_chat_recovering"
|
|
223
|
+
};
|
|
224
|
+
//#endregion
|
|
194
225
|
//#region src/chat/wire-types.ts
|
|
195
226
|
/**
|
|
196
227
|
* Enum for message types to improve type safety and maintainability
|
|
@@ -235,6 +266,6 @@ let MessageType = /* @__PURE__ */ function(MessageType) {
|
|
|
235
266
|
return MessageType;
|
|
236
267
|
}({});
|
|
237
268
|
//#endregion
|
|
238
|
-
export { transition as n,
|
|
269
|
+
export { StreamAccumulator as a, transition as i, CHAT_MESSAGE_TYPES as n, STREAM_RESUME_NONE_REASONS as r, MessageType as t };
|
|
239
270
|
|
|
240
|
-
//# sourceMappingURL=wire-types-
|
|
271
|
+
//# sourceMappingURL=wire-types-CU9rLoeS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wire-types-CU9rLoeS.js","names":[],"sources":["../src/chat/stream-accumulator.ts","../src/chat/broadcast-state.ts","../src/chat/protocol.ts","../src/chat/wire-types.ts"],"sourcesContent":["/**\n * StreamAccumulator — unified chunk-to-message builder.\n *\n * Used by @cloudflare/ai-chat (server + client) and @cloudflare/think\n * to incrementally build a UIMessage from stream chunks. Wraps\n * applyChunkToParts and handles the metadata chunk types (start, finish,\n * message-metadata, error) that applyChunkToParts does not cover.\n *\n * The accumulator signals domain-specific concerns (early persistence,\n * cross-message tool updates) via ChunkAction returns — callers handle\n * these according to their context.\n */\n\nimport type { UIMessage } from \"ai\";\nimport { applyChunkToParts, type StreamChunkData } from \"./message-builder\";\n\nfunction asMetadata(value: unknown): Record<string, unknown> | undefined {\n if (value != null && typeof value === \"object\" && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n return undefined;\n}\n\nexport interface StreamAccumulatorOptions {\n messageId: string;\n continuation?: boolean;\n existingParts?: UIMessage[\"parts\"];\n existingMetadata?: Record<string, unknown>;\n}\n\nexport type ChunkAction =\n | {\n type: \"start\";\n messageId?: string;\n metadata?: Record<string, unknown>;\n }\n | {\n type: \"finish\";\n finishReason?: string;\n metadata?: Record<string, unknown>;\n }\n | { type: \"message-metadata\"; metadata: Record<string, unknown> }\n | { type: \"tool-approval-request\"; toolCallId: string }\n | {\n type: \"cross-message-tool-update\";\n updateType: \"output-available\" | \"output-error\";\n toolCallId: string;\n output?: unknown;\n errorText?: string;\n preliminary?: boolean;\n }\n | { type: \"error\"; error: string };\n\nexport interface ChunkResult {\n handled: boolean;\n action?: ChunkAction;\n}\n\nexport class StreamAccumulator {\n messageId: string;\n readonly parts: UIMessage[\"parts\"];\n metadata?: Record<string, unknown>;\n private _isContinuation: boolean;\n\n constructor(options: StreamAccumulatorOptions) {\n this.messageId = options.messageId;\n this._isContinuation = options.continuation ?? false;\n this.parts = options.existingParts ? [...options.existingParts] : [];\n this.metadata = options.existingMetadata\n ? { ...options.existingMetadata }\n : undefined;\n }\n\n applyChunk(chunk: StreamChunkData): ChunkResult {\n const handled = applyChunkToParts(this.parts, chunk);\n\n // Detect tool-approval-request for early persistence signaling\n if (chunk.type === \"tool-approval-request\" && chunk.toolCallId) {\n return {\n handled,\n action: { type: \"tool-approval-request\", toolCallId: chunk.toolCallId }\n };\n }\n\n // Detect cross-message tool output/error: applyChunkToParts returns true\n // for recognized types but silently does nothing when the toolCallId\n // doesn't exist in the current parts array.\n if (\n (chunk.type === \"tool-output-available\" ||\n chunk.type === \"tool-output-error\") &&\n chunk.toolCallId\n ) {\n const foundInParts = this.parts.some(\n (p) => \"toolCallId\" in p && p.toolCallId === chunk.toolCallId\n );\n if (!foundInParts) {\n return {\n handled,\n action: {\n type: \"cross-message-tool-update\",\n updateType:\n chunk.type === \"tool-output-available\"\n ? \"output-available\"\n : \"output-error\",\n toolCallId: chunk.toolCallId,\n output: chunk.output,\n errorText: chunk.errorText,\n preliminary: chunk.preliminary\n }\n };\n }\n }\n\n if (!handled) {\n switch (chunk.type) {\n case \"start\": {\n if (chunk.messageId != null && !this._isContinuation) {\n this.messageId = chunk.messageId;\n }\n const startMeta = asMetadata(chunk.messageMetadata);\n if (startMeta) {\n this.metadata = this.metadata\n ? { ...this.metadata, ...startMeta }\n : { ...startMeta };\n }\n return {\n handled: true,\n action: {\n type: \"start\",\n messageId: chunk.messageId,\n metadata: startMeta\n }\n };\n }\n case \"finish\": {\n const finishMeta = asMetadata(chunk.messageMetadata);\n if (finishMeta) {\n this.metadata = this.metadata\n ? { ...this.metadata, ...finishMeta }\n : { ...finishMeta };\n }\n const finishReason =\n \"finishReason\" in chunk\n ? (chunk.finishReason as string)\n : undefined;\n return {\n handled: true,\n action: {\n type: \"finish\",\n finishReason,\n metadata: finishMeta\n }\n };\n }\n case \"message-metadata\": {\n const msgMeta = asMetadata(chunk.messageMetadata);\n if (msgMeta) {\n this.metadata = this.metadata\n ? { ...this.metadata, ...msgMeta }\n : { ...msgMeta };\n }\n return {\n handled: true,\n action: {\n type: \"message-metadata\",\n metadata: msgMeta ?? {}\n }\n };\n }\n case \"finish-step\": {\n return { handled: true };\n }\n case \"error\": {\n return {\n handled: true,\n action: {\n type: \"error\",\n error: chunk.errorText ?? JSON.stringify(chunk)\n }\n };\n }\n }\n }\n\n return { handled };\n }\n\n /** Snapshot the current state as a UIMessage. */\n toMessage(): UIMessage {\n return {\n id: this.messageId,\n role: \"assistant\",\n parts: [...this.parts],\n ...(this.metadata != null && { metadata: this.metadata })\n } as UIMessage;\n }\n\n /**\n * Merge this accumulator's message into an existing message array.\n * Handles continuation (walk backward for last assistant), replacement\n * (update existing by messageId), or append (new message).\n */\n mergeInto(messages: UIMessage[]): UIMessage[] {\n let existingIdx = messages.findIndex((m) => m.id === this.messageId);\n\n if (existingIdx < 0 && this._isContinuation) {\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role === \"assistant\") {\n existingIdx = i;\n break;\n }\n }\n }\n\n const messageId =\n existingIdx >= 0 ? messages[existingIdx].id : this.messageId;\n\n const partialMessage: UIMessage = {\n id: messageId,\n role: \"assistant\",\n parts: [...this.parts],\n ...(this.metadata != null && { metadata: this.metadata })\n } as UIMessage;\n\n if (existingIdx >= 0) {\n const updated = [...messages];\n updated[existingIdx] = partialMessage;\n return updated;\n }\n return [...messages, partialMessage];\n }\n}\n","/**\n * Broadcast stream state machine.\n *\n * Manages the lifecycle of a StreamAccumulator for broadcast/resume\n * streams — the path where this client is *observing* a stream owned\n * by another tab or resumed after reconnect, rather than the transport-\n * owned path that feeds directly into useChat.\n *\n * The transition function is pure (no React, no WebSocket, no side\n * effects). Callers dispatch events and apply the returned state +\n * messagesUpdate. Side effects (sending ACKs, calling onData) stay\n * in the caller.\n */\n\nimport type { UIMessage } from \"ai\";\nimport { StreamAccumulator } from \"./stream-accumulator\";\nimport type { StreamChunkData } from \"./message-builder\";\n\n// ── State ──────────────────────────────────────────────────────────\n\nexport type BroadcastStreamState =\n | { status: \"idle\" }\n | {\n status: \"observing\";\n streamId: string;\n accumulator: StreamAccumulator;\n };\n\n// ── Events ─────────────────────────────────────────────────────────\n\nexport type BroadcastStreamEvent =\n | {\n type: \"response\";\n streamId: string;\n /** Fallback message ID for a new accumulator (ignored if one exists for this stream). */\n messageId: string;\n chunkData?: unknown;\n done?: boolean;\n error?: boolean;\n replay?: boolean;\n replayComplete?: boolean;\n continuation?: boolean;\n /** Required when continuation=true so the accumulator can pick up existing parts. */\n currentMessages?: UIMessage[];\n }\n | {\n type: \"resume-fallback\";\n streamId: string;\n messageId: string;\n }\n | { type: \"clear\" };\n\n// ── Result ─────────────────────────────────────────────────────────\n\nexport interface TransitionResult {\n state: BroadcastStreamState;\n messagesUpdate?: (prev: UIMessage[]) => UIMessage[];\n isStreaming: boolean;\n}\n\n// ── Transition ─────────────────────────────────────────────────────\n\nexport function transition(\n state: BroadcastStreamState,\n event: BroadcastStreamEvent\n): TransitionResult {\n switch (event.type) {\n case \"clear\":\n return { state: { status: \"idle\" }, isStreaming: false };\n\n case \"resume-fallback\": {\n const accumulator = new StreamAccumulator({\n messageId: event.messageId\n });\n return {\n state: {\n status: \"observing\",\n streamId: event.streamId,\n accumulator\n },\n isStreaming: true\n };\n }\n\n case \"response\": {\n let accumulator: StreamAccumulator;\n\n // A replayed `start` chunk means the server is re-sending the stream\n // buffer from chunk 0 (resume replay). Re-initialize the accumulator\n // instead of appending into an existing one: replaying into an\n // accumulator that already holds this stream's parts would duplicate\n // them (a second `text-start` unconditionally opens a second text\n // part — #1733). Re-initializing makes replay idempotent under any\n // number of replays, including a second replay triggered by a\n // duplicate STREAM_RESUMING → ACK cycle or a reconnect.\n const isReplayedStart =\n event.replay === true &&\n (event.chunkData as { type?: string } | null | undefined)?.type ===\n \"start\";\n\n if (\n state.status === \"idle\" ||\n state.streamId !== event.streamId ||\n isReplayedStart\n ) {\n let messageId = event.messageId;\n let existingParts: UIMessage[\"parts\"] | undefined;\n let existingMetadata: Record<string, unknown> | undefined;\n\n if (event.continuation && event.currentMessages) {\n for (let i = event.currentMessages.length - 1; i >= 0; i--) {\n if (event.currentMessages[i].role === \"assistant\") {\n messageId = event.currentMessages[i].id;\n existingParts = [...event.currentMessages[i].parts];\n if (event.currentMessages[i].metadata != null) {\n existingMetadata = {\n ...(event.currentMessages[i].metadata as Record<\n string,\n unknown\n >)\n };\n }\n break;\n }\n }\n }\n\n accumulator = new StreamAccumulator({\n messageId,\n continuation: event.continuation,\n existingParts,\n existingMetadata\n });\n } else {\n accumulator = state.accumulator;\n }\n\n if (event.chunkData) {\n accumulator.applyChunk(event.chunkData as StreamChunkData);\n }\n\n let messagesUpdate: ((prev: UIMessage[]) => UIMessage[]) | undefined;\n\n if (event.done) {\n messagesUpdate = (prev) => accumulator.mergeInto(prev);\n return {\n state: { status: \"idle\" },\n messagesUpdate,\n isStreaming: false\n };\n }\n\n if (event.chunkData && !event.replay) {\n messagesUpdate = (prev) => accumulator.mergeInto(prev);\n } else if (event.replayComplete) {\n messagesUpdate = (prev) => accumulator.mergeInto(prev);\n }\n\n return {\n state: {\n status: \"observing\",\n streamId: event.streamId,\n accumulator\n },\n messagesUpdate,\n isStreaming: true\n };\n }\n }\n}\n","/**\n * Wire protocol message type constants for the cf_agent_chat_* protocol.\n *\n * These are the string values used on the wire between agent servers and\n * clients. Both @cloudflare/ai-chat (via its MessageType enum) and\n * @cloudflare/think use these values.\n */\nexport const STREAM_RESUME_NONE_REASONS = {\n /** No active, pending, or terminal stream exists for this agent. */\n IDLE: \"idle\",\n /** An active tool continuation is owned by another live connection. */\n CONTINUATION_OWNED: \"continuation-owned\"\n} as const;\n\nexport type StreamResumeNoneReason =\n (typeof STREAM_RESUME_NONE_REASONS)[keyof typeof STREAM_RESUME_NONE_REASONS];\n\nexport const CHAT_MESSAGE_TYPES = {\n CHAT_MESSAGES: \"cf_agent_chat_messages\",\n USE_CHAT_REQUEST: \"cf_agent_use_chat_request\",\n USE_CHAT_RESPONSE: \"cf_agent_use_chat_response\",\n CHAT_CLEAR: \"cf_agent_chat_clear\",\n CHAT_REQUEST_CANCEL: \"cf_agent_chat_request_cancel\",\n STREAM_RESUMING: \"cf_agent_stream_resuming\",\n STREAM_RESUME_ACK: \"cf_agent_stream_resume_ack\",\n STREAM_RESUME_REQUEST: \"cf_agent_stream_resume_request\",\n STREAM_RESUME_NONE: \"cf_agent_stream_resume_none\",\n // Server→client: a turn has been accepted but its resumable stream has not\n // started yet (queued, debouncing, waiting on MCP setup, or running async\n // work in `onChatMessage`). Sent in response to a resume request (or on\n // connect) so a reconnecting/re-mounting client keeps its expectation instead\n // of resolving its resume probe to \"no stream\" and then false-timing-out the\n // turn. Resolved by a later `STREAM_RESUMING` (stream started) or\n // `STREAM_RESUME_NONE` (turn settled without streaming). Backward-compatible —\n // clients that don't understand it ignore it. See issue #1784.\n STREAM_PENDING: \"cf_agent_stream_pending\",\n TOOL_RESULT: \"cf_agent_tool_result\",\n TOOL_APPROVAL: \"cf_agent_tool_approval\",\n MESSAGE_UPDATED: \"cf_agent_message_updated\",\n // Server→client: a durable chat turn is being recovered (interrupted by a\n // deploy/eviction or a stream-stall watchdog abort and now resuming). Sent\n // when a recovery continuation is scheduled and cleared on every terminal\n // outcome; `@cloudflare/think` also replays it on connect so a client that\n // joins mid-recovery learns it. Purely a progress hint — backward-compatible\n // (clients that don't understand it ignore it). See issue #1620.\n CHAT_RECOVERING: \"cf_agent_chat_recovering\"\n} as const;\n","import type { JSONSchema7, UIMessage } from \"ai\";\nimport type { StreamResumeNoneReason } from \"./protocol\";\n\n/**\n * Enum for message types to improve type safety and maintainability\n */\nexport enum MessageType {\n CF_AGENT_CHAT_MESSAGES = \"cf_agent_chat_messages\",\n CF_AGENT_USE_CHAT_REQUEST = \"cf_agent_use_chat_request\",\n CF_AGENT_USE_CHAT_RESPONSE = \"cf_agent_use_chat_response\",\n CF_AGENT_CHAT_CLEAR = \"cf_agent_chat_clear\",\n CF_AGENT_CHAT_REQUEST_CANCEL = \"cf_agent_chat_request_cancel\",\n\n /** Sent by server when client connects and there's an active stream to resume */\n CF_AGENT_STREAM_RESUMING = \"cf_agent_stream_resuming\",\n /** Sent by client to acknowledge stream resuming notification and request chunks */\n CF_AGENT_STREAM_RESUME_ACK = \"cf_agent_stream_resume_ack\",\n /** Sent by client after message handler is ready, requesting stream resume check */\n CF_AGENT_STREAM_RESUME_REQUEST = \"cf_agent_stream_resume_request\",\n /** Sent by server when client requests resume but no active stream exists */\n CF_AGENT_STREAM_RESUME_NONE = \"cf_agent_stream_resume_none\",\n /**\n * Sent by server when a turn is accepted but its resumable stream has not\n * started yet (queued / debouncing / waiting on MCP / async setup). Tells a\n * reconnecting client to keep waiting rather than resolve its resume probe to\n * \"no stream\". Resolved by a later `CF_AGENT_STREAM_RESUMING` (stream started)\n * or `CF_AGENT_STREAM_RESUME_NONE` (settled without streaming). See #1784.\n */\n CF_AGENT_STREAM_PENDING = \"cf_agent_stream_pending\",\n\n /** Client sends tool result to server (for client-side tools) */\n CF_AGENT_TOOL_RESULT = \"cf_agent_tool_result\",\n /** Server notifies client that a message was updated (e.g., tool result applied) */\n CF_AGENT_MESSAGE_UPDATED = \"cf_agent_message_updated\",\n /** Client sends tool approval response to server (for tools with needsApproval) */\n CF_AGENT_TOOL_APPROVAL = \"cf_agent_tool_approval\",\n\n /**\n * Server→client progress hint: a durable chat turn is being recovered\n * (interrupted by a deploy/eviction or a stream-stall watchdog abort and now\n * resuming). Sent when a recovery continuation is scheduled and cleared on\n * every terminal outcome. (`@cloudflare/think` also replays it on connect;\n * `@cloudflare/ai-chat` broadcasts the live signal only — see #1645.)\n * Backward-compatible — clients that don't understand it ignore it. See #1620.\n */\n CF_AGENT_CHAT_RECOVERING = \"cf_agent_chat_recovering\"\n}\n\n/**\n * Types of messages sent from the Agent to clients\n */\nexport type OutgoingMessage<ChatMessage extends UIMessage = UIMessage> =\n | {\n /** Indicates this message is a command to clear chat history */\n type: MessageType.CF_AGENT_CHAT_CLEAR;\n }\n | {\n /** Indicates this message contains updated chat messages */\n type: MessageType.CF_AGENT_CHAT_MESSAGES;\n /** Array of chat messages */\n messages: readonly ChatMessage[];\n }\n | {\n /** Indicates this message is a response to a chat request */\n type: MessageType.CF_AGENT_USE_CHAT_RESPONSE;\n /** Unique ID of the request this response corresponds to */\n id: string;\n /** Content body of the response */\n body: string;\n /** Whether this is the final chunk of the response */\n done: boolean;\n /** Whether this response contains an error */\n error?: boolean;\n /** Whether this is a continuation (append to last assistant message) */\n continuation?: boolean;\n /** Whether this chunk is being replayed from storage (stream resumption) */\n replay?: boolean;\n /** Signals that replay of stored chunks is complete (stream is still active) */\n replayComplete?: boolean;\n }\n | {\n /** Indicates the server is resuming an active stream */\n type: MessageType.CF_AGENT_STREAM_RESUMING;\n /** The request ID of the stream being resumed */\n id: string;\n /** Present when this offer directly answers a client resume probe. */\n probeId?: string;\n }\n | {\n /** Server notifies client that a message was updated (e.g., tool result applied) */\n type: MessageType.CF_AGENT_MESSAGE_UPDATED;\n /** The updated message */\n message: ChatMessage;\n }\n | {\n /** Server responds to a resume request with no stream for this client. */\n type: MessageType.CF_AGENT_STREAM_RESUME_NONE;\n /**\n * Why no stream was offered. Only `idle` proves global inactivity;\n * omitted by older servers and by non-authoritative delayed releases.\n */\n reason?: StreamResumeNoneReason;\n /** Correlates an authoritative response to its client resume probe. */\n probeId?: string;\n }\n | {\n /**\n * Server signals an accepted turn whose resumable stream has not started\n * yet — the client should keep waiting for `STREAM_RESUMING` (or a later\n * `STREAM_RESUME_NONE`) rather than give up. See #1784.\n */\n type: MessageType.CF_AGENT_STREAM_PENDING;\n /** The accepted request id, when known. */\n id?: string;\n /** Correlates a direct keep-waiting response to its client probe. */\n probeId?: string;\n }\n | {\n /**\n * Progress hint: a durable chat turn is being recovered (`recovering:\n * true`) or recovery has resolved (`recovering: false`). Purely advisory;\n * a client renders a \"recovering…\" indicator while true.\n */\n type: MessageType.CF_AGENT_CHAT_RECOVERING;\n /** Whether recovery is in progress (true) or has resolved (false). */\n recovering: boolean;\n /** The recovery-root request id of the turn being recovered, if known. */\n id?: string;\n };\n\n/**\n * Types of messages sent from clients to the Agent\n */\nexport type IncomingMessage<ChatMessage extends UIMessage = UIMessage> =\n | {\n /** Indicates this message is a command to clear chat history */\n type: MessageType.CF_AGENT_CHAT_CLEAR;\n }\n | {\n /** Indicates this message is a request to the chat API */\n type: MessageType.CF_AGENT_USE_CHAT_REQUEST;\n /** Unique ID for this request */\n id: string;\n /** Request initialization options */\n init: Pick<\n RequestInit,\n | \"method\"\n | \"keepalive\"\n | \"headers\"\n | \"body\"\n | \"redirect\"\n | \"integrity\"\n | \"credentials\"\n | \"mode\"\n | \"referrer\"\n | \"referrerPolicy\"\n | \"window\"\n >;\n }\n | {\n /** Indicates this message contains updated chat messages */\n type: MessageType.CF_AGENT_CHAT_MESSAGES;\n /** Array of chat messages */\n messages: ChatMessage[];\n }\n | {\n /** Indicates the user wants to stop generation of this message */\n type: MessageType.CF_AGENT_CHAT_REQUEST_CANCEL;\n id: string;\n }\n | {\n /** Client acknowledges stream resuming notification and is ready to receive chunks */\n type: MessageType.CF_AGENT_STREAM_RESUME_ACK;\n /** The request ID of the stream being resumed */\n id: string;\n }\n | {\n /** Client requests stream resume check after message handler is registered */\n type: MessageType.CF_AGENT_STREAM_RESUME_REQUEST;\n /** Opaque correlation id echoed by direct server responses. */\n probeId?: string;\n }\n | {\n /** Client sends tool result to server (for client-side tools) */\n type: MessageType.CF_AGENT_TOOL_RESULT;\n /** The tool call ID this result is for */\n toolCallId: string;\n /** The name of the tool */\n toolName: string;\n /** The output from the tool execution */\n output: unknown;\n /** Override the tool part state (e.g. \"output-error\" for custom denial) */\n state?: \"output-available\" | \"output-error\";\n /** Error message when state is \"output-error\" */\n errorText?: string;\n /** Whether server should auto-continue the conversation after applying result */\n autoContinue?: boolean;\n /** Client tool schemas for continuation (client is source of truth) */\n clientTools?: Array<{\n name: string;\n description?: string;\n parameters?: JSONSchema7;\n }>;\n }\n | {\n /** Client sends tool approval response to server (for tools with needsApproval) */\n type: MessageType.CF_AGENT_TOOL_APPROVAL;\n /** The tool call ID this approval is for */\n toolCallId: string;\n /** Whether the tool execution was approved */\n approved: boolean;\n /** Whether server should auto-continue the conversation after applying approval */\n autoContinue?: boolean;\n };\n"],"mappings":"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"}
|
package/dist/workflows.d.ts
CHANGED
package/docs/agent-class.md
CHANGED
|
@@ -183,7 +183,15 @@ There's also `this.broadcast` that sends a WS message to all connected clients (
|
|
|
183
183
|
|
|
184
184
|
### `this.name`
|
|
185
185
|
|
|
186
|
-
|
|
186
|
+
Since [2026-03-15](https://developers.cloudflare.com/changelog/post/2026-03-15-durable-object-id-name/), the Workers runtime populates `ctx.id.name` inside a Durable Object addressed via `idFromName()` or `getByName()`, including in alarm handlers. Constructor-time availability isn't spelled out in the docs, but workerd's own tests pin it ([workerd#6421](https://github.com/cloudflare/workerd/pull/6421)), as do `partyserver`'s runtime-contract tests. `partyserver` reads `ctx.id.name` first, so for named access `this.name` resolves natively with no extra machinery.
|
|
187
|
+
|
|
188
|
+
`ctx.id.name` is still `undefined` in these cases (see [the DO id docs](https://developers.cloudflare.com/durable-objects/api/id/#name)):
|
|
189
|
+
|
|
190
|
+
- the object is addressed via `idFromString()` (even if the id was originally created with `idFromName()`) or `newUniqueId()` — deliberate design, not a gap;
|
|
191
|
+
- the name is longer than 1,024 bytes;
|
|
192
|
+
- the alarm firing was scheduled before 2026-03-15, or was scheduled from a context that itself had no name (reschedule it from a `fetch()` or RPC handler where the name is available).
|
|
193
|
+
|
|
194
|
+
For those cases `partyserver` falls back to a legacy name record in storage (written automatically during named-access initialization, or by the `setName()` bootstrap for raw-id DOs), and `this.name` throws if no name can be resolved at all.
|
|
187
195
|
|
|
188
196
|
## Layer 2: Agent
|
|
189
197
|
|
package/docs/configuration.md
CHANGED
|
@@ -226,6 +226,26 @@ export default defineConfig({
|
|
|
226
226
|
|
|
227
227
|
The `agents()` plugin is safe to include even if your project does not use decorators. It only runs the transform on files that contain `@` syntax.
|
|
228
228
|
|
|
229
|
+
### Turndown Stub
|
|
230
|
+
|
|
231
|
+
By default, the `agents()` Vite plugin also replaces imports of the `turndown`
|
|
232
|
+
package with a Worker-safe stub. This prevents `just-bash`, used by Think's
|
|
233
|
+
workspace `bash` tool and skill scripts, from pulling `turndown`'s Node DOM
|
|
234
|
+
fallback into the Worker bundle. That fallback expects Node's global `require()`,
|
|
235
|
+
which is not available in Workers.
|
|
236
|
+
|
|
237
|
+
If your app imports `turndown` directly for HTML-to-Markdown conversion, disable
|
|
238
|
+
the stub so your import resolves to the real package:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
export default defineConfig({
|
|
242
|
+
plugins: [agents({ stubTurndown: false }), react(), cloudflare()]
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
When the stub is enabled and app code calls `turndown()`, the stub throws an
|
|
247
|
+
error that points back to this option.
|
|
248
|
+
|
|
229
249
|
The starter template and all examples include this plugin by default.
|
|
230
250
|
|
|
231
251
|
## Generating Types
|
package/docs/mcp-client.md
CHANGED
|
@@ -111,6 +111,45 @@ await this.addMcpServer("github", "https://mcp.github.com/mcp", {
|
|
|
111
111
|
|
|
112
112
|
These options are persisted and used when reconnecting after hibernation or after OAuth completion. Default: 3 attempts, 500ms base delay, 5s max delay. See [Retries](./retries.md) for more details.
|
|
113
113
|
|
|
114
|
+
### Elicitation
|
|
115
|
+
|
|
116
|
+
MCP servers can request input from the client during a tool call (`elicitation/create`). To respond, configure an elicitation handler before MCP connections are registered or restored:
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
import { Agent } from "agents";
|
|
120
|
+
|
|
121
|
+
class MyAgent extends Agent<Env> {
|
|
122
|
+
onStart() {
|
|
123
|
+
this.mcp.configureElicitationHandlers({
|
|
124
|
+
url: async (request, serverId) => {
|
|
125
|
+
// Deliver a url-mode elicitation link out-of-band
|
|
126
|
+
return { action: "accept" as const, content: {} };
|
|
127
|
+
},
|
|
128
|
+
form: async (request, serverId) => {
|
|
129
|
+
// Collect values matching request.params.requestedSchema
|
|
130
|
+
return { action: "accept" as const, content: {} };
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The advertised modes are persisted with each MCP server, so a connection restored from storage after hibernation re-advertises the same modes at the handshake; the handlers themselves re-attach when `onStart()` runs. Configuring a handler after an MCP connection is already active updates the in-memory handler, but the server only sees new advertised elicitation modes after that connection reconnects.
|
|
138
|
+
|
|
139
|
+
Connections advertise only the elicitation modes with configured handlers at the `initialize` handshake: configure `form` to advertise form-mode elicitation, `url` to advertise url-mode elicitation (MCP spec 2025-11-25 — url mode is used for sensitive flows like OAuth URLs), or both to advertise both modes. Without handlers, connections advertise no elicitation capability, so spec-compliant servers use their non-elicitation fallbacks instead of sending requests the agent cannot answer.
|
|
140
|
+
|
|
141
|
+
To override the advertised modes, declare them explicitly — an explicit declaration always wins and is persisted with the server options, surviving hibernation:
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
await this.addMcpServer("portal", "https://portal.example.com/mcp", {
|
|
145
|
+
client: {
|
|
146
|
+
capabilities: {
|
|
147
|
+
elicitation: { form: {} } // form-mode only, even with both handlers configured
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
114
153
|
### URL Security
|
|
115
154
|
|
|
116
155
|
MCP server URLs are validated before connection to prevent Server-Side Request Forgery (SSRF). The following URL targets are blocked:
|
|
@@ -266,17 +305,20 @@ Once connected, access the server's capabilities:
|
|
|
266
305
|
|
|
267
306
|
### Getting Available Tools
|
|
268
307
|
|
|
308
|
+
Use `listTools()` when you need to discover or inspect the raw MCP catalog without preparing tools for an AI SDK model call:
|
|
309
|
+
|
|
269
310
|
```typescript
|
|
270
|
-
const
|
|
311
|
+
const tools = this.mcp.listTools();
|
|
271
312
|
|
|
272
|
-
|
|
273
|
-
for (const tool of state.tools) {
|
|
313
|
+
for (const tool of tools) {
|
|
274
314
|
console.log(`Tool: ${tool.name}`);
|
|
275
315
|
console.log(` From server: ${tool.serverId}`);
|
|
276
316
|
console.log(` Description: ${tool.description}`);
|
|
277
317
|
}
|
|
278
318
|
```
|
|
279
319
|
|
|
320
|
+
`getMcpServers().tools` exposes the same raw tool shape as part of the full client state sent to connected applications. Neither API converts JSON Schemas to Zod.
|
|
321
|
+
|
|
280
322
|
### Resources and Prompts
|
|
281
323
|
|
|
282
324
|
```typescript
|
|
@@ -322,7 +364,13 @@ async function chat(prompt: string) {
|
|
|
322
364
|
}
|
|
323
365
|
```
|
|
324
366
|
|
|
325
|
-
> **Note:** `getMcpServers().tools`
|
|
367
|
+
> **Note:** Use `this.mcp.listTools()` or `getMcpServers().tools` for discovery and inspection. Call `this.mcp.getAITools()` only when preparing tools for the AI SDK because it converts each MCP input and output JSON Schema to Zod.
|
|
368
|
+
|
|
369
|
+
### AI tool schema conversion lifetime
|
|
370
|
+
|
|
371
|
+
`getAITools()` reuses converted schemas while a live connection keeps the same current catalog. Repeated filtered or unfiltered calls still return fresh tool records and execute closures. Connections excluded by a filter are not converted.
|
|
372
|
+
|
|
373
|
+
The next call converts schemas again after discovery replaces a catalog or the live connection changes. If a custom integration mutates a schema, assign a new `inputSchema` or `outputSchema` object, or replace the catalog array, so `getAITools()` detects the change.
|
|
326
374
|
|
|
327
375
|
## Managing Servers
|
|
328
376
|
|