feat(code-runtime): complete sandboxed Node execution and Session fixtures
This commit is contained in:
1 parent
f6904f4615
commit
8e19ec4962
82 files changed
+2580
-348
No files matched your search
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/experimental/code-runtime-python/README.md
|
||||
README.md: 23595845e41d77b3a204e786c0d878b044ef6d4b
|
||||
README.zh.md: a9b758089a10954abb8c4f3861eaa6816cb9ee45
|
||||
README.md: 123f53fba415a28dff0730bd5b6e5ed45debfcd7
|
||||
README.zh.md: 8de7cc3cfd10b23650285ab23469d4509fc12c4a
|
||||
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
|
||||
|
||||
## Summary
|
||||
|
||||
This private experimental package lets source-checkout compositions run model-generated Python in a fresh CPython 3.10+ subprocess for each request. Programs can use top-level `await` and `return`, call configured bindings, and write normal stdout/stderr while receiving explicit completion or failure results. Resource budgets and process-group teardown contain runaway work, but the subprocess is not a security boundary: model code has bash-equivalent trust, no state persists across runs, and no shipped profile enables this runtime.
|
||||
This private experimental package lets source-checkout compositions run model-generated Python in a fresh CPython 3.10+ subprocess for each request. Programs can use top-level `await` and `return`, call configured bindings, and write normal stdout/stderr while receiving explicit completion or failure results. Resource budgets and process-group teardown contain runaway work, but the subprocess is not a security boundary: direct Python operations have no filesystem sandbox, no state persists across runs, and no shipped profile enables this runtime.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@@ -25,7 +25,9 @@ This private experimental package lets source-checkout compositions run model-ge
|
||||
<a id="use-this-package"></a>
|
||||
## Use this package
|
||||
|
||||
Choose this private experimental package only in an explicit source-checkout composition. Register `PythonCodeRuntime` beside `dsh-tools` and `run()` executes each program in a fresh CPython 3.10+ subprocess, resolving with `result.value` on success and `result.error` on failure (the orthogonal `CodeRunFailure.kind` taxonomy classifies parse failures, thrown exceptions, invalid completions, output overflows, budget expiry, aborts, and substrate death). It rejects only for seam misuse — a malformed binding namespace, or a call after disposal. Configuration is rejected at load: a non-Unix platform; an explicit `pythonBin` that is not an executable regular file or a bare name that does not resolve on `PATH`; a non-CPython, pre-3.10, or probe-failing interpreter; a non-positive or non-integer budget; a `maxLogBytes` below the truncation-marker floor (64); a timer value `setTimeout` would clamp; a budget larger than the effective fd-3 frame cap (lowered when the host heap cannot safely parse a near-cap frame); or an `addressSpaceMb`/output-budget pair whose worst-case peak would breach `RLIMIT_AS`.
|
||||
Choose this private experimental package only in an explicit source-checkout composition. Register `PythonCodeRuntime` beside `dsh-tools`; `run(resolve(request))` executes each program in a fresh CPython 3.10+ subprocess, resolving with `result.value` on success and `result.error` on failure (the orthogonal `CodeRunFailure.kind` taxonomy classifies parse failures, thrown exceptions, invalid completions, output overflows, budget expiry, aborts, and substrate death). It rejects only for seam misuse — a malformed binding namespace, or a call after disposal. Configuration is rejected at load: a non-Unix platform; an explicit `pythonBin` that is not an executable regular file or a bare name that does not resolve on `PATH`; a non-CPython, pre-3.10, or probe-failing interpreter; a non-positive or non-integer budget; a `maxLogBytes` below the truncation-marker floor (64); a timer value `setTimeout` would clamp; a budget larger than the effective fd-3 frame cap (lowered when the host heap cannot safely parse a near-cap frame); or an `addressSpaceMb`/output-budget pair whose worst-case peak would breach `RLIMIT_AS`.
|
||||
|
||||
`resolve(request)` accepts an absolute `cwd` and uses the provider's configured `maxWallMs` deadline (600,000 ms by default). Explicit `timeoutMs` overrides and sandbox policies are unsupported and reject before execution. This provider does not advertise `sandboxMode` or return confinement facts.
|
||||
|
||||
### What you get
|
||||
|
||||
@@ -89,7 +91,7 @@ Read these when the runtime contract is not enough. They move from the seam defi
|
||||
- [Code runtime seam](../../code-runtime/code-runtime/README.md) — the abstract contract this backend implements.
|
||||
- [fd-3 protocol Agent Note](../../../.agents/notes/implemented/architecture/2026-07-31-code-runtime-python-fd3-protocol.md) — design rationale and wire contract.
|
||||
- [Settlement-fixes Agent Note](../../../.agents/notes/archived/bug-fix/2026-07-31-code-runtime-python-settlement-fixes.md) — settlement, metering, and containment fixes and their regression cases.
|
||||
- [Worker-thread backend](../../code-runtime/code-runtime-node/README.md) — the released TypeScript sibling.
|
||||
- [Node process backend](../../code-runtime/code-runtime-node/README.md) — the released TypeScript sibling.
|
||||
- [Code runtime subsystem reference](../../../docs/subsystems/code-runtime.md) — request/result vocabulary, bindings, and failure taxonomy.
|
||||
|
||||
-----
|
||||
@@ -114,7 +116,7 @@ These limits define what the package does and does not cover; they are current p
|
||||
- **A descendant that escapes the child's process group with `setsid()` is not reaped by the group teardown** — `kill(-pid)` cannot reach it; the run still settles on the value the done frame decided, and the close-deadline backstop forces settlement if the orphan holds the pipes open, but the orphan itself outlives the fiber until it exits on its own.
|
||||
- **A `log` frame that arrives after settlement is dropped** — once the run has settled, host-side capture is closed; a late fd-3 `log` frame (from a thread that outlived the done frame) is discarded rather than appended to `logs`.
|
||||
- **A binding REPLY value has no seam-level byte or depth cap** — `maxValueBytes` meters only the done frame's completion value; a wide binding reply is rebuilt host-side (`snapshotJsonValue` traversal) and encoded whole, bounded on both sides only by process memory (like a binding argument, which has no child-side budget either).
|
||||
- **No shipped profile mounts this provider** — the keyless `ptc-python-turn` snapshot replaces the headless PTC runtime through the real Loader; released profiles continue to use the worker-thread backend.
|
||||
- **No shipped profile mounts this provider** — the keyless `ptc-python-turn` snapshot replaces the headless PTC runtime through the real Loader; released profiles use the sandboxed Node process backend.
|
||||
- **Cross-channel log interleaving is backend-dependent** — Python stdout, stderr, and fd-3 log frames travel independently; each channel preserves its own order, while their total order in `result.logs` may differ.
|
||||
- **CPython 3.10 or newer is required** — the configured executable is resolved and version-probed at load; unsupported interpreters fail before `ctx.codeRuntime` is registered.
|
||||
- **The truncation-marker text and the tempdir prefix keep the pre-rename short names** — the marker `[dsh-code-runtime-python] log capture truncated at <N> bytes` and the `dsh-code-runtime-python-` tempdir prefix are byte-anchored by tests and are independent of the npm package name; promotion (dropping the `experimental-` prefix) does not rename them.
|
||||
|
||||
@@ -9,7 +9,7 @@ kind: "package-reference"
|
||||
|
||||
## 概述
|
||||
|
||||
这个私有实验包可让源码检出组合在每次请求时都用全新的 CPython 3.10+ 子进程运行模型生成的 Python。程序可以使用顶层 `await` 和 `return`、调用已配置的 binding、正常写入 stdout/stderr,并获得明确的完成或失败结果。资源预算和进程组拆卸会约束失控的工作,但子进程不是安全边界:模型代码具有与 bash 同等的信任,运行之间不保留状态,且没有已发布 profile 启用此 runtime。
|
||||
这个私有实验包可让源码检出组合在每次请求时都用全新的 CPython 3.10+ 子进程运行模型生成的 Python。程序可以使用顶层 `await` 和 `return`、调用已配置的 binding、正常写入 stdout/stderr,并获得明确的完成或失败结果。资源预算和进程组拆卸会约束失控的工作,但子进程不是安全边界:直接 Python 操作没有文件系统沙箱,运行之间不保留状态,且没有已发布 profile 启用此 runtime。
|
||||
|
||||
## 目录
|
||||
|
||||
@@ -25,7 +25,9 @@ kind: "package-reference"
|
||||
<a id="use-this-package"></a>
|
||||
## 使用本包
|
||||
|
||||
仅在显式源码检出组合中选择这个私有实验包。将 `PythonCodeRuntime` 与 `dsh-tools` 一起注册后,`run()` 会在全新的 CPython 3.10+ 子进程中执行每个程序;成功时以 `result.value` resolve,失败时以 `result.error` resolve(正交的 `CodeRunFailure.kind` 分类涵盖解析失败、抛出异常、无效完成值、输出溢出、预算到期、中止与执行基底终止)。仅有 seam 误用会 reject——binding 命名空间不合法,或在 dispose 后调用。配置在加载期拒绝:非 Unix 平台;不是可执行普通文件的显式 `pythonBin`,或无法在 `PATH` 上解析的裸名;非 CPython、低于 3.10 或探测失败的解释器;非正或非整数预算;低于截断标记下限(64)的 `maxLogBytes`;会被 `setTimeout` 截断的定时器值;超过有效 fd-3 帧上限的预算(宿主堆无法安全解析接近上限的帧时,该上限会降低);或最坏峰值会突破 `RLIMIT_AS` 的 `addressSpaceMb`/输出预算组合。
|
||||
仅在显式源码检出组合中选择这个私有实验包。将 `PythonCodeRuntime` 与 `dsh-tools` 一起注册后,`run(resolve(request))` 会在全新的 CPython 3.10+ 子进程中执行每个程序;成功时以 `result.value` resolve,失败时以 `result.error` resolve(正交的 `CodeRunFailure.kind` 分类涵盖解析失败、抛出异常、无效完成值、输出溢出、预算到期、中止与执行基底终止)。仅有 seam 误用会 reject——binding 命名空间不合法,或在 dispose 后调用。配置在加载期拒绝:非 Unix 平台;不是可执行普通文件的显式 `pythonBin`,或无法在 `PATH` 上解析的裸名;非 CPython、低于 3.10 或探测失败的解释器;非正或非整数预算;低于截断标记下限(64)的 `maxLogBytes`;会被 `setTimeout` 截断的定时器值;超过有效 fd-3 帧上限的预算(宿主堆无法安全解析接近上限的帧时,该上限会降低);或最坏峰值会突破 `RLIMIT_AS` 的 `addressSpaceMb`/输出预算组合。
|
||||
|
||||
`resolve(request)` 接受绝对 `cwd`,并使用提供方配置的 `maxWallMs` 截止时间(默认 600,000 ms)。显式 `timeoutMs` 覆盖与沙箱策略不受支持,会在执行前拒绝。本提供方不声明 `sandboxMode`,也不返回约束事实。
|
||||
|
||||
### 你得到什么
|
||||
|
||||
@@ -89,7 +91,7 @@ kind: "package-reference"
|
||||
- [Code runtime seam](../../code-runtime/code-runtime/README.zh.md) — 本后端实现的抽象契约。
|
||||
- [fd-3 协议 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-31-code-runtime-python-fd3-protocol.zh.md) — 设计理由与 wire 契约。
|
||||
- [结算修复 Agent Note](../../../.agents/notes/archived/bug-fix/2026-07-31-code-runtime-python-settlement-fixes.md) — 结算、计量与隔离修复及其回归用例。
|
||||
- [Worker 线程后端](../../code-runtime/code-runtime-node/README.zh.md) — 已发布的 TypeScript 兄弟。
|
||||
- [Node 进程后端](../../code-runtime/code-runtime-node/README.zh.md) — 已发布的 TypeScript 兄弟。
|
||||
- [Code runtime 子系统参考](../../../docs/subsystems/code-runtime.zh.md) — 请求/结果词汇、binding 与失败分类。
|
||||
|
||||
-----
|
||||
@@ -114,12 +116,11 @@ kind: "package-reference"
|
||||
- **以 `setsid()` 逃出子进程组后代不被组拆卸回收**——`kill(-pid)` 够不到它;运行仍按 done 帧决定的值结算,若该孤儿持有管道,close 截止兜底会强制结算,但孤儿本身在自行退出前一直存活到 fiber 之外。
|
||||
- **结算后到达的 `log` 帧被丢弃**——运行一旦结算,宿主侧捕获即关闭;迟到的 fd-3 `log` 帧(来自比 done 帧存活更久的线程)会被丢弃,而不是追加到 `logs`。
|
||||
- **binding 回复值没有 seam 级字节或深度上限**——`maxValueBytes` 只计量 done 帧的完成值;宽 binding 回复在宿主侧重建(`snapshotJsonValue` 遍历)并整帧编码,两侧都只受进程内存约束(与没有子进程侧预算的 binding 实参一样)。
|
||||
- **已发布 profile 均不挂载本提供方**——keyless `ptc-python-turn` 快照通过真实 Loader 替换 headless PTC 运行时;已发布 profile 继续使用 Worker 线程后端。
|
||||
- **已发布 profile 均不挂载本提供方**——keyless `ptc-python-turn` 快照通过真实 Loader 替换 headless PTC 运行时;已发布 profile 使用沙箱 Node 进程后端。
|
||||
- **跨通道日志交错由后端决定**——Python stdout、stderr 与 fd-3 日志帧彼此独立传输;每个通道保留自身顺序,但它们在 `result.logs` 中的总顺序可能不同。
|
||||
- **需要 CPython 3.10 或更高版本**——配置的可执行文件会在加载期完成解析与版本探测;不受支持的解释器会在 `ctx.codeRuntime` 注册前失败。
|
||||
- **截断标记文本与临时目录前缀保留改名前的短名**——标记 `[dsh-code-runtime-python] log capture truncated at <N> bytes` 与 `dsh-code-runtime-python-` 临时目录前缀被测试逐字节锚定,且独立于 npm 包名;promotion(去掉 `experimental-` 前缀)不会重命名它们。
|
||||
- **`run()` 是一次性的**——`logs` 只有在 `CodeRunResult` resolve 后才能获得;没有为运行中程序产生的输出提供流式日志或进度接口。
|
||||
|
||||
- **运行之间不保留状态**——每次请求都在全新子进程中执行;持久 REPL 风格内核在某个后端带来自己的日志方案之前保持延期。
|
||||
- **原始长度超过有效帧解析上限的 fd-3 帧会让本次运行以 worker-exit 结算**——上限为 64 MiB,或当宿主的配置堆无法安全解析接近上限的帧时更低(`hostFrameParseCeiling`);`maxLogBytes`/`maxValueBytes` 在加载期被限制到同一上限,因此诚实子进程的帧总能放得下;模型构造的超过该上限的 binding 实参(一个在 seam 层没有预算的值)会触发同一上限——这是该 OOM 防护的已接受残余。
|
||||
- **停止读取回复的子进程会在回复积压超过 1024 帧时以 worker-exit 结算运行**——宿主每次写一条回复,管道满时等待 `drain`;只持续发送调用而不消费回复的子进程会让保留的积压(及其钉住的 binding 结果)一直增长到墙钟,因此积压上限让运行提前失败。binding 结果在 seam 层没有字节上限,所以这是计数上限而非字节上限。
|
||||
|
||||
Reference in new issue
Block a user