dsh-ptc-moejs 0.0.0-stage → 0.1.1
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 +159 -2
- package/README.zh_CN.md +160 -0
- package/cordis.patch.yml +9 -0
- package/lib/binary.d.ts +15 -0
- package/lib/binary.js +28 -0
- package/lib/index.d.ts +78 -0
- package/lib/index.js +189 -0
- package/lib/protocol.d.ts +89 -0
- package/lib/protocol.js +180 -0
- package/lib/sidecar.d.ts +60 -0
- package/lib/sidecar.js +282 -0
- package/package.json +73 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Calcium-Ion
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,160 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<img src="https://raw.githubusercontent.com/Calcium-Ion/moejs/main/docs/assets/moejs-logo.svg" width="160" alt="moejs">
|
|
4
|
+
|
|
5
|
+
# dsh-ptc-moejs
|
|
6
|
+
|
|
7
|
+
A PTC runtime for the DeepSeek Harness that runs the model's programs in moejs, a JavaScript engine written in Go.
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="./README.zh_CN.md">简体中文</a> |
|
|
11
|
+
<strong>English</strong>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
In the PTC mode of the [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (dsh), the model answers with a TypeScript program that calls tools. This runtime runs each program in a fresh [moejs](https://github.com/Calcium-Ion/moejs) runtime inside one sidecar process that stays up between programs. A program with three tool calls takes 0.18 ms here, and 95 ms on dsh's Node runtime, which starts a Node process for every program.
|
|
17
|
+
|
|
18
|
+
Everything a program does to the machine is a tool call, which dsh runs with that tool's own policy, approvals and sandbox. A run stops at its deadline, even in an endless loop, and when it goes past its memory budget. The sidecar is a static Go binary that dsh starts as a plain child process, so the runtime also works in containers where bubblewrap cannot run.
|
|
19
|
+
|
|
20
|
+
## Performance
|
|
21
|
+
|
|
22
|
+
Both runtimes ran the same program through dsh's `PtcRuntime` service API: three tool calls, each answered at once by its binding, and a returned object.
|
|
23
|
+
|
|
24
|
+
| Runtime | Per call | Load to first result |
|
|
25
|
+
|---|--:|--:|
|
|
26
|
+
| dsh-ptc-moejs | 0.18 ms | 5.8 ms |
|
|
27
|
+
| dsh-ptc-runtime-node, workspace-write | 94.8 ms | 131.7 ms |
|
|
28
|
+
| dsh-ptc-runtime-node, danger-full-access | 91.2 ms | 135.0 ms |
|
|
29
|
+
|
|
30
|
+
A call is one `resolve` and `run`, from the program text to the result. Load to first result runs from mounting the plugin to the end of its first run, and for moejs it includes starting the sidecar. Workspace-write is the default file policy of a dsh Session, under which the Node runtime starts each program inside bubblewrap. Danger-full-access starts it directly.
|
|
31
|
+
|
|
32
|
+
Every round started each runtime in a fresh Node process. The 5 rounds alternated the order of the runtimes on 8 cores of an Intel Core i5-13500H, with Linux 6.18, Node 24.22.1, Go 1.26.6, dsh 0.2.1-rc.2 and moejs v0.1.0-alpha.9. The table gives the median of the round medians, which stayed within 0.16 to 0.20 ms for moejs and within 90 to 96 ms for Node.
|
|
33
|
+
|
|
34
|
+
## Features
|
|
35
|
+
|
|
36
|
+
### Every effect is a tool call
|
|
37
|
+
|
|
38
|
+
A program can call the tools dsh gives it and write to `console`. To read a file, run a command or fetch a page, it calls a tool, and dsh runs that call through its tool pipeline with the tool's own policy, approvals and sandbox, as it does for the Node runtime. dsh's session log records each call.
|
|
39
|
+
|
|
40
|
+
### A fresh runtime for every run
|
|
41
|
+
|
|
42
|
+
The sidecar starts when the plugin loads and keeps running. Each run gets its own moejs runtime, so every program starts from clean globals, and one sidecar can have several runs in flight at once.
|
|
43
|
+
|
|
44
|
+
### Limits on every run
|
|
45
|
+
|
|
46
|
+
A run may allocate 512 MiB by default, garbage included. A deadline, an abort from dsh or a profile shutdown stops a run at once. Console output, the returned value and each message to the sidecar have their own size caps, listed under [Configuration](#configuration).
|
|
47
|
+
|
|
48
|
+
### TypeScript as on the Node runtime
|
|
49
|
+
|
|
50
|
+
Programs are TypeScript with erasable syntax, the rule dsh's Node runtime applies: the types are removed, and `enum`, namespaces with values and parameter properties are syntax errors. A program is the body of an async function, so `await` and `return` work at its top level.
|
|
51
|
+
|
|
52
|
+
### One static binary
|
|
53
|
+
|
|
54
|
+
The sidecar is a self-contained Go binary built with `CGO_ENABLED=0`. dsh starts it as a plain child process, and starting that process is all the runtime asks of the host. The package manager installs the binary for your platform as an optional dependency.
|
|
55
|
+
|
|
56
|
+
## Differences from the Node runtime
|
|
57
|
+
|
|
58
|
+
Programs get the JavaScript standard library and the tools. `require`, `import`, `process`, `Buffer`, `fs` and `setTimeout` are undefined in a program, and a program that needs a file asks the `read` tool for it. If your programs use Node APIs, keep dsh's Node runtime for that profile.
|
|
59
|
+
|
|
60
|
+
Every file access of a program happens inside a tool call, and dsh rejects a `run_code` call that carries `sandbox_permissions`. When the file sandbox denies a tool call, the program retries that call with `sandbox_permissions` and `justification` among its arguments, and you approve each such call. On the Node runtime, one approval can also cover the file operations a program does itself through Node APIs.
|
|
61
|
+
|
|
62
|
+
## Quick start
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
dsh plugin --profile web add dsh-ptc-moejs
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Use the name of the profile you run in place of `web`. The sidecar binary comes as an optional dependency for darwin-arm64, darwin-x64, linux-arm64, linux-x64 and win32-x64, so install with optional dependencies enabled.
|
|
69
|
+
|
|
70
|
+
Installing adds a disabled `ptc-moejs` row to the profile. To select it, add this patch to the profile's `cordis.patch.yml`, which is `~/.dsh/profiles/<name>/cordis.patch.yml` (or the same path under `$DSH_HOME`):
|
|
71
|
+
|
|
72
|
+
```yaml
|
|
73
|
+
# Replace the Node PTC runtime with moejs.
|
|
74
|
+
- id: ptc-runtime
|
|
75
|
+
disabled: true
|
|
76
|
+
- id: ptc-moejs
|
|
77
|
+
disabled: false
|
|
78
|
+
# Turn PTC mode on.
|
|
79
|
+
- id: tools
|
|
80
|
+
config:
|
|
81
|
+
mode: ptc
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`dsh --profile web --dump-config` prints the composed rows. In the web and headless profiles, `DSH_TOOLS_MODE=ptc` also turns PTC mode on for one launch, in place of the `tools` entry. To go back to the Node runtime, delete the first two entries.
|
|
85
|
+
|
|
86
|
+
## Configuration
|
|
87
|
+
|
|
88
|
+
Options go on the `ptc-moejs` row of the same patch:
|
|
89
|
+
|
|
90
|
+
```yaml
|
|
91
|
+
- id: ptc-moejs
|
|
92
|
+
disabled: false
|
|
93
|
+
config:
|
|
94
|
+
memoryLimitMb: 1024
|
|
95
|
+
timeoutMs: 300000
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
| Option | Default | Meaning |
|
|
99
|
+
|---|--:|---|
|
|
100
|
+
| `timeoutMs` | 120000 | Deadline of a run in milliseconds when the model asks for none. Tool calls and approvals count toward it. |
|
|
101
|
+
| `maxTimeoutMs` | 600000 | The longest deadline a run may ask for. |
|
|
102
|
+
| `memoryLimitMb` | 512 | MiB a run may allocate over its whole life, garbage included. Past it the run stops with `timeout` and a message that names the budget. |
|
|
103
|
+
| `maxLogBytes` | 16 MiB | Bytes of console output a run keeps, counted as the JSON array dsh stores. Output past it is dropped, a marker line ends the logs, and the run goes on. |
|
|
104
|
+
| `maxValueBytes` | 16 MiB | Bytes of the returned value as JSON. A larger value fails the run with `output-limit`. Error messages are cut to this size. |
|
|
105
|
+
| `maxPendingCalls` | 128 | Tool calls a run may have in flight at once. One more fails the run with `protocol`. |
|
|
106
|
+
| `maxFrameBytes` | 64 MiB | The largest message between dsh and the sidecar: a program, the arguments of a tool call or one tool result. A larger tool result reaches the program as a rejected call. |
|
|
107
|
+
|
|
108
|
+
The deadline defaults follow dsh's Node runtime. dsh spills tool results longer than 12,500 tokens to a file, so the log and value budgets are limits on memory use.
|
|
109
|
+
|
|
110
|
+
## What a program sees
|
|
111
|
+
|
|
112
|
+
A program is the body of an async function. Its parameters are the binding namespaces dsh passes (`tools` in PTC mode), the error class of each namespace (`ToolCallError`, which carries the tool's name in `toolName`) and `console` with `log`, `info`, `warn`, `error` and `debug`. The rest is the JavaScript standard library, which in moejs includes `TextEncoder`, `TextDecoder`, `atob`, `btoa`, `structuredClone` and `queueMicrotask`.
|
|
113
|
+
|
|
114
|
+
The model writes a program like this one, and the runtime hands the returned value to dsh:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
const head = await tools.bash({ description: "Read the last commit", command: "git log -1 --oneline" });
|
|
118
|
+
return { head: head.stdout?.text };
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The returned value goes back to dsh as lossless JSON: plain objects, arrays, strings, finite numbers, booleans and `null`. A value that holds `undefined`, a function, a `BigInt`, a cycle, `NaN`, `Infinity`, `-0`, a `Date`, a `Map` or a class instance fails the run with `invalid-output`, and the message gives the path of the first such member, for example `a BigInt at $.items[3].size`. A program that returns nothing completes with no value. Tool arguments follow the same rule, and a call with arguments that are not lossless JSON rejects inside the program before dsh sees it.
|
|
122
|
+
|
|
123
|
+
| Failure kind | When |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `exception` | The program throws, has a syntax error, or uses TypeScript that changes run-time behaviour, such as `enum`. |
|
|
126
|
+
| `timeout` | The deadline passes, or the program allocates more than `memoryLimitMb`. |
|
|
127
|
+
| `abort` | dsh cancels the run, or the profile shuts the runtime down. |
|
|
128
|
+
| `invalid-output` | The returned value is not lossless JSON. |
|
|
129
|
+
| `output-limit` | The returned value is larger than `maxValueBytes`. |
|
|
130
|
+
| `protocol` | More than `maxPendingCalls` tool calls are in flight, or the sidecar sent a malformed message. |
|
|
131
|
+
| `worker-exit` | The sidecar process died. The next run starts a new one. |
|
|
132
|
+
|
|
133
|
+
## Limitations
|
|
134
|
+
|
|
135
|
+
- Every call starts with a fresh runtime. A persistent mode that keeps variables between calls, like the Node runtime's REPL mode, is planned for a later version.
|
|
136
|
+
- The memory budget counts what a run allocates, garbage included. A long run that builds and drops a lot of data can stop before its live data reaches the budget.
|
|
137
|
+
- Values nest up to 10,000 levels deep in both directions, the depth moejs's JSON reader and writer handle.
|
|
138
|
+
- `console` output imitates Node's `util.inspect` for strings, numbers, plain objects, arrays, `Map`, `Set`, errors and class instances, and breaks a line at 72 characters.
|
|
139
|
+
- moejs interrupts running JavaScript, and a compile runs to its end once it starts. A program that takes long to parse still ends at its deadline for dsh, and the sidecar drops the run once the parse returns.
|
|
140
|
+
- If a tool call that the program never awaits fails, the failure stays in that unread promise.
|
|
141
|
+
- This version has been tested on Linux x64. The other four binaries are cross-compiled from the same source.
|
|
142
|
+
|
|
143
|
+
## Development
|
|
144
|
+
|
|
145
|
+
```sh
|
|
146
|
+
pnpm install
|
|
147
|
+
pnpm run build:sidecar # the sidecar for this machine, into npm/<package>/bin
|
|
148
|
+
pnpm run build # lib/
|
|
149
|
+
pnpm test # unit tests and the behaviour suite against the sidecar
|
|
150
|
+
go test ./... # the sidecar's Go tests
|
|
151
|
+
DSH_REPO=/path/to/deepseek-harness pnpm run e2e # headless dsh with dsh's mock LLM
|
|
152
|
+
DSH_REPO=/path/to/deepseek-harness pnpm run bench # the measurements above
|
|
153
|
+
pnpm run build:binaries # all five platform binaries
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Building the sidecar takes Go 1.25 or later, with `CGO_ENABLED=0`. `DSH_REPO` is a dsh checkout after `pnpm install` and `pnpm run build`. The e2e test installs this checkout into a temporary profile with `dsh plugin add`, runs one headless task in which the mock model calls `run_code`, and compares the Session log with the same task on the Node runtime.
|
|
157
|
+
|
|
158
|
+
## License
|
|
159
|
+
|
|
160
|
+
MIT. The sidecar binary in each platform package also contains moejs (Apache License 2.0) and the Go standard library (BSD 3-Clause License). Their license texts are in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md), which ships with each platform package.
|
package/README.zh_CN.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="https://raw.githubusercontent.com/Calcium-Ion/moejs/main/docs/assets/moejs-logo.svg" width="160" alt="moejs">
|
|
4
|
+
|
|
5
|
+
# dsh-ptc-moejs
|
|
6
|
+
|
|
7
|
+
DeepSeek Harness 的 PTC 运行时,用 Go 写的 JavaScript 引擎 moejs 运行模型写的程序。
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<strong>简体中文</strong> |
|
|
11
|
+
<a href="./README.md">English</a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(dsh)的 PTC 模式下,模型给出一段调用工具的 TypeScript 程序。这个运行时让一个 sidecar 进程常驻,每段程序在里面拿到一个新的 [moejs](https://github.com/Calcium-Ion/moejs) 运行时。调用三次工具的程序在这里用时 0.18 ms。dsh 的 Node 运行时每段程序都要启动一个 Node 进程,同样的程序用时 95 ms。
|
|
17
|
+
|
|
18
|
+
程序对机器做的每件事都是一次工具调用,dsh 按该工具自己的策略、审批和沙箱执行这次调用。运行到了截止时间就会停下,死循环里也一样,分配的内存超过预算时也会停下。sidecar 是静态链接的 Go 程序,dsh 把它当作普通子进程启动,所以在无法运行 bubblewrap 的容器里也能用。
|
|
19
|
+
|
|
20
|
+
## 性能
|
|
21
|
+
|
|
22
|
+
两个运行时通过 dsh 的 `PtcRuntime` 服务接口运行同一段程序:三次工具调用,每次由绑定函数立即返回,最后返回一个对象。
|
|
23
|
+
|
|
24
|
+
| 运行时 | 每次调用 | 从加载到第一个结果 |
|
|
25
|
+
|---|--:|--:|
|
|
26
|
+
| dsh-ptc-moejs | 0.18 ms | 5.8 ms |
|
|
27
|
+
| dsh-ptc-runtime-node,workspace-write | 94.8 ms | 131.7 ms |
|
|
28
|
+
| dsh-ptc-runtime-node,danger-full-access | 91.2 ms | 135.0 ms |
|
|
29
|
+
|
|
30
|
+
一次调用是一次 `resolve` 加一次 `run`,从程序文本到结果。从加载到第一个结果是从挂载插件到第一次运行结束,moejs 的这段时间包含启动 sidecar。workspace-write 是 dsh 会话的默认文件策略,这时 Node 运行时在 bubblewrap 里启动每段程序。danger-full-access 下直接启动。
|
|
31
|
+
|
|
32
|
+
每一轮里,每个运行时都在新的 Node 进程中启动。5 轮交替顺序运行,绑定在 Intel Core i5-13500H 的 8 个核上,环境是 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.16 到 0.20 ms 之间,Node 在 90 到 96 ms 之间。
|
|
33
|
+
|
|
34
|
+
## 功能
|
|
35
|
+
|
|
36
|
+
### 对机器的每次操作都是工具调用
|
|
37
|
+
|
|
38
|
+
程序能调用 dsh 提供的工具,能向 `console` 输出。读文件、执行命令、抓取网页都要调用工具,dsh 用工具流水线执行这次调用,由该工具自己的策略、审批和沙箱处理,和 Node 运行时一样。dsh 的会话日志记录每一次调用。
|
|
39
|
+
|
|
40
|
+
### 每次运行一个新运行时
|
|
41
|
+
|
|
42
|
+
sidecar 在插件加载时启动,之后一直运行。每次运行拿到自己的 moejs 运行时,每段程序都从干净的全局环境开始,同一个 sidecar 里可以同时进行多次运行。
|
|
43
|
+
|
|
44
|
+
### 每次运行都有上限
|
|
45
|
+
|
|
46
|
+
一次运行默认可以分配 512 MiB,垃圾也算在内。截止时间到了、dsh 中止或者 profile 关闭时,运行立即停止。console 输出、返回值和发给 sidecar 的每条消息各有大小上限,见[配置](#配置)。
|
|
47
|
+
|
|
48
|
+
### 和 Node 运行时一样的 TypeScript
|
|
49
|
+
|
|
50
|
+
程序是只用可擦除语法的 TypeScript,规则和 dsh 的 Node 运行时相同:类型标注被删掉,`enum`、带值的 namespace 和参数属性都是语法错误。程序是一个 async 函数的函数体,顶层可以用 `await` 和 `return`。
|
|
51
|
+
|
|
52
|
+
### 一个静态程序
|
|
53
|
+
|
|
54
|
+
sidecar 是用 `CGO_ENABLED=0` 构建的自包含 Go 程序。dsh 把它当作普通子进程启动,运行时对宿主机的要求就是能启动这个进程。包管理器以可选依赖的形式安装对应平台的程序。
|
|
55
|
+
|
|
56
|
+
## 和 Node 运行时的区别
|
|
57
|
+
|
|
58
|
+
程序能用 JavaScript 标准库和工具。`require`、`import`、`process`、`Buffer`、`fs` 和 `setTimeout` 在程序里都是 undefined,程序要读文件时调用 `read` 工具。如果你的程序要用 Node API,这个 profile 继续用 dsh 的 Node 运行时。
|
|
59
|
+
|
|
60
|
+
程序的每次文件访问都发生在工具调用里,带 `sandbox_permissions` 的 `run_code` 调用会被 dsh 拒绝。文件沙箱拒绝某次工具调用时,程序在这次调用的参数里带上 `sandbox_permissions` 和 `justification` 重试,每次这样的调用都要你批准。用 Node 运行时的话,程序通过 Node API 自己做的文件操作还能用一次批准全部放行。
|
|
61
|
+
|
|
62
|
+
## 快速上手
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
dsh plugin --profile web add dsh-ptc-moejs
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
把 `web` 换成你使用的 profile 名。sidecar 程序以可选依赖的形式提供,覆盖 darwin-arm64、darwin-x64、linux-arm64、linux-x64 和 win32-x64,安装时需要启用可选依赖。
|
|
69
|
+
|
|
70
|
+
安装后 profile 里多出一行处于禁用状态的 `ptc-moejs`。要启用它,把下面的补丁加进 profile 的 `cordis.patch.yml`,路径是 `~/.dsh/profiles/<name>/cordis.patch.yml`(设置了 `$DSH_HOME` 时在它下面的同一路径):
|
|
71
|
+
|
|
72
|
+
```yaml
|
|
73
|
+
# 用 moejs 替换 Node PTC 运行时。
|
|
74
|
+
- id: ptc-runtime
|
|
75
|
+
disabled: true
|
|
76
|
+
- id: ptc-moejs
|
|
77
|
+
disabled: false
|
|
78
|
+
# 打开 PTC 模式。
|
|
79
|
+
- id: tools
|
|
80
|
+
config:
|
|
81
|
+
mode: ptc
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`dsh --profile web --dump-config` 会打印组合后的配置行。在 web 和 headless profile 里,也可以用 `DSH_TOOLS_MODE=ptc` 在单次启动时打开 PTC 模式,代替 `tools` 那一项。要换回 Node 运行时,删掉前两项。
|
|
85
|
+
|
|
86
|
+
## 配置
|
|
87
|
+
|
|
88
|
+
选项写在同一个补丁的 `ptc-moejs` 行上:
|
|
89
|
+
|
|
90
|
+
```yaml
|
|
91
|
+
- id: ptc-moejs
|
|
92
|
+
disabled: false
|
|
93
|
+
config:
|
|
94
|
+
memoryLimitMb: 1024
|
|
95
|
+
timeoutMs: 300000
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
| 选项 | 默认值 | 含义 |
|
|
99
|
+
|---|--:|---|
|
|
100
|
+
| `timeoutMs` | 120000 | 模型没有指定时,一次运行的截止时间(毫秒)。工具调用和审批的时间都算在内。 |
|
|
101
|
+
| `maxTimeoutMs` | 600000 | 一次运行可以请求的最长截止时间。 |
|
|
102
|
+
| `memoryLimitMb` | 512 | 一次运行在整个生命周期里可以分配的 MiB 数,垃圾也算在内。超过后运行以 `timeout` 停止,消息里写明是哪个预算。 |
|
|
103
|
+
| `maxLogBytes` | 16 MiB | 一次运行保留的 console 输出字节数,按 dsh 存储的 JSON 数组计算。超出部分被丢弃,日志以一行标记结尾,运行继续。 |
|
|
104
|
+
| `maxValueBytes` | 16 MiB | 返回值序列化成 JSON 后的字节数。更大的值让运行以 `output-limit` 失败。错误消息也截断到这个长度。 |
|
|
105
|
+
| `maxPendingCalls` | 128 | 一次运行同时进行中的工具调用数。再多一个,运行以 `protocol` 失败。 |
|
|
106
|
+
| `maxFrameBytes` | 64 MiB | dsh 和 sidecar 之间单条消息的上限:一段程序、一次工具调用的参数或一个工具结果。更大的工具结果在程序里表现为被拒绝的调用。 |
|
|
107
|
+
|
|
108
|
+
截止时间的默认值和 dsh 的 Node 运行时一致。dsh 会把超过 12,500 token 的工具结果转存到文件,所以日志和返回值的预算用来限制内存占用。
|
|
109
|
+
|
|
110
|
+
## 程序能看到什么
|
|
111
|
+
|
|
112
|
+
程序是一个 async 函数的函数体。函数的参数有 dsh 传入的绑定命名空间(PTC 模式下是 `tools`)、每个命名空间的错误类(`ToolCallError`,`toolName` 属性里是工具名),以及带 `log`、`info`、`warn`、`error`、`debug` 的 `console`。其余是 JavaScript 标准库,moejs 的标准库里有 `TextEncoder`、`TextDecoder`、`atob`、`btoa`、`structuredClone` 和 `queueMicrotask`。
|
|
113
|
+
|
|
114
|
+
模型写出的程序是这样的,运行时把返回值交给 dsh:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
const head = await tools.bash({ description: "Read the last commit", command: "git log -1 --oneline" });
|
|
118
|
+
return { head: head.stdout?.text };
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
返回值以无损 JSON 的形式交给 dsh:普通对象、数组、字符串、有限数字、布尔值和 `null`。值里含有 `undefined`、函数、`BigInt`、循环引用、`NaN`、`Infinity`、`-0`、`Date`、`Map` 或类的实例时,运行以 `invalid-output` 失败,消息给出第一个这样的成员的路径,例如 `a BigInt at $.items[3].size`。程序没有返回值时,运行正常结束,结果里没有值。工具参数遵守同样的规则,参数不是无损 JSON 的调用在发给 dsh 之前就在程序内部被拒绝。
|
|
122
|
+
|
|
123
|
+
| 失败类型 | 什么时候出现 |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `exception` | 程序抛出异常、有语法错误,或者用了会改变运行行为的 TypeScript,例如 `enum`。 |
|
|
126
|
+
| `timeout` | 超过截止时间,或者分配的内存超过 `memoryLimitMb`。 |
|
|
127
|
+
| `abort` | dsh 取消了这次运行,或者 profile 关闭了运行时。 |
|
|
128
|
+
| `invalid-output` | 返回值不是无损 JSON。 |
|
|
129
|
+
| `output-limit` | 返回值大于 `maxValueBytes`。 |
|
|
130
|
+
| `protocol` | 同时进行中的工具调用超过 `maxPendingCalls`,或者 sidecar 发来了格式错误的消息。 |
|
|
131
|
+
| `worker-exit` | sidecar 进程退出了。下一次运行会启动新的进程。 |
|
|
132
|
+
|
|
133
|
+
## 限制
|
|
134
|
+
|
|
135
|
+
- 每次调用都从新的运行时开始。在调用之间保留变量的持久模式(类似 Node 运行时的 REPL 模式)计划在以后的版本中加入。
|
|
136
|
+
- 内存预算统计的是一次运行分配的内存,垃圾也算在内。一次运行如果反复创建又丢弃大量数据,可能在存活数据达到预算之前就被停止。
|
|
137
|
+
- 两个方向的值最多嵌套 10,000 层,这是 moejs 的 JSON 读写能处理的深度。
|
|
138
|
+
- `console` 的输出仿照 Node 的 `util.inspect`,覆盖字符串、数字、普通对象、数组、`Map`、`Set`、错误和类的实例,单行超过 72 个字符时换行。
|
|
139
|
+
- moejs 的中断作用于正在运行的 JavaScript,编译一旦开始会一直进行到结束。解析很慢的程序到截止时间时,dsh 一侧照常结束这次运行,sidecar 等解析返回后丢弃它。
|
|
140
|
+
- 程序没有 await 的工具调用如果失败,这个失败只留在那个没人读取的 promise 里。
|
|
141
|
+
- 这个版本在 Linux x64 上测试过。其余四个平台的程序由同一份源码交叉编译。
|
|
142
|
+
|
|
143
|
+
## 开发
|
|
144
|
+
|
|
145
|
+
```sh
|
|
146
|
+
pnpm install
|
|
147
|
+
pnpm run build:sidecar # 为本机构建 sidecar,放到 npm/<package>/bin
|
|
148
|
+
pnpm run build # lib/
|
|
149
|
+
pnpm test # 单元测试,以及针对 sidecar 的行为测试
|
|
150
|
+
go test ./... # sidecar 的 Go 测试
|
|
151
|
+
DSH_REPO=/path/to/deepseek-harness pnpm run e2e # 用 dsh 的 mock LLM 跑 headless dsh
|
|
152
|
+
DSH_REPO=/path/to/deepseek-harness pnpm run bench # 上面的测量
|
|
153
|
+
pnpm run build:binaries # 五个平台的程序
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
构建 sidecar 需要 Go 1.25 或更新版本,使用 `CGO_ENABLED=0`。`DSH_REPO` 是执行过 `pnpm install` 和 `pnpm run build` 的 dsh 源码目录。端到端测试用 `dsh plugin add` 把这个目录装进临时 profile,跑一个 headless 任务,让 mock 模型调用 `run_code`,再和 Node 运行时跑同一任务的会话日志做比较。
|
|
157
|
+
|
|
158
|
+
## 许可证
|
|
159
|
+
|
|
160
|
+
MIT。各平台包里的常驻进程二进制还包含 moejs(Apache License 2.0)和 Go 标准库(BSD 3-Clause License),它们的许可证原文在 [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) 里,随每个平台包一起发布。
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# The dsh-ptc-moejs bundle layer: it mounts the moejs PTC runtime as a
|
|
2
|
+
# disabled row, so installing the package changes nothing on its own. A
|
|
3
|
+
# profile selects it with a patch that disables the ptc-runtime row (the Node
|
|
4
|
+
# runtime) and enables this one; see the README.
|
|
5
|
+
|
|
6
|
+
- insert:
|
|
7
|
+
- id: ptc-moejs
|
|
8
|
+
name: dsh-ptc-moejs
|
|
9
|
+
disabled: true
|
package/lib/binary.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finds the sidecar binary. Each supported platform has its own package,
|
|
3
|
+
* installed as an optional dependency, as esbuild ships its binary.
|
|
4
|
+
* @module dsh-ptc-moejs/binary
|
|
5
|
+
*/
|
|
6
|
+
/** The platforms that have a binary package. */
|
|
7
|
+
export declare const PLATFORMS: readonly ["darwin-arm64", "darwin-x64", "linux-arm64", "linux-x64", "win32-x64"];
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the sidecar binary for this platform.
|
|
10
|
+
* @param platform - the Node platform name.
|
|
11
|
+
* @param arch - the Node architecture name.
|
|
12
|
+
* @returns the absolute path of the binary.
|
|
13
|
+
* @throws When no binary package for the platform is installed.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveBinary(platform?: string, arch?: string): string;
|
package/lib/binary.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finds the sidecar binary. Each supported platform has its own package,
|
|
3
|
+
* installed as an optional dependency, as esbuild ships its binary.
|
|
4
|
+
* @module dsh-ptc-moejs/binary
|
|
5
|
+
*/
|
|
6
|
+
import { createRequire } from 'node:module';
|
|
7
|
+
/** The platforms that have a binary package. */
|
|
8
|
+
export const PLATFORMS = ['darwin-arm64', 'darwin-x64', 'linux-arm64', 'linux-x64', 'win32-x64'];
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the sidecar binary for this platform.
|
|
11
|
+
* @param platform - the Node platform name.
|
|
12
|
+
* @param arch - the Node architecture name.
|
|
13
|
+
* @returns the absolute path of the binary.
|
|
14
|
+
* @throws When no binary package for the platform is installed.
|
|
15
|
+
*/
|
|
16
|
+
export function resolveBinary(platform = process.platform, arch = process.arch) {
|
|
17
|
+
const target = `${platform}-${arch}`;
|
|
18
|
+
const pkg = `dsh-ptc-moejs-${target}`;
|
|
19
|
+
try {
|
|
20
|
+
return createRequire(import.meta.url).resolve(`${pkg}/bin/moejs-ptc${platform === 'win32' ? '.exe' : ''}`);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
const supported = PLATFORMS.includes(target);
|
|
24
|
+
throw new Error(supported
|
|
25
|
+
? `dsh-ptc-moejs: the sidecar package ${pkg} is not installed; reinstall with optional dependencies enabled`
|
|
26
|
+
: `dsh-ptc-moejs: no sidecar binary for ${target} (supported: ${PLATFORMS.join(', ')})`);
|
|
27
|
+
}
|
|
28
|
+
}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A PTC runtime for the DeepSeek Harness that runs each program in a fresh
|
|
3
|
+
* moejs runtime inside one long-lived sidecar process. A program's effects on
|
|
4
|
+
* the machine are its tool calls, which dsh's tool pipeline confines.
|
|
5
|
+
* @module dsh-ptc-moejs
|
|
6
|
+
*/
|
|
7
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
8
|
+
import { PtcRuntime } from '@deepseek-ai/dsh-ptc-runtime';
|
|
9
|
+
import type { PtcBindingNamespace, PtcRunRequest, PtcRunResult, PtcRunSpec } from '@deepseek-ai/dsh-ptc-runtime';
|
|
10
|
+
import z from '@deepseek-ai/schemastery';
|
|
11
|
+
import type { Namespace } from './sidecar.ts';
|
|
12
|
+
/** Plugin configuration. */
|
|
13
|
+
export interface Config {
|
|
14
|
+
/** Default elapsed deadline of a run in milliseconds, tool calls included. */
|
|
15
|
+
timeoutMs?: number;
|
|
16
|
+
/** Largest deadline a run may request. */
|
|
17
|
+
maxTimeoutMs?: number;
|
|
18
|
+
/** MiB one run may allocate in total, garbage included, before it is stopped. */
|
|
19
|
+
memoryLimitMb?: number;
|
|
20
|
+
/** Bytes of serialized logs a run keeps; capture stops at the budget. */
|
|
21
|
+
maxLogBytes?: number;
|
|
22
|
+
/** Bytes of serialized completion value; a larger value fails as output-limit. */
|
|
23
|
+
maxValueBytes?: number;
|
|
24
|
+
/** Tool calls one run may have in flight at once. */
|
|
25
|
+
maxPendingCalls?: number;
|
|
26
|
+
/** Bytes of one frame between host and sidecar: a program, a tool result or tool arguments. */
|
|
27
|
+
maxFrameBytes?: number;
|
|
28
|
+
}
|
|
29
|
+
/** Configuration with every default filled in. */
|
|
30
|
+
export type ResolvedConfig = Required<Config>;
|
|
31
|
+
/** The defaults, with the Node runtime's 120 s and 600 s deadlines. */
|
|
32
|
+
export declare const MoejsConfig: z<Config>;
|
|
33
|
+
/**
|
|
34
|
+
* Fill in defaults and validate the configuration.
|
|
35
|
+
* @param config - configuration as given.
|
|
36
|
+
* @returns the complete configuration.
|
|
37
|
+
* @throws When a value is out of range.
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveConfig(config?: Config): ResolvedConfig;
|
|
40
|
+
/**
|
|
41
|
+
* Validate the binding namespaces of a request and read each function once.
|
|
42
|
+
* Names follow the rules dsh shares across PTC runtimes, so a namespace list
|
|
43
|
+
* valid here is valid on every runtime.
|
|
44
|
+
* @param bindings - the request's namespaces.
|
|
45
|
+
* @returns the namespaces keyed by their global.
|
|
46
|
+
* @throws When a name is unusable, reserved or used twice.
|
|
47
|
+
*/
|
|
48
|
+
export declare function validateBindings(bindings: readonly PtcBindingNamespace[]): Map<string, Namespace>;
|
|
49
|
+
/** The moejs PTC runtime: `ctx.ptcRuntime` backed by one moejs-ptc sidecar. */
|
|
50
|
+
export declare class MoejsPtcRuntime extends PtcRuntime {
|
|
51
|
+
static Config: z<Config>;
|
|
52
|
+
readonly language = "typescript";
|
|
53
|
+
readonly isolation = "process";
|
|
54
|
+
private readonly config;
|
|
55
|
+
private readonly sidecar;
|
|
56
|
+
private disposed;
|
|
57
|
+
constructor(ctx: Context, config: Config);
|
|
58
|
+
get executionInstructions(): string;
|
|
59
|
+
get timeout(): {
|
|
60
|
+
defaultMs: number;
|
|
61
|
+
maxMs: number;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Fill in the working directory and deadline of a request.
|
|
65
|
+
* @param request - program, bindings and optional choices.
|
|
66
|
+
* @returns the request with a cwd and a capped deadline, or null for none.
|
|
67
|
+
* @throws When the request names a sandbox policy, a relative cwd or an invalid timeout.
|
|
68
|
+
*/
|
|
69
|
+
resolve(request: PtcRunRequest): PtcRunSpec;
|
|
70
|
+
/**
|
|
71
|
+
* Run a resolved program. Its outcome is a field of the result.
|
|
72
|
+
* @param spec - what resolve returned.
|
|
73
|
+
* @returns the program's logs and its value or failure.
|
|
74
|
+
* @throws When the spec was not resolved or its bindings break the seam's rules.
|
|
75
|
+
*/
|
|
76
|
+
run(spec: PtcRunSpec): Promise<PtcRunResult>;
|
|
77
|
+
}
|
|
78
|
+
export default MoejsPtcRuntime;
|