@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.
- package/LICENSE +21 -0
- package/README.md +174 -0
- package/dist/catalog.d.mts +74 -0
- package/dist/catalog.d.mts.map +1 -0
- package/dist/catalog.mjs +164 -0
- package/dist/catalog.mjs.map +1 -0
- package/dist/classifier-tool.d.mts +78 -0
- package/dist/classifier-tool.d.mts.map +1 -0
- package/dist/classifier-tool.mjs +125 -0
- package/dist/classifier-tool.mjs.map +1 -0
- package/dist/executor.d.mts +108 -0
- package/dist/executor.d.mts.map +1 -0
- package/dist/executor.mjs +1 -0
- package/dist/index.d.mts +8 -0
- package/dist/index.mjs +7 -0
- package/dist/node.d.mts +35 -0
- package/dist/node.d.mts.map +1 -0
- package/dist/node.mjs +393 -0
- package/dist/node.mjs.map +1 -0
- package/dist/output.d.mts +42 -0
- package/dist/output.d.mts.map +1 -0
- package/dist/output.mjs +184 -0
- package/dist/output.mjs.map +1 -0
- package/dist/search.d.mts +23 -0
- package/dist/search.d.mts.map +1 -0
- package/dist/search.mjs +58 -0
- package/dist/search.mjs.map +1 -0
- package/dist/store.d.mts +77 -0
- package/dist/store.d.mts.map +1 -0
- package/dist/store.mjs +61 -0
- package/dist/store.mjs.map +1 -0
- package/dist/tool.d.mts +103 -0
- package/dist/tool.d.mts.map +1 -0
- package/dist/tool.mjs +392 -0
- package/dist/tool.mjs.map +1 -0
- package/package.json +63 -0
- package/src/catalog.ts +305 -0
- package/src/classifier-tool.ts +227 -0
- package/src/executor.ts +122 -0
- package/src/index.ts +87 -0
- package/src/node.ts +546 -0
- package/src/output.ts +287 -0
- package/src/search.ts +113 -0
- package/src/store.ts +135 -0
- package/src/tool.ts +766 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { Effect, Predicate, Semaphore } from "effect";
|
|
2
|
+
import { AgentInputUsage, AgentOutputUsage, AgentUsage, ToolResult } from "@yolk-sdk/agent/protocol";
|
|
3
|
+
import * as Schema from "effect/Schema";
|
|
4
|
+
import { ClassificationResult, ClassifierQuestions, ClassifierState } from "@yolk-sdk/agent/classification";
|
|
5
|
+
import { makeTool, modelVisibleToolError, modelVisibleToolErrorStructuredContent } from "@yolk-sdk/agent/tools";
|
|
6
|
+
//#region src/classifier-tool.ts
|
|
7
|
+
/** Default name of the classifier tool. */
|
|
8
|
+
const classifierToolName = "classify";
|
|
9
|
+
/** Default cap of concurrent classifications per script. */
|
|
10
|
+
const defaultClassifierToolMaxConcurrency = 100;
|
|
11
|
+
/** Default cap of concurrent classifications per process, across scripts and registrations. */
|
|
12
|
+
const defaultClassifierProcessMaxConcurrency = 200;
|
|
13
|
+
const permitCount = (max) => Math.max(1, Math.floor(max));
|
|
14
|
+
/** A classifier concurrency limiter allowing `max` (at least 1) classifications at once. */
|
|
15
|
+
const makeClassifierConcurrencyLimiter = (max) => {
|
|
16
|
+
const permits = permitCount(max);
|
|
17
|
+
const semaphore = Semaphore.makeUnsafe(permits);
|
|
18
|
+
return {
|
|
19
|
+
max: permits,
|
|
20
|
+
withPermit: (effect) => semaphore.withPermits(1)(effect)
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
/** The process-wide limiter every classifier tool uses unless given `processLimiter`. Default
|
|
24
|
+
* 200 concurrent classifications (200 concurrent AI Gateway classifications finished in about
|
|
25
|
+
* 1.2 s without rate-limit errors in a live probe).
|
|
26
|
+
*/
|
|
27
|
+
const defaultClassifierProcessLimiter = makeClassifierConcurrencyLimiter(200);
|
|
28
|
+
/** The classifier tool's input: one state and its named questions. Provider options stay
|
|
29
|
+
* host-owned and are never taken from scripts.
|
|
30
|
+
*/
|
|
31
|
+
const ClassifierToolParams = Schema.Struct({
|
|
32
|
+
state: ClassifierState,
|
|
33
|
+
questions: ClassifierQuestions
|
|
34
|
+
});
|
|
35
|
+
const defaultDescription = (maxConcurrency) => [
|
|
36
|
+
"Classify one item with a classifier model: answer named typed questions about one `state` (a string, JSON object, or JSON array) with probabilities.",
|
|
37
|
+
"Question types: `boolean` (`probability` of true), `choice` (one of the `criteria` keys, with `probabilities`), and `score` (an ordinal level from the `criteria` list, lowest first).",
|
|
38
|
+
`Call it once per item; at most ${maxConcurrency} classifications of one script run at once and further calls queue.`
|
|
39
|
+
].join(" ");
|
|
40
|
+
/** Token usage of a classification as agent usage. `costUsd` has no agent usage field; it stays
|
|
41
|
+
* in the result's `structuredContent.usage`, so nested-call records carry token usage only.
|
|
42
|
+
*/
|
|
43
|
+
const agentUsage = (usage) => AgentUsage.make({
|
|
44
|
+
input: AgentInputUsage.make({ total: usage.inputTokens }),
|
|
45
|
+
output: AgentOutputUsage.make({ total: usage.outputTokens })
|
|
46
|
+
});
|
|
47
|
+
const errorReason = (error) => Predicate.isTagged(error, "ClassificationRequestInvalid") ? "invalid_input" : "unavailable";
|
|
48
|
+
const billedUsage = (error) => Predicate.isTagged(error, "ClassificationResponseInvalid") ? error.usage : void 0;
|
|
49
|
+
/** The parent tool call id of a nested call id `<parentToolCallId>/<seq>`, else the id itself. */
|
|
50
|
+
const scriptKey = (call) => {
|
|
51
|
+
const separator = call.id.lastIndexOf("/");
|
|
52
|
+
return separator > 0 ? call.id.slice(0, separator) : call.id;
|
|
53
|
+
};
|
|
54
|
+
const withUsage = (fields, usage) => {
|
|
55
|
+
if (usage !== void 0) fields.usage = agentUsage(usage);
|
|
56
|
+
return ToolResult.make(fields);
|
|
57
|
+
};
|
|
58
|
+
const successResult = (call, result) => withUsage({
|
|
59
|
+
toolCallId: call.id,
|
|
60
|
+
content: JSON.stringify(result.answers),
|
|
61
|
+
structuredContent: result
|
|
62
|
+
}, result.usage);
|
|
63
|
+
const errorResult = (call, name, error) => withUsage({
|
|
64
|
+
toolCallId: call.id,
|
|
65
|
+
content: error.message,
|
|
66
|
+
isError: true,
|
|
67
|
+
structuredContent: modelVisibleToolErrorStructuredContent(modelVisibleToolError({
|
|
68
|
+
tool: name,
|
|
69
|
+
reason: errorReason(error),
|
|
70
|
+
message: error.message
|
|
71
|
+
}))
|
|
72
|
+
}, billedUsage(error));
|
|
73
|
+
/**
|
|
74
|
+
* A code-mode-only (`callableBy: 'codemode'`, `discovery: 'listed'`) read tool that classifies one
|
|
75
|
+
* item per call with a classifier model. Its content is the compact JSON of the answers, its
|
|
76
|
+
* `structuredContent` the full `ClassificationResult` (which nested calls resolve to), and its
|
|
77
|
+
* token usage is reported on `ToolResult.usage`. Classifier errors become model-visible error
|
|
78
|
+
* results; usage billed before a response error is kept.
|
|
79
|
+
*
|
|
80
|
+
* Each call takes a per-script permit (`maxConcurrency`) and then a process permit
|
|
81
|
+
* (`processLimiter`), so one script's queue never holds process permits it cannot use.
|
|
82
|
+
*/
|
|
83
|
+
const makeClassifierTool = (options) => {
|
|
84
|
+
const name = options.name ?? "classify";
|
|
85
|
+
const maxConcurrency = permitCount(options.maxConcurrency ?? 100);
|
|
86
|
+
const processLimiter = options.processLimiter === void 0 ? defaultClassifierProcessLimiter : options.processLimiter;
|
|
87
|
+
const withProcessPermit = (effect) => processLimiter === false ? effect : processLimiter.withPermit(effect);
|
|
88
|
+
const classify = Predicate.isFunction(options.classify) ? options.classify : options.classify.classify;
|
|
89
|
+
const scripts = /* @__PURE__ */ new Map();
|
|
90
|
+
const withScriptPermit = (call, effect) => Effect.acquireUseRelease(Effect.sync(() => {
|
|
91
|
+
const key = scriptKey(call);
|
|
92
|
+
const entry = scripts.get(key) ?? {
|
|
93
|
+
semaphore: Semaphore.makeUnsafe(maxConcurrency),
|
|
94
|
+
users: 0
|
|
95
|
+
};
|
|
96
|
+
entry.users++;
|
|
97
|
+
scripts.set(key, entry);
|
|
98
|
+
return {
|
|
99
|
+
key,
|
|
100
|
+
entry
|
|
101
|
+
};
|
|
102
|
+
}), ({ entry }) => entry.semaphore.withPermits(1)(withProcessPermit(effect)), ({ key, entry }) => Effect.sync(() => {
|
|
103
|
+
entry.users--;
|
|
104
|
+
if (entry.users === 0 && scripts.get(key) === entry) scripts.delete(key);
|
|
105
|
+
}));
|
|
106
|
+
return makeTool({
|
|
107
|
+
name,
|
|
108
|
+
description: options.description ?? defaultDescription(maxConcurrency),
|
|
109
|
+
parameters: ClassifierToolParams,
|
|
110
|
+
output: ClassificationResult,
|
|
111
|
+
access: "read",
|
|
112
|
+
callableBy: "codemode",
|
|
113
|
+
discovery: "listed",
|
|
114
|
+
execute: ({ call, params }) => {
|
|
115
|
+
return withScriptPermit(call, classify({
|
|
116
|
+
state: params.state,
|
|
117
|
+
questions: params.questions
|
|
118
|
+
})).pipe(Effect.map((result) => successResult(call, result)), Effect.catch((error) => Effect.succeed(errorResult(call, name, error))));
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
//#endregion
|
|
123
|
+
export { ClassifierToolParams, classifierToolName, defaultClassifierProcessLimiter, defaultClassifierProcessMaxConcurrency, defaultClassifierToolMaxConcurrency, makeClassifierConcurrencyLimiter, makeClassifierTool };
|
|
124
|
+
|
|
125
|
+
//# sourceMappingURL=classifier-tool.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classifier-tool.mjs","names":[],"sources":["../src/classifier-tool.ts"],"sourcesContent":["import { Effect, Predicate, Semaphore } from 'effect'\nimport * as Schema from 'effect/Schema'\nimport {\n ClassificationResult,\n ClassifierQuestions,\n ClassifierState,\n type ClassificationError,\n type ClassificationRequest,\n type ClassificationUsage,\n type ClassifierModel\n} from '@yolk-sdk/agent/classification'\nimport {\n AgentInputUsage,\n AgentOutputUsage,\n AgentUsage,\n ToolResult,\n type ToolCall\n} from '@yolk-sdk/agent/protocol'\nimport {\n makeTool,\n modelVisibleToolError,\n modelVisibleToolErrorStructuredContent,\n type ModelVisibleToolErrorReason,\n type ToolRegistration\n} from '@yolk-sdk/agent/tools'\n\n/** Default name of the classifier tool. */\nexport const classifierToolName = 'classify'\n\n/** Default cap of concurrent classifications per script. */\nexport const defaultClassifierToolMaxConcurrency = 100\n\n/** Default cap of concurrent classifications per process, across scripts and registrations. */\nexport const defaultClassifierProcessMaxConcurrency = 200\n\n/** A concurrency cap shared by every classifier tool registration that uses it. */\nexport type ClassifierConcurrencyLimiter = {\n /** Maximum concurrent classifications. */\n readonly max: number\n /** Runs `effect` holding one permit. Waiting is interruptible and never leaks a permit; the\n * permit is released when `effect` ends, however it ends.\n */\n readonly withPermit: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>\n}\n\nconst permitCount = (max: number) => Math.max(1, Math.floor(max))\n\n/** A classifier concurrency limiter allowing `max` (at least 1) classifications at once. */\nexport const makeClassifierConcurrencyLimiter = (max: number): ClassifierConcurrencyLimiter => {\n const permits = permitCount(max)\n const semaphore = Semaphore.makeUnsafe(permits)\n\n return { max: permits, withPermit: effect => semaphore.withPermits(1)(effect) }\n}\n\n/** The process-wide limiter every classifier tool uses unless given `processLimiter`. Default\n * 200 concurrent classifications (200 concurrent AI Gateway classifications finished in about\n * 1.2 s without rate-limit errors in a live probe).\n */\nexport const defaultClassifierProcessLimiter = makeClassifierConcurrencyLimiter(\n defaultClassifierProcessMaxConcurrency\n)\n\ntype ClassifierModelService = (typeof ClassifierModel)['Service']\n\ntype Classify = ClassifierModelService['classify']\n\nexport type MakeClassifierToolOptions = {\n /** `ClassifierModel`'s `classify`, or the service value itself. */\n readonly classify: Classify | ClassifierModelService\n /** Default `classify`. */\n readonly name?: string\n /** Concurrent classifications per script (keyed by the parent tool call id of the nested call\n * id `<parentToolCallId>/<seq>`, or the call id itself); default 100.\n */\n readonly maxConcurrency?: number\n /** Limiter shared across scripts and registrations; default `defaultClassifierProcessLimiter`\n * (200 per process). `false` disables the process cap (the per-script cap still applies).\n */\n readonly processLimiter?: ClassifierConcurrencyLimiter | false\n readonly description?: string\n}\n\n/** The classifier tool's input: one state and its named questions. Provider options stay\n * host-owned and are never taken from scripts.\n */\nexport const ClassifierToolParams = Schema.Struct({\n state: ClassifierState,\n questions: ClassifierQuestions\n})\n\nconst defaultDescription = (maxConcurrency: number) =>\n [\n 'Classify one item with a classifier model: answer named typed questions about one `state` (a string, JSON object, or JSON array) with probabilities.',\n 'Question types: `boolean` (`probability` of true), `choice` (one of the `criteria` keys, with `probabilities`), and `score` (an ordinal level from the `criteria` list, lowest first).',\n `Call it once per item; at most ${maxConcurrency} classifications of one script run at once and further calls queue.`\n ].join(' ')\n\n/** Token usage of a classification as agent usage. `costUsd` has no agent usage field; it stays\n * in the result's `structuredContent.usage`, so nested-call records carry token usage only.\n */\nconst agentUsage = (usage: ClassificationUsage): AgentUsage =>\n AgentUsage.make({\n input: AgentInputUsage.make({ total: usage.inputTokens }),\n output: AgentOutputUsage.make({ total: usage.outputTokens })\n })\n\nconst errorReason = (error: ClassificationError): ModelVisibleToolErrorReason =>\n Predicate.isTagged(error, 'ClassificationRequestInvalid') ? 'invalid_input' : 'unavailable'\n\nconst billedUsage = (error: ClassificationError) =>\n Predicate.isTagged(error, 'ClassificationResponseInvalid') ? error.usage : undefined\n\n/** The parent tool call id of a nested call id `<parentToolCallId>/<seq>`, else the id itself. */\nconst scriptKey = (call: ToolCall) => {\n const separator = call.id.lastIndexOf('/')\n\n return separator > 0 ? call.id.slice(0, separator) : call.id\n}\n\ntype ResultFields = {\n toolCallId: string\n content: string\n isError?: boolean\n structuredContent: unknown\n usage?: AgentUsage\n}\n\nconst withUsage = (fields: ResultFields, usage: ClassificationUsage | undefined) => {\n if (usage !== undefined) {\n fields.usage = agentUsage(usage)\n }\n\n return ToolResult.make(fields)\n}\n\nconst successResult = (call: ToolCall, result: ClassificationResult) =>\n withUsage(\n { toolCallId: call.id, content: JSON.stringify(result.answers), structuredContent: result },\n result.usage\n )\n\nconst errorResult = (call: ToolCall, name: string, error: ClassificationError) =>\n withUsage(\n {\n toolCallId: call.id,\n content: error.message,\n isError: true,\n structuredContent: modelVisibleToolErrorStructuredContent(\n modelVisibleToolError({ tool: name, reason: errorReason(error), message: error.message })\n )\n },\n billedUsage(error)\n )\n\n/**\n * A code-mode-only (`callableBy: 'codemode'`, `discovery: 'listed'`) read tool that classifies one\n * item per call with a classifier model. Its content is the compact JSON of the answers, its\n * `structuredContent` the full `ClassificationResult` (which nested calls resolve to), and its\n * token usage is reported on `ToolResult.usage`. Classifier errors become model-visible error\n * results; usage billed before a response error is kept.\n *\n * Each call takes a per-script permit (`maxConcurrency`) and then a process permit\n * (`processLimiter`), so one script's queue never holds process permits it cannot use.\n */\nexport const makeClassifierTool = <Context>(\n options: MakeClassifierToolOptions\n): ToolRegistration<Context> => {\n const name = options.name ?? classifierToolName\n\n const maxConcurrency = permitCount(options.maxConcurrency ?? defaultClassifierToolMaxConcurrency)\n\n const processLimiter =\n options.processLimiter === undefined ? defaultClassifierProcessLimiter : options.processLimiter\n\n const withProcessPermit = <A, E>(effect: Effect.Effect<A, E>) =>\n processLimiter === false ? effect : processLimiter.withPermit(effect)\n\n const classify = Predicate.isFunction(options.classify)\n ? options.classify\n : options.classify.classify\n\n // One semaphore per script (parent tool call), removed when its last classification ends.\n const scripts = new Map<string, { readonly semaphore: Semaphore.Semaphore; users: number }>()\n\n const withScriptPermit = <A, E>(call: ToolCall, effect: Effect.Effect<A, E>) =>\n Effect.acquireUseRelease(\n Effect.sync(() => {\n const key = scriptKey(call)\n\n const entry = scripts.get(key) ?? {\n semaphore: Semaphore.makeUnsafe(maxConcurrency),\n users: 0\n }\n\n entry.users++\n scripts.set(key, entry)\n\n return { key, entry }\n }),\n ({ entry }) => entry.semaphore.withPermits(1)(withProcessPermit(effect)),\n ({ key, entry }) =>\n Effect.sync(() => {\n entry.users--\n\n if (entry.users === 0 && scripts.get(key) === entry) scripts.delete(key)\n })\n )\n\n return makeTool<Context, typeof ClassifierToolParams>({\n name,\n description: options.description ?? defaultDescription(maxConcurrency),\n parameters: ClassifierToolParams,\n output: ClassificationResult,\n access: 'read',\n callableBy: 'codemode',\n discovery: 'listed',\n execute: ({ call, params }) => {\n const request: ClassificationRequest = { state: params.state, questions: params.questions }\n\n return withScriptPermit(call, classify(request)).pipe(\n Effect.map(result => successResult(call, result)),\n Effect.catch(error => Effect.succeed(errorResult(call, name, error)))\n )\n }\n })\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { ToolJsonSchema } from "@yolk-sdk/agent/protocol";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
|
|
4
|
+
//#region src/executor.d.ts
|
|
5
|
+
/** A JSON Schema document. Only shapes declarations; values are never validated against it. */
|
|
6
|
+
type CodeModeJsonSchema = ToolJsonSchema;
|
|
7
|
+
/** 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. */readonly signal: AbortSignal;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* A function a script can call. Tools are called as `tools.<identifier>(args)` and
|
|
14
|
+
* `tools["<name>"](args)`; the identifier replaces characters that are not valid in a JavaScript
|
|
15
|
+
* identifier with `_`. Arguments and results make a JSON round trip; a rejection surfaces in the
|
|
16
|
+
* script as an `Error` with the same message (`makeCodeModeTool` rejects with
|
|
17
|
+
* `<call label>: <message>`, the label being `tools.<identifier>` or `tools["<name>"]`).
|
|
18
|
+
*
|
|
19
|
+
* `args` is the JSON value of the script's first argument (`undefined` when there is none), read
|
|
20
|
+
* once the way `JSON.stringify` reads it (getters and `toJSON` run once); the checked value is the
|
|
21
|
+
* one sent. Executors reject, inside the script and without calling `execute`, values that JSON
|
|
22
|
+
* would silently change instead of coercing them: non-finite numbers, invalid `Date`s,
|
|
23
|
+
* `undefined`/function/symbol array items and holes, function/symbol/bigint values, cycles, and
|
|
24
|
+
* non-plain objects without `toJSON` (`Map`, `Set`, `RegExp`, `Error`, class instances), including
|
|
25
|
+
* inside `toJSON` results. `undefined`-valued object keys count as absent, as JSON drops them; a
|
|
26
|
+
* valid `Date` passes as its ISO string. An argument too large or deep to check completely is
|
|
27
|
+
* rejected, never sent unchecked (the pi executor: more than 100000 values or 64 levels). A
|
|
28
|
+
* rejection names the call the way scripts reach it, as `makeCodeModeTool` does: the first tool
|
|
29
|
+
* with an identifier is `tools.<identifier>`, a later one `tools["<name>"]`.
|
|
30
|
+
*/
|
|
31
|
+
type CodeModeExecutorTool = {
|
|
32
|
+
readonly name: string;
|
|
33
|
+
readonly description?: string;
|
|
34
|
+
readonly inputSchema?: CodeModeJsonSchema;
|
|
35
|
+
readonly outputSchema?: CodeModeJsonSchema;
|
|
36
|
+
/** Diagnostic call label for rejection messages: `tools.<identifier>`, or `tools["<name>"]`
|
|
37
|
+
* when an earlier tool holds the identifier (a tool whose name is taken too is unreachable and
|
|
38
|
+
* the label only names it). `makeCodeModeTool` sets it; executors fall back to pi's binding rule.
|
|
39
|
+
*/
|
|
40
|
+
readonly callLabel?: string;
|
|
41
|
+
readonly execute: (args: unknown, context: CodeModeToolContext) => Promise<unknown>;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* A top-level function (`name`) or namespace member (`namespace.member`) for scripts. Globals are
|
|
45
|
+
* host helpers: they are not tools and are not recorded as nested calls.
|
|
46
|
+
*/
|
|
47
|
+
type CodeModeExecutorGlobal = Omit<CodeModeExecutorTool, 'callLabel'> & {
|
|
48
|
+
/** `execute` receives every call argument as an array instead of the first one. */readonly spread?: boolean; /** TypeScript parameter list and return type for declarations. */
|
|
49
|
+
readonly signature?: string;
|
|
50
|
+
};
|
|
51
|
+
/** One item of script output, in the order the script produced it. `data` is base64. */
|
|
52
|
+
type CodeModeOutputItem = {
|
|
53
|
+
readonly type: 'text';
|
|
54
|
+
readonly text: string;
|
|
55
|
+
} | {
|
|
56
|
+
readonly type: 'image';
|
|
57
|
+
readonly data: string;
|
|
58
|
+
readonly mimeType: string;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* - `script`: the script threw, failed to parse, or its TypeScript could not be stripped.
|
|
62
|
+
* - `timeout`: the deadline expired.
|
|
63
|
+
* - `aborted`: the caller's signal fired.
|
|
64
|
+
* - `sandbox`: the engine failed outside the script's control.
|
|
65
|
+
*/
|
|
66
|
+
type CodeModeErrorKind = 'script' | 'timeout' | 'aborted' | 'sandbox';
|
|
67
|
+
type CodeModeError = {
|
|
68
|
+
readonly kind: CodeModeErrorKind;
|
|
69
|
+
readonly message: string;
|
|
70
|
+
readonly stack?: string;
|
|
71
|
+
};
|
|
72
|
+
/** Keys a successful script changed with `store()`; `delete` lists keys stored as `undefined`. */
|
|
73
|
+
type CodeModeStoreWrites = {
|
|
74
|
+
readonly set: CodeModeStore;
|
|
75
|
+
readonly delete: ReadonlyArray<string>;
|
|
76
|
+
};
|
|
77
|
+
type CodeModeExecutionResult = {
|
|
78
|
+
readonly ok: boolean; /** The script's return value (JSON); absent when it returned nothing. */
|
|
79
|
+
readonly value?: unknown;
|
|
80
|
+
readonly error?: CodeModeError; /** Kept for failed executions too, up to the failure. */
|
|
81
|
+
readonly output: ReadonlyArray<CodeModeOutputItem>; /** Only successful executions report writes. */
|
|
82
|
+
readonly storeWrites?: CodeModeStoreWrites;
|
|
83
|
+
};
|
|
84
|
+
type CodeModeExecuteOptions = {
|
|
85
|
+
readonly tools: ReadonlyArray<CodeModeExecutorTool>;
|
|
86
|
+
readonly globals: ReadonlyArray<CodeModeExecutorGlobal>; /** Overall deadline, including time spent in tools and waiting for a free execution slot. */
|
|
87
|
+
readonly timeoutMs: number; /** Heap cap of the script's VM. */
|
|
88
|
+
readonly memoryLimitBytes: number; /** Values the script reads with `load(key)`. */
|
|
89
|
+
readonly store: CodeModeStore;
|
|
90
|
+
readonly signal: AbortSignal;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Runs one script. `code` is the body of an async function (top-level `await` and `return`) and
|
|
94
|
+
* may carry TypeScript annotations: executors strip them or report a `script` error. The promise
|
|
95
|
+
* resolves for every script outcome, including failures, timeouts, and aborts; a rejection is
|
|
96
|
+
* treated as a `sandbox` failure. Running tool calls must be cancelled through their signal when
|
|
97
|
+
* the script ends. Tool arguments follow the JSON rules of `CodeModeExecutorTool`: pass their JSON
|
|
98
|
+
* value and reject what JSON would silently change.
|
|
99
|
+
*
|
|
100
|
+
* `@yolk-sdk/codemode/node` provides `makePiCodeModeExecutor`; hosts on other runtimes supply their
|
|
101
|
+
* own engine behind this interface.
|
|
102
|
+
*/
|
|
103
|
+
type CodeModeExecutor = {
|
|
104
|
+
readonly execute: (code: string, options: CodeModeExecuteOptions) => Promise<CodeModeExecutionResult>;
|
|
105
|
+
};
|
|
106
|
+
//#endregion
|
|
107
|
+
export { CodeModeError, CodeModeErrorKind, CodeModeExecuteOptions, CodeModeExecutionResult, CodeModeExecutor, CodeModeExecutorGlobal, CodeModeExecutorTool, CodeModeJsonSchema, CodeModeOutputItem, CodeModeStore, CodeModeStoreWrites, CodeModeToolContext };
|
|
108
|
+
//# sourceMappingURL=executor.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.d.mts","names":[],"sources":["../src/executor.ts"],"mappings":";;;;;KAIY,kBAAA,GAAqB,cAAc;AAA/C;AAAA,KAGY,aAAA,GAAgB,QAAA,CAAS,MAAA,SAAe,MAAA,CAAO,IAAA;AAAA,KAE/C,mBAAA;EALmC,uGAOpC,MAAA,EAAQ,WAAW;AAAA;;;;;;;;;;;;;AAJiC;AAE/D;;;;AAE8B;AAsB9B;KAAY,oBAAA;EAAA,SACD,IAAA;EAAA,SACA,WAAA;EAAA,SACA,WAAA,GAAc,kBAAA;EAAA,SACd,YAAA,GAAe,kBAAA;EAM2C;;;;EAAA,SAD1D,SAAA;EAAA,SACA,OAAA,GAAU,IAAA,WAAe,OAAA,EAAS,mBAAA,KAAwB,OAAA;AAAA;;;;;KAOzD,sBAAA,GAAyB,IAAI,CAAC,oBAAA;EAPG,4FASlC,MAAA,YAT0D;EAAA,SAW1D,SAAA;AAAA;AAJX;AAAA,KAQY,kBAAA;EAAA,SACG,IAAA;EAAA,SAAuB,IAAA;AAAA;EAAA,SACvB,IAAA;EAAA,SAAwB,IAAA;EAAA,SAAuB,QAAA;AAAA;AAN1C;AAIpB;;;;;AAJoB,KAcR,iBAAA;AAAA,KAEA,aAAA;EAAA,SACD,IAAA,EAAM,iBAAiB;EAAA,SACvB,OAAA;EAAA,SACA,KAAA;AAAA;AALX;AAAA,KASY,mBAAA;EAAA,SACD,GAAA,EAAK,aAAA;EAAA,SACL,MAAA,EAAQ,aAAa;AAAA;AAAA,KAGpB,uBAAA;EAAA,SACD,EAAA;WAEA,KAAA;EAAA,SACA,KAAA,GAAQ,aAAA,EAfF;EAAA,SAiBN,MAAA,EAAQ,aAAA,CAAc,kBAAA,GAftB;EAAA,SAiBA,WAAA,GAAc,mBAAA;AAAA;AAAA,KAGb,sBAAA;EAAA,SACD,KAAA,EAAO,aAAA,CAAc,oBAAA;EAAA,SACrB,OAAA,EAAS,aAAA,CAAc,sBAAA,GAhBF;EAAA,SAkBrB,SAAA,UAnBK;EAAA,SAqBL,gBAAA,UApBQ;EAAA,SAsBR,KAAA,EAAO,aAAA;EAAA,SACP,MAAA,EAAQ,WAAA;AAAA;;;;;;;;;;;;KAcP,gBAAA;EAAA,SACD,OAAA,GACP,IAAA,UACA,OAAA,EAAS,sBAAA,KACN,OAAA,CAAQ,uBAAA;AAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { CodeModeError, CodeModeErrorKind, CodeModeExecuteOptions, CodeModeExecutionResult, CodeModeExecutor, CodeModeExecutorGlobal, CodeModeExecutorTool, CodeModeJsonSchema, CodeModeOutputItem, CodeModeStore, CodeModeStoreWrites, CodeModeToolContext } from "./executor.mjs";
|
|
2
|
+
import { CodeModeCatalogTool, CodeModeDescriptionInput, CodeModeListing, CodeModeToolExposure, codeModeCatalog, defaultCodeModeInlineBudget, describeCodeModeTool, estimateCodeModeTokens, findCodeModeTool, renderCodeModeDescription, selectCodeModeListing } from "./catalog.mjs";
|
|
3
|
+
import { ClassifierConcurrencyLimiter, ClassifierToolParams, MakeClassifierToolOptions, classifierToolName, defaultClassifierProcessLimiter, defaultClassifierProcessMaxConcurrency, defaultClassifierToolMaxConcurrency, makeClassifierConcurrencyLimiter, makeClassifierTool } from "./classifier-tool.mjs";
|
|
4
|
+
import { CodeModeSearchHit, CodeModeSearchOptions, codeModeSearchTokens, defaultCodeModeSearchLimit, searchCodeModeTools } from "./search.mjs";
|
|
5
|
+
import { CodeModeCallSummary, CodeModeResultSegment, boundCodeModeSegments, codeModeResultSegments, codeModeSegmentsContent, defaultCodeModeMaxImageBytes, defaultCodeModeMaxImages, summarizeCodeModeCalls } from "./output.mjs";
|
|
6
|
+
import { CodeModeInterruptedCall, CodeModeInterruptedCallStatus, CodeModeInterruptedCalls, CodeModeStructuredContent, CodeModeToolResultEntry, codeModeStoreFromToolResults, maxCodeModeStoreTotalChars, maxCodeModeStoreValueChars } from "./store.mjs";
|
|
7
|
+
import { CodeModeAfterNestedCallInput, CodeModeLimits, CodeModeNestedCallOutcome, MakeCodeModeToolOptions, codeModeDeadlineMarginMs, codeModeMinimumTimeoutMs, codeModeToolName, defaultCodeModeLimits, makeCodeModeTool } from "./tool.mjs";
|
|
8
|
+
export { type ClassifierConcurrencyLimiter, ClassifierToolParams, type CodeModeAfterNestedCallInput, type CodeModeCallSummary, 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 CodeModeStore, type CodeModeStoreWrites, type CodeModeStructuredContent, type CodeModeToolContext, type CodeModeToolExposure, type CodeModeToolResultEntry, type MakeClassifierToolOptions, type MakeCodeModeToolOptions, boundCodeModeSegments, classifierToolName, codeModeCatalog, codeModeDeadlineMarginMs, codeModeMinimumTimeoutMs, codeModeResultSegments, codeModeSearchTokens, codeModeSegmentsContent, codeModeStoreFromToolResults, codeModeToolName, defaultClassifierProcessLimiter, defaultClassifierProcessMaxConcurrency, defaultClassifierToolMaxConcurrency, defaultCodeModeInlineBudget, defaultCodeModeLimits, defaultCodeModeMaxImageBytes, defaultCodeModeMaxImages, defaultCodeModeSearchLimit, describeCodeModeTool, estimateCodeModeTokens, findCodeModeTool, makeClassifierConcurrencyLimiter, makeClassifierTool, makeCodeModeTool, maxCodeModeStoreTotalChars, maxCodeModeStoreValueChars, renderCodeModeDescription, searchCodeModeTools, selectCodeModeListing, summarizeCodeModeCalls };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { codeModeCatalog, defaultCodeModeInlineBudget, describeCodeModeTool, estimateCodeModeTokens, findCodeModeTool, renderCodeModeDescription, selectCodeModeListing } from "./catalog.mjs";
|
|
2
|
+
import { ClassifierToolParams, classifierToolName, defaultClassifierProcessLimiter, defaultClassifierProcessMaxConcurrency, defaultClassifierToolMaxConcurrency, makeClassifierConcurrencyLimiter, makeClassifierTool } from "./classifier-tool.mjs";
|
|
3
|
+
import { codeModeSearchTokens, defaultCodeModeSearchLimit, searchCodeModeTools } from "./search.mjs";
|
|
4
|
+
import { boundCodeModeSegments, codeModeResultSegments, codeModeSegmentsContent, defaultCodeModeMaxImageBytes, defaultCodeModeMaxImages, summarizeCodeModeCalls } from "./output.mjs";
|
|
5
|
+
import { codeModeDeadlineMarginMs, codeModeMinimumTimeoutMs, codeModeToolName, defaultCodeModeLimits, makeCodeModeTool } from "./tool.mjs";
|
|
6
|
+
import { codeModeStoreFromToolResults, maxCodeModeStoreTotalChars, maxCodeModeStoreValueChars } from "./store.mjs";
|
|
7
|
+
export { ClassifierToolParams, boundCodeModeSegments, classifierToolName, codeModeCatalog, codeModeDeadlineMarginMs, codeModeMinimumTimeoutMs, codeModeResultSegments, codeModeSearchTokens, codeModeSegmentsContent, codeModeStoreFromToolResults, codeModeToolName, defaultClassifierProcessLimiter, defaultClassifierProcessMaxConcurrency, defaultClassifierToolMaxConcurrency, defaultCodeModeInlineBudget, defaultCodeModeLimits, defaultCodeModeMaxImageBytes, defaultCodeModeMaxImages, defaultCodeModeSearchLimit, describeCodeModeTool, estimateCodeModeTokens, findCodeModeTool, makeClassifierConcurrencyLimiter, makeClassifierTool, makeCodeModeTool, maxCodeModeStoreTotalChars, maxCodeModeStoreValueChars, renderCodeModeDescription, searchCodeModeTools, selectCodeModeListing, summarizeCodeModeCalls };
|
package/dist/node.d.mts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { CodeModeExecutor } from "./executor.mjs";
|
|
2
|
+
import { CodemodeWasmModule } from "@earendil-works/pi-codemode";
|
|
3
|
+
|
|
4
|
+
//#region src/node.d.ts
|
|
5
|
+
/** Default cap of concurrent script executions per executor. */
|
|
6
|
+
declare const defaultMaxConcurrentExecutions = 4;
|
|
7
|
+
type PiCodeModeExecutorOptions = {
|
|
8
|
+
/**
|
|
9
|
+
* Compiled `quickjs-wasi/quickjs.wasm` (see pi's `loadQuickJSWasm`). Default: the file of the
|
|
10
|
+
* installed `quickjs-wasi` package, compiled once per process.
|
|
11
|
+
*/
|
|
12
|
+
readonly wasm?: CodemodeWasmModule | Promise<CodemodeWasmModule>;
|
|
13
|
+
/**
|
|
14
|
+
* Worker entry that imports `@earendil-works/pi-codemode/worker`. Default: pi's own worker file,
|
|
15
|
+
* which exists on disk when the package is not bundled (Next `serverExternalPackages`).
|
|
16
|
+
*/
|
|
17
|
+
readonly workerUrl?: string | URL; /** Concurrent executions of this executor; more wait for a free slot. Default 4. */
|
|
18
|
+
readonly maxConcurrentExecutions?: number;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* A `CodeModeExecutor` on `@earendil-works/pi-codemode`: every execution gets a fresh QuickJS VM
|
|
22
|
+
* (WebAssembly) in a fresh worker thread, closed when the execution ends. Applies the timeout,
|
|
23
|
+
* heap cap, abort signal, and store, strips TypeScript annotations first, rejects tool arguments
|
|
24
|
+
* that JSON would silently change inside the script (see `CodeModeExecutorTool`), and caps concurrent
|
|
25
|
+
* executions per executor; create one executor per process (module scope) so the cap is
|
|
26
|
+
* per process.
|
|
27
|
+
*
|
|
28
|
+
* Node only. The worker inherits a copy of `process.env` (scripts cannot read it: the VM has no
|
|
29
|
+
* `process`). Under Next.js, list `@yolk-sdk/codemode`, `@earendil-works/pi-codemode`, and
|
|
30
|
+
* `quickjs-wasi` in `serverExternalPackages` so the worker file and wasm stay on disk.
|
|
31
|
+
*/
|
|
32
|
+
declare const makePiCodeModeExecutor: (options?: PiCodeModeExecutorOptions) => CodeModeExecutor;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { PiCodeModeExecutorOptions, defaultMaxConcurrentExecutions, makePiCodeModeExecutor };
|
|
35
|
+
//# sourceMappingURL=node.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.d.mts","names":[],"sources":["../src/node.ts"],"mappings":";;;;;cAsBa,8BAAA;AAAA,KAED,yBAAA;EAF+B;;;AAAA;EAAA,SAOhC,IAAA,GAAO,kBAAA,GAAqB,OAAA,CAAQ,kBAAA;EALV;;;;EAAA,SAU1B,SAAA,YAAqB,GAAA,EAAA;EAAA,SAErB,uBAAA;AAAA;;;;;;;;;AAAuB;AA8alC;;;cAAa,sBAAA,GACX,OAAA,GAAS,yBAAA,KACR,gBA6EF"}
|