@eddyskywalker/dsh-chatgpt-subscription 0.60.0 → 0.61.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.
- package/CHANGELOG.md +26 -0
- package/lib/client.js.map +1 -1
- package/lib/codemode.js +319 -89
- package/lib/entry-plan-mode-CLRKkgYG.js +1787 -0
- package/lib/{entry-progress-updates-CxQewio9.js → entry-progress-updates-DZ0ZRus4.js} +14 -1
- package/lib/index.js +905 -574
- package/lib/plan-mode.js +2 -779
- package/lib/progress-updates.js +1 -1
- package/lib/types/compat.d.ts +1 -1
- package/lib/types/entry-codemode.d.ts.map +1 -1
- package/lib/types/entry-plan-mode.d.ts +136 -0
- package/lib/types/entry-plan-mode.d.ts.map +1 -1
- package/lib/types/host/agent-preset.d.ts.map +1 -1
- package/lib/types/host/claude/adapter.d.ts +16 -0
- package/lib/types/host/claude/adapter.d.ts.map +1 -1
- package/lib/types/host/claude/client.d.ts +7 -1
- package/lib/types/host/claude/client.d.ts.map +1 -1
- package/lib/types/host/claude/mapper.d.ts +9 -0
- package/lib/types/host/claude/mapper.d.ts.map +1 -1
- package/lib/types/host/claude/types.d.ts +10 -0
- package/lib/types/host/claude/types.d.ts.map +1 -1
- package/lib/types/host/cline-pass/mapper.d.ts.map +1 -1
- package/lib/types/host/codemode/discovery.d.ts.map +1 -1
- package/lib/types/host/codemode/rules.d.ts.map +1 -1
- package/lib/types/host/codemode/transport.d.ts +38 -0
- package/lib/types/host/codemode/transport.d.ts.map +1 -1
- package/lib/types/host/command-code/mapper.d.ts.map +1 -1
- package/lib/types/host/common/orphan-tool-calls.d.ts +48 -0
- package/lib/types/host/common/orphan-tool-calls.d.ts.map +1 -0
- package/lib/types/host/dispatch-execution-guard.d.ts +32 -2
- package/lib/types/host/dispatch-execution-guard.d.ts.map +1 -1
- package/lib/types/host/glm-coding-plan/mapper-anthropic.d.ts.map +1 -1
- package/lib/types/host/glm-coding-plan/mapper-openai.d.ts.map +1 -1
- package/lib/types/host/kimi-code/mapper.d.ts.map +1 -1
- package/lib/types/host/minimax-code/mapper.d.ts.map +1 -1
- package/lib/types/host/model-catalog.d.ts +21 -0
- package/lib/types/host/model-catalog.d.ts.map +1 -1
- package/lib/types/host/opendesign/mapper.d.ts.map +1 -1
- package/lib/types/host/plan-mode-cache-guard.d.ts.map +1 -1
- package/lib/types/host/progress-updates/nudge.d.ts +13 -1
- package/lib/types/host/progress-updates/nudge.d.ts.map +1 -1
- package/lib/types/host/qoder/mapper.d.ts.map +1 -1
- package/lib/types/host/responses-client.d.ts.map +1 -1
- package/lib/types/host/responses-mapper.d.ts +12 -0
- package/lib/types/host/responses-mapper.d.ts.map +1 -1
- package/lib/types/host/settled-job-guard.d.ts +77 -0
- package/lib/types/host/settled-job-guard.d.ts.map +1 -0
- package/lib/types/host/step-code/mapper.d.ts.map +1 -1
- package/lib/types/host/workbuddy/mapper.d.ts.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/presets/dispatch/README.md +7 -10
- package/presets/dispatch/agent.cordis.yml +4 -11
package/lib/codemode.js
CHANGED
|
@@ -112,13 +112,19 @@ function searchDefinition(name, rules, catalog) {
|
|
|
112
112
|
additionalProperties: false,
|
|
113
113
|
required: [
|
|
114
114
|
"name",
|
|
115
|
+
"namespace",
|
|
115
116
|
"description",
|
|
116
117
|
"parameters"
|
|
117
118
|
],
|
|
118
119
|
properties: {
|
|
119
120
|
name: { type: "string" },
|
|
121
|
+
namespace: {
|
|
122
|
+
type: "string",
|
|
123
|
+
description: "Namespace of the tool: an MCP server name, or \"plugin\"."
|
|
124
|
+
},
|
|
120
125
|
description: { type: "string" },
|
|
121
|
-
parameters: {}
|
|
126
|
+
parameters: {},
|
|
127
|
+
output: { description: "The tool's declared output schema, when it has one." }
|
|
122
128
|
}
|
|
123
129
|
}
|
|
124
130
|
}
|
|
@@ -126,7 +132,7 @@ function searchDefinition(name, rules, catalog) {
|
|
|
126
132
|
},
|
|
127
133
|
render: (_args, value) => [{
|
|
128
134
|
type: "text",
|
|
129
|
-
text: value.tools.length === 0 ? "No tool matched that query, and this session has no tools to browse." : (value.fallback === true ? "No exact match. These are the callable tools, grouped by namespace — call one by exact name, or refine the query:" : "") + value.tools.map((hit) => hit.name + "\n " + hit.description + "\n parameters: " + safeJson(hit.parameters)).join("\n\n")
|
|
135
|
+
text: value.tools.length === 0 ? "No tool matched that query, and this session has no tools to browse." : (value.fallback === true ? "No exact match. These are the callable tools, grouped by namespace — call one by exact name, or refine the query:" : "") + value.tools.map((hit) => hit.name + " [" + hit.namespace + "]\n " + hit.description + "\n parameters: " + safeJson(hit.parameters) + (hit.output === void 0 ? "" : "\n output: " + safeJson(hit.output))).join("\n\n")
|
|
130
136
|
}]
|
|
131
137
|
},
|
|
132
138
|
async execute(args) {
|
|
@@ -151,8 +157,10 @@ function searchDefinition(name, rules, catalog) {
|
|
|
151
157
|
const hits = scored.slice(0, limit);
|
|
152
158
|
if (hits.length > 0) return { tools: hits.map((row) => ({
|
|
153
159
|
name: row.indexed.entry.name,
|
|
160
|
+
namespace: namespaceOfName(row.indexed.entry.name),
|
|
154
161
|
description: clip(row.indexed.entry.description),
|
|
155
|
-
parameters: row.indexed.entry.parameters
|
|
162
|
+
parameters: row.indexed.entry.parameters,
|
|
163
|
+
...row.indexed.entry.output === void 0 ? {} : { output: row.indexed.entry.output }
|
|
156
164
|
})) };
|
|
157
165
|
return {
|
|
158
166
|
tools: browseByNamespace(candidates, limit),
|
|
@@ -183,13 +191,15 @@ function describeDefinition(name, catalog) {
|
|
|
183
191
|
properties: {
|
|
184
192
|
found: { type: "boolean" },
|
|
185
193
|
name: { type: "string" },
|
|
194
|
+
namespace: { type: "string" },
|
|
186
195
|
description: { type: "string" },
|
|
187
|
-
parameters: {}
|
|
196
|
+
parameters: {},
|
|
197
|
+
output: { description: "The tool's declared output schema, when it has one." }
|
|
188
198
|
}
|
|
189
199
|
},
|
|
190
200
|
render: (_args, value) => [{
|
|
191
201
|
type: "text",
|
|
192
|
-
text: value.found !== true ? "No tool named that is callable from this session." : String(value.name) + "\n" + String(value.description) + "\nparameters: " + safeJson(value.parameters)
|
|
202
|
+
text: value.found !== true ? "No tool named that is callable from this session." : String(value.name) + " [" + String(value.namespace) + "]\n" + String(value.description) + "\nparameters: " + safeJson(value.parameters) + (value.output === void 0 ? "" : "\noutput: " + safeJson(value.output))
|
|
193
203
|
}]
|
|
194
204
|
},
|
|
195
205
|
async execute(args) {
|
|
@@ -200,8 +210,10 @@ function describeDefinition(name, catalog) {
|
|
|
200
210
|
return {
|
|
201
211
|
found: true,
|
|
202
212
|
name: entry.name,
|
|
213
|
+
namespace: namespaceOfName(entry.name),
|
|
203
214
|
description: clip(entry.description),
|
|
204
|
-
parameters: entry.parameters
|
|
215
|
+
parameters: entry.parameters,
|
|
216
|
+
...entry.output === void 0 ? {} : { output: entry.output }
|
|
205
217
|
};
|
|
206
218
|
}
|
|
207
219
|
};
|
|
@@ -272,8 +284,10 @@ function browseByNamespace(entries, limit) {
|
|
|
272
284
|
if (rows.length >= limit) return rows;
|
|
273
285
|
rows.push({
|
|
274
286
|
name: entry.name,
|
|
287
|
+
namespace,
|
|
275
288
|
description: clip(namespace + ": " + entry.description),
|
|
276
|
-
parameters: entry.parameters
|
|
289
|
+
parameters: entry.parameters,
|
|
290
|
+
...entry.output === void 0 ? {} : { output: entry.output }
|
|
277
291
|
});
|
|
278
292
|
}
|
|
279
293
|
return rows;
|
|
@@ -587,16 +601,16 @@ function buildCodemodeSectionText(input) {
|
|
|
587
601
|
"```py",
|
|
588
602
|
"# standard tools and hidden tools alike",
|
|
589
603
|
"import asyncio",
|
|
590
|
-
"
|
|
591
|
-
"
|
|
592
|
-
"[
|
|
604
|
+
"found = await tools.glob({\"pattern\": \"src/**/*.ts\"})",
|
|
605
|
+
"pages = await asyncio.gather(*(tools.read({\"file_path\": p}) for p in found[\"paths\"]))",
|
|
606
|
+
"[l[\"text\"] for page in pages for l in page[\"lines\"] if \"TODO\" in l[\"text\"]]",
|
|
593
607
|
"```"
|
|
594
608
|
] : [
|
|
595
609
|
"```js",
|
|
596
610
|
"// standard tools and hidden tools alike",
|
|
597
|
-
"const
|
|
598
|
-
"const
|
|
599
|
-
retained ? "
|
|
611
|
+
"const { paths } = await tools.glob({ pattern: \"src/**/*.ts\" })",
|
|
612
|
+
"const pages = await Promise.all(paths.map((p) => tools.read({ file_path: p })))",
|
|
613
|
+
retained ? "pages.flatMap((r) => r.lines.filter((l) => l.text.includes(\"TODO\")).map((l) => l.text))" : "return pages.flatMap((r) => r.lines.filter((l) => l.text.includes(\"TODO\")).map((l) => l.text))",
|
|
600
614
|
"```"
|
|
601
615
|
];
|
|
602
616
|
return [
|
|
@@ -610,6 +624,9 @@ function buildCodemodeSectionText(input) {
|
|
|
610
624
|
"",
|
|
611
625
|
python ? retained ? "- A retained Python REPL: top-level await and a final expression. Its variables and imported modules survive into your later programs in this session." : "- Async Python program: top-level await works; print or evaluate a final expression to produce output." : retained ? "- A retained TypeScript REPL: top-level declarations, await, and a final expression. Its variables and imported modules survive into your later programs in this session." : "- Erasable TypeScript, run as the body of an async function: top-level await and return work.",
|
|
612
626
|
"- Only what you return or console.log reaches the conversation; every intermediate result stays in the program, so filter and aggregate there instead of returning raw payloads.",
|
|
627
|
+
"- File, search, and shell tool results are named-field objects, never bare strings (whereas `job_list` returns a bare array): `glob` answers `{ root, paths }`, `grep` answers `{ matches }` with matches shaped `{ path, lineNumber, line }`, `read` answers `{ path, offset, lines, totalLines }` with `lines` items shaped `{ number, text }`, and `win_bash` answers `{ stdout, stderr, exitCode }`. A named-field object carries its list or text inside those fields — `r.lines.map((l) => l.text)` — so never call `.map`, `.filter`, `.split`, `.splitlines` or `in` on the envelope itself.",
|
|
628
|
+
"- Discovery returns whole objects too: `" + listTool + "` answers `{ tools: [{ name, description, namespace }] }`, `" + searchTool + "` answers `{ tools: [{ name, namespace, description, parameters, output? }], fallback? }`, and `" + describeTool + "` answers `{ found, name?, namespace?, description?, parameters?, output? }`.",
|
|
629
|
+
"- Tool argument objects and `return` values must be lossless JSON: **never leave `undefined` in an object property value** — `{ a: x.opt }` fails the call when `x.opt === undefined`. Omit the key, write `x.opt ?? null`, or print it with `console.log`.",
|
|
613
630
|
"- Prefer batching multi-step read/search chains inside one " + run + " program instead of issuing many sequential single-tool turns.",
|
|
614
631
|
"- Independent read-only calls may overlap under Promise.all; mutating calls run alone, in submission order.",
|
|
615
632
|
"- A failed sub-call rejects; try/catch or Promise.allSettled it to keep the calls that succeeded. Completed calls are real side effects and are not rolled back.",
|
|
@@ -617,8 +634,8 @@ function buildCodemodeSectionText(input) {
|
|
|
617
634
|
"### Discovery API (callable from inside a program)",
|
|
618
635
|
"",
|
|
619
636
|
"- tools." + listTool + "({ namespace? }) - every callable tool as { name, description, namespace }, hidden ones included.",
|
|
620
|
-
"- tools." + searchTool + "({ query, limit?, namespace? }) - ranked matches with their full parameter schemas; the intended first step. Query with the English keywords the tool is likely named and described with (tool names and descriptions are English); a phrase in another language is only matched literally.",
|
|
621
|
-
"- tools." + describeTool + "({ name }) - one tool description
|
|
637
|
+
"- tools." + searchTool + "({ query, limit?, namespace? }) - ranked matches with their full parameter schemas and output schema when the tool declares one; the intended first step. Query with the English keywords the tool is likely named and described with (tool names and descriptions are English); a phrase in another language is only matched literally.",
|
|
638
|
+
"- tools." + describeTool + "({ name }) - one tool description, its complete parameter schema, and its output schema when declared.",
|
|
622
639
|
"",
|
|
623
640
|
"If a hidden tool name is called directly instead of from a program, the call is refused and the refusal names the discovery call to make."
|
|
624
641
|
].join("\n");
|
|
@@ -1188,7 +1205,7 @@ function createTransportDefinition(host, rules) {
|
|
|
1188
1205
|
const owner = host.stateOwner(exec.agent);
|
|
1189
1206
|
let maxSubCalls = rules.options.maxSubCalls;
|
|
1190
1207
|
let attempts = 0;
|
|
1191
|
-
const
|
|
1208
|
+
const lane = createCommitLane(rules.options.maxParallelSubCalls, () => runController.signal.aborted);
|
|
1192
1209
|
const definitions = /* @__PURE__ */ new Map();
|
|
1193
1210
|
for (const entry of catalog) definitions.set(entry.name, entry);
|
|
1194
1211
|
const functions = Object.create(null);
|
|
@@ -1212,36 +1229,58 @@ function createTransportDefinition(host, rules) {
|
|
|
1212
1229
|
name: entry.name,
|
|
1213
1230
|
arguments: loggedArgs
|
|
1214
1231
|
});
|
|
1215
|
-
const
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1232
|
+
const mode = isConcurrencySafe(definitions.get(entry.name), parsed) ? "parallel" : "exclusive";
|
|
1233
|
+
return lane.schedule(mode, {
|
|
1234
|
+
start() {
|
|
1235
|
+
dispatchLog.start();
|
|
1236
|
+
},
|
|
1237
|
+
body: async () => await host.callTool({
|
|
1238
|
+
name: entry.name,
|
|
1239
|
+
args: parsed,
|
|
1240
|
+
callId,
|
|
1241
|
+
signal: nestedSignal,
|
|
1242
|
+
agent: exec.agent,
|
|
1243
|
+
toolSet: exec.toolSet
|
|
1244
|
+
}),
|
|
1245
|
+
/**
|
|
1246
|
+
* 提交阶段:按**提交顺序**逐条执行,落在会话里的副作用因此与提交顺序一致。
|
|
1247
|
+
*
|
|
1248
|
+
* 内核把这几步全部放在同一条有序 lane 里(`ptc.ts` 的 commit 游标):回放内容
|
|
1249
|
+
* 投影、settle 事件、图片与附加上下文、`concludeTurn`、放行互斥屏障。并发的
|
|
1250
|
+
* body 谁先跑完都不影响这一串的顺序——否则界面里的子调用记录会错位,延迟上下文
|
|
1251
|
+
* 也会插到错误的位置。
|
|
1252
|
+
*/
|
|
1253
|
+
async commit(parked) {
|
|
1254
|
+
if (!parked.ok) {
|
|
1255
|
+
dispatchLog.settle({ error: messageOf$1(parked.error) });
|
|
1256
|
+
throw parked.error;
|
|
1257
|
+
}
|
|
1258
|
+
const outcome = parked.value;
|
|
1259
|
+
const isError = outcome.error !== void 0;
|
|
1260
|
+
const content = await shapeDispatchContent(host, {
|
|
1224
1261
|
agent: exec.agent,
|
|
1225
|
-
|
|
1262
|
+
name: entry.name,
|
|
1263
|
+
arguments: loggedArgs,
|
|
1264
|
+
isError,
|
|
1265
|
+
content: clipContent(outcome.content)
|
|
1226
1266
|
});
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1267
|
+
dispatchLog.settle({
|
|
1268
|
+
...outcome,
|
|
1269
|
+
content
|
|
1270
|
+
});
|
|
1271
|
+
if (!isError && Array.isArray(outcome.content) && hasImageBlock(outcome.content)) {
|
|
1272
|
+
const imageMsg = host.createImageContext?.(outcome.content);
|
|
1273
|
+
if (imageMsg !== void 0) exec.deferContext?.(imageMsg);
|
|
1274
|
+
}
|
|
1275
|
+
if (Array.isArray(outcome.additionalContexts)) for (const contextMsg of outcome.additionalContexts) exec.deferContext?.(contextMsg);
|
|
1276
|
+
if (!isError && outcome.concluded === true) exec.concludeTurn?.();
|
|
1277
|
+
if (runController.signal.aborted) throw new Error(runToolName + " run is over; " + entry.name + " result discarded");
|
|
1278
|
+
if (outcome.error !== void 0) throw new Error(outcome.error);
|
|
1279
|
+
return outcome.value !== void 0 ? outcome.value : outcome.text ?? null;
|
|
1280
|
+
},
|
|
1281
|
+
abandon() {
|
|
1282
|
+
throw new Error(runToolName + " run is over; " + entry.name + " tool call abandoned");
|
|
1235
1283
|
}
|
|
1236
|
-
if (Array.isArray(outcome.additionalContexts)) for (const contextMsg of outcome.additionalContexts) exec.deferContext?.(contextMsg);
|
|
1237
|
-
if (outcome.error === void 0 && outcome.concluded === true) exec.concludeTurn?.();
|
|
1238
|
-
if (runController.signal.aborted) throw new Error(runToolName + " run is over; " + entry.name + " result discarded");
|
|
1239
|
-
if (outcome.error !== void 0) throw new Error(outcome.error);
|
|
1240
|
-
return outcome.value !== void 0 ? outcome.value : outcome.text ?? null;
|
|
1241
|
-
};
|
|
1242
|
-
const mode = isConcurrencySafe(definitions.get(entry.name), parsed) ? "parallel" : "exclusive";
|
|
1243
|
-
return scheduler.schedule(mode, run, () => {
|
|
1244
|
-
throw new Error(runToolName + " run is over; " + entry.name + " tool call abandoned");
|
|
1245
1284
|
});
|
|
1246
1285
|
};
|
|
1247
1286
|
Object.defineProperty(functions, entry.name, {
|
|
@@ -1259,6 +1298,8 @@ function createTransportDefinition(host, rules) {
|
|
|
1259
1298
|
* `stateOwner`,避免把单次授权固化到常驻环境中(对齐内核 `ptc.ts`)。
|
|
1260
1299
|
*/
|
|
1261
1300
|
let approvedEscalatedMode;
|
|
1301
|
+
/** 这次运行在入口那边的登记;结算时释放(重跑会重新登记一次)。 */
|
|
1302
|
+
let untrackRun = () => {};
|
|
1262
1303
|
const attempt = async () => {
|
|
1263
1304
|
const authority = await host.runAuthority?.(exec.agent, nestedSignal);
|
|
1264
1305
|
const standingPolicy = authority?.sandboxPolicy;
|
|
@@ -1284,6 +1325,13 @@ function createTransportDefinition(host, rules) {
|
|
|
1284
1325
|
};
|
|
1285
1326
|
escalated = modeOfPolicy(effectivePolicy) !== modeOfPolicy(standingPolicy);
|
|
1286
1327
|
}
|
|
1328
|
+
untrackRun();
|
|
1329
|
+
const runMode = modeOfPolicy(effectivePolicy);
|
|
1330
|
+
untrackRun = host.trackRun?.({
|
|
1331
|
+
agent: exec.agent,
|
|
1332
|
+
controller: runController,
|
|
1333
|
+
...runMode === void 0 ? {} : { mode: runMode }
|
|
1334
|
+
}) ?? (() => {});
|
|
1287
1335
|
const request = {
|
|
1288
1336
|
program: input.code,
|
|
1289
1337
|
bindings: [{
|
|
@@ -1316,7 +1364,8 @@ function createTransportDefinition(host, rules) {
|
|
|
1316
1364
|
}
|
|
1317
1365
|
} finally {
|
|
1318
1366
|
runController.abort(runToolName + " settled");
|
|
1319
|
-
await
|
|
1367
|
+
await lane.drain();
|
|
1368
|
+
untrackRun();
|
|
1320
1369
|
}
|
|
1321
1370
|
if (result.error !== void 0) {
|
|
1322
1371
|
const logsText = Array.isArray(result.logs) && result.logs.length > 0 ? "\nCaptured output:\n" + result.logs.join("\n") : "";
|
|
@@ -1409,7 +1458,7 @@ function createDispatchLog(agent, entry) {
|
|
|
1409
1458
|
write("tool/ptc-dispatch", {
|
|
1410
1459
|
...base,
|
|
1411
1460
|
isError: outcome.error !== void 0,
|
|
1412
|
-
content:
|
|
1461
|
+
content: Array.isArray(outcome.content) ? outcome.content : [],
|
|
1413
1462
|
...outcome.meta === void 0 ? {} : { meta: outcome.meta },
|
|
1414
1463
|
...outcome.errorInfo === void 0 ? {} : { error: outcome.errorInfo }
|
|
1415
1464
|
});
|
|
@@ -1461,7 +1510,10 @@ function readArguments(args) {
|
|
|
1461
1510
|
const record = args;
|
|
1462
1511
|
const code = record["code"];
|
|
1463
1512
|
const description = record["description"];
|
|
1464
|
-
if (typeof code !== "string" || code.trim().length === 0)
|
|
1513
|
+
if (typeof code !== "string" || code.trim().length === 0) {
|
|
1514
|
+
const received = Object.keys(record);
|
|
1515
|
+
throw new Error("codemode: invalid code: expected a non-empty string" + (received.length === 0 ? "" : " (received keys: " + received.join(", ") + "; pass program source in \"code\")"));
|
|
1516
|
+
}
|
|
1465
1517
|
if (typeof description !== "string" || description.trim().length === 0) throw new Error("codemode: invalid description: expected a non-empty string");
|
|
1466
1518
|
const escalation = validateEscalationArgs(record["sandbox_permissions"], record["justification"]);
|
|
1467
1519
|
const timeoutMs = record["timeoutMs"];
|
|
@@ -1497,72 +1549,157 @@ function isConcurrencySafe(entry, args) {
|
|
|
1497
1549
|
}
|
|
1498
1550
|
}
|
|
1499
1551
|
/**
|
|
1500
|
-
*
|
|
1552
|
+
* 子调用的并发、提交顺序与互斥屏障(对齐内核 `ptc.ts` 的 ordered commit lane)。
|
|
1553
|
+
*
|
|
1554
|
+
* - `start()`(记 start 事件 + 启动 body)在 lane 里按提交顺序执行:`parallel` body 之间
|
|
1555
|
+
* 可以并发重叠,最多 `maxParallel` 个同时在飞;
|
|
1556
|
+
* - `commit()`(记 settle 事件 + 转发图片/附加上下文 + `concludeTurn` + 放行互斥屏障)
|
|
1557
|
+
* 按**提交顺序**逐条执行:前一条没提交完,后一条的结果不会先落到会话里;
|
|
1558
|
+
* - `exclusive` 调用等所有在飞 body 结算后独占运行,并独占到自己 commit 完成才放行后续;
|
|
1559
|
+
* - 排队期间若外层运行已中止,直接放弃且不启动任务(未启动调用零 `start` 事件);
|
|
1560
|
+
* - `drain()` 等到 lane 彻底静止(所有 body 结算、所有 commit 完成)才返回。
|
|
1561
|
+
*
|
|
1562
|
+
* 程序拿到的值在 commit 里结算(与内核同):body 结束不保证顺序,提交才是顺序。
|
|
1501
1563
|
*
|
|
1502
|
-
*
|
|
1503
|
-
* -
|
|
1504
|
-
*
|
|
1505
|
-
* - `drain()` 等待全部已启动任务结算。
|
|
1564
|
+
* @param maxParallel - 同时在飞的上限。
|
|
1565
|
+
* @param isAborted - 外层运行是否已中止。
|
|
1566
|
+
* @returns 调度入口与排空。
|
|
1506
1567
|
*/
|
|
1507
|
-
function
|
|
1568
|
+
function createCommitLane(maxParallel, isAborted) {
|
|
1508
1569
|
const cap = Math.max(1, maxParallel);
|
|
1509
1570
|
const queue = [];
|
|
1571
|
+
const commitQueue = [];
|
|
1510
1572
|
const inFlight = /* @__PURE__ */ new Set();
|
|
1511
1573
|
let exclusiveActive = false;
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1574
|
+
let driving = false;
|
|
1575
|
+
let driverRun = Promise.resolve();
|
|
1576
|
+
let wake;
|
|
1577
|
+
const wakeup = () => {
|
|
1578
|
+
const release = wake;
|
|
1579
|
+
wake = void 0;
|
|
1580
|
+
release?.();
|
|
1581
|
+
};
|
|
1582
|
+
/**
|
|
1583
|
+
* 唯一的有序车道:每一轮先提交队首已结算的调用,再在有空位时启动下一个排队调用,
|
|
1584
|
+
* 否则睡到「有 body 结算或有新提交」为止。三条队列全空即静止。
|
|
1585
|
+
*/
|
|
1586
|
+
const drive = () => {
|
|
1587
|
+
if (driving) return driverRun;
|
|
1588
|
+
driving = true;
|
|
1589
|
+
driverRun = (async () => {
|
|
1590
|
+
try {
|
|
1591
|
+
for (;;) {
|
|
1592
|
+
const signal = new Promise((resolve) => {
|
|
1593
|
+
wake = resolve;
|
|
1594
|
+
});
|
|
1595
|
+
const commitHead = commitQueue[0];
|
|
1596
|
+
if (commitHead !== void 0 && commitHead.settled) {
|
|
1597
|
+
commitQueue.shift();
|
|
1598
|
+
await commitHead.commit();
|
|
1599
|
+
if (commitHead.mode === "exclusive") exclusiveActive = false;
|
|
1600
|
+
continue;
|
|
1601
|
+
}
|
|
1602
|
+
const head = queue[0];
|
|
1603
|
+
if (head !== void 0) {
|
|
1604
|
+
if (isAborted()) {
|
|
1605
|
+
queue.shift();
|
|
1606
|
+
head.abandon();
|
|
1607
|
+
continue;
|
|
1608
|
+
}
|
|
1609
|
+
if (!exclusiveActive && (head.mode === "exclusive" ? inFlight.size === 0 : inFlight.size < cap)) {
|
|
1610
|
+
if (head.mode === "exclusive") exclusiveActive = true;
|
|
1611
|
+
queue.shift();
|
|
1612
|
+
commitQueue.push(head);
|
|
1613
|
+
head.start();
|
|
1614
|
+
const flight = head.flight.finally(() => {
|
|
1615
|
+
inFlight.delete(flight);
|
|
1616
|
+
wakeup();
|
|
1617
|
+
});
|
|
1618
|
+
inFlight.add(flight);
|
|
1619
|
+
continue;
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
if (queue.length === 0 && commitQueue.length === 0 && inFlight.size === 0) return;
|
|
1623
|
+
await signal;
|
|
1624
|
+
}
|
|
1625
|
+
} finally {
|
|
1626
|
+
driving = false;
|
|
1627
|
+
wake = void 0;
|
|
1527
1628
|
}
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
head.start();
|
|
1531
|
-
}
|
|
1629
|
+
})();
|
|
1630
|
+
return driverRun;
|
|
1532
1631
|
};
|
|
1533
1632
|
return {
|
|
1534
|
-
schedule(mode, task
|
|
1633
|
+
schedule(mode, task) {
|
|
1535
1634
|
return new Promise((resolve, reject) => {
|
|
1536
|
-
|
|
1635
|
+
let parked;
|
|
1636
|
+
const entry = {
|
|
1537
1637
|
mode,
|
|
1538
|
-
|
|
1638
|
+
settled: false,
|
|
1639
|
+
flight: Promise.resolve(),
|
|
1640
|
+
start() {
|
|
1641
|
+
task.start();
|
|
1642
|
+
entry.flight = (async () => {
|
|
1643
|
+
try {
|
|
1644
|
+
parked = {
|
|
1645
|
+
ok: true,
|
|
1646
|
+
value: await task.body()
|
|
1647
|
+
};
|
|
1648
|
+
} catch (error) {
|
|
1649
|
+
parked = {
|
|
1650
|
+
ok: false,
|
|
1651
|
+
error
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
entry.settled = true;
|
|
1655
|
+
wakeup();
|
|
1656
|
+
})();
|
|
1657
|
+
},
|
|
1658
|
+
async commit() {
|
|
1659
|
+
const result = parked;
|
|
1660
|
+
if (result === void 0) return;
|
|
1539
1661
|
try {
|
|
1540
|
-
|
|
1662
|
+
resolve(await task.commit(result));
|
|
1541
1663
|
} catch (error) {
|
|
1542
1664
|
reject(error);
|
|
1543
1665
|
}
|
|
1544
1666
|
},
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
}
|
|
1551
|
-
inFlight.add(tracked);
|
|
1667
|
+
abandon() {
|
|
1668
|
+
try {
|
|
1669
|
+
task.abandon();
|
|
1670
|
+
} catch (error) {
|
|
1671
|
+
reject(error);
|
|
1672
|
+
}
|
|
1552
1673
|
}
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1674
|
+
};
|
|
1675
|
+
queue.push(entry);
|
|
1676
|
+
wakeup();
|
|
1677
|
+
drive();
|
|
1555
1678
|
});
|
|
1556
1679
|
},
|
|
1557
1680
|
async drain() {
|
|
1558
|
-
|
|
1559
|
-
while (inFlight.size > 0) {
|
|
1560
|
-
await Promise.allSettled([...inFlight]);
|
|
1561
|
-
pump();
|
|
1562
|
-
}
|
|
1681
|
+
await drive();
|
|
1563
1682
|
}
|
|
1564
1683
|
};
|
|
1565
1684
|
}
|
|
1685
|
+
/**
|
|
1686
|
+
* 回放内容的投影:先按日志上限裁剪,再交给入口的 `tools/ptc-dispatch-log` 瀑布。
|
|
1687
|
+
*
|
|
1688
|
+
* 瀑布失败**不**影响这条记录:回落裁剪后的原始内容(内核 `ptc.ts` 同此)。
|
|
1689
|
+
*/
|
|
1690
|
+
async function shapeDispatchContent(host, dispatch) {
|
|
1691
|
+
if (typeof host.shapeDispatchLog !== "function") return dispatch.content;
|
|
1692
|
+
try {
|
|
1693
|
+
const shaped = await host.shapeDispatchLog(dispatch);
|
|
1694
|
+
return Array.isArray(shaped) ? shaped : dispatch.content;
|
|
1695
|
+
} catch {
|
|
1696
|
+
return dispatch.content;
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
/** 一个抛出值的消息。 */
|
|
1700
|
+
function messageOf$1(error) {
|
|
1701
|
+
return error instanceof Error ? error.message : String(error);
|
|
1702
|
+
}
|
|
1566
1703
|
const JSON_INDENT = " ";
|
|
1567
1704
|
const MAX_JSON_INDENT_CHARS = 10;
|
|
1568
1705
|
/** 迭代式 JSON 渲染:与内核 `ptc.ts` 一致,深于 5 层后自动紧凑化,防止深层缩进膨胀。 */
|
|
@@ -1744,6 +1881,76 @@ function apply(ctx, config = {}) {
|
|
|
1744
1881
|
if (typeof context.effect === "function") context.effect(() => () => {
|
|
1745
1882
|
retention.dispose();
|
|
1746
1883
|
}, "codemode retained environments");
|
|
1884
|
+
/** 在飞运行:会话键 → 该会话里正在跑的程序。 */
|
|
1885
|
+
const activeRuns = /* @__PURE__ */ new Map();
|
|
1886
|
+
/**
|
|
1887
|
+
* 登记一只在飞运行,返回运行结算时的 disposer。
|
|
1888
|
+
*
|
|
1889
|
+
* 传输层每轮运行(含常驻环境换策略后的重跑)都登记一次,入口因此知道「这一轮跑在哪个
|
|
1890
|
+
* 会话、当时的沙箱模式是什么」。会话切模式时,模式不匹配的运行必须停:常驻环境换策略
|
|
1891
|
+
* 会抛错,而已经跑起来的那一轮不能继续用旧权限往下写。
|
|
1892
|
+
*/
|
|
1893
|
+
const trackRun = (run) => {
|
|
1894
|
+
const key = sessionKeyOf(run.agent);
|
|
1895
|
+
if (key === void 0) return () => void 0;
|
|
1896
|
+
const record = {
|
|
1897
|
+
controller: run.controller,
|
|
1898
|
+
...run.mode === void 0 ? {} : { mode: run.mode }
|
|
1899
|
+
};
|
|
1900
|
+
let bucket = activeRuns.get(key);
|
|
1901
|
+
if (bucket === void 0) {
|
|
1902
|
+
bucket = /* @__PURE__ */ new Set();
|
|
1903
|
+
activeRuns.set(key, bucket);
|
|
1904
|
+
}
|
|
1905
|
+
bucket.add(record);
|
|
1906
|
+
return () => {
|
|
1907
|
+
const current = activeRuns.get(key);
|
|
1908
|
+
if (current === void 0) return;
|
|
1909
|
+
current.delete(record);
|
|
1910
|
+
if (current.size === 0) activeRuns.delete(key);
|
|
1911
|
+
};
|
|
1912
|
+
};
|
|
1913
|
+
/**
|
|
1914
|
+
* 会话的 `sandbox/mode` 事件:生效模式已经变了的在飞运行直接取消(内核 `PtcCalls` 同此)。
|
|
1915
|
+
*
|
|
1916
|
+
* 只取消「能确认模式变了」的那些:拿不到模式(运行时不管沙箱)或模式相同的运行一律不动。
|
|
1917
|
+
*/
|
|
1918
|
+
const cancelOnModeChange = (session, event) => {
|
|
1919
|
+
if (typeof event !== "object" || event === null) return;
|
|
1920
|
+
const record = event;
|
|
1921
|
+
if (record.type !== "sandbox/mode") return;
|
|
1922
|
+
const mode = (typeof record.data === "object" && record.data !== null ? record.data : void 0)?.mode;
|
|
1923
|
+
if (typeof mode !== "string") return;
|
|
1924
|
+
const key = sessionKeyOf(session);
|
|
1925
|
+
if (key === void 0) return;
|
|
1926
|
+
for (const run of activeRuns.get(key) ?? []) if (run.mode !== void 0 && run.mode !== mode) run.controller.abort(/* @__PURE__ */ new Error("sandbox mode changed"));
|
|
1927
|
+
};
|
|
1928
|
+
if (typeof context.on === "function") try {
|
|
1929
|
+
const off = context.on("session/event", cancelOnModeChange, { global: true });
|
|
1930
|
+
if (typeof context.effect === "function") context.effect(() => () => {
|
|
1931
|
+
if (typeof off === "function") off();
|
|
1932
|
+
}, "codemode sandbox mode cancellation");
|
|
1933
|
+
} catch (error) {
|
|
1934
|
+
log("warn", "[codemode] could not subscribe to session/event; an in-flight code_run keeps running after the session sandbox mode changes: " + messageOf(error));
|
|
1935
|
+
}
|
|
1936
|
+
/**
|
|
1937
|
+
* 把一条子调用回放记录交给 `tools/ptc-dispatch-log` 瀑布(内核 `ptc.ts` 的同名钩子)。
|
|
1938
|
+
*
|
|
1939
|
+
* 监听器的失败必须在这里吞掉:它决定的只是日志副本长什么样,不是程序拿到的结果。内核
|
|
1940
|
+
* 用 `scopeTarget(tools, agent)` 当接收方;本包不依赖 `@deepseek-ai/dsh-scope`,直接以
|
|
1941
|
+
* `tools` 为接收方——作用域过滤由宿主自己的监听器完成。
|
|
1942
|
+
*/
|
|
1943
|
+
const shapeDispatchLog = async (dispatch) => {
|
|
1944
|
+
const waterfall = context.waterfall;
|
|
1945
|
+
if (typeof waterfall !== "function") return dispatch.content;
|
|
1946
|
+
try {
|
|
1947
|
+
const shaped = await waterfall.call(context, tools, "tools/ptc-dispatch-log", dispatch, () => Promise.resolve(dispatch.content));
|
|
1948
|
+
return Array.isArray(shaped) ? shaped : dispatch.content;
|
|
1949
|
+
} catch (error) {
|
|
1950
|
+
log("warn", "tools: ptc-dispatch-log listener failed for " + dispatch.name + ": " + messageOf(error) + "; logging the original settled content");
|
|
1951
|
+
return dispatch.content;
|
|
1952
|
+
}
|
|
1953
|
+
};
|
|
1747
1954
|
const scopeKey = findScopeKey(ctx);
|
|
1748
1955
|
if (rules.options.nativeScope && scopeKey === void 0) log("warn", "[codemode] no registry scope tag on this context: masking falls back to name rules only (" + rules.options.hiddenPrefixes.join(", ") + "). Mount this row inside an agent preset to hide every tool the preset does not own.");
|
|
1749
1956
|
/** 最近一次 describe 收到的完整定义集(含被遮蔽的)。 */
|
|
@@ -1794,6 +2001,8 @@ function apply(ctx, config = {}) {
|
|
|
1794
2001
|
source: { kind: PLUGIN_MESSAGE_SOURCE_KIND }
|
|
1795
2002
|
});
|
|
1796
2003
|
},
|
|
2004
|
+
trackRun,
|
|
2005
|
+
shapeDispatchLog,
|
|
1797
2006
|
async callTool(request) {
|
|
1798
2007
|
const call = {
|
|
1799
2008
|
type: "tool-call",
|
|
@@ -1884,10 +2093,13 @@ function toCatalogEntry(definition) {
|
|
|
1884
2093
|
if (typeof toolName !== "string" || toolName.length === 0) return void 0;
|
|
1885
2094
|
const description = typeof record["description"] === "string" ? record["description"] : "";
|
|
1886
2095
|
const safe = record["isConcurrencySafe"];
|
|
2096
|
+
const declaredOutput = record["output"];
|
|
2097
|
+
const output = typeof declaredOutput === "object" && declaredOutput !== null ? declaredOutput.schema : void 0;
|
|
1887
2098
|
return {
|
|
1888
2099
|
name: toolName,
|
|
1889
2100
|
description,
|
|
1890
2101
|
parameters: record["parameters"],
|
|
2102
|
+
...output === void 0 ? {} : { output },
|
|
1891
2103
|
...typeof safe === "function" ? { isConcurrencySafe: safe } : {}
|
|
1892
2104
|
};
|
|
1893
2105
|
}
|
|
@@ -1933,6 +2145,24 @@ function errorInfoOf(result) {
|
|
|
1933
2145
|
function nonEmptyString(value) {
|
|
1934
2146
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1935
2147
|
}
|
|
2148
|
+
/**
|
|
2149
|
+
* 一个会话的身份键。
|
|
2150
|
+
*
|
|
2151
|
+
* 优先用会话 id(与内核 `session/event` 监听器里的 `session.id` 对齐);取不到 id 时退回
|
|
2152
|
+
* 会话对象本身——调用方 agent 上的 `session` 与事件里的会话是同一个对象,靠身份比对同样
|
|
2153
|
+
* 成立。连会话都没有时返回 `undefined`,这类运行不参与取消。
|
|
2154
|
+
*
|
|
2155
|
+
* @param value - 一个 agent,或一个会话对象。
|
|
2156
|
+
* @returns 会话 id / 会话对象,或 `undefined`。
|
|
2157
|
+
*/
|
|
2158
|
+
function sessionKeyOf(value) {
|
|
2159
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
2160
|
+
const nested = value.session;
|
|
2161
|
+
const session = typeof nested === "object" && nested !== null ? nested : value;
|
|
2162
|
+
const id = session.id;
|
|
2163
|
+
if (typeof id === "string" && id.length > 0) return id;
|
|
2164
|
+
return session;
|
|
2165
|
+
}
|
|
1936
2166
|
/** 把内容块里的文本拼起来。 */
|
|
1937
2167
|
function textOf(content) {
|
|
1938
2168
|
if (!Array.isArray(content)) return "";
|