@animalabs/agent-framework 0.7.0 → 0.7.3
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/dist/src/code-execution/index.d.ts +14 -0
- package/dist/src/code-execution/index.d.ts.map +1 -0
- package/dist/src/code-execution/index.js +13 -0
- package/dist/src/code-execution/index.js.map +1 -0
- package/dist/src/code-execution/py-runner.d.ts +96 -0
- package/dist/src/code-execution/py-runner.d.ts.map +1 -0
- package/dist/src/code-execution/py-runner.js +371 -0
- package/dist/src/code-execution/py-runner.js.map +1 -0
- package/dist/src/code-execution/runtime-py.d.ts +32 -0
- package/dist/src/code-execution/runtime-py.d.ts.map +1 -0
- package/dist/src/code-execution/runtime-py.js +265 -0
- package/dist/src/code-execution/runtime-py.js.map +1 -0
- package/dist/src/code-execution/tool-definition.d.ts +16 -0
- package/dist/src/code-execution/tool-definition.d.ts.map +1 -0
- package/dist/src/code-execution/tool-definition.js +47 -0
- package/dist/src/code-execution/tool-definition.js.map +1 -0
- package/dist/src/framework.d.ts +43 -1
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +274 -26
- package/dist/src/framework.js.map +1 -1
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcpl/channel-registry.d.ts +12 -0
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +45 -0
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/prose-grammar.d.ts +24 -0
- package/dist/src/mcpl/prose-grammar.d.ts.map +1 -0
- package/dist/src/mcpl/prose-grammar.js +33 -0
- package/dist/src/mcpl/prose-grammar.js.map +1 -0
- package/dist/src/mcpl/prose-stream-router.d.ts +58 -0
- package/dist/src/mcpl/prose-stream-router.d.ts.map +1 -0
- package/dist/src/mcpl/prose-stream-router.js +168 -0
- package/dist/src/mcpl/prose-stream-router.js.map +1 -0
- package/dist/src/types/framework.d.ts +33 -0
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/test/code-execution.test.d.ts +11 -0
- package/dist/test/code-execution.test.d.ts.map +1 -0
- package/dist/test/code-execution.test.js +423 -0
- package/dist/test/code-execution.test.js.map +1 -0
- package/dist/test/prose-stream-router.test.d.ts +2 -0
- package/dist/test/prose-stream-router.test.d.ts.map +1 -0
- package/dist/test/prose-stream-router.test.js +87 -0
- package/dist/test/prose-stream-router.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/code-execution/index.ts +19 -0
- package/src/code-execution/py-runner.ts +439 -0
- package/src/code-execution/runtime-py.ts +265 -0
- package/src/code-execution/tool-definition.ts +54 -0
- package/src/framework.ts +307 -28
- package/src/index.ts +17 -0
- package/src/mcpl/channel-registry.ts +53 -0
- package/src/mcpl/prose-grammar.ts +40 -0
- package/src/mcpl/prose-stream-router.ts +161 -0
- package/src/types/framework.ts +35 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side programmatic tool calling (PTC) for the connectome native stack.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the surface Anthropic's server-side PTC was trained on
|
|
5
|
+
* (code_execution tool + tools exposed as async python functions), but runs
|
|
6
|
+
* the interpreter on the host and dispatches inner tool calls in-process
|
|
7
|
+
* through the framework's own tool routing — intermediate results never
|
|
8
|
+
* enter the model's context; the full trace stays on-box.
|
|
9
|
+
*/
|
|
10
|
+
export { PyRunner, buildInjectedTools } from './py-runner.js';
|
|
11
|
+
export type { InjectedTool, ExecResult, PyRunnerOptions, ScriptToolCallHandler, } from './py-runner.js';
|
|
12
|
+
export { PYTHON_RUNTIME_SOURCE } from './runtime-py.js';
|
|
13
|
+
export { buildCodeExecutionToolDefinition, CODE_EXECUTION_TOOL_NAME } from './tool-definition.js';
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/code-execution/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAC9D,YAAY,EACV,YAAY,EACZ,UAAU,EACV,eAAe,EACf,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,gCAAgC,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side programmatic tool calling (PTC) for the connectome native stack.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the surface Anthropic's server-side PTC was trained on
|
|
5
|
+
* (code_execution tool + tools exposed as async python functions), but runs
|
|
6
|
+
* the interpreter on the host and dispatches inner tool calls in-process
|
|
7
|
+
* through the framework's own tool routing — intermediate results never
|
|
8
|
+
* enter the model's context; the full trace stays on-box.
|
|
9
|
+
*/
|
|
10
|
+
export { PyRunner, buildInjectedTools } from './py-runner.js';
|
|
11
|
+
export { PYTHON_RUNTIME_SOURCE } from './runtime-py.js';
|
|
12
|
+
export { buildCodeExecutionToolDefinition, CODE_EXECUTION_TOOL_NAME } from './tool-definition.js';
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/code-execution/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAO9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,gCAAgC,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PyRunner — subprocess lifecycle + protocol driver for the client-side
|
|
3
|
+
* programmatic tool calling (`code_execution`) runtime.
|
|
4
|
+
*
|
|
5
|
+
* One runner per agent. The interpreter is spawned lazily on first exec,
|
|
6
|
+
* persists between execs (script globals survive — container-reuse
|
|
7
|
+
* semantics), and is reclaimed after an idle period, mirroring the managed
|
|
8
|
+
* runtime's ~5-minute container reclaim.
|
|
9
|
+
*
|
|
10
|
+
* This is a ROBUSTNESS boundary, not a security sandbox — same doctrine as
|
|
11
|
+
* GateScript: the agent already has broader host access through its tools.
|
|
12
|
+
* What this class guarantees is liveness: a wedged or runaway script cannot
|
|
13
|
+
* hang the agent turn (cancel -> grace -> SIGKILL -> respawn) and a crashed
|
|
14
|
+
* interpreter surfaces as a tool result, never as an unhandled rejection.
|
|
15
|
+
*/
|
|
16
|
+
export interface InjectedTool {
|
|
17
|
+
/** Python identifier the tool is bound to (sanitized, `--` -> `__`). */
|
|
18
|
+
pyName: string;
|
|
19
|
+
/** Exact framework tool name, sent back on tool_call ops. */
|
|
20
|
+
toolName: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ExecResult {
|
|
23
|
+
stdout: string;
|
|
24
|
+
stderr: string;
|
|
25
|
+
returnCode: number;
|
|
26
|
+
/** Set when the host killed the script (deadline / abort / crash). */
|
|
27
|
+
aborted?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/** Resolves inner tool calls. Must never reject — map errors to strings. */
|
|
30
|
+
export type ScriptToolCallHandler = (toolName: string, args: Record<string, unknown>) => Promise<string>;
|
|
31
|
+
export interface PyRunnerOptions {
|
|
32
|
+
pythonPath?: string;
|
|
33
|
+
/** Per-tool-call timeout surfaced as TimeoutError inside the script. */
|
|
34
|
+
toolCallTimeoutMs?: number;
|
|
35
|
+
/** Whole-script deadline; exceeded -> cancel, grace, kill. */
|
|
36
|
+
scriptTimeoutMs?: number;
|
|
37
|
+
/** Idle interpreter reclaim (state lost), mirroring container reclaim. */
|
|
38
|
+
idleReclaimMs?: number;
|
|
39
|
+
onToolCall: ScriptToolCallHandler;
|
|
40
|
+
/** Log prefix, typically the agent name. */
|
|
41
|
+
label?: string;
|
|
42
|
+
}
|
|
43
|
+
export declare class PyRunner {
|
|
44
|
+
private readonly pythonPath;
|
|
45
|
+
private readonly toolCallTimeoutMs;
|
|
46
|
+
private readonly scriptTimeoutMs;
|
|
47
|
+
private readonly idleReclaimMs;
|
|
48
|
+
private readonly onToolCall;
|
|
49
|
+
private readonly label;
|
|
50
|
+
private child;
|
|
51
|
+
private childReady;
|
|
52
|
+
private reader;
|
|
53
|
+
private runtimeDir;
|
|
54
|
+
private pending;
|
|
55
|
+
private idleTimer;
|
|
56
|
+
private execCounter;
|
|
57
|
+
private disposed;
|
|
58
|
+
constructor(options: PyRunnerOptions);
|
|
59
|
+
get busy(): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Run one script. Tools are (re-)injected before every exec so the
|
|
62
|
+
* interpreter always reflects the current tool surface (list_changed etc.).
|
|
63
|
+
* Never rejects: every failure mode resolves to an ExecResult.
|
|
64
|
+
*/
|
|
65
|
+
exec(code: string, tools: InjectedTool[]): Promise<ExecResult>;
|
|
66
|
+
/**
|
|
67
|
+
* Abort a running script (turn cancelled, agent reset/stopped). The
|
|
68
|
+
* interpreter is reclaimed: after an abort its state is suspect (a script
|
|
69
|
+
* died midway), and the next exec gets a fresh one.
|
|
70
|
+
*/
|
|
71
|
+
abort(reason: string): void;
|
|
72
|
+
dispose(): void;
|
|
73
|
+
private ensureChild;
|
|
74
|
+
private readyResolver;
|
|
75
|
+
private handleLine;
|
|
76
|
+
private settlePending;
|
|
77
|
+
private send;
|
|
78
|
+
/** Kill interpreter + clean temp dir. State is lost (by design). */
|
|
79
|
+
private reclaim;
|
|
80
|
+
private teardownChild;
|
|
81
|
+
private armIdleTimer;
|
|
82
|
+
private clearIdleTimer;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Map framework tool names to python identifiers: `--` (the framework's
|
|
86
|
+
* prefix separator) becomes `__`, and every remaining character that is not
|
|
87
|
+
* valid in a python identifier becomes `_` (fleet reality: module and server
|
|
88
|
+
* ids contain single hyphens — `mcpl-admin`, `dog-events` — found live on
|
|
89
|
+
* the first canary run, 2026-07-26). A leading digit gets a `_` prefix.
|
|
90
|
+
*
|
|
91
|
+
* Names that sanitize to nothing, or that collide after sanitization, are
|
|
92
|
+
* skipped loudly — a skipped tool is unreachable from scripts (the
|
|
93
|
+
* exact-name tools[...] dict is built from this same list).
|
|
94
|
+
*/
|
|
95
|
+
export declare function buildInjectedTools(toolNames: string[], log?: (message: string) => void): InjectedTool[];
|
|
96
|
+
//# sourceMappingURL=py-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"py-runner.d.ts","sourceRoot":"","sources":["../../../src/code-execution/py-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AASH,MAAM,WAAW,YAAY;IAC3B,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,4EAA4E;AAC5E,MAAM,MAAM,qBAAqB,GAAG,CAClC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC1B,OAAO,CAAC,MAAM,CAAC,CAAC;AAErB,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wEAAwE;IACxE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8DAA8D;IAC9D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,qBAAqB,CAAC;IAClC,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAeD,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAwB;IACnD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAE/B,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,SAAS,CAA8C;IAC/D,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,QAAQ,CAAS;gBAEb,OAAO,EAAE,eAAe;IASpC,IAAI,IAAI,IAAI,OAAO,CAElB;IAED;;;;OAIG;IACG,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC;IAiEpE;;;;OAIG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAY3B,OAAO,IAAI,IAAI;YAgBD,WAAW;IA2EzB,OAAO,CAAC,aAAa,CAA6B;IAElD,OAAO,CAAC,UAAU;IA8ClB,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,IAAI;IAYZ,oEAAoE;IACpE,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,aAAa;IA8BrB,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,cAAc;CAMvB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EAAE,EACnB,GAAG,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAA8B,GACvD,YAAY,EAAE,CAmBhB"}
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PyRunner — subprocess lifecycle + protocol driver for the client-side
|
|
3
|
+
* programmatic tool calling (`code_execution`) runtime.
|
|
4
|
+
*
|
|
5
|
+
* One runner per agent. The interpreter is spawned lazily on first exec,
|
|
6
|
+
* persists between execs (script globals survive — container-reuse
|
|
7
|
+
* semantics), and is reclaimed after an idle period, mirroring the managed
|
|
8
|
+
* runtime's ~5-minute container reclaim.
|
|
9
|
+
*
|
|
10
|
+
* This is a ROBUSTNESS boundary, not a security sandbox — same doctrine as
|
|
11
|
+
* GateScript: the agent already has broader host access through its tools.
|
|
12
|
+
* What this class guarantees is liveness: a wedged or runaway script cannot
|
|
13
|
+
* hang the agent turn (cancel -> grace -> SIGKILL -> respawn) and a crashed
|
|
14
|
+
* interpreter surfaces as a tool result, never as an unhandled rejection.
|
|
15
|
+
*/
|
|
16
|
+
import { spawn } from 'node:child_process';
|
|
17
|
+
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
18
|
+
import { tmpdir } from 'node:os';
|
|
19
|
+
import { join } from 'node:path';
|
|
20
|
+
import { createInterface } from 'node:readline';
|
|
21
|
+
import { PYTHON_RUNTIME_SOURCE } from './runtime-py.js';
|
|
22
|
+
const DEFAULT_TOOL_CALL_TIMEOUT_MS = 270_000;
|
|
23
|
+
const DEFAULT_SCRIPT_TIMEOUT_MS = 600_000;
|
|
24
|
+
const DEFAULT_IDLE_RECLAIM_MS = 300_000;
|
|
25
|
+
const CANCEL_GRACE_MS = 10_000;
|
|
26
|
+
export class PyRunner {
|
|
27
|
+
pythonPath;
|
|
28
|
+
toolCallTimeoutMs;
|
|
29
|
+
scriptTimeoutMs;
|
|
30
|
+
idleReclaimMs;
|
|
31
|
+
onToolCall;
|
|
32
|
+
label;
|
|
33
|
+
child = null;
|
|
34
|
+
childReady = null;
|
|
35
|
+
reader = null;
|
|
36
|
+
runtimeDir = null;
|
|
37
|
+
pending = null;
|
|
38
|
+
idleTimer = null;
|
|
39
|
+
execCounter = 0;
|
|
40
|
+
disposed = false;
|
|
41
|
+
constructor(options) {
|
|
42
|
+
this.pythonPath = options.pythonPath ?? 'python3';
|
|
43
|
+
this.toolCallTimeoutMs = options.toolCallTimeoutMs ?? DEFAULT_TOOL_CALL_TIMEOUT_MS;
|
|
44
|
+
this.scriptTimeoutMs = options.scriptTimeoutMs ?? DEFAULT_SCRIPT_TIMEOUT_MS;
|
|
45
|
+
this.idleReclaimMs = options.idleReclaimMs ?? DEFAULT_IDLE_RECLAIM_MS;
|
|
46
|
+
this.onToolCall = options.onToolCall;
|
|
47
|
+
this.label = options.label ?? 'pytc';
|
|
48
|
+
}
|
|
49
|
+
get busy() {
|
|
50
|
+
return this.pending !== null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Run one script. Tools are (re-)injected before every exec so the
|
|
54
|
+
* interpreter always reflects the current tool surface (list_changed etc.).
|
|
55
|
+
* Never rejects: every failure mode resolves to an ExecResult.
|
|
56
|
+
*/
|
|
57
|
+
async exec(code, tools) {
|
|
58
|
+
if (this.disposed) {
|
|
59
|
+
return { stdout: '', stderr: 'code_execution runner disposed', returnCode: 1, aborted: true };
|
|
60
|
+
}
|
|
61
|
+
if (this.pending) {
|
|
62
|
+
return {
|
|
63
|
+
stdout: '',
|
|
64
|
+
stderr: 'RuntimeError: another code_execution script is already running for this agent',
|
|
65
|
+
returnCode: 1,
|
|
66
|
+
aborted: true,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
this.clearIdleTimer();
|
|
70
|
+
try {
|
|
71
|
+
await this.ensureChild();
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
75
|
+
this.reclaim('spawn-failed');
|
|
76
|
+
return {
|
|
77
|
+
stdout: '',
|
|
78
|
+
stderr: `Failed to start python runtime (${this.pythonPath}): ${message}`,
|
|
79
|
+
returnCode: 1,
|
|
80
|
+
aborted: true,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const execId = `e${++this.execCounter}`;
|
|
84
|
+
const result = await new Promise((resolve) => {
|
|
85
|
+
const pending = {
|
|
86
|
+
id: execId,
|
|
87
|
+
resolve,
|
|
88
|
+
deadlineTimer: null,
|
|
89
|
+
killTimer: null,
|
|
90
|
+
settled: false,
|
|
91
|
+
};
|
|
92
|
+
this.pending = pending;
|
|
93
|
+
pending.deadlineTimer = setTimeout(() => {
|
|
94
|
+
// Deadline: ask politely first (script sees CancelledError and its
|
|
95
|
+
// exec_result still flows back), then kill on unresponsiveness.
|
|
96
|
+
this.send({ op: 'cancel', id: execId, reason: 'deadline' });
|
|
97
|
+
pending.killTimer = setTimeout(() => {
|
|
98
|
+
this.settlePending({
|
|
99
|
+
stdout: '',
|
|
100
|
+
stderr: `script killed after exceeding ${Math.round(this.scriptTimeoutMs / 1000)}s deadline`,
|
|
101
|
+
returnCode: 1,
|
|
102
|
+
aborted: true,
|
|
103
|
+
});
|
|
104
|
+
this.reclaim('deadline-kill');
|
|
105
|
+
}, CANCEL_GRACE_MS);
|
|
106
|
+
}, this.scriptTimeoutMs);
|
|
107
|
+
this.send({
|
|
108
|
+
op: 'init',
|
|
109
|
+
tools: tools.map((t) => ({ py_name: t.pyName, tool_name: t.toolName })),
|
|
110
|
+
call_timeout_s: Math.round(this.toolCallTimeoutMs / 1000),
|
|
111
|
+
});
|
|
112
|
+
this.send({ op: 'exec', id: execId, code });
|
|
113
|
+
});
|
|
114
|
+
this.armIdleTimer();
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Abort a running script (turn cancelled, agent reset/stopped). The
|
|
119
|
+
* interpreter is reclaimed: after an abort its state is suspect (a script
|
|
120
|
+
* died midway), and the next exec gets a fresh one.
|
|
121
|
+
*/
|
|
122
|
+
abort(reason) {
|
|
123
|
+
if (!this.pending)
|
|
124
|
+
return;
|
|
125
|
+
this.send({ op: 'cancel', id: this.pending.id, reason });
|
|
126
|
+
this.settlePending({
|
|
127
|
+
stdout: '',
|
|
128
|
+
stderr: `script aborted by host: ${reason}`,
|
|
129
|
+
returnCode: 1,
|
|
130
|
+
aborted: true,
|
|
131
|
+
});
|
|
132
|
+
this.reclaim(`abort: ${reason}`);
|
|
133
|
+
}
|
|
134
|
+
dispose() {
|
|
135
|
+
if (this.disposed)
|
|
136
|
+
return;
|
|
137
|
+
this.disposed = true;
|
|
138
|
+
if (this.pending) {
|
|
139
|
+
this.settlePending({
|
|
140
|
+
stdout: '',
|
|
141
|
+
stderr: 'code_execution runner disposed',
|
|
142
|
+
returnCode: 1,
|
|
143
|
+
aborted: true,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
this.reclaim('dispose');
|
|
147
|
+
}
|
|
148
|
+
// -------------------------------------------------------------------------
|
|
149
|
+
async ensureChild() {
|
|
150
|
+
if (this.child && this.child.exitCode === null && !this.child.killed) {
|
|
151
|
+
return this.childReady ?? Promise.resolve();
|
|
152
|
+
}
|
|
153
|
+
this.teardownChild();
|
|
154
|
+
this.runtimeDir = mkdtempSync(join(tmpdir(), 'af-pytc-'));
|
|
155
|
+
const runtimePath = join(this.runtimeDir, 'runtime.py');
|
|
156
|
+
writeFileSync(runtimePath, PYTHON_RUNTIME_SOURCE, 'utf8');
|
|
157
|
+
const child = spawn(this.pythonPath, [runtimePath], {
|
|
158
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
159
|
+
env: { ...process.env, PYTHONUNBUFFERED: '1' },
|
|
160
|
+
});
|
|
161
|
+
this.child = child;
|
|
162
|
+
child.stderr.setEncoding('utf8');
|
|
163
|
+
child.stderr.on('data', (chunk) => {
|
|
164
|
+
// Outside an exec, python stderr is runtime diagnostics (crash
|
|
165
|
+
// tracebacks etc.) — surface them in host logs.
|
|
166
|
+
console.error(`[pytc:${this.label}] ${chunk.trimEnd()}`);
|
|
167
|
+
});
|
|
168
|
+
child.on('error', (err) => {
|
|
169
|
+
// Spawn failure lands here (e.g. python3 missing); readiness rejects.
|
|
170
|
+
console.error(`[pytc:${this.label}] python process error: ${err.message}`);
|
|
171
|
+
});
|
|
172
|
+
child.on('exit', (exitCode, signal) => {
|
|
173
|
+
if (this.pending) {
|
|
174
|
+
this.settlePending({
|
|
175
|
+
stdout: '',
|
|
176
|
+
stderr: `python runtime exited unexpectedly (code=${exitCode}, signal=${signal ?? 'none'})`,
|
|
177
|
+
returnCode: 1,
|
|
178
|
+
aborted: true,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
if (this.child === child) {
|
|
182
|
+
this.teardownChild();
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
this.reader = createInterface({ input: child.stdout });
|
|
186
|
+
this.reader.on('line', (line) => this.handleLine(line));
|
|
187
|
+
this.childReady = new Promise((resolve, reject) => {
|
|
188
|
+
const onReady = () => {
|
|
189
|
+
cleanup();
|
|
190
|
+
resolve();
|
|
191
|
+
};
|
|
192
|
+
const onExit = () => {
|
|
193
|
+
cleanup();
|
|
194
|
+
reject(new Error('python runtime exited before becoming ready'));
|
|
195
|
+
};
|
|
196
|
+
const onError = (err) => {
|
|
197
|
+
cleanup();
|
|
198
|
+
reject(err);
|
|
199
|
+
};
|
|
200
|
+
const timeout = setTimeout(() => {
|
|
201
|
+
cleanup();
|
|
202
|
+
reject(new Error('python runtime did not become ready within 15s'));
|
|
203
|
+
}, 15_000);
|
|
204
|
+
const cleanup = () => {
|
|
205
|
+
clearTimeout(timeout);
|
|
206
|
+
this.readyResolver = null;
|
|
207
|
+
child.off('exit', onExit);
|
|
208
|
+
child.off('error', onError);
|
|
209
|
+
};
|
|
210
|
+
this.readyResolver = onReady;
|
|
211
|
+
child.on('exit', onExit);
|
|
212
|
+
child.on('error', onError);
|
|
213
|
+
});
|
|
214
|
+
return this.childReady;
|
|
215
|
+
}
|
|
216
|
+
readyResolver = null;
|
|
217
|
+
handleLine(line) {
|
|
218
|
+
let msg;
|
|
219
|
+
try {
|
|
220
|
+
msg = JSON.parse(line);
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
console.error(`[pytc:${this.label}] non-protocol stdout line: ${line.slice(0, 200)}`);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
switch (msg.op) {
|
|
227
|
+
case 'ready':
|
|
228
|
+
this.readyResolver?.();
|
|
229
|
+
return;
|
|
230
|
+
case 'tool_call': {
|
|
231
|
+
const callId = msg.id;
|
|
232
|
+
const toolName = msg.name;
|
|
233
|
+
if (!callId || !toolName)
|
|
234
|
+
return;
|
|
235
|
+
const args = msg.args && typeof msg.args === 'object' && !Array.isArray(msg.args)
|
|
236
|
+
? msg.args
|
|
237
|
+
: {};
|
|
238
|
+
this.onToolCall(toolName, args)
|
|
239
|
+
.catch((err) => `Error: ${err instanceof Error ? err.message : String(err)}`)
|
|
240
|
+
.then((result) => {
|
|
241
|
+
this.send({ op: 'tool_result', id: callId, result });
|
|
242
|
+
});
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
case 'exec_result': {
|
|
246
|
+
if (this.pending && msg.id === this.pending.id) {
|
|
247
|
+
this.settlePending({
|
|
248
|
+
stdout: msg.stdout ?? '',
|
|
249
|
+
stderr: msg.stderr ?? '',
|
|
250
|
+
returnCode: typeof msg.return_code === 'number' ? msg.return_code : 1,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
default:
|
|
256
|
+
console.error(`[pytc:${this.label}] unknown protocol op: ${String(msg.op)}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
settlePending(result) {
|
|
260
|
+
const pending = this.pending;
|
|
261
|
+
if (!pending || pending.settled)
|
|
262
|
+
return;
|
|
263
|
+
pending.settled = true;
|
|
264
|
+
if (pending.deadlineTimer)
|
|
265
|
+
clearTimeout(pending.deadlineTimer);
|
|
266
|
+
if (pending.killTimer)
|
|
267
|
+
clearTimeout(pending.killTimer);
|
|
268
|
+
this.pending = null;
|
|
269
|
+
pending.resolve(result);
|
|
270
|
+
}
|
|
271
|
+
send(obj) {
|
|
272
|
+
const child = this.child;
|
|
273
|
+
if (!child || child.exitCode !== null || child.killed)
|
|
274
|
+
return;
|
|
275
|
+
try {
|
|
276
|
+
child.stdin.write(JSON.stringify(obj) + '\n');
|
|
277
|
+
}
|
|
278
|
+
catch (err) {
|
|
279
|
+
console.error(`[pytc:${this.label}] protocol write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/** Kill interpreter + clean temp dir. State is lost (by design). */
|
|
283
|
+
reclaim(reason) {
|
|
284
|
+
if (this.child) {
|
|
285
|
+
console.error(`[pytc:${this.label}] reclaiming python interpreter (${reason})`);
|
|
286
|
+
}
|
|
287
|
+
this.teardownChild();
|
|
288
|
+
this.clearIdleTimer();
|
|
289
|
+
}
|
|
290
|
+
teardownChild() {
|
|
291
|
+
if (this.reader) {
|
|
292
|
+
this.reader.close();
|
|
293
|
+
this.reader = null;
|
|
294
|
+
}
|
|
295
|
+
if (this.child) {
|
|
296
|
+
const child = this.child;
|
|
297
|
+
this.child = null;
|
|
298
|
+
this.childReady = null;
|
|
299
|
+
this.readyResolver = null;
|
|
300
|
+
child.removeAllListeners('exit');
|
|
301
|
+
try {
|
|
302
|
+
child.stdin.end();
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
// stream may already be destroyed
|
|
306
|
+
}
|
|
307
|
+
if (child.exitCode === null && !child.killed) {
|
|
308
|
+
child.kill('SIGKILL');
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (this.runtimeDir) {
|
|
312
|
+
try {
|
|
313
|
+
rmSync(this.runtimeDir, { recursive: true, force: true });
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
// temp cleanup is best-effort
|
|
317
|
+
}
|
|
318
|
+
this.runtimeDir = null;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
armIdleTimer() {
|
|
322
|
+
this.clearIdleTimer();
|
|
323
|
+
if (this.idleReclaimMs <= 0 || !this.child)
|
|
324
|
+
return;
|
|
325
|
+
this.idleTimer = setTimeout(() => {
|
|
326
|
+
if (!this.pending)
|
|
327
|
+
this.reclaim('idle');
|
|
328
|
+
}, this.idleReclaimMs);
|
|
329
|
+
// Do not hold the process open just to reclaim an idle interpreter.
|
|
330
|
+
this.idleTimer.unref?.();
|
|
331
|
+
}
|
|
332
|
+
clearIdleTimer() {
|
|
333
|
+
if (this.idleTimer) {
|
|
334
|
+
clearTimeout(this.idleTimer);
|
|
335
|
+
this.idleTimer = null;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Map framework tool names to python identifiers: `--` (the framework's
|
|
341
|
+
* prefix separator) becomes `__`, and every remaining character that is not
|
|
342
|
+
* valid in a python identifier becomes `_` (fleet reality: module and server
|
|
343
|
+
* ids contain single hyphens — `mcpl-admin`, `dog-events` — found live on
|
|
344
|
+
* the first canary run, 2026-07-26). A leading digit gets a `_` prefix.
|
|
345
|
+
*
|
|
346
|
+
* Names that sanitize to nothing, or that collide after sanitization, are
|
|
347
|
+
* skipped loudly — a skipped tool is unreachable from scripts (the
|
|
348
|
+
* exact-name tools[...] dict is built from this same list).
|
|
349
|
+
*/
|
|
350
|
+
export function buildInjectedTools(toolNames, log = (m) => console.error(m)) {
|
|
351
|
+
const seen = new Map();
|
|
352
|
+
const injected = [];
|
|
353
|
+
for (const toolName of toolNames) {
|
|
354
|
+
let pyName = toolName.replace(/--/g, '__').replace(/[^A-Za-z0-9_]/g, '_');
|
|
355
|
+
if (/^[0-9]/.test(pyName))
|
|
356
|
+
pyName = '_' + pyName;
|
|
357
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(pyName)) {
|
|
358
|
+
log(`[pytc] tool '${toolName}' skipped: cannot derive a python identifier`);
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
const existing = seen.get(pyName);
|
|
362
|
+
if (existing) {
|
|
363
|
+
log(`[pytc] tool '${toolName}' skipped: python name '${pyName}' collides with '${existing}'`);
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
seen.set(pyName, toolName);
|
|
367
|
+
injected.push({ pyName, toolName });
|
|
368
|
+
}
|
|
369
|
+
return injected;
|
|
370
|
+
}
|
|
371
|
+
//# sourceMappingURL=py-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"py-runner.js","sourceRoot":"","sources":["../../../src/code-execution/py-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,KAAK,EAAuC,MAAM,oBAAoB,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAkB,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAoCxD,MAAM,4BAA4B,GAAG,OAAO,CAAC;AAC7C,MAAM,yBAAyB,GAAG,OAAO,CAAC;AAC1C,MAAM,uBAAuB,GAAG,OAAO,CAAC;AACxC,MAAM,eAAe,GAAG,MAAM,CAAC;AAU/B,MAAM,OAAO,QAAQ;IACF,UAAU,CAAS;IACnB,iBAAiB,CAAS;IAC1B,eAAe,CAAS;IACxB,aAAa,CAAS;IACtB,UAAU,CAAwB;IAClC,KAAK,CAAS;IAEvB,KAAK,GAA0C,IAAI,CAAC;IACpD,UAAU,GAAyB,IAAI,CAAC;IACxC,MAAM,GAAqB,IAAI,CAAC;IAChC,UAAU,GAAkB,IAAI,CAAC;IACjC,OAAO,GAAuB,IAAI,CAAC;IACnC,SAAS,GAAyC,IAAI,CAAC;IACvD,WAAW,GAAG,CAAC,CAAC;IAChB,QAAQ,GAAG,KAAK,CAAC;IAEzB,YAAY,OAAwB;QAClC,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,SAAS,CAAC;QAClD,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,IAAI,4BAA4B,CAAC;QACnF,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,yBAAyB,CAAC;QAC5E,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,uBAAuB,CAAC;QACtE,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAAC;IACvC,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC;IAC/B,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,IAAI,CAAC,IAAY,EAAE,KAAqB;QAC5C,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,gCAAgC,EAAE,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAChG,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO;gBACL,MAAM,EAAE,EAAE;gBACV,MAAM,EAAE,+EAA+E;gBACvF,UAAU,EAAE,CAAC;gBACb,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,cAAc,EAAE,CAAC;QAEtB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QAC3B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;YAC7B,OAAO;gBACL,MAAM,EAAE,EAAE;gBACV,MAAM,EAAE,mCAAmC,IAAI,CAAC,UAAU,MAAM,OAAO,EAAE;gBACzE,UAAU,EAAE,CAAC;gBACb,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC;QACxC,MAAM,MAAM,GAAG,MAAM,IAAI,OAAO,CAAa,CAAC,OAAO,EAAE,EAAE;YACvD,MAAM,OAAO,GAAgB;gBAC3B,EAAE,EAAE,MAAM;gBACV,OAAO;gBACP,aAAa,EAAE,IAAI;gBACnB,SAAS,EAAE,IAAI;gBACf,OAAO,EAAE,KAAK;aACf,CAAC;YACF,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;YAEvB,OAAO,CAAC,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE;gBACtC,mEAAmE;gBACnE,gEAAgE;gBAChE,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;gBAC5D,OAAO,CAAC,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;oBAClC,IAAI,CAAC,aAAa,CAAC;wBACjB,MAAM,EAAE,EAAE;wBACV,MAAM,EAAE,iCAAiC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,YAAY;wBAC5F,UAAU,EAAE,CAAC;wBACb,OAAO,EAAE,IAAI;qBACd,CAAC,CAAC;oBACH,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;gBAChC,CAAC,EAAE,eAAe,CAAC,CAAC;YACtB,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;YAEzB,IAAI,CAAC,IAAI,CAAC;gBACR,EAAE,EAAE,MAAM;gBACV,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;gBACvE,cAAc,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;aAC1D,CAAC,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,MAAc;QAClB,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,aAAa,CAAC;YACjB,MAAM,EAAE,EAAE;YACV,MAAM,EAAE,2BAA2B,MAAM,EAAE;YAC3C,UAAU,EAAE,CAAC;YACb,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,CAAC,UAAU,MAAM,EAAE,CAAC,CAAC;IACnC,CAAC;IAED,OAAO;QACL,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC,aAAa,CAAC;gBACjB,MAAM,EAAE,EAAE;gBACV,MAAM,EAAE,gCAAgC;gBACxC,UAAU,EAAE,CAAC;gBACb,OAAO,EAAE,IAAI;aACd,CAAC,CAAC;QACL,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC1B,CAAC;IAED,4EAA4E;IAEpE,KAAK,CAAC,WAAW;QACvB,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YACrE,OAAO,IAAI,CAAC,UAAU,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAC9C,CAAC;QACD,IAAI,CAAC,aAAa,EAAE,CAAC;QAErB,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;QAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QACxD,aAAa,CAAC,WAAW,EAAE,qBAAqB,EAAE,MAAM,CAAC,CAAC;QAE1D,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,WAAW,CAAC,EAAE;YAClD,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;YAC/B,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE;SAC/C,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QAEnB,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACjC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACxC,+DAA+D;YAC/D,gDAAgD;YAChD,OAAO,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAC3D,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACxB,sEAAsE;YACtE,OAAO,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,KAAK,2BAA2B,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7E,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE;YACpC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACjB,IAAI,CAAC,aAAa,CAAC;oBACjB,MAAM,EAAE,EAAE;oBACV,MAAM,EAAE,4CAA4C,QAAQ,YAAY,MAAM,IAAI,MAAM,GAAG;oBAC3F,UAAU,EAAE,CAAC;oBACb,OAAO,EAAE,IAAI;iBACd,CAAC,CAAC;YACL,CAAC;YACD,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;gBACzB,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QACvD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QAExD,IAAI,CAAC,UAAU,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACtD,MAAM,OAAO,GAAG,GAAG,EAAE;gBACnB,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC;YACF,MAAM,MAAM,GAAG,GAAG,EAAE;gBAClB,OAAO,EAAE,CAAC;gBACV,MAAM,CAAC,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC,CAAC;YACnE,CAAC,CAAC;YACF,MAAM,OAAO,GAAG,CAAC,GAAU,EAAE,EAAE;gBAC7B,OAAO,EAAE,CAAC;gBACV,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC,CAAC;YACF,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC9B,OAAO,EAAE,CAAC;gBACV,MAAM,CAAC,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC,CAAC;YACtE,CAAC,EAAE,MAAM,CAAC,CAAC;YACX,MAAM,OAAO,GAAG,GAAG,EAAE;gBACnB,YAAY,CAAC,OAAO,CAAC,CAAC;gBACtB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;gBAC1B,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAC1B,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9B,CAAC,CAAC;YACF,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;YAC7B,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACzB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAEO,aAAa,GAAwB,IAAI,CAAC;IAE1C,UAAU,CAAC,IAAY;QAC7B,IAAI,GAAwH,CAAC;QAC7H,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,KAAK,+BAA+B,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACtF,OAAO;QACT,CAAC;QAED,QAAQ,GAAG,CAAC,EAAE,EAAE,CAAC;YACf,KAAK,OAAO;gBACV,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;gBACvB,OAAO;YAET,KAAK,WAAW,CAAC,CAAC,CAAC;gBACjB,MAAM,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC;gBACtB,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,CAAC;gBAC1B,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ;oBAAE,OAAO;gBACjC,MAAM,IAAI,GACR,GAAG,CAAC,IAAI,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;oBAClE,CAAC,CAAE,GAAG,CAAC,IAAgC;oBACvC,CAAC,CAAC,EAAE,CAAC;gBACT,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC;qBAC5B,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;qBAC5E,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;oBACf,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;gBACvD,CAAC,CAAC,CAAC;gBACL,OAAO;YACT,CAAC;YAED,KAAK,aAAa,CAAC,CAAC,CAAC;gBACnB,IAAI,IAAI,CAAC,OAAO,IAAI,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;oBAC/C,IAAI,CAAC,aAAa,CAAC;wBACjB,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE;wBACxB,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE;wBACxB,UAAU,EAAE,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;qBACtE,CAAC,CAAC;gBACL,CAAC;gBACD,OAAO;YACT,CAAC;YAED;gBACE,OAAO,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,KAAK,0BAA0B,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;IAEO,aAAa,CAAC,MAAkB;QACtC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC7B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO;YAAE,OAAO;QACxC,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;QACvB,IAAI,OAAO,CAAC,aAAa;YAAE,YAAY,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAC/D,IAAI,OAAO,CAAC,SAAS;YAAE,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACvD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC;IAEO,IAAI,CAAC,GAAY;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,MAAM;YAAE,OAAO;QAC9D,IAAI,CAAC;YACH,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;QAChD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CACX,SAAS,IAAI,CAAC,KAAK,4BAA4B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAClG,CAAC;QACJ,CAAC;IACH,CAAC;IAED,oEAAoE;IAC5D,OAAO,CAAC,MAAc;QAC5B,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,KAAK,oCAAoC,MAAM,GAAG,CAAC,CAAC;QAClF,CAAC;QACD,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IAEO,aAAa;QACnB,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACpB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACrB,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;YACzB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAClB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;YACvB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;YAC1B,KAAK,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;YACjC,IAAI,CAAC;gBACH,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;YACpB,CAAC;YAAC,MAAM,CAAC;gBACP,kCAAkC;YACpC,CAAC;YACD,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;gBAC7C,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACxB,CAAC;QACH,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,IAAI,CAAC;gBACH,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAC5D,CAAC;YAAC,MAAM,CAAC;gBACP,8BAA8B;YAChC,CAAC;YACD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;IACH,CAAC;IAEO,YAAY;QAClB,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAO;QACnD,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;YAC/B,IAAI,CAAC,IAAI,CAAC,OAAO;gBAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC1C,CAAC,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QACvB,oEAAoE;QACpE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC;IAC3B,CAAC;IAEO,cAAc;QACpB,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC7B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,CAAC;IACH,CAAC;CACF;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAChC,SAAmB,EACnB,MAAiC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IAExD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IACvC,MAAM,QAAQ,GAAmB,EAAE,CAAC;IACpC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,IAAI,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;QAC1E,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;YAAE,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC;QACjD,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7C,GAAG,CAAC,gBAAgB,QAAQ,8CAA8C,CAAC,CAAC;YAC5E,SAAS;QACX,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAClC,IAAI,QAAQ,EAAE,CAAC;YACb,GAAG,CAAC,gBAAgB,QAAQ,2BAA2B,MAAM,oBAAoB,QAAQ,GAAG,CAAC,CAAC;YAC9F,SAAS;QACX,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python runtime for client-side programmatic tool calling (PTC).
|
|
3
|
+
*
|
|
4
|
+
* This is the interpreter-side half of the `code_execution` tool: a small
|
|
5
|
+
* asyncio REPL that mimics the environment Anthropic's server-side PTC was
|
|
6
|
+
* trained on (see docs/en/agents-and-tools/tool-use/programmatic-tool-calling):
|
|
7
|
+
*
|
|
8
|
+
* - scripts run with top-level `await` (PyCF_ALLOW_TOP_LEVEL_AWAIT)
|
|
9
|
+
* - host tools are injected as async functions taking a single dict and
|
|
10
|
+
* returning a string (the tool_result text)
|
|
11
|
+
* - a pending tool call that gets no response raises TimeoutError with the
|
|
12
|
+
* same message shape the managed runtime produces
|
|
13
|
+
* - interpreter globals persist across execs (container-reuse semantics)
|
|
14
|
+
*
|
|
15
|
+
* Embedded as a TS string because tsc does not copy non-TS assets to dist/;
|
|
16
|
+
* PyRunner materializes it to a temp file at spawn.
|
|
17
|
+
*
|
|
18
|
+
* Protocol (JSON lines):
|
|
19
|
+
* host -> py : {op:"init", tools:[{py_name,tool_name}], call_timeout_s}
|
|
20
|
+
* {op:"exec", id, code}
|
|
21
|
+
* {op:"tool_result", id, result}
|
|
22
|
+
* {op:"cancel", id, reason?}
|
|
23
|
+
* py -> host : {op:"ready"}
|
|
24
|
+
* {op:"tool_call", id, exec_id, name, args}
|
|
25
|
+
* {op:"exec_result", id, stdout, stderr, return_code}
|
|
26
|
+
*
|
|
27
|
+
* IMPORTANT: the python source below must not contain backticks or the
|
|
28
|
+
* sequence dollar+brace (TS template literal syntax). String.raw preserves
|
|
29
|
+
* backslashes, so \n inside python string literals is fine.
|
|
30
|
+
*/
|
|
31
|
+
export declare const PYTHON_RUNTIME_SOURCE: string;
|
|
32
|
+
//# sourceMappingURL=runtime-py.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-py.d.ts","sourceRoot":"","sources":["../../../src/code-execution/runtime-py.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,eAAO,MAAM,qBAAqB,EAAE,MAyOnC,CAAC"}
|