agents 0.13.3 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +6 -4
  2. package/dist/{agent-tool-types-l98LCbBl.d.ts → agent-tool-types-BAJWu8s4.d.ts} +474 -117
  3. package/dist/agent-tool-types.d.ts +13 -11
  4. package/dist/{agent-tools-Bg5ilERh.d.ts → agent-tools-0R6KEert.d.ts} +2 -2
  5. package/dist/{agent-tools-BAdX1vdI.js → agent-tools-DYrkT-Kx.js} +46 -6
  6. package/dist/agent-tools-DYrkT-Kx.js.map +1 -0
  7. package/dist/agent-tools.d.ts +14 -20
  8. package/dist/agent-tools.js +10 -6
  9. package/dist/agent-tools.js.map +1 -1
  10. package/dist/browser/ai.d.ts +1 -1
  11. package/dist/browser/ai.js +1 -1
  12. package/dist/browser/index.d.ts +1 -1
  13. package/dist/browser/index.js +1 -1
  14. package/dist/browser/tanstack-ai.d.ts +1 -1
  15. package/dist/browser/tanstack-ai.js +1 -1
  16. package/dist/chat/index.d.ts +162 -19
  17. package/dist/chat/index.js +97 -13
  18. package/dist/chat/index.js.map +1 -1
  19. package/dist/chat-sdk/index.d.ts +5 -5
  20. package/dist/chat-sdk/index.js +2 -2
  21. package/dist/chat-sdk/index.js.map +1 -1
  22. package/dist/{classPrivateFieldGet2-Evpt0SEr.js → classPrivateFieldGet2-D_obpP6O.js} +5 -5
  23. package/dist/classPrivateMethodInitSpec-10iTYB7F.js +7 -0
  24. package/dist/{client-D1kFXo80.js → client-FUizKzj2.js} +299 -95
  25. package/dist/client-FUizKzj2.js.map +1 -0
  26. package/dist/client.d.ts +1 -1
  27. package/dist/{compaction-helpers-B-pG5J22.d.ts → compaction-helpers-BEUILPss.d.ts} +59 -33
  28. package/dist/{compaction-helpers-fJyf8j4m.js → compaction-helpers-iiKMr2TQ.js} +22 -3
  29. package/dist/compaction-helpers-iiKMr2TQ.js.map +1 -0
  30. package/dist/{do-oauth-client-provider-4OKQU9rT.d.ts → do-oauth-client-provider-D4ZwyBDu.d.ts} +21 -1
  31. package/dist/{email-J0GGS3sa.d.ts → email-CL27preh.d.ts} +1 -1
  32. package/dist/email.d.ts +2 -2
  33. package/dist/experimental/memory/session/index.d.ts +30 -25
  34. package/dist/experimental/memory/session/index.js +7 -2
  35. package/dist/experimental/memory/session/index.js.map +1 -1
  36. package/dist/experimental/memory/utils/index.d.ts +12 -10
  37. package/dist/experimental/memory/utils/index.js +2 -2
  38. package/dist/{index-DKey3P4s.d.ts → index-RJ4OxMOe.d.ts} +270 -1
  39. package/dist/index.d.ts +74 -67
  40. package/dist/index.js +485 -64
  41. package/dist/index.js.map +1 -1
  42. package/dist/{internal_context-BZrMS0B5.d.ts → internal_context-Dg4Cgjcu.d.ts} +1 -1
  43. package/dist/internal_context.d.ts +1 -1
  44. package/dist/mcp/client.d.ts +17 -13
  45. package/dist/mcp/client.js +2 -2
  46. package/dist/mcp/do-oauth-client-provider.d.ts +1 -1
  47. package/dist/mcp/do-oauth-client-provider.js +143 -17
  48. package/dist/mcp/do-oauth-client-provider.js.map +1 -1
  49. package/dist/mcp/index.d.ts +35 -27
  50. package/dist/mcp/index.js +402 -69
  51. package/dist/mcp/index.js.map +1 -1
  52. package/dist/observability/index.d.ts +1 -1
  53. package/dist/observability/index.js +15 -1
  54. package/dist/observability/index.js.map +1 -1
  55. package/dist/react.d.ts +3 -3
  56. package/dist/react.js +1 -1
  57. package/dist/{retries-BVdRl5ZE.d.ts → retries-CF_HKSlJ.d.ts} +1 -1
  58. package/dist/retries.d.ts +1 -1
  59. package/dist/serializable.d.ts +1 -1
  60. package/dist/{shared-Cvj92byG.d.ts → shared-4CAYLCTO.d.ts} +1 -1
  61. package/dist/{shared-CiKaIK4h.js → shared-BIpUk4G5.js} +3 -7
  62. package/dist/{shared-CiKaIK4h.js.map → shared-BIpUk4G5.js.map} +1 -1
  63. package/dist/skills/index.d.ts +236 -0
  64. package/dist/skills/index.js +1326 -0
  65. package/dist/skills/index.js.map +1 -0
  66. package/dist/sub-routing.d.ts +6 -6
  67. package/dist/{tool-output-truncation-CH-khbZ3.js → tool-output-truncation-CNnnGZQ3.js} +1 -1
  68. package/dist/{tool-output-truncation-CH-khbZ3.js.map → tool-output-truncation-CNnnGZQ3.js.map} +1 -1
  69. package/dist/{types-_JjKmv-l.d.ts → types-6Zo2zfoO.d.ts} +1 -1
  70. package/dist/types.d.ts +1 -1
  71. package/dist/vite.d.ts +1 -1
  72. package/dist/vite.js +248 -2
  73. package/dist/vite.js.map +1 -1
  74. package/dist/{workflow-types-Dkzg4hAx.d.ts → workflow-types-SrZK_o9p.d.ts} +1 -1
  75. package/dist/workflow-types.d.ts +1 -1
  76. package/dist/workflows.d.ts +13 -3
  77. package/dist/workflows.js +10 -1
  78. package/dist/workflows.js.map +1 -1
  79. package/package.json +31 -13
  80. package/skills-module.d.ts +22 -0
  81. package/dist/agent-tools-BAdX1vdI.js.map +0 -1
  82. package/dist/client-D1kFXo80.js.map +0 -1
  83. package/dist/compaction-helpers-fJyf8j4m.js.map +0 -1
