@feng0531/dsh-execution-bus 0.4.0 → 0.4.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/README.md +3 -3
- package/dist/bus.d.ts +1 -0
- package/dist/bus.js +13 -7
- package/dist/diagnostics.d.ts +2 -0
- package/dist/diagnostics.js +10 -6
- package/docs/GITHUB-COMPARISON.md +38 -0
- package/docs/OPTIMIZATION.md +1 -1
- package/docs/protocol.md +1 -1
- package/docs/releases/0.4.1.md +15 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
“萧何”是产品与模式显示名,执行总线是其技术功能;不新增人物 Agent,不进行角色扮演。仓库 dsh-execution-bus、npm 包名、execution_bus 工具名及 execution-bus-test 预设 ID 保持不变。历史版本发布说明保留当时名称。
|
|
6
6
|
|
|
7
|
-
DSH 独立插件:`@feng0531/dsh-execution-bus` · 当前版本 **0.4.
|
|
7
|
+
DSH 独立插件:`@feng0531/dsh-execution-bus` · 当前版本 **0.4.1**
|
|
8
8
|
|
|
9
9
|
> 萧何模式从宿主已声明的 PTC 组合派生,保留日常工具,再叠加可选执行总线。已验证隔离的真实 Node PTC → 原 read + 总线调用,以及脚本模型内存会话;真实模型、桌面界面与实际沙箱仍未验收。**不支持重启后恢复任务。**
|
|
10
10
|
|
|
@@ -115,7 +115,7 @@ flowchart TD
|
|
|
115
115
|
2. 在你选定的隔离 DSH 配置依赖目录安装固定版本,不要在 DSH 程序安装目录执行:
|
|
116
116
|
|
|
117
117
|
```powershell
|
|
118
|
-
npm install --ignore-scripts @feng0531/dsh-execution-bus@0.4.
|
|
118
|
+
npm install --ignore-scripts @feng0531/dsh-execution-bus@0.4.1
|
|
119
119
|
```
|
|
120
120
|
|
|
121
121
|
这是 npm 目录示例。若目标配置使用 pnpm,应沿用其包管理器,不混用锁文件。遇到宿主 peer 版本冲突就停止,不用强制安装绕过。
|
|
@@ -250,7 +250,7 @@ npm run test:install # 临时目录安装,不操作日常 DSH
|
|
|
250
250
|
|
|
251
251
|
- [协议和投递语义](<docs/protocol.md>):字段、状态、重复处理与取消规则。
|
|
252
252
|
- [宿主兼容性与源码调用链](<docs/host-compatibility.md>):为什么可以等待,为什么不能跨重启恢复。
|
|
253
|
-
- [验证记录](<docs/validation.md>)、[0.4.
|
|
253
|
+
- [验证记录](<docs/validation.md>)、[0.4.1 发布说明](<docs/releases/0.4.1.md>):已验证与未验证范围。
|
|
254
254
|
- [维护者发布流程](<docs/RELEASING.md>):标签 CI、安装包校验与正式发布门禁。
|
|
255
255
|
|
|
256
256
|
## 许可
|
package/dist/bus.d.ts
CHANGED
package/dist/bus.js
CHANGED
|
@@ -61,6 +61,7 @@ export class ExecutionBus {
|
|
|
61
61
|
queue = [];
|
|
62
62
|
readyOwners = [];
|
|
63
63
|
running = 0;
|
|
64
|
+
retainedResultBytes = 0;
|
|
64
65
|
closed = false;
|
|
65
66
|
telemetry = new Diagnostics();
|
|
66
67
|
diagnostics() {
|
|
@@ -70,10 +71,7 @@ export class ExecutionBus {
|
|
|
70
71
|
queued: this.queue.length,
|
|
71
72
|
running: this.running,
|
|
72
73
|
records: this.entries.size,
|
|
73
|
-
retainedResultBytes:
|
|
74
|
-
(e.view.result === undefined
|
|
75
|
-
? 0
|
|
76
|
-
: Buffer.byteLength(JSON.stringify(e.view.result))), 0),
|
|
74
|
+
retainedResultBytes: this.retainedResultBytes,
|
|
77
75
|
};
|
|
78
76
|
}
|
|
79
77
|
closing;
|
|
@@ -173,8 +171,11 @@ export class ExecutionBus {
|
|
|
173
171
|
}
|
|
174
172
|
sweep(now = Date.now()) {
|
|
175
173
|
for (const [k, e] of this.entries)
|
|
176
|
-
if (terminal(e.view.state) &&
|
|
174
|
+
if (terminal(e.view.state) &&
|
|
175
|
+
now - e.created > this.options.retentionMs) {
|
|
176
|
+
this.retainedResultBytes -= e.resultBytes;
|
|
177
177
|
this.entries.delete(k);
|
|
178
|
+
}
|
|
178
179
|
}
|
|
179
180
|
submit(owner, raw, onEvent, invoke) {
|
|
180
181
|
if (this.closed)
|
|
@@ -216,6 +217,7 @@ export class ExecutionBus {
|
|
|
216
217
|
const done = new Promise((r) => (resolve = r));
|
|
217
218
|
e = {
|
|
218
219
|
trace: this.telemetry.begin(),
|
|
220
|
+
resultBytes: 0,
|
|
219
221
|
invoke,
|
|
220
222
|
correlationId: m.messageId,
|
|
221
223
|
owner: owner.sessionId,
|
|
@@ -319,7 +321,7 @@ export class ExecutionBus {
|
|
|
319
321
|
this.finish(e, "cancelled");
|
|
320
322
|
}
|
|
321
323
|
}
|
|
322
|
-
finish(e, state, result, error) {
|
|
324
|
+
finish(e, state, result, error, resultBytes = 0) {
|
|
323
325
|
if (terminal(e.view.state))
|
|
324
326
|
return;
|
|
325
327
|
// Prepare the delivery snapshot BEFORE committing an irreversible terminal state.
|
|
@@ -334,6 +336,7 @@ export class ExecutionBus {
|
|
|
334
336
|
}
|
|
335
337
|
catch {
|
|
336
338
|
state = "unknown";
|
|
339
|
+
resultBytes = 0;
|
|
337
340
|
next = {
|
|
338
341
|
requestId: e.view.requestId,
|
|
339
342
|
state,
|
|
@@ -344,6 +347,8 @@ export class ExecutionBus {
|
|
|
344
347
|
};
|
|
345
348
|
snapshot = freezeJson({ ...next });
|
|
346
349
|
}
|
|
350
|
+
e.resultBytes = resultBytes;
|
|
351
|
+
this.retainedResultBytes += resultBytes;
|
|
347
352
|
e.created = Date.now();
|
|
348
353
|
e.view = { ...next, sequence: next.sequence - 1 };
|
|
349
354
|
this.telemetry.record(e.trace, "terminal");
|
|
@@ -413,7 +418,7 @@ export class ExecutionBus {
|
|
|
413
418
|
this.finish(e, "unknown", undefined, "RESULT_NOT_RETAINED");
|
|
414
419
|
return;
|
|
415
420
|
}
|
|
416
|
-
this.finish(e, "succeeded", JSON.parse(json));
|
|
421
|
+
this.finish(e, "succeeded", JSON.parse(json), undefined, Buffer.byteLength(json));
|
|
417
422
|
}
|
|
418
423
|
catch (err) {
|
|
419
424
|
const cancelled = e.controller.signal.aborted &&
|
|
@@ -448,6 +453,7 @@ export class ExecutionBus {
|
|
|
448
453
|
await Promise.all(entries.map((e) => e.done));
|
|
449
454
|
this.handlers.clear();
|
|
450
455
|
this.entries.clear();
|
|
456
|
+
this.retainedResultBytes = 0;
|
|
451
457
|
this.queue = [];
|
|
452
458
|
this.readyOwners = [];
|
|
453
459
|
this.shutdownPhase = "closed";
|
package/dist/diagnostics.d.ts
CHANGED
package/dist/diagnostics.js
CHANGED
|
@@ -2,25 +2,29 @@ import { performance } from "node:perf_hooks";
|
|
|
2
2
|
/** In-memory numeric metadata only: never accepts request/session strings, input, output or error text. */
|
|
3
3
|
export class Diagnostics {
|
|
4
4
|
next = 0;
|
|
5
|
-
events =
|
|
5
|
+
events = new Array(128);
|
|
6
|
+
cursor = 0;
|
|
7
|
+
size = 0;
|
|
6
8
|
counts = {};
|
|
7
9
|
begin() {
|
|
8
10
|
return { operation: ++this.next, at: performance.now() };
|
|
9
11
|
}
|
|
10
12
|
record(context, stage) {
|
|
11
13
|
this.counts[stage] = (this.counts[stage] ?? 0) + 1;
|
|
12
|
-
this.events.
|
|
14
|
+
this.events[this.cursor] = {
|
|
13
15
|
operation: context.operation,
|
|
14
16
|
stage,
|
|
15
17
|
elapsedMs: Math.max(0, performance.now() - context.at),
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
};
|
|
19
|
+
this.cursor = (this.cursor + 1) % 128;
|
|
20
|
+
this.size = Math.min(this.size + 1, 128);
|
|
19
21
|
}
|
|
20
22
|
snapshot() {
|
|
21
23
|
return {
|
|
22
24
|
counts: { ...this.counts },
|
|
23
|
-
traces: this.
|
|
25
|
+
traces: Array.from({ length: this.size }, (_, index) => ({
|
|
26
|
+
...this.events[(this.cursor - this.size + 128 + index) % 128],
|
|
27
|
+
})),
|
|
24
28
|
};
|
|
25
29
|
}
|
|
26
30
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# GitHub 源码对比与诊断优化
|
|
2
|
+
|
|
3
|
+
## 调研范围
|
|
4
|
+
|
|
5
|
+
本轮对比萧何 v0.4.0(55e1d66)的队列生命周期、事件通知和诊断成本。通过 GitHub 仓库检索及 raw 源码读取,使用以下固定标签,不把 star 数作为质量证明,也未构建这些项目。
|
|
6
|
+
|
|
7
|
+
| 项目 | 实际查看内容 | 对萧何的结论 |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| [p-queue v8.1.0 实现](https://github.com/sindresorhus/p-queue/blob/v8.1.0/source/index.ts)与[测试](https://github.com/sindresorhus/p-queue/blob/v8.1.0/test/test.ts) | pending 计数、onEmpty/onIdle、abort 与 finally | 对照检查排队为空不等于运行结束;保留萧何合作取消后排空语义,不引入其超时/取消竞速直接释放槽位的行为 |
|
|
10
|
+
| [EventEmitter3 5.0.1 实现](https://github.com/primus/eventemitter3/blob/5.0.1/index.js)与[测试](https://github.com/primus/eventemitter3/blob/5.0.1/test/test.js) | emit 前保存监听数组长度,移除重建监听列表 | 支持为派发明确边界;萧何 0.4.0 已有批次快照、解除后跳过和嵌套事件顺序,保持自己的公开契约,不换库 |
|
|
11
|
+
| [Denque v2.1.0 实现](https://github.com/invertase/denque/blob/v2.1.0/index.js)与[测试](https://github.com/invertase/denque/blob/v2.1.0/test/denque.js) | head/tail 索引、容量和环绕行为 | 用同类索引思想自行实现固定 128 槽诊断环,不搬可增长 deque 或新增运行依赖 |
|
|
12
|
+
|
|
13
|
+
一次额外 deque 候选链接返回 404,未作为实现依据。参考代码不是本项目实际运行依赖;只借鉴行为和测试思路,没有复制上游源码。
|
|
14
|
+
|
|
15
|
+
## 本轮实施
|
|
16
|
+
|
|
17
|
+
1. **固定诊断环**:0.4.0 达到上限后每条事件 push 再 shift,搬移旧记录;0.4.1 使用固定槽位覆盖和游标,读取时导出最近 128 条的时间顺序副本。
|
|
18
|
+
2. **保留结果字节缓存**:0.4.0 每次 diagnostics 遍历记录并重新 JSON.stringify;0.4.1 复用保存时已算出的 UTF-8 字节,在终态快照保存成功后提交缓存。过期逐项扣除、关闭归零,unknown 或快照失败不计入。
|
|
19
|
+
3. **行为保持**:请求协议、容量、会话轮转、合作取消、关闭阶段和通知重入语义不变;不增加任务队列依赖,也不改变宿主授权。
|
|
20
|
+
|
|
21
|
+
## 可重复证据
|
|
22
|
+
|
|
23
|
+
回归 test/github-comparison.test.mjs 先在旧产物运行:
|
|
24
|
+
|
|
25
|
+
| 对比工作负载 | 0.4.0 实际计数 | 0.4.1 预期/回归结果 |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| 写入 1000 条诊断 | Array.shift 调用 872 次 | 0 次;仍只保存编号 873–1000 的 128 条 |
|
|
28
|
+
| 两条保存结果,查询诊断 50 次 | 额外 JSON.stringify 100 次 | 0 次,字节数完全相同 |
|
|
29
|
+
| 注入结果快照失败,再关闭 | unknown,不保存结果 | 字节计数保持 0;关闭 phase closed、queued/running 为 0 |
|
|
30
|
+
|
|
31
|
+
前两项优化前断言失败,第三项在前后均通过。以上是明确工作负载下的操作次数,不是耗时加速比、模型轮次或生产吞吐结论。若结果本身仅很小或诊断很少调用,实际收益也可能很小。新增字节缓存有维护成本,因此专门覆盖 Unicode、过期和失败归零。
|
|
32
|
+
|
|
33
|
+
## 暂不做
|
|
34
|
+
|
|
35
|
+
- 不用开源队列直接替换当前调度器:宿主 Job 与原工具结果排空约束不同。
|
|
36
|
+
- 不把 normal timeout 当作任务取消成功,不放弃仍运行的 Handler。
|
|
37
|
+
- 不做无锁/线程池、消息持久化或自动副作用重跑。
|
|
38
|
+
- 不因库测试通过就声称当前桌面、真实模型或沙箱验收通过。
|
package/docs/OPTIMIZATION.md
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
|
|
34
34
|
阶段 elapsedMs 相对于本次受理的单调时钟时间:started 减 accepted 为排队时间、terminal 减 started 为执行时间、returned 减 terminal 为首次返回标记延迟。cancel_requested 与 terminal 间隔只能解释为取消请求到执行终态,未必为成功取消。
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
从 0.4.1 起,结果字节在保存时缓存、记录过期和关闭时扣除/归零;查询不再遍历记录或序列化结果。诊断写入使用 128 槽固定环,读取时才按时间顺序复制最多 128 条。环形轨迹不会记录结果内容,但仍是内部运行信息,不作为可信审计/授权依据。进程关闭后不恢复。
|
|
37
37
|
|
|
38
38
|
## 引擎经验来源
|
|
39
39
|
|
package/docs/protocol.md
CHANGED
|
@@ -98,7 +98,7 @@ core 不传配置时用默认值;显式配置必须完整包含 concurrency、
|
|
|
98
98
|
|
|
99
99
|
core 实例的 `diagnostics()` 返回只读用途的独立快照:阶段计数、最近 128 条 `{operation,stage,elapsedMs}` 轨迹、关闭阶段、队列/运行/记录数及保留结果字节数。operation 是实例内递增数值,不是消息或会话 ID;elapsedMs 以单调时钟计算,阶段时间均相对受理时间,可相减得到排队/执行/返回延迟。复用计数仅在附着受理成功时增加;返回阶段按逻辑结果首次标记,不代表每个工具调用都持久化。
|
|
100
100
|
|
|
101
|
-
宿主执行所有者另有内部 Job 创建/释放/异常聚合轨迹,不作为模型工具或公开服务导出;其 operation 是独立编号,不能与 core 轨迹按编号关联。数据只在内存,不保存参数、结果、Handler 名称、请求/会话 ID
|
|
101
|
+
宿主执行所有者另有内部 Job 创建/释放/异常聚合轨迹,不作为模型工具或公开服务导出;其 operation 是独立编号,不能与 core 轨迹按编号关联。数据只在内存,不保存参数、结果、Handler 名称、请求/会话 ID、异常文本或聊天记录。总计数会持续累积,128 槽环形轨迹有界且按时间顺序导出;结果字节只在保存/过期/关闭时更新,查询不读取结果内容或重复序列化。异常计数不等于每次清理最终失败。
|
|
102
102
|
|
|
103
103
|
## 7. 扩展 Handler
|
|
104
104
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# 萧何 / Xiao He 0.4.1
|
|
2
|
+
|
|
3
|
+
## 优化
|
|
4
|
+
|
|
5
|
+
结合固定版本的 p-queue、EventEmitter3 和 Denque 实现/测试对照,收敛诊断热路径开销,保持 0.4.0 执行语义。
|
|
6
|
+
|
|
7
|
+
- 诊断最近 128 条轨迹改为固定槽位环,写入不再 shift 搬移旧项;快照仍按时间顺序复制。
|
|
8
|
+
- 保存结果时缓存字节数,过期时扣除、关闭归零;diagnostics 查询不再遍历所有结果和重复序列化。结果快照失败或 unknown 不占缓存。
|
|
9
|
+
- 增加三项对比回归及可追溯 GitHub 对比说明。1000 条事件的 shift 从 872 次降为 0;两个结果查询诊断 50 次的额外序列化从 100 次降为 0。不是速度提升比例。
|
|
10
|
+
|
|
11
|
+
## 兼容与验证
|
|
12
|
+
|
|
13
|
+
不改变消息协议、工具或预设标识、会话轮转/通知重入/关闭契约;不新增运行依赖。全量测试、标签 Windows/Ubuntu × Node 22/24、包白名单与生产隔离安装按既有门禁执行。
|
|
14
|
+
|
|
15
|
+
DSH 0.2.0-rc.2,非商业源码公开许可。真实模型、桌面 UI 和实际沙箱未验收,不支持跨重启恢复,不安装或重启日常 DSH。
|