🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

moejs 程序运行时 Calcium-Ion/dsh-ptc-moejs

DeepSeek Harnessv0.2.2
Calcium-Ion5 天前有提交已提供中文文档扫描:含敏感能力

DeepSeek Harness 的 PTC 运行时,在 moejs 里运行模型写的程序。moejs 是用 Go 写的…

37/100
综合分
7
GitHub 星标
551
npm 周下载
—/5
用户评分

数据截至 2026/10/27 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add dsh-ptc-moejs
npm 源 · dsh-ptc-moejs 的发布者账号已校验与本仓库一致

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · security
装得上吗
本站已在 dsh 0.2.1-rc.2 下真实安装成功
非静态推断
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 5 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查✓ 自动检查通过
展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包dsh-ptc-moejs @ 0.2.2
✓Node 引擎要求 ^22.19.0 || >=24.0.0 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/30 21:10:47

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令
查看扫描证据(共 1 处,展示前 1 条)
  • 执行外部命令来自配置/变量
    lib/launch.js:52
    child = spawn(command, args, { cwd, env: sidecarEnvironment(), stdio: [...stdio], windowsHide: true });
扫描于 2026/10/31(1 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/dsh-util-values@deepseek-ai/schemastery@deepseek-ai/cordis@deepseek-ai/dsh-fs@deepseek-ai/dsh-fs-local@deepseek-ai/dsh-ptc-runtime@deepseek-ai/dsh-sandbox@deepseek-ai/dsh-sandbox-local@deepseek-ai/dsh-sandbox-policy@deepseek-ai/dsh-session@deepseek-ai/dsh-session-projection
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

DeepSeek Harness 的 PTC 运行时,在 moejs 里运行模型程序,moejs 是用 Go 写的 JavaScript 引擎

v0.2.22026-10-26
dsh-ptc-moejs 0.2.2 修复了 Windows 上 dsh 桌面版的 run_code。此前在 read-only 和 workspace-write 文件模式下,每次运行都失败,报 invalid frame from the moejs sidecar: a frame came before the boot-ack。 · 在 Windows 上,dsh 的沙箱 runner 是一个脚本,dsh 用自己的可执行文件启动它,桌面版里这个可执行文件是 Electron。只有设置了 ELECTRON_RUN_AS_NODE,Electron 才会以 Node 模式运行这个脚本,而从 0.1.3 起,sidecar 的环境里去掉了这个变量。现在 sidecar 也保留 ELECTRON_RUN_AS_NODE,和 dsh 的 Node 运行时一样。dsh 的其他环境变量仍然会去掉,API key 也不会进到 sidecar 里。 · danger-full-access、Linux 和 macOS 不受影响。 · sidecar 程序没有变,平台包仍是 0.2.1。 感谢 @TomXPRIME 在 #1 里报告了这个问题,并在 #2 里修复了它。 升级:dsh plugin --profile add dsh-ptc-moejs@latest。新安装见 README。
v0.2.12026-10-23
dsh-ptc-moejs now gives programs timers, imports and the console output of dsh's Node runtime, and it runs dsh's workflow tool. · setTimeout, setInterval, setImmediate and their clear functions work as in Node, and node:timers/promises gives their promise forms. Timer callbacks count toward the run's memory budget and deadline, and timers still pending when the program returns never run. maxPendingTimers (default 65536) bounds how many a run keeps pending. · await import("./helper.ts") loads the .ts, .mts, .js and .mjs files of the working directory, by a relative path, an absolute path or a file: URL. The sidecar reads them under the run's sandbox, no path or symbolic link leads out of the working directory, and a file compiles once for each content it has. Files load when dsh's execution world is this host. · node:path, node:util, node:timers, node:timers/promises and node:vm are built in, and data: URLs of JavaScript import too. · dsh's workflow tool runs on this runtime. Its guest, driving seven agents that answer at once, takes about 3.4 ms here and about 125 ms on the Node runtime. · console and util.inspect render values with a port of Node 22's util.inspect, with the options dsh's Node runtime gives console: arrays grouped in columns, Promise { }, and the rest. util.inspect takes Node's options, and util.format is Node's. · A promise that rejects with no handler now fails the run with Uncaught (in promise), as an unhandled rejection ends the Node runtime's process. A tool call that a program never awaits fails the run that way when the tool fails. · The message of a failed run gives the program's own columns on its first line, and an uncaught binding error names its binding, as in ToolCallError (tools.write). · A program with three tool calls still takes about 0.4 ms, and a first run about 16 ms under bubblewrap and about 6 ms without it. · The sidecar speaks a new protocol, so the platform packages move to 0.2.1 with this version. 0.2.0 is not on npm: its
v0.1.32026-10-23
dsh-ptc-moejs 现在给 sidecar 加上了 dsh 的 Node 运行时给程序的那层限制。 · sidecar 在每个会话策略的文件沙箱里运行:Linux 上是 bubblewrap 或 Landlock,macOS 上是 Seatbelt,Windows 上是受限令牌 runner。danger-full-access 下直接启动。没有 runner 能执行受限模式时,运行以 sandbox-unavailable 失败,和 Node 运行时一样,所以在 bubblewrap 和 Landlock 都用不了的容器里要用 danger-full-access。 · dsh 的环境变量里,sidecar 只保留 PATH、PATHEXT、SYSTEMROOT、WINDIR、TEMP 和 TMP,API key 不会进到 sidecar 里。 · run_code 和在 Node 运行时上一样接受 sandbox_permissions 和 justification。程序只通过工具访问文件,所以沙箱拒绝某次工具调用时,要在那次调用上带 sandbox_permissions。 · 会话的文件模式一变,这个会话里的运行就停,每个结果都带上它所在沙箱的情况。 · 在 Linux 上,加了沙箱的 sidecar 从打开的二进制文件运行,所以插件装在哪里都能启动,包括 /tmp。它在 ps 里显示为 /proc/self/fd/3。 · 第一次运行在 bubblewrap 下约 16 ms,不加沙箱约 7 ms,每段程序的开销和 0.1.2 差不多。 · 在一次长的智能体对话里,每段程序比 Node 运行时少用 125 ms。README 里算了一天下来能省多少。 · 插件现在需要 dsh 的 fs、沙箱和沙箱策略服务,dsh 的配置档都会挂载这些服务。 · sidecar 二进制没变,平台包仍是 0.1.0。 安装:dsh plugin --profile add dsh-ptc-moejs,再按 README 里的配置档补丁启用。
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。

README

由 DeepSeek 最新模型翻译生成
dsh-ptc-moejs DeepSeek Harness 的 PTC 运行时,在 moejs 里运行模型写的程序。moejs 是用 Go 写的 JavaScript 引擎。 在 DeepSeek Harness(dsh)的 PTC 模式下,模型用一个 TypeScript 程序作答,程序里调用工具。这个运行时把每个程序放进一个全新的 moejs 运行时里跑,运行时位于 sidecar 进程内,程序之间 sidecar 一直存活。一个程序调用三次工具,在这里耗时 0.4 ms,在 dsh 的 Node 运行时上耗时 110 ms,因为后者每个程序都要启动一个 Node 进程。 程序可以等待定时器,可以从工作目录导入辅助模块,可以使用 node:path、node:util 和 node:timers。dsh 的工作流工具也在这里运行它的脚本。 程序对机器做的每件事都是一次工具调用,dsh 按该工具自己的策略、审批和沙箱执行。sidecar 本身只读取程序导入的代码文件,它获得的隔离与 dsh 的 Node 运行时给程序的一致:会话策略的文件沙箱,以及同样精简的环境变量。运行到截止时间会停止,哪怕程序在死循环里;超出内存预算也会停止。
展开完整文档(README 剩余 1 节)
dsh-ptc-moejs

DeepSeek Harness 的 PTC 运行时,在 moejs 里运行模型写的程序。moejs 是用 Go 写的 JavaScript 引擎。

在 DeepSeek Harness(dsh)的 PTC 模式下,模型用一个 TypeScript 程序作答,程序里调用工具。这个运行时把每个程序放进一个全新的 moejs 运行时里跑,运行时位于 sidecar 进程内,程序之间 sidecar 一直存活。一个程序调用三次工具,在这里耗时 0.4 ms,在 dsh 的 Node 运行时上耗时 110 ms,因为后者每个程序都要启动一个 Node 进程。

程序可以等待定时器,可以从工作目录导入辅助模块,可以使用 node:path、node:util 和 node:timers。dsh 的工作流工具也在这里运行它的脚本。

程序对机器做的每件事都是一次工具调用,dsh 按该工具自己的策略、审批和沙箱执行。sidecar 本身只读取程序导入的代码文件,它获得的隔离与 dsh 的 Node 运行时给程序的一致:会话策略的文件沙箱,以及同样精简的环境变量。运行到截止时间会停止,哪怕程序在死循环里;超出内存预算也会停止。

性能

两个运行时都通过 dsh 的 PtcRuntime 服务 API 运行同一个程序,挂载在 dsh 自己的服务上:三次工具调用,每次都由其绑定立即应答,最后返回一个对象。

| 运行时 | 每次调用 | 从加载到首个结果 |
| dsh-ptc-moejs,workspace-write | 0.42 ms | 16.4 ms |
| dsh-ptc-moejs,danger-full-access | 0.42 ms | 6.8 ms |
| dsh-ptc-runtime-node,workspace-write | 110.4 ms | 164.4 ms |
| dsh-ptc-runtime-node,danger-full-access | 103.0 ms | 149.2 ms |

一次调用指一次 resolve 加一次 run,从程序文本到结果。workspace-write 是 dsh 会话默认的文件策略,两个运行时都在 bubblewrap 里启动进程:Node 运行时每个程序一个进程,moejs 一个 sidecar 服务多个程序。danger-full-access 直接启动进程。从加载到首个结果,指从挂载插件到首次运行结束;对 moejs 来说,还包括启动 sidecar。

每一轮都在全新的 Node 进程里启动每个运行时。5 轮交替两个运行时的先后顺序,环境为 Intel Core i5-13500H、Linux 6.18、Node 24.22.1、Go 1.26.6、dsh 0.2.1-rc.2 和 moejs v0.1.0-alpha.9。表中给出各轮中位数的中位数,moejs 稳定在 0.37 到 0.46 ms,Node 稳定在 100 到 114 ms。

一段很长的智能体对话

bench/agent.mjs 在每个运行时上,于无头 dsh 中跑一轮对话。脚本化模型对五个 dsh 包的副本发起 40 次 run_code 调用,这些程序又对 dsh 的工具发起 90 次调用:glob、read、grep 和 bash。两个运行时都在同一台机器上以 workspace-write 运行。表中给出每个运行时 3 轮对话的平均值。

| | dsh-ptc-runtime-node | dsh-ptc-moejs |
| run_code 耗时 | 8.6 s | 3.6 s |
| 其中嵌套工具调用 | 3.5 s | 3.3 s |
| 对话的实际耗时 | 10.1 s | 5.0 s |
| dsh 及其子进程的 CPU 时间 | 8.4 s | 5.2 s |
| dsh 及其子进程的峰值内存(PSS) | 440 MiB | 437 MiB |
| PTC 运行时进程的峰值内存(PSS) | 34.0 MiB | 18.5 MiB |
| PTC 运行时的进程数(含 bubblewrap) | 120 | 3 |

每个程序在 moejs 上少花 125 ms,具体在 89 到 159 ms 之间,视程序而定,CPU 时间少用 82 ms。没有程序变慢。工具调用在两个运行时上耗时大致相同。差异来自 Node 运行时为每个程序在 bubblewrap 内启动的 Node 进程。两个运行时的内存峰值大致相同,其中 dsh 进程本身约占 385 MiB。Node 运行时为每个程序启动的进程达到 34 MiB,随程序退出而结束。moejs sidecar 及其 bubblewrap 进程在整个对话期间常驻,峰值 18.5 MiB。

一天的智能体工作

程序越多,节省越多。脚本化模型立即作答,真实模型每步的耗时在两个运行时上增加相同,所以节省的就是智能体等待程序的时间。表中把实测的每程序 125 ms 和 82 ms CPU 时间乘以一天的程序数。示例假设每 10 秒一个程序。

| 一天的程序数 | 例如 | 节省的等待 | 节省的 CPU 时间 |
| 200 | 半小时的智能体工作 | 25 s | 16 s |
| 1,000 | 约 3 小时 | 2.1 min | 1.4 min |
| 2,880 | 一个智能体忙 8 小时 | 6.0 min | 3.9 min |
| 11,520 | 四个这样的智能体并行 | 24 min | 16 min |

忙碌的智能体在 8 小时里有 10.3 分钟花在 Node 运行时的 run_code 上,在 moejs 上是 4.3 分钟。要估算你自己的情况,把智能体一天运行的程序数乘以 125 ms。

特性

每个副作用都是一次工具调用

程序可以调用 dsh 给它的工具,也可以写 console。要读文件、跑命令或抓取页面,它调用工具,dsh 按该工具自己的策略、审批和沙箱,通过工具管线执行这次调用,与 Node 运行时一致。dsh 的会话日志记录每次调用。sidecar 自己读取的唯一内容,是程序导入的代码文件,读取时处于本次运行策略的沙箱之下。

Node 运行时的沙箱

sidecar 与 Node 运行时的程序进程处于同样的隔离之下:
- 策略是 dsh 为本次运行解析出的那一份:会话的文件模式及其工作区根目录;没有会话的运行,则用部署的默认值。
- dsh 的沙箱服务用对应宿主平台的 runner 限制 sidecar:Linux 上是 bubblewrap 或 Landlock,macOS 上是 Seatbelt,Windows 上是受限令牌 runner。danger-full-access 启动 sidecar 时不带 runner。
- 受限模式失败即关闭。没有 runner 能限制 sidecar 时,运行以 sandbox-unavailable 失败,在 Node 运行时上也是如此。所以,bubblewrap 和 Landlock 都用不了的容器,需要 danger-full-access。
- 在 dsh 进程的环境变量中,sidecar 只保留 PATH、PATHEXT、SYSTEMROOT、WINDIR、TEMP 和 TMP、Node 运行时保留的那些变量,以及 ELECTRON_RUN_AS_NODE,好让 dsh Desktop 的 Electron 能运行 Windows 沙箱 runner。API 密钥不会进入其中。
- 会话的文件模式一旦改变,该会话在旧模式下的运行就会停止。

在 Linux 上,插件自己打开 sidecar 的二进制文件,受限的 sidecar 从打开的文件运行。因此,即使沙箱隐藏了二进制文件所在目录,sidecar 也能启动,就像 bubblewrap 在 workspace-write 下隐藏 /tmp 那样。ps 会把这样的 sidecar 列为 /proc/self/fd/3。

每种在用文件策略都有自己的 sidecar,模式和工作区根目录相同的会话共享同一个。沙箱作用于 sidecar 进程。除了读取自己导入的文件,程序本身没有文件、网络或进程访问权限。所以,工具调用能做什么由策略决定,而不是由程序决定。

插件自己启动受限命令。dsh 的子进程服务负责启动 Node 运行时的进程,在 Linux 上还会把每个进程放进 systemd scope,并置于 Node 引导程序之后,以便回收程序遗留的子进程。这会增加约 80 ms 的启动时间,而 sidecar 不启动任何子进程。sidecar 始终运行在运行 dsh 的宿主上:如果部署的执行环境在另一台宿主上,比如通过 SSH 连接,受限模式会以 sandbox-unavailable 失败。

每次运行都用全新运行时

默认策略的 sidecar 在插件加载时启动,各 sidecar 在程序之间持续运行。连续五分钟没有运行后,它会退出,下一次运行再启动它。每次运行都有自己的 moejs 运行时,所以每个程序都从干净的全局环境开始,一个 sidecar 也可以同时有多次运行在进行。

每次运行的限制

一次运行默认最多分配 512 MiB,含垃圾回收占用。截止时间、来自 dsh 的中止或配置档关闭,都会立即停止运行。控制台输出、返回值以及发往 sidecar 的每条消息各有大小上限,列在 Configuration 下。

TypeScript 与 Node 运行时一致

程序使用 TypeScript 编写,语法必须可擦除,这是 dsh 的 Node 运行时执行的规则:类型会被移除,enum、带值的命名空间和参数属性都属于语法错误。程序本身是 async 函数体,所以 await 和 return 可以在顶层使用。

定时器

setTimeout、setInterval、setImmediate 及其对应的清除函数,行为与 Node 中一致,Timeout 对象带有 ref、unref、hasRef、refresh 和 close,node:timers/promises 提供它们的 Promise 形式。程序可以在工具调用之间等待,也可以轮询或按退避策略重试:

const { setTimeout: sleep } = await import("node:timers/promises");
for (let attempt = 1; attempt <= 10; attempt++) {
const status = await tools.bash({ description: "Check the build", command: "cat build/status" });
if (status.stdout?.text.trim() === "done") return "built";
await sleep(2000);
}
return "still building";

定时器回调在程序自己的运行时里执行,因此会计入程序的内存预算和截止时间。程序返回时仍未触发的定时器永远不会执行,因为 Node 运行时的进程会随程序一起结束。

模块

程序用 await import(...) 加载模块:

- 工作目录下的 .ts、.mts、.js 和 .mjs 文件,引用时带上扩展名,可以用相对路径、绝对路径或 file: URL。被导入的模块可以用 import 声明导入其他文件,也可以使用 console 和定时器。它从程序接收工具绑定,作为参数传入。
- node:path、node:util、node:timers、node:timers/promises 和 node:vm,也可以不带 node: 前缀。
- JavaScript 的 data: URL。

const { summarize } = await import("./scripts/summarize.ts");
return await summarize(tools, "docs/");

sidecar 自己读取被导入的文件,读取发生在本次运行的沙箱内,并按内容编译一次:之后运行再导入同一个未改动的文件时,会跳过编译。工作目录之外的路径、指向目录外的符号链接、包名、JSON 文件以及其他 Node 模块都会报错,错误信息会说明原因。只有当 dsh 的执行环境就是运行 dsh 的主机时,文件才会加载。执行环境是另一台主机时(例如通过 SSH 连接的主机),内置模块仍然可以加载。

工作流

dsh 的工作流工具就运行在这个运行时上。它的 guest 从 data: URL 加载,并通过 node:vm 运行每个工作流脚本;脚本在程序自己的运行时中执行,上下文的属性就是它的全局名称。脚本调用 agent()、parallel()、pipeline()、phase() 和 log() 的方式与在 Node 运行时上相同。

单个静态二进制

sidecar 是一个自包含的 Go 二进制,用 CGO_ENABLED=0 构建。包管理器会把对应平台的二进制作为可选依赖安装。

与 Node 运行时的差异

程序可以使用 JavaScript 标准库、工具、Node 的定时器,以及五个 Node 模块:node:path、node:util、node:timers、node:timers/promises 和 node:vm。require、process、Buffer、fs、包以及其他 Node 模块都不可用。程序如果需要读取文件内容,就向 read 工具请求。如果你的程序用到其他 Node API,请为该配置档保留 dsh 的 Node 运行时。

在 Node 运行时上,程序里的 import("./helper.ts") 会相对 dsh 自己的 bootstrap 文件解析,所以那里的程序要用绝对路径导入文件。两种写法在这里都能用,相对路径会相对工作目录解析。

node:vm 在程序自己的运行时里执行脚本。脚本的 timeout 选项不会生效,而本次运行的截止时间会终止没有结束的脚本。脚本里的 var 和 function 声明留在脚本内部;给上下文里不存在的名字赋值,会在程序的运行时上创建一个全局变量,而 Node 会把两者都放到上下文上。因此,一个无限循环的工作流最终会以它 run_code 调用的 timeout 结束,而在 Node 运行时上报告的是脚本自身的超时。这类脚本的栈帧显示 node:vm,而不是脚本的文件名。

run_code 像在 Node 运行时上一样接受 sandbox_permissions 和 justification,获批的请求会在 sidecar 中以更宽的模式运行程序。除了读取它导入的文件,程序自身不做任何文件访问,所以该批准不会让它多做别的事。当文件沙箱拒绝某次工具调用时,程序会在参数中带上 sandbox_permissions 和 justification 重试该调用,你需要逐次批准。在 Node 运行时上,对 run_code 的批准也覆盖程序自己通过 Node API 做的文件操作。

快速开始

dsh plugin --profile web add dsh-ptc-moejs

把 web 换成你实际运行的配置档名称。sidecar 二进制作为可选依赖提供,覆盖 darwin-arm64、darwin-x64、linux-arm64、linux-x64 和 win32-x64,所以安装时要启用可选依赖。

安装会往配置档里加一行禁用的 ptc-moejs。要选中它,把下面这段补丁加到配置档的 cordis.patch.yml,路径是 ~/.dsh/profiles/<name>/cordis.patch.yml(或 $DSH_HOME 下的同一路径):

Replace the Node PTC runtime with moejs.
- id: ptc-runtime
disabled: true
- id: ptc-moejs
disabled: false
Turn PTC mode on.
- id: tools
config:
mode: ptc

dsh --profile web --dump-config 会打印合成后的行。在 web 和 headless 配置档里,DSH_TOOLS_MODE=ptc 也能为单次启动打开 PTC 模式,替代 tools 条目。要回到 Node 运行时,删掉前两个条目。

配置

选项写在同一个补丁的 ptc-moejs 行上:

- id: ptc-moejs
disabled: false
config:
memoryLimitMb: 1024
timeoutMs: 300000

| 选项 | 默认值 | 含义 |
| timeoutMs | 120000 | 模型未指定时,一次运行的截止时间,单位毫秒。工具调用和审批都计入其中。 |
| maxTimeoutMs | 600000 | 一次运行可以申请的最长截止时间。 |
| memoryLimitMb | 512 | 一次运行在整个生命周期内可分配的内存,单位 MiB,含垃圾回收占用的部分。超出后运行以 timeout 停止,并给出消息说明预算。 |
| maxLogBytes | 16 MiB | 一次运行保留的控制台输出字节数,按 dsh 存储的 JSON 数组计算。超出部分丢弃,日志末尾加一行标记,运行继续。 |
| maxValueBytes | 16 MiB | 返回值按 JSON 计算的字节数。值更大时运行以 output-limit 失败。错误消息会截断到这个大小。 |
| maxPendingCalls | 128 | 一次运行同时可处于进行中的工具调用数。再多一个,运行以 protocol 失败。 |
| maxPendingTimers | 65536 | 一次运行同时可挂起的定时器和 immediate 数。再多一个,程序内抛出 RangeError。 |
| maxFrameBytes | 64 MiB | dsh 与 sidecar 之间单条消息的最大字节数:一个程序、一次工具调用的参数,或一个工具结果。工具结果更大时,程序收到的是被拒绝的调用。 |

截止时间的默认值沿用 dsh 的 Node 运行时。dsh 会把超过 12,500 token 的工具结果溢出到文件,所以日志和值的预算限制的是内存占用。

程序能看到什么

程序是一个 async 函数的函数体。它的参数是 dsh 传入的绑定命名空间(PTC 模式下是 tools)、每个命名空间的错误类(ToolCallError,在 toolName 中带上工具名),以及带 log、info、warn、error 和 debug 的 console。console 也是全局变量,setTimeout、setInterval、setImmediate 及其清除函数同样如此,所以程序导入的模块可以用它们;绑定命名空间则不是全局的。其余是 JavaScript 标准库,在 moejs 中包括 TextEncoder、TextDecoder、atob、btoa、structuredClone 和 queueMicrotask。

模型写出的程序像下面这样,运行时把返回值交给 dsh:

const head = await tools.bash({ description: "Read the last commit", command: "git log -1 --oneline" });
return { head: head.stdout?.text };

返回值以无损 JSON 的形式回到 dsh:普通对象、数组、字符串、有限数字、布尔值和 null。值里含 undefined、函数、BigInt、循环引用、NaN、Infinity、-0、Date、Map 或类实例时,运行以 invalid-output 失败,消息给出第一个这类成员的路径,例如 a BigInt at $.items[3].size。不返回任何值的程序以无值完成。工具参数遵循同样的规则,参数不是无损 JSON 的调用会在 dsh 看到之前就在程序内被拒绝。

| 失败类型 | 何时发生 |
| exception | 程序或定时器回调抛出异常,promise 拒绝且无人处理,程序有语法错误,或使用了会改变运行时行为的 TypeScript 特性,比如 enum。 |
| timeout | 超过截止时间,或程序分配的内存超过 memoryLimitMb。 |
| abort | dsh 取消本次运行,配置档关闭运行时,或会话的文件模式发生变化。 |
| invalid-output | 返回值不是无损 JSON。 |
| output-limit | 返回值大于 maxValueBytes。 |
| protocol | 正在进行的工具调用超过 maxPendingCalls 个,或 sidecar 发来格式错误的消息。 |
| sandbox-unavailable | 文件模式需要沙箱,但本机上没有 runner 能约束 sidecar,或 runner 在 sidecar 运行前就失败了。 |
| worker-exit | sidecar 进程退出或无法启动。下次运行会启动新的 sidecar。 |

结果还会带上 sidecar 运行所处沙箱的信息,与 Node 运行时一致:模式,以及在受限模式下,runner 是完全执行还是部分执行该模式。

限制

- 每次调用都从全新的运行时开始。像 Node 运行时的 REPL 模式那样,在调用之间保留变量的持久模式,计划在后续版本提供。
- 内存预算统计一次运行分配的全部内存,包括垃圾。长时间运行、反复创建和丢弃大量数据时,可能在存活数据还没达到预算前就停止。
- 值的嵌套深度双向最多 10,000 层,这是 moejs 的 JSON 读写器能处理的深度。
- console 用 Node 22 的 util.inspect 移植版渲染值,选项与 dsh 的 Node 运行时给的一致:深度 4、100 项、字符串 10,000 个字符。Node 24 写对象的 symbol 键时不加括号。Node 的 util.inspect 用 showProxy 显示代理的目标,还会显示迭代器和 WeakMap 的条目,moejs 不提供这些。
- 一次运行最多导入 256 个文件,每个最大 1 MiB,总计 8 MiB。moejs 没有 import attributes,所以用不了 JSON 模块。程序是函数体,因此用 import() 导入,而 import 声明在它导入的模块里可用。
- moejs 会中断运行中的 JavaScript,而编译一旦开始就会跑到结束。解析耗时很长的程序对 dsh 来说仍会在截止时间结束,sidecar 会在解析返回后丢弃这次运行。
- promise 的回调任务跑完时仍没有处理器的拒绝会让运行失败,就像未处理的拒绝会结束 Node 运行时的进程。程序从未 await 的工具调用在工具失败时会以这种方式让运行失败,async 定时器回调里的异常也一样。消息以 Uncaught (in promise) 开头。
- 程序首行的栈帧列号,从程序所属函数的起点算起。运行失败时的消息和 console 给出的是程序自身的列号;程序自己读取的错误 stack,仍从函数起点算起。
- 每次 push,CI 都会在 Linux x64、Linux arm64、macOS arm64 和 Windows x64 上跑测试。macOS x64 未测试:它的二进制从同一份源码交叉编译而来。

开发

pnpm install
pnpm run build:sidecar # the sidecar for this machine, into npm/<package>/bin
pnpm run build # lib/
pnpm test # unit tests and the behaviour suite against the sidecar, on dsh's own services
go test ./... # the sidecar's Go tests
DSH_REPO=/path/to/deepseek-harness pnpm run e2e # headless dsh with dsh's mock LLM
DSH_REPO=/path/to/deepseek-harness pnpm run bench # the measurements above
DSH_REPO=/path/to/deepseek-harness pnpm run bench:agent # the long conversation, on Linux
DSH_REPO=/path/to/deepseek-harness pnpm run bench:features # timers, modules and dsh's workflow guest on both runtimes
DSH_REPO=/path/to/deepseek-harness pnpm run compare # the same programs on both runtimes, and where they differ
pnpm run build:binaries # all five platform binaries

构建 sidecar 需要 Go 1.25 或更高版本,并设置 CGO_ENABLED=0。测试会挂载 dsh 的 fs、sandbox 和 policy 包,其中一项测试在 runner 可用时,会在本机沙箱下运行程序。DSH_REPO 是执行过 pnpm install 和 pnpm run build 的 dsh 检出目录。e2e 测试用 dsh plugin add 把这份检出安装到临时配置档,跑一个无头任务,让 mock 模型调用 run_code,再把 Session 日志连同提供给模型的 run_code 参数,与 Node 运行时上的同一任务做对比。compare 会在这个运行时和 Node 运行时上各跑约一百个程序,打印一份 Markdown 报告,列出两者日志、取值和失败之处的差异。

许可证

MIT。每个平台包里的 sidecar 二进制还包含 moejs(Apache License 2.0)、Go 标准库(BSD 3-Clause License),以及 Node.js path 模块和 util.inspect 的移植版(MIT License)。它们的许可证文本在 THIRD_PARTY_NOTICES.md 中,随每个平台包一起发布。
预览分享卡片长什么样 ↓Calcium-Ion/dsh-ptc-moejs
分享 · DeepSeek Harness Hub
Calcium-Ion/dsh-ptc-moejs

DeepSeek Harness 的 PTC 运行时,在 moejs 里运行模型写的程序。moejs 是用 Go 写的…

回到顶部