@@ -0,0 +1,1326 @@
1
+ import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, r as _assertClassBrand, t as _classPrivateFieldGet2 } from "../classPrivateFieldGet2-D_obpP6O.js";
2
+ import { t as _classPrivateMethodInitSpec } from "../classPrivateMethodInitSpec-10iTYB7F.js";
3
+ import { tool } from "ai";
4
+ import { RpcTarget } from "cloudflare:workers";
5
+ import { z } from "zod";
6
+ import { parse } from "yaml";
7
+ //#region src/skills/frontmatter.ts
8
+ function parseSkillFrontmatter(raw) {
9
+ const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
10
+ if (!match) return {
11
+ data: {},
12
+ body: raw
13
+ };
14
+ const parsed = parse(match[1] ?? "");
15
+ return {
16
+ data: parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {},
17
+ body: match[2] ?? ""
18
+ };
19
+ }
20
+ function optionalString(value) {
21
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
22
+ }
23
+ function optionalRecord(value) {
24
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
25
+ }
26
+ function parseSkillMarkdown(raw) {
27
+ const { data, body } = parseSkillFrontmatter(raw);
28
+ const name = optionalString(data.name);
29
+ const description = optionalString(data.description);
30
+ if (!name || !description) return null;
31
+ return {
32
+ name,
33
+ description,
34
+ body,
35
+ compatibility: optionalString(data.compatibility),
36
+ license: optionalString(data.license),
37
+ allowedTools: optionalString(data["allowed-tools"]),
38
+ metadata: optionalRecord(data.metadata)
39
+ };
40
+ }
41
+ //#endregion
42
+ //#region src/skills/types.ts
43
+ function validateSkillResourcePath(path) {
44
+ if (path.startsWith("/") || path.includes("\0") || path.split("/").some((part) => part === "" || part === "." || part === "..")) return `Skill resource path must be a normalized relative path: ${path}`;
45
+ return null;
46
+ }
47
+ //#endregion
48
+ //#region src/skills/manifest.ts
49
+ function descriptorFromEntry(sourceId, entry) {
50
+ return {
51
+ name: entry.name,
52
+ description: entry.description,
53
+ compatibility: entry.compatibility,
54
+ license: entry.license,
55
+ allowedTools: entry.allowedTools,
56
+ metadata: entry.metadata,
57
+ sourceId,
58
+ version: entry.version
59
+ };
60
+ }
61
+ function contentFromEntry(sourceId, entry) {
62
+ return {
63
+ ...descriptorFromEntry(sourceId, entry),
64
+ body: entry.body,
65
+ rawContent: entry.rawContent,
66
+ resources: entry.resources?.filter((resource) => validateSkillResourcePath(resource.path) === null).map(({ content: _content, ...resource }) => ({ ...resource }))
67
+ };
68
+ }
69
+ function fromManifest(manifest) {
70
+ const byName = new Map(manifest.skills.map((skill) => [skill.name, skill]));
71
+ return {
72
+ id: manifest.id,
73
+ fingerprint: manifest.fingerprint,
74
+ async list() {
75
+ return manifest.skills.map((skill) => descriptorFromEntry(manifest.id, skill));
76
+ },
77
+ async load(name) {
78
+ const skill = byName.get(name);
79
+ return skill ? contentFromEntry(manifest.id, skill) : null;
80
+ },
81
+ async readResource(name, path) {
82
+ const skill = byName.get(name);
83
+ if (validateSkillResourcePath(path) !== null) return null;
84
+ const resource = skill?.resources?.find((entry) => entry.path === path);
85
+ if (resource && validateSkillResourcePath(resource.path) !== null) return null;
86
+ return resource ? { ...resource } : null;
87
+ }
88
+ };
89
+ }
90
+ //#endregion
91
+ //#region src/skills/r2.ts
92
+ function normalizePrefix(prefix) {
93
+ if (!prefix) return "";
94
+ return prefix.endsWith("/") ? prefix : `${prefix}/`;
95
+ }
96
+ function resourceKind(path) {
97
+ if (path.startsWith("references/")) return "reference";
98
+ if (path.startsWith("scripts/")) return "script";
99
+ if (path.startsWith("assets/")) return "asset";
100
+ return "file";
101
+ }
102
+ const TEXT_EXTENSIONS = new Set([
103
+ ".bash",
104
+ ".css",
105
+ ".csv",
106
+ ".html",
107
+ ".js",
108
+ ".json",
109
+ ".jsx",
110
+ ".md",
111
+ ".mjs",
112
+ ".py",
113
+ ".sh",
114
+ ".svg",
115
+ ".ts",
116
+ ".tsx",
117
+ ".txt",
118
+ ".xml",
119
+ ".yaml",
120
+ ".yml"
121
+ ]);
122
+ const MIME_TYPES = new Map([
123
+ [".css", "text/css"],
124
+ [".gif", "image/gif"],
125
+ [".html", "text/html"],
126
+ [".jpg", "image/jpeg"],
127
+ [".jpeg", "image/jpeg"],
128
+ [".js", "text/javascript"],
129
+ [".json", "application/json"],
130
+ [".md", "text/markdown"],
131
+ [".mjs", "text/javascript"],
132
+ [".pdf", "application/pdf"],
133
+ [".png", "image/png"],
134
+ [".py", "text/x-python"],
135
+ [".sh", "text/x-shellscript"],
136
+ [".svg", "image/svg+xml"],
137
+ [".ts", "text/typescript"],
138
+ [".tsx", "text/typescript"],
139
+ [".txt", "text/plain"],
140
+ [".webp", "image/webp"],
141
+ [".woff", "font/woff"],
142
+ [".woff2", "font/woff2"],
143
+ [".xml", "application/xml"],
144
+ [".yaml", "application/yaml"],
145
+ [".yml", "application/yaml"]
146
+ ]);
147
+ function extensionOf$1(path) {
148
+ const file = path.split("/").at(-1) ?? path;
149
+ const index = file.lastIndexOf(".");
150
+ return index === -1 ? "" : file.slice(index).toLowerCase();
151
+ }
152
+ function resourceEncoding(path) {
153
+ return TEXT_EXTENSIONS.has(extensionOf$1(path)) ? "text" : "base64";
154
+ }
155
+ function resourceMimeType(path) {
156
+ return MIME_TYPES.get(extensionOf$1(path));
157
+ }
158
+ function base64Encode(bytes) {
159
+ let binary = "";
160
+ const view = new Uint8Array(bytes);
161
+ for (let i = 0; i < view.length; i++) binary += String.fromCharCode(view[i]);
162
+ return btoa(binary);
163
+ }
164
+ async function readObjectFingerprint(bucket, key, path) {
165
+ const object = await bucket.get(key);
166
+ if (!object) return null;
167
+ const encoding = resourceEncoding(path);
168
+ return `${encoding}:${encoding === "base64" ? base64Encode(await object.arrayBuffer()) : await object.text()}`;
169
+ }
170
+ function stableHash(parts) {
171
+ let hash = 2166136261;
172
+ for (const part of parts) {
173
+ for (let i = 0; i < part.length; i++) {
174
+ hash ^= part.charCodeAt(i);
175
+ hash = Math.imul(hash, 16777619);
176
+ }
177
+ hash ^= 255;
178
+ hash = Math.imul(hash, 16777619);
179
+ }
180
+ return (hash >>> 0).toString(36);
181
+ }
182
+ function objectFingerprintPart(object) {
183
+ return [
184
+ object.key,
185
+ String(object.size),
186
+ object.etag,
187
+ object.uploaded?.toISOString() ?? ""
188
+ ].join(":");
189
+ }
190
+ async function listAllObjects(bucket, prefix) {
191
+ const objects = [];
192
+ let cursor;
193
+ let truncated = true;
194
+ while (truncated) {
195
+ const listed = await bucket.list({
196
+ prefix,
197
+ cursor
198
+ });
199
+ objects.push(...listed.objects);
200
+ truncated = listed.truncated;
201
+ cursor = listed.truncated ? listed.cursor : void 0;
202
+ }
203
+ return objects.sort((a, b) => a.key.localeCompare(b.key));
204
+ }
205
+ async function readObject(bucket, key) {
206
+ const object = await bucket.get(key);
207
+ return object ? object.text() : null;
208
+ }
209
+ async function readResourceObject(bucket, key, descriptor) {
210
+ const object = await bucket.get(key);
211
+ if (!object) return null;
212
+ const encoding = descriptor.encoding ?? resourceEncoding(descriptor.path);
213
+ const content = encoding === "base64" ? base64Encode(await object.arrayBuffer()) : await object.text();
214
+ return {
215
+ ...descriptor,
216
+ encoding,
217
+ content
218
+ };
219
+ }
220
+ function r2(bucket, options = {}) {
221
+ const prefix = normalizePrefix(options.prefix);
222
+ const id = options.id ?? `r2:${prefix || "/"}`;
223
+ const allowedSkills = options.skills?.length ? new Set(options.skills) : null;
224
+ const fingerprintMode = options.fingerprint ?? "metadata";
225
+ const refreshIntervalMs = options.refreshIntervalMs ?? 6e4;
226
+ let fingerprint = id;
227
+ let loaded = false;
228
+ let indexedAt = 0;
229
+ let byName = /* @__PURE__ */ new Map();
230
+ let resourcesByName = /* @__PURE__ */ new Map();
231
+ async function refreshIndex(force = false) {
232
+ if (loaded && !force && Date.now() - indexedAt < refreshIntervalMs) return;
233
+ const objects = await listAllObjects(bucket, prefix);
234
+ const objectsByKey = new Map(objects.map((object) => [object.key, object]));
235
+ const skillDirectories = objects.map((object) => object.key.slice(prefix.length)).filter((key) => key.endsWith("/SKILL.md")).map((key) => key.slice(0, -9)).filter((directory) => directory && !directory.includes("/"));
236
+ const nextByName = /* @__PURE__ */ new Map();
237
+ const nextResourcesByName = /* @__PURE__ */ new Map();
238
+ const fingerprintParts = [];
239
+ for (const directory of skillDirectories) {
240
+ const skillKey = `${prefix}${directory}/SKILL.md`;
241
+ const rawContent = await readObject(bucket, skillKey);
242
+ if (!rawContent) continue;
243
+ const parsed = parseSkillMarkdown(rawContent);
244
+ if (!parsed || allowedSkills?.has(parsed.name) === false) continue;
245
+ const resourceKeys = objects.map((object) => object.key).filter((key) => key.startsWith(`${prefix}${directory}/`) && key !== skillKey).filter((key) => validateSkillResourcePath(key.slice(`${prefix}${directory}/`.length)) === null);
246
+ const resources = [];
247
+ for (const key of resourceKeys) {
248
+ const path = key.slice(`${prefix}${directory}/`.length);
249
+ const listedResource = objectsByKey.get(key);
250
+ resources.push({
251
+ path,
252
+ kind: resourceKind(path),
253
+ size: listedResource?.size,
254
+ encoding: resourceEncoding(path),
255
+ mimeType: resourceMimeType(path)
256
+ });
257
+ }
258
+ const descriptor = {
259
+ name: parsed.name,
260
+ description: parsed.description,
261
+ compatibility: parsed.compatibility,
262
+ license: parsed.license,
263
+ allowedTools: parsed.allowedTools,
264
+ metadata: parsed.metadata,
265
+ sourceId: id
266
+ };
267
+ const content = {
268
+ ...descriptor,
269
+ body: parsed.body,
270
+ rawContent,
271
+ resources: resources.map((resource) => ({ ...resource }))
272
+ };
273
+ if (!nextByName.has(parsed.name)) {
274
+ nextByName.set(parsed.name, {
275
+ descriptor,
276
+ content,
277
+ directory
278
+ });
279
+ nextResourcesByName.set(parsed.name, resources);
280
+ }
281
+ const skillObjects = [skillKey, ...resourceKeys].map((key) => objectsByKey.get(key)).filter((object) => Boolean(object));
282
+ if (fingerprintMode === "content") {
283
+ fingerprintParts.push(rawContent);
284
+ for (const key of resourceKeys) {
285
+ const path = key.slice(`${prefix}${directory}/`.length);
286
+ fingerprintParts.push(await readObjectFingerprint(bucket, key, path) ?? "");
287
+ }
288
+ } else fingerprintParts.push(...skillObjects.map(objectFingerprintPart));
289
+ }
290
+ byName = nextByName;
291
+ resourcesByName = nextResourcesByName;
292
+ fingerprint = `${id}:${stableHash(fingerprintParts)}`;
293
+ loaded = true;
294
+ indexedAt = Date.now();
295
+ }
296
+ return {
297
+ id,
298
+ get fingerprint() {
299
+ return fingerprint;
300
+ },
301
+ async list() {
302
+ await refreshIndex();
303
+ return [...byName.values()].map(({ descriptor }) => ({ ...descriptor }));
304
+ },
305
+ async load(name) {
306
+ await refreshIndex();
307
+ const skill = byName.get(name);
308
+ return skill ? { ...skill.content } : null;
309
+ },
310
+ async readResource(name, path) {
311
+ if (validateSkillResourcePath(path) !== null) return null;
312
+ await refreshIndex();
313
+ const skill = byName.get(name);
314
+ if (!skill) return null;
315
+ const resource = resourcesByName.get(name)?.find((entry) => entry.path === path);
316
+ if (!resource) return null;
317
+ return readResourceObject(bucket, `${prefix}${skill.directory}/${path}`, resource);
318
+ },
319
+ async refresh() {
320
+ await refreshIndex();
321
+ }
322
+ };
323
+ }
324
+ //#endregion
325
+ //#region src/skills/runner.ts
326
+ const DEFAULT_SCRIPT_TIMEOUT_MS = 3e4;
327
+ const MAX_OUTPUT_ARTIFACT_BYTES = 64e3;
328
+ const MAX_OUTPUT_ARTIFACTS = 20;
329
+ const SUPPORTED_SCRIPT_EXTENSIONS = new Set([
330
+ ".js",
331
+ ".mjs",
332
+ ".ts",
333
+ ".tsx",
334
+ ".py",
335
+ ".sh",
336
+ ".bash"
337
+ ]);
338
+ let runnerExperimentalWarned = false;
339
+ function extensionOf(path) {
340
+ const file = path.split("/").at(-1) ?? path;
341
+ const index = file.lastIndexOf(".");
342
+ return index === -1 ? "" : file.slice(index).toLowerCase();
343
+ }
344
+ function effectiveTimeout(options) {
345
+ return options.timeout ?? DEFAULT_SCRIPT_TIMEOUT_MS;
346
+ }
347
+ function effectiveWorkspaceAccess(options) {
348
+ if (options.workspace) return options.workspace;
349
+ return options.workspaceInstance ? "read" : "none";
350
+ }
351
+ function validateSkillScriptPath(path) {
352
+ if (!path.startsWith("scripts/")) return {
353
+ ok: false,
354
+ error: `Skill script path must start with "scripts/": ${path}`
355
+ };
356
+ if (path.startsWith("/") || path.includes("\0") || path.split("/").some((part) => part === "" || part === "." || part === "..")) return {
357
+ ok: false,
358
+ error: `Skill script path must be a normalized relative path under "scripts/": ${path}`
359
+ };
360
+ const extension = extensionOf(path);
361
+ if (!SUPPORTED_SCRIPT_EXTENSIONS.has(extension)) return {
362
+ ok: false,
363
+ error: `Unsupported skill script extension "${extension || "(none)"}" for ${path}. Supported extensions: ${[...SUPPORTED_SCRIPT_EXTENSIONS].join(", ")}`
364
+ };
365
+ if (extension === ".sh" || extension === ".bash") return {
366
+ ok: true,
367
+ runtime: "bash"
368
+ };
369
+ if (extension === ".py") return {
370
+ ok: true,
371
+ runtime: "python"
372
+ };
373
+ if (extension === ".ts" || extension === ".tsx") return {
374
+ ok: true,
375
+ runtime: "typescript"
376
+ };
377
+ return {
378
+ ok: true,
379
+ runtime: "javascript"
380
+ };
381
+ }
382
+ function validateMountedResourcePaths(request) {
383
+ for (const resource of request.resources ?? []) {
384
+ const pathError = validateSkillResourcePath(resource.path);
385
+ if (pathError) throw new Error(pathError);
386
+ }
387
+ }
388
+ function skillScriptContext(request) {
389
+ return { skill: {
390
+ name: request.skill.name,
391
+ description: request.skill.description,
392
+ compatibility: request.skill.compatibility,
393
+ license: request.skill.license,
394
+ allowedTools: request.skill.allowedTools,
395
+ metadata: request.skill.metadata,
396
+ sourceId: request.skill.sourceId,
397
+ version: request.skill.version
398
+ } };
399
+ }
400
+ /**
401
+ * Text bundled resources exposed to function-style JS/TS scripts via
402
+ * `ctx.files`. Binary resources are omitted in v1.
403
+ */
404
+ function textFilesMap(request) {
405
+ const files = {};
406
+ for (const resource of request.resources ?? []) if ((resource.encoding ?? "text") === "text") files[resource.path] = resource.content;
407
+ return files;
408
+ }
409
+ function base64ToBytes(value) {
410
+ const binary = atob(value);
411
+ const bytes = new Uint8Array(binary.length);
412
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
413
+ return bytes;
414
+ }
415
+ function stdinText(stdin) {
416
+ return typeof stdin === "string" ? stdin : String(stdin ?? "");
417
+ }
418
+ function mountedFiles(request) {
419
+ const files = {
420
+ "/input.json": {
421
+ content: JSON.stringify(request.input),
422
+ encoding: "text"
423
+ },
424
+ "/context.json": {
425
+ content: JSON.stringify(skillScriptContext(request)),
426
+ encoding: "text"
427
+ },
428
+ "/skill/SKILL.md": {
429
+ content: request.skill.rawContent ?? request.skill.body,
430
+ encoding: "text"
431
+ }
432
+ };
433
+ for (const resource of request.resources ?? []) {
434
+ const pathError = validateSkillResourcePath(resource.path);
435
+ if (pathError) throw new Error(pathError);
436
+ files[`/skill/${resource.path}`] = {
437
+ content: resource.content,
438
+ encoding: resource.encoding ?? "text"
439
+ };
440
+ }
441
+ files[`/skill/${request.path}`] = {
442
+ content: request.source,
443
+ encoding: "text"
444
+ };
445
+ return files;
446
+ }
447
+ function bashFiles(request) {
448
+ const files = {
449
+ "/input.json": JSON.stringify(request.input),
450
+ "/context.json": JSON.stringify(skillScriptContext(request)),
451
+ "/skill-script.sh": request.source,
452
+ "/skill/SKILL.md": request.skill.rawContent ?? request.skill.body,
453
+ [`/skill/${request.path}`]: request.source
454
+ };
455
+ for (const resource of request.resources ?? []) {
456
+ const pathError = validateSkillResourcePath(resource.path);
457
+ if (pathError) throw new Error(pathError);
458
+ files[`/skill/${resource.path}`] = (resource.encoding ?? "text") === "base64" ? base64ToBytes(resource.content) : resource.content;
459
+ }
460
+ return files;
461
+ }
462
+ /**
463
+ * Wrap a function-style JS/TS skill module so it runs inside the codemode
464
+ * sandbox. The module must `export default` an async `run(input, ctx)`; we
465
+ * rewrite the default export to a local binding, build the `ctx` capability
466
+ * object from the host bridge proxy (`__host`), and invoke it.
467
+ */
468
+ function scriptModule(source, request) {
469
+ const runnableSource = stripStrayExports(source.replace(/^\s*export\s+default\s+/m, "const __skillRun = "));
470
+ const skillMeta = skillScriptContext(request).skill;
471
+ return [
472
+ "async () => {",
473
+ ` const input = ${JSON.stringify(request.input)};`,
474
+ ` const __skill = ${JSON.stringify(skillMeta)};`,
475
+ ` const __files = ${JSON.stringify(textFilesMap(request))};`,
476
+ " const workspace = {",
477
+ " readFile: (path) => __host.readFile(path),",
478
+ " listFiles: (path = \".\") => __host.listFiles(path),",
479
+ " glob: (pattern) => __host.glob(pattern),",
480
+ " stat: (path) => __host.stat(path),",
481
+ " writeFile: (path, content) => __host.writeFile({ path, content })",
482
+ " };",
483
+ " const tools = new Proxy(",
484
+ " { call: (name, input) => __host.callTool({ name, input }) },",
485
+ " {",
486
+ " get: (target, prop) =>",
487
+ " prop in target",
488
+ " ? target[prop]",
489
+ " : (input) => __host.callTool({ name: String(prop), input })",
490
+ " }",
491
+ " );",
492
+ " const output = {",
493
+ " writeFile: (name, content) => __host.writeOutput({ name, content })",
494
+ " };",
495
+ " const ctx = { skill: __skill, files: __files, workspace, tools, output };",
496
+ "",
497
+ runnableSource,
498
+ "",
499
+ " if (typeof __skillRun !== \"function\") {",
500
+ " throw new Error(\"Skill script default export must be a function (input, ctx).\");",
501
+ " }",
502
+ " return await __skillRun(input, ctx);",
503
+ "}"
504
+ ].join("\n");
505
+ }
506
+ function moduleSource(module) {
507
+ if (typeof module === "string") return module;
508
+ return module?.js ?? module?.cjs ?? null;
509
+ }
510
+ /**
511
+ * Remove `export { ... }` blocks (illegal inside the function wrapper) and
512
+ * rewrite a `export { X as default }` binding to the local `__skillRun`.
513
+ */
514
+ function stripStrayExports(source) {
515
+ return source.replace(/\n?export\s*\{[\s\S]*?\};?/g, "");
516
+ }
517
+ function rewriteBundledSource(source) {
518
+ const defaultExport = source.match(/export\s*\{\s*([A-Za-z_$][\w$]*)\s+as\s+default\s*\};?/m);
519
+ let out = source;
520
+ if (defaultExport) out = out.replace(defaultExport[0], `const __skillRun = ${defaultExport[1]};`);
521
+ return stripStrayExports(out);
522
+ }
523
+ async function prepareJavaScriptSource(request, runtime) {
524
+ const files = {};
525
+ for (const resource of request.resources ?? []) {
526
+ const extension = extensionOf(resource.path);
527
+ if (resource.kind === "script" && (resource.encoding ?? "text") === "text" && [
528
+ ".js",
529
+ ".mjs",
530
+ ".ts",
531
+ ".tsx"
532
+ ].includes(extension)) files[resource.path] = resource.content;
533
+ }
534
+ files[request.path] = request.source;
535
+ if (!(runtime === "typescript" || Object.keys(files).length > 1)) return request.source;
536
+ const { createWorker } = await import("@cloudflare/worker-bundler");
537
+ const result = await createWorker({
538
+ files,
539
+ entryPoint: request.path,
540
+ bundle: true
541
+ });
542
+ const compiled = moduleSource(result.modules[result.mainModule]) ?? moduleSource(Object.values(result.modules)[0]);
543
+ if (!compiled) throw new Error(`Failed to compile skill script: ${request.path}`);
544
+ return rewriteBundledSource(compiled);
545
+ }
546
+ async function executeToolFromSet(tools, name, input) {
547
+ const target = tools?.[name];
548
+ const execute = target && "execute" in target ? target.execute : void 0;
549
+ if (!execute) throw new Error(`Tool not available: ${name}`);
550
+ return execute(input);
551
+ }
552
+ function stringifyHostResult(result) {
553
+ return JSON.stringify({ result });
554
+ }
555
+ function stringifyHostError(error) {
556
+ return JSON.stringify({ error: error instanceof Error ? error.message : String(error) });
557
+ }
558
+ var _tools = /* @__PURE__ */ new WeakMap();
559
+ var _workspace = /* @__PURE__ */ new WeakMap();
560
+ var _workspaceAccess = /* @__PURE__ */ new WeakMap();
561
+ var _outputs = /* @__PURE__ */ new WeakMap();
562
+ var _SkillScriptHostBridge_brand = /* @__PURE__ */ new WeakSet();
563
+ /**
564
+ * The single source of truth for skill-script capabilities and permission
565
+ * enforcement. Every runtime delegates here:
566
+ *
567
+ * - JavaScript/TypeScript reach it through a codemode `ToolProvider`.
568
+ * - Python receives it as an RPC `RpcTarget` (JSON-marshalling methods).
569
+ * - Bash calls it from `just-bash` custom commands.
570
+ *
571
+ * Construct a fresh bridge per `run()` so the per-invocation `/output`
572
+ * artifact buffer is never shared between concurrent script runs.
573
+ */
574
+ var SkillScriptHostBridge = class extends RpcTarget {
575
+ constructor(tools, workspace, workspaceAccess) {
576
+ super();
577
+ _classPrivateMethodInitSpec(this, _SkillScriptHostBridge_brand);
578
+ _classPrivateFieldInitSpec(this, _tools, void 0);
579
+ _classPrivateFieldInitSpec(this, _workspace, void 0);
580
+ _classPrivateFieldInitSpec(this, _workspaceAccess, void 0);
581
+ _classPrivateFieldInitSpec(this, _outputs, /* @__PURE__ */ new Map());
582
+ _classPrivateFieldSet2(_tools, this, tools);
583
+ _classPrivateFieldSet2(_workspace, this, workspace);
584
+ _classPrivateFieldSet2(_workspaceAccess, this, workspaceAccess);
585
+ }
586
+ get workspaceAccess() {
587
+ return _classPrivateFieldGet2(_workspaceAccess, this);
588
+ }
589
+ hasTools() {
590
+ return Boolean(_classPrivateFieldGet2(_tools, this) && Object.keys(_classPrivateFieldGet2(_tools, this)).length > 0);
591
+ }
592
+ async callTool(name, input) {
593
+ return executeToolFromSet(_classPrivateFieldGet2(_tools, this), name, input);
594
+ }
595
+ async readFile(path) {
596
+ return _assertClassBrand(_SkillScriptHostBridge_brand, this, _requireWorkspace).call(this, "read").readFile(path);
597
+ }
598
+ async listFiles(path = ".") {
599
+ return _assertClassBrand(_SkillScriptHostBridge_brand, this, _requireWorkspace).call(this, "read").readDir(path);
600
+ }
601
+ async glob(pattern) {
602
+ return _assertClassBrand(_SkillScriptHostBridge_brand, this, _requireWorkspace).call(this, "read").glob(pattern);
603
+ }
604
+ async stat(path) {
605
+ const info = await _assertClassBrand(_SkillScriptHostBridge_brand, this, _requireWorkspace).call(this, "read").stat(path);
606
+ if (!info) return null;
607
+ return {
608
+ type: info.type,
609
+ size: info.size ?? 0
610
+ };
611
+ }
612
+ async writeFile(path, content) {
613
+ await _assertClassBrand(_SkillScriptHostBridge_brand, this, _requireWorkspace).call(this, "read-write").writeFile(path, content);
614
+ }
615
+ writeOutput(name, content) {
616
+ const key = String(name);
617
+ const text = typeof content === "string" ? content : String(content ?? "");
618
+ if (new TextEncoder().encode(text).byteLength > MAX_OUTPUT_ARTIFACT_BYTES) throw new Error(`Output artifact "${key}" exceeds ${MAX_OUTPUT_ARTIFACT_BYTES} bytes.`);
619
+ if (!_classPrivateFieldGet2(_outputs, this).has(key) && _classPrivateFieldGet2(_outputs, this).size >= MAX_OUTPUT_ARTIFACTS) throw new Error(`Too many skill output artifacts (max ${MAX_OUTPUT_ARTIFACTS}).`);
620
+ _classPrivateFieldGet2(_outputs, this).set(key, {
621
+ path: key,
622
+ encoding: "text",
623
+ content: text
624
+ });
625
+ }
626
+ getOutputFiles() {
627
+ return [..._classPrivateFieldGet2(_outputs, this).values()];
628
+ }
629
+ async tool(name, inputJson = "{}") {
630
+ try {
631
+ const input = inputJson.trim() ? JSON.parse(inputJson) : {};
632
+ return stringifyHostResult(await this.callTool(name, input));
633
+ } catch (error) {
634
+ return stringifyHostError(error);
635
+ }
636
+ }
637
+ async workspaceReadFile(path) {
638
+ try {
639
+ return stringifyHostResult(await this.readFile(path));
640
+ } catch (error) {
641
+ return stringifyHostError(error);
642
+ }
643
+ }
644
+ async workspaceListFiles(path = ".") {
645
+ try {
646
+ return stringifyHostResult(await this.listFiles(path));
647
+ } catch (error) {
648
+ return stringifyHostError(error);
649
+ }
650
+ }
651
+ async workspaceGlob(pattern) {
652
+ try {
653
+ return stringifyHostResult(await this.glob(pattern));
654
+ } catch (error) {
655
+ return stringifyHostError(error);
656
+ }
657
+ }
658
+ async workspaceWriteFile(path, content) {
659
+ try {
660
+ await this.writeFile(path, content);
661
+ return stringifyHostResult(null);
662
+ } catch (error) {
663
+ return stringifyHostError(error);
664
+ }
665
+ }
666
+ };
667
+ function _requireWorkspace(access) {
668
+ if (!_classPrivateFieldGet2(_workspace, this) || _classPrivateFieldGet2(_workspaceAccess, this) === "none") throw new Error("Workspace access is not available.");
669
+ if (access === "read-write" && _classPrivateFieldGet2(_workspaceAccess, this) !== "read-write") throw new Error("Workspace write access is not available.");
670
+ return _classPrivateFieldGet2(_workspace, this);
671
+ }
672
+ /**
673
+ * Expose the bridge to JS/TS scripts as a single codemode provider namespace
674
+ * (`__host`). The sandbox `ctx` object wraps these calls into the friendly
675
+ * `workspace` / `tools` / `output` surface (see {@link scriptModule}).
676
+ */
677
+ function hostProvider(bridge) {
678
+ return {
679
+ name: "__host",
680
+ tools: {
681
+ callTool: { execute: (a) => {
682
+ const { name, input } = a;
683
+ return bridge.callTool(name, input);
684
+ } },
685
+ readFile: { execute: (a) => bridge.readFile(String(a)) },
686
+ listFiles: { execute: (a) => bridge.listFiles(typeof a === "string" ? a : ".") },
687
+ glob: { execute: (a) => bridge.glob(String(a)) },
688
+ stat: { execute: (a) => bridge.stat(String(a)) },
689
+ writeFile: { execute: (a) => {
690
+ const { path, content } = a;
691
+ return bridge.writeFile(path, content);
692
+ } },
693
+ writeOutput: { execute: async (a) => {
694
+ const { name, content } = a;
695
+ bridge.writeOutput(name, content);
696
+ return null;
697
+ } }
698
+ }
699
+ };
700
+ }
701
+ function pythonScriptModule(request) {
702
+ const source = request.source;
703
+ const sourceLiteral = JSON.stringify(source);
704
+ const filesLiteral = JSON.stringify(mountedFiles(request));
705
+ return String.raw`
706
+ import asyncio
707
+ import base64
708
+ import contextlib
709
+ import inspect
710
+ import io
711
+ import json
712
+ import os
713
+ import sys
714
+ import time
715
+ import types
716
+ from js import Object
717
+ from pyodide.ffi import to_js as pyodide_to_js
718
+ from workers import WorkerEntrypoint
719
+
720
+ SKILL_SOURCE = ${sourceLiteral}
721
+ SKILL_FILES = ${filesLiteral}
722
+
723
+ async def maybe_await(value):
724
+ if inspect.isawaitable(value):
725
+ return await value
726
+ return value
727
+
728
+ async def decode_host_response(raw):
729
+ data = json.loads(str(raw))
730
+ if "error" in data:
731
+ raise Exception(data["error"])
732
+ return data.get("result")
733
+
734
+ def to_js(obj):
735
+ return pyodide_to_js(obj, dict_converter=Object.fromEntries)
736
+
737
+ def materialize_files():
738
+ os.makedirs("/output", exist_ok=True)
739
+ for path, file in SKILL_FILES.items():
740
+ directory = os.path.dirname(path)
741
+ if directory:
742
+ os.makedirs(directory, exist_ok=True)
743
+ mode = "wb" if file.get("encoding") == "base64" else "w"
744
+ with open(path, mode) as handle:
745
+ if file.get("encoding") == "base64":
746
+ handle.write(base64.b64decode(file.get("content", "")))
747
+ else:
748
+ handle.write(file.get("content", ""))
749
+
750
+ def collect_output_files():
751
+ output_files = []
752
+ if not os.path.isdir("/output"):
753
+ return output_files
754
+
755
+ for root, _dirs, files in os.walk("/output"):
756
+ for name in sorted(files):
757
+ path = os.path.join(root, name)
758
+ with open(path, "rb") as handle:
759
+ content = handle.read()
760
+ if len(content) > ${MAX_OUTPUT_ARTIFACT_BYTES}:
761
+ raise Exception(f"Output artifact exceeds ${MAX_OUTPUT_ARTIFACT_BYTES} bytes: {path}")
762
+ try:
763
+ output_files.append({
764
+ "path": path,
765
+ "encoding": "text",
766
+ "content": content.decode("utf-8")
767
+ })
768
+ except UnicodeDecodeError:
769
+ output_files.append({
770
+ "path": path,
771
+ "encoding": "base64",
772
+ "content": base64.b64encode(content).decode("ascii")
773
+ })
774
+
775
+ return sorted(output_files, key=lambda file: file["path"])
776
+
777
+ def looks_function_style(source):
778
+ return "def run(" in source or "async def run(" in source
779
+
780
+ def timeout_trace(deadline):
781
+ def trace(frame, event, arg):
782
+ if time.monotonic() > deadline:
783
+ raise TimeoutError("Python script execution timed out")
784
+ return trace
785
+ return trace
786
+
787
+ class ToolNamespace:
788
+ def __init__(self, host):
789
+ self.host = host
790
+
791
+ async def call(self, name, input=None):
792
+ raw = await self.host.tool(name, json.dumps(input if input is not None else {}))
793
+ return await decode_host_response(raw)
794
+
795
+ def __getattr__(self, name):
796
+ async def call_tool(input=None):
797
+ return await self.call(name, input)
798
+ return call_tool
799
+
800
+ class WorkspaceNamespace:
801
+ def __init__(self, host):
802
+ self.host = host
803
+
804
+ async def read_file(self, path):
805
+ raw = await self.host.workspaceReadFile(path)
806
+ return await decode_host_response(raw)
807
+
808
+ async def list_files(self, path="."):
809
+ raw = await self.host.workspaceListFiles(path)
810
+ return await decode_host_response(raw)
811
+
812
+ async def glob(self, pattern):
813
+ raw = await self.host.workspaceGlob(pattern)
814
+ return await decode_host_response(raw)
815
+
816
+ async def write_file(self, path, content):
817
+ raw = await self.host.workspaceWriteFile(path, content)
818
+ return await decode_host_response(raw)
819
+
820
+ class Default(WorkerEntrypoint):
821
+ async def evaluate(self, input, ctx, host, timeout_ms=None):
822
+ materialize_files()
823
+ try:
824
+ if looks_function_style(SKILL_SOURCE):
825
+ skill_module = types.ModuleType("skill_script")
826
+ skill_module.tools = ToolNamespace(host)
827
+ skill_module.workspace = WorkspaceNamespace(host)
828
+ exec(SKILL_SOURCE, skill_module.__dict__)
829
+ if not hasattr(skill_module, "run") or not callable(skill_module.run):
830
+ raise Exception("Python function-style skill script must define a callable run(input, ctx).")
831
+ execution = maybe_await(skill_module.run(input, ctx))
832
+ previous_trace = sys.gettrace()
833
+ if timeout_ms is not None:
834
+ sys.settrace(timeout_trace(time.monotonic() + (timeout_ms / 1000)))
835
+ try:
836
+ if timeout_ms is not None:
837
+ result = await asyncio.wait_for(execution, timeout_ms / 1000)
838
+ else:
839
+ result = await execution
840
+ finally:
841
+ sys.settrace(previous_trace)
842
+ return to_js({
843
+ "result": result,
844
+ "logs": [],
845
+ "mode": "function",
846
+ "outputFiles": collect_output_files()
847
+ })
848
+
849
+ stdout = io.StringIO()
850
+ stderr = io.StringIO()
851
+ previous_stdin = sys.stdin
852
+ previous_trace = sys.gettrace()
853
+ if timeout_ms is not None:
854
+ sys.settrace(timeout_trace(time.monotonic() + (timeout_ms / 1000)))
855
+ sys.stdin = io.StringIO(json.dumps(input))
856
+ try:
857
+ namespace = {"__name__": "__main__", "__file__": "/skill/script.py"}
858
+ with contextlib.redirect_stdout(stdout), contextlib.redirect_stderr(stderr):
859
+ exec(SKILL_SOURCE, namespace)
860
+ finally:
861
+ sys.stdin = previous_stdin
862
+ sys.settrace(previous_trace)
863
+ return to_js({
864
+ "result": {
865
+ "stdout": stdout.getvalue(),
866
+ "stderr": stderr.getvalue(),
867
+ "exitCode": 0
868
+ },
869
+ "logs": [],
870
+ "mode": "cli",
871
+ "outputFiles": collect_output_files()
872
+ })
873
+ except TimeoutError:
874
+ return to_js({"error": "Python script execution timed out", "logs": []})
875
+ except SystemExit as err:
876
+ return to_js({
877
+ "result": {
878
+ "stdout": stdout.getvalue() if "stdout" in locals() else "",
879
+ "stderr": stderr.getvalue() if "stderr" in locals() else "",
880
+ "exitCode": int(err.code) if isinstance(err.code, int) else 1
881
+ },
882
+ "logs": [],
883
+ "mode": "cli",
884
+ "outputFiles": collect_output_files()
885
+ })
886
+ except asyncio.TimeoutError:
887
+ return to_js({"error": "Python script execution timed out", "logs": []})
888
+ except Exception as err:
889
+ return to_js({"error": str(err), "logs": []})
890
+ `;
891
+ }
892
+ async function runPythonScript(request, options, bridge) {
893
+ const execution = options.loader.get(`skill-python-${crypto.randomUUID()}`, () => ({
894
+ compatibilityDate: "2026-05-23",
895
+ compatibilityFlags: ["python_workers", "disable_python_external_sdk"],
896
+ mainModule: "skill_runner.py",
897
+ modules: { "skill_runner.py": pythonScriptModule(request) },
898
+ globalOutbound: options.network ? void 0 : null
899
+ })).getEntrypoint().evaluate(request.input, skillScriptContext(request), bridge, effectiveTimeout(options));
900
+ let timeout = null;
901
+ const timeoutPromise = new Promise((_, reject) => {
902
+ timeout = setTimeout(() => reject(/* @__PURE__ */ new Error("Python script execution timed out")), effectiveTimeout(options));
903
+ });
904
+ try {
905
+ const response = await Promise.race([execution, timeoutPromise]);
906
+ if (response.error) throw new Error(response.error);
907
+ const outputFiles = response.outputFiles ?? [];
908
+ if (response.mode === "cli") {
909
+ if (typeof response.result === "object" && response.result !== null && outputFiles.length > 0) return {
910
+ ...response.result,
911
+ outputFiles
912
+ };
913
+ return response.result;
914
+ }
915
+ if (response.logs?.length || outputFiles.length > 0) return {
916
+ result: response.result,
917
+ ...response.logs?.length ? { logs: response.logs } : {},
918
+ ...outputFiles.length > 0 ? { outputFiles } : {}
919
+ };
920
+ return response.result;
921
+ } finally {
922
+ if (timeout) clearTimeout(timeout);
923
+ }
924
+ }
925
+ async function runBashScript(request, options, bridge) {
926
+ const { Bash, defineCommand } = await import("just-bash");
927
+ const customCommands = [];
928
+ if (bridge.workspaceAccess !== "none") {
929
+ customCommands.push(defineCommand("workspace-read", async (args) => {
930
+ const path = args[0];
931
+ if (!path) return {
932
+ stdout: "",
933
+ stderr: "Missing path\n",
934
+ exitCode: 2
935
+ };
936
+ try {
937
+ return {
938
+ stdout: await bridge.readFile(path) ?? "",
939
+ stderr: "",
940
+ exitCode: 0
941
+ };
942
+ } catch (error) {
943
+ return {
944
+ stdout: "",
945
+ stderr: `${error instanceof Error ? error.message : String(error)}\n`,
946
+ exitCode: 1
947
+ };
948
+ }
949
+ }), defineCommand("workspace-list", async (args) => {
950
+ const path = args[0] ?? ".";
951
+ try {
952
+ return {
953
+ stdout: JSON.stringify(await bridge.listFiles(path)) + "\n",
954
+ stderr: "",
955
+ exitCode: 0
956
+ };
957
+ } catch (error) {
958
+ return {
959
+ stdout: "",
960
+ stderr: `${error instanceof Error ? error.message : String(error)}\n`,
961
+ exitCode: 1
962
+ };
963
+ }
964
+ }), defineCommand("workspace-glob", async (args) => {
965
+ const pattern = args[0];
966
+ if (!pattern) return {
967
+ stdout: "",
968
+ stderr: "Missing pattern\n",
969
+ exitCode: 2
970
+ };
971
+ try {
972
+ return {
973
+ stdout: JSON.stringify(await bridge.glob(pattern)) + "\n",
974
+ stderr: "",
975
+ exitCode: 0
976
+ };
977
+ } catch (error) {
978
+ return {
979
+ stdout: "",
980
+ stderr: `${error instanceof Error ? error.message : String(error)}\n`,
981
+ exitCode: 1
982
+ };
983
+ }
984
+ }));
985
+ if (bridge.workspaceAccess === "read-write") customCommands.push(defineCommand("workspace-write", async (args, ctx) => {
986
+ const path = args[0];
987
+ if (!path) return {
988
+ stdout: "",
989
+ stderr: "Missing path\n",
990
+ exitCode: 2
991
+ };
992
+ try {
993
+ await bridge.writeFile(path, stdinText(ctx.stdin));
994
+ return {
995
+ stdout: "",
996
+ stderr: "",
997
+ exitCode: 0
998
+ };
999
+ } catch (error) {
1000
+ return {
1001
+ stdout: "",
1002
+ stderr: `${error instanceof Error ? error.message : String(error)}\n`,
1003
+ exitCode: 1
1004
+ };
1005
+ }
1006
+ }));
1007
+ }
1008
+ if (bridge.hasTools()) customCommands.push(defineCommand("tool", async (args, ctx) => {
1009
+ const name = args[0];
1010
+ if (!name) return {
1011
+ stdout: "",
1012
+ stderr: "Missing tool name\n",
1013
+ exitCode: 2
1014
+ };
1015
+ try {
1016
+ const rawInput = args[1] ?? stdinText(ctx.stdin) ?? "{}";
1017
+ const input = rawInput.trim() ? JSON.parse(rawInput) : {};
1018
+ const result = await bridge.callTool(name, input);
1019
+ return {
1020
+ stdout: JSON.stringify(result) + "\n",
1021
+ stderr: "",
1022
+ exitCode: 0
1023
+ };
1024
+ } catch (error) {
1025
+ return {
1026
+ stdout: "",
1027
+ stderr: `${error instanceof Error ? error.message : String(error)}\n`,
1028
+ exitCode: 1
1029
+ };
1030
+ }
1031
+ }));
1032
+ const controller = new AbortController();
1033
+ const timeout = setTimeout(() => controller.abort(), effectiveTimeout(options));
1034
+ try {
1035
+ const result = await new Bash({
1036
+ files: bashFiles(request),
1037
+ customCommands,
1038
+ defenseInDepth: true,
1039
+ network: options.network ? {} : void 0
1040
+ }).exec("bash /skill-script.sh", {
1041
+ signal: controller.signal,
1042
+ stdin: JSON.stringify(request.input)
1043
+ });
1044
+ return {
1045
+ stdout: result.stdout,
1046
+ stderr: result.stderr,
1047
+ exitCode: result.exitCode
1048
+ };
1049
+ } finally {
1050
+ if (timeout) clearTimeout(timeout);
1051
+ }
1052
+ }
1053
+ async function runJavaScriptScript(request, options, bridge, runtime) {
1054
+ if (!/^\s*export\s+default\s+/m.test(request.source)) throw new Error("JS/TS skill scripts must `export default` an async run(input, ctx) function.");
1055
+ const { DynamicWorkerExecutor, resolveProvider } = await import("@cloudflare/codemode");
1056
+ const source = await prepareJavaScriptSource(request, runtime);
1057
+ const result = await new DynamicWorkerExecutor({
1058
+ loader: options.loader,
1059
+ timeout: effectiveTimeout(options),
1060
+ globalOutbound: options.network ? void 0 : null
1061
+ }).execute(scriptModule(source, request), [resolveProvider(hostProvider(bridge))]);
1062
+ if (result.error) {
1063
+ const logs = result.logs?.length ? `\n\nConsole output:\n${result.logs.join("\n")}` : "";
1064
+ throw new Error(`${result.error}${logs}`);
1065
+ }
1066
+ const outputFiles = bridge.getOutputFiles();
1067
+ if (result.logs?.length || outputFiles.length > 0) return {
1068
+ result: result.result,
1069
+ ...result.logs?.length ? { logs: result.logs } : {},
1070
+ ...outputFiles.length > 0 ? { outputFiles } : {}
1071
+ };
1072
+ return result.result;
1073
+ }
1074
+ /**
1075
+ * Create a skill script runner backed by a Worker Loader.
1076
+ *
1077
+ * Capabilities are opt-in and enforced by a single host bridge: no network and
1078
+ * no tools by default, read-only workspace access when `workspaceInstance` is
1079
+ * provided. JS/TS scripts are function-style (`export default run(input, ctx)`)
1080
+ * and receive `ctx = { skill, files, workspace, tools, output }`. Python and
1081
+ * Bash use the path-based `/skill`, `/input.json`, `/output` contract.
1082
+ *
1083
+ * @experimental Skill script execution is experimental and may change before
1084
+ * stabilizing.
1085
+ */
1086
+ function runner(options) {
1087
+ return { async run(request) {
1088
+ if (!runnerExperimentalWarned) {
1089
+ runnerExperimentalWarned = true;
1090
+ console.warn("[think] skills.runner script execution is experimental; the API and capabilities may change.");
1091
+ }
1092
+ const tools = typeof options.tools === "function" ? await options.tools() : options.tools;
1093
+ const validation = validateSkillScriptPath(request.path);
1094
+ if (!validation.ok) throw new Error(validation.error);
1095
+ validateMountedResourcePaths(request);
1096
+ const bridge = new SkillScriptHostBridge(tools, options.workspaceInstance, effectiveWorkspaceAccess(options));
1097
+ if (validation.runtime === "bash") return await runBashScript(request, options, bridge);
1098
+ if (validation.runtime === "python") return await runPythonScript(request, options, bridge);
1099
+ return await runJavaScriptScript(request, options, bridge, validation.runtime);
1100
+ } };
1101
+ }
1102
+ //#endregion
1103
+ //#region src/skills/registry.ts
1104
+ const SKILL_CONTEXT_LABEL = "think_skills";
1105
+ function stableSourceFingerprint(sources) {
1106
+ return sources.map((source) => `${source.id}:${source.fingerprint}`).join("|");
1107
+ }
1108
+ function wrapSkillContent(skill) {
1109
+ const version = skill.version ? ` version="${skill.version}"` : "";
1110
+ const resourceList = skill.resources?.length ? [
1111
+ "",
1112
+ "<skill_resources>",
1113
+ ...skill.resources.map((resource) => ` <file kind="${resource.kind}" encoding="${resource.encoding ?? "text"}"${resource.size === void 0 ? "" : ` size="${resource.size}"`}>${resource.path}</file>`),
1114
+ "</skill_resources>"
1115
+ ].join("\n") : "";
1116
+ return [
1117
+ `<skill_content name="${skill.name}"${version}>`,
1118
+ skill.body.trim(),
1119
+ resourceList,
1120
+ "</skill_content>"
1121
+ ].join("\n");
1122
+ }
1123
+ function renderResourceList(resources) {
1124
+ if (!resources?.length) return "No bundled resources.";
1125
+ return resources.map((resource) => {
1126
+ const encoding = resource.encoding ?? "text";
1127
+ const size = resource.size === void 0 ? "" : `, ${resource.size} bytes`;
1128
+ const mimeType = resource.mimeType ? `, ${resource.mimeType}` : "";
1129
+ return `- ${resource.path} (${resource.kind}, ${encoding}${mimeType}${size})`;
1130
+ }).join("\n");
1131
+ }
1132
+ function validateResourcePath(path) {
1133
+ if (path.startsWith("../")) return `Resource paths cannot use "../". To read from another skill, use a qualified path like "other-skill/references/file.md".`;
1134
+ return validateSkillResourcePath(path);
1135
+ }
1136
+ var SkillRegistry = class {
1137
+ constructor(sources, scriptRunner = null) {
1138
+ this.contextLabel = SKILL_CONTEXT_LABEL;
1139
+ this.warnings = [];
1140
+ this.descriptors = /* @__PURE__ */ new Map();
1141
+ this.sourceBySkill = /* @__PURE__ */ new Map();
1142
+ this.loaded = false;
1143
+ this.sources = sources;
1144
+ this.scriptRunner = scriptRunner;
1145
+ }
1146
+ get fingerprint() {
1147
+ return stableSourceFingerprint(this.sources);
1148
+ }
1149
+ async load() {
1150
+ if (this.loaded) return;
1151
+ this.descriptors.clear();
1152
+ this.sourceBySkill.clear();
1153
+ this.warnings.length = 0;
1154
+ for (const source of this.sources) {
1155
+ let descriptors;
1156
+ try {
1157
+ descriptors = await source.list();
1158
+ } catch (error) {
1159
+ this.warnings.push(`Skill source "${source.id}" failed to list skills and was skipped: ${error instanceof Error ? error.message : String(error)}`);
1160
+ continue;
1161
+ }
1162
+ for (const descriptor of descriptors) {
1163
+ const existing = this.descriptors.get(descriptor.name);
1164
+ if (existing) {
1165
+ this.warnings.push(`Duplicate skill "${descriptor.name}" from ${source.id} ignored; already registered from ${existing.sourceId}.`);
1166
+ continue;
1167
+ }
1168
+ this.descriptors.set(descriptor.name, {
1169
+ ...descriptor,
1170
+ sourceId: descriptor.sourceId ?? source.id
1171
+ });
1172
+ this.sourceBySkill.set(descriptor.name, source);
1173
+ }
1174
+ }
1175
+ this.loaded = true;
1176
+ }
1177
+ async refresh() {
1178
+ const refreshErrors = [];
1179
+ await Promise.all(this.sources.map(async (source) => {
1180
+ try {
1181
+ await source.refresh?.();
1182
+ } catch (error) {
1183
+ refreshErrors.push(`Skill source "${source.id}" failed to refresh: ${error instanceof Error ? error.message : String(error)}`);
1184
+ }
1185
+ }));
1186
+ this.loaded = false;
1187
+ await this.load();
1188
+ this.warnings.push(...refreshErrors);
1189
+ }
1190
+ async snapshot() {
1191
+ await this.load();
1192
+ const catalog = [];
1193
+ for (const descriptor of this.descriptors.values()) catalog.push(`- ${descriptor.name}: ${descriptor.description}`);
1194
+ return {
1195
+ fingerprint: this.fingerprint,
1196
+ catalogPrompt: catalog.length ? [
1197
+ "Available skills. When a task matches a skill, use activate_skill with its name before proceeding.",
1198
+ "",
1199
+ ...catalog
1200
+ ].join("\n") : null
1201
+ };
1202
+ }
1203
+ async systemPrompt() {
1204
+ return (await this.snapshot()).catalogPrompt;
1205
+ }
1206
+ async loadSkill(name) {
1207
+ await this.load();
1208
+ const source = this.sourceBySkill.get(name);
1209
+ return source ? source.load(name) : null;
1210
+ }
1211
+ resolveResourceTarget(name, path) {
1212
+ const pathError = validateResourcePath(path);
1213
+ if (pathError) return {
1214
+ ok: false,
1215
+ error: pathError
1216
+ };
1217
+ if (name) return {
1218
+ ok: true,
1219
+ name,
1220
+ path
1221
+ };
1222
+ const [candidateName, ...rest] = path.split("/");
1223
+ if (!candidateName || rest.length === 0) return {
1224
+ ok: false,
1225
+ error: "Resource path must include a skill name when name is omitted, for example: cloudflare-brand/references/tokens.md"
1226
+ };
1227
+ if (!this.descriptors.has(candidateName)) return {
1228
+ ok: false,
1229
+ error: `Unknown skill in qualified resource path: ${candidateName}`
1230
+ };
1231
+ return {
1232
+ ok: true,
1233
+ name: candidateName,
1234
+ path: rest.join("/")
1235
+ };
1236
+ }
1237
+ async readResource(name, path) {
1238
+ const source = this.sourceBySkill.get(name);
1239
+ if (!source?.readResource) return `Skill "${name}" has no readable resources.`;
1240
+ return await source.readResource(name, path) ?? `Resource not found: ${name}/${path}`;
1241
+ }
1242
+ async readSkillResources(skill) {
1243
+ const resources = [];
1244
+ for (const descriptor of skill.resources ?? []) {
1245
+ const resource = await this.readResource(skill.name, descriptor.path);
1246
+ if (typeof resource !== "string") resources.push(resource);
1247
+ }
1248
+ return resources;
1249
+ }
1250
+ tools() {
1251
+ const modelSkillNames = [...this.descriptors.values()].map((skill) => skill.name);
1252
+ const tools = {};
1253
+ if (modelSkillNames.length > 0) tools.activate_skill = tool({
1254
+ description: "Activate a skill by name. Use this when the user's task matches one of the available skills.",
1255
+ inputSchema: z.object({ name: z.enum(modelSkillNames) }),
1256
+ execute: async ({ name }) => {
1257
+ const skill = await this.loadSkill(name);
1258
+ if (!skill) return `Skill not found: ${name}`;
1259
+ return [
1260
+ wrapSkillContent(skill),
1261
+ "",
1262
+ "Bundled resources:",
1263
+ renderResourceList(skill.resources)
1264
+ ].join("\n");
1265
+ }
1266
+ });
1267
+ if (modelSkillNames.length > 0) tools.read_skill_resource = tool({
1268
+ description: "Read a bundled resource from an available skill by relative path. Pass name and path, or use a qualified path like skill-name/references/file.md.",
1269
+ inputSchema: z.object({
1270
+ name: z.enum(modelSkillNames).optional(),
1271
+ path: z.string().min(1)
1272
+ }),
1273
+ execute: async ({ name, path }) => {
1274
+ const target = this.resolveResourceTarget(name, path);
1275
+ if (!target.ok) return target.error;
1276
+ const resource = await this.readResource(target.name, target.path);
1277
+ if (typeof resource === "string") return resource;
1278
+ const encoding = resource.encoding ?? "text";
1279
+ const mimeType = resource.mimeType ? ` mimeType="${resource.mimeType}"` : "";
1280
+ return [
1281
+ `<skill_resource name="${target.name}" path="${resource.path}" kind="${resource.kind}" encoding="${encoding}"${mimeType}>`,
1282
+ resource.content,
1283
+ "</skill_resource>"
1284
+ ].join("\n");
1285
+ }
1286
+ });
1287
+ if (modelSkillNames.length > 0 && this.scriptRunner) tools.run_skill_script = tool({
1288
+ description: "Run a bundled script resource from an available skill. Use only when a skill instructs you to run a script.",
1289
+ inputSchema: z.object({
1290
+ name: z.enum(modelSkillNames),
1291
+ path: z.string().min(1),
1292
+ input: z.unknown().default({})
1293
+ }),
1294
+ execute: async ({ name, path, input = {} }) => {
1295
+ const validation = validateSkillScriptPath(path);
1296
+ if (!validation.ok) return validation.error;
1297
+ const skill = await this.loadSkill(name);
1298
+ if (!skill) return `Skill not found: ${name}`;
1299
+ const script = skill.resources?.find((resource) => resource.path === path);
1300
+ if (!script) return `Script not found: ${name}/${path}`;
1301
+ if (script.kind !== "script") return `Resource is not a script: ${name}/${path}`;
1302
+ const source = this.sourceBySkill.get(name);
1303
+ if (!source?.readResource) return `Skill "${name}" has no readable resources.`;
1304
+ const resource = await source.readResource(name, path);
1305
+ if (!resource) return `Script not found: ${name}/${path}`;
1306
+ if ((resource.encoding ?? "text") !== "text") return `Script resource must be text, got ${resource.encoding}: ${name}/${path}`;
1307
+ try {
1308
+ return await this.scriptRunner.run({
1309
+ skill,
1310
+ path,
1311
+ source: resource.content,
1312
+ input,
1313
+ resources: await this.readSkillResources(skill)
1314
+ });
1315
+ } catch (error) {
1316
+ return `Skill script failed: ${error instanceof Error ? error.message : String(error)}`;
1317
+ }
1318
+ }
1319
+ });
1320
+ return tools;
1321
+ }
1322
+ };
1323
+ //#endregion
1324
+ export { SkillRegistry, fromManifest, parseSkillFrontmatter, parseSkillMarkdown, r2, runner };
1325
+
1326
+ //# sourceMappingURL=index.js.map