@feng0531/dsh-execution-bus 0.2.4 → 0.2.6

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 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.2.4**
7
+ DSH 独立插件:`@feng0531/dsh-execution-bus` · 当前版本 **0.2.6**
8
8
 
9
9
  > 当前是早期可运行版本,不是完整生产验收版。已验证真实 DSH 组件和脚本模型驱动的内存会话;真实模型、桌面界面和 PTC 完整链路尚未验收。**不支持重启后恢复任务。**
10
10
 
@@ -102,7 +102,7 @@ flowchart TD
102
102
  2. 在你选定的隔离 DSH 配置依赖目录安装固定版本,不要在 DSH 程序安装目录执行:
103
103
 
104
104
  ```powershell
105
- npm install --ignore-scripts @feng0531/dsh-execution-bus@0.2.4
105
+ npm install --ignore-scripts @feng0531/dsh-execution-bus@0.2.6
106
106
  ```
107
107
 
108
108
  这是 npm 目录示例。若目标配置使用 pnpm,应沿用其包管理器,不混用锁文件。遇到宿主 peer 版本冲突就停止,不用强制安装绕过。
@@ -231,7 +231,7 @@ npm run test:install # 临时目录安装,不操作日常 DSH
231
231
 
232
232
  - [协议和投递语义](<docs/protocol.md>):字段、状态、重复处理与取消规则。
233
233
  - [宿主兼容性与源码调用链](<docs/host-compatibility.md>):为什么可以等待,为什么不能跨重启恢复。
234
- - [验证记录](<docs/validation.md>)、[0.2.4 发布说明](<docs/releases/0.2.4.md>):已验证与未验证范围。
234
+ - [验证记录](<docs/validation.md>)、[0.2.6 发布说明](<docs/releases/0.2.6.md>):已验证与未验证范围。
235
235
  - [维护者发布流程](<docs/RELEASING.md>):标签 CI、安装包校验与正式发布门禁。
236
236
 
237
237
  ## 许可
package/dist/bus.js CHANGED
@@ -68,16 +68,23 @@ export class ExecutionBus {
68
68
  retentionMs: 3600000,
69
69
  }) {
70
70
  this.options = options;
71
- for (const v of Object.values(options))
71
+ this.options = Object.freeze({ ...options });
72
+ for (const v of Object.values(this.options))
72
73
  if (!Number.isSafeInteger(v) || v < 1)
73
74
  throw Error("INVALID_OPTIONS");
74
75
  }
75
76
  register(name, handler) {
76
77
  if (this.handlers.has(name))
77
78
  throw Error("DUPLICATE_HANDLER");
78
- this.handlers.set(name, handler);
79
+ const registration = { handler, token: Symbol(name) };
80
+ this.handlers.set(name, registration);
81
+ let disposed = false;
79
82
  return () => {
80
- this.handlers.delete(name);
83
+ if (disposed)
84
+ return;
85
+ disposed = true;
86
+ if (this.handlers.get(name)?.token === registration.token)
87
+ this.handlers.delete(name);
81
88
  };
82
89
  }
