pi-ptc-subagents 7.3.0 → 7.5.0

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.
@@ -0,0 +1,197 @@
1
+ import { stripTypeScriptTypes } from "node:module";
2
+ import { parentPort, workerData } from "node:worker_threads";
3
+ import vm from "node:vm";
4
+ // src/compile.ts
5
+ function compileProgram(source) {
6
+ return `"use strict";\n${stripTypeScriptTypes(`async function __ptcProgram() {\n${source}\n}`, { mode: "strip" })}\n;globalThis.__ptcProgram = __ptcProgram;`;
7
+ }
8
+ // src/worker-main.ts
9
+ const CELL_PREAMBLE = `"use strict";
10
+ (function () {
11
+ var __bridge = globalThis.__ptcCall;
12
+ var __emit = globalThis.__ptcConsole;
13
+ var __args = JSON.parse(globalThis.__ptcArgsJson);
14
+ // contextified 全局属性不可 delete(vm 限制)但可写——置 undefined 即脱手,桥只剩序言闭包可达
15
+ globalThis.__ptcCall = undefined;
16
+ globalThis.__ptcConsole = undefined;
17
+ globalThis.__ptcArgsJson = undefined;
18
+ function __ptcFmt(v) {
19
+ if (typeof v === "string") return v;
20
+ try { var s = JSON.stringify(v); return s === undefined ? String(v) : s; } catch (e) { return String(v); }
21
+ }
22
+ function __ptcJoin(values) {
23
+ var out = [];
24
+ for (var i = 0; i < values.length; i++) out.push(__ptcFmt(values[i]));
25
+ return out.join(" ");
26
+ }
27
+ var __ptcTools = {};
28
+ __PTC_NAMES__.forEach(function (name) {
29
+ __ptcTools[name] = function (args) {
30
+ return new Promise(function (resolve, reject) {
31
+ __bridge(name, JSON.stringify(args === undefined ? null : args)).then(function (json) {
32
+ var value;
33
+ try { value = JSON.parse(json); } catch (e) { reject(e); return; }
34
+ resolve(value);
35
+ }, reject);
36
+ });
37
+ };
38
+ });
39
+ globalThis.tools = Object.freeze(__ptcTools);
40
+ globalThis.console = Object.freeze({
41
+ log: function () { __emit("log", __ptcJoin(arguments)); },
42
+ info: function () { __emit("info", __ptcJoin(arguments)); },
43
+ warn: function () { __emit("warn", __ptcJoin(arguments)); },
44
+ error: function () { __emit("error", __ptcJoin(arguments)); },
45
+ debug: function () { __emit("debug", __ptcJoin(arguments)); }
46
+ });
47
+ globalThis.args = Object.freeze(__args);
48
+ if (__PTC_WORKFLOW__) {
49
+ globalThis.workflow = Object.freeze({
50
+ log: function () { __emit("log", __ptcJoin(arguments)); },
51
+ phase: function (name) { __emit("log", "— phase: " + name + " —"); },
52
+ parallel: function (items) { return Promise.allSettled(items.map(function (run) { return run(); })); },
53
+ pipeline: function (items, step) {
54
+ var results = [];
55
+ return items.reduce(function (chain, item, index) {
56
+ return chain.then(function () { return Promise.resolve(step(item, index)).then(function (r) { results.push(r); }); });
57
+ }, Promise.resolve()).then(function () { return results; });
58
+ }
59
+ });
60
+ }
61
+ })();
62
+ `;
63
+ function workerMain(deps) {
64
+ let bindingNames = [];
65
+ let surface = "run_code";
66
+ let pending = /* @__PURE__ */ new Map();
67
+ let nextCallId = 1;
68
+ let currentRunId = null;
69
+ let logs = [];
70
+ deps.onFrame((frame) => {
71
+ if (frame.kind === "init") {
72
+ bindingNames = frame.bindingNames;
73
+ surface = frame.surface;
74
+ deps.post({ kind: "ready" });
75
+ return;
76
+ }
77
+ if (frame.kind === "reset") {
78
+ currentRunId = null;
79
+ for (const { reject } of pending.values()) reject(/* @__PURE__ */ new Error("run reset:worker 被复用"));
80
+ pending = /* @__PURE__ */ new Map();
81
+ deps.post({ kind: "ready" });
82
+ return;
83
+ }
84
+ if (frame.kind === "result") {
85
+ const entry = pending.get(frame.callId);
86
+ if (!entry) return;
87
+ pending.delete(frame.callId);
88
+ if (frame.ok) {
89
+ let json;
90
+ try {
91
+ json = JSON.stringify(frame.value ?? null);
92
+ } catch {
93
+ json = void 0;
94
+ }
95
+ if (json === void 0) entry.reject(/* @__PURE__ */ new Error("binding 返回值不可 JSON 序列化(如循环引用)"));
96
+ else entry.resolve(json);
97
+ } else entry.reject(new Error(frame.error));
98
+ return;
99
+ }
100
+ if (frame.kind === "cancel") return;
101
+ run(frame.code, frame.id, frame.args);
102
+ });
103
+ function preamble() {
104
+ return CELL_PREAMBLE.replace(/__PTC_NAMES__/g, () => JSON.stringify(bindingNames)).replace(/__PTC_WORKFLOW__/g, () => surface === "workflow" ? "true" : "false");
105
+ }
106
+ async function run(code, runId, args) {
107
+ currentRunId = runId;
108
+ logs = [];
109
+ const postLog = (level, text) => {
110
+ logs.push(text);
111
+ deps.post({
112
+ kind: "log",
113
+ runId,
114
+ level,
115
+ text
116
+ });
117
+ };
118
+ const outcome = await execute(code, vm.createContext({
119
+ __ptcArgsJson: JSON.stringify(args ?? {}),
120
+ __ptcCall(name, argsJson) {
121
+ if (currentRunId === null) return Promise.reject(/* @__PURE__ */ new Error("run 已结束:reset 后不再接受工具调用"));
122
+ let parsed = null;
123
+ try {
124
+ parsed = JSON.parse(argsJson);
125
+ } catch {
126
+ return Promise.reject(/* @__PURE__ */ new Error("工具参数不是合法 JSON"));
127
+ }
128
+ const call = new Promise((resolve, reject) => {
129
+ const callId = nextCallId++;
130
+ pending.set(callId, {
131
+ resolve,
132
+ reject
133
+ });
134
+ deps.post({
135
+ kind: "call",
136
+ runId: currentRunId ?? "",
137
+ callId,
138
+ name,
139
+ args: parsed
140
+ });
141
+ });
142
+ call.catch(() => {});
143
+ return call;
144
+ },
145
+ __ptcConsole(level, text) {
146
+ postLog(level, String(text));
147
+ }
148
+ }));
149
+ deps.post({
150
+ kind: "settled",
151
+ runId,
152
+ outcome
153
+ });
154
+ }
155
+ function errorMessage(error) {
156
+ if (error instanceof Error) return error.message;
157
+ if (error && typeof error === "object" && typeof error.message === "string") return error.message;
158
+ return String(error);
159
+ }
160
+ async function execute(code, context) {
161
+ try {
162
+ vm.runInContext(preamble(), context, { filename: "ptc_preamble.js" });
163
+ vm.runInContext(compileProgram(code), context, { filename: "run_code.js" });
164
+ const value = await context.__ptcProgram.call(context);
165
+ if (value === void 0) return {
166
+ status: "fulfilled",
167
+ value: { $void: true },
168
+ logs
169
+ };
170
+ const json = JSON.stringify(value);
171
+ if (json === void 0) return {
172
+ status: "rejected",
173
+ error: "程序返回值不可序列化",
174
+ logs
175
+ };
176
+ return {
177
+ status: "fulfilled",
178
+ value: JSON.parse(json),
179
+ logs
180
+ };
181
+ } catch (error) {
182
+ return {
183
+ status: "rejected",
184
+ error: errorMessage(error),
185
+ logs
186
+ };
187
+ }
188
+ }
189
+ }
190
+ // src/worker-entry.ts
191
+ if (!parentPort) throw new Error("worker-entry 只能在 worker 线程里加载");
192
+ Object.freeze(workerData);
193
+ workerMain({
194
+ post: (frame) => parentPort.postMessage(frame),
195
+ onFrame: (handler) => parentPort.on("message", (frame) => handler(frame))
196
+ });
197
+ export {};
@@ -16,3 +16,4 @@ pi 面上,搜索能力**只活在被派出的子代理里**(父会话不注
16
16
  - 模型可见文本预算 12,000 字符;超了按排名**整条丢弃** + 告知丢了哪些(要哪个用 open 补抓)——不按字符截断
17
17
  - 配额每进程计(`searchMaxCalls`,默认 30 次/进程,0 = 不限;只计成功调用)
18
18
  - 开关 `webDsSearch`(`~/.pi/agent/ptc.json`)是布尔,**默认关**:开关关 = 工具不存在;开关开但缺凭据 = 注册但调用返回可读错误
19
+ - **开关关着就别派这类任务,也别去改它**:那是用户的配置,不是你的开关——改了等于替用户开了一个有网络出口、按次计费的能力。直接说明「搜索没开,做不到」;用户要的话他会自己开。实测(2026-11-01):读到「默认关」后去改配置、翻源码找开关位置的会话,比老老实实说做不到的慢 4 倍
@@ -7,6 +7,7 @@
7
7
 
8
8
  - 绑定表 = 内置名单 ∩ 会话活跃工具(`--tools` 收窄的会话不会拿到被滤掉的绑定)
9
9
  - `bash` 不在程序绑定表里——旧 ADR-0040 的 opt-in 尚未接线,当前没有任何开关能开它
10
+ - 程序内还能调 `task_records({ scope })` 取任务记录、`task_output({ taskId })` 取孩子交回的正文(归约后的散文)——返回 `{text}`;`text` 空时看 `status`:还在跑(`queued`/`running`/`stopping`)就等一会儿再取,别当没产出
10
11
  - 程序返回值是归约后的结果;undefined 返回标记为「无完成值」
11
12
 
12
13
  默认 2 分钟超时(可经 timeoutMs 调整)。输出超 50 KB 按 pi 截断契约切尾,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-ptc-subagents",
3
- "version": "7.3.0",
3
+ "version": "7.5.0",
4
4
  "description": "Programmable tool calling (PTC) and subagent fan-out for pi",
5
5
  "keywords": [
6
6
  "pi",
@@ -18,6 +18,9 @@
18
18
  "type": "git",
19
19
  "url": "git+https://github.com/a1121611810/pi-ptc-subagents.git"
20
20
  },
21
+ "bin": {
22
+ "ptc": "./dist/ptc/cli.js"
23
+ },
21
24
  "files": [
22
25
  "dist/**/*.js",
23
26
  "docs/routes/**/*.md",
@@ -27,18 +30,20 @@
27
30
  "type": "module",
28
31
  "main": "./dist/index.js",
29
32
  "exports": {
30
- ".": "./dist/index.js"
33
+ ".": "./dist/index.js",
34
+ "./ptc": "./dist/ptc/index.js"
31
35
  },
32
36
  "scripts": {
33
37
  "build": "pnpm -r --filter=!@pi-ptc/website run build",
34
38
  "build:kimi": "pnpm --filter @pi-ptc/kimi-mcp run build && node scripts/assemble-kimi.mjs",
35
- "prepack": "pnpm run build && rm -rf dist docs/routes && cp -R packages/entry/dist dist && cp -R packages/routes/docs/routes docs/routes",
39
+ "prepack": "pnpm run build && rm -rf dist docs/routes && cp -R packages/entry/dist dist && cp -R packages/ptc/dist dist/ptc && cp -R packages/routes/docs/routes docs/routes",
36
40
  "check": "pnpm -r --filter=!@pi-ptc/website run check",
37
41
  "lint": "oxlint",
38
- "test": "pnpm run test:pi && pnpm run test:output && pnpm run test:kimi",
42
+ "test": "pnpm run test:pi && pnpm run test:output && pnpm run test:kimi && pnpm run test:standalone",
39
43
  "test:pi": "node --test tests/pi/*.test.mjs",
40
44
  "test:output": "node --test tests/output/*.test.mjs",
41
45
  "test:kimi": "node --test tests/kimi/*.test.mjs",
46
+ "test:standalone": "node --test tests/standalone/*.test.mjs",
42
47
  "pretest": "pnpm run build",
43
48
  "fmt": "oxfmt",
44
49
  "fmt:check": "oxfmt --check"