@yolk-sdk/codemode 0.1.0-canary.100

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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -0
  3. package/dist/catalog.d.mts +74 -0
  4. package/dist/catalog.d.mts.map +1 -0
  5. package/dist/catalog.mjs +164 -0
  6. package/dist/catalog.mjs.map +1 -0
  7. package/dist/classifier-tool.d.mts +78 -0
  8. package/dist/classifier-tool.d.mts.map +1 -0
  9. package/dist/classifier-tool.mjs +125 -0
  10. package/dist/classifier-tool.mjs.map +1 -0
  11. package/dist/executor.d.mts +108 -0
  12. package/dist/executor.d.mts.map +1 -0
  13. package/dist/executor.mjs +1 -0
  14. package/dist/index.d.mts +8 -0
  15. package/dist/index.mjs +7 -0
  16. package/dist/node.d.mts +35 -0
  17. package/dist/node.d.mts.map +1 -0
  18. package/dist/node.mjs +393 -0
  19. package/dist/node.mjs.map +1 -0
  20. package/dist/output.d.mts +42 -0
  21. package/dist/output.d.mts.map +1 -0
  22. package/dist/output.mjs +184 -0
  23. package/dist/output.mjs.map +1 -0
  24. package/dist/search.d.mts +23 -0
  25. package/dist/search.d.mts.map +1 -0
  26. package/dist/search.mjs +58 -0
  27. package/dist/search.mjs.map +1 -0
  28. package/dist/store.d.mts +77 -0
  29. package/dist/store.d.mts.map +1 -0
  30. package/dist/store.mjs +61 -0
  31. package/dist/store.mjs.map +1 -0
  32. package/dist/tool.d.mts +103 -0
  33. package/dist/tool.d.mts.map +1 -0
  34. package/dist/tool.mjs +392 -0
  35. package/dist/tool.mjs.map +1 -0
  36. package/package.json +63 -0
  37. package/src/catalog.ts +305 -0
  38. package/src/classifier-tool.ts +227 -0
  39. package/src/executor.ts +122 -0
  40. package/src/index.ts +87 -0
  41. package/src/node.ts +546 -0
  42. package/src/output.ts +287 -0
  43. package/src/search.ts +113 -0
  44. package/src/store.ts +135 -0
  45. package/src/tool.ts +766 -0
@@ -0,0 +1,184 @@
1
+ import { ImagePart, TextPart, inlineBase64AttachmentSource } from "@yolk-sdk/agent/protocol";
2
+ //#region src/output.ts
3
+ const isHighSurrogate = (code) => code >= 55296 && code <= 56319;
4
+ const isLowSurrogate = (code) => code >= 56320 && code <= 57343;
5
+ const headCut = (text, length) => length > 0 && length < text.length && isHighSurrogate(text.charCodeAt(length - 1)) ? length - 1 : length;
6
+ const tailCut = (text, start) => start > 0 && start < text.length && isLowSurrogate(text.charCodeAt(start)) ? start + 1 : start;
7
+ const textLength = (segments) => segments.reduce((total, segment) => total + (segment.type === "text" ? segment.text.length : 0), 0);
8
+ const omissionMarker = (characters, images) => `\n\n[… ${characters} characters${images > 0 ? ` and ${images} image${images === 1 ? "" : "s"}` : ""} omitted …]\n\n`;
9
+ const markerReserve = 80;
10
+ /**
11
+ * Keeps at most about `maxChars` characters of text: the head and the tail of the segments, with an
12
+ * omission marker in between. Images inside the omitted middle are dropped and counted.
13
+ */
14
+ const boundCodeModeSegments = (segments, maxChars) => {
15
+ const total = textLength(segments);
16
+ if (total <= maxChars) return segments;
17
+ const budget = Math.max(0, maxChars - markerReserve);
18
+ let headBudget = Math.ceil(budget / 2);
19
+ let tailBudget = budget - headBudget;
20
+ const head = [];
21
+ let headIndex = 0;
22
+ let headPartial = 0;
23
+ for (; headIndex < segments.length && headBudget > 0; headIndex++) {
24
+ const segment = segments[headIndex];
25
+ if (segment === void 0) break;
26
+ if (segment.type === "image") {
27
+ head.push(segment);
28
+ continue;
29
+ }
30
+ if (segment.text.length <= headBudget) {
31
+ head.push(segment);
32
+ headBudget -= segment.text.length;
33
+ continue;
34
+ }
35
+ headPartial = headCut(segment.text, headBudget);
36
+ head.push({
37
+ type: "text",
38
+ text: segment.text.slice(0, headPartial)
39
+ });
40
+ headBudget = 0;
41
+ break;
42
+ }
43
+ const tail = [];
44
+ let tailIndex = segments.length - 1;
45
+ let tailStart;
46
+ for (; tailIndex >= headIndex && tailBudget > 0; tailIndex--) {
47
+ const segment = segments[tailIndex];
48
+ if (segment === void 0) break;
49
+ if (segment.type === "image") {
50
+ if (tailIndex === headIndex) break;
51
+ tail.unshift(segment);
52
+ continue;
53
+ }
54
+ const available = tailIndex === headIndex ? segment.text.length - headPartial : segment.text.length;
55
+ if (available <= tailBudget && tailIndex !== headIndex) {
56
+ tail.unshift(segment);
57
+ tailBudget -= available;
58
+ continue;
59
+ }
60
+ tailStart = tailCut(segment.text, segment.text.length - Math.min(available, tailBudget));
61
+ tail.unshift({
62
+ type: "text",
63
+ text: segment.text.slice(tailStart)
64
+ });
65
+ tailBudget = 0;
66
+ break;
67
+ }
68
+ const keptText = textLength(head) + textLength(tail);
69
+ const keptImages = [...head, ...tail].filter((segment) => segment.type === "image").length;
70
+ const allImages = segments.filter((segment) => segment.type === "image").length;
71
+ return [
72
+ ...head,
73
+ {
74
+ type: "text",
75
+ text: omissionMarker(total - keptText, allImages - keptImages)
76
+ },
77
+ ...tail
78
+ ];
79
+ };
80
+ const mergeText = (segments) => segments.reduce((merged, segment) => {
81
+ const last = merged.at(-1);
82
+ if (segment.type === "text" && last?.type === "text") merged[merged.length - 1] = {
83
+ type: "text",
84
+ text: last.text + segment.text
85
+ };
86
+ else if (segment.type === "image" || segment.text.length > 0) merged.push(segment);
87
+ return merged;
88
+ }, []);
89
+ /** Agent `Content`: a plain string without images, ordered text and image parts otherwise. */
90
+ const codeModeSegmentsContent = (segments) => {
91
+ const merged = mergeText(segments);
92
+ if (merged.every((segment) => segment.type === "text")) return merged.map((segment) => segment.type === "text" ? segment.text : "").join("");
93
+ return merged.map((segment) => segment.type === "text" ? TextPart.make({ text: segment.text }) : ImagePart.make({
94
+ source: inlineBase64AttachmentSource(segment.data),
95
+ mimeType: segment.mimeType
96
+ }));
97
+ };
98
+ const statusOrder = [
99
+ "ok",
100
+ "error",
101
+ "cancelled"
102
+ ];
103
+ /** `name: 2 ok, 1 error; other: 1 cancelled`, by first appearance. */
104
+ const summarizeCodeModeCalls = (calls) => {
105
+ if (calls.length === 0) return "none";
106
+ const byName = /* @__PURE__ */ new Map();
107
+ for (const call of calls) {
108
+ const counts = byName.get(call.name) ?? /* @__PURE__ */ new Map();
109
+ counts.set(call.status, (counts.get(call.status) ?? 0) + 1);
110
+ byName.set(call.name, counts);
111
+ }
112
+ return [...byName].map(([name, counts]) => `${name}: ${statusOrder.flatMap((status) => {
113
+ const count = counts.get(status);
114
+ return count === void 0 ? [] : [`${count} ${status}`];
115
+ }).join(", ")}`).join("; ");
116
+ };
117
+ const stackFrames = (stack) => stack === void 0 ? [] : stack.split("\n").filter((line) => /^\s+at\s/.test(line) || /^\s*\S+:\d+/.test(line));
118
+ const encodeValue = (value) => {
119
+ try {
120
+ return JSON.stringify(value) ?? "undefined";
121
+ } catch {
122
+ return "[unserializable value]";
123
+ }
124
+ };
125
+ /** Default cap of images kept in one result. */
126
+ const defaultCodeModeMaxImages = 8;
127
+ /** Default cap of base64 image data kept in one result, in characters (4 MiB). */
128
+ const defaultCodeModeMaxImageBytes = 4 * 1024 * 1024;
129
+ const imageOmissionNote = (dropped, maxImages, maxImageBytes) => `\n\n[… ${dropped} image${dropped === 1 ? "" : "s"} omitted: a result keeps at most ${maxImages} images and ${maxImageBytes} characters of base64 image data …]`;
130
+ /**
131
+ * Model-visible segments of one execution: a `Script completed` / `Script failed` header with the
132
+ * wall time, the output in order, the JSON return value (omitted when undefined), and for failures
133
+ * the error and the tool calls already made. Images are kept in order while they fit `maxImages`
134
+ * (default 8) and `maxImageBytes` of base64 in total (default 4 MiB); an image that would exceed
135
+ * either is dropped and counted in an omission note after the output. The result is then bounded
136
+ * by `maxChars` with a head-and-tail cut, which also drops images in the omitted middle.
137
+ */
138
+ const codeModeResultSegments = (input) => {
139
+ const { result } = input;
140
+ const maxImages = Math.max(0, Math.floor(input.maxImages ?? 8));
141
+ const maxImageBytes = Math.max(0, Math.floor(input.maxImageBytes ?? 4194304));
142
+ const ms = Math.round(input.wallTimeMs);
143
+ const segments = [];
144
+ const text = (value) => {
145
+ segments.push({
146
+ type: "text",
147
+ text: value
148
+ });
149
+ };
150
+ text(result.ok ? `Script completed in ${ms} ms.` : `Script failed after ${ms} ms.`);
151
+ if (result.output.length > 0) {
152
+ text("\n\nOutput:\n");
153
+ let keptImages = 0;
154
+ let keptImageBytes = 0;
155
+ let droppedImages = 0;
156
+ result.output.forEach((item, index) => {
157
+ const previous = result.output[index - 1];
158
+ if (item.type === "text") text(previous?.type === "text" ? `\n${item.text}` : item.text);
159
+ else if (keptImages < maxImages && keptImageBytes + item.data.length <= maxImageBytes) {
160
+ keptImages++;
161
+ keptImageBytes += item.data.length;
162
+ segments.push(item);
163
+ } else droppedImages++;
164
+ });
165
+ if (droppedImages > 0) text(imageOmissionNote(droppedImages, maxImages, maxImageBytes));
166
+ }
167
+ if (result.ok && result.value !== void 0) text(`\n\nReturn value:\n${encodeValue(result.value)}`);
168
+ if (!result.ok) {
169
+ const error = result.error ?? {
170
+ kind: "sandbox",
171
+ message: "The script failed without an error."
172
+ };
173
+ text(`\n\nScript error: ${error.kind}: ${error.message}`);
174
+ const frames = stackFrames(error.stack);
175
+ if (frames.length > 0) text(`\n${frames.join("\n")}`);
176
+ text(`\n\nTool calls made before the failure (they are not undone): ${summarizeCodeModeCalls(input.calls)}.`);
177
+ }
178
+ if (result.ok && result.output.length === 0 && result.value === void 0) text(" No output and no return value.");
179
+ return boundCodeModeSegments(mergeText(segments), input.maxChars);
180
+ };
181
+ //#endregion
182
+ export { boundCodeModeSegments, codeModeResultSegments, codeModeSegmentsContent, defaultCodeModeMaxImageBytes, defaultCodeModeMaxImages, summarizeCodeModeCalls };
183
+
184
+ //# sourceMappingURL=output.mjs.map
@@ -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":";;AAYA,MAAM,mBAAmB,SAAiB,QAAQ,SAAU,QAAQ;AAEpE,MAAM,kBAAkB,SAAiB,QAAQ,SAAU,QAAQ;AAGnE,MAAM,WAAW,MAAc,WAC7B,SAAS,KAAK,SAAS,KAAK,UAAU,gBAAgB,KAAK,WAAW,SAAS,CAAC,CAAC,IAC7E,SAAS,IACT;AAEN,MAAM,WAAW,MAAc,UAC7B,QAAQ,KAAK,QAAQ,KAAK,UAAU,eAAe,KAAK,WAAW,KAAK,CAAC,IAAI,QAAQ,IAAI;AAE3F,MAAM,cAAc,aAClB,SAAS,QACN,OAAO,YAAY,SAAS,QAAQ,SAAS,SAAS,QAAQ,KAAK,SAAS,IAC7E,CACF;AAEF,MAAM,kBAAkB,YAAoB,WAC1C,UAAU,WAAW,aAAa,SAAS,IAAI,QAAQ,OAAO,QAAQ,WAAW,IAAI,KAAK,QAAQ,GAAG;AAEvG,MAAM,gBAAgB;;;;;AAMtB,MAAa,yBACX,UACA,aACyC;CACzC,MAAM,QAAQ,WAAW,QAAQ;CAEjC,IAAI,SAAS,UAAU,OAAO;CAE9B,MAAM,SAAS,KAAK,IAAI,GAAG,WAAW,aAAa;CACnD,IAAI,aAAa,KAAK,KAAK,SAAS,CAAC;CACrC,IAAI,aAAa,SAAS;CAE1B,MAAM,OAAqC,CAAC;CAC5C,IAAI,YAAY;CAChB,IAAI,cAAc;CAElB,OAAO,YAAY,SAAS,UAAU,aAAa,GAAG,aAAa;EACjE,MAAM,UAAU,SAAS;EAEzB,IAAI,YAAY,KAAA,GAAW;EAE3B,IAAI,QAAQ,SAAS,SAAS;GAC5B,KAAK,KAAK,OAAO;GACjB;EACF;EAEA,IAAI,QAAQ,KAAK,UAAU,YAAY;GACrC,KAAK,KAAK,OAAO;GACjB,cAAc,QAAQ,KAAK;GAC3B;EACF;EAEA,cAAc,QAAQ,QAAQ,MAAM,UAAU;EAC9C,KAAK,KAAK;GAAE,MAAM;GAAQ,MAAM,QAAQ,KAAK,MAAM,GAAG,WAAW;EAAE,CAAC;EACpE,aAAa;EACb;CACF;CAEA,MAAM,OAAqC,CAAC;CAC5C,IAAI,YAAY,SAAS,SAAS;CAClC,IAAI;CAEJ,OAAO,aAAa,aAAa,aAAa,GAAG,aAAa;EAC5D,MAAM,UAAU,SAAS;EAEzB,IAAI,YAAY,KAAA,GAAW;EAE3B,IAAI,QAAQ,SAAS,SAAS;GAC5B,IAAI,cAAc,WAAW;GAC7B,KAAK,QAAQ,OAAO;GACpB;EACF;EAEA,MAAM,YACJ,cAAc,YAAY,QAAQ,KAAK,SAAS,cAAc,QAAQ,KAAK;EAE7E,IAAI,aAAa,cAAc,cAAc,WAAW;GACtD,KAAK,QAAQ,OAAO;GACpB,cAAc;GACd;EACF;EAEA,YAAY,QAAQ,QAAQ,MAAM,QAAQ,KAAK,SAAS,KAAK,IAAI,WAAW,UAAU,CAAC;EACvF,KAAK,QAAQ;GAAE,MAAM;GAAQ,MAAM,QAAQ,KAAK,MAAM,SAAS;EAAE,CAAC;EAClE,aAAa;EACb;CACF;CAEA,MAAM,WAAW,WAAW,IAAI,IAAI,WAAW,IAAI;CACnD,MAAM,aAAa,CAAC,GAAG,MAAM,GAAG,IAAI,EAAE,QAAO,YAAW,QAAQ,SAAS,OAAO,EAAE;CAClF,MAAM,YAAY,SAAS,QAAO,YAAW,QAAQ,SAAS,OAAO,EAAE;CAEvE,OAAO;EACL,GAAG;EACH;GAAE,MAAM;GAAQ,MAAM,eAAe,QAAQ,UAAU,YAAY,UAAU;EAAE;EAC/E,GAAG;CACL;AACF;AAEA,MAAM,aAAa,aACjB,SAAS,QAAsC,QAAQ,YAAY;CACjE,MAAM,OAAO,OAAO,GAAG,EAAE;CAEzB,IAAI,QAAQ,SAAS,UAAU,MAAM,SAAS,QAC5C,OAAO,OAAO,SAAS,KAAK;EAAE,MAAM;EAAQ,MAAM,KAAK,OAAO,QAAQ;CAAK;MACtE,IAAI,QAAQ,SAAS,WAAW,QAAQ,KAAK,SAAS,GAC3D,OAAO,KAAK,OAAO;CAGrB,OAAO;AACT,GAAG,CAAC,CAAC;;AAGP,MAAa,2BACX,aACY;CACZ,MAAM,SAAS,UAAU,QAAQ;CAEjC,IAAI,OAAO,OAAM,YAAW,QAAQ,SAAS,MAAM,GACjD,OAAO,OAAO,KAAI,YAAY,QAAQ,SAAS,SAAS,QAAQ,OAAO,EAAG,EAAE,KAAK,EAAE;CAGrF,OAAO,OAAO,KAAI,YAChB,QAAQ,SAAS,SACb,SAAS,KAAK,EAAE,MAAM,QAAQ,KAAK,CAAC,IACpC,UAAU,KAAK;EACb,QAAQ,6BAA6B,QAAQ,IAAI;EACjD,UAAU,QAAQ;CACpB,CAAC,CACP;AACF;AAOA,MAAM,cAAmD;CAAC;CAAM;CAAS;AAAW;;AAGpF,MAAa,0BAA0B,UAAsD;CAC3F,IAAI,MAAM,WAAW,GAAG,OAAO;CAE/B,MAAM,yBAAS,IAAI,IAA+C;CAElE,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,SAAS,OAAO,IAAI,KAAK,IAAI,qBAAK,IAAI,IAAkC;EAE9E,OAAO,IAAI,KAAK,SAAS,OAAO,IAAI,KAAK,MAAM,KAAK,KAAK,CAAC;EAC1D,OAAO,IAAI,KAAK,MAAM,MAAM;CAC9B;CAEA,OAAO,CAAC,GAAG,MAAM,EACd,KACE,CAAC,MAAM,YACN,GAAG,KAAK,IAAI,YACT,SAAQ,WAAU;EACjB,MAAM,QAAQ,OAAO,IAAI,MAAM;EAE/B,OAAO,UAAU,KAAA,IAAY,CAAC,IAAI,CAAC,GAAG,MAAM,GAAG,QAAQ;CACzD,CAAC,EACA,KAAK,IAAI,GAChB,EACC,KAAK,IAAI;AACd;AAEA,MAAM,eAAe,UACnB,UAAU,KAAA,IACN,CAAC,IACD,MAAM,MAAM,IAAI,EAAE,QAAO,SAAQ,WAAW,KAAK,IAAI,KAAK,cAAc,KAAK,IAAI,CAAC;AAExF,MAAM,eAAe,UAA2B;CAC9C,IAAI;EACF,OAAO,KAAK,UAAU,KAAK,KAAK;CAClC,QAAQ;EACN,OAAO;CACT;AACF;;AAGA,MAAa,2BAA2B;;AAGxC,MAAa,+BAA+B,IAAI,OAAO;AAEvD,MAAM,qBAAqB,SAAiB,WAAmB,kBAC7D,UAAU,QAAQ,QAAQ,YAAY,IAAI,KAAK,IAAI,mCAAmC,UAAU,cAAc,cAAc;;;;;;;;;AAU9H,MAAa,0BAA0B,UAOK;CAC1C,MAAM,EAAE,WAAW;CACnB,MAAM,YAAY,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,aAAA,CAAqC,CAAC;CACrF,MAAM,gBAAgB,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,iBAAA,OAA6C,CAAC;CACjG,MAAM,KAAK,KAAK,MAAM,MAAM,UAAU;CACtC,MAAM,WAAyC,CAAC;CAEhD,MAAM,QAAQ,UAAkB;EAC9B,SAAS,KAAK;GAAE,MAAM;GAAQ,MAAM;EAAM,CAAC;CAC7C;CAEA,KAAK,OAAO,KAAK,uBAAuB,GAAG,QAAQ,uBAAuB,GAAG,KAAK;CAElF,IAAI,OAAO,OAAO,SAAS,GAAG;EAC5B,KAAK,eAAe;EAEpB,IAAI,aAAa;EACjB,IAAI,iBAAiB;EACrB,IAAI,gBAAgB;EAEpB,OAAO,OAAO,SAAS,MAAM,UAAU;GACrC,MAAM,WAAW,OAAO,OAAO,QAAQ;GAEvC,IAAI,KAAK,SAAS,QAChB,KAAK,UAAU,SAAS,SAAS,KAAK,KAAK,SAAS,KAAK,IAAI;QACxD,IAAI,aAAa,aAAa,iBAAiB,KAAK,KAAK,UAAU,eAAe;IACvF;IACA,kBAAkB,KAAK,KAAK;IAC5B,SAAS,KAAK,IAAI;GACpB,OACE;EAEJ,CAAC;EAED,IAAI,gBAAgB,GAAG,KAAK,kBAAkB,eAAe,WAAW,aAAa,CAAC;CACxF;CAEA,IAAI,OAAO,MAAM,OAAO,UAAU,KAAA,GAChC,KAAK,sBAAsB,YAAY,OAAO,KAAK,GAAG;CAGxD,IAAI,CAAC,OAAO,IAAI;EACd,MAAM,QAAQ,OAAO,SAAS;GAC5B,MAAM;GACN,SAAS;EACX;EAEA,KAAK,qBAAqB,MAAM,KAAK,IAAI,MAAM,SAAS;EAExD,MAAM,SAAS,YAAY,MAAM,KAAK;EAEtC,IAAI,OAAO,SAAS,GAAG,KAAK,KAAK,OAAO,KAAK,IAAI,GAAG;EAEpD,KACE,iEAAiE,uBAAuB,MAAM,KAAK,EAAE,EACvG;CACF;CAEA,IAAI,OAAO,MAAM,OAAO,OAAO,WAAW,KAAK,OAAO,UAAU,KAAA,GAC9D,KAAK,iCAAiC;CAGxC,OAAO,sBAAsB,UAAU,QAAQ,GAAG,MAAM,QAAQ;AAClE"}
@@ -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"}
@@ -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"}
@@ -0,0 +1,77 @@
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; /** Set on the result of a re-executed call whose earlier execution was abandoned. */
11
+ readonly interrupted?: true; /** With `interrupted`: the abandoned execution's ledgered nested calls. */
12
+ readonly interruptedCalls?: CodeModeInterruptedCalls;
13
+ /** With `interrupted`, instead of `interruptedCalls`: the nested calls could not be listed
14
+ * (the ledger's `list` failed), so any of them may have been applied.
15
+ */
16
+ readonly interruptedCallsUnavailable?: true;
17
+ };
18
+ };
19
+ /** What an abandoned execution's ledgered nested call is known to have done: `applied` (recorded
20
+ * a result), `failed` (recorded a `ToolError` or an `isError` result), or `unknown` (started, no
21
+ * recorded outcome, so it may have been applied).
22
+ */
23
+ type CodeModeInterruptedCallStatus = 'applied' | 'failed' | 'unknown';
24
+ /** One ledgered nested call of an abandoned execution. `key` is its ledger key
25
+ * (`<toolCallId>/<seq>`); `args` is compact JSON, cut with a trailing `…` (then no longer JSON).
26
+ */
27
+ type CodeModeInterruptedCall = {
28
+ readonly key: string;
29
+ readonly toolName: string;
30
+ readonly args: string;
31
+ readonly status: CodeModeInterruptedCallStatus;
32
+ };
33
+ /**
34
+ * `structuredContent.codemode.interruptedCalls`: plain JSON, in sequence order, bounded like
35
+ * `nestedCalls` (at most `limits.maxNestedCalls` calls; arguments within 8 KiB per call and
36
+ * 32 KiB in total). `complete: false` means calls were dropped or arguments cut, here or already
37
+ * in the ledger entry (`ToolLedgerEntry.argsTruncated`); `counts` covers every ledgered nested
38
+ * call, dropped ones included. Calls the ledger policy skips (by default read-only calls) never
39
+ * appear.
40
+ */
41
+ type CodeModeInterruptedCalls = {
42
+ readonly calls: ReadonlyArray<CodeModeInterruptedCall>;
43
+ readonly complete: boolean;
44
+ readonly counts: {
45
+ readonly applied: number;
46
+ readonly failed: number;
47
+ readonly unknown: number;
48
+ };
49
+ };
50
+ /** Largest stored value: characters of JSON (256 KiB), as the pi engine counts them. */
51
+ declare const maxCodeModeStoreValueChars: number;
52
+ /** Largest store: characters of keys plus JSON values (1 MiB), as the pi engine counts them. */
53
+ declare const maxCodeModeStoreTotalChars: number;
54
+ /** One prior tool result with the name of the tool that produced it. */
55
+ type CodeModeToolResultEntry = {
56
+ readonly toolName: string;
57
+ readonly result: {
58
+ readonly structuredContent?: unknown;
59
+ };
60
+ };
61
+ /**
62
+ * Rebuilds the code mode store from prior tool results, oldest first: applies the `storeWrites` of
63
+ * each successful script of the code mode tool named `toolName` (default `codemode`) in order.
64
+ * Results of other tools (even when their `structuredContent` looks like a code mode result),
65
+ * failed scripts, and malformed contents are skipped. Transcript `ToolResultMessage`s carry no tool
66
+ * name; pair each with the name of its assistant tool call.
67
+ *
68
+ * The rebuilt store keeps the engine bounds: a write whose JSON value exceeds 256 Ki characters, or
69
+ * that would grow the store beyond 1 Mi characters (keys plus JSON values), is dropped and the key
70
+ * keeps its previous value. Deletions always apply. Pass the value from `loadStore`.
71
+ */
72
+ declare const codeModeStoreFromToolResults: (entries: ReadonlyArray<CodeModeToolResultEntry>, options?: {
73
+ readonly toolName?: string;
74
+ }) => CodeModeStore;
75
+ //#endregion
76
+ export { CodeModeInterruptedCall, CodeModeInterruptedCallStatus, CodeModeInterruptedCalls, CodeModeStructuredContent, CodeModeToolResultEntry, codeModeStoreFromToolResults, maxCodeModeStoreTotalChars, maxCodeModeStoreValueChars };
77
+ //# 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,mBAAA,EADd;IAAA,SAGA,WAAA,SAFc;IAAA,SAId,gBAAA,GAAmB,wBAAwB;IAA3C;;;IAAA,SAIA,2BAAA;EAAA;AAAA;;;;AAQ4B;KAA7B,6BAAA;;;;KAKA,uBAAA;EAAA,SACD,GAAA;EAAA,SACA,QAAA;EAAA,SACA,IAAA;EAAA,SACA,MAAA,EAAQ,6BAA6B;AAAA;AAAA;AAWhD;;;;;;;AAXgD,KAWpC,wBAAA;EAAA,SACD,KAAA,EAAO,aAAa,CAAC,uBAAA;EAAA,SACrB,QAAA;EAAA,SACA,MAAA;IAAA,SACE,OAAA;IAAA,SACA,MAAA;IAAA,SACA,OAAA;EAAA;AAAA;;cAmBA,0BAAA;AAAuC;AAAA,cAGvC,0BAAA;;KAGD,uBAAA;EAAA,SACD,QAAA;EAAA,SACA,MAAA;IAAA,SAAmB,iBAAA;EAAA;AAAA;;;;;;AAAiB;AAc/C;;;;;cAAa,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 /** Set on the result of a re-executed call whose earlier execution was abandoned. */\n readonly interrupted?: true\n /** With `interrupted`: the abandoned execution's ledgered nested calls. */\n readonly interruptedCalls?: CodeModeInterruptedCalls\n /** With `interrupted`, instead of `interruptedCalls`: the nested calls could not be listed\n * (the ledger's `list` failed), so any of them may have been applied.\n */\n readonly interruptedCallsUnavailable?: true\n }\n}\n\n/** What an abandoned execution's ledgered nested call is known to have done: `applied` (recorded\n * a result), `failed` (recorded a `ToolError` or an `isError` result), or `unknown` (started, no\n * recorded outcome, so it may have been applied).\n */\nexport type CodeModeInterruptedCallStatus = 'applied' | 'failed' | 'unknown'\n\n/** One ledgered nested call of an abandoned execution. `key` is its ledger key\n * (`<toolCallId>/<seq>`); `args` is compact JSON, cut with a trailing `…` (then no longer JSON).\n */\nexport type CodeModeInterruptedCall = {\n readonly key: string\n readonly toolName: string\n readonly args: string\n readonly status: CodeModeInterruptedCallStatus\n}\n\n/**\n * `structuredContent.codemode.interruptedCalls`: plain JSON, in sequence order, bounded like\n * `nestedCalls` (at most `limits.maxNestedCalls` calls; arguments within 8 KiB per call and\n * 32 KiB in total). `complete: false` means calls were dropped or arguments cut, here or already\n * in the ledger entry (`ToolLedgerEntry.argsTruncated`); `counts` covers every ledgered nested\n * call, dropped ones included. Calls the ledger policy skips (by default read-only calls) never\n * appear.\n */\nexport type CodeModeInterruptedCalls = {\n readonly calls: ReadonlyArray<CodeModeInterruptedCall>\n readonly complete: boolean\n readonly counts: {\n readonly applied: number\n readonly failed: number\n readonly unknown: number\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":";;;;AAyDA,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"}
@@ -0,0 +1,103 @@
1
+ import { CodeModeExecutor, CodeModeStore } from "./executor.mjs";
2
+ import { Effect } from "effect";
3
+ import { ToolCall, ToolResult } 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;
14
+ /** Nested tool calls per script; further calls reject with an Error. Default 256. The
15
+ * `nestedCalls` record keeps up to this many calls, at most `nestedToolCallMaxRecordedCalls`
16
+ * (4096) whatever the limit (argument byte budgets still apply).
17
+ */
18
+ readonly maxNestedCalls?: number; /** Model-visible result characters, cut head and tail with an omission marker. Default 40000. */
19
+ readonly maxOutputChars?: number; /** Images kept in the result, in order; later images are dropped with a note. Default 8. */
20
+ readonly maxImages?: number;
21
+ /** Base64 characters of images kept in the result in total; an image that would exceed it is
22
+ * dropped with a note. Default 4 MiB.
23
+ */
24
+ readonly maxImageBytes?: number;
25
+ };
26
+ declare const defaultCodeModeLimits: {
27
+ readonly timeoutMs: 120000;
28
+ readonly memoryLimitBytes: number;
29
+ readonly maxNestedCalls: 256;
30
+ readonly maxOutputChars: 40000;
31
+ readonly maxImages: 8;
32
+ readonly maxImageBytes: number;
33
+ };
34
+ /** Kept between a host deadline and the script deadline, for result handling. */
35
+ declare const codeModeDeadlineMarginMs = 5000;
36
+ /** A deadline never clamps a script below this timeout. */
37
+ declare const codeModeMinimumTimeoutMs = 1000;
38
+ type MakeCodeModeToolOptions<Context> = {
39
+ readonly executor: CodeModeExecutor; /** Default `codemode`. */
40
+ readonly name?: string; /** Estimated tokens (four characters each) for listing nested tools. Default 3000. */
41
+ readonly inlineBudget?: number;
42
+ readonly limits?: CodeModeLimits;
43
+ /**
44
+ * Epoch milliseconds by which the tool call must end, for example the remaining function budget
45
+ * of a Workflow step. The script timeout is clamped to it minus 5 s, but never below 1 s.
46
+ */
47
+ readonly deadline?: (context: Context) => number | undefined;
48
+ /**
49
+ * The store scripts read with `load(key)`, usually rebuilt from the transcript with
50
+ * `codeModeStoreFromToolResults`. Without it every script starts with an empty store.
51
+ */
52
+ readonly loadStore?: (context: Context) => Effect.Effect<CodeModeStore, ToolError>;
53
+ /**
54
+ * Runs before each nested call executes, with the nested call (id `<toolCallId>/<seq>`) and the
55
+ * host context. A failure rejects that call in the script with the message and records it as an
56
+ * `error` without executing it. Host decorators around the `ToolExecutor` (outside
57
+ * `ResolvedToolSet.execute`) never see nested calls: put per-call run-authority checks here or in
58
+ * registration-level wrappers.
59
+ */
60
+ readonly beforeNestedCall?: (input: {
61
+ readonly call: ToolCall;
62
+ readonly context: Context;
63
+ }) => Effect.Effect<void, string>;
64
+ /**
65
+ * Runs after each nested call that `beforeNestedCall` admitted, with its outcome: `success` (a
66
+ * result without `isError`), `failure` (an error result or an unexpected failure), or
67
+ * `interrupted` (cancelled, for example still running when the script ended). `durationMs`
68
+ * covers the execution; `result` is present when the call produced one. Calls rejected by
69
+ * `beforeNestedCall` or the call limit never ran and are not reported. The script waits for the
70
+ * hook before it sees the call settle. A failing hook (a failure, a defect, or a synchronous
71
+ * throw) is logged and never changes the call's result.
72
+ */
73
+ readonly afterNestedCall?: (input: CodeModeAfterNestedCallInput<Context>) => Effect.Effect<void>;
74
+ };
75
+ /** Outcome of a nested call for `afterNestedCall`. */
76
+ type CodeModeNestedCallOutcome = 'success' | 'failure' | 'interrupted';
77
+ type CodeModeAfterNestedCallInput<Context> = {
78
+ readonly call: ToolCall;
79
+ readonly outcome: CodeModeNestedCallOutcome;
80
+ readonly durationMs: number;
81
+ readonly context: Context;
82
+ readonly result?: ToolResult;
83
+ };
84
+ /**
85
+ * The code mode tool: one registration (default name `codemode`, input `{ code }`) whose scripts
86
+ * call the other code-mode-callable tools of the same `resolveTools` resolution. Nested calls run
87
+ * through the resolved execute path with the same host context (input decoding, enablement,
88
+ * registration wrappers), get ids `<toolCallId>/<seq>`, and are recorded on the result's
89
+ * `nestedCalls`. The resolved description lists the callable tools (see
90
+ * `renderCodeModeDescription`).
91
+ *
92
+ * Access is `write`: scripts can call any write tool the resolution exposes to code mode; each
93
+ * nested call keeps its own access metadata and host wrappers.
94
+ *
95
+ * With a tool ledger (`resolveTools(..., { ledger })`) the code mode call itself is ledgered, so a
96
+ * re-executed call never runs its script again: a completed call returns its stored result, an
97
+ * in-flight one is awaited, and an abandoned one returns an interrupted result listing its
98
+ * ledgered nested calls. Nested write calls are ledgered under `<toolCallId>/<seq>`.
99
+ */
100
+ declare const makeCodeModeTool: <Context>(options: MakeCodeModeToolOptions<Context>) => ToolRegistration<Context>;
101
+ //#endregion
102
+ export { CodeModeAfterNestedCallInput, CodeModeLimits, CodeModeNestedCallOutcome, MakeCodeModeToolOptions, codeModeDeadlineMarginMs, codeModeMinimumTimeoutMs, codeModeToolName, defaultCodeModeLimits, makeCodeModeTool };
103
+ //# sourceMappingURL=tool.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.d.mts","names":[],"sources":["../src/tool.ts"],"mappings":";;;;;;;;cAsEa,gBAAA;AAAb;AAAA,KAGY,cAAA;kGAED,SAAA,WALkB;EAAA,SAOlB,gBAAA;EAJe;;;;EAAA,SASf,cAAA;EAAA,SAEA,cAAA,WAEA;EAAA,SAAA,SAAA;EAIa;AAAA;AAGxB;EAHwB,SAAb,aAAA;AAAA;AAAA,cAGE,qBAAA;EAAA;;;;;;;;cAUA,wBAAA;;cAGA,wBAAA;AAAA,KAED,uBAAA;EAAA,SACD,QAAA,EAAU,gBAAA,EAHR;EAAA,SAKF,IAAA;WAEA,YAAA;EAAA,SACA,MAAA,GAAS,cAAA;EANR;;;;EAAA,SAWD,QAAA,IAAY,OAAA,EAAS,OAAA;EAAA;;;;EAAA,SAKrB,SAAA,IAAa,OAAA,EAAS,OAAA,KAAY,MAAA,CAAO,MAAA,CAAO,aAAA,EAAe,SAAA;EASvD;;;;;;;EAAA,SADR,gBAAA,IAAoB,KAAA;IAAA,SAClB,IAAA,EAAM,QAAA;IAAA,SACN,OAAA,EAAS,OAAA;EAAA,MACd,MAAA,CAAO,MAAA;EAxBJ;;;;;;;;;EAAA,SAkCA,eAAA,IAAmB,KAAA,EAAO,4BAAA,CAA6B,OAAA,MAAa,MAAA,CAAO,MAAA;AAAA;;KAI1E,yBAAA;AAAA,KAEA,4BAAA;EAAA,SACD,IAAA,EAAM,QAAA;EAAA,SACN,OAAA,EAAS,yBAAA;EAAA,SACT,UAAA;EAAA,SACA,OAAA,EAAS,OAAA;EAAA,SACT,MAAA,GAAS,UAAA;AAAA;;;;;;;;;;AAXwE;AAI5F;;;;AAAqC;AAErC;cAskBa,gBAAA,YACX,OAAA,EAAS,uBAAA,CAAwB,OAAA,MAChC,gBAAA,CAAiB,OAAA"}