@feng0531/dsh-execution-bus 0.2.12 → 0.4.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/README.md +18 -6
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/cordis.patch.yml +4 -23
- package/dist/bus.d.ts +27 -0
- package/dist/bus.js +92 -18
- package/dist/diagnostics.d.ts +38 -0
- package/dist/diagnostics.js +26 -0
- package/dist/execution.d.ts +61 -0
- package/dist/execution.js +174 -0
- package/dist/plugin.js +69 -141
- package/dist/preset.d.ts +12 -0
- package/dist/preset.js +73 -0
- package/dist/test-mode.d.ts +1 -1
- package/dist/test-mode.js +8 -17
- package/docs/FLOW-TEST.md +2 -1
- package/docs/OPTIMIZATION.md +47 -0
- package/docs/host-compatibility.md +5 -3
- package/docs/protocol.md +10 -4
- package/docs/releases/0.3.0.md +21 -0
- package/docs/releases/0.4.0.md +17 -0
- package/docs/validation.md +5 -5
- package/package.json +19 -4
package/dist/plugin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { ExecutionOwner } from "./execution.js";
|
|
3
3
|
import { ExecutionBus, parse } from "./bus.js";
|
|
4
4
|
import { testHandler, readHandler } from "./handlers.js";
|
|
5
5
|
export const name = "execution-bus";
|
|
@@ -8,12 +8,8 @@ export function apply(ctx) {
|
|
|
8
8
|
const bus = new ExecutionBus();
|
|
9
9
|
bus.register("test", testHandler);
|
|
10
10
|
bus.register("host.read", readHandler);
|
|
11
|
-
const
|
|
12
|
-
ctx.effect(() =>
|
|
13
|
-
await bus.close();
|
|
14
|
-
await Promise.allSettled(sharedJobs.values());
|
|
15
|
-
sharedJobs.clear();
|
|
16
|
-
});
|
|
11
|
+
const executions = new ExecutionOwner();
|
|
12
|
+
ctx.effect(() => () => executions.close(() => bus.close()));
|
|
17
13
|
ctx.tools.register({
|
|
18
14
|
name: "execution_bus",
|
|
19
15
|
description: "萧何 / Xiao He. Submit a version 1 request and WAIT for its terminal result in this tool call. Reuse requestId only for the same logical operation. Handlers: test {delayMs:0..30000,fail:boolean}, host.read {file_path,offset?,limit?<=100}. Control types status/result/cancel use empty payload. Acceptance is volatile, not execution success. No polling needed; no restart resume.",
|
|
@@ -42,147 +38,79 @@ export function apply(ctx) {
|
|
|
42
38
|
schema: {},
|
|
43
39
|
render: (_a, v) => [{ type: "text", text: JSON.stringify(v) }],
|
|
44
40
|
},
|
|
45
|
-
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
41
|
+
execute(raw, exec) {
|
|
42
|
+
if (executions.phase !== "open")
|
|
43
|
+
return Promise.reject(Error("CLOSED"));
|
|
44
|
+
return executions.track(dispatch(raw, exec));
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
async function dispatch(raw, exec) {
|
|
48
|
+
if (!exec.agent)
|
|
49
|
+
throw Error("OWNING_SESSION_REQUIRED");
|
|
50
|
+
const owner = {
|
|
51
|
+
sessionId: String(exec.agent.id),
|
|
52
|
+
callId: String(exec.callId),
|
|
53
|
+
}, m = parse(raw);
|
|
54
|
+
if (m.type !== "request")
|
|
55
|
+
return bus.command(owner, m);
|
|
56
|
+
let projection;
|
|
57
|
+
const attached = bus.submit(owner, m, (event) => projection?.progress(event), async (input, signal) => {
|
|
58
|
+
const result = await ctx.tools.execute({
|
|
59
|
+
name: "read",
|
|
60
|
+
callId: (String(exec.callId) +
|
|
61
|
+
":bus:" +
|
|
62
|
+
randomUUID()),
|
|
63
|
+
rootCallId: exec.rootCallId,
|
|
64
|
+
parent: exec.token,
|
|
65
|
+
agent: exec.agent,
|
|
66
|
+
arguments: input,
|
|
67
|
+
signal,
|
|
72
68
|
});
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
finally {
|
|
82
|
-
attached.detach();
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
const abort = () => attached.cancel();
|
|
86
|
-
const jobKey = JSON.stringify([owner.sessionId, m.requestId]);
|
|
87
|
-
let settled = sharedJobs.get(jobKey);
|
|
88
|
-
if (!settled) {
|
|
89
|
-
try {
|
|
90
|
-
const outcome = attached.done.then((v) => ({
|
|
91
|
-
status: v.state === "succeeded"
|
|
92
|
-
? "completed"
|
|
93
|
-
: v.state === "cancelled"
|
|
94
|
-
? "killed"
|
|
95
|
-
: "failed",
|
|
96
|
-
detail: v.state,
|
|
97
|
-
}));
|
|
98
|
-
const job = ctx.jobs.start({
|
|
99
|
-
kind: "execution-bus",
|
|
100
|
-
label: "萧何 / Xiao He",
|
|
101
|
-
owner: exec.agent.id,
|
|
102
|
-
run(h) {
|
|
103
|
-
handle = h;
|
|
104
|
-
return {
|
|
105
|
-
done: outcome,
|
|
106
|
-
cancel: abort,
|
|
107
|
-
};
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
const ownerId = exec.agent.id;
|
|
111
|
-
// One registered host waiter suppresses extra completion messages for every attachment.
|
|
112
|
-
settled = (async () => {
|
|
113
|
-
try {
|
|
114
|
-
for (;;) {
|
|
115
|
-
const view = await ctx.jobs.wait(job, 60000, ownerId);
|
|
116
|
-
if (view.status !== "running" && view.status !== "stopping")
|
|
117
|
-
break;
|
|
118
|
-
}
|
|
119
|
-
ctx.jobs.remove(job, ownerId);
|
|
120
|
-
}
|
|
121
|
-
catch (error) {
|
|
122
|
-
// rc.2 has no typed not-found code. Match only this minted Job and
|
|
123
|
-
// confirm absence in the original owner scope; never swallow access/service errors.
|
|
124
|
-
let alreadyRemoved = false;
|
|
125
|
-
try {
|
|
126
|
-
alreadyRemoved =
|
|
127
|
-
error instanceof Error &&
|
|
128
|
-
error.message === `unknown job ${job}` &&
|
|
129
|
-
!ctx.jobs.list(ownerId).some((view) => view.id === job);
|
|
130
|
-
}
|
|
131
|
-
catch {
|
|
132
|
-
/* Preserve the original wait/cleanup error. */
|
|
133
|
-
}
|
|
134
|
-
// A broken wait channel does not terminate execution. Keep caller cancellation
|
|
135
|
-
// attached, drain both producer and host outcome, then attempt cleanup once.
|
|
136
|
-
await attached.done;
|
|
137
|
-
if (!alreadyRemoved) {
|
|
138
|
-
// No scope-owned observer: teardown can remove it before settlement.
|
|
139
|
-
// After producer drain, allow a bounded host commit window. This is
|
|
140
|
-
// program-side cleanup only, never execution retry or model polling.
|
|
141
|
-
for (let attempt = 0; attempt < 20; attempt++) {
|
|
142
|
-
try {
|
|
143
|
-
const view = ctx.jobs
|
|
144
|
-
.list(ownerId)
|
|
145
|
-
.find((view) => view.id === job && view.owner === ownerId);
|
|
146
|
-
if (!view)
|
|
147
|
-
break;
|
|
148
|
-
if (view.status !== "running" &&
|
|
149
|
-
view.status !== "stopping") {
|
|
150
|
-
ctx.jobs.remove(job, ownerId);
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
catch {
|
|
155
|
-
break;
|
|
156
|
-
}
|
|
157
|
-
await pause(10);
|
|
158
|
-
}
|
|
159
|
-
throw error;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
})();
|
|
163
|
-
sharedJobs.set(jobKey, settled);
|
|
164
|
-
void settled.then(() => sharedJobs.delete(jobKey), () => sharedJobs.delete(jobKey));
|
|
165
|
-
}
|
|
166
|
-
catch {
|
|
167
|
-
abort();
|
|
168
|
-
await attached.done;
|
|
169
|
-
attached.detach();
|
|
170
|
-
throw Error("HOST_JOB_UNAVAILABLE");
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
exec.signal.addEventListener("abort", abort, { once: true });
|
|
174
|
-
if (exec.signal.aborted)
|
|
175
|
-
abort();
|
|
69
|
+
if (result.isError)
|
|
70
|
+
throw Error("HOST_READ_FAILED");
|
|
71
|
+
return result.value;
|
|
72
|
+
});
|
|
73
|
+
// Terminal reuse is a read of an existing result, not admission of new work.
|
|
74
|
+
if (attached.accepted.payload.state !== "queued" &&
|
|
75
|
+
attached.accepted.payload.state !== "running") {
|
|
176
76
|
try {
|
|
177
|
-
await settled;
|
|
178
77
|
const value = await attached.done;
|
|
179
78
|
attached.returned();
|
|
180
79
|
return value;
|
|
181
80
|
}
|
|
182
81
|
finally {
|
|
183
|
-
exec.signal.removeEventListener("abort", abort);
|
|
184
82
|
attached.detach();
|
|
185
83
|
}
|
|
186
|
-
}
|
|
187
|
-
|
|
84
|
+
}
|
|
85
|
+
const abort = () => attached.cancel();
|
|
86
|
+
const jobKey = JSON.stringify([owner.sessionId, m.requestId]);
|
|
87
|
+
let execution = executions.get(jobKey);
|
|
88
|
+
if (execution)
|
|
89
|
+
attached.detach(); // only the first attachment projects progress into the shared Job
|
|
90
|
+
if (!execution) {
|
|
91
|
+
try {
|
|
92
|
+
execution = executions.create(jobKey, ctx.jobs, exec.agent.id, attached);
|
|
93
|
+
projection = execution;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
abort();
|
|
97
|
+
await attached.done;
|
|
98
|
+
attached.detach();
|
|
99
|
+
throw Error("HOST_JOB_UNAVAILABLE");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
exec.signal.addEventListener("abort", abort, { once: true });
|
|
103
|
+
if (exec.signal.aborted)
|
|
104
|
+
abort();
|
|
105
|
+
try {
|
|
106
|
+
await execution.settled;
|
|
107
|
+
const value = await attached.done;
|
|
108
|
+
attached.returned();
|
|
109
|
+
return value;
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
exec.signal.removeEventListener("abort", abort);
|
|
113
|
+
attached.detach();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
188
116
|
}
|
package/dist/preset.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Context, Service } from "@deepseek-ai/cordis";
|
|
2
|
+
import type { PresetDefinition } from "@deepseek-ai/dsh-agent-preset-registry";
|
|
3
|
+
export declare const PRESET_ID = "execution-bus-test";
|
|
4
|
+
/** Capture the host-declared composition without evaluating !!js or changing disabled rows. */
|
|
5
|
+
export declare function derivePreset(content: string): PresetDefinition;
|
|
6
|
+
/** Host-plane registrar. readDocument is a public serialized declaration, not a private registry read. */
|
|
7
|
+
export default class XiaoHePreset {
|
|
8
|
+
private ctx;
|
|
9
|
+
static inject: string[];
|
|
10
|
+
constructor(ctx: Context);
|
|
11
|
+
[Service.init](): AsyncGenerator<() => Promise<void>, void, unknown>;
|
|
12
|
+
}
|
package/dist/preset.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Service } from "@deepseek-ai/cordis";
|
|
2
|
+
import { entryListProblem } from "@deepseek-ai/dsh-agent-preset-registry";
|
|
3
|
+
import { entryListSchema } from "@deepseek-ai/cordis-plugin-include";
|
|
4
|
+
import { load } from "js-yaml";
|
|
5
|
+
import { setTimeout as pause } from "node:timers/promises";
|
|
6
|
+
export const PRESET_ID = "execution-bus-test";
|
|
7
|
+
/** Capture the host-declared composition without evaluating !!js or changing disabled rows. */
|
|
8
|
+
export function derivePreset(content) {
|
|
9
|
+
if (Buffer.byteLength(content) > 1048576)
|
|
10
|
+
throw Error("PTC_PRESET_TOO_LARGE");
|
|
11
|
+
const plugins = load(content, { schema: entryListSchema });
|
|
12
|
+
const problem = entryListProblem(plugins);
|
|
13
|
+
if (problem)
|
|
14
|
+
throw Error("Invalid PTC preset: " + problem);
|
|
15
|
+
const rows = plugins;
|
|
16
|
+
const inspect = (entries) => {
|
|
17
|
+
for (const row of entries) {
|
|
18
|
+
if (row.id === "xiao-he-execution-bus" ||
|
|
19
|
+
row.name?.startsWith("@feng0531/dsh-execution-bus"))
|
|
20
|
+
throw Error("PTC_PRESET_ALREADY_CONTAINS_BUS");
|
|
21
|
+
// readDocument does not publish the declaring baseUrl. Do not silently
|
|
22
|
+
// rebase relative modules/includes into the registrar's different context.
|
|
23
|
+
if (row.name?.startsWith(".") ||
|
|
24
|
+
row.name === "@deepseek-ai/cordis-plugin-include")
|
|
25
|
+
throw Error("PTC_PRESET_RELATIVE_BASE_UNSUPPORTED");
|
|
26
|
+
if (row.group && Array.isArray(row.config))
|
|
27
|
+
inspect(row.config);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
inspect(rows);
|
|
31
|
+
return {
|
|
32
|
+
id: PRESET_ID,
|
|
33
|
+
name: "萧何 / Xiao He",
|
|
34
|
+
order: 20,
|
|
35
|
+
description: "继承宿主 PTC 工具组合,叠加执行请求排队、等待与结果回传。普通任务使用原工具,授权和沙箱由宿主管理;不支持重启恢复。",
|
|
36
|
+
plugins: [
|
|
37
|
+
...rows,
|
|
38
|
+
{
|
|
39
|
+
id: "xiao-he-execution-bus",
|
|
40
|
+
name: "@feng0531/dsh-execution-bus/test-mode",
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** Host-plane registrar. readDocument is a public serialized declaration, not a private registry read. */
|
|
46
|
+
export default class XiaoHePreset {
|
|
47
|
+
ctx;
|
|
48
|
+
static inject = ["agentPresets"];
|
|
49
|
+
constructor(ctx) {
|
|
50
|
+
this.ctx = ctx;
|
|
51
|
+
}
|
|
52
|
+
async *[Service.init]() {
|
|
53
|
+
let document;
|
|
54
|
+
for (let attempt = 0; attempt < 50; attempt++) {
|
|
55
|
+
try {
|
|
56
|
+
document = await this.ctx.agentPresets.readDocument("ptc");
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (!(error instanceof Error) ||
|
|
61
|
+
!("code" in error) ||
|
|
62
|
+
error.code !== "agent-preset/not-found")
|
|
63
|
+
throw error;
|
|
64
|
+
if (attempt === 49)
|
|
65
|
+
throw Error("Xiao He requires a declared ptc preset. Load the host PTC preset before this bundle.");
|
|
66
|
+
await pause(100);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (!document)
|
|
70
|
+
throw Error("PTC_PRESET_UNAVAILABLE");
|
|
71
|
+
yield await this.ctx.agentPresets.register(derivePreset(document.content));
|
|
72
|
+
}
|
|
73
|
+
}
|
package/dist/test-mode.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Context } from "@deepseek-ai/cordis";
|
|
2
2
|
export declare const name = "execution-bus-test-mode";
|
|
3
3
|
export declare const inject: string[];
|
|
4
|
-
/**
|
|
4
|
+
/** An additive preset row: presentation, tool inventory and authority belong to the host PTC composition. */
|
|
5
5
|
export declare function apply(ctx: Context): void;
|
package/dist/test-mode.js
CHANGED
|
@@ -1,24 +1,15 @@
|
|
|
1
1
|
import { scopeOf } from "@deepseek-ai/dsh-scope";
|
|
2
2
|
import { apply as applyBus } from "./plugin.js";
|
|
3
|
-
export const name = "execution-bus-test-mode";
|
|
4
|
-
export const inject = ["tools", "jobs"];
|
|
5
|
-
/**
|
|
3
|
+
export const name = "execution-bus-test-mode"; // legacy export identity, intentionally stable
|
|
4
|
+
export const inject = ["tools", "jobs", "systemPrompt"];
|
|
5
|
+
/** An additive preset row: presentation, tool inventory and authority belong to the host PTC composition. */
|
|
6
6
|
export function apply(ctx) {
|
|
7
7
|
if (scopeOf(ctx) === undefined)
|
|
8
8
|
throw Error("Xiao He mode requires an Agent preset scope");
|
|
9
|
-
ctx.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"read",
|
|
13
|
-
|
|
14
|
-
"job_output",
|
|
15
|
-
"job_kill",
|
|
16
|
-
]);
|
|
17
|
-
// Scope-local file suite also declares write/edit; the execution guard denies them.
|
|
18
|
-
// restrict() only accepts known GLOBAL tool names, so it cannot filter sibling preset rows.
|
|
19
|
-
// Restriction filters inherited tools; a guard also fences tools registered by sibling preset rows.
|
|
20
|
-
ctx.tools.guard((exec) => allowed.has(exec.name)
|
|
21
|
-
? undefined
|
|
22
|
-
: "Xiao He mode allows only bus, read and job controls");
|
|
9
|
+
ctx.effect(() => ctx.systemPrompt.section({
|
|
10
|
+
name: "XIAO_HE_EXECUTION_BUS",
|
|
11
|
+
order: 1700,
|
|
12
|
+
text: "你正在萧何模式中。这不是角色扮演。保留宿主 PTC 的日常工具能力与权限规则。普通编辑、搜索、命令、安装和其他工作使用原工具,不要声称此模式只读。execution_bus 是可选协调入口,目前只支持 test 与 host.read,不支持任意命令,不要强制所有任务走总线。通过 run_code 调用 await tools.execution_bus(...),受原有时间预算约束。总线请求只提交一次并等待结果,不循环查询;同一逻辑操作重发沿用 requestId。cancelRequested 不等于已取消,unknown 不代表执行失败,不承诺跨重启恢复。后台命令仅返回 Job ID 不代表命令已经完成。",
|
|
13
|
+
}));
|
|
23
14
|
applyBus(ctx);
|
|
24
15
|
}
|
package/docs/FLOW-TEST.md
CHANGED
|
@@ -35,7 +35,8 @@ npm run test:install
|
|
|
35
35
|
- 已受理 Handler 与替换注册并存,旧注销函数不破坏新注册。
|
|
36
36
|
- 宿主 read 真正经过工具管线;前置拒绝仍生效。
|
|
37
37
|
- Job 先行移除、等待/清理异常与卸载交错。
|
|
38
|
-
-
|
|
38
|
+
- 派生预设成功激活、原 ptc 声明不变、普通 Agent 不可见总线、write/edit 保留且宿主策略可拒绝。
|
|
39
|
+
- 真实 Node PTC 进程调用原 read 与总线;测试使用显式非约束策略,不代表桌面沙箱通过。
|
|
39
40
|
|
|
40
41
|
以上是场景摘要,不表示真实模型/宿主 GUI 已通过。
|
|
41
42
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# 引擎经验优化整合与验收
|
|
2
|
+
|
|
3
|
+
本轮统一在 0.4.0 交付,不将普通 BUG 修复冒充整体架构优化,不声称模型性能已提高。运行逻辑仅作用于萧何总线及其 Job 关联;保留 0.3.0 的 PTC 派生方式、权限和原工具能力。
|
|
4
|
+
|
|
5
|
+
## 优先级与交付
|
|
6
|
+
|
|
7
|
+
| 优先级 | 交付 | 范围与限制 |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| P1 | 执行所有权集中 | 内部 ExecutionHandle 关联 core completion、请求级取消、宿主 Job 和进度;ExecutionOwner 集中跟踪共享投影及工具交付。不是第二个执行状态机 |
|
|
10
|
+
| P1 | 阶段化关闭 | 停止受理 → 请求取消及排空 core → 等待活跃工具交付 → 等待并释放 Job 关联。重复关闭返回同一 Promise,先发布 closing 再触发取消,允许回调重入 |
|
|
11
|
+
| P1 | 通知批次边界 | 每次固定名单;新增监听从后续事件接收;解除的监听跳过;嵌套事件按请求内序号排在当前批之后。同步与异步错误继续隔离 |
|
|
12
|
+
| P2 | 有界脱敏诊断 | core diagnostics 快照及内部 Job 聚合轨迹。每个环最近 128 条;只存数值编号、阶段、耗时、计数,不存业务内容 |
|
|
13
|
+
| P2 | 会话轮转 | 两会话积压测试确认 FIFO 的队头延迟后,实现受理后轮转;保持会话内 FIFO 和并发上限,无入队公平保证或时间 SLA |
|
|
14
|
+
| 暂缓 | 按资源串行 | 没有真实写 Handler,不引入资源锁;PTC 原工具不被全局接管 |
|
|
15
|
+
| 不做 | 工作流/线程系统 | 不加入 DAG、线程池、无锁队列、对象池或按引用数自动取消 |
|
|
16
|
+
|
|
17
|
+
## 实测证据及不能推导的结论
|
|
18
|
+
|
|
19
|
+
确定性压力用例中,A0 已运行,A1–A12 排队后 B0 入队:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
原 FIFO: A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12,B0
|
|
23
|
+
会话轮转:A0,A1,B0,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
这是已受理任务的开始顺序对比,不是生产延迟、吞吐或模型轮次提升比例。运行任务不可抢占,新会话可能仍因队列满被拒绝。同一会话多个长任务也可能占用并发槽,轮转不等于资源配额。
|
|
27
|
+
|
|
28
|
+
另外验证三会话、并发为二、取消一个排队任务:只执行其余 23 个不同请求,未超过并发上限,同会话开始顺序保持 FIFO。已有真实宿主等待/清理故障与卸载回归继续执行,不以纯 core 测试替代。
|
|
29
|
+
|
|
30
|
+
## 如何查看诊断
|
|
31
|
+
|
|
32
|
+
从包的 ./core 使用 ExecutionBus,调用 `bus.diagnostics()`。这不是新的 Agent 工具,也不提供跨会话读取入口;生产插件内部所有者的诊断尚未公开为服务或 UI。
|
|
33
|
+
|
|
34
|
+
阶段 elapsedMs 相对于本次受理的单调时钟时间:started 减 accepted 为排队时间、terminal 减 started 为执行时间、returned 减 terminal 为首次返回标记延迟。cancel_requested 与 terminal 间隔只能解释为取消请求到执行终态,未必为成功取消。
|
|
35
|
+
|
|
36
|
+
查询快照会遍历最多 recordLimit 条记录计算保留结果字节数;只在开发者调用时发生。环形轨迹不会记录结果内容,但仍是内部运行信息,不作为可信审计/授权依据。进程关闭后不恢复。
|
|
37
|
+
|
|
38
|
+
## 引擎经验来源
|
|
39
|
+
|
|
40
|
+
- [Bevy Task v0.16.1](https://github.com/bevyengine/bevy/blob/v0.16.1/crates/bevy_tasks/src/task.rs):区分任务完成、取消和 detach;没有照搬 Rust Drop 语义。
|
|
41
|
+
- [Bevy CommandQueue v0.16.1](https://github.com/bevyengine/bevy/blob/v0.16.1/crates/bevy_ecs/src/world/command_queue.rs):先确定消费边界再调用外部代码;未移植 unsafe/字节布局。
|
|
42
|
+
- [Godot 4.4 MessageQueue](https://github.com/godotengine/godot/blob/4.4/core/object/message_queue.cpp):派发重入与消费位置的明确约束。
|
|
43
|
+
- [Godot 4.4 WorkerThreadPool](https://github.com/godotengine/godot/blob/4.4/core/object/worker_thread_pool.h):分阶段关闭;没有增加线程池。
|
|
44
|
+
- [Unity Addressables](https://docs.unity3d.com/Packages/com.unity.addressables@1.21/manual/AddressableAssetsAsyncOperationHandle.html):区分异步操作所有权、释放和取消,不增加模拟指针引用计数。
|
|
45
|
+
- [Stride Scheduler](https://github.com/stride3d/stride/blob/master/sources/core/Stride.Core.MicroThreading/Scheduler.cs)、[O3DE JobCancelGroup](https://github.com/o3de/o3de/blob/development/Code/Framework/AzCore/AzCore/Jobs/JobCancelGroup.h):作为调研参考,分支链接可变;不宣称固定 SHA 或已构建这些引擎。
|
|
46
|
+
|
|
47
|
+
本项目是依据原则自行实现,没有复制这些引擎的源码或引入其运行依赖。
|
|
@@ -23,7 +23,7 @@ AgentLoop 发起工具调用
|
|
|
23
23
|
|
|
24
24
|
### 与 PTC 的关系
|
|
25
25
|
|
|
26
|
-
PTC 是可选入口:外层 `run_code`
|
|
26
|
+
PTC 是可选入口:外层 `run_code` 可以调用同一个工具。它不是总线本身,也不会因使用本插件取消自身时间预算。0.3.0 起,“萧何”从目标宿主 ptc 的公开声明派生,保留呈现与全部声明行,不再强制 native 或施加只读 guard。派生组合加入可选总线,其他预设不变。真实 Node PTC 隔离进程已验证原 read 与总线共存;不是桌面/真实模型/实际沙箱的全链路验收。
|
|
27
27
|
|
|
28
28
|
## 源码证据定位
|
|
29
29
|
|
|
@@ -50,10 +50,12 @@ PTC 是可选入口:外层 `run_code` 可以调用同一个工具。它不是
|
|
|
50
50
|
|
|
51
51
|
- 标准 Cordis 工具注册、异步等待及生命周期清理。
|
|
52
52
|
- 复用宿主工具管线和 Job 服务,不自行启动进程或实现独立 HTTP 执行服务。
|
|
53
|
-
- bundle patch
|
|
53
|
+
- bundle patch 加载派生注册器,从已声明 ptc 读取组合快照并注册可选预设,由宿主既有 Agent 预设界面消费元数据,不需要自定义前端。readDocument 在宿主注释中用于查看声明,本插件只读取其序列化数据再注册独立预设,并不认领源预设的运行实例或私有基准地址。
|
|
54
54
|
- 同预设范围内共享有界总线,但查询/取消按宿主 Agent 的会话身份分隔。
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
旧版只读 guard 在 0.3.0 移除,预设保留源 ptc 的工具与权限,不再削减普通写入/命令能力,也不自动启用源组合禁用行。它不是进程或权限沙箱,不隔离进程资源消耗。宿主 cwd 只是路径解析基准,不是文件访问围栏。
|
|
57
|
+
|
|
58
|
+
派生使用 readDocument 序列化声明及宿主 YAML 方言,不访问注册表私有字段,不求值 !!js。它是加载时快照;没有公开的源声明 baseUrl,因此拒绝相对模块或 include 文件,避免错误重定位。完整自定义预设等价性不可仅由模块行保留推断,外部依赖和宿主注入仍需原实例验证。
|
|
57
59
|
|
|
58
60
|
### 当前不提供
|
|
59
61
|
|
package/docs/protocol.md
CHANGED
|
@@ -44,7 +44,7 @@ core 的 `submit()` 返回 `accepted`、`done` Promise 和取消/解除通知/
|
|
|
44
44
|
|
|
45
45
|
通知包含版本、消息 ID、请求 ID、关联 ID(correlationId)、请求内序号、类型与 payload。accepted 的 correlationId 对应本次提交消息,序号是当前快照;后续事件关联首次请求 messageId,新增序号。类型包括 started、progress、cancel_requested 和 succeeded、failed、cancelled、unknown 四种终态事件。
|
|
46
46
|
|
|
47
|
-
Handler 报告的进度序号必须递增;重复、乱序及终态后进度忽略,百分比须在 0–100 内。事件及 payload 在通知前冻结。每个附着独立解除通知,detach
|
|
47
|
+
Handler 报告的进度序号必须递增;重复、乱序及终态后进度忽略,百分比须在 0–100 内。事件及 payload 在通知前冻结。每个附着独立解除通知,detach 不取消执行;取消是另一个操作。派发开始时固定本批名单,派发期间新增监听从下个事件开始接收;已解除且尚未轮到的回调跳过。回调中触发的嵌套事件按序排在当前批之后,不跨越当前通知。观察者抛错或异步拒绝被隔离,不记录异常原文,也不等待观察者资源完成。
|
|
48
48
|
|
|
49
49
|
## 3. 执行状态和交付标记
|
|
50
50
|
|
|
@@ -80,13 +80,13 @@ queued → running → succeeded / failed / cancelled / unknown
|
|
|
80
80
|
| 单结果 JSON | 64 KiB |
|
|
81
81
|
| 终态保留期 | 自完成起 1 小时 |
|
|
82
82
|
|
|
83
|
-
新执行请求受队列和记录容量限制;运行中重复附着受消息/监听名额限制;未过期终态复用不申请新 Job。宿主新 Job
|
|
83
|
+
新执行请求受队列和记录容量限制;运行中重复附着受消息/监听名额限制;未过期终态复用不申请新 Job。宿主新 Job 准入还有独立容量要求。已受理的排队任务按会话轮转,各会话内部 FIFO;不抢占运行任务,没有完成时间 SLA,单会话仍可占满受理容量,不保证其他会话一定获得入队名额。
|
|
84
84
|
|
|
85
85
|
core 不传配置时用默认值;显式配置必须完整包含 concurrency、queueLimit、recordLimit、resultBytes、retentionMs 五项正安全整数。拒绝缺项、额外字段与访问器;构造时复制冻结,无动态更新接口。
|
|
86
86
|
|
|
87
87
|
保留期通过新请求或控制消息触发惰性清理,也可调用实例 `sweep()`;不是精确的一小时定时删除,闲置时记录可能继续占内存,但后续正常查询会先清理过期记录。无磁盘任务库。结果大小检查在 Handler 返回后进行,Handler 自身也必须限制内存分配。敏感返回内容仍可能进入宿主会话日志,本插件不是秘密识别器。
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
关闭幂等且可重入,多次 close 返回同一 Promise。core 先停止受理,取消队列和运行任务,排空后清理内存;宿主执行所有者继续等原工具交付路径及 Job 关联结束,再释放。等待某个 Handler 的通知回调里不应反向 await 整个关闭完成。正常路径复用 Job 窗口等待;服务异常后仍先排空执行,再有界检查记录并保留原服务错误。持续故障可能留记录到宿主生命周期清理,不合作 Handler 可能阻碍关闭。
|
|
90
90
|
|
|
91
91
|
## 5. 重试与恢复语义
|
|
92
92
|
|
|
@@ -94,7 +94,13 @@ core 不传配置时用默认值;显式配置必须完整包含 concurrency、
|
|
|
94
94
|
|
|
95
95
|
同存活实例、保存期内的请求复用不是端到端 exactly-once;每个实际工具调用仍有自己的宿主结果记录。没有外部终态注入接口,也没有跨重启恢复:任一阶段进程崩溃都会丢失插件内存状态。宿主日志不能复活旧 Promise,不追加聊天消息冒充原调用恢复。
|
|
96
96
|
|
|
97
|
-
## 6.
|
|
97
|
+
## 6. 诊断
|
|
98
|
+
|
|
99
|
+
core 实例的 `diagnostics()` 返回只读用途的独立快照:阶段计数、最近 128 条 `{operation,stage,elapsedMs}` 轨迹、关闭阶段、队列/运行/记录数及保留结果字节数。operation 是实例内递增数值,不是消息或会话 ID;elapsedMs 以单调时钟计算,阶段时间均相对受理时间,可相减得到排队/执行/返回延迟。复用计数仅在附着受理成功时增加;返回阶段按逻辑结果首次标记,不代表每个工具调用都持久化。
|
|
100
|
+
|
|
101
|
+
宿主执行所有者另有内部 Job 创建/释放/异常聚合轨迹,不作为模型工具或公开服务导出;其 operation 是独立编号,不能与 core 轨迹按编号关联。数据只在内存,不保存参数、结果、Handler 名称、请求/会话 ID、异常文本或聊天记录。总计数会持续累积,环形轨迹有界;异常计数不等于每次清理最终失败。
|
|
102
|
+
|
|
103
|
+
## 7. 扩展 Handler
|
|
98
104
|
|
|
99
105
|
包的 `./core` 导出 ExecutionBus、Handler、Capability 类型。Handler 只接收输入、requestId、signal、progress 和可选最小 capability,不接收聊天历史。register 返回绑定唯一注册令牌的幂等注销函数,不会误删后来同名注册;已受理请求继续持有原 Handler。
|
|
100
106
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# 萧何 / Xiao He 0.3.0
|
|
2
|
+
|
|
3
|
+
## 能力补全
|
|
4
|
+
|
|
5
|
+
本版将萧何从只读验证模式改为“宿主 PTC 工具组合 + 可选执行总线”。加载时读取目标宿主已声明的 ptc 组合快照,保留原配置、平台表达式和禁用项,再追加总线和使用说明。移除萧何的 native 强制呈现与只读白名单。原预设、默认选择及技术 ID 不变。
|
|
6
|
+
|
|
7
|
+
普通终端、编辑、搜索、技能、委派等任务仍调用原工具,经过宿主原有授权、沙箱与工作目录规则,不强制走总线。总线仍仅支持 test / host.read;不能把命令启动或 Job ID 当成完成。源 PTC 中禁用的专用插件管理工具不会自动启用。
|
|
8
|
+
|
|
9
|
+
## 升级注意
|
|
10
|
+
|
|
11
|
+
**旧只读模式升级后工具能力扩大。** 请先审阅目标 PTC 配置再加载,新建专门会话验证;不自动安装或重启日常 DSH。需要宿主先提供 ptc 声明,缺失时最多等待约 5 秒后报错,不静默回退。源预设编辑不会自动热同步;按宿主正常方式重载派生行后,新会话使用新快照,已有会话遵循宿主版本保留规则。
|
|
12
|
+
|
|
13
|
+
公开 readDocument 不提供原声明 baseUrl,因此本版拒绝含相对模块/include 文件的组合,避免错误重定位;不声称兼容全部自定义 PTC。源预设的组件缺失、策略禁止或禁用项会继续影响能力。
|
|
14
|
+
|
|
15
|
+
## 验证
|
|
16
|
+
|
|
17
|
+
声明保真测试覆盖分组、禁用管理工具、!!js 平台表达式;真实 Cordis 生命周期和 AgentPresetRegistry 验证派生注册、write/edit 保留、宿主拒绝策略继续生效、原 PTC 未被修改、原 Agent 不可见总线。真实 Node PTC 子进程验证 run_code 同时调用原 read 和执行总线。
|
|
18
|
+
|
|
19
|
+
PTC 测试采用显式 danger-full-access 策略与测试 Agent,不能视为桌面沙箱、真实模型或所有日常工具的全链路验收。不提供真实模型性能提升比例。无持久化或跨重启恢复能力。
|
|
20
|
+
|
|
21
|
+
非商业源码公开许可,宿主兼容目标仍为 DSH 0.2.0-rc.2。
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# 萧何 / Xiao He 0.4.0
|
|
2
|
+
|
|
3
|
+
## 本版变化
|
|
4
|
+
|
|
5
|
+
统一交付引擎调研中优先级最高的生命周期优化,不改变请求级取消、内存保存或宿主权限规则。
|
|
6
|
+
|
|
7
|
+
- 内部执行句柄集中持有完成结果、请求取消及宿主 Job 投影,core 仍是执行状态唯一事实源。共享执行只由首个附着投影进度,重复调用不重复创建 Job。
|
|
8
|
+
- 关闭分阶段、可重入并幂等,多次 close 返回同一完成 Promise,等待已启动执行和活跃工具交付后释放关联。
|
|
9
|
+
- 固定事件通知批次:回调中新增监听不接收当前事件,解除监听跳过尚未调用部分;嵌套取消/终态按序派发。事件仍不可变,观察者异常继续隔离。
|
|
10
|
+
- core 增加 diagnostics():最近 128 条数值阶段轨迹及容量快照,内部执行所有者另计 Job 事件。无业务参数、结果、请求/会话 ID 或错误文本,无新增模型工具。
|
|
11
|
+
- 压力验证后将已受理调度改为按会话轮转,保持会话内 FIFO;不抢占、不保证入队公平或完成时间。
|
|
12
|
+
|
|
13
|
+
## 验证和范围
|
|
14
|
+
|
|
15
|
+
新增通知重入/关闭、诊断脱敏及多会话调度回归。确定性用例 B0 从 A 的 12 个积压任务之后提前至 A1 后执行,不代表真实模型性能提升;具体顺序与边界见优化说明。
|
|
16
|
+
|
|
17
|
+
沿用 PTC 声明派生,原工具不强制经过总线;Handler 仍只含 test/host.read。资源锁、DAG、线程池、对象池、跨重启恢复不在本版。真实模型、桌面 UI 和实际沙箱仍未验收。兼容 DSH 0.2.0-rc.2,非商业源码公开许可;不安装或重启日常 DSH。
|
package/docs/validation.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 验证范围与使用限制
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
本文说明当前版本的证据边界,不是生产环境认证。当前优化基线为 **v0.4.0**,新增执行所有权/关闭、通知重入边界、数值诊断及会话轮转;预设沿用 v0.3.0 的宿主 PTC 声明派生方式。验证环境为 Windows、Node.js 24.21.0,宿主组件锁定 DSH 0.2.0-rc.2、Cordis 4.0.4。
|
|
4
4
|
|
|
5
5
|
## 已验证什么?
|
|
6
6
|
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
| 总线核心 | 参数、队列、并发、重复请求、冲突、取消竞争、结果保存、TTL、监听与注销 | 程序内部约束及已覆盖异常路径可重复检查 |
|
|
10
10
|
| 真实宿主组件 | Cordis、ToolRuntime、LocalJobs、LocalFS、文件工具 | 请求经过真实工具管线;权限拒绝、Job 进度、共享任务与清理逻辑有效;Agent 身份为夹具 |
|
|
11
11
|
| 真实会话循环 | AgentLoop、AgentRegistry、SessionStore;脚本模型适配器 | 内存会话中原工具结果只提交一次,完成后再进入下一次适配器调用 |
|
|
12
|
-
| 预设隔离 | AgentPresetRegistry 与 Loader |
|
|
12
|
+
| 预设隔离 | AgentPresetRegistry 与 Loader | 派生预设可激活;源 ptc 声明不变、普通 Agent 不可见总线;原 write/edit/Job 工具保留,宿主前置策略拒绝仍有效 |
|
|
13
13
|
| 故障注入 | 克隆失败、观察者拒绝、Job 等待/清理异常、先行移除与卸载交错 | 指定故障下的回归约束;不证明所有故障组合都已覆盖 |
|
|
14
14
|
| 候选包 | 严格文件白名单、临时目录生产依赖安装、入口注册 | 包可安装和加载;安装验证使用简化宿主夹具,不是桌面插件管理器验收 |
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
此前 v0.3.0 本地回归为 **51/51 通过**;本轮新增生命周期与多会话压力回归,最新数量以命令输出为准。测试项数量是用例数量,不是全部产品验收条数。当前结果请重新运行下述命令;文档测试数不应代替实际输出。
|
|
17
17
|
|
|
18
18
|
```powershell
|
|
19
19
|
npm ci --include=dev --ignore-scripts
|
|
@@ -40,7 +40,7 @@ eb95b4e51675f5f1821275e0812ef4dd4984b1aba45a52fbc6bab3518bbf6f38
|
|
|
40
40
|
|---|---|
|
|
41
41
|
| 当前桌面真实模型发起请求并继续推理 | 未验收;脚本适配器不是实际模型 |
|
|
42
42
|
| 当前桌面预设卡片、进度界面、授权提示和实际沙箱 | 未验收;组件事件和 LocalFS 测试不等于桌面 UI/沙箱 |
|
|
43
|
-
| PTC
|
|
43
|
+
| 实际模型/桌面 PTC 与时间预算、沙箱行为 | 真实 Node PTC 隔离子进程已调用原 read 和总线;使用显式 danger-full-access 测试策略与测试 Agent,不是桌面沙箱/真实模型验收 |
|
|
44
44
|
| 从真实 tgz 经宿主插件管理器加载预设、卸载与回滚 | 未完成完整桌面流程;源码工作区的 Loader 测试不能替代 |
|
|
45
45
|
| 磁盘会话结果提交及重启恢复原调用 | 未验收磁盘提交;本插件不支持跨重启恢复 |
|
|
46
46
|
| 减少真实模型轮次、状态查询或总耗时的对照 | 未验证,不给提升比例 |
|
|
@@ -49,7 +49,7 @@ eb95b4e51675f5f1821275e0812ef4dd4984b1aba45a52fbc6bab3518bbf6f38
|
|
|
49
49
|
## 需要知道的运行约束
|
|
50
50
|
|
|
51
51
|
1. 受理、队列、结果及交付标记均在内存。进程关闭会丢失,宿主会话日志不等于插件恢复执行权。
|
|
52
|
-
2.
|
|
52
|
+
2. 同一预设的会话共享总线容量,已受理任务会话轮转、会话内 FIFO;结果访问按会话隔离,不是独立进程或资源安全隔离,也不保证入队名额公平。
|
|
53
53
|
3. 取消任一正在等待的重复调用会请求取消整个逻辑执行,不是仅停止该等待者。
|
|
54
54
|
4. 已终态且未过期的请求可直接复用结果,不申请新 Job;仍须通过身份、冲突及消息额度检查。
|
|
55
55
|
5. 等待服务异常后先排空原执行,再有界检查宿主记录;服务持续不可用时记录可能留到所属生命周期清理。不合作 Handler 仍可能阻碍关闭。
|