@cloudflare/codemode 0.0.8 → 0.1.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.
@@ -0,0 +1,392 @@
1
+ import { createAuxiliaryTypeStore, createTypeAlias, printNode, zodToTs } from "zod-to-ts";
2
+
3
+ //#region src/types.ts
4
+ const JS_RESERVED = new Set([
5
+ "abstract",
6
+ "arguments",
7
+ "await",
8
+ "boolean",
9
+ "break",
10
+ "byte",
11
+ "case",
12
+ "catch",
13
+ "char",
14
+ "class",
15
+ "const",
16
+ "continue",
17
+ "debugger",
18
+ "default",
19
+ "delete",
20
+ "do",
21
+ "double",
22
+ "else",
23
+ "enum",
24
+ "eval",
25
+ "export",
26
+ "extends",
27
+ "false",
28
+ "final",
29
+ "finally",
30
+ "float",
31
+ "for",
32
+ "function",
33
+ "goto",
34
+ "if",
35
+ "implements",
36
+ "import",
37
+ "in",
38
+ "instanceof",
39
+ "int",
40
+ "interface",
41
+ "let",
42
+ "long",
43
+ "native",
44
+ "new",
45
+ "null",
46
+ "package",
47
+ "private",
48
+ "protected",
49
+ "public",
50
+ "return",
51
+ "short",
52
+ "static",
53
+ "super",
54
+ "switch",
55
+ "synchronized",
56
+ "this",
57
+ "throw",
58
+ "throws",
59
+ "transient",
60
+ "true",
61
+ "try",
62
+ "typeof",
63
+ "undefined",
64
+ "var",
65
+ "void",
66
+ "volatile",
67
+ "while",
68
+ "with",
69
+ "yield"
70
+ ]);
71
+ /**
72
+ * Sanitize a tool name into a valid JavaScript identifier.
73
+ * Replaces hyphens, dots, and spaces with `_`, strips other invalid chars,
74
+ * prefixes digit-leading names with `_`, and appends `_` to JS reserved words.
75
+ */
76
+ function sanitizeToolName(name) {
77
+ if (!name) return "_";
78
+ let sanitized = name.replace(/[-.\s]/g, "_");
79
+ sanitized = sanitized.replace(/[^a-zA-Z0-9_$]/g, "");
80
+ if (!sanitized) return "_";
81
+ if (/^[0-9]/.test(sanitized)) sanitized = "_" + sanitized;
82
+ if (JS_RESERVED.has(sanitized)) sanitized = sanitized + "_";
83
+ return sanitized;
84
+ }
85
+ function toCamelCase(str) {
86
+ return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()).replace(/^[a-z]/, (letter) => letter.toUpperCase());
87
+ }
88
+ /**
89
+ * Extract field descriptions from a schema and format as @param lines.
90
+ * Returns an array of `@param input.fieldName - description` lines.
91
+ */
92
+ function extractParamDescriptions(schema) {
93
+ const descriptions = extractDescriptions(schema);
94
+ return Object.entries(descriptions).map(([fieldName, desc]) => `@param input.${fieldName} - ${desc}`);
95
+ }
96
+ /**
97
+ * Check if a value is a Zod schema (has _zod property).
98
+ */
99
+ function isZodSchema(value) {
100
+ return value !== null && typeof value === "object" && "_zod" in value && value._zod !== void 0;
101
+ }
102
+ /**
103
+ * Check if a value is an AI SDK jsonSchema wrapper.
104
+ * The jsonSchema wrapper has a [Symbol] with jsonSchema property.
105
+ */
106
+ function isJsonSchemaWrapper(value) {
107
+ if (value === null || typeof value !== "object") return false;
108
+ if ("jsonSchema" in value) return true;
109
+ const symbols = Object.getOwnPropertySymbols(value);
110
+ for (const sym of symbols) {
111
+ const symValue = value[sym];
112
+ if (symValue && typeof symValue === "object" && "jsonSchema" in symValue) return true;
113
+ }
114
+ return false;
115
+ }
116
+ /**
117
+ * Extract JSON schema from an AI SDK jsonSchema wrapper.
118
+ */
119
+ function extractJsonSchema(wrapper) {
120
+ if (wrapper === null || typeof wrapper !== "object") return null;
121
+ if ("jsonSchema" in wrapper) return wrapper.jsonSchema;
122
+ const symbols = Object.getOwnPropertySymbols(wrapper);
123
+ for (const sym of symbols) {
124
+ const symValue = wrapper[sym];
125
+ if (symValue && typeof symValue === "object" && "jsonSchema" in symValue) return symValue.jsonSchema;
126
+ }
127
+ return null;
128
+ }
129
+ /**
130
+ * Check if a property name needs quoting in TypeScript.
131
+ */
132
+ function needsQuotes(name) {
133
+ return !/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
134
+ }
135
+ /**
136
+ * Escape a character as a unicode escape sequence if it is a control character.
137
+ */
138
+ function escapeControlChar(ch) {
139
+ const code = ch.charCodeAt(0);
140
+ if (code <= 31 || code === 127) return "\\u" + code.toString(16).padStart(4, "0");
141
+ return ch;
142
+ }
143
+ /**
144
+ * Quote a property name if needed.
145
+ * Escapes backslashes, quotes, and control characters.
146
+ */
147
+ function quoteProp(name) {
148
+ if (needsQuotes(name)) {
149
+ let escaped = "";
150
+ for (const ch of name) if (ch === "\\") escaped += "\\\\";
151
+ else if (ch === "\"") escaped += "\\\"";
152
+ else if (ch === "\n") escaped += "\\n";
153
+ else if (ch === "\r") escaped += "\\r";
154
+ else if (ch === " ") escaped += "\\t";
155
+ else if (ch === "\u2028") escaped += "\\u2028";
156
+ else if (ch === "\u2029") escaped += "\\u2029";
157
+ else escaped += escapeControlChar(ch);
158
+ return `"${escaped}"`;
159
+ }
160
+ return name;
161
+ }
162
+ /**
163
+ * Escape a string for use inside a double-quoted TypeScript string literal.
164
+ * Handles backslashes, quotes, newlines, control characters, and line/paragraph separators.
165
+ */
166
+ function escapeStringLiteral(s) {
167
+ let out = "";
168
+ for (const ch of s) if (ch === "\\") out += "\\\\";
169
+ else if (ch === "\"") out += "\\\"";
170
+ else if (ch === "\n") out += "\\n";
171
+ else if (ch === "\r") out += "\\r";
172
+ else if (ch === " ") out += "\\t";
173
+ else if (ch === "\u2028") out += "\\u2028";
174
+ else if (ch === "\u2029") out += "\\u2029";
175
+ else out += escapeControlChar(ch);
176
+ return out;
177
+ }
178
+ /**
179
+ * Escape a string for use inside a JSDoc comment.
180
+ * Prevents premature comment closure from star-slash sequences.
181
+ */
182
+ function escapeJsDoc(text) {
183
+ return text.replace(/\*\//g, "*\\/");
184
+ }
185
+ /**
186
+ * Resolve an internal JSON Pointer $ref (e.g. #/definitions/Foo) against the root schema.
187
+ * Returns null for external URLs or unresolvable paths.
188
+ */
189
+ function resolveRef(ref, root) {
190
+ if (ref === "#") return root;
191
+ if (!ref.startsWith("#/")) return null;
192
+ const segments = ref.slice(2).split("/").map((s) => s.replace(/~1/g, "/").replace(/~0/g, "~"));
193
+ let current = root;
194
+ for (const seg of segments) {
195
+ if (current === null || typeof current !== "object") return null;
196
+ current = current[seg];
197
+ if (current === void 0) return null;
198
+ }
199
+ if (typeof current === "boolean") return current;
200
+ if (current === null || typeof current !== "object") return null;
201
+ return current;
202
+ }
203
+ /**
204
+ * Convert a JSON Schema to a TypeScript type string.
205
+ * This is a direct conversion without going through Zod.
206
+ */
207
+ function jsonSchemaToTypeString(schema, indent, ctx) {
208
+ if (typeof schema === "boolean") return schema ? "unknown" : "never";
209
+ if (ctx.depth >= ctx.maxDepth) return "unknown";
210
+ if (ctx.seen.has(schema)) return "unknown";
211
+ const nextCtx = {
212
+ ...ctx,
213
+ depth: ctx.depth + 1,
214
+ seen: new Set([...ctx.seen, schema])
215
+ };
216
+ if (schema.$ref) {
217
+ const resolved = resolveRef(schema.$ref, ctx.root);
218
+ if (!resolved) return "unknown";
219
+ return applyNullable(jsonSchemaToTypeString(resolved, indent, nextCtx), schema);
220
+ }
221
+ if (schema.anyOf) return applyNullable(schema.anyOf.map((s) => jsonSchemaToTypeString(s, indent, nextCtx)).join(" | "), schema);
222
+ if (schema.oneOf) return applyNullable(schema.oneOf.map((s) => jsonSchemaToTypeString(s, indent, nextCtx)).join(" | "), schema);
223
+ if (schema.allOf) return applyNullable(schema.allOf.map((s) => jsonSchemaToTypeString(s, indent, nextCtx)).join(" & "), schema);
224
+ if (schema.enum) {
225
+ if (schema.enum.length === 0) return "never";
226
+ return applyNullable(schema.enum.map((v) => {
227
+ if (v === null) return "null";
228
+ if (typeof v === "string") return "\"" + escapeStringLiteral(v) + "\"";
229
+ if (typeof v === "object") return JSON.stringify(v) ?? "unknown";
230
+ return String(v);
231
+ }).join(" | "), schema);
232
+ }
233
+ if (schema.const !== void 0) return applyNullable(schema.const === null ? "null" : typeof schema.const === "string" ? "\"" + escapeStringLiteral(schema.const) + "\"" : typeof schema.const === "object" ? JSON.stringify(schema.const) ?? "unknown" : String(schema.const), schema);
234
+ const type = schema.type;
235
+ if (type === "string") return applyNullable("string", schema);
236
+ if (type === "number" || type === "integer") return applyNullable("number", schema);
237
+ if (type === "boolean") return applyNullable("boolean", schema);
238
+ if (type === "null") return "null";
239
+ if (type === "array") {
240
+ const prefixItems = schema.prefixItems;
241
+ if (Array.isArray(prefixItems)) return applyNullable(`[${prefixItems.map((s) => jsonSchemaToTypeString(s, indent, nextCtx)).join(", ")}]`, schema);
242
+ if (Array.isArray(schema.items)) return applyNullable(`[${schema.items.map((s) => jsonSchemaToTypeString(s, indent, nextCtx)).join(", ")}]`, schema);
243
+ if (schema.items) return applyNullable(`${jsonSchemaToTypeString(schema.items, indent, nextCtx)}[]`, schema);
244
+ return applyNullable("unknown[]", schema);
245
+ }
246
+ if (type === "object" || schema.properties) {
247
+ const props = schema.properties || {};
248
+ const required = new Set(schema.required || []);
249
+ const lines = [];
250
+ for (const [propName, propSchema] of Object.entries(props)) {
251
+ if (typeof propSchema === "boolean") {
252
+ const boolType = propSchema ? "unknown" : "never";
253
+ const optionalMark = required.has(propName) ? "" : "?";
254
+ lines.push(`${indent} ${quoteProp(propName)}${optionalMark}: ${boolType};`);
255
+ continue;
256
+ }
257
+ const isRequired = required.has(propName);
258
+ const propType = jsonSchemaToTypeString(propSchema, indent + " ", nextCtx);
259
+ const desc = propSchema.description;
260
+ const format = propSchema.format;
261
+ if (desc || format) {
262
+ const descText = desc ? escapeJsDoc(desc.replace(/\r?\n/g, " ")) : void 0;
263
+ const formatTag = format ? `@format ${escapeJsDoc(format)}` : void 0;
264
+ if (descText && formatTag) {
265
+ lines.push(`${indent} /**`);
266
+ lines.push(`${indent} * ${descText}`);
267
+ lines.push(`${indent} * ${formatTag}`);
268
+ lines.push(`${indent} */`);
269
+ } else lines.push(`${indent} /** ${descText ?? formatTag} */`);
270
+ }
271
+ const quotedName = quoteProp(propName);
272
+ const optionalMark = isRequired ? "" : "?";
273
+ lines.push(`${indent} ${quotedName}${optionalMark}: ${propType};`);
274
+ }
275
+ if (schema.additionalProperties) {
276
+ const valueType = schema.additionalProperties === true ? "unknown" : jsonSchemaToTypeString(schema.additionalProperties, indent + " ", nextCtx);
277
+ lines.push(`${indent} [key: string]: ${valueType};`);
278
+ }
279
+ if (lines.length === 0) {
280
+ if (schema.additionalProperties === false) return applyNullable("{}", schema);
281
+ return applyNullable("Record<string, unknown>", schema);
282
+ }
283
+ return applyNullable(`{\n${lines.join("\n")}\n${indent}}`, schema);
284
+ }
285
+ if (Array.isArray(type)) return applyNullable(type.map((t) => {
286
+ if (t === "string") return "string";
287
+ if (t === "number" || t === "integer") return "number";
288
+ if (t === "boolean") return "boolean";
289
+ if (t === "null") return "null";
290
+ if (t === "array") return "unknown[]";
291
+ if (t === "object") return "Record<string, unknown>";
292
+ return "unknown";
293
+ }).join(" | "), schema);
294
+ return "unknown";
295
+ }
296
+ /**
297
+ * Apply OpenAPI 3.0 `nullable: true` to a type result.
298
+ */
299
+ function applyNullable(result, schema) {
300
+ if (result !== "unknown" && result !== "never" && schema?.nullable === true) return `${result} | null`;
301
+ return result;
302
+ }
303
+ /**
304
+ * Extract field descriptions from a schema.
305
+ * Works with Zod schemas (via .shape) and jsonSchema wrappers (via .properties).
306
+ */
307
+ function extractDescriptions(schema) {
308
+ const descriptions = {};
309
+ const shape = schema.shape;
310
+ if (shape && typeof shape === "object") {
311
+ for (const [fieldName, fieldSchema] of Object.entries(shape)) {
312
+ const desc = fieldSchema.description;
313
+ if (desc) descriptions[fieldName] = desc;
314
+ }
315
+ return descriptions;
316
+ }
317
+ if (isJsonSchemaWrapper(schema)) {
318
+ const jsonSchema = extractJsonSchema(schema);
319
+ if (jsonSchema?.properties) {
320
+ for (const [fieldName, propSchema] of Object.entries(jsonSchema.properties)) if (propSchema && typeof propSchema === "object" && propSchema.description) descriptions[fieldName] = propSchema.description;
321
+ }
322
+ }
323
+ return descriptions;
324
+ }
325
+ /**
326
+ * Safely convert a schema to TypeScript type string.
327
+ * Handles Zod schemas and AI SDK jsonSchema wrappers.
328
+ * Returns "unknown" if the schema cannot be represented in TypeScript.
329
+ */
330
+ function safeSchemaToTs(schema, typeName, auxiliaryTypeStore) {
331
+ try {
332
+ if (isZodSchema(schema)) return printNode(createTypeAlias(zodToTs(schema, { auxiliaryTypeStore }).node, typeName));
333
+ if (isJsonSchemaWrapper(schema)) {
334
+ const jsonSchema = extractJsonSchema(schema);
335
+ if (jsonSchema) return `type ${typeName} = ${jsonSchemaToTypeString(jsonSchema, "", {
336
+ root: jsonSchema,
337
+ depth: 0,
338
+ seen: /* @__PURE__ */ new Set(),
339
+ maxDepth: 20
340
+ })}`;
341
+ }
342
+ return `type ${typeName} = unknown`;
343
+ } catch {
344
+ return `type ${typeName} = unknown`;
345
+ }
346
+ }
347
+ /**
348
+ * Generate TypeScript type definitions from tool descriptors or an AI SDK ToolSet.
349
+ * These types can be included in tool descriptions to help LLMs write correct code.
350
+ */
351
+ function generateTypes(tools) {
352
+ let availableTools = "";
353
+ let availableTypes = "";
354
+ const auxiliaryTypeStore = createAuxiliaryTypeStore();
355
+ for (const [toolName, tool] of Object.entries(tools)) {
356
+ const safeName = sanitizeToolName(toolName);
357
+ const camelName = toCamelCase(safeName);
358
+ try {
359
+ const inputSchema = "inputSchema" in tool ? tool.inputSchema : tool.parameters;
360
+ const outputSchema = "outputSchema" in tool ? tool.outputSchema : void 0;
361
+ const description = tool.description;
362
+ const inputType = safeSchemaToTs(inputSchema, `${camelName}Input`, auxiliaryTypeStore);
363
+ const outputType = outputSchema ? safeSchemaToTs(outputSchema, `${camelName}Output`, auxiliaryTypeStore) : `type ${camelName}Output = unknown`;
364
+ availableTypes += `\n${inputType.trim()}`;
365
+ availableTypes += `\n${outputType.trim()}`;
366
+ const paramDescs = inputSchema ? extractParamDescriptions(inputSchema) : [];
367
+ const jsdocLines = [];
368
+ if (description?.trim()) jsdocLines.push(escapeJsDoc(description.trim().replace(/\r?\n/g, " ")));
369
+ else jsdocLines.push(escapeJsDoc(toolName));
370
+ for (const pd of paramDescs) jsdocLines.push(escapeJsDoc(pd.replace(/\r?\n/g, " ")));
371
+ const jsdocBody = jsdocLines.map((l) => `\t * ${l}`).join("\n");
372
+ availableTools += `\n\t/**\n${jsdocBody}\n\t */`;
373
+ availableTools += `\n\t${safeName}: (input: ${camelName}Input) => Promise<${camelName}Output>;`;
374
+ availableTools += "\n";
375
+ } catch {
376
+ availableTypes += `\ntype ${camelName}Input = unknown`;
377
+ availableTypes += `\ntype ${camelName}Output = unknown`;
378
+ availableTools += `\n\t/**\n\t * ${escapeJsDoc(toolName)}\n\t */`;
379
+ availableTools += `\n\t${safeName}: (input: ${camelName}Input) => Promise<${camelName}Output>;`;
380
+ availableTools += "\n";
381
+ }
382
+ }
383
+ availableTools = `\ndeclare const codemode: {${availableTools}}`;
384
+ return `
385
+ ${availableTypes}
386
+ ${availableTools}
387
+ `.trim();
388
+ }
389
+
390
+ //#endregion
391
+ export { sanitizeToolName as n, generateTypes as t };
392
+ //# sourceMappingURL=types-CpkEgXwN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-CpkEgXwN.js","names":["printNodeZodToTs"],"sources":["../src/types.ts"],"sourcesContent":["import {\n zodToTs,\n printNode as printNodeZodToTs,\n createTypeAlias,\n createAuxiliaryTypeStore\n} from \"zod-to-ts\";\nimport type { ZodType } from \"zod\";\nimport type { ToolSet } from \"ai\";\nimport type { JSONSchema7, JSONSchema7Definition } from \"json-schema\";\n\ninterface ConversionContext {\n root: JSONSchema7;\n depth: number;\n seen: Set<unknown>;\n maxDepth: number;\n}\n\nconst JS_RESERVED = new Set([\n \"abstract\",\n \"arguments\",\n \"await\",\n \"boolean\",\n \"break\",\n \"byte\",\n \"case\",\n \"catch\",\n \"char\",\n \"class\",\n \"const\",\n \"continue\",\n \"debugger\",\n \"default\",\n \"delete\",\n \"do\",\n \"double\",\n \"else\",\n \"enum\",\n \"eval\",\n \"export\",\n \"extends\",\n \"false\",\n \"final\",\n \"finally\",\n \"float\",\n \"for\",\n \"function\",\n \"goto\",\n \"if\",\n \"implements\",\n \"import\",\n \"in\",\n \"instanceof\",\n \"int\",\n \"interface\",\n \"let\",\n \"long\",\n \"native\",\n \"new\",\n \"null\",\n \"package\",\n \"private\",\n \"protected\",\n \"public\",\n \"return\",\n \"short\",\n \"static\",\n \"super\",\n \"switch\",\n \"synchronized\",\n \"this\",\n \"throw\",\n \"throws\",\n \"transient\",\n \"true\",\n \"try\",\n \"typeof\",\n \"undefined\",\n \"var\",\n \"void\",\n \"volatile\",\n \"while\",\n \"with\",\n \"yield\"\n]);\n\n/**\n * Sanitize a tool name into a valid JavaScript identifier.\n * Replaces hyphens, dots, and spaces with `_`, strips other invalid chars,\n * prefixes digit-leading names with `_`, and appends `_` to JS reserved words.\n */\nexport function sanitizeToolName(name: string): string {\n if (!name) return \"_\";\n\n // Replace common separators with underscores\n let sanitized = name.replace(/[-.\\s]/g, \"_\");\n\n // Strip any remaining non-identifier characters\n sanitized = sanitized.replace(/[^a-zA-Z0-9_$]/g, \"\");\n\n if (!sanitized) return \"_\";\n\n // Prefix with _ if starts with a digit\n if (/^[0-9]/.test(sanitized)) {\n sanitized = \"_\" + sanitized;\n }\n\n // Append _ to reserved words\n if (JS_RESERVED.has(sanitized)) {\n sanitized = sanitized + \"_\";\n }\n\n return sanitized;\n}\n\nfunction toCamelCase(str: string) {\n return str\n .replace(/_([a-z])/g, (_, letter) => letter.toUpperCase())\n .replace(/^[a-z]/, (letter) => letter.toUpperCase());\n}\n\n/**\n * Extract field descriptions from a schema and format as @param lines.\n * Returns an array of `@param input.fieldName - description` lines.\n */\nfunction extractParamDescriptions(schema: unknown): string[] {\n const descriptions = extractDescriptions(schema);\n return Object.entries(descriptions).map(\n ([fieldName, desc]) => `@param input.${fieldName} - ${desc}`\n );\n}\n\nexport interface ToolDescriptor {\n description?: string;\n inputSchema: ZodType;\n outputSchema?: ZodType;\n execute?: (args: unknown) => Promise<unknown>;\n}\n\nexport type ToolDescriptors = Record<string, ToolDescriptor>;\n\n/**\n * Check if a value is a Zod schema (has _zod property).\n */\nfunction isZodSchema(value: unknown): value is ZodType {\n return (\n value !== null &&\n typeof value === \"object\" &&\n \"_zod\" in value &&\n (value as { _zod?: unknown })._zod !== undefined\n );\n}\n\n/**\n * Check if a value is an AI SDK jsonSchema wrapper.\n * The jsonSchema wrapper has a [Symbol] with jsonSchema property.\n */\nfunction isJsonSchemaWrapper(\n value: unknown\n): value is { jsonSchema: JSONSchema7 } {\n if (value === null || typeof value !== \"object\") return false;\n\n // AI SDK jsonSchema wrapper stores data in a symbol property\n // but also exposes jsonSchema directly in some versions\n if (\"jsonSchema\" in value) {\n return true;\n }\n\n // Check for symbol-based storage (AI SDK internal)\n const symbols = Object.getOwnPropertySymbols(value);\n for (const sym of symbols) {\n const symValue = (value as Record<symbol, unknown>)[sym];\n if (symValue && typeof symValue === \"object\" && \"jsonSchema\" in symValue) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Extract JSON schema from an AI SDK jsonSchema wrapper.\n */\nfunction extractJsonSchema(wrapper: unknown): JSONSchema7 | null {\n if (wrapper === null || typeof wrapper !== \"object\") return null;\n\n // Direct property access\n if (\"jsonSchema\" in wrapper) {\n return (wrapper as { jsonSchema: JSONSchema7 }).jsonSchema;\n }\n\n // Symbol-based storage\n const symbols = Object.getOwnPropertySymbols(wrapper);\n for (const sym of symbols) {\n const symValue = (wrapper as Record<symbol, unknown>)[sym];\n if (symValue && typeof symValue === \"object\" && \"jsonSchema\" in symValue) {\n return (symValue as { jsonSchema: JSONSchema7 }).jsonSchema;\n }\n }\n\n return null;\n}\n\n/**\n * Check if a property name needs quoting in TypeScript.\n */\nfunction needsQuotes(name: string): boolean {\n // Valid JS identifier: starts with letter, $, or _, followed by letters, digits, $, _\n return !/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);\n}\n\n/**\n * Escape a character as a unicode escape sequence if it is a control character.\n */\nfunction escapeControlChar(ch: string): string {\n const code = ch.charCodeAt(0);\n if (code <= 0x1f || code === 0x7f) {\n return \"\\\\u\" + code.toString(16).padStart(4, \"0\");\n }\n return ch;\n}\n\n/**\n * Quote a property name if needed.\n * Escapes backslashes, quotes, and control characters.\n */\nfunction quoteProp(name: string): string {\n if (needsQuotes(name)) {\n let escaped = \"\";\n for (const ch of name) {\n if (ch === \"\\\\\") escaped += \"\\\\\\\\\";\n else if (ch === '\"') escaped += '\\\\\"';\n else if (ch === \"\\n\") escaped += \"\\\\n\";\n else if (ch === \"\\r\") escaped += \"\\\\r\";\n else if (ch === \"\\t\") escaped += \"\\\\t\";\n else if (ch === \"\\u2028\") escaped += \"\\\\u2028\";\n else if (ch === \"\\u2029\") escaped += \"\\\\u2029\";\n else escaped += escapeControlChar(ch);\n }\n return `\"${escaped}\"`;\n }\n return name;\n}\n\n/**\n * Escape a string for use inside a double-quoted TypeScript string literal.\n * Handles backslashes, quotes, newlines, control characters, and line/paragraph separators.\n */\nfunction escapeStringLiteral(s: string): string {\n let out = \"\";\n for (const ch of s) {\n if (ch === \"\\\\\") out += \"\\\\\\\\\";\n else if (ch === '\"') out += '\\\\\"';\n else if (ch === \"\\n\") out += \"\\\\n\";\n else if (ch === \"\\r\") out += \"\\\\r\";\n else if (ch === \"\\t\") out += \"\\\\t\";\n else if (ch === \"\\u2028\") out += \"\\\\u2028\";\n else if (ch === \"\\u2029\") out += \"\\\\u2029\";\n else out += escapeControlChar(ch);\n }\n return out;\n}\n\n/**\n * Escape a string for use inside a JSDoc comment.\n * Prevents premature comment closure from star-slash sequences.\n */\nfunction escapeJsDoc(text: string): string {\n return text.replace(/\\*\\//g, \"*\\\\/\");\n}\n\n/**\n * Resolve an internal JSON Pointer $ref (e.g. #/definitions/Foo) against the root schema.\n * Returns null for external URLs or unresolvable paths.\n */\nfunction resolveRef(\n ref: string,\n root: JSONSchema7\n): JSONSchema7Definition | null {\n // \"#\" is a valid self-reference to the root schema\n if (ref === \"#\") return root;\n\n if (!ref.startsWith(\"#/\")) return null;\n\n const segments = ref\n .slice(2)\n .split(\"/\")\n .map((s) => s.replace(/~1/g, \"/\").replace(/~0/g, \"~\"));\n\n let current: unknown = root;\n for (const seg of segments) {\n if (current === null || typeof current !== \"object\") return null;\n current = (current as Record<string, unknown>)[seg];\n if (current === undefined) return null;\n }\n\n // Allow both object schemas and boolean schemas (true = any, false = never)\n if (typeof current === \"boolean\") return current;\n if (current === null || typeof current !== \"object\") return null;\n return current as JSONSchema7;\n}\n\n/**\n * Convert a JSON Schema to a TypeScript type string.\n * This is a direct conversion without going through Zod.\n */\nfunction jsonSchemaToTypeString(\n schema: JSONSchema7Definition,\n indent: string,\n ctx: ConversionContext\n): string {\n // Handle boolean schemas\n if (typeof schema === \"boolean\") {\n return schema ? \"unknown\" : \"never\";\n }\n\n // Depth guard\n if (ctx.depth >= ctx.maxDepth) return \"unknown\";\n\n // Circular reference guard\n if (ctx.seen.has(schema)) return \"unknown\";\n\n const nextCtx: ConversionContext = {\n ...ctx,\n depth: ctx.depth + 1,\n seen: new Set([...ctx.seen, schema])\n };\n\n // Handle $ref\n if (schema.$ref) {\n const resolved = resolveRef(schema.$ref, ctx.root);\n if (!resolved) return \"unknown\";\n return applyNullable(\n jsonSchemaToTypeString(resolved, indent, nextCtx),\n schema\n );\n }\n\n // Handle anyOf/oneOf (union types)\n if (schema.anyOf) {\n const types = schema.anyOf.map((s) =>\n jsonSchemaToTypeString(s, indent, nextCtx)\n );\n return applyNullable(types.join(\" | \"), schema);\n }\n if (schema.oneOf) {\n const types = schema.oneOf.map((s) =>\n jsonSchemaToTypeString(s, indent, nextCtx)\n );\n return applyNullable(types.join(\" | \"), schema);\n }\n\n // Handle allOf (intersection types)\n if (schema.allOf) {\n const types = schema.allOf.map((s) =>\n jsonSchemaToTypeString(s, indent, nextCtx)\n );\n return applyNullable(types.join(\" & \"), schema);\n }\n\n // Handle enum\n if (schema.enum) {\n if (schema.enum.length === 0) return \"never\";\n const result = schema.enum\n .map((v) => {\n if (v === null) return \"null\";\n if (typeof v === \"string\") return '\"' + escapeStringLiteral(v) + '\"';\n if (typeof v === \"object\") return JSON.stringify(v) ?? \"unknown\";\n return String(v);\n })\n .join(\" | \");\n return applyNullable(result, schema);\n }\n\n // Handle const\n if (schema.const !== undefined) {\n const result =\n schema.const === null\n ? \"null\"\n : typeof schema.const === \"string\"\n ? '\"' + escapeStringLiteral(schema.const) + '\"'\n : typeof schema.const === \"object\"\n ? (JSON.stringify(schema.const) ?? \"unknown\")\n : String(schema.const);\n return applyNullable(result, schema);\n }\n\n // Handle type\n const type = schema.type;\n\n if (type === \"string\") return applyNullable(\"string\", schema);\n if (type === \"number\" || type === \"integer\")\n return applyNullable(\"number\", schema);\n if (type === \"boolean\") return applyNullable(\"boolean\", schema);\n if (type === \"null\") return \"null\";\n\n if (type === \"array\") {\n // Tuple support: prefixItems (JSON Schema 2020-12)\n const prefixItems = (schema as Record<string, unknown>)\n .prefixItems as JSONSchema7Definition[];\n if (Array.isArray(prefixItems)) {\n const types = prefixItems.map((s) =>\n jsonSchemaToTypeString(s, indent, nextCtx)\n );\n return applyNullable(`[${types.join(\", \")}]`, schema);\n }\n\n // Tuple support: items as array (draft-07)\n if (Array.isArray(schema.items)) {\n const types = schema.items.map((s) =>\n jsonSchemaToTypeString(s, indent, nextCtx)\n );\n return applyNullable(`[${types.join(\", \")}]`, schema);\n }\n\n if (schema.items) {\n const itemType = jsonSchemaToTypeString(schema.items, indent, nextCtx);\n return applyNullable(`${itemType}[]`, schema);\n }\n return applyNullable(\"unknown[]\", schema);\n }\n\n if (type === \"object\" || schema.properties) {\n const props = schema.properties || {};\n const required = new Set(schema.required || []);\n const lines: string[] = [];\n\n for (const [propName, propSchema] of Object.entries(props)) {\n if (typeof propSchema === \"boolean\") {\n const boolType = propSchema ? \"unknown\" : \"never\";\n const optionalMark = required.has(propName) ? \"\" : \"?\";\n lines.push(\n `${indent} ${quoteProp(propName)}${optionalMark}: ${boolType};`\n );\n continue;\n }\n\n const isRequired = required.has(propName);\n const propType = jsonSchemaToTypeString(\n propSchema,\n indent + \" \",\n nextCtx\n );\n const desc = propSchema.description;\n const format = propSchema.format;\n\n if (desc || format) {\n const descText = desc\n ? escapeJsDoc(desc.replace(/\\r?\\n/g, \" \"))\n : undefined;\n const formatTag = format ? `@format ${escapeJsDoc(format)}` : undefined;\n\n if (descText && formatTag) {\n // Multi-line JSDoc when both description and format are present\n lines.push(`${indent} /**`);\n lines.push(`${indent} * ${descText}`);\n lines.push(`${indent} * ${formatTag}`);\n lines.push(`${indent} */`);\n } else {\n lines.push(`${indent} /** ${descText ?? formatTag} */`);\n }\n }\n\n const quotedName = quoteProp(propName);\n const optionalMark = isRequired ? \"\" : \"?\";\n lines.push(`${indent} ${quotedName}${optionalMark}: ${propType};`);\n }\n\n // Handle additionalProperties\n // NOTE: In TypeScript, an index signature [key: string]: T requires all\n // named properties to be assignable to T. If any named property has an\n // incompatible type, the generated type is invalid. We emit it anyway\n // since it's more informative for LLMs consuming these types.\n if (schema.additionalProperties) {\n const valueType =\n schema.additionalProperties === true\n ? \"unknown\"\n : jsonSchemaToTypeString(\n schema.additionalProperties,\n indent + \" \",\n nextCtx\n );\n lines.push(`${indent} [key: string]: ${valueType};`);\n }\n\n if (lines.length === 0) {\n // additionalProperties: false means no keys allowed → empty object\n if (schema.additionalProperties === false) {\n return applyNullable(\"{}\", schema);\n }\n return applyNullable(\"Record<string, unknown>\", schema);\n }\n\n const result = `{\\n${lines.join(\"\\n\")}\\n${indent}}`;\n return applyNullable(result, schema);\n }\n\n // Handle array of types (e.g., [\"string\", \"null\"])\n if (Array.isArray(type)) {\n const types = type.map((t) => {\n if (t === \"string\") return \"string\";\n if (t === \"number\" || t === \"integer\") return \"number\";\n if (t === \"boolean\") return \"boolean\";\n if (t === \"null\") return \"null\";\n if (t === \"array\") return \"unknown[]\";\n if (t === \"object\") return \"Record<string, unknown>\";\n return \"unknown\";\n });\n return applyNullable(types.join(\" | \"), schema);\n }\n\n return \"unknown\";\n}\n\n/**\n * Apply OpenAPI 3.0 `nullable: true` to a type result.\n */\nfunction applyNullable(result: string, schema: unknown): string {\n if (\n result !== \"unknown\" &&\n result !== \"never\" &&\n (schema as Record<string, unknown>)?.nullable === true\n ) {\n return `${result} | null`;\n }\n return result;\n}\n\n/**\n * Extract field descriptions from a schema.\n * Works with Zod schemas (via .shape) and jsonSchema wrappers (via .properties).\n */\nfunction extractDescriptions(schema: unknown): Record<string, string> {\n const descriptions: Record<string, string> = {};\n\n // Try Zod schema shape\n const shape = (schema as { shape?: Record<string, ZodType> }).shape;\n if (shape && typeof shape === \"object\") {\n for (const [fieldName, fieldSchema] of Object.entries(shape)) {\n const desc = (fieldSchema as { description?: string }).description;\n if (desc) {\n descriptions[fieldName] = desc;\n }\n }\n return descriptions;\n }\n\n // Try JSON Schema properties (for jsonSchema wrapper)\n if (isJsonSchemaWrapper(schema)) {\n const jsonSchema = extractJsonSchema(schema);\n if (jsonSchema?.properties) {\n for (const [fieldName, propSchema] of Object.entries(\n jsonSchema.properties\n )) {\n if (\n propSchema &&\n typeof propSchema === \"object\" &&\n propSchema.description\n ) {\n descriptions[fieldName] = propSchema.description;\n }\n }\n }\n }\n\n return descriptions;\n}\n\n/**\n * Safely convert a schema to TypeScript type string.\n * Handles Zod schemas and AI SDK jsonSchema wrappers.\n * Returns \"unknown\" if the schema cannot be represented in TypeScript.\n */\nfunction safeSchemaToTs(\n schema: unknown,\n typeName: string,\n auxiliaryTypeStore: ReturnType<typeof createAuxiliaryTypeStore>\n): string {\n try {\n // For Zod schemas, use zod-to-ts\n if (isZodSchema(schema)) {\n const result = zodToTs(schema, { auxiliaryTypeStore });\n return printNodeZodToTs(createTypeAlias(result.node, typeName));\n }\n\n // For JSON Schema wrapper, convert directly to TypeScript\n if (isJsonSchemaWrapper(schema)) {\n const jsonSchema = extractJsonSchema(schema);\n if (jsonSchema) {\n const ctx: ConversionContext = {\n root: jsonSchema,\n depth: 0,\n seen: new Set(),\n maxDepth: 20\n };\n const typeBody = jsonSchemaToTypeString(jsonSchema, \"\", ctx);\n return `type ${typeName} = ${typeBody}`;\n }\n }\n\n return `type ${typeName} = unknown`;\n } catch {\n // If the schema cannot be represented, fall back to unknown\n return `type ${typeName} = unknown`;\n }\n}\n\n/**\n * Generate TypeScript type definitions from tool descriptors or an AI SDK ToolSet.\n * These types can be included in tool descriptions to help LLMs write correct code.\n */\nexport function generateTypes(tools: ToolDescriptors | ToolSet): string {\n let availableTools = \"\";\n let availableTypes = \"\";\n\n const auxiliaryTypeStore = createAuxiliaryTypeStore();\n\n for (const [toolName, tool] of Object.entries(tools)) {\n const safeName = sanitizeToolName(toolName);\n const camelName = toCamelCase(safeName);\n\n try {\n // Handle both our ToolDescriptor and AI SDK Tool types\n const inputSchema =\n \"inputSchema\" in tool ? tool.inputSchema : tool.parameters;\n const outputSchema =\n \"outputSchema\" in tool ? tool.outputSchema : undefined;\n const description = tool.description;\n\n const inputType = safeSchemaToTs(\n inputSchema,\n `${camelName}Input`,\n auxiliaryTypeStore\n );\n\n const outputType = outputSchema\n ? safeSchemaToTs(outputSchema, `${camelName}Output`, auxiliaryTypeStore)\n : `type ${camelName}Output = unknown`;\n\n availableTypes += `\\n${inputType.trim()}`;\n availableTypes += `\\n${outputType.trim()}`;\n\n // Build JSDoc comment with description and param descriptions\n const paramDescs = inputSchema\n ? extractParamDescriptions(inputSchema)\n : [];\n const jsdocLines: string[] = [];\n if (description?.trim()) {\n jsdocLines.push(escapeJsDoc(description.trim().replace(/\\r?\\n/g, \" \")));\n } else {\n jsdocLines.push(escapeJsDoc(toolName));\n }\n for (const pd of paramDescs) {\n jsdocLines.push(escapeJsDoc(pd.replace(/\\r?\\n/g, \" \")));\n }\n\n const jsdocBody = jsdocLines.map((l) => `\\t * ${l}`).join(\"\\n\");\n availableTools += `\\n\\t/**\\n${jsdocBody}\\n\\t */`;\n availableTools += `\\n\\t${safeName}: (input: ${camelName}Input) => Promise<${camelName}Output>;`;\n availableTools += \"\\n\";\n } catch {\n // One bad tool should not break the others — emit unknown types\n availableTypes += `\\ntype ${camelName}Input = unknown`;\n availableTypes += `\\ntype ${camelName}Output = unknown`;\n\n availableTools += `\\n\\t/**\\n\\t * ${escapeJsDoc(toolName)}\\n\\t */`;\n availableTools += `\\n\\t${safeName}: (input: ${camelName}Input) => Promise<${camelName}Output>;`;\n availableTools += \"\\n\";\n }\n }\n\n availableTools = `\\ndeclare const codemode: {${availableTools}}`;\n\n return `\n${availableTypes}\n${availableTools}\n `.trim();\n}\n"],"mappings":";;;AAiBA,MAAM,cAAc,IAAI,IAAI;CAC1B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;AAOF,SAAgB,iBAAiB,MAAsB;AACrD,KAAI,CAAC,KAAM,QAAO;CAGlB,IAAI,YAAY,KAAK,QAAQ,WAAW,IAAI;AAG5C,aAAY,UAAU,QAAQ,mBAAmB,GAAG;AAEpD,KAAI,CAAC,UAAW,QAAO;AAGvB,KAAI,SAAS,KAAK,UAAU,CAC1B,aAAY,MAAM;AAIpB,KAAI,YAAY,IAAI,UAAU,CAC5B,aAAY,YAAY;AAG1B,QAAO;;AAGT,SAAS,YAAY,KAAa;AAChC,QAAO,IACJ,QAAQ,cAAc,GAAG,WAAW,OAAO,aAAa,CAAC,CACzD,QAAQ,WAAW,WAAW,OAAO,aAAa,CAAC;;;;;;AAOxD,SAAS,yBAAyB,QAA2B;CAC3D,MAAM,eAAe,oBAAoB,OAAO;AAChD,QAAO,OAAO,QAAQ,aAAa,CAAC,KACjC,CAAC,WAAW,UAAU,gBAAgB,UAAU,KAAK,OACvD;;;;;AAeH,SAAS,YAAY,OAAkC;AACrD,QACE,UAAU,QACV,OAAO,UAAU,YACjB,UAAU,SACT,MAA6B,SAAS;;;;;;AAQ3C,SAAS,oBACP,OACsC;AACtC,KAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;AAIxD,KAAI,gBAAgB,MAClB,QAAO;CAIT,MAAM,UAAU,OAAO,sBAAsB,MAAM;AACnD,MAAK,MAAM,OAAO,SAAS;EACzB,MAAM,WAAY,MAAkC;AACpD,MAAI,YAAY,OAAO,aAAa,YAAY,gBAAgB,SAC9D,QAAO;;AAIX,QAAO;;;;;AAMT,SAAS,kBAAkB,SAAsC;AAC/D,KAAI,YAAY,QAAQ,OAAO,YAAY,SAAU,QAAO;AAG5D,KAAI,gBAAgB,QAClB,QAAQ,QAAwC;CAIlD,MAAM,UAAU,OAAO,sBAAsB,QAAQ;AACrD,MAAK,MAAM,OAAO,SAAS;EACzB,MAAM,WAAY,QAAoC;AACtD,MAAI,YAAY,OAAO,aAAa,YAAY,gBAAgB,SAC9D,QAAQ,SAAyC;;AAIrD,QAAO;;;;;AAMT,SAAS,YAAY,MAAuB;AAE1C,QAAO,CAAC,6BAA6B,KAAK,KAAK;;;;;AAMjD,SAAS,kBAAkB,IAAoB;CAC7C,MAAM,OAAO,GAAG,WAAW,EAAE;AAC7B,KAAI,QAAQ,MAAQ,SAAS,IAC3B,QAAO,QAAQ,KAAK,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI;AAEnD,QAAO;;;;;;AAOT,SAAS,UAAU,MAAsB;AACvC,KAAI,YAAY,KAAK,EAAE;EACrB,IAAI,UAAU;AACd,OAAK,MAAM,MAAM,KACf,KAAI,OAAO,KAAM,YAAW;WACnB,OAAO,KAAK,YAAW;WACvB,OAAO,KAAM,YAAW;WACxB,OAAO,KAAM,YAAW;WACxB,OAAO,IAAM,YAAW;WACxB,OAAO,SAAU,YAAW;WAC5B,OAAO,SAAU,YAAW;MAChC,YAAW,kBAAkB,GAAG;AAEvC,SAAO,IAAI,QAAQ;;AAErB,QAAO;;;;;;AAOT,SAAS,oBAAoB,GAAmB;CAC9C,IAAI,MAAM;AACV,MAAK,MAAM,MAAM,EACf,KAAI,OAAO,KAAM,QAAO;UACf,OAAO,KAAK,QAAO;UACnB,OAAO,KAAM,QAAO;UACpB,OAAO,KAAM,QAAO;UACpB,OAAO,IAAM,QAAO;UACpB,OAAO,SAAU,QAAO;UACxB,OAAO,SAAU,QAAO;KAC5B,QAAO,kBAAkB,GAAG;AAEnC,QAAO;;;;;;AAOT,SAAS,YAAY,MAAsB;AACzC,QAAO,KAAK,QAAQ,SAAS,OAAO;;;;;;AAOtC,SAAS,WACP,KACA,MAC8B;AAE9B,KAAI,QAAQ,IAAK,QAAO;AAExB,KAAI,CAAC,IAAI,WAAW,KAAK,CAAE,QAAO;CAElC,MAAM,WAAW,IACd,MAAM,EAAE,CACR,MAAM,IAAI,CACV,KAAK,MAAM,EAAE,QAAQ,OAAO,IAAI,CAAC,QAAQ,OAAO,IAAI,CAAC;CAExD,IAAI,UAAmB;AACvB,MAAK,MAAM,OAAO,UAAU;AAC1B,MAAI,YAAY,QAAQ,OAAO,YAAY,SAAU,QAAO;AAC5D,YAAW,QAAoC;AAC/C,MAAI,YAAY,OAAW,QAAO;;AAIpC,KAAI,OAAO,YAAY,UAAW,QAAO;AACzC,KAAI,YAAY,QAAQ,OAAO,YAAY,SAAU,QAAO;AAC5D,QAAO;;;;;;AAOT,SAAS,uBACP,QACA,QACA,KACQ;AAER,KAAI,OAAO,WAAW,UACpB,QAAO,SAAS,YAAY;AAI9B,KAAI,IAAI,SAAS,IAAI,SAAU,QAAO;AAGtC,KAAI,IAAI,KAAK,IAAI,OAAO,CAAE,QAAO;CAEjC,MAAM,UAA6B;EACjC,GAAG;EACH,OAAO,IAAI,QAAQ;EACnB,MAAM,IAAI,IAAI,CAAC,GAAG,IAAI,MAAM,OAAO,CAAC;EACrC;AAGD,KAAI,OAAO,MAAM;EACf,MAAM,WAAW,WAAW,OAAO,MAAM,IAAI,KAAK;AAClD,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,cACL,uBAAuB,UAAU,QAAQ,QAAQ,EACjD,OACD;;AAIH,KAAI,OAAO,MAIT,QAAO,cAHO,OAAO,MAAM,KAAK,MAC9B,uBAAuB,GAAG,QAAQ,QAAQ,CAC3C,CAC0B,KAAK,MAAM,EAAE,OAAO;AAEjD,KAAI,OAAO,MAIT,QAAO,cAHO,OAAO,MAAM,KAAK,MAC9B,uBAAuB,GAAG,QAAQ,QAAQ,CAC3C,CAC0B,KAAK,MAAM,EAAE,OAAO;AAIjD,KAAI,OAAO,MAIT,QAAO,cAHO,OAAO,MAAM,KAAK,MAC9B,uBAAuB,GAAG,QAAQ,QAAQ,CAC3C,CAC0B,KAAK,MAAM,EAAE,OAAO;AAIjD,KAAI,OAAO,MAAM;AACf,MAAI,OAAO,KAAK,WAAW,EAAG,QAAO;AASrC,SAAO,cARQ,OAAO,KACnB,KAAK,MAAM;AACV,OAAI,MAAM,KAAM,QAAO;AACvB,OAAI,OAAO,MAAM,SAAU,QAAO,OAAM,oBAAoB,EAAE,GAAG;AACjE,OAAI,OAAO,MAAM,SAAU,QAAO,KAAK,UAAU,EAAE,IAAI;AACvD,UAAO,OAAO,EAAE;IAChB,CACD,KAAK,MAAM,EACe,OAAO;;AAItC,KAAI,OAAO,UAAU,OASnB,QAAO,cAPL,OAAO,UAAU,OACb,SACA,OAAO,OAAO,UAAU,WACtB,OAAM,oBAAoB,OAAO,MAAM,GAAG,OAC1C,OAAO,OAAO,UAAU,WACrB,KAAK,UAAU,OAAO,MAAM,IAAI,YACjC,OAAO,OAAO,MAAM,EACD,OAAO;CAItC,MAAM,OAAO,OAAO;AAEpB,KAAI,SAAS,SAAU,QAAO,cAAc,UAAU,OAAO;AAC7D,KAAI,SAAS,YAAY,SAAS,UAChC,QAAO,cAAc,UAAU,OAAO;AACxC,KAAI,SAAS,UAAW,QAAO,cAAc,WAAW,OAAO;AAC/D,KAAI,SAAS,OAAQ,QAAO;AAE5B,KAAI,SAAS,SAAS;EAEpB,MAAM,cAAe,OAClB;AACH,MAAI,MAAM,QAAQ,YAAY,CAI5B,QAAO,cAAc,IAHP,YAAY,KAAK,MAC7B,uBAAuB,GAAG,QAAQ,QAAQ,CAC3C,CAC8B,KAAK,KAAK,CAAC,IAAI,OAAO;AAIvD,MAAI,MAAM,QAAQ,OAAO,MAAM,CAI7B,QAAO,cAAc,IAHP,OAAO,MAAM,KAAK,MAC9B,uBAAuB,GAAG,QAAQ,QAAQ,CAC3C,CAC8B,KAAK,KAAK,CAAC,IAAI,OAAO;AAGvD,MAAI,OAAO,MAET,QAAO,cAAc,GADJ,uBAAuB,OAAO,OAAO,QAAQ,QAAQ,CACrC,KAAK,OAAO;AAE/C,SAAO,cAAc,aAAa,OAAO;;AAG3C,KAAI,SAAS,YAAY,OAAO,YAAY;EAC1C,MAAM,QAAQ,OAAO,cAAc,EAAE;EACrC,MAAM,WAAW,IAAI,IAAI,OAAO,YAAY,EAAE,CAAC;EAC/C,MAAM,QAAkB,EAAE;AAE1B,OAAK,MAAM,CAAC,UAAU,eAAe,OAAO,QAAQ,MAAM,EAAE;AAC1D,OAAI,OAAO,eAAe,WAAW;IACnC,MAAM,WAAW,aAAa,YAAY;IAC1C,MAAM,eAAe,SAAS,IAAI,SAAS,GAAG,KAAK;AACnD,UAAM,KACJ,GAAG,OAAO,MAAM,UAAU,SAAS,GAAG,aAAa,IAAI,SAAS,GACjE;AACD;;GAGF,MAAM,aAAa,SAAS,IAAI,SAAS;GACzC,MAAM,WAAW,uBACf,YACA,SAAS,QACT,QACD;GACD,MAAM,OAAO,WAAW;GACxB,MAAM,SAAS,WAAW;AAE1B,OAAI,QAAQ,QAAQ;IAClB,MAAM,WAAW,OACb,YAAY,KAAK,QAAQ,UAAU,IAAI,CAAC,GACxC;IACJ,MAAM,YAAY,SAAS,WAAW,YAAY,OAAO,KAAK;AAE9D,QAAI,YAAY,WAAW;AAEzB,WAAM,KAAK,GAAG,OAAO,SAAS;AAC9B,WAAM,KAAK,GAAG,OAAO,SAAS,WAAW;AACzC,WAAM,KAAK,GAAG,OAAO,SAAS,YAAY;AAC1C,WAAM,KAAK,GAAG,OAAO,SAAS;UAE9B,OAAM,KAAK,GAAG,OAAO,UAAU,YAAY,UAAU,KAAK;;GAI9D,MAAM,aAAa,UAAU,SAAS;GACtC,MAAM,eAAe,aAAa,KAAK;AACvC,SAAM,KAAK,GAAG,OAAO,MAAM,aAAa,aAAa,IAAI,SAAS,GAAG;;AAQvE,MAAI,OAAO,sBAAsB;GAC/B,MAAM,YACJ,OAAO,yBAAyB,OAC5B,YACA,uBACE,OAAO,sBACP,SAAS,QACT,QACD;AACP,SAAM,KAAK,GAAG,OAAO,qBAAqB,UAAU,GAAG;;AAGzD,MAAI,MAAM,WAAW,GAAG;AAEtB,OAAI,OAAO,yBAAyB,MAClC,QAAO,cAAc,MAAM,OAAO;AAEpC,UAAO,cAAc,2BAA2B,OAAO;;AAIzD,SAAO,cADQ,MAAM,MAAM,KAAK,KAAK,CAAC,IAAI,OAAO,IACpB,OAAO;;AAItC,KAAI,MAAM,QAAQ,KAAK,CAUrB,QAAO,cATO,KAAK,KAAK,MAAM;AAC5B,MAAI,MAAM,SAAU,QAAO;AAC3B,MAAI,MAAM,YAAY,MAAM,UAAW,QAAO;AAC9C,MAAI,MAAM,UAAW,QAAO;AAC5B,MAAI,MAAM,OAAQ,QAAO;AACzB,MAAI,MAAM,QAAS,QAAO;AAC1B,MAAI,MAAM,SAAU,QAAO;AAC3B,SAAO;GACP,CACyB,KAAK,MAAM,EAAE,OAAO;AAGjD,QAAO;;;;;AAMT,SAAS,cAAc,QAAgB,QAAyB;AAC9D,KACE,WAAW,aACX,WAAW,WACV,QAAoC,aAAa,KAElD,QAAO,GAAG,OAAO;AAEnB,QAAO;;;;;;AAOT,SAAS,oBAAoB,QAAyC;CACpE,MAAM,eAAuC,EAAE;CAG/C,MAAM,QAAS,OAA+C;AAC9D,KAAI,SAAS,OAAO,UAAU,UAAU;AACtC,OAAK,MAAM,CAAC,WAAW,gBAAgB,OAAO,QAAQ,MAAM,EAAE;GAC5D,MAAM,OAAQ,YAAyC;AACvD,OAAI,KACF,cAAa,aAAa;;AAG9B,SAAO;;AAIT,KAAI,oBAAoB,OAAO,EAAE;EAC/B,MAAM,aAAa,kBAAkB,OAAO;AAC5C,MAAI,YAAY,YACd;QAAK,MAAM,CAAC,WAAW,eAAe,OAAO,QAC3C,WAAW,WACZ,CACC,KACE,cACA,OAAO,eAAe,YACtB,WAAW,YAEX,cAAa,aAAa,WAAW;;;AAM7C,QAAO;;;;;;;AAQT,SAAS,eACP,QACA,UACA,oBACQ;AACR,KAAI;AAEF,MAAI,YAAY,OAAO,CAErB,QAAOA,UAAiB,gBADT,QAAQ,QAAQ,EAAE,oBAAoB,CAAC,CACP,MAAM,SAAS,CAAC;AAIjE,MAAI,oBAAoB,OAAO,EAAE;GAC/B,MAAM,aAAa,kBAAkB,OAAO;AAC5C,OAAI,WAQF,QAAO,QAAQ,SAAS,KADP,uBAAuB,YAAY,IANrB;IAC7B,MAAM;IACN,OAAO;IACP,sBAAM,IAAI,KAAK;IACf,UAAU;IACX,CAC2D;;AAKhE,SAAO,QAAQ,SAAS;SAClB;AAEN,SAAO,QAAQ,SAAS;;;;;;;AAQ5B,SAAgB,cAAc,OAA0C;CACtE,IAAI,iBAAiB;CACrB,IAAI,iBAAiB;CAErB,MAAM,qBAAqB,0BAA0B;AAErD,MAAK,MAAM,CAAC,UAAU,SAAS,OAAO,QAAQ,MAAM,EAAE;EACpD,MAAM,WAAW,iBAAiB,SAAS;EAC3C,MAAM,YAAY,YAAY,SAAS;AAEvC,MAAI;GAEF,MAAM,cACJ,iBAAiB,OAAO,KAAK,cAAc,KAAK;GAClD,MAAM,eACJ,kBAAkB,OAAO,KAAK,eAAe;GAC/C,MAAM,cAAc,KAAK;GAEzB,MAAM,YAAY,eAChB,aACA,GAAG,UAAU,QACb,mBACD;GAED,MAAM,aAAa,eACf,eAAe,cAAc,GAAG,UAAU,SAAS,mBAAmB,GACtE,QAAQ,UAAU;AAEtB,qBAAkB,KAAK,UAAU,MAAM;AACvC,qBAAkB,KAAK,WAAW,MAAM;GAGxC,MAAM,aAAa,cACf,yBAAyB,YAAY,GACrC,EAAE;GACN,MAAM,aAAuB,EAAE;AAC/B,OAAI,aAAa,MAAM,CACrB,YAAW,KAAK,YAAY,YAAY,MAAM,CAAC,QAAQ,UAAU,IAAI,CAAC,CAAC;OAEvE,YAAW,KAAK,YAAY,SAAS,CAAC;AAExC,QAAK,MAAM,MAAM,WACf,YAAW,KAAK,YAAY,GAAG,QAAQ,UAAU,IAAI,CAAC,CAAC;GAGzD,MAAM,YAAY,WAAW,KAAK,MAAM,QAAQ,IAAI,CAAC,KAAK,KAAK;AAC/D,qBAAkB,YAAY,UAAU;AACxC,qBAAkB,OAAO,SAAS,YAAY,UAAU,oBAAoB,UAAU;AACtF,qBAAkB;UACZ;AAEN,qBAAkB,UAAU,UAAU;AACtC,qBAAkB,UAAU,UAAU;AAEtC,qBAAkB,iBAAiB,YAAY,SAAS,CAAC;AACzD,qBAAkB,OAAO,SAAS,YAAY,UAAU,oBAAoB,UAAU;AACtF,qBAAkB;;;AAItB,kBAAiB,8BAA8B,eAAe;AAE9D,QAAO;EACP,eAAe;EACf,eAAe;IACb,MAAM"}
@@ -0,0 +1,124 @@
1
+ import { test, expect } from "@playwright/test";
2
+
3
+ /**
4
+ * E2E tests for @cloudflare/codemode with a real AI binding.
5
+ *
6
+ * These verify the full pipeline:
7
+ * user prompt → LLM generates code via createCodeTool → DynamicWorkerExecutor
8
+ * runs the code in an isolated Worker → tool functions called via RPC → result returned.
9
+ *
10
+ * Uses Workers AI (@cf/zai-org/glm-4.7-flash) — no API key needed.
11
+ */
12
+
13
+ async function runChat(
14
+ request: import("@playwright/test").APIRequestContext,
15
+ baseURL: string,
16
+ userMessage: string
17
+ ): Promise<string> {
18
+ const res = await request.post(`${baseURL}/run`, {
19
+ headers: { "Content-Type": "application/json" },
20
+ data: {
21
+ messages: [
22
+ {
23
+ id: `msg-${crypto.randomUUID()}`,
24
+ role: "user",
25
+ parts: [{ type: "text", text: userMessage }]
26
+ }
27
+ ]
28
+ },
29
+ timeout: 45_000
30
+ });
31
+ expect(res.ok()).toBe(true);
32
+ return res.text();
33
+ }
34
+
35
+ test.describe("codemode e2e (Workers AI)", () => {
36
+ test.setTimeout(45_000);
37
+
38
+ test("LLM generates and executes code that calls addNumbers tool", async ({
39
+ request,
40
+ baseURL
41
+ }) => {
42
+ const response = await runChat(
43
+ request,
44
+ baseURL!,
45
+ "What is 17 + 25? Use the codemode tool with the addNumbers function to calculate this."
46
+ );
47
+
48
+ // The response stream should contain the answer 42 somewhere
49
+ // (either in the tool result or the LLM's text response)
50
+ expect(response).toContain("42");
51
+ });
52
+
53
+ test("LLM generates and executes code that calls getWeather tool", async ({
54
+ request,
55
+ baseURL
56
+ }) => {
57
+ const response = await runChat(
58
+ request,
59
+ baseURL!,
60
+ "What is the weather in London? Use the codemode tool with the getWeather function."
61
+ );
62
+
63
+ // The getWeather tool returns { city: "London", temperature: 22, condition: "Sunny" }
64
+ // The LLM should mention London or the weather data in its response
65
+ const lower = response.toLowerCase();
66
+ expect(
67
+ lower.includes("london") ||
68
+ lower.includes("22") ||
69
+ lower.includes("sunny")
70
+ ).toBe(true);
71
+ });
72
+
73
+ test("LLM generates and executes code that calls listProjects tool", async ({
74
+ request,
75
+ baseURL
76
+ }) => {
77
+ const response = await runChat(
78
+ request,
79
+ baseURL!,
80
+ "List all projects using the codemode tool with the listProjects function."
81
+ );
82
+
83
+ // listProjects returns Alpha and Beta
84
+ const lower = response.toLowerCase();
85
+ expect(lower.includes("alpha") || lower.includes("beta")).toBe(true);
86
+ });
87
+
88
+ test("LLM generates code with multiple tool calls", async ({
89
+ request,
90
+ baseURL
91
+ }) => {
92
+ const response = await runChat(
93
+ request,
94
+ baseURL!,
95
+ "Using the codemode tool, first get the weather in Paris, then add the numbers 10 and 5. Return both results."
96
+ );
97
+
98
+ // Should contain evidence of both tool calls completing
99
+ const lower = response.toLowerCase();
100
+ expect(
101
+ lower.includes("paris") ||
102
+ lower.includes("22") ||
103
+ lower.includes("15") ||
104
+ lower.includes("sunny")
105
+ ).toBe(true);
106
+ });
107
+
108
+ test("generateTypes returns valid type definitions", async ({
109
+ request,
110
+ baseURL
111
+ }) => {
112
+ const res = await request.get(`${baseURL}/types`);
113
+ expect(res.ok()).toBe(true);
114
+
115
+ const data = await res.json();
116
+ const types = data.types as string;
117
+
118
+ expect(types).toContain("declare const codemode");
119
+ expect(types).toContain("addNumbers");
120
+ expect(types).toContain("getWeather");
121
+ expect(types).toContain("createProject");
122
+ expect(types).toContain("listProjects");
123
+ });
124
+ });
@@ -0,0 +1,24 @@
1
+ import { defineConfig } from "@playwright/test";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ const PORT = 8798;
6
+ const e2eDir = dirname(fileURLToPath(import.meta.url));
7
+ const configPath = join(e2eDir, "wrangler.jsonc");
8
+
9
+ export default defineConfig({
10
+ testDir: e2eDir,
11
+ testMatch: "*.spec.ts",
12
+ timeout: 60_000,
13
+ retries: 2,
14
+ workers: 1,
15
+ use: {
16
+ baseURL: `http://localhost:${PORT}`
17
+ },
18
+ webServer: {
19
+ command: `lsof -ti tcp:${PORT} | xargs kill -9 2>/dev/null; npx wrangler dev --config ${configPath} --port ${PORT}`,
20
+ port: PORT,
21
+ reuseExistingServer: !process.env.CI,
22
+ timeout: 30_000
23
+ }
24
+ });