83
90
  key(o, id) {
@@ -87,7 +94,7 @@ export class ExecutionBus {
87
94
  }
88
95
  emit(e, type, payload, correlationId = "") {
89
96
  e.view.sequence++;
90
- const event = {
97
+ const event = freezeJson({
91
98
  protocolVersion: 1,
92
99
  messageId: randomUUID(),
93
100
  requestId: e.view.requestId,
@@ -95,7 +102,7 @@ export class ExecutionBus {
95
102
  sequence: e.view.sequence,
96
103
  type,
97
104
  payload,
98
- };
105
+ });
99
106
  for (const f of e.listeners)
100
107
  try {
101
108
  // Observe promise rejection without awaiting or blocking task settlement.
@@ -141,14 +148,15 @@ export class ExecutionBus {
141
148
  const previous = e.messages.get(m.messageId);
142
149
  if (previous && previous !== canonical(m))
143
150
  throw Error("MESSAGE_CONFLICT");
144
- if (!previous) {
145
- if (e.messages.size >= 128)
146
- throw Error("MESSAGE_LIMIT");
151
+ if (!previous && e.messages.size >= 128)
152
+ throw Error("MESSAGE_LIMIT");
153
+ // Acquire the attachment before committing its message identity. Rejection is side-effect free.
154
+ const attached = this.attachment(e, onEvent, m.messageId);
155
+ if (!previous)
147
156
  e.messages.set(m.messageId, canonical(m));
148
- }
149
- return this.attachment(e, onEvent, m.messageId);
157
+ return attached;
150
158
  }
151
- const handler = this.handlers.get(p.handler);
159
+ const handler = this.handlers.get(p.handler)?.handler;
152
160
  if (!handler)
153
161
  throw Error("UNKNOWN_HANDLER");
154
162
  handler.validate(input);
package/docs/protocol.md CHANGED
@@ -16,7 +16,7 @@ type 为 request 时 payload 为 {handler,input};type 为 status / result / ca
16
16
 
17
17
  核心 submit 返回 accepted 事件和 done Promise。受理只意味着已进入内存记录和队列,durable:false;不是成功。事件包含 protocolVersion、messageId、requestId、correlationId(首次请求 messageId)、sequence、type、payload。每请求 sequence 单调递增,accepted 是当前序号的快照,不占新增序号。
18
18
 
19
- 事件类型:accepted、started、progress、cancel_requested、succeeded、failed、cancelled、unknown。进度只接受 Handler 正整数序号递增的百分比,重复、乱序、终态后进度丢弃。进程内投递,无外部消息接收端,也不支持任意远端终态注入;终态只由总线持有的执行 Promise 结算。观察者是最佳努力通知:同步抛错及异步 Promise 拒绝均被隔离,不记录异常原文;不等待观察者完成,不保证关闭前异步通知已经完成,观察者须自行管理资源。
19
+ 事件类型:accepted、started、progress、cancel_requested、succeeded、failed、cancelled、unknown。进度只接受 Handler 正整数序号递增的百分比,重复、乱序、终态后进度丢弃。进程内投递,无外部消息接收端,也不支持任意远端终态注入;终态只由总线持有的执行 Promise 结算。事件及其 payload 在投递前深度冻结,观察者不得修改请求关联、序号、类型或进度内容。观察者是最佳努力通知:同步抛错及异步 Promise 拒绝均被隔离,不记录异常原文;不等待观察者完成,不保证关闭前异步通知已经完成,观察者须自行管理资源。
20
20
 
21
21
  工具的 request 保持 pending,最终返回 RecordView(requestId,state,sequence,cancelRequested,delivery,result?,error?,progress?);status/result/cancel 返回同一种当前快照。工具不会提前返回 accepted 或 taskId,也不会用聊天进度消息唤醒模型。Job 的 UI progress 只含状态/百分比。
22
22
 
@@ -31,7 +31,7 @@ queued → cancelled。终态不可变;取消终态任务只返回原状态。
31
31
 
32
32
  ## 容量和清理
33
33
 
34
- 默认并发 2,待分发队列 32,记录 256,单结果 JSON 64 KiB;所有请求先经上限检查,满则 BACKPRESSURE,不自动重试。Job 仍受宿主自己的每 owner 容量限制。队列不提供公平性 SLA,单会话可占满实例。
34
+ 默认并发 2,待分发队列 32,记录 256,单结果 JSON 64 KiB;所有请求先经上限检查,满则 BACKPRESSURE,不自动重试。Job 仍受宿主自己的每 owner 容量限制。队列不提供公平性 SLA,单会话可占满实例。core 构造时复制并冻结容量配置,之后修改调用方原对象不改变总线限制;没有动态配置更新接口。重复附着只有成功取得观察者名额后才提交新消息记账,WAITER_LIMIT 拒绝不占用消息 ID 或额度。
35
35
 
36
36
  终态自完成后保存一小时;新受理及控制消息查询时惰性清理,可通过 core.sweep 主动清理。无定时器常驻、无磁盘结果存储、无日志输出。插件关闭先停止受理,取消队列与运行任务,等待合作 Handler,清理内存;不合作的 Handler 会阻碍关闭,插件无法安全硬杀同进程代码。
37
37
 
@@ -47,6 +47,6 @@ queued → cancelled。终态不可变;取消终态任务只返回原状态。
47
47
 
48
48
  ## 扩展 Handler
49
49
 
50
- core 导出 ExecutionBus、Handler 和 Capability 类型。register(name,handler) 返回注销函数;同名拒绝。Handler 提供 validate 和 async run,执行上下文仅有 requestId、signal、progress 与最小执行 capability,不传聊天历史。注销不影响已受理任务捕获的 Handler;关闭时等待它们。
50
+ core 导出 ExecutionBus、Handler 和 Capability 类型。register(name,handler) 返回幂等注销函数,绑定唯一注册令牌;旧函数不会删除后来同名注册,即使复用同一 Handler 对象。同名活跃注册拒绝。Handler 提供 validate 和 async run,执行上下文仅有 requestId、signal、progress 与最小执行 capability,不传聊天历史。注销不影响已受理任务捕获的 Handler;关闭时等待它们。
51
51
 
52
52
  当前插件内置注册 test 与 host.read。尚未公开 Cordis Handler 注册服务,其他插件不能假定存在 ctx.executionBus;跨插件动态注册属于后续事项。
@@ -0,0 +1,14 @@
1
+ # 萧何 / Xiao He 0.2.5
2
+
3
+ ## BUG 修复
4
+
5
+ - Handler 注销函数绑定唯一注册令牌并支持幂等清理。旧注销函数重复执行不会误删后来同名注册,即使复用相同 Handler 对象。已受理请求继续使用它捕获的旧 Handler,新请求使用当前注册。
6
+ - 事件和 payload 在通知观察者前深度冻结,前一个观察者无法污染后续观察者收到的 requestId、sequence、type 或进度。同步/异步异常继续隔离。
7
+
8
+ ## 验证
9
+
10
+ 新增三项回归,覆盖相同对象重新注册、已受理任务与替换 Handler 并存、前序观察者修改事件被隔离;三项在修复前均失败。全量测试、标签四矩阵 CI、包内容、生产隔离安装及两端公开包一致性按既有门禁验证。
11
+
12
+ ## 边界
13
+
14
+ 兼容目标仍为 DSH 0.2.0-rc.2,不更改品牌或技术标识。真实模型、桌面 GUI 与 PTC 完整链路未验收;不支持跨重启恢复。非商业源码公开许可,不自动安装或重启 DSH。
@@ -0,0 +1,14 @@
1
+ # 萧何 / Xiao He 0.2.6
2
+
3
+ ## BUG 修复
4
+
5
+ - 重复附着在取得观察者名额后才提交消息记账,WAITER_LIMIT 拒绝不再占用消息编号或耗尽后续查询/结果重取额度。已有重复识别和取消保留槽语义不变。
6
+ - 容量配置在构造时复制并冻结,不再保留调用方可变引用;外部修改原配置不改变队列、记录、并发或结果大小限制。
7
+
8
+ ## 验证
9
+
10
+ 新增两项回归在修复前均失败,覆盖 96 次拒绝附着后消息编号可用和结果可重取、外部配置修改不影响限制。全量回归、标签四矩阵 CI、包白名单和生产依赖隔离安装按既有门禁执行。
11
+
12
+ ## 边界
13
+
14
+ 只修复 core 接口边界,不新增业务执行能力或动态配置接口。DSH 0.2.0-rc.2;非商业源码公开许可。真实模型/GUI/PTC 完整链路未验收,不支持跨重启恢复。发布不自动安装或重启 DSH。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feng0531/dsh-execution-bus",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "license": "SEE LICENSE IN LICENSE",