@yolk-sdk/codemode 0.1.0-canary.96
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +132 -0
- package/dist/catalog.d.mts +60 -0
- package/dist/catalog.d.mts.map +1 -0
- package/dist/catalog.mjs +143 -0
- package/dist/catalog.mjs.map +1 -0
- package/dist/classifier-tool.d.mts +78 -0
- package/dist/classifier-tool.d.mts.map +1 -0
- package/dist/classifier-tool.mjs +125 -0
- package/dist/classifier-tool.mjs.map +1 -0
- package/dist/executor.d.mts +89 -0
- package/dist/executor.d.mts.map +1 -0
- package/dist/executor.mjs +1 -0
- package/dist/index.d.mts +8 -0
- package/dist/index.mjs +7 -0
- package/dist/node.d.mts +34 -0
- package/dist/node.d.mts.map +1 -0
- package/dist/node.mjs +215 -0
- package/dist/node.mjs.map +1 -0
- package/dist/output.d.mts +42 -0
- package/dist/output.d.mts.map +1 -0
- package/dist/output.mjs +184 -0
- package/dist/output.mjs.map +1 -0
- package/dist/search.d.mts +23 -0
- package/dist/search.d.mts.map +1 -0
- package/dist/search.mjs +58 -0
- package/dist/search.mjs.map +1 -0
- package/dist/store.d.mts +40 -0
- package/dist/store.d.mts.map +1 -0
- package/dist/store.mjs +61 -0
- package/dist/store.mjs.map +1 -0
- package/dist/tool.d.mts +75 -0
- package/dist/tool.d.mts.map +1 -0
- package/dist/tool.mjs +282 -0
- package/dist/tool.mjs.map +1 -0
- package/package.json +63 -0
- package/src/catalog.ts +274 -0
- package/src/classifier-tool.ts +227 -0
- package/src/executor.ts +103 -0
- package/src/index.ts +76 -0
- package/src/node.ts +334 -0
- package/src/output.ts +287 -0
- package/src/search.ts +113 -0
- package/src/store.ts +93 -0
- package/src/tool.ts +547 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.mjs","names":[],"sources":["../src/output.ts"],"sourcesContent":["import {\n ImagePart,\n inlineBase64AttachmentSource,\n TextPart,\n type Content,\n type NestedToolCallStatus\n} from '@yolk-sdk/agent/protocol'\nimport type { CodeModeExecutionResult, CodeModeOutputItem } from './executor.ts'\n\n/** Text or image piece of the model-visible result, in order. */\nexport type CodeModeResultSegment = CodeModeOutputItem\n\nconst isHighSurrogate = (code: number) => code >= 0xd800 && code <= 0xdbff\n\nconst isLowSurrogate = (code: number) => code >= 0xdc00 && code <= 0xdfff\n\n// Cut points never split a surrogate pair.\nconst headCut = (text: string, length: number) =>\n length > 0 && length < text.length && isHighSurrogate(text.charCodeAt(length - 1))\n ? length - 1\n : length\n\nconst tailCut = (text: string, start: number) =>\n start > 0 && start < text.length && isLowSurrogate(text.charCodeAt(start)) ? start + 1 : start\n\nconst textLength = (segments: ReadonlyArray<CodeModeResultSegment>) =>\n segments.reduce(\n (total, segment) => total + (segment.type === 'text' ? segment.text.length : 0),\n 0\n )\n\nconst omissionMarker = (characters: number, images: number) =>\n `\\n\\n[… ${characters} characters${images > 0 ? ` and ${images} image${images === 1 ? '' : 's'}` : ''} omitted …]\\n\\n`\n\nconst markerReserve = 80\n\n/**\n * Keeps at most about `maxChars` characters of text: the head and the tail of the segments, with an\n * omission marker in between. Images inside the omitted middle are dropped and counted.\n */\nexport const boundCodeModeSegments = (\n segments: ReadonlyArray<CodeModeResultSegment>,\n maxChars: number\n): ReadonlyArray<CodeModeResultSegment> => {\n const total = textLength(segments)\n\n if (total <= maxChars) return segments\n\n const budget = Math.max(0, maxChars - markerReserve)\n let headBudget = Math.ceil(budget / 2)\n let tailBudget = budget - headBudget\n\n const head: Array<CodeModeResultSegment> = []\n let headIndex = 0\n let headPartial = 0\n\n for (; headIndex < segments.length && headBudget > 0; headIndex++) {\n const segment = segments[headIndex]\n\n if (segment === undefined) break\n\n if (segment.type === 'image') {\n head.push(segment)\n continue\n }\n\n if (segment.text.length <= headBudget) {\n head.push(segment)\n headBudget -= segment.text.length\n continue\n }\n\n headPartial = headCut(segment.text, headBudget)\n head.push({ type: 'text', text: segment.text.slice(0, headPartial) })\n headBudget = 0\n break\n }\n\n const tail: Array<CodeModeResultSegment> = []\n let tailIndex = segments.length - 1\n let tailStart: number | undefined\n\n for (; tailIndex >= headIndex && tailBudget > 0; tailIndex--) {\n const segment = segments[tailIndex]\n\n if (segment === undefined) break\n\n if (segment.type === 'image') {\n if (tailIndex === headIndex) break\n tail.unshift(segment)\n continue\n }\n\n const available =\n tailIndex === headIndex ? segment.text.length - headPartial : segment.text.length\n\n if (available <= tailBudget && tailIndex !== headIndex) {\n tail.unshift(segment)\n tailBudget -= available\n continue\n }\n\n tailStart = tailCut(segment.text, segment.text.length - Math.min(available, tailBudget))\n tail.unshift({ type: 'text', text: segment.text.slice(tailStart) })\n tailBudget = 0\n break\n }\n\n const keptText = textLength(head) + textLength(tail)\n const keptImages = [...head, ...tail].filter(segment => segment.type === 'image').length\n const allImages = segments.filter(segment => segment.type === 'image').length\n\n return [\n ...head,\n { type: 'text', text: omissionMarker(total - keptText, allImages - keptImages) },\n ...tail\n ]\n}\n\nconst mergeText = (segments: ReadonlyArray<CodeModeResultSegment>) =>\n segments.reduce<Array<CodeModeResultSegment>>((merged, segment) => {\n const last = merged.at(-1)\n\n if (segment.type === 'text' && last?.type === 'text') {\n merged[merged.length - 1] = { type: 'text', text: last.text + segment.text }\n } else if (segment.type === 'image' || segment.text.length > 0) {\n merged.push(segment)\n }\n\n return merged\n }, [])\n\n/** Agent `Content`: a plain string without images, ordered text and image parts otherwise. */\nexport const codeModeSegmentsContent = (\n segments: ReadonlyArray<CodeModeResultSegment>\n): Content => {\n const merged = mergeText(segments)\n\n if (merged.every(segment => segment.type === 'text')) {\n return merged.map(segment => (segment.type === 'text' ? segment.text : '')).join('')\n }\n\n return merged.map(segment =>\n segment.type === 'text'\n ? TextPart.make({ text: segment.text })\n : ImagePart.make({\n source: inlineBase64AttachmentSource(segment.data),\n mimeType: segment.mimeType\n })\n )\n}\n\nexport type CodeModeCallSummary = {\n readonly name: string\n readonly status: NestedToolCallStatus\n}\n\nconst statusOrder: ReadonlyArray<NestedToolCallStatus> = ['ok', 'error', 'cancelled']\n\n/** `name: 2 ok, 1 error; other: 1 cancelled`, by first appearance. */\nexport const summarizeCodeModeCalls = (calls: ReadonlyArray<CodeModeCallSummary>): string => {\n if (calls.length === 0) return 'none'\n\n const byName = new Map<string, Map<NestedToolCallStatus, number>>()\n\n for (const call of calls) {\n const counts = byName.get(call.name) ?? new Map<NestedToolCallStatus, number>()\n\n counts.set(call.status, (counts.get(call.status) ?? 0) + 1)\n byName.set(call.name, counts)\n }\n\n return [...byName]\n .map(\n ([name, counts]) =>\n `${name}: ${statusOrder\n .flatMap(status => {\n const count = counts.get(status)\n\n return count === undefined ? [] : [`${count} ${status}`]\n })\n .join(', ')}`\n )\n .join('; ')\n}\n\nconst stackFrames = (stack: string | undefined) =>\n stack === undefined\n ? []\n : stack.split('\\n').filter(line => /^\\s+at\\s/.test(line) || /^\\s*\\S+:\\d+/.test(line))\n\nconst encodeValue = (value: unknown): string => {\n try {\n return JSON.stringify(value) ?? 'undefined'\n } catch {\n return '[unserializable value]'\n }\n}\n\n/** Default cap of images kept in one result. */\nexport const defaultCodeModeMaxImages = 8\n\n/** Default cap of base64 image data kept in one result, in characters (4 MiB). */\nexport const defaultCodeModeMaxImageBytes = 4 * 1024 * 1024\n\nconst imageOmissionNote = (dropped: number, maxImages: number, maxImageBytes: number) =>\n `\\n\\n[… ${dropped} image${dropped === 1 ? '' : 's'} omitted: a result keeps at most ${maxImages} images and ${maxImageBytes} characters of base64 image data …]`\n\n/**\n * Model-visible segments of one execution: a `Script completed` / `Script failed` header with the\n * wall time, the output in order, the JSON return value (omitted when undefined), and for failures\n * the error and the tool calls already made. Images are kept in order while they fit `maxImages`\n * (default 8) and `maxImageBytes` of base64 in total (default 4 MiB); an image that would exceed\n * either is dropped and counted in an omission note after the output. The result is then bounded\n * by `maxChars` with a head-and-tail cut, which also drops images in the omitted middle.\n */\nexport const codeModeResultSegments = (input: {\n readonly result: CodeModeExecutionResult\n readonly wallTimeMs: number\n readonly calls: ReadonlyArray<CodeModeCallSummary>\n readonly maxChars: number\n readonly maxImages?: number\n readonly maxImageBytes?: number\n}): ReadonlyArray<CodeModeResultSegment> => {\n const { result } = input\n const maxImages = Math.max(0, Math.floor(input.maxImages ?? defaultCodeModeMaxImages))\n const maxImageBytes = Math.max(0, Math.floor(input.maxImageBytes ?? defaultCodeModeMaxImageBytes))\n const ms = Math.round(input.wallTimeMs)\n const segments: Array<CodeModeResultSegment> = []\n\n const text = (value: string) => {\n segments.push({ type: 'text', text: value })\n }\n\n text(result.ok ? `Script completed in ${ms} ms.` : `Script failed after ${ms} ms.`)\n\n if (result.output.length > 0) {\n text('\\n\\nOutput:\\n')\n\n let keptImages = 0\n let keptImageBytes = 0\n let droppedImages = 0\n\n result.output.forEach((item, index) => {\n const previous = result.output[index - 1]\n\n if (item.type === 'text') {\n text(previous?.type === 'text' ? `\\n${item.text}` : item.text)\n } else if (keptImages < maxImages && keptImageBytes + item.data.length <= maxImageBytes) {\n keptImages++\n keptImageBytes += item.data.length\n segments.push(item)\n } else {\n droppedImages++\n }\n })\n\n if (droppedImages > 0) text(imageOmissionNote(droppedImages, maxImages, maxImageBytes))\n }\n\n if (result.ok && result.value !== undefined) {\n text(`\\n\\nReturn value:\\n${encodeValue(result.value)}`)\n }\n\n if (!result.ok) {\n const error = result.error ?? {\n kind: 'sandbox',\n message: 'The script failed without an error.'\n }\n\n text(`\\n\\nScript error: ${error.kind}: ${error.message}`)\n\n const frames = stackFrames(error.stack)\n\n if (frames.length > 0) text(`\\n${frames.join('\\n')}`)\n\n text(\n `\\n\\nTool calls made before the failure (they are not undone): ${summarizeCodeModeCalls(input.calls)}.`\n )\n }\n\n if (result.ok && result.output.length === 0 && result.value === undefined) {\n text(' No output and no return value.')\n }\n\n return boundCodeModeSegments(mergeText(segments), input.maxChars)\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { CodeModeCatalogTool } from "./catalog.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/search.d.ts
|
|
4
|
+
/** Default number of `searchTools` results. */
|
|
5
|
+
declare const defaultCodeModeSearchLimit = 8;
|
|
6
|
+
/** Lowercase word tokens; splits on non-alphanumerics and camelCase boundaries. */
|
|
7
|
+
declare const codeModeSearchTokens: (text: string) => ReadonlyArray<string>;
|
|
8
|
+
type CodeModeSearchOptions = {
|
|
9
|
+
readonly limit?: number;
|
|
10
|
+
readonly namespace?: string;
|
|
11
|
+
};
|
|
12
|
+
type CodeModeSearchHit = {
|
|
13
|
+
/** The identifier scripts call as `tools.<name>(args)`. */readonly name: string;
|
|
14
|
+
readonly description: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* BM25 over each tool's identifier, raw name, description, namespace, and namespace description. Ties keep catalog order.
|
|
18
|
+
* A query without words lists the (namespace-filtered) tools in catalog order.
|
|
19
|
+
*/
|
|
20
|
+
declare const searchCodeModeTools: (catalog: ReadonlyArray<CodeModeCatalogTool>, query: string, options?: CodeModeSearchOptions) => ReadonlyArray<CodeModeSearchHit>;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { CodeModeSearchHit, CodeModeSearchOptions, codeModeSearchTokens, defaultCodeModeSearchLimit, searchCodeModeTools };
|
|
23
|
+
//# sourceMappingURL=search.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.mts","names":[],"sources":["../src/search.ts"],"mappings":";;;;cAGa,0BAAA;AAAb;AAAA,cAOa,oBAAA,GAAwB,IAAA,aAAe,aAAa;AAAA,KAiCrD,qBAAA;EAAA,SACD,KAAA;EAAA,SACA,SAAS;AAAA;AAAA,KAGR,iBAAA;sEAED,IAAA;EAAA,SACA,WAAW;AAAA;;;;AANF;cAaP,mBAAA,GACX,OAAA,EAAS,aAAA,CAAc,mBAAA,GACvB,KAAA,UACA,OAAA,GAAS,qBAAA,KACR,aAAA,CAAc,iBAAA"}
|
package/dist/search.mjs
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
//#region src/search.ts
|
|
2
|
+
/** Default number of `searchTools` results. */
|
|
3
|
+
const defaultCodeModeSearchLimit = 8;
|
|
4
|
+
const k1 = 1.2;
|
|
5
|
+
const b = .75;
|
|
6
|
+
/** Lowercase word tokens; splits on non-alphanumerics and camelCase boundaries. */
|
|
7
|
+
const codeModeSearchTokens = (text) => text.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/).filter((token) => token.length > 0);
|
|
8
|
+
const indexTool = (tool) => {
|
|
9
|
+
const tokens = codeModeSearchTokens([
|
|
10
|
+
tool.identifier,
|
|
11
|
+
tool.name,
|
|
12
|
+
tool.description,
|
|
13
|
+
tool.namespace,
|
|
14
|
+
tool.namespaceDescription ?? ""
|
|
15
|
+
].join(" "));
|
|
16
|
+
const frequencies = /* @__PURE__ */ new Map();
|
|
17
|
+
for (const token of tokens) frequencies.set(token, (frequencies.get(token) ?? 0) + 1);
|
|
18
|
+
return {
|
|
19
|
+
tool,
|
|
20
|
+
frequencies,
|
|
21
|
+
length: tokens.length
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* BM25 over each tool's identifier, raw name, description, namespace, and namespace description. Ties keep catalog order.
|
|
26
|
+
* A query without words lists the (namespace-filtered) tools in catalog order.
|
|
27
|
+
*/
|
|
28
|
+
const searchCodeModeTools = (catalog, query, options = {}) => {
|
|
29
|
+
const limit = Math.max(0, Math.floor(options.limit ?? 8));
|
|
30
|
+
const documents = (options.namespace === void 0 ? catalog : catalog.filter((tool) => tool.namespace === options.namespace)).map(indexTool);
|
|
31
|
+
const terms = [...new Set(codeModeSearchTokens(query))];
|
|
32
|
+
const hit = (tool) => ({
|
|
33
|
+
name: tool.identifier,
|
|
34
|
+
description: tool.description
|
|
35
|
+
});
|
|
36
|
+
if (terms.length === 0) return documents.slice(0, limit).map((document) => hit(document.tool));
|
|
37
|
+
const averageLength = documents.reduce((total, document) => total + document.length, 0) / Math.max(1, documents.length);
|
|
38
|
+
const inverse = new Map(terms.map((term) => {
|
|
39
|
+
const containing = documents.filter((document) => document.frequencies.has(term)).length;
|
|
40
|
+
return [term, Math.log(1 + (documents.length - containing + .5) / (containing + .5))];
|
|
41
|
+
}));
|
|
42
|
+
return documents.map((document, index) => {
|
|
43
|
+
return {
|
|
44
|
+
document,
|
|
45
|
+
index,
|
|
46
|
+
score: terms.reduce((total, term) => {
|
|
47
|
+
const frequency = document.frequencies.get(term) ?? 0;
|
|
48
|
+
if (frequency === 0) return total;
|
|
49
|
+
const norm = k1 * (1 - b + b * document.length / Math.max(1, averageLength));
|
|
50
|
+
return total + (inverse.get(term) ?? 0) * frequency * (k1 + 1) / (frequency + norm);
|
|
51
|
+
}, 0)
|
|
52
|
+
};
|
|
53
|
+
}).filter((entry) => entry.score > 0).sort((left, right) => right.score - left.score || left.index - right.index).slice(0, limit).map((entry) => hit(entry.document.tool));
|
|
54
|
+
};
|
|
55
|
+
//#endregion
|
|
56
|
+
export { codeModeSearchTokens, defaultCodeModeSearchLimit, searchCodeModeTools };
|
|
57
|
+
|
|
58
|
+
//# sourceMappingURL=search.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.mjs","names":[],"sources":["../src/search.ts"],"sourcesContent":["import type { CodeModeCatalogTool } from './catalog.ts'\n\n/** Default number of `searchTools` results. */\nexport const defaultCodeModeSearchLimit = 8\n\nconst k1 = 1.2\n\nconst b = 0.75\n\n/** Lowercase word tokens; splits on non-alphanumerics and camelCase boundaries. */\nexport const codeModeSearchTokens = (text: string): ReadonlyArray<string> =>\n text\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter(token => token.length > 0)\n\ntype IndexedTool = {\n readonly tool: CodeModeCatalogTool\n readonly frequencies: ReadonlyMap<string, number>\n readonly length: number\n}\n\nconst indexTool = (tool: CodeModeCatalogTool): IndexedTool => {\n const tokens = codeModeSearchTokens(\n [\n tool.identifier,\n tool.name,\n tool.description,\n tool.namespace,\n tool.namespaceDescription ?? ''\n ].join(' ')\n )\n\n const frequencies = new Map<string, number>()\n\n for (const token of tokens) {\n frequencies.set(token, (frequencies.get(token) ?? 0) + 1)\n }\n\n return { tool, frequencies, length: tokens.length }\n}\n\nexport type CodeModeSearchOptions = {\n readonly limit?: number\n readonly namespace?: string\n}\n\nexport type CodeModeSearchHit = {\n /** The identifier scripts call as `tools.<name>(args)`. */\n readonly name: string\n readonly description: string\n}\n\n/**\n * BM25 over each tool's identifier, raw name, description, namespace, and namespace description. Ties keep catalog order.\n * A query without words lists the (namespace-filtered) tools in catalog order.\n */\nexport const searchCodeModeTools = (\n catalog: ReadonlyArray<CodeModeCatalogTool>,\n query: string,\n options: CodeModeSearchOptions = {}\n): ReadonlyArray<CodeModeSearchHit> => {\n const limit = Math.max(0, Math.floor(options.limit ?? defaultCodeModeSearchLimit))\n\n const scoped =\n options.namespace === undefined\n ? catalog\n : catalog.filter(tool => tool.namespace === options.namespace)\n\n const documents = scoped.map(indexTool)\n const terms = [...new Set(codeModeSearchTokens(query))]\n\n const hit = (tool: CodeModeCatalogTool): CodeModeSearchHit => ({\n name: tool.identifier,\n description: tool.description\n })\n\n if (terms.length === 0) {\n return documents.slice(0, limit).map(document => hit(document.tool))\n }\n\n const averageLength =\n documents.reduce((total, document) => total + document.length, 0) /\n Math.max(1, documents.length)\n\n const inverse = new Map(\n terms.map((term): readonly [string, number] => {\n const containing = documents.filter(document => document.frequencies.has(term)).length\n\n return [term, Math.log(1 + (documents.length - containing + 0.5) / (containing + 0.5))]\n })\n )\n\n return documents\n .map((document, index) => {\n const score = terms.reduce((total, term) => {\n const frequency = document.frequencies.get(term) ?? 0\n\n if (frequency === 0) return total\n\n const norm = k1 * (1 - b + (b * document.length) / Math.max(1, averageLength))\n\n return total + ((inverse.get(term) ?? 0) * frequency * (k1 + 1)) / (frequency + norm)\n }, 0)\n\n return { document, index, score }\n })\n .filter(entry => entry.score > 0)\n .sort((left, right) => right.score - left.score || left.index - right.index)\n .slice(0, limit)\n .map(entry => hit(entry.document.tool))\n}\n"],"mappings":";;AAGA,MAAa,6BAA6B;AAE1C,MAAM,KAAK;AAEX,MAAM,IAAI;;AAGV,MAAa,wBAAwB,SACnC,KACG,QAAQ,sBAAsB,OAAO,EACrC,YAAY,EACZ,MAAM,YAAY,EAClB,QAAO,UAAS,MAAM,SAAS,CAAC;AAQrC,MAAM,aAAa,SAA2C;CAC5D,MAAM,SAAS,qBACb;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,wBAAwB;CAC/B,EAAE,KAAK,GAAG,CACZ;CAEA,MAAM,8BAAc,IAAI,IAAoB;CAE5C,KAAK,MAAM,SAAS,QAClB,YAAY,IAAI,QAAQ,YAAY,IAAI,KAAK,KAAK,KAAK,CAAC;CAG1D,OAAO;EAAE;EAAM;EAAa,QAAQ,OAAO;CAAO;AACpD;;;;;AAiBA,MAAa,uBACX,SACA,OACA,UAAiC,CAAC,MACG;CACrC,MAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,SAAA,CAAmC,CAAC;CAOjF,MAAM,aAJJ,QAAQ,cAAc,KAAA,IAClB,UACA,QAAQ,QAAO,SAAQ,KAAK,cAAc,QAAQ,SAAS,GAExC,IAAI,SAAS;CACtC,MAAM,QAAQ,CAAC,GAAG,IAAI,IAAI,qBAAqB,KAAK,CAAC,CAAC;CAEtD,MAAM,OAAO,UAAkD;EAC7D,MAAM,KAAK;EACX,aAAa,KAAK;CACpB;CAEA,IAAI,MAAM,WAAW,GACnB,OAAO,UAAU,MAAM,GAAG,KAAK,EAAE,KAAI,aAAY,IAAI,SAAS,IAAI,CAAC;CAGrE,MAAM,gBACJ,UAAU,QAAQ,OAAO,aAAa,QAAQ,SAAS,QAAQ,CAAC,IAChE,KAAK,IAAI,GAAG,UAAU,MAAM;CAE9B,MAAM,UAAU,IAAI,IAClB,MAAM,KAAK,SAAoC;EAC7C,MAAM,aAAa,UAAU,QAAO,aAAY,SAAS,YAAY,IAAI,IAAI,CAAC,EAAE;EAEhF,OAAO,CAAC,MAAM,KAAK,IAAI,KAAK,UAAU,SAAS,aAAa,OAAQ,aAAa,GAAI,CAAC;CACxF,CAAC,CACH;CAEA,OAAO,UACJ,KAAK,UAAU,UAAU;EAWxB,OAAO;GAAE;GAAU;GAAO,OAVZ,MAAM,QAAQ,OAAO,SAAS;IAC1C,MAAM,YAAY,SAAS,YAAY,IAAI,IAAI,KAAK;IAEpD,IAAI,cAAc,GAAG,OAAO;IAE5B,MAAM,OAAO,MAAM,IAAI,IAAK,IAAI,SAAS,SAAU,KAAK,IAAI,GAAG,aAAa;IAE5E,OAAO,SAAU,QAAQ,IAAI,IAAI,KAAK,KAAK,aAAa,KAAK,MAAO,YAAY;GAClF,GAAG,CAE2B;EAAE;CAClC,CAAC,EACA,QAAO,UAAS,MAAM,QAAQ,CAAC,EAC/B,MAAM,MAAM,UAAU,MAAM,QAAQ,KAAK,SAAS,KAAK,QAAQ,MAAM,KAAK,EAC1E,MAAM,GAAG,KAAK,EACd,KAAI,UAAS,IAAI,MAAM,SAAS,IAAI,CAAC;AAC1C"}
|
package/dist/store.d.mts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { CodeModeStore, CodeModeStoreWrites } from "./executor.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/store.d.ts
|
|
4
|
+
/** `structuredContent` of a code mode `ToolResult`. `storeWrites` is present only for successful
|
|
5
|
+
* scripts that changed the store.
|
|
6
|
+
*/
|
|
7
|
+
type CodeModeStructuredContent = {
|
|
8
|
+
readonly codemode: {
|
|
9
|
+
readonly ok: boolean;
|
|
10
|
+
readonly storeWrites?: CodeModeStoreWrites;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
/** Largest stored value: characters of JSON (256 KiB), as the pi engine counts them. */
|
|
14
|
+
declare const maxCodeModeStoreValueChars: number;
|
|
15
|
+
/** Largest store: characters of keys plus JSON values (1 MiB), as the pi engine counts them. */
|
|
16
|
+
declare const maxCodeModeStoreTotalChars: number;
|
|
17
|
+
/** One prior tool result with the name of the tool that produced it. */
|
|
18
|
+
type CodeModeToolResultEntry = {
|
|
19
|
+
readonly toolName: string;
|
|
20
|
+
readonly result: {
|
|
21
|
+
readonly structuredContent?: unknown;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Rebuilds the code mode store from prior tool results, oldest first: applies the `storeWrites` of
|
|
26
|
+
* each successful script of the code mode tool named `toolName` (default `codemode`) in order.
|
|
27
|
+
* Results of other tools (even when their `structuredContent` looks like a code mode result),
|
|
28
|
+
* failed scripts, and malformed contents are skipped. Transcript `ToolResultMessage`s carry no tool
|
|
29
|
+
* name; pair each with the name of its assistant tool call.
|
|
30
|
+
*
|
|
31
|
+
* The rebuilt store keeps the engine bounds: a write whose JSON value exceeds 256 Ki characters, or
|
|
32
|
+
* that would grow the store beyond 1 Mi characters (keys plus JSON values), is dropped and the key
|
|
33
|
+
* keeps its previous value. Deletions always apply. Pass the value from `loadStore`.
|
|
34
|
+
*/
|
|
35
|
+
declare const codeModeStoreFromToolResults: (entries: ReadonlyArray<CodeModeToolResultEntry>, options?: {
|
|
36
|
+
readonly toolName?: string;
|
|
37
|
+
}) => CodeModeStore;
|
|
38
|
+
//#endregion
|
|
39
|
+
export { CodeModeStructuredContent, CodeModeToolResultEntry, codeModeStoreFromToolResults, maxCodeModeStoreTotalChars, maxCodeModeStoreValueChars };
|
|
40
|
+
//# sourceMappingURL=store.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.mts","names":[],"sources":["../src/store.ts"],"mappings":";;;;;AAQA;KAAY,yBAAA;EAAA,SACD,QAAA;IAAA,SACE,EAAA;IAAA,SACA,WAAA,GAAc,mBAAmB;EAAA;AAAA;;cAmBjC,0BAAA;AAnBiC;AAAA,cAsBjC,0BAAA;;KAGD,uBAAA;EAAA,SACD,QAAA;EAAA,SACA,MAAA;IAAA,SAAmB,iBAAA;EAAA;AAAA;;AALuB;AAGrD;;;;;;;;AAE+C;cAclC,4BAAA,GACX,OAAA,EAAS,aAAA,CAAc,uBAAA,GACvB,OAAA;EAAA,SAAoB,QAAA;AAAA,MACnB,aAAA"}
|
package/dist/store.mjs
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import "./tool.mjs";
|
|
2
|
+
import { Option } from "effect";
|
|
3
|
+
import * as Schema from "effect/Schema";
|
|
4
|
+
//#region src/store.ts
|
|
5
|
+
const StoreWritesSchema = Schema.Struct({
|
|
6
|
+
set: Schema.Record(Schema.String, Schema.Json),
|
|
7
|
+
delete: Schema.Array(Schema.String)
|
|
8
|
+
});
|
|
9
|
+
const CodeModeStructuredContentSchema = Schema.Struct({ codemode: Schema.Struct({
|
|
10
|
+
ok: Schema.Boolean,
|
|
11
|
+
storeWrites: Schema.optionalKey(StoreWritesSchema)
|
|
12
|
+
}) });
|
|
13
|
+
const decodeStructuredContent = Schema.decodeUnknownOption(CodeModeStructuredContentSchema);
|
|
14
|
+
/** Largest stored value: characters of JSON (256 KiB), as the pi engine counts them. */
|
|
15
|
+
const maxCodeModeStoreValueChars = 256 * 1024;
|
|
16
|
+
/** Largest store: characters of keys plus JSON values (1 MiB), as the pi engine counts them. */
|
|
17
|
+
const maxCodeModeStoreTotalChars = 1024 * 1024;
|
|
18
|
+
/**
|
|
19
|
+
* Rebuilds the code mode store from prior tool results, oldest first: applies the `storeWrites` of
|
|
20
|
+
* each successful script of the code mode tool named `toolName` (default `codemode`) in order.
|
|
21
|
+
* Results of other tools (even when their `structuredContent` looks like a code mode result),
|
|
22
|
+
* failed scripts, and malformed contents are skipped. Transcript `ToolResultMessage`s carry no tool
|
|
23
|
+
* name; pair each with the name of its assistant tool call.
|
|
24
|
+
*
|
|
25
|
+
* The rebuilt store keeps the engine bounds: a write whose JSON value exceeds 256 Ki characters, or
|
|
26
|
+
* that would grow the store beyond 1 Mi characters (keys plus JSON values), is dropped and the key
|
|
27
|
+
* keeps its previous value. Deletions always apply. Pass the value from `loadStore`.
|
|
28
|
+
*/
|
|
29
|
+
const codeModeStoreFromToolResults = (entries, options = {}) => {
|
|
30
|
+
const toolName = options.toolName ?? "codemode";
|
|
31
|
+
const store = /* @__PURE__ */ new Map();
|
|
32
|
+
let total = 0;
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
if (entry.toolName !== toolName) continue;
|
|
35
|
+
const content = decodeStructuredContent(entry.result.structuredContent);
|
|
36
|
+
if (Option.isNone(content) || !content.value.codemode.ok) continue;
|
|
37
|
+
const writes = content.value.codemode.storeWrites;
|
|
38
|
+
if (writes === void 0) continue;
|
|
39
|
+
for (const key of writes.delete) {
|
|
40
|
+
total -= store.get(key)?.chars ?? 0;
|
|
41
|
+
store.delete(key);
|
|
42
|
+
}
|
|
43
|
+
for (const [key, value] of Object.entries(writes.set)) {
|
|
44
|
+
const json = JSON.stringify(value);
|
|
45
|
+
if (json.length > 262144) continue;
|
|
46
|
+
const chars = key.length + json.length;
|
|
47
|
+
const next = total - (store.get(key)?.chars ?? 0) + chars;
|
|
48
|
+
if (next > 1048576) continue;
|
|
49
|
+
store.set(key, {
|
|
50
|
+
value,
|
|
51
|
+
chars
|
|
52
|
+
});
|
|
53
|
+
total = next;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return Object.fromEntries([...store].map(([key, stored]) => [key, stored.value]));
|
|
57
|
+
};
|
|
58
|
+
//#endregion
|
|
59
|
+
export { codeModeStoreFromToolResults, maxCodeModeStoreTotalChars, maxCodeModeStoreValueChars };
|
|
60
|
+
|
|
61
|
+
//# sourceMappingURL=store.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.mjs","names":[],"sources":["../src/store.ts"],"sourcesContent":["import { Option } from 'effect'\nimport * as Schema from 'effect/Schema'\nimport type { CodeModeStore, CodeModeStoreWrites } from './executor.ts'\nimport { codeModeToolName } from './tool.ts'\n\n/** `structuredContent` of a code mode `ToolResult`. `storeWrites` is present only for successful\n * scripts that changed the store.\n */\nexport type CodeModeStructuredContent = {\n readonly codemode: {\n readonly ok: boolean\n readonly storeWrites?: CodeModeStoreWrites\n }\n}\n\nconst StoreWritesSchema = Schema.Struct({\n set: Schema.Record(Schema.String, Schema.Json),\n delete: Schema.Array(Schema.String)\n})\n\nconst CodeModeStructuredContentSchema = Schema.Struct({\n codemode: Schema.Struct({\n ok: Schema.Boolean,\n storeWrites: Schema.optionalKey(StoreWritesSchema)\n })\n})\n\nconst decodeStructuredContent = Schema.decodeUnknownOption(CodeModeStructuredContentSchema)\n\n/** Largest stored value: characters of JSON (256 KiB), as the pi engine counts them. */\nexport const maxCodeModeStoreValueChars = 256 * 1024\n\n/** Largest store: characters of keys plus JSON values (1 MiB), as the pi engine counts them. */\nexport const maxCodeModeStoreTotalChars = 1024 * 1024\n\n/** One prior tool result with the name of the tool that produced it. */\nexport type CodeModeToolResultEntry = {\n readonly toolName: string\n readonly result: { readonly structuredContent?: unknown }\n}\n\n/**\n * Rebuilds the code mode store from prior tool results, oldest first: applies the `storeWrites` of\n * each successful script of the code mode tool named `toolName` (default `codemode`) in order.\n * Results of other tools (even when their `structuredContent` looks like a code mode result),\n * failed scripts, and malformed contents are skipped. Transcript `ToolResultMessage`s carry no tool\n * name; pair each with the name of its assistant tool call.\n *\n * The rebuilt store keeps the engine bounds: a write whose JSON value exceeds 256 Ki characters, or\n * that would grow the store beyond 1 Mi characters (keys plus JSON values), is dropped and the key\n * keeps its previous value. Deletions always apply. Pass the value from `loadStore`.\n */\nexport const codeModeStoreFromToolResults = (\n entries: ReadonlyArray<CodeModeToolResultEntry>,\n options: { readonly toolName?: string } = {}\n): CodeModeStore => {\n const toolName = options.toolName ?? codeModeToolName\n const store = new Map<string, { readonly value: Schema.Json; readonly chars: number }>()\n let total = 0\n\n for (const entry of entries) {\n if (entry.toolName !== toolName) continue\n\n const content = decodeStructuredContent(entry.result.structuredContent)\n\n if (Option.isNone(content) || !content.value.codemode.ok) continue\n\n const writes = content.value.codemode.storeWrites\n\n if (writes === undefined) continue\n\n for (const key of writes.delete) {\n total -= store.get(key)?.chars ?? 0\n store.delete(key)\n }\n\n for (const [key, value] of Object.entries(writes.set)) {\n const json = JSON.stringify(value)\n\n if (json.length > maxCodeModeStoreValueChars) continue\n\n const chars = key.length + json.length\n const next = total - (store.get(key)?.chars ?? 0) + chars\n\n if (next > maxCodeModeStoreTotalChars) continue\n\n store.set(key, { value, chars })\n total = next\n }\n }\n\n return Object.fromEntries([...store].map(([key, stored]) => [key, stored.value]))\n}\n"],"mappings":";;;;AAeA,MAAM,oBAAoB,OAAO,OAAO;CACtC,KAAK,OAAO,OAAO,OAAO,QAAQ,OAAO,IAAI;CAC7C,QAAQ,OAAO,MAAM,OAAO,MAAM;AACpC,CAAC;AAED,MAAM,kCAAkC,OAAO,OAAO,EACpD,UAAU,OAAO,OAAO;CACtB,IAAI,OAAO;CACX,aAAa,OAAO,YAAY,iBAAiB;AACnD,CAAC,EACH,CAAC;AAED,MAAM,0BAA0B,OAAO,oBAAoB,+BAA+B;;AAG1F,MAAa,6BAA6B,MAAM;;AAGhD,MAAa,6BAA6B,OAAO;;;;;;;;;;;;AAmBjD,MAAa,gCACX,SACA,UAA0C,CAAC,MACzB;CAClB,MAAM,WAAW,QAAQ,YAAA;CACzB,MAAM,wBAAQ,IAAI,IAAqE;CACvF,IAAI,QAAQ;CAEZ,KAAK,MAAM,SAAS,SAAS;EAC3B,IAAI,MAAM,aAAa,UAAU;EAEjC,MAAM,UAAU,wBAAwB,MAAM,OAAO,iBAAiB;EAEtE,IAAI,OAAO,OAAO,OAAO,KAAK,CAAC,QAAQ,MAAM,SAAS,IAAI;EAE1D,MAAM,SAAS,QAAQ,MAAM,SAAS;EAEtC,IAAI,WAAW,KAAA,GAAW;EAE1B,KAAK,MAAM,OAAO,OAAO,QAAQ;GAC/B,SAAS,MAAM,IAAI,GAAG,GAAG,SAAS;GAClC,MAAM,OAAO,GAAG;EAClB;EAEA,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,GAAG,GAAG;GACrD,MAAM,OAAO,KAAK,UAAU,KAAK;GAEjC,IAAI,KAAK,SAAA,QAAqC;GAE9C,MAAM,QAAQ,IAAI,SAAS,KAAK;GAChC,MAAM,OAAO,SAAS,MAAM,IAAI,GAAG,GAAG,SAAS,KAAK;GAEpD,IAAI,OAAA,SAAmC;GAEvC,MAAM,IAAI,KAAK;IAAE;IAAO;GAAM,CAAC;GAC/B,QAAQ;EACV;CACF;CAEA,OAAO,OAAO,YAAY,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,KAAK,YAAY,CAAC,KAAK,OAAO,KAAK,CAAC,CAAC;AAClF"}
|
package/dist/tool.d.mts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { CodeModeExecutor, CodeModeStore } from "./executor.mjs";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import { ToolCall } from "@yolk-sdk/agent/protocol";
|
|
4
|
+
import { ToolRegistration } from "@yolk-sdk/agent/tools";
|
|
5
|
+
import { ToolError } from "@yolk-sdk/agent/loop";
|
|
6
|
+
|
|
7
|
+
//#region src/tool.d.ts
|
|
8
|
+
/** Default name of the code mode tool. */
|
|
9
|
+
declare const codeModeToolName = "codemode";
|
|
10
|
+
/** Host-owned limits of one script. */
|
|
11
|
+
type CodeModeLimits = {
|
|
12
|
+
/** Overall deadline including nested calls. Default 120000 (clamped by `deadline`). */readonly timeoutMs?: number; /** VM heap cap. Default 64 MiB. */
|
|
13
|
+
readonly memoryLimitBytes?: number; /** Nested tool calls per script; further calls reject with an Error. Default 256. */
|
|
14
|
+
readonly maxNestedCalls?: number; /** Model-visible result characters, cut head and tail with an omission marker. Default 40000. */
|
|
15
|
+
readonly maxOutputChars?: number; /** Images kept in the result, in order; later images are dropped with a note. Default 8. */
|
|
16
|
+
readonly maxImages?: number;
|
|
17
|
+
/** Base64 characters of images kept in the result in total; an image that would exceed it is
|
|
18
|
+
* dropped with a note. Default 4 MiB.
|
|
19
|
+
*/
|
|
20
|
+
readonly maxImageBytes?: number;
|
|
21
|
+
};
|
|
22
|
+
declare const defaultCodeModeLimits: {
|
|
23
|
+
readonly timeoutMs: 120000;
|
|
24
|
+
readonly memoryLimitBytes: number;
|
|
25
|
+
readonly maxNestedCalls: 256;
|
|
26
|
+
readonly maxOutputChars: 40000;
|
|
27
|
+
readonly maxImages: 8;
|
|
28
|
+
readonly maxImageBytes: number;
|
|
29
|
+
};
|
|
30
|
+
/** Kept between a host deadline and the script deadline, for result handling. */
|
|
31
|
+
declare const codeModeDeadlineMarginMs = 5000;
|
|
32
|
+
/** A deadline never clamps a script below this timeout. */
|
|
33
|
+
declare const codeModeMinimumTimeoutMs = 1000;
|
|
34
|
+
type MakeCodeModeToolOptions<Context> = {
|
|
35
|
+
readonly executor: CodeModeExecutor; /** Default `codemode`. */
|
|
36
|
+
readonly name?: string; /** Estimated tokens (four characters each) for listing nested tools. Default 3000. */
|
|
37
|
+
readonly inlineBudget?: number;
|
|
38
|
+
readonly limits?: CodeModeLimits;
|
|
39
|
+
/**
|
|
40
|
+
* Epoch milliseconds by which the tool call must end, for example the remaining function budget
|
|
41
|
+
* of a Workflow step. The script timeout is clamped to it minus 5 s, but never below 1 s.
|
|
42
|
+
*/
|
|
43
|
+
readonly deadline?: (context: Context) => number | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* The store scripts read with `load(key)`, usually rebuilt from the transcript with
|
|
46
|
+
* `codeModeStoreFromToolResults`. Without it every script starts with an empty store.
|
|
47
|
+
*/
|
|
48
|
+
readonly loadStore?: (context: Context) => Effect.Effect<CodeModeStore, ToolError>;
|
|
49
|
+
/**
|
|
50
|
+
* Runs before each nested call executes, with the nested call (id `<toolCallId>/<seq>`) and the
|
|
51
|
+
* host context. A failure rejects that call in the script with the message and records it as an
|
|
52
|
+
* `error` without executing it. Host decorators around the `ToolExecutor` (outside
|
|
53
|
+
* `ResolvedToolSet.execute`) never see nested calls: put per-call run-authority checks here or in
|
|
54
|
+
* registration-level wrappers.
|
|
55
|
+
*/
|
|
56
|
+
readonly beforeNestedCall?: (input: {
|
|
57
|
+
readonly call: ToolCall;
|
|
58
|
+
readonly context: Context;
|
|
59
|
+
}) => Effect.Effect<void, string>;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* The code mode tool: one registration (default name `codemode`, input `{ code }`) whose scripts
|
|
63
|
+
* call the other code-mode-callable tools of the same `resolveTools` resolution. Nested calls run
|
|
64
|
+
* through the resolved execute path with the same host context (input decoding, enablement,
|
|
65
|
+
* registration wrappers), get ids `<toolCallId>/<seq>`, and are recorded on the result's
|
|
66
|
+
* `nestedCalls`. The resolved description lists the callable tools (see
|
|
67
|
+
* `renderCodeModeDescription`).
|
|
68
|
+
*
|
|
69
|
+
* Access is `write`: scripts can call any write tool the resolution exposes to code mode; each
|
|
70
|
+
* nested call keeps its own access metadata and host wrappers.
|
|
71
|
+
*/
|
|
72
|
+
declare const makeCodeModeTool: <Context>(options: MakeCodeModeToolOptions<Context>) => ToolRegistration<Context>;
|
|
73
|
+
//#endregion
|
|
74
|
+
export { CodeModeLimits, MakeCodeModeToolOptions, codeModeDeadlineMarginMs, codeModeMinimumTimeoutMs, codeModeToolName, defaultCodeModeLimits, makeCodeModeTool };
|
|
75
|
+
//# sourceMappingURL=tool.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.d.mts","names":[],"sources":["../src/tool.ts"],"mappings":";;;;;;;;cA0Da,gBAAA;AAAb;AAAA,KAGY,cAAA;kGAED,SAAA,WALkB;EAAA,SAOlB,gBAAA,WAJe;EAAA,SAMf,cAAA,WANe;EAAA,SAQf,cAAA,WAJA;EAAA,SAMA,SAAA;EAFA;;;EAAA,SAMA,aAAA;AAAA;AAAA,cAGE,qBAAA;EAAA;;;;;;;;cAUA,wBAAA;;cAGA,wBAAA;AAAA,KAED,uBAAA;EAAA,SACD,QAAA,EAAU,gBAAA;WAEV,IAAA,WAR0B;EAAA,SAU1B,YAAA;EAAA,SACA,MAAA,GAAS,cAAA;;;AARiB;AAErC;WAWW,QAAA,IAAY,OAAA,EAAS,OAAA;EAXG;;;;EAAA,SAgBxB,SAAA,IAAa,OAAA,EAAS,OAAA,KAAY,MAAA,CAAO,MAAA,CAAO,aAAA,EAAe,SAAA;EAAf;;;;;;;EAAA,SAQhD,gBAAA,IAAoB,KAAA;IAAA,SAClB,IAAA,EAAM,QAAA;IAAA,SACN,OAAA,EAAS,OAAA;EAAA,MACd,MAAA,CAAO,MAAA;AAAA;;;;;;;;;;;;cA2YF,gBAAA,YACX,OAAA,EAAS,uBAAA,CAAwB,OAAA,MAChC,gBAAA,CAAiB,OAAA"}
|
package/dist/tool.mjs
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { codeModeCatalog, describeCodeModeTool, findCodeModeTool, renderCodeModeDescription } from "./catalog.mjs";
|
|
2
|
+
import { searchCodeModeTools } from "./search.mjs";
|
|
3
|
+
import { codeModeResultSegments, codeModeSegmentsContent, defaultCodeModeMaxImageBytes } from "./output.mjs";
|
|
4
|
+
import { Cause, Clock, Data, Duration, Effect, Exit, Fiber, FiberSet, Option, Predicate, Scope } from "effect";
|
|
5
|
+
import { ToolCall, ToolResult, contentPartText, emptyNestedToolCallRecorder, nestedToolCallResultFields, recordNestedToolCall } from "@yolk-sdk/agent/protocol";
|
|
6
|
+
import * as Schema from "effect/Schema";
|
|
7
|
+
import { makeTool } from "@yolk-sdk/agent/tools";
|
|
8
|
+
import { ToolError } from "@yolk-sdk/agent/loop";
|
|
9
|
+
//#region src/tool.ts
|
|
10
|
+
/** Default name of the code mode tool. */
|
|
11
|
+
const codeModeToolName = "codemode";
|
|
12
|
+
const defaultCodeModeLimits = {
|
|
13
|
+
timeoutMs: 12e4,
|
|
14
|
+
memoryLimitBytes: 64 * 1024 * 1024,
|
|
15
|
+
maxNestedCalls: 256,
|
|
16
|
+
maxOutputChars: 4e4,
|
|
17
|
+
maxImages: 8,
|
|
18
|
+
maxImageBytes: defaultCodeModeMaxImageBytes
|
|
19
|
+
};
|
|
20
|
+
/** Kept between a host deadline and the script deadline, for result handling. */
|
|
21
|
+
const codeModeDeadlineMarginMs = 5e3;
|
|
22
|
+
/** A deadline never clamps a script below this timeout. */
|
|
23
|
+
const codeModeMinimumTimeoutMs = 1e3;
|
|
24
|
+
const CodeModeParams = Schema.Struct({ code: Schema.String.annotate({ description: "Raw JavaScript: the body of an async function. Top-level await and return work. Not a function declaration and not a module." }) });
|
|
25
|
+
var CodeModeExecutorRejected = class extends Data.TaggedError("CodeModeExecutorRejected") {};
|
|
26
|
+
const errorMessage = (error) => error instanceof Error ? error.message : Predicate.isString(error) ? error : "unknown error";
|
|
27
|
+
const nestedText = (content) => Predicate.isString(content) ? content : content.map(contentPartText).filter((text) => text.length > 0).join("\n");
|
|
28
|
+
/** What a nested call resolves to: `structuredContent` for tools with an output schema (when
|
|
29
|
+
* present), text content otherwise; error results reject with their text.
|
|
30
|
+
*/
|
|
31
|
+
const resolveNestedResult = (tool, result) => {
|
|
32
|
+
const text = nestedText(result.content);
|
|
33
|
+
if (result.isError === true) return {
|
|
34
|
+
ok: false,
|
|
35
|
+
message: text.length > 0 ? text : `Tool ${tool.name} failed.`
|
|
36
|
+
};
|
|
37
|
+
return tool.structured && result.structuredContent !== void 0 ? {
|
|
38
|
+
ok: true,
|
|
39
|
+
value: result.structuredContent
|
|
40
|
+
} : {
|
|
41
|
+
ok: true,
|
|
42
|
+
value: text
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
const recordedOutcome = (exit, durationMs) => {
|
|
46
|
+
if (Exit.isFailure(exit)) return Cause.hasInterruptsOnly(exit.cause) ? {
|
|
47
|
+
status: "cancelled",
|
|
48
|
+
durationMs
|
|
49
|
+
} : {
|
|
50
|
+
status: "error",
|
|
51
|
+
durationMs,
|
|
52
|
+
error: "The tool call failed unexpectedly."
|
|
53
|
+
};
|
|
54
|
+
const result = exit.value;
|
|
55
|
+
const outcome = result.isError === true ? {
|
|
56
|
+
status: "error",
|
|
57
|
+
durationMs,
|
|
58
|
+
error: nestedText(result.content)
|
|
59
|
+
} : {
|
|
60
|
+
status: "ok",
|
|
61
|
+
durationMs
|
|
62
|
+
};
|
|
63
|
+
if (result.usage !== void 0) outcome.usage = result.usage;
|
|
64
|
+
return outcome;
|
|
65
|
+
};
|
|
66
|
+
const SearchOptions = Schema.Struct({
|
|
67
|
+
limit: Schema.optionalKey(Schema.Finite),
|
|
68
|
+
namespace: Schema.optionalKey(Schema.String)
|
|
69
|
+
});
|
|
70
|
+
const decodeSearchOptions = Schema.decodeUnknownOption(SearchOptions);
|
|
71
|
+
/** `searchTools`, `describeTool`, and `describeNamespace` over every nested tool. */
|
|
72
|
+
const discoveryGlobals = (catalog) => [
|
|
73
|
+
{
|
|
74
|
+
name: "searchTools",
|
|
75
|
+
spread: true,
|
|
76
|
+
execute: (args) => {
|
|
77
|
+
const [query, options] = Array.isArray(args) ? args : [];
|
|
78
|
+
return Promise.resolve(searchCodeModeTools(catalog, Predicate.isString(query) ? query : "", Option.getOrElse(decodeSearchOptions(options ?? {}), () => ({}))));
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: "describeTool",
|
|
83
|
+
execute: (name) => {
|
|
84
|
+
const tool = Predicate.isString(name) ? findCodeModeTool(catalog, name) : void 0;
|
|
85
|
+
return Promise.resolve(tool === void 0 ? void 0 : describeCodeModeTool(tool));
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: "describeNamespace",
|
|
90
|
+
execute: (name) => {
|
|
91
|
+
const tools = catalog.filter((tool) => tool.namespace === name);
|
|
92
|
+
if (!Predicate.isString(name) || tools.length === 0) return Promise.resolve(void 0);
|
|
93
|
+
const description = tools.find((tool) => tool.namespaceDescription !== void 0)?.namespaceDescription;
|
|
94
|
+
const namespace = {
|
|
95
|
+
name,
|
|
96
|
+
tools: tools.map((tool) => ({
|
|
97
|
+
name: tool.identifier,
|
|
98
|
+
description: tool.description
|
|
99
|
+
}))
|
|
100
|
+
};
|
|
101
|
+
if (description !== void 0) namespace.description = description;
|
|
102
|
+
return Promise.resolve(namespace);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
];
|
|
106
|
+
const hasStoreWrites = (result) => result.storeWrites !== void 0 && (Object.keys(result.storeWrites.set).length > 0 || result.storeWrites.delete.length > 0);
|
|
107
|
+
const timeoutFor = (limit, deadline, now) => deadline === void 0 ? limit : Math.max(codeModeMinimumTimeoutMs, Math.min(limit, deadline - now - codeModeDeadlineMarginMs));
|
|
108
|
+
/**
|
|
109
|
+
* Waits at most `codeModeDeadlineMarginMs` for an effect (interrupting nested fibers) to finish; it
|
|
110
|
+
* keeps running detached when a nested call does not respond to interruption in time.
|
|
111
|
+
*/
|
|
112
|
+
const boundedWait = (effect) => Effect.forkDetach(effect).pipe(Effect.flatMap((fiber) => Fiber.await(fiber).pipe(Effect.timeoutOption(Duration.millis(codeModeDeadlineMarginMs)), Effect.interruptible)), Effect.asVoid);
|
|
113
|
+
const runScript = (input) => Effect.gen(function* () {
|
|
114
|
+
const { call, limits, nested, options } = input;
|
|
115
|
+
const store = options.loadStore === void 0 ? {} : yield* options.loadStore(input.context);
|
|
116
|
+
const startedAt = yield* Clock.currentTimeMillis;
|
|
117
|
+
const timeoutMs = timeoutFor(limits.timeoutMs, options.deadline?.(input.context), startedAt);
|
|
118
|
+
const catalog = codeModeCatalog(nested.tools);
|
|
119
|
+
return yield* Effect.acquireUseRelease(Scope.make(), (scope) => Effect.gen(function* () {
|
|
120
|
+
const fibers = yield* FiberSet.make().pipe(Scope.provide(scope));
|
|
121
|
+
const runFork = yield* FiberSet.runtime(fibers)();
|
|
122
|
+
const records = [];
|
|
123
|
+
const callTool = (tool) => (args, { signal }) => {
|
|
124
|
+
if (signal.aborted) return Promise.reject(/* @__PURE__ */ new Error(`tools.${tool.identifier} was cancelled.`));
|
|
125
|
+
if (records.length >= limits.maxNestedCalls) return Promise.reject(/* @__PURE__ */ new Error(`Nested call limit reached: a script may make at most ${limits.maxNestedCalls} tool calls. Batch the work or return partial results.`));
|
|
126
|
+
const params = args === void 0 ? {} : args;
|
|
127
|
+
const record = {
|
|
128
|
+
id: `${call.id}/${records.length + 1}`,
|
|
129
|
+
name: tool.name,
|
|
130
|
+
args: params
|
|
131
|
+
};
|
|
132
|
+
records.push(record);
|
|
133
|
+
const nestedCall = ToolCall.make({
|
|
134
|
+
id: record.id,
|
|
135
|
+
name: tool.name,
|
|
136
|
+
params
|
|
137
|
+
});
|
|
138
|
+
const admitted = options.beforeNestedCall === void 0 ? nested.execute(nestedCall) : options.beforeNestedCall({
|
|
139
|
+
call: nestedCall,
|
|
140
|
+
context: input.context
|
|
141
|
+
}).pipe(Effect.matchEffect({
|
|
142
|
+
onFailure: (message) => Effect.succeed(ToolResult.make({
|
|
143
|
+
toolCallId: nestedCall.id,
|
|
144
|
+
content: message,
|
|
145
|
+
isError: true
|
|
146
|
+
})),
|
|
147
|
+
onSuccess: () => nested.execute(nestedCall)
|
|
148
|
+
}));
|
|
149
|
+
const execution = Effect.gen(function* () {
|
|
150
|
+
const started = yield* Clock.currentTimeMillis;
|
|
151
|
+
return yield* admitted.pipe(Effect.onExit((exit) => Effect.map(Clock.currentTimeMillis, (finished) => {
|
|
152
|
+
record.outcome = recordedOutcome(exit, finished - started);
|
|
153
|
+
})));
|
|
154
|
+
});
|
|
155
|
+
return new Promise((resolve, reject) => {
|
|
156
|
+
runFork(execution, { signal }).addObserver((exit) => {
|
|
157
|
+
if (Exit.isFailure(exit)) {
|
|
158
|
+
reject(/* @__PURE__ */ new Error(Cause.hasInterruptsOnly(exit.cause) ? `tools.${tool.identifier} was cancelled.` : `tools.${tool.identifier} failed unexpectedly.`));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const resolution = resolveNestedResult(tool, exit.value);
|
|
162
|
+
if (resolution.ok) resolve(resolution.value);
|
|
163
|
+
else reject(new Error(resolution.message));
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
const tools = catalog.map((tool) => ({
|
|
168
|
+
name: tool.name,
|
|
169
|
+
description: tool.description,
|
|
170
|
+
inputSchema: tool.inputSchema,
|
|
171
|
+
outputSchema: tool.outputSchema,
|
|
172
|
+
execute: callTool(tool)
|
|
173
|
+
}));
|
|
174
|
+
const result = yield* Effect.tryPromise({
|
|
175
|
+
try: (signal) => options.executor.execute(input.code, {
|
|
176
|
+
tools,
|
|
177
|
+
globals: discoveryGlobals(catalog),
|
|
178
|
+
timeoutMs,
|
|
179
|
+
memoryLimitBytes: limits.memoryLimitBytes,
|
|
180
|
+
store,
|
|
181
|
+
signal
|
|
182
|
+
}),
|
|
183
|
+
catch: (error) => new CodeModeExecutorRejected({ message: errorMessage(error) })
|
|
184
|
+
}).pipe(Effect.catch((error) => Effect.succeed({
|
|
185
|
+
ok: false,
|
|
186
|
+
error: {
|
|
187
|
+
kind: "sandbox",
|
|
188
|
+
message: `The code mode executor failed: ${error.message}`
|
|
189
|
+
},
|
|
190
|
+
output: []
|
|
191
|
+
})), Effect.timeoutOption(Duration.millis(timeoutMs + codeModeDeadlineMarginMs)), Effect.map(Option.getOrElse(() => ({
|
|
192
|
+
ok: false,
|
|
193
|
+
error: {
|
|
194
|
+
kind: "timeout",
|
|
195
|
+
message: `Execution timed out after ${timeoutMs} ms (the executor did not stop in time)`
|
|
196
|
+
},
|
|
197
|
+
output: []
|
|
198
|
+
}))));
|
|
199
|
+
yield* boundedWait(FiberSet.clear(fibers));
|
|
200
|
+
const finishedAt = yield* Clock.currentTimeMillis;
|
|
201
|
+
const recorded = records.map((record) => ({
|
|
202
|
+
id: record.id,
|
|
203
|
+
name: record.name,
|
|
204
|
+
args: record.args,
|
|
205
|
+
...record.outcome ?? { status: "cancelled" }
|
|
206
|
+
}));
|
|
207
|
+
const { nestedCalls, usage } = nestedToolCallResultFields(recorded.reduce(recordNestedToolCall, emptyNestedToolCallRecorder));
|
|
208
|
+
const content = codeModeSegmentsContent(codeModeResultSegments({
|
|
209
|
+
result,
|
|
210
|
+
wallTimeMs: finishedAt - startedAt,
|
|
211
|
+
calls: recorded,
|
|
212
|
+
maxChars: limits.maxOutputChars,
|
|
213
|
+
maxImages: limits.maxImages,
|
|
214
|
+
maxImageBytes: limits.maxImageBytes
|
|
215
|
+
}));
|
|
216
|
+
const structuredContent = { codemode: result.ok && result.storeWrites !== void 0 && hasStoreWrites(result) ? {
|
|
217
|
+
ok: true,
|
|
218
|
+
storeWrites: result.storeWrites
|
|
219
|
+
} : { ok: result.ok } };
|
|
220
|
+
const fields = {
|
|
221
|
+
toolCallId: call.id,
|
|
222
|
+
content,
|
|
223
|
+
structuredContent,
|
|
224
|
+
nestedCalls
|
|
225
|
+
};
|
|
226
|
+
if (!result.ok) fields.isError = true;
|
|
227
|
+
if (usage !== void 0) fields.usage = usage;
|
|
228
|
+
return ToolResult.make(fields);
|
|
229
|
+
}), (scope, exit) => boundedWait(Scope.close(scope, exit)));
|
|
230
|
+
});
|
|
231
|
+
/**
|
|
232
|
+
* The code mode tool: one registration (default name `codemode`, input `{ code }`) whose scripts
|
|
233
|
+
* call the other code-mode-callable tools of the same `resolveTools` resolution. Nested calls run
|
|
234
|
+
* through the resolved execute path with the same host context (input decoding, enablement,
|
|
235
|
+
* registration wrappers), get ids `<toolCallId>/<seq>`, and are recorded on the result's
|
|
236
|
+
* `nestedCalls`. The resolved description lists the callable tools (see
|
|
237
|
+
* `renderCodeModeDescription`).
|
|
238
|
+
*
|
|
239
|
+
* Access is `write`: scripts can call any write tool the resolution exposes to code mode; each
|
|
240
|
+
* nested call keeps its own access metadata and host wrappers.
|
|
241
|
+
*/
|
|
242
|
+
const makeCodeModeTool = (options) => {
|
|
243
|
+
const name = options.name ?? "codemode";
|
|
244
|
+
const limits = {
|
|
245
|
+
...defaultCodeModeLimits,
|
|
246
|
+
...options.limits
|
|
247
|
+
};
|
|
248
|
+
const inlineBudget = options.inlineBudget ?? 3e3;
|
|
249
|
+
const store = options.loadStore !== void 0;
|
|
250
|
+
return makeTool({
|
|
251
|
+
name,
|
|
252
|
+
description: renderCodeModeDescription({
|
|
253
|
+
tools: [],
|
|
254
|
+
inlineBudget,
|
|
255
|
+
store
|
|
256
|
+
}),
|
|
257
|
+
parameters: CodeModeParams,
|
|
258
|
+
access: "write",
|
|
259
|
+
nestedToolAccess: true,
|
|
260
|
+
describe: ({ tools }) => renderCodeModeDescription({
|
|
261
|
+
tools,
|
|
262
|
+
inlineBudget,
|
|
263
|
+
store
|
|
264
|
+
}),
|
|
265
|
+
execute: ({ call, context, params, nested }) => nested === void 0 ? Effect.fail(new ToolError({
|
|
266
|
+
tool: name,
|
|
267
|
+
cause: "unavailable",
|
|
268
|
+
message: `${name} requires nested tool access; resolve it with resolveTools.`
|
|
269
|
+
})) : runScript({
|
|
270
|
+
options,
|
|
271
|
+
limits,
|
|
272
|
+
call,
|
|
273
|
+
context,
|
|
274
|
+
code: params.code,
|
|
275
|
+
nested
|
|
276
|
+
})
|
|
277
|
+
});
|
|
278
|
+
};
|
|
279
|
+
//#endregion
|
|
280
|
+
export { codeModeDeadlineMarginMs, codeModeMinimumTimeoutMs, codeModeToolName, defaultCodeModeLimits, makeCodeModeTool };
|
|
281
|
+
|
|
282
|
+
//# sourceMappingURL=tool.mjs.map
|