@yolk-sdk/codemode 0.1.0-canary.122 → 0.1.0-canary.124
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/README.md +2 -2
- package/dist/catalog.d.mts +46 -38
- package/dist/catalog.d.mts.map +1 -1
- package/dist/catalog.mjs +6 -4
- package/dist/catalog.mjs.map +1 -1
- package/dist/classifier-tool.d.mts +15 -15
- package/dist/classifier-tool.d.mts.map +1 -1
- package/dist/classifier-tool.mjs +4 -4
- package/dist/classifier-tool.mjs.map +1 -1
- package/dist/executor.d.mts +29 -24
- package/dist/executor.d.mts.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +2 -2
- package/dist/node.d.mts +5 -8
- package/dist/node.d.mts.map +1 -1
- package/dist/node.mjs +3 -3
- package/dist/node.mjs.map +1 -1
- package/dist/output.d.mts +8 -12
- package/dist/output.d.mts.map +1 -1
- package/dist/output.mjs +2 -3
- package/dist/output.mjs.map +1 -1
- package/dist/search.d.mts +10 -11
- package/dist/search.d.mts.map +1 -1
- package/dist/search.mjs +1 -2
- package/dist/search.mjs.map +1 -1
- package/dist/store.d.mts +15 -16
- package/dist/store.d.mts.map +1 -1
- package/dist/store.mjs +3 -4
- package/dist/store.mjs.map +1 -1
- package/dist/tool.d.mts +23 -20
- package/dist/tool.d.mts.map +1 -1
- package/dist/tool.mjs +5 -5
- package/dist/tool.mjs.map +1 -1
- package/package.json +4 -4
package/dist/executor.d.mts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import * as Schema from "effect/Schema";
|
|
2
2
|
import { ToolJsonSchema } from "@yolk-sdk/agent/protocol";
|
|
3
|
-
|
|
4
|
-
//#region src/executor.d.ts
|
|
5
3
|
/** A JSON Schema document. Only shapes declarations; values are never validated against it. */
|
|
6
|
-
type CodeModeJsonSchema = ToolJsonSchema;
|
|
4
|
+
export type CodeModeJsonSchema = ToolJsonSchema;
|
|
7
5
|
/** Store contents: small JSON values by key. */
|
|
8
|
-
type CodeModeStore = Readonly<Record<string, Schema.Json>>;
|
|
9
|
-
type CodeModeToolContext = {
|
|
10
|
-
/** Aborted when the script ends (unawaited calls), times out, or the execution is aborted. */
|
|
6
|
+
export type CodeModeStore = Readonly<Record<string, Schema.Json>>;
|
|
7
|
+
export type CodeModeToolContext = {
|
|
8
|
+
/** Aborted when the script ends (unawaited calls), times out, or the execution is aborted. */
|
|
9
|
+
readonly signal: AbortSignal;
|
|
11
10
|
};
|
|
12
11
|
/**
|
|
13
12
|
* A function a script can call. Tools are called as `tools.<identifier>(args)` and
|
|
@@ -28,7 +27,7 @@ type CodeModeToolContext = {
|
|
|
28
27
|
* rejection names the call the way scripts reach it, as `makeCodeModeTool` does: the first tool
|
|
29
28
|
* with an identifier is `tools.<identifier>`, a later one `tools["<name>"]`.
|
|
30
29
|
*/
|
|
31
|
-
type CodeModeExecutorTool = {
|
|
30
|
+
export type CodeModeExecutorTool = {
|
|
32
31
|
readonly name: string;
|
|
33
32
|
readonly description?: string;
|
|
34
33
|
readonly inputSchema?: CodeModeJsonSchema;
|
|
@@ -44,12 +43,14 @@ type CodeModeExecutorTool = {
|
|
|
44
43
|
* A top-level function (`name`) or namespace member (`namespace.member`) for scripts. Globals are
|
|
45
44
|
* host helpers: they are not tools and are not recorded as nested calls.
|
|
46
45
|
*/
|
|
47
|
-
type CodeModeExecutorGlobal = Omit<CodeModeExecutorTool, 'callLabel'> & {
|
|
48
|
-
/** `execute` receives every call argument as an array instead of the first one. */
|
|
46
|
+
export type CodeModeExecutorGlobal = Omit<CodeModeExecutorTool, 'callLabel'> & {
|
|
47
|
+
/** `execute` receives every call argument as an array instead of the first one. */
|
|
48
|
+
readonly spread?: boolean;
|
|
49
|
+
/** TypeScript parameter list and return type for declarations. */
|
|
49
50
|
readonly signature?: string;
|
|
50
51
|
};
|
|
51
52
|
/** One item of script output, in the order the script produced it. `data` is base64. */
|
|
52
|
-
type CodeModeOutputItem = {
|
|
53
|
+
export type CodeModeOutputItem = {
|
|
53
54
|
readonly type: 'text';
|
|
54
55
|
readonly text: string;
|
|
55
56
|
} | {
|
|
@@ -63,29 +64,35 @@ type CodeModeOutputItem = {
|
|
|
63
64
|
* - `aborted`: the caller's signal fired.
|
|
64
65
|
* - `sandbox`: the engine failed outside the script's control.
|
|
65
66
|
*/
|
|
66
|
-
type CodeModeErrorKind = 'script' | 'timeout' | 'aborted' | 'sandbox';
|
|
67
|
-
type CodeModeError = {
|
|
67
|
+
export type CodeModeErrorKind = 'script' | 'timeout' | 'aborted' | 'sandbox';
|
|
68
|
+
export type CodeModeError = {
|
|
68
69
|
readonly kind: CodeModeErrorKind;
|
|
69
70
|
readonly message: string;
|
|
70
71
|
readonly stack?: string;
|
|
71
72
|
};
|
|
72
73
|
/** Keys a successful script changed with `store()`; `delete` lists keys stored as `undefined`. */
|
|
73
|
-
type CodeModeStoreWrites = {
|
|
74
|
+
export type CodeModeStoreWrites = {
|
|
74
75
|
readonly set: CodeModeStore;
|
|
75
76
|
readonly delete: ReadonlyArray<string>;
|
|
76
77
|
};
|
|
77
|
-
type CodeModeExecutionResult = {
|
|
78
|
-
readonly ok: boolean;
|
|
78
|
+
export type CodeModeExecutionResult = {
|
|
79
|
+
readonly ok: boolean;
|
|
80
|
+
/** The script's return value (JSON); absent when it returned nothing. */
|
|
79
81
|
readonly value?: unknown;
|
|
80
|
-
readonly error?: CodeModeError;
|
|
81
|
-
|
|
82
|
+
readonly error?: CodeModeError;
|
|
83
|
+
/** Kept for failed executions too, up to the failure. */
|
|
84
|
+
readonly output: ReadonlyArray<CodeModeOutputItem>;
|
|
85
|
+
/** Only successful executions report writes. */
|
|
82
86
|
readonly storeWrites?: CodeModeStoreWrites;
|
|
83
87
|
};
|
|
84
|
-
type CodeModeExecuteOptions = {
|
|
88
|
+
export type CodeModeExecuteOptions = {
|
|
85
89
|
readonly tools: ReadonlyArray<CodeModeExecutorTool>;
|
|
86
|
-
readonly globals: ReadonlyArray<CodeModeExecutorGlobal>;
|
|
87
|
-
|
|
88
|
-
readonly
|
|
90
|
+
readonly globals: ReadonlyArray<CodeModeExecutorGlobal>;
|
|
91
|
+
/** Overall deadline, including time spent in tools and waiting for a free execution slot. */
|
|
92
|
+
readonly timeoutMs: number;
|
|
93
|
+
/** Heap cap of the script's VM. */
|
|
94
|
+
readonly memoryLimitBytes: number;
|
|
95
|
+
/** Values the script reads with `load(key)`. */
|
|
89
96
|
readonly store: CodeModeStore;
|
|
90
97
|
readonly signal: AbortSignal;
|
|
91
98
|
/**
|
|
@@ -109,9 +116,7 @@ type CodeModeExecuteOptions = {
|
|
|
109
116
|
* `@yolk-sdk/codemode/node` provides `makePiCodeModeExecutor`; hosts on other runtimes supply their
|
|
110
117
|
* own engine behind this interface.
|
|
111
118
|
*/
|
|
112
|
-
type CodeModeExecutor = {
|
|
119
|
+
export type CodeModeExecutor = {
|
|
113
120
|
readonly execute: (code: string, options: CodeModeExecuteOptions) => Promise<CodeModeExecutionResult>;
|
|
114
121
|
};
|
|
115
|
-
//#endregion
|
|
116
|
-
export { CodeModeError, CodeModeErrorKind, CodeModeExecuteOptions, CodeModeExecutionResult, CodeModeExecutor, CodeModeExecutorGlobal, CodeModeExecutorTool, CodeModeJsonSchema, CodeModeOutputItem, CodeModeStore, CodeModeStoreWrites, CodeModeToolContext };
|
|
117
122
|
//# sourceMappingURL=executor.d.mts.map
|
package/dist/executor.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.d.mts","names":[],"sources":["../src/executor.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"executor.d.mts","names":[],"sources":["../src/executor.ts"],"mappings":";;;YAIY,qBAAqB;;YAGrB,gBAAgB,SAAS,eAAe,OAAO;YAE/C;;WAED,QAAQ;;;;;;;;;;;;;;;;;;;;;YAsBP;WACD;WACA;WACA,cAAc;WACd,eAAe;;;;;WAKf;WACA,UAAU,eAAe,SAAS,wBAAwB;;;;;;YAOzD,yBAAyB,KAAK;;WAE/B;;WAEA;;;YAIC;WACG;WAAuB;;WACvB;WAAwB;WAAuB;;;;;;;;YAQlD;YAEA;WACD,MAAM;WACN;WACA;;;YAIC;WACD,KAAK;WACL,QAAQ;;YAGP;WACD;;WAEA;WACA,QAAQ;;WAER,QAAQ,cAAc;;WAEtB,cAAc;;YAGb;WACD,OAAO,cAAc;WACrB,SAAS,cAAc;;WAEvB;;WAEA;;WAEA,OAAO;WACP,QAAQ;;;;;;WAMR,UAAU;aAAyB;aAAsB;;;;;;;;;;;;;;YAcxD;WACD,UACP,cACA,SAAS,2BACN,QAAQ"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { CodeModeError, CodeModeErrorKind, CodeModeExecuteOptions, CodeModeExecutionResult, CodeModeExecutor, CodeModeExecutorGlobal, CodeModeExecutorTool, CodeModeJsonSchema, CodeModeOutputItem, CodeModeStore, CodeModeStoreWrites, CodeModeToolContext } from "./executor.mjs";
|
|
2
2
|
import { CodeModeCatalogSummary, CodeModeCatalogTool, CodeModeDescriptionInput, CodeModeListing, CodeModeStagingDescription, CodeModeToolExposure, codeModeCatalog, codeModeCatalogInstruction, codeModeCatalogInstructionKey, codeModeStageOnlyCatalog, codeModeToolSignature, defaultCodeModeInlineBudget, describeCodeModeTool, estimateCodeModeTokens, findCodeModeTool, maxCodeModeSearchSignatureChars, renderCodeModeCatalog, renderCodeModeDescription, renderCodeModeInstructedDescription, selectCodeModeListing, summarizeCodeModeCatalog } from "./catalog.mjs";
|
|
3
|
-
import { ClassifierConcurrencyLimiter, ClassifierToolParams, MakeClassifierToolOptions, classifierToolName, defaultClassifierProcessLimiter, defaultClassifierProcessMaxConcurrency, defaultClassifierToolMaxConcurrency, makeClassifierConcurrencyLimiter, makeClassifierTool } from "./classifier-tool.mjs";
|
|
4
3
|
import { CodeModeSearchHit, CodeModeSearchOptions, codeModeSearchTokens, defaultCodeModeSearchLimit, searchCodeModeTools } from "./search.mjs";
|
|
5
4
|
import { CodeModeCallSummary, CodeModeResultSegment, boundCodeModeSegments, codeModeResultSegments, codeModeSegmentsContent, defaultCodeModeMaxImageBytes, defaultCodeModeMaxImages, summarizeCodeModeCalls } from "./output.mjs";
|
|
6
5
|
import { CodeModeInterruptedCall, CodeModeInterruptedCallStatus, CodeModeInterruptedCalls, CodeModeStagedPlan, CodeModeStructuredContent, CodeModeToolResultEntry, codeModeStoreFromToolResults, maxCodeModeStoreTotalChars, maxCodeModeStoreValueChars } from "./store.mjs";
|
|
7
6
|
import { CodeModeAfterNestedCallInput, CodeModeLimits, CodeModeNestedCallOutcome, CodeModeStagingOptions, MakeCodeModeToolOptions, codeModeDeadlineMarginMs, codeModeMinimumTimeoutMs, codeModeToolName, defaultCodeModeLimits, makeCodeModeTool } from "./tool.mjs";
|
|
7
|
+
import { ClassifierConcurrencyLimiter, ClassifierToolParams, MakeClassifierToolOptions, classifierToolName, defaultClassifierProcessLimiter, defaultClassifierProcessMaxConcurrency, defaultClassifierToolMaxConcurrency, makeClassifierConcurrencyLimiter, makeClassifierTool } from "./classifier-tool.mjs";
|
|
8
8
|
export { type ClassifierConcurrencyLimiter, ClassifierToolParams, type CodeModeAfterNestedCallInput, type CodeModeCallSummary, CodeModeCatalogSummary, type CodeModeCatalogTool, type CodeModeDescriptionInput, type CodeModeError, type CodeModeErrorKind, type CodeModeExecuteOptions, type CodeModeExecutionResult, type CodeModeExecutor, type CodeModeExecutorGlobal, type CodeModeExecutorTool, type CodeModeInterruptedCall, type CodeModeInterruptedCallStatus, type CodeModeInterruptedCalls, type CodeModeJsonSchema, type CodeModeLimits, type CodeModeListing, type CodeModeNestedCallOutcome, type CodeModeOutputItem, type CodeModeResultSegment, type CodeModeSearchHit, type CodeModeSearchOptions, type CodeModeStagedPlan, type CodeModeStagingDescription, type CodeModeStagingOptions, type CodeModeStore, type CodeModeStoreWrites, type CodeModeStructuredContent, type CodeModeToolContext, type CodeModeToolExposure, type CodeModeToolResultEntry, type MakeClassifierToolOptions, type MakeCodeModeToolOptions, boundCodeModeSegments, classifierToolName, codeModeCatalog, codeModeCatalogInstruction, codeModeCatalogInstructionKey, codeModeDeadlineMarginMs, codeModeMinimumTimeoutMs, codeModeResultSegments, codeModeSearchTokens, codeModeSegmentsContent, codeModeStageOnlyCatalog, codeModeStoreFromToolResults, codeModeToolName, codeModeToolSignature, defaultClassifierProcessLimiter, defaultClassifierProcessMaxConcurrency, defaultClassifierToolMaxConcurrency, defaultCodeModeInlineBudget, defaultCodeModeLimits, defaultCodeModeMaxImageBytes, defaultCodeModeMaxImages, defaultCodeModeSearchLimit, describeCodeModeTool, estimateCodeModeTokens, findCodeModeTool, makeClassifierConcurrencyLimiter, makeClassifierTool, makeCodeModeTool, maxCodeModeSearchSignatureChars, maxCodeModeStoreTotalChars, maxCodeModeStoreValueChars, renderCodeModeCatalog, renderCodeModeDescription, renderCodeModeInstructedDescription, searchCodeModeTools, selectCodeModeListing, summarizeCodeModeCalls, summarizeCodeModeCatalog };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CodeModeCatalogSummary, codeModeCatalog, codeModeCatalogInstruction, codeModeCatalogInstructionKey, codeModeStageOnlyCatalog, codeModeToolSignature, defaultCodeModeInlineBudget, describeCodeModeTool, estimateCodeModeTokens, findCodeModeTool, maxCodeModeSearchSignatureChars, renderCodeModeCatalog, renderCodeModeDescription, renderCodeModeInstructedDescription, selectCodeModeListing, summarizeCodeModeCatalog } from "./catalog.mjs";
|
|
2
|
-
import { ClassifierToolParams, classifierToolName, defaultClassifierProcessLimiter, defaultClassifierProcessMaxConcurrency, defaultClassifierToolMaxConcurrency, makeClassifierConcurrencyLimiter, makeClassifierTool } from "./classifier-tool.mjs";
|
|
3
2
|
import { codeModeSearchTokens, defaultCodeModeSearchLimit, searchCodeModeTools } from "./search.mjs";
|
|
4
3
|
import { boundCodeModeSegments, codeModeResultSegments, codeModeSegmentsContent, defaultCodeModeMaxImageBytes, defaultCodeModeMaxImages, summarizeCodeModeCalls } from "./output.mjs";
|
|
5
|
-
import { codeModeDeadlineMarginMs, codeModeMinimumTimeoutMs, codeModeToolName, defaultCodeModeLimits, makeCodeModeTool } from "./tool.mjs";
|
|
6
4
|
import { codeModeStoreFromToolResults, maxCodeModeStoreTotalChars, maxCodeModeStoreValueChars } from "./store.mjs";
|
|
5
|
+
import { codeModeDeadlineMarginMs, codeModeMinimumTimeoutMs, codeModeToolName, defaultCodeModeLimits, makeCodeModeTool } from "./tool.mjs";
|
|
6
|
+
import { ClassifierToolParams, classifierToolName, defaultClassifierProcessLimiter, defaultClassifierProcessMaxConcurrency, defaultClassifierToolMaxConcurrency, makeClassifierConcurrencyLimiter, makeClassifierTool } from "./classifier-tool.mjs";
|
|
7
7
|
export { ClassifierToolParams, CodeModeCatalogSummary, boundCodeModeSegments, classifierToolName, codeModeCatalog, codeModeCatalogInstruction, codeModeCatalogInstructionKey, codeModeDeadlineMarginMs, codeModeMinimumTimeoutMs, codeModeResultSegments, codeModeSearchTokens, codeModeSegmentsContent, codeModeStageOnlyCatalog, codeModeStoreFromToolResults, codeModeToolName, codeModeToolSignature, defaultClassifierProcessLimiter, defaultClassifierProcessMaxConcurrency, defaultClassifierToolMaxConcurrency, defaultCodeModeInlineBudget, defaultCodeModeLimits, defaultCodeModeMaxImageBytes, defaultCodeModeMaxImages, defaultCodeModeSearchLimit, describeCodeModeTool, estimateCodeModeTokens, findCodeModeTool, makeClassifierConcurrencyLimiter, makeClassifierTool, makeCodeModeTool, maxCodeModeSearchSignatureChars, maxCodeModeStoreTotalChars, maxCodeModeStoreValueChars, renderCodeModeCatalog, renderCodeModeDescription, renderCodeModeInstructedDescription, searchCodeModeTools, selectCodeModeListing, summarizeCodeModeCalls, summarizeCodeModeCatalog };
|
package/dist/node.d.mts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { CodeModeExecutor } from "./executor.mjs";
|
|
2
2
|
import { CodemodeWasmModule } from "@earendil-works/pi-codemode";
|
|
3
|
-
|
|
4
|
-
//#region src/node.d.ts
|
|
5
3
|
/** Default cap of concurrent script executions per executor. */
|
|
6
|
-
declare const defaultMaxConcurrentExecutions = 4;
|
|
7
|
-
type PiCodeModeExecutorOptions = {
|
|
4
|
+
export declare const defaultMaxConcurrentExecutions = 4;
|
|
5
|
+
export type PiCodeModeExecutorOptions = {
|
|
8
6
|
/**
|
|
9
7
|
* Compiled `quickjs-wasi/quickjs.wasm` (see pi's `loadQuickJSWasm`). Default: the file of the
|
|
10
8
|
* installed `quickjs-wasi` package, compiled once per process.
|
|
@@ -14,7 +12,8 @@ type PiCodeModeExecutorOptions = {
|
|
|
14
12
|
* Worker entry that imports `@earendil-works/pi-codemode/worker`. Default: pi's own worker file,
|
|
15
13
|
* which exists on disk when the package is not bundled (Next `serverExternalPackages`).
|
|
16
14
|
*/
|
|
17
|
-
readonly workerUrl?: string | URL;
|
|
15
|
+
readonly workerUrl?: string | URL;
|
|
16
|
+
/** Concurrent executions of this executor; more wait for a free slot. Default 4. */
|
|
18
17
|
readonly maxConcurrentExecutions?: number;
|
|
19
18
|
};
|
|
20
19
|
/**
|
|
@@ -29,7 +28,5 @@ type PiCodeModeExecutorOptions = {
|
|
|
29
28
|
* `process`). Under Next.js, list `@yolk-sdk/codemode`, `@earendil-works/pi-codemode`, and
|
|
30
29
|
* `quickjs-wasi` in `serverExternalPackages` so the worker file and wasm stay on disk.
|
|
31
30
|
*/
|
|
32
|
-
declare const makePiCodeModeExecutor: (options?: PiCodeModeExecutorOptions) => CodeModeExecutor;
|
|
33
|
-
//#endregion
|
|
34
|
-
export { PiCodeModeExecutorOptions, defaultMaxConcurrentExecutions, makePiCodeModeExecutor };
|
|
31
|
+
export declare const makePiCodeModeExecutor: (options?: PiCodeModeExecutorOptions) => CodeModeExecutor;
|
|
35
32
|
//# sourceMappingURL=node.d.mts.map
|
package/dist/node.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.d.mts","names":[],"sources":["../src/node.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"node.d.mts","names":[],"sources":["../src/node.ts"],"mappings":";;;qBAsBa;YAED;;;;;WAKD,OAAO,qBAAqB,QAAQ;;;;;WAKpC,qBAAqB;;WAErB;;;;;;;;;;;;;;qBAwbE,yBAAsB,UACxB,8BACR"}
|
package/dist/node.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import * as nodeModule from "node:module";
|
|
|
3
3
|
import { Option, Predicate } from "effect";
|
|
4
4
|
import * as Schema from "effect/Schema";
|
|
5
5
|
import { CodemodeSandbox } from "@earendil-works/pi-codemode";
|
|
6
|
-
|
|
6
|
+
// src/node.ts
|
|
7
7
|
/** Default cap of concurrent script executions per executor. */
|
|
8
8
|
const defaultMaxConcurrentExecutions = 4;
|
|
9
9
|
/** Abort- and deadline-aware counting semaphore for executions. */
|
|
@@ -212,7 +212,8 @@ const guardPrefix = "return (async (tools, console) => {";
|
|
|
212
212
|
/** JSON as a JavaScript expression (U+2028/U+2029 escaped for older parsers). */
|
|
213
213
|
const jsonSource = (value) => JSON.stringify(value).replace(/[\u2028\u2029]/g, (char) => char === "\u2028" ? "\\u2028" : "\\u2029");
|
|
214
214
|
const guardSuffix = (tools, unbound) => {
|
|
215
|
-
const
|
|
215
|
+
const names = tools.map((tool) => tool.name);
|
|
216
|
+
const labels = codeModeCallLabels(names);
|
|
216
217
|
const pairs = tools.map((tool, index) => [tool.name, tool.callLabel ?? labels[index] ?? `tools[${JSON.stringify(tool.name)}]`]);
|
|
217
218
|
const rejected = (unbound ?? []).map((entry) => [entry.key, entry.message]);
|
|
218
219
|
return `\n})(${toolsGuardSource}(tools, ${jsonSource(pairs)}, ${jsonSource(rejected)}), console)`;
|
|
@@ -394,7 +395,6 @@ const makePiCodeModeExecutor = (options = {}) => {
|
|
|
394
395
|
}
|
|
395
396
|
} };
|
|
396
397
|
};
|
|
397
|
-
//#endregion
|
|
398
398
|
export { defaultMaxConcurrentExecutions, makePiCodeModeExecutor };
|
|
399
399
|
|
|
400
400
|
//# sourceMappingURL=node.mjs.map
|
package/dist/node.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.mjs","names":[],"sources":["../src/node.ts"],"sourcesContent":["import * as nodeModule from 'node:module'\nimport { Option, Predicate } from 'effect'\nimport * as Schema from 'effect/Schema'\nimport {\n CodemodeSandbox,\n type CodemodeResult,\n type CodemodeSandboxOptions,\n type CodemodeTool,\n type CodemodeWasmModule\n} from '@earendil-works/pi-codemode'\nimport { codeModeCallLabels } from './catalog.ts'\nimport type {\n CodeModeError,\n CodeModeExecuteOptions,\n CodeModeExecutionResult,\n CodeModeExecutor,\n CodeModeExecutorGlobal,\n CodeModeExecutorTool,\n CodeModeStoreWrites\n} from './executor.ts'\n\n/** Default cap of concurrent script executions per executor. */\nexport const defaultMaxConcurrentExecutions = 4\n\nexport type PiCodeModeExecutorOptions = {\n /**\n * Compiled `quickjs-wasi/quickjs.wasm` (see pi's `loadQuickJSWasm`). Default: the file of the\n * installed `quickjs-wasi` package, compiled once per process.\n */\n readonly wasm?: CodemodeWasmModule | Promise<CodemodeWasmModule>\n /**\n * Worker entry that imports `@earendil-works/pi-codemode/worker`. Default: pi's own worker file,\n * which exists on disk when the package is not bundled (Next `serverExternalPackages`).\n */\n readonly workerUrl?: string | URL\n /** Concurrent executions of this executor; more wait for a free slot. Default 4. */\n readonly maxConcurrentExecutions?: number\n}\n\ntype SlotOutcome = 'acquired' | 'aborted' | 'timeout'\n\ntype Waiter = (outcome: SlotOutcome) => void\n\n/** Abort- and deadline-aware counting semaphore for executions. */\nconst makeExecutionSlots = (max: number) => {\n let active = 0\n const waiting: Array<Waiter> = []\n\n const acquire = (signal: AbortSignal, timeoutMs: number): Promise<SlotOutcome> => {\n if (signal.aborted) return Promise.resolve('aborted')\n\n if (active < max) {\n active++\n\n return Promise.resolve('acquired')\n }\n\n return new Promise(resolve => {\n let timer: ReturnType<typeof setTimeout> | undefined\n\n const settle: Waiter = outcome => {\n const index = waiting.indexOf(settle)\n\n if (index !== -1) waiting.splice(index, 1)\n\n if (timer !== undefined) clearTimeout(timer)\n signal.removeEventListener('abort', onAbort)\n resolve(outcome)\n }\n\n const onAbort = () => settle('aborted')\n\n waiting.push(settle)\n signal.addEventListener('abort', onAbort, { once: true })\n\n if (Number.isFinite(timeoutMs)) {\n timer = setTimeout(() => settle('timeout'), Math.max(0, timeoutMs))\n }\n })\n }\n\n const release = () => {\n const next = waiting[0]\n\n if (next === undefined) {\n active--\n } else {\n // The slot passes straight to the next waiter.\n next('acquired')\n }\n }\n\n return { acquire, release }\n}\n\nconst wrapperPrefix = 'async function __yolkCodeMode() {\\n'\n\nconst wrapperSuffix = '\\n}'\n\n/** Most values one argument may hold; a larger argument is rejected. */\nconst maxArgumentValues = 100_000\n\n/** Deepest object/array nesting one argument may have; deeper arguments are rejected. */\nconst maxArgumentDepth = 64\n\n/**\n * Evaluated in the VM before the script runs: `(tools, labels) => guardedTools`, with `labels` the\n * `[name, callLabel]` pairs of the tools in order (`codeModeCallLabels`). Each call walks its first\n * argument once, the way `JSON.stringify` reads it (each own enumerable string key and each array\n * index read once, so getters run once; a callable `toJSON` is called once with the key, `''` at\n * the root, and its result is walked at the same path), and builds a fresh copy (null-prototype\n * objects and arrays) that is what the call sends. It rejects with a `TypeError` naming the call\n * label and path when JSON would change the value instead of carrying it: non-finite numbers,\n * invalid `Date`s, `undefined`/function/symbol array items and holes, function/symbol/bigint\n * values, cycles, and objects that are neither plain (prototype `Object.prototype` or `null`) nor\n * arrays and have no `toJSON`. An argument with more than `maxArgumentValues` values or nested more\n * than `maxArgumentDepth` levels is rejected too, never sent unchecked. `undefined`-valued keys are\n * left out (absent, as JSON drops them); the same object twice (not a cycle) is copied twice. An\n * error thrown by a getter or `toJSON` rejects the call with that error. A rejected call never\n * reaches the host. Every member of pi's `tools` is wrapped; unknown members fall through to pi's\n * proxy (close-match suggestions). Not a security boundary (`globalThis.tools` is unguarded):\n * arguments are still decoded by the host.\n */\nconst toolsGuardSource = `(function (tools, labels, unbound) {\n // Captured before the script runs, so a script that changes built-ins cannot change the checks.\n const { apply, getPrototypeOf, setPrototypeOf } = Reflect\n const isFinite = Number.isFinite\n const isArray = Array.isArray\n const { keys, create, defineProperty } = Object\n const getTime = Date.prototype.getTime\n const TypeErrorCtor = TypeError\n const SetCtor = Set\n const setAdd = Set.prototype.add\n const setHas = Set.prototype.has\n const setDelete = Set.prototype.delete\n const ObjectProto = Object.prototype\n const { trunc, min } = Math\n const stringify = JSON.stringify\n const plainJson = 'pass plain JSON (objects, arrays, strings, finite numbers, booleans, null)'\n const maxValues = ${maxArgumentValues}\n const maxDepth = ${maxArgumentDepth}\n class Rejected {\n constructor(message) {\n defineProperty(this, 'message', { value: message })\n }\n }\n const keyPath = (path, key) =>\n /^[A-Za-z_$][\\\\w$]*$/.test(key)\n ? (path === '' ? key : path + '.' + key)\n : path + '[' + stringify(key) + ']'\n const typeName = value => {\n const proto = getPrototypeOf(value)\n const name = proto && typeof proto.constructor === 'function' ? proto.constructor.name : ''\n return name ? (/^[AEIOU]/.test(name) ? 'an ' : 'a ') + name : 'a non-plain object'\n }\n // The JSON copy of a value already read from its holder at key (undefined: absent), or throws\n // Rejected with why JSON would not carry it unchanged.\n const snapshot = (read, key, path, slot, walk) => {\n const at = path === '' ? 'is ' : 'at ' + path + ' is '\n if (++walk.values > maxValues) {\n throw new Rejected('has more than ' + maxValues + ' values; split the work across calls')\n }\n let value = read\n const kind = typeof value\n if ((kind === 'object' && value !== null) || kind === 'function' || kind === 'bigint') {\n // The intrinsic getTime reads a Date's internal time (and throws for anything else).\n let time\n try {\n time = apply(getTime, value, [])\n } catch {\n time = undefined\n }\n if (time !== undefined && !isFinite(time)) {\n throw new Rejected(at + 'an invalid Date; pass a valid Date or an ISO string')\n }\n const toJSON = value.toJSON\n if (typeof toJSON === 'function') value = apply(toJSON, value, [key])\n }\n switch (typeof value) {\n case 'string':\n case 'boolean':\n return value\n case 'number':\n if (isFinite(value)) return value\n throw new Rejected(\n at + value + '; pass a finite number' + (slot === 'key' ? ' or omit the key' : '')\n )\n case 'undefined':\n if (slot === 'item') throw new Rejected(at + 'undefined; arrays cannot hold undefined')\n return undefined\n case 'bigint':\n case 'function':\n case 'symbol':\n throw new Rejected(at + 'a ' + typeof value + '; ' + plainJson)\n }\n if (value === null) return null\n if (apply(setHas, walk.ancestors, [value])) {\n throw new Rejected(at + 'a circular reference; ' + plainJson)\n }\n if (walk.depth >= maxDepth) {\n throw new Rejected('at ' + path + ' is nested more than ' + maxDepth + ' levels deep')\n }\n const array = isArray(value)\n if (!array) {\n const proto = getPrototypeOf(value)\n if (proto !== ObjectProto && proto !== null) {\n throw new Rejected(at + typeName(value) + '; ' + plainJson)\n }\n }\n apply(setAdd, walk.ancestors, [value])\n walk.depth++\n let copy\n if (array) {\n // LengthOfArrayLike, read once, as JSON.stringify does.\n // Unary plus is ToNumber: one coercion, and it throws for a bigint as JSON.stringify does.\n const raw = +value.length\n const length = raw > 0 ? min(trunc(raw), 9007199254740991) : 0\n // No prototype: no inherited toJSON or index setter can change the checked copy when pi\n // serializes it (still an array to JSON.stringify).\n copy = []\n setPrototypeOf(copy, null)\n for (let i = 0; i < length; i++) {\n copy[i] = snapshot(value[i], '' + i, path + '[' + i + ']', 'item', walk)\n }\n } else {\n copy = create(null)\n // Indexed over the fresh key array: no iterator a script could replace.\n const names = keys(value)\n for (let i = 0; i < names.length; i++) {\n const name = names[i]\n const item = snapshot(value[name], name, keyPath(path, name), 'key', walk)\n if (item !== undefined) copy[name] = item\n }\n }\n walk.depth--\n apply(setDelete, walk.ancestors, [value])\n return copy\n }\n const wrap = (label, call) => async (...args) => {\n let json\n try {\n json = snapshot(args[0], '', '', 'root', { values: 0, depth: 0, ancestors: new SetCtor() })\n } catch (error) {\n if (error instanceof Rejected) throw new TypeErrorCtor(label + ': argument ' + error.message)\n throw error\n }\n return call(json)\n }\n // Each tool's function sits at tools[name] unless an earlier tool holds that key, in which case\n // that earlier function is already wrapped.\n const wrappers = new Map()\n for (const [name, label] of labels) {\n const call = name in tools ? tools[name] : undefined\n if (typeof call === 'function' && !wrappers.has(call)) wrappers.set(call, wrap(label, call))\n }\n const guarded = Object.create(null)\n for (const key of Object.keys(tools)) {\n const call = tools[key]\n if (!wrappers.has(call)) wrappers.set(call, wrap('tools[' + JSON.stringify(key) + ']', call))\n guarded[key] = wrappers.get(call)\n }\n // Unbound keys (stage-only tools) reject with their hint instead of being undefined.\n const rejections = create(null)\n for (const [key, message] of unbound) {\n if (!(key in guarded)) rejections[key] = async () => { throw new TypeErrorCtor(message) }\n }\n return new Proxy(Object.freeze(guarded), {\n get: (target, key) => (key in target ? target[key] : key in rejections ? rejections[key] : tools[key])\n })\n})`\n\n// pi runs `(async (tools, console) => {<code>\\n})`. The script runs in an inner function of the\n// same shape whose `tools` is guarded; the prefix shares line 1 with the script (only line-1\n// columns shift) and the guard follows the script, so its line numbers are unchanged.\nconst guardPrefix = 'return (async (tools, console) => {'\n\n/** JSON as a JavaScript expression (U+2028/U+2029 escaped for older parsers). */\nconst jsonSource = (value: unknown) =>\n JSON.stringify(value).replace(/[\\u2028\\u2029]/g, char =>\n char === '\\u2028' ? '\\\\u2028' : '\\\\u2029'\n )\n\nconst guardSuffix = (\n tools: ReadonlyArray<CodeModeExecutorTool>,\n unbound: CodeModeExecuteOptions['unbound']\n) => {\n const names = tools.map(tool => tool.name)\n const labels = codeModeCallLabels(names)\n\n // `makeCodeModeTool` supplies each label; other callers get pi's binding rule.\n const pairs = tools.map((tool, index) => [\n tool.name,\n tool.callLabel ?? labels[index] ?? `tools[${JSON.stringify(tool.name)}]`\n ])\n\n const rejected = (unbound ?? []).map(entry => [entry.key, entry.message])\n\n return `\\n})(${toolsGuardSource}(tools, ${jsonSource(pairs)}, ${jsonSource(rejected)}), console)`\n}\n\nconst lineTerminators = /\\r\\n?|[\\n\\u2028\\u2029]/g\n\n/** Lines of the script as QuickJS counts them (at least; never fewer). */\nconst scriptLineCount = (code: string) => (code.match(lineTerminators)?.length ?? 0) + 1\n\nconst scriptFrame = /codemode\\.js:(\\d+):\\d+/\n\n/** Drops stack frames of the wrapper and guard, which sit below the script's last line. */\nconst withoutWrapperFrames = (stack: string, scriptLines: number) =>\n stack\n .split('\\n')\n .filter(line => {\n const frame = scriptFrame.exec(line)\n\n return frame === null || Number(frame[1]) <= scriptLines\n })\n .join('\\n')\n\nconst ErrorWithCode = Schema.Struct({ code: Schema.String })\n\nconst errorCode = (error: unknown) =>\n Option.getOrUndefined(Option.map(Schema.decodeUnknownOption(ErrorWithCode)(error), e => e.code))\n\nconst syntaxLocation = (error: Error) => {\n const match = /^[^\\n]*:(\\d+)\\n([^\\n]*)\\n([^\\n]*)\\n/.exec(error.stack ?? '')\n\n if (match === null) return ''\n\n const line = Number(match[1]) - 1\n\n return line >= 1 ? ` (line ${line})\\n${match[2] ?? ''}\\n${match[3] ?? ''}` : ''\n}\n\nconst typeStripMessage = (error: unknown) => {\n const message = error instanceof Error ? error.message : String(error)\n\n if (errorCode(error) === 'ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX') {\n return `${message}. Write plain JavaScript: type annotations are stripped, but enums, namespaces, and parameter properties are not supported.`\n }\n\n const location = error instanceof Error ? syntaxLocation(error) : ''\n\n return `The script could not be parsed: ${message}${location}`\n}\n\ntype Stripped =\n | { readonly ok: true; readonly code: string }\n | { readonly ok: false; readonly error: CodeModeError }\n\n/**\n * Strips TypeScript annotations with Node's `stripTypeScriptTypes` (Node 22.13+; whitespace\n * replacement keeps line and column numbers). The script is wrapped in an async function so\n * top-level `return` and `await` parse. On Node without the API, code passes through unchanged.\n */\nconst stripTypes = (code: string): Stripped => {\n if (!Predicate.isFunction(nodeModule.stripTypeScriptTypes)) return { ok: true, code }\n\n try {\n const stripped = nodeModule.stripTypeScriptTypes(`${wrapperPrefix}${code}${wrapperSuffix}`, {\n mode: 'strip'\n })\n\n if (!stripped.startsWith(wrapperPrefix) || !stripped.endsWith(wrapperSuffix)) {\n return { ok: false, error: { kind: 'script', message: 'The script could not be parsed.' } }\n }\n\n return {\n ok: true,\n code: stripped.slice(wrapperPrefix.length, stripped.length - wrapperSuffix.length)\n }\n } catch (error) {\n return { ok: false, error: { kind: 'script', message: typeStripMessage(error) } }\n }\n}\n\nconst piTool = (tool: CodeModeExecutorTool): CodemodeTool => {\n const converted: CodemodeTool = {\n name: tool.name,\n execute: (args, context) => tool.execute(args, context)\n }\n\n if (tool.description !== undefined) converted.description = tool.description\n\n if (tool.inputSchema !== undefined) converted.inputSchema = tool.inputSchema\n\n if (tool.outputSchema !== undefined) converted.outputSchema = tool.outputSchema\n\n return converted\n}\n\nconst piGlobal = (global: CodeModeExecutorGlobal): CodemodeTool => {\n const converted = piTool(global)\n\n if (global.spread !== undefined) converted.spread = global.spread\n\n if (global.signature !== undefined) converted.signature = global.signature\n\n return converted\n}\n\nconst failure = (error: CodeModeError): CodeModeExecutionResult => ({\n ok: false,\n error,\n output: []\n})\n\nconst StoreWrites = Schema.Struct({\n set: Schema.Record(Schema.String, Schema.Json),\n delete: Schema.Array(Schema.String)\n})\n\nconst decodeStoreWrites = Schema.decodeUnknownOption(StoreWrites)\n\ntype MutableCodeModeError = {\n kind: CodeModeError['kind']\n message: string\n stack?: string\n}\n\ntype MutableExecutionResult = {\n ok: boolean\n value?: unknown\n error?: CodeModeError\n output: CodeModeExecutionResult['output']\n storeWrites?: CodeModeStoreWrites\n}\n\nconst fromPiResult = (result: CodemodeResult, scriptLines: number): CodeModeExecutionResult => {\n if (result.ok) {\n const storeWrites = decodeStoreWrites(result.storeWrites)\n\n if (Option.isNone(storeWrites)) {\n return failure({\n kind: 'sandbox',\n message: 'The script reported store writes that are not JSON.'\n })\n }\n\n const converted: MutableExecutionResult = {\n ok: true,\n output: result.output,\n storeWrites: storeWrites.value\n }\n\n if (result.value !== undefined) converted.value = result.value\n\n return converted\n }\n\n const { kind, name, message, stack } = result.error\n const named = name !== undefined && name.length > 0 && !message.startsWith(name)\n\n const error: MutableCodeModeError = {\n kind,\n message: named ? `${name}: ${message}` : message\n }\n\n if (stack !== undefined) error.stack = withoutWrapperFrames(stack, scriptLines)\n\n return { ok: false, error, output: result.output }\n}\n\nconst describeError = (error: unknown) => (error instanceof Error ? error.message : String(error))\n\n/**\n * A `CodeModeExecutor` on `@earendil-works/pi-codemode`: every execution gets a fresh QuickJS VM\n * (WebAssembly) in a fresh worker thread, closed when the execution ends. Applies the timeout,\n * heap cap, abort signal, and store, strips TypeScript annotations first, rejects tool arguments\n * that JSON would silently change inside the script (see `CodeModeExecutorTool`), and caps concurrent\n * executions per executor; create one executor per process (module scope) so the cap is\n * per process.\n *\n * Node only. The worker inherits a copy of `process.env` (scripts cannot read it: the VM has no\n * `process`). Under Next.js, list `@yolk-sdk/codemode`, `@earendil-works/pi-codemode`, and\n * `quickjs-wasi` in `serverExternalPackages` so the worker file and wasm stay on disk.\n */\nexport const makePiCodeModeExecutor = (\n options: PiCodeModeExecutorOptions = {}\n): CodeModeExecutor => {\n const slots = makeExecutionSlots(\n Math.max(1, Math.floor(options.maxConcurrentExecutions ?? defaultMaxConcurrentExecutions))\n )\n\n const run = async (code: string, execution: CodeModeExecuteOptions, queuedAt: number) => {\n const remaining = () => execution.timeoutMs - (Date.now() - queuedAt)\n const stripped = stripTypes(code)\n\n if (!stripped.ok) return failure(stripped.error)\n\n const timeoutMs = remaining()\n\n if (timeoutMs <= 0) {\n return failure({\n kind: 'timeout',\n message: `Execution timed out after ${execution.timeoutMs} ms`\n })\n }\n\n const sandboxOptions: CodemodeSandboxOptions = {\n tools: execution.tools.map(piTool),\n globals: execution.globals.map(piGlobal),\n timeoutMs,\n memoryLimitBytes: execution.memoryLimitBytes\n }\n\n if (options.wasm !== undefined) sandboxOptions.wasm = options.wasm\n\n if (options.workerUrl !== undefined) sandboxOptions.workerUrl = options.workerUrl\n\n let sandbox: CodemodeSandbox\n\n try {\n sandbox = new CodemodeSandbox(sandboxOptions)\n } catch (error) {\n return failure({\n kind: 'sandbox',\n message: `Could not create the sandbox: ${describeError(error)}`\n })\n }\n\n try {\n return fromPiResult(\n await sandbox.execute(\n `${guardPrefix}${stripped.code}${guardSuffix(execution.tools, execution.unbound)}`,\n {\n signal: execution.signal,\n store: execution.store,\n timeoutMs\n }\n ),\n scriptLineCount(stripped.code)\n )\n } finally {\n await sandbox.close()\n }\n }\n\n return {\n execute: async (code, execution) => {\n const queuedAt = Date.now()\n const slot = await slots.acquire(execution.signal, execution.timeoutMs)\n\n if (slot === 'aborted') return failure({ kind: 'aborted', message: 'Execution aborted' })\n\n if (slot === 'timeout') {\n return failure({\n kind: 'timeout',\n message: `Execution timed out after ${execution.timeoutMs} ms while waiting for a free execution slot`\n })\n }\n\n try {\n return await run(code, execution, queuedAt)\n } finally {\n slots.release()\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"node.mjs","names":[],"sources":["../src/node.ts"],"sourcesContent":["import * as nodeModule from 'node:module'\nimport { Option, Predicate } from 'effect'\nimport * as Schema from 'effect/Schema'\nimport {\n CodemodeSandbox,\n type CodemodeResult,\n type CodemodeSandboxOptions,\n type CodemodeTool,\n type CodemodeWasmModule\n} from '@earendil-works/pi-codemode'\nimport { codeModeCallLabels } from './catalog.ts'\nimport type {\n CodeModeError,\n CodeModeExecuteOptions,\n CodeModeExecutionResult,\n CodeModeExecutor,\n CodeModeExecutorGlobal,\n CodeModeExecutorTool,\n CodeModeStoreWrites\n} from './executor.ts'\n\n/** Default cap of concurrent script executions per executor. */\nexport const defaultMaxConcurrentExecutions = 4\n\nexport type PiCodeModeExecutorOptions = {\n /**\n * Compiled `quickjs-wasi/quickjs.wasm` (see pi's `loadQuickJSWasm`). Default: the file of the\n * installed `quickjs-wasi` package, compiled once per process.\n */\n readonly wasm?: CodemodeWasmModule | Promise<CodemodeWasmModule>\n /**\n * Worker entry that imports `@earendil-works/pi-codemode/worker`. Default: pi's own worker file,\n * which exists on disk when the package is not bundled (Next `serverExternalPackages`).\n */\n readonly workerUrl?: string | URL\n /** Concurrent executions of this executor; more wait for a free slot. Default 4. */\n readonly maxConcurrentExecutions?: number\n}\n\ntype SlotOutcome = 'acquired' | 'aborted' | 'timeout'\n\ntype Waiter = (outcome: SlotOutcome) => void\n\n/** Abort- and deadline-aware counting semaphore for executions. */\nconst makeExecutionSlots = (max: number) => {\n let active = 0\n const waiting: Array<Waiter> = []\n\n const acquire = (signal: AbortSignal, timeoutMs: number): Promise<SlotOutcome> => {\n if (signal.aborted) return Promise.resolve('aborted')\n\n if (active < max) {\n active++\n\n return Promise.resolve('acquired')\n }\n\n return new Promise(resolve => {\n let timer: ReturnType<typeof setTimeout> | undefined\n\n const settle: Waiter = outcome => {\n const index = waiting.indexOf(settle)\n\n if (index !== -1) waiting.splice(index, 1)\n\n if (timer !== undefined) clearTimeout(timer)\n signal.removeEventListener('abort', onAbort)\n resolve(outcome)\n }\n\n const onAbort = () => settle('aborted')\n\n waiting.push(settle)\n signal.addEventListener('abort', onAbort, { once: true })\n\n if (Number.isFinite(timeoutMs)) {\n timer = setTimeout(() => settle('timeout'), Math.max(0, timeoutMs))\n }\n })\n }\n\n const release = () => {\n const next = waiting[0]\n\n if (next === undefined) {\n active--\n } else {\n // The slot passes straight to the next waiter.\n next('acquired')\n }\n }\n\n return { acquire, release }\n}\n\nconst wrapperPrefix = 'async function __yolkCodeMode() {\\n'\n\nconst wrapperSuffix = '\\n}'\n\n/** Most values one argument may hold; a larger argument is rejected. */\nconst maxArgumentValues = 100_000\n\n/** Deepest object/array nesting one argument may have; deeper arguments are rejected. */\nconst maxArgumentDepth = 64\n\n/**\n * Evaluated in the VM before the script runs: `(tools, labels) => guardedTools`, with `labels` the\n * `[name, callLabel]` pairs of the tools in order (`codeModeCallLabels`). Each call walks its first\n * argument once, the way `JSON.stringify` reads it (each own enumerable string key and each array\n * index read once, so getters run once; a callable `toJSON` is called once with the key, `''` at\n * the root, and its result is walked at the same path), and builds a fresh copy (null-prototype\n * objects and arrays) that is what the call sends. It rejects with a `TypeError` naming the call\n * label and path when JSON would change the value instead of carrying it: non-finite numbers,\n * invalid `Date`s, `undefined`/function/symbol array items and holes, function/symbol/bigint\n * values, cycles, and objects that are neither plain (prototype `Object.prototype` or `null`) nor\n * arrays and have no `toJSON`. An argument with more than `maxArgumentValues` values or nested more\n * than `maxArgumentDepth` levels is rejected too, never sent unchecked. `undefined`-valued keys are\n * left out (absent, as JSON drops them); the same object twice (not a cycle) is copied twice. An\n * error thrown by a getter or `toJSON` rejects the call with that error. A rejected call never\n * reaches the host. Every member of pi's `tools` is wrapped; unknown members fall through to pi's\n * proxy (close-match suggestions). Not a security boundary (`globalThis.tools` is unguarded):\n * arguments are still decoded by the host.\n */\nconst toolsGuardSource = `(function (tools, labels, unbound) {\n // Captured before the script runs, so a script that changes built-ins cannot change the checks.\n const { apply, getPrototypeOf, setPrototypeOf } = Reflect\n const isFinite = Number.isFinite\n const isArray = Array.isArray\n const { keys, create, defineProperty } = Object\n const getTime = Date.prototype.getTime\n const TypeErrorCtor = TypeError\n const SetCtor = Set\n const setAdd = Set.prototype.add\n const setHas = Set.prototype.has\n const setDelete = Set.prototype.delete\n const ObjectProto = Object.prototype\n const { trunc, min } = Math\n const stringify = JSON.stringify\n const plainJson = 'pass plain JSON (objects, arrays, strings, finite numbers, booleans, null)'\n const maxValues = ${maxArgumentValues}\n const maxDepth = ${maxArgumentDepth}\n class Rejected {\n constructor(message) {\n defineProperty(this, 'message', { value: message })\n }\n }\n const keyPath = (path, key) =>\n /^[A-Za-z_$][\\\\w$]*$/.test(key)\n ? (path === '' ? key : path + '.' + key)\n : path + '[' + stringify(key) + ']'\n const typeName = value => {\n const proto = getPrototypeOf(value)\n const name = proto && typeof proto.constructor === 'function' ? proto.constructor.name : ''\n return name ? (/^[AEIOU]/.test(name) ? 'an ' : 'a ') + name : 'a non-plain object'\n }\n // The JSON copy of a value already read from its holder at key (undefined: absent), or throws\n // Rejected with why JSON would not carry it unchanged.\n const snapshot = (read, key, path, slot, walk) => {\n const at = path === '' ? 'is ' : 'at ' + path + ' is '\n if (++walk.values > maxValues) {\n throw new Rejected('has more than ' + maxValues + ' values; split the work across calls')\n }\n let value = read\n const kind = typeof value\n if ((kind === 'object' && value !== null) || kind === 'function' || kind === 'bigint') {\n // The intrinsic getTime reads a Date's internal time (and throws for anything else).\n let time\n try {\n time = apply(getTime, value, [])\n } catch {\n time = undefined\n }\n if (time !== undefined && !isFinite(time)) {\n throw new Rejected(at + 'an invalid Date; pass a valid Date or an ISO string')\n }\n const toJSON = value.toJSON\n if (typeof toJSON === 'function') value = apply(toJSON, value, [key])\n }\n switch (typeof value) {\n case 'string':\n case 'boolean':\n return value\n case 'number':\n if (isFinite(value)) return value\n throw new Rejected(\n at + value + '; pass a finite number' + (slot === 'key' ? ' or omit the key' : '')\n )\n case 'undefined':\n if (slot === 'item') throw new Rejected(at + 'undefined; arrays cannot hold undefined')\n return undefined\n case 'bigint':\n case 'function':\n case 'symbol':\n throw new Rejected(at + 'a ' + typeof value + '; ' + plainJson)\n }\n if (value === null) return null\n if (apply(setHas, walk.ancestors, [value])) {\n throw new Rejected(at + 'a circular reference; ' + plainJson)\n }\n if (walk.depth >= maxDepth) {\n throw new Rejected('at ' + path + ' is nested more than ' + maxDepth + ' levels deep')\n }\n const array = isArray(value)\n if (!array) {\n const proto = getPrototypeOf(value)\n if (proto !== ObjectProto && proto !== null) {\n throw new Rejected(at + typeName(value) + '; ' + plainJson)\n }\n }\n apply(setAdd, walk.ancestors, [value])\n walk.depth++\n let copy\n if (array) {\n // LengthOfArrayLike, read once, as JSON.stringify does.\n // Unary plus is ToNumber: one coercion, and it throws for a bigint as JSON.stringify does.\n const raw = +value.length\n const length = raw > 0 ? min(trunc(raw), 9007199254740991) : 0\n // No prototype: no inherited toJSON or index setter can change the checked copy when pi\n // serializes it (still an array to JSON.stringify).\n copy = []\n setPrototypeOf(copy, null)\n for (let i = 0; i < length; i++) {\n copy[i] = snapshot(value[i], '' + i, path + '[' + i + ']', 'item', walk)\n }\n } else {\n copy = create(null)\n // Indexed over the fresh key array: no iterator a script could replace.\n const names = keys(value)\n for (let i = 0; i < names.length; i++) {\n const name = names[i]\n const item = snapshot(value[name], name, keyPath(path, name), 'key', walk)\n if (item !== undefined) copy[name] = item\n }\n }\n walk.depth--\n apply(setDelete, walk.ancestors, [value])\n return copy\n }\n const wrap = (label, call) => async (...args) => {\n let json\n try {\n json = snapshot(args[0], '', '', 'root', { values: 0, depth: 0, ancestors: new SetCtor() })\n } catch (error) {\n if (error instanceof Rejected) throw new TypeErrorCtor(label + ': argument ' + error.message)\n throw error\n }\n return call(json)\n }\n // Each tool's function sits at tools[name] unless an earlier tool holds that key, in which case\n // that earlier function is already wrapped.\n const wrappers = new Map()\n for (const [name, label] of labels) {\n const call = name in tools ? tools[name] : undefined\n if (typeof call === 'function' && !wrappers.has(call)) wrappers.set(call, wrap(label, call))\n }\n const guarded = Object.create(null)\n for (const key of Object.keys(tools)) {\n const call = tools[key]\n if (!wrappers.has(call)) wrappers.set(call, wrap('tools[' + JSON.stringify(key) + ']', call))\n guarded[key] = wrappers.get(call)\n }\n // Unbound keys (stage-only tools) reject with their hint instead of being undefined.\n const rejections = create(null)\n for (const [key, message] of unbound) {\n if (!(key in guarded)) rejections[key] = async () => { throw new TypeErrorCtor(message) }\n }\n return new Proxy(Object.freeze(guarded), {\n get: (target, key) => (key in target ? target[key] : key in rejections ? rejections[key] : tools[key])\n })\n})`\n\n// pi runs `(async (tools, console) => {<code>\\n})`. The script runs in an inner function of the\n// same shape whose `tools` is guarded; the prefix shares line 1 with the script (only line-1\n// columns shift) and the guard follows the script, so its line numbers are unchanged.\nconst guardPrefix = 'return (async (tools, console) => {'\n\n/** JSON as a JavaScript expression (U+2028/U+2029 escaped for older parsers). */\nconst jsonSource = (value: unknown) =>\n JSON.stringify(value).replace(/[\\u2028\\u2029]/g, char =>\n char === '\\u2028' ? '\\\\u2028' : '\\\\u2029'\n )\n\nconst guardSuffix = (\n tools: ReadonlyArray<CodeModeExecutorTool>,\n unbound: CodeModeExecuteOptions['unbound']\n) => {\n const names = tools.map(tool => tool.name)\n const labels = codeModeCallLabels(names)\n\n // `makeCodeModeTool` supplies each label; other callers get pi's binding rule.\n const pairs = tools.map((tool, index) => [\n tool.name,\n tool.callLabel ?? labels[index] ?? `tools[${JSON.stringify(tool.name)}]`\n ])\n\n const rejected = (unbound ?? []).map(entry => [entry.key, entry.message])\n\n return `\\n})(${toolsGuardSource}(tools, ${jsonSource(pairs)}, ${jsonSource(rejected)}), console)`\n}\n\nconst lineTerminators = /\\r\\n?|[\\n\\u2028\\u2029]/g\n\n/** Lines of the script as QuickJS counts them (at least; never fewer). */\nconst scriptLineCount = (code: string) => (code.match(lineTerminators)?.length ?? 0) + 1\n\nconst scriptFrame = /codemode\\.js:(\\d+):\\d+/\n\n/** Drops stack frames of the wrapper and guard, which sit below the script's last line. */\nconst withoutWrapperFrames = (stack: string, scriptLines: number) =>\n stack\n .split('\\n')\n .filter(line => {\n const frame = scriptFrame.exec(line)\n\n return frame === null || Number(frame[1]) <= scriptLines\n })\n .join('\\n')\n\nconst ErrorWithCode = Schema.Struct({ code: Schema.String })\n\nconst errorCode = (error: unknown) =>\n Option.getOrUndefined(Option.map(Schema.decodeUnknownOption(ErrorWithCode)(error), e => e.code))\n\nconst syntaxLocation = (error: Error) => {\n const match = /^[^\\n]*:(\\d+)\\n([^\\n]*)\\n([^\\n]*)\\n/.exec(error.stack ?? '')\n\n if (match === null) return ''\n\n const line = Number(match[1]) - 1\n\n return line >= 1 ? ` (line ${line})\\n${match[2] ?? ''}\\n${match[3] ?? ''}` : ''\n}\n\nconst typeStripMessage = (error: unknown) => {\n const message = error instanceof Error ? error.message : String(error)\n\n if (errorCode(error) === 'ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX') {\n return `${message}. Write plain JavaScript: type annotations are stripped, but enums, namespaces, and parameter properties are not supported.`\n }\n\n const location = error instanceof Error ? syntaxLocation(error) : ''\n\n return `The script could not be parsed: ${message}${location}`\n}\n\ntype Stripped =\n | { readonly ok: true; readonly code: string }\n | { readonly ok: false; readonly error: CodeModeError }\n\n/**\n * Strips TypeScript annotations with Node's `stripTypeScriptTypes` (Node 22.13+; whitespace\n * replacement keeps line and column numbers). The script is wrapped in an async function so\n * top-level `return` and `await` parse. On Node without the API, code passes through unchanged.\n */\nconst stripTypes = (code: string): Stripped => {\n if (!Predicate.isFunction(nodeModule.stripTypeScriptTypes)) return { ok: true, code }\n\n try {\n const stripped = nodeModule.stripTypeScriptTypes(`${wrapperPrefix}${code}${wrapperSuffix}`, {\n mode: 'strip'\n })\n\n if (!stripped.startsWith(wrapperPrefix) || !stripped.endsWith(wrapperSuffix)) {\n return { ok: false, error: { kind: 'script', message: 'The script could not be parsed.' } }\n }\n\n return {\n ok: true,\n code: stripped.slice(wrapperPrefix.length, stripped.length - wrapperSuffix.length)\n }\n } catch (error) {\n return { ok: false, error: { kind: 'script', message: typeStripMessage(error) } }\n }\n}\n\nconst piTool = (tool: CodeModeExecutorTool): CodemodeTool => {\n const converted: CodemodeTool = {\n name: tool.name,\n execute: (args, context) => tool.execute(args, context)\n }\n\n if (tool.description !== undefined) converted.description = tool.description\n\n if (tool.inputSchema !== undefined) converted.inputSchema = tool.inputSchema\n\n if (tool.outputSchema !== undefined) converted.outputSchema = tool.outputSchema\n\n return converted\n}\n\nconst piGlobal = (global: CodeModeExecutorGlobal): CodemodeTool => {\n const converted = piTool(global)\n\n if (global.spread !== undefined) converted.spread = global.spread\n\n if (global.signature !== undefined) converted.signature = global.signature\n\n return converted\n}\n\nconst failure = (error: CodeModeError): CodeModeExecutionResult => ({\n ok: false,\n error,\n output: []\n})\n\nconst StoreWrites = Schema.Struct({\n set: Schema.Record(Schema.String, Schema.Json),\n delete: Schema.Array(Schema.String)\n})\n\nconst decodeStoreWrites = Schema.decodeUnknownOption(StoreWrites)\n\ntype MutableCodeModeError = {\n kind: CodeModeError['kind']\n message: string\n stack?: string\n}\n\ntype MutableExecutionResult = {\n ok: boolean\n value?: unknown\n error?: CodeModeError\n output: CodeModeExecutionResult['output']\n storeWrites?: CodeModeStoreWrites\n}\n\nconst fromPiResult = (result: CodemodeResult, scriptLines: number): CodeModeExecutionResult => {\n if (result.ok) {\n const storeWrites = decodeStoreWrites(result.storeWrites)\n\n if (Option.isNone(storeWrites)) {\n return failure({\n kind: 'sandbox',\n message: 'The script reported store writes that are not JSON.'\n })\n }\n\n const converted: MutableExecutionResult = {\n ok: true,\n output: result.output,\n storeWrites: storeWrites.value\n }\n\n if (result.value !== undefined) converted.value = result.value\n\n return converted\n }\n\n const { kind, name, message, stack } = result.error\n const named = name !== undefined && name.length > 0 && !message.startsWith(name)\n\n const error: MutableCodeModeError = {\n kind,\n message: named ? `${name}: ${message}` : message\n }\n\n if (stack !== undefined) error.stack = withoutWrapperFrames(stack, scriptLines)\n\n return { ok: false, error, output: result.output }\n}\n\nconst describeError = (error: unknown) => (error instanceof Error ? error.message : String(error))\n\n/**\n * A `CodeModeExecutor` on `@earendil-works/pi-codemode`: every execution gets a fresh QuickJS VM\n * (WebAssembly) in a fresh worker thread, closed when the execution ends. Applies the timeout,\n * heap cap, abort signal, and store, strips TypeScript annotations first, rejects tool arguments\n * that JSON would silently change inside the script (see `CodeModeExecutorTool`), and caps concurrent\n * executions per executor; create one executor per process (module scope) so the cap is\n * per process.\n *\n * Node only. The worker inherits a copy of `process.env` (scripts cannot read it: the VM has no\n * `process`). Under Next.js, list `@yolk-sdk/codemode`, `@earendil-works/pi-codemode`, and\n * `quickjs-wasi` in `serverExternalPackages` so the worker file and wasm stay on disk.\n */\nexport const makePiCodeModeExecutor = (\n options: PiCodeModeExecutorOptions = {}\n): CodeModeExecutor => {\n const slots = makeExecutionSlots(\n Math.max(1, Math.floor(options.maxConcurrentExecutions ?? defaultMaxConcurrentExecutions))\n )\n\n const run = async (code: string, execution: CodeModeExecuteOptions, queuedAt: number) => {\n const remaining = () => execution.timeoutMs - (Date.now() - queuedAt)\n const stripped = stripTypes(code)\n\n if (!stripped.ok) return failure(stripped.error)\n\n const timeoutMs = remaining()\n\n if (timeoutMs <= 0) {\n return failure({\n kind: 'timeout',\n message: `Execution timed out after ${execution.timeoutMs} ms`\n })\n }\n\n const sandboxOptions: CodemodeSandboxOptions = {\n tools: execution.tools.map(piTool),\n globals: execution.globals.map(piGlobal),\n timeoutMs,\n memoryLimitBytes: execution.memoryLimitBytes\n }\n\n if (options.wasm !== undefined) sandboxOptions.wasm = options.wasm\n\n if (options.workerUrl !== undefined) sandboxOptions.workerUrl = options.workerUrl\n\n let sandbox: CodemodeSandbox\n\n try {\n sandbox = new CodemodeSandbox(sandboxOptions)\n } catch (error) {\n return failure({\n kind: 'sandbox',\n message: `Could not create the sandbox: ${describeError(error)}`\n })\n }\n\n try {\n return fromPiResult(\n await sandbox.execute(\n `${guardPrefix}${stripped.code}${guardSuffix(execution.tools, execution.unbound)}`,\n {\n signal: execution.signal,\n store: execution.store,\n timeoutMs\n }\n ),\n scriptLineCount(stripped.code)\n )\n } finally {\n await sandbox.close()\n }\n }\n\n return {\n execute: async (code, execution) => {\n const queuedAt = Date.now()\n const slot = await slots.acquire(execution.signal, execution.timeoutMs)\n\n if (slot === 'aborted') return failure({ kind: 'aborted', message: 'Execution aborted' })\n\n if (slot === 'timeout') {\n return failure({\n kind: 'timeout',\n message: `Execution timed out after ${execution.timeoutMs} ms while waiting for a free execution slot`\n })\n }\n\n try {\n return await run(code, execution, queuedAt)\n } finally {\n slots.release()\n }\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/output.d.mts
CHANGED
|
@@ -1,26 +1,24 @@
|
|
|
1
1
|
import { CodeModeExecutionResult, CodeModeOutputItem } from "./executor.mjs";
|
|
2
2
|
import { Content, NestedToolCallStatus } from "@yolk-sdk/agent/protocol";
|
|
3
|
-
|
|
4
|
-
//#region src/output.d.ts
|
|
5
3
|
/** Text or image piece of the model-visible result, in order. */
|
|
6
|
-
type CodeModeResultSegment = CodeModeOutputItem;
|
|
4
|
+
export type CodeModeResultSegment = CodeModeOutputItem;
|
|
7
5
|
/**
|
|
8
6
|
* Keeps at most about `maxChars` characters of text: the head and the tail of the segments, with an
|
|
9
7
|
* omission marker in between. Images inside the omitted middle are dropped and counted.
|
|
10
8
|
*/
|
|
11
|
-
declare const boundCodeModeSegments: (segments: ReadonlyArray<CodeModeResultSegment>, maxChars: number) => ReadonlyArray<CodeModeResultSegment>;
|
|
9
|
+
export declare const boundCodeModeSegments: (segments: ReadonlyArray<CodeModeResultSegment>, maxChars: number) => ReadonlyArray<CodeModeResultSegment>;
|
|
12
10
|
/** Agent `Content`: a plain string without images, ordered text and image parts otherwise. */
|
|
13
|
-
declare const codeModeSegmentsContent: (segments: ReadonlyArray<CodeModeResultSegment>) => Content;
|
|
14
|
-
type CodeModeCallSummary = {
|
|
11
|
+
export declare const codeModeSegmentsContent: (segments: ReadonlyArray<CodeModeResultSegment>) => Content;
|
|
12
|
+
export type CodeModeCallSummary = {
|
|
15
13
|
readonly name: string;
|
|
16
14
|
readonly status: NestedToolCallStatus;
|
|
17
15
|
};
|
|
18
16
|
/** `name: 2 ok, 1 error; other: 1 cancelled`, by first appearance. */
|
|
19
|
-
declare const summarizeCodeModeCalls: (calls: ReadonlyArray<CodeModeCallSummary>) => string;
|
|
17
|
+
export declare const summarizeCodeModeCalls: (calls: ReadonlyArray<CodeModeCallSummary>) => string;
|
|
20
18
|
/** Default cap of images kept in one result. */
|
|
21
|
-
declare const defaultCodeModeMaxImages = 8;
|
|
19
|
+
export declare const defaultCodeModeMaxImages = 8;
|
|
22
20
|
/** Default cap of base64 image data kept in one result, in characters (4 MiB). */
|
|
23
|
-
declare const defaultCodeModeMaxImageBytes: number;
|
|
21
|
+
export declare const defaultCodeModeMaxImageBytes: number;
|
|
24
22
|
/**
|
|
25
23
|
* Model-visible segments of one execution: a `Script completed` / `Script failed` header with the
|
|
26
24
|
* wall time, the output in order, the JSON return value (omitted when undefined), and for failures
|
|
@@ -29,7 +27,7 @@ declare const defaultCodeModeMaxImageBytes: number;
|
|
|
29
27
|
* either is dropped and counted in an omission note after the output. The result is then bounded
|
|
30
28
|
* by `maxChars` with a head-and-tail cut, which also drops images in the omitted middle.
|
|
31
29
|
*/
|
|
32
|
-
declare const codeModeResultSegments: (input: {
|
|
30
|
+
export declare const codeModeResultSegments: (input: {
|
|
33
31
|
readonly result: CodeModeExecutionResult;
|
|
34
32
|
readonly wallTimeMs: number;
|
|
35
33
|
readonly calls: ReadonlyArray<CodeModeCallSummary>;
|
|
@@ -37,6 +35,4 @@ declare const codeModeResultSegments: (input: {
|
|
|
37
35
|
readonly maxImages?: number;
|
|
38
36
|
readonly maxImageBytes?: number;
|
|
39
37
|
}) => ReadonlyArray<CodeModeResultSegment>;
|
|
40
|
-
//#endregion
|
|
41
|
-
export { CodeModeCallSummary, CodeModeResultSegment, boundCodeModeSegments, codeModeResultSegments, codeModeSegmentsContent, defaultCodeModeMaxImageBytes, defaultCodeModeMaxImages, summarizeCodeModeCalls };
|
|
42
38
|
//# sourceMappingURL=output.d.mts.map
|
package/dist/output.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output.d.mts","names":[],"sources":["../src/output.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"output.d.mts","names":[],"sources":["../src/output.ts"],"mappings":";;;YAUY,wBAAwB;;;;;qBA8BvB,wBAAqB,UACtB,cAAc,wBAAsB,qBAE7C,cAAc;;qBA0FJ,0BAAuB,UACxB,cAAc,2BACvB;YAiBS;WACD;WACA,QAAQ;;;qBAMN,yBAAsB,OAAW,cAAc;;qBAwC/C;;qBAGA;;;;;;;;;qBAaA,yBAAsB;WACxB,QAAQ;WACR;WACA,OAAO,cAAc;WACrB;WACA;WACA;MACP,cAAc"}
|
package/dist/output.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ImagePart, TextPart, inlineBase64AttachmentSource } from "@yolk-sdk/agent/protocol";
|
|
2
|
-
|
|
2
|
+
// src/output.ts
|
|
3
3
|
const isHighSurrogate = (code) => code >= 55296 && code <= 56319;
|
|
4
4
|
const isLowSurrogate = (code) => code >= 56320 && code <= 57343;
|
|
5
5
|
const headCut = (text, length) => length > 0 && length < text.length && isHighSurrogate(text.charCodeAt(length - 1)) ? length - 1 : length;
|
|
@@ -125,7 +125,7 @@ const encodeValue = (value) => {
|
|
|
125
125
|
/** Default cap of images kept in one result. */
|
|
126
126
|
const defaultCodeModeMaxImages = 8;
|
|
127
127
|
/** Default cap of base64 image data kept in one result, in characters (4 MiB). */
|
|
128
|
-
const defaultCodeModeMaxImageBytes =
|
|
128
|
+
const defaultCodeModeMaxImageBytes = 4194304;
|
|
129
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
130
|
/**
|
|
131
131
|
* Model-visible segments of one execution: a `Script completed` / `Script failed` header with the
|
|
@@ -178,7 +178,6 @@ const codeModeResultSegments = (input) => {
|
|
|
178
178
|
if (result.ok && result.output.length === 0 && result.value === void 0) text(" No output and no return value.");
|
|
179
179
|
return boundCodeModeSegments(mergeText(segments), input.maxChars);
|
|
180
180
|
};
|
|
181
|
-
//#endregion
|
|
182
181
|
export { boundCodeModeSegments, codeModeResultSegments, codeModeSegmentsContent, defaultCodeModeMaxImageBytes, defaultCodeModeMaxImages, summarizeCodeModeCalls };
|
|
183
182
|
|
|
184
183
|
//# sourceMappingURL=output.mjs.map
|
package/dist/output.mjs.map
CHANGED
|
@@ -1 +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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"}
|
|
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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"}
|
package/dist/search.d.mts
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { CodeModeCatalogTool } from "./catalog.mjs";
|
|
2
|
-
|
|
3
|
-
//#region src/search.d.ts
|
|
2
|
+
import { toolSearchTokens } from "@yolk-sdk/agent/tools";
|
|
4
3
|
/** Default number of `searchTools` results. */
|
|
5
|
-
declare const defaultCodeModeSearchLimit = 8;
|
|
6
|
-
type CodeModeSearchOptions = {
|
|
4
|
+
export declare const defaultCodeModeSearchLimit = 8;
|
|
5
|
+
export type CodeModeSearchOptions = {
|
|
7
6
|
readonly limit?: number;
|
|
8
7
|
readonly namespace?: string;
|
|
9
8
|
};
|
|
10
|
-
type CodeModeSearchHit = {
|
|
9
|
+
export type CodeModeSearchHit = {
|
|
11
10
|
/** The identifier scripts call as `tools.<name>(args)`, or for a stage-only tool the name
|
|
12
11
|
* scripts pass to `stage(name, args)`.
|
|
13
12
|
*/
|
|
14
13
|
readonly name: string;
|
|
15
|
-
readonly description: string;
|
|
16
|
-
|
|
14
|
+
readonly description: string;
|
|
15
|
+
/** The call with its argument and result types (`codeModeToolSignature`), bounded. */
|
|
16
|
+
readonly signature: string;
|
|
17
|
+
/** A stage-only tool (ADR 0007): no `tools` binding, only `stage()` it. */
|
|
17
18
|
readonly stageOnly?: true;
|
|
18
19
|
};
|
|
19
20
|
/**
|
|
@@ -21,9 +22,7 @@ type CodeModeSearchHit = {
|
|
|
21
22
|
* namespace, and namespace description. Ties keep catalog order. A query without words lists the
|
|
22
23
|
* (namespace-filtered) tools in catalog order.
|
|
23
24
|
*/
|
|
24
|
-
declare const searchCodeModeTools: (catalog: ReadonlyArray<CodeModeCatalogTool>, query: string, options?: CodeModeSearchOptions) => ReadonlyArray<CodeModeSearchHit>;
|
|
25
|
+
export declare const searchCodeModeTools: (catalog: ReadonlyArray<CodeModeCatalogTool>, query: string, options?: CodeModeSearchOptions) => ReadonlyArray<CodeModeSearchHit>;
|
|
25
26
|
/** Lowercase word tokens of code mode search (the shared `toolSearchTokens`). */
|
|
26
|
-
declare const codeModeSearchTokens:
|
|
27
|
-
//#endregion
|
|
28
|
-
export { CodeModeSearchHit, CodeModeSearchOptions, codeModeSearchTokens, defaultCodeModeSearchLimit, searchCodeModeTools };
|
|
27
|
+
export declare const codeModeSearchTokens: typeof toolSearchTokens;
|
|
29
28
|
//# sourceMappingURL=search.d.mts.map
|
package/dist/search.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.mts","names":[],"sources":["../src/search.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"search.d.mts","names":[],"sources":["../src/search.ts"],"mappings":";;;qBAIa;YAED;WACD;WACA;;YAGC;;;;WAID;WACA;;WAEA;;WAEA;;;;;;;qBAQE,sBAAmB,SACrB,cAAc,sBAAoB,eAC9B,UACJ,0BACR,cAAc;;qBAqCJ,6BAAoB"}
|
package/dist/search.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { codeModeToolSignature } from "./catalog.mjs";
|
|
2
2
|
import { rankToolSearchDocuments, toolSearchTokens } from "@yolk-sdk/agent/tools";
|
|
3
|
-
|
|
3
|
+
// src/search.ts
|
|
4
4
|
/** Default number of `searchTools` results. */
|
|
5
5
|
const defaultCodeModeSearchLimit = 8;
|
|
6
6
|
/**
|
|
@@ -33,7 +33,6 @@ const searchCodeModeTools = (catalog, query, options = {}) => {
|
|
|
33
33
|
};
|
|
34
34
|
/** Lowercase word tokens of code mode search (the shared `toolSearchTokens`). */
|
|
35
35
|
const codeModeSearchTokens = toolSearchTokens;
|
|
36
|
-
//#endregion
|
|
37
36
|
export { codeModeSearchTokens, defaultCodeModeSearchLimit, searchCodeModeTools };
|
|
38
37
|
|
|
39
38
|
//# sourceMappingURL=search.mjs.map
|
package/dist/search.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.mjs","names":[],"sources":["../src/search.ts"],"sourcesContent":["import { rankToolSearchDocuments, toolSearchTokens } from '@yolk-sdk/agent/tools'\nimport { codeModeToolSignature, type CodeModeCatalogTool } from './catalog.ts'\n\n/** Default number of `searchTools` results. */\nexport const defaultCodeModeSearchLimit = 8\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)`, or for a stage-only tool the name\n * scripts pass to `stage(name, args)`.\n */\n readonly name: string\n readonly description: string\n /** The call with its argument and result types (`codeModeToolSignature`), bounded. */\n readonly signature: string\n /** A stage-only tool (ADR 0007): no `tools` binding, only `stage()` it. */\n readonly stageOnly?: true\n}\n\n/**\n * BM25 (the shared `rankToolSearchDocuments`) over each tool's identifier, raw name, description,\n * namespace, and namespace description. Ties keep catalog order. A query without words lists the\n * (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 scoped =\n options.namespace === undefined\n ? catalog\n : catalog.filter(tool => tool.namespace === options.namespace)\n\n const hit = (tool: CodeModeCatalogTool): CodeModeSearchHit =>\n tool.exposure === 'stage'\n ? {\n name: tool.name,\n description: tool.description,\n signature: codeModeToolSignature(tool),\n stageOnly: true\n }\n : {\n name: tool.identifier,\n description: tool.description,\n signature: codeModeToolSignature(tool)\n }\n\n return rankToolSearchDocuments(\n scoped.map(tool => ({\n value: tool,\n text: [\n tool.identifier,\n tool.name,\n tool.description,\n tool.namespace,\n tool.namespaceDescription ?? ''\n ].join(' ')\n })),\n query,\n options.limit ?? defaultCodeModeSearchLimit\n ).map(hit)\n}\n\n/** Lowercase word tokens of code mode search (the shared `toolSearchTokens`). */\nexport const codeModeSearchTokens = toolSearchTokens\n"],"mappings":";;;;AAIA,MAAa,6BAA6B;;;;;;AAwB1C,MAAa,uBACX,SACA,OACA,UAAiC,CAAC,MACG;CACrC,MAAM,SACJ,QAAQ,cAAc,KAAA,IAClB,UACA,QAAQ,QAAO,SAAQ,KAAK,cAAc,QAAQ,SAAS;CAEjE,MAAM,OAAO,SACX,KAAK,aAAa,UACd;EACE,MAAM,KAAK;EACX,aAAa,KAAK;EAClB,WAAW,sBAAsB,IAAI;EACrC,WAAW;CACb,IACA;EACE,MAAM,KAAK;EACX,aAAa,KAAK;EAClB,WAAW,sBAAsB,IAAI;CACvC;CAEN,OAAO,wBACL,OAAO,KAAI,UAAS;EAClB,OAAO;EACP,MAAM;GACJ,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK,wBAAwB;EAC/B,
|
|
1
|
+
{"version":3,"file":"search.mjs","names":[],"sources":["../src/search.ts"],"sourcesContent":["import { rankToolSearchDocuments, toolSearchTokens } from '@yolk-sdk/agent/tools'\nimport { codeModeToolSignature, type CodeModeCatalogTool } from './catalog.ts'\n\n/** Default number of `searchTools` results. */\nexport const defaultCodeModeSearchLimit = 8\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)`, or for a stage-only tool the name\n * scripts pass to `stage(name, args)`.\n */\n readonly name: string\n readonly description: string\n /** The call with its argument and result types (`codeModeToolSignature`), bounded. */\n readonly signature: string\n /** A stage-only tool (ADR 0007): no `tools` binding, only `stage()` it. */\n readonly stageOnly?: true\n}\n\n/**\n * BM25 (the shared `rankToolSearchDocuments`) over each tool's identifier, raw name, description,\n * namespace, and namespace description. Ties keep catalog order. A query without words lists the\n * (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 scoped =\n options.namespace === undefined\n ? catalog\n : catalog.filter(tool => tool.namespace === options.namespace)\n\n const hit = (tool: CodeModeCatalogTool): CodeModeSearchHit =>\n tool.exposure === 'stage'\n ? {\n name: tool.name,\n description: tool.description,\n signature: codeModeToolSignature(tool),\n stageOnly: true\n }\n : {\n name: tool.identifier,\n description: tool.description,\n signature: codeModeToolSignature(tool)\n }\n\n return rankToolSearchDocuments(\n scoped.map(tool => ({\n value: tool,\n text: [\n tool.identifier,\n tool.name,\n tool.description,\n tool.namespace,\n tool.namespaceDescription ?? ''\n ].join(' ')\n })),\n query,\n options.limit ?? defaultCodeModeSearchLimit\n ).map(hit)\n}\n\n/** Lowercase word tokens of code mode search (the shared `toolSearchTokens`). */\nexport const codeModeSearchTokens = toolSearchTokens\n"],"mappings":";;;;AAIA,MAAa,6BAA6B;;;;;;AAwB1C,MAAa,uBACX,SACA,OACA,UAAiC,CAAC,MACG;CACrC,MAAM,SACJ,QAAQ,cAAc,KAAA,IAClB,UACA,QAAQ,QAAO,SAAQ,KAAK,cAAc,QAAQ,SAAS;CAEjE,MAAM,OAAO,SACX,KAAK,aAAa,UACd;EACE,MAAM,KAAK;EACX,aAAa,KAAK;EAClB,WAAW,sBAAsB,IAAI;EACrC,WAAW;CACb,IACA;EACE,MAAM,KAAK;EACX,aAAa,KAAK;EAClB,WAAW,sBAAsB,IAAI;CACvC;CAEN,OAAO,wBACL,OAAO,KAAI,UAAS;EAClB,OAAO;EACP,MAAM;GACJ,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK,wBAAwB;EAC/B,CAAC,CAAC,KAAK,GAAG;CACZ,EAAE,GACF,OACA,QAAQ,SAAA,CACV,CAAC,CAAC,IAAI,GAAG;AACX;;AAGA,MAAa,uBAAuB"}
|