@feng0531/dsh-execution-bus 0.12.0 → 0.12.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 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.12.0**
7
+ DSH 独立插件:`@feng0531/dsh-execution-bus` · 当前版本 **0.12.1**
8
8
 
9
9
  > 萧何模式从宿主已声明的 PTC 组合派生,保留日常工具,再叠加可选执行总线。已验证隔离的真实 Node PTC → 原 read + 总线调用,以及脚本模型内存会话;真实模型、桌面界面与实际沙箱仍未验收。**不支持重启后恢复任务。**
10
10
 
@@ -130,7 +130,7 @@ flowchart TD
130
130
  2. 在你选定的隔离 DSH 配置依赖目录安装固定版本,不要在 DSH 程序安装目录执行:
131
131
 
132
132
  ```powershell
133
- npm install --ignore-scripts @feng0531/dsh-execution-bus@0.12.0
133
+ npm install --ignore-scripts @feng0531/dsh-execution-bus@0.12.1
134
134
  ```
135
135
 
136
136
  这是 npm 目录示例。若目标配置使用 pnpm,应沿用其包管理器,不混用锁文件。遇到宿主 peer 版本冲突就停止,不用强制安装绕过。
@@ -205,14 +205,14 @@ PTC 是 DSH 的程序化工具调用入口。在 `run_code` 内使用 `await too
205
205
 
206
206
  **跨步骤取回的正确姿势(0.11.1 补充):** 同一 `run_code` 程序内,未完成的 request 会阻塞后发工具调用的结果提交(宿主按提交顺序结算);不要在同一个程序里“启动后立刻 status 轮询”。启动步骤只发请求并尽早结束程序,下一步再用同一 `requestId` 的 `status`/`result`(或重发同一 request)附着取回——pilot 实测单次执行、返回 `reused:true`。
207
207
 
208
- ### 调度与互斥(0.12.0)
208
+ ### 调度与互斥(0.12.0 起,0.12.1 修正)
209
209
 
210
- - **队列类别**:内置 Handler 分为 `fs`(read/glob/grep/edit/write)、`command`(pwsh)、`diagnostic`(test)。同会话内按类别优先级 + 等待老化选择:长命令不会长期压住文件/搜索类任务,等待超过阈值会逐步削平类别差距。
211
- - **舱壁**:Handler 可声明 `maxConcurrency`,同一 Handler 运行数受限;`execution_bus_state` 的 `waitReason` 会显示 `concurrency`/`dispatch`/`resource`,运行超过 30 秒的任务标记 `longRunning`。
212
- - **资源互斥**:`host.edit`/`host.write` 按 `file_path` 资源键在**同一总线实例内**串行,避免同文件并发写;失败/取消/关闭都会释放。它是插件内互斥,不是 OS 文件锁或跨进程保证。
213
- - **唯一任务**:`host.read`/`host.glob`/`host.grep` 声明 payload 去重;同会话同输入且已有成功结果时,新 requestId 直接复用并返回 `reused:true`(失败结果不缓存,不会把失败当成功复用)。
210
+ - **队列类别**:内置 Handler 分为 `fs`(read/glob/grep/edit/write)、`command`(pwsh)、`diagnostic`(test)。同会话内按类别优先级 + 等待老化选择:等待越久提升越多、不封顶(最低到 0 后按 FIFO),避免低优先类别饥饿。
211
+ - **舱壁**:Handler 可声明 `maxConcurrency`,同一 Handler 运行数受限;`execution_bus_state` 的 `waitReason` 会显示 `concurrency`/`bulkhead`/`resource`/`dispatch`,运行超过 30 秒的任务标记 `longRunning`。
212
+ - **资源互斥**:`host.edit`/`host.write` 按 `file_path` 资源键在同一总线实例内串行,避免同文件并发写;失败/取消/关闭都会释放。它是插件内互斥,不是 OS 文件锁或跨进程保证;同一文件的相对/绝对、大小写等别名写法可能绕过互斥。
213
+ - **唯一任务**:核心 Handler 接口支持 `dedupe:"payload"`;**0.12.1 起内置 host.* 不启用**——文件、权限或工作目录变化后缓存旧结果会静默返回过期内容。扩展 Handler 可显式声明并自行承担失效责任(失败/取消/unknown 不缓存)。
214
214
 
215
- **0.9.0 起,预算到期不再连带取消已受理执行。** `run_code` 因时间预算到期或程序结束时,宿主会中断其中的工具子调用;总线把这种中断视为**只停止等待**:已受理执行与宿主 Job 继续,程序返回预算错误后,可在后续 `run_code` 中用同一 `requestId` 的 `status`/`result` 取回结果,或用 `cancel` 取消。用户主动中断、会话取消、沙箱模式变化等其他原因仍会请求取消整个执行。该识别依赖 DSH 0.2.1-rc.2 的中断原因字符串(见[宿主兼容性](<docs/host-compatibility.md>)),不是宿主公开稳定契约;进程退出仍会丢失全部内存状态,保留期一小时。
215
+ **0.9.0 起,预算到期不再连带取消已受理执行。** `run_code` 因时间预算到期或程序结束时,宿主会中断其中的工具子调用;总线把这种中断视为**只停止等待**:已受理执行与宿主 Job 继续,程序结束或预算到期后,可在后续 `run_code` 中用同一 `requestId` 的 `status`/`result` 读快照,或重发同一 request 等待终态(`reused:true`),或用 `cancel` 取消。用户主动中断、会话取消、沙箱模式变化等其他原因仍会请求取消整个执行。该识别依赖 DSH 0.2.1-rc.2 的中断原因字符串(见[宿主兼容性](<docs/host-compatibility.md>)),不是宿主公开稳定契约;进程退出仍会丢失全部内存状态,保留期一小时。
216
216
 
217
217
  ## 如何理解任务状态?
218
218
 
@@ -287,7 +287,7 @@ npm run test:install # 临时目录安装,不操作日常 DSH
287
287
 
288
288
  - [协议和投递语义](<docs/protocol.md>):字段、状态、重复处理与取消规则。
289
289
  - [宿主兼容性与源码调用链](<docs/host-compatibility.md>):为什么可以等待,为什么不能跨重启恢复。
290
- - [验证记录](<docs/validation.md>)、[0.12.0 发布说明](<docs/releases/0.12.0.md>):已验证与未验证范围。
290
+ - [验证记录](<docs/validation.md>)、[0.12.1 发布说明](<docs/releases/0.12.1.md>):已验证与未验证范围。
291
291
  - [同类项目源码整合](<docs/INTEGRATION.md>):固定来源、采用范围、许可与验证边界。
292
292
  - [维护者发布流程](<docs/RELEASING.md>):标签 CI、安装包校验与正式发布门禁。
293
293
 
package/dist/bus.d.ts CHANGED
@@ -101,9 +101,14 @@ export declare class ExecutionBus {
101
101
  /** 唯一任务命中已成功终态时,构造一个不再执行 Handler 的只读结果记录。 */
102
102
  private syntheticUnique;
103
103
  private releaseResources;
104
+ /** 优先类别表按自有属性查;未知类别回退 default 权重,避免原型链 NaN。 */
105
+ private classRank;
106
+ /** 当前阻塞来源:Handler 舱壁优先于资源锁;undefined 表示可运行。 */
107
+ private blockedBy;
104
108
  /** 是否有空闲槽位供此 Entry 开始执行(Handler 舱壁 + 资源互斥)。 */
105
109
  private eligible;
106
- /** 类别优先级 + 等待老化:分数越低越先出队,长时间等待会逐步压过类别差异。 */
110
+ /** 类别优先级 + 等待老化:等待越久提升越多(不封顶),最低到 0;
111
+ * 同分时按队列先后(FIFO)决出,避免低优先类别饥饿。 */
107
112
  private effectivePriority;
108
113
  /** 对已到期的排队任务结算 QUEUE_WAIT_TIMEOUT,再从队列移除。 */
109
114
  private expireQueued;
package/dist/bus.js CHANGED
@@ -12,6 +12,10 @@ const isTerminal = (s) => !["queued", "running"].includes(s);
12
12
  function hashPayload(payload) {
13
13
  return createHash("sha256").update(canonical(payload)).digest("hex");
14
14
  }
15
+ /** 消息身份只保存摘要,避免为每个 messageId 保留完整消息文本占用内存。 */
16
+ function hashMessage(m) {
17
+ return createHash("sha256").update(canonical(m)).digest("hex");
18
+ }
15
19
  /** Volatile single-process bus; no acceptance durability or automatic execution retry. */
16
20
  export class ExecutionBus {
17
21
  options;
@@ -52,25 +56,44 @@ export class ExecutionBus {
52
56
  Array.isArray(scheduler) ||
53
57
  Reflect.ownKeys(scheduler).some((key) => typeof key !== "string" || !schedulerKeys.includes(key)))
54
58
  throw Error(ERRORS.INVALID_OPTIONS);
55
- if (!Number.isSafeInteger(scheduler.queueAgingMs) ||
56
- scheduler.queueAgingMs < 1 ||
57
- !Number.isSafeInteger(scheduler.longRunningMs) ||
58
- scheduler.longRunningMs < 1)
59
- throw Error(ERRORS.INVALID_OPTIONS);
60
- const order = scheduler.queueClassOrder;
61
- if (!order || typeof order !== "object" || Array.isArray(order))
59
+ for (const key of Reflect.ownKeys(scheduler)) {
60
+ const descriptor = Object.getOwnPropertyDescriptor(scheduler, key);
61
+ if (!("value" in descriptor) || !descriptor.enumerable)
62
+ throw Error(ERRORS.INVALID_OPTIONS);
63
+ }
64
+ // 只读取一次:拒绝 accessor,避免校验值与落库值不一致。
65
+ const queueAgingMs = scheduler.queueAgingMs;
66
+ const longRunningMs = scheduler.longRunningMs;
67
+ const rawOrder = scheduler.queueClassOrder;
68
+ if (!Number.isSafeInteger(queueAgingMs) ||
69
+ queueAgingMs < 1 ||
70
+ !Number.isSafeInteger(longRunningMs) ||
71
+ longRunningMs < 1 ||
72
+ !rawOrder ||
73
+ typeof rawOrder !== "object" ||
74
+ Array.isArray(rawOrder))
62
75
  throw Error(ERRORS.INVALID_OPTIONS);
63
- for (const [className, rank] of Object.entries(order))
64
- if (typeof className !== "string" ||
65
- className.length === 0 ||
76
+ const order = Object.create(null);
77
+ for (const className of Reflect.ownKeys(rawOrder)) {
78
+ if (typeof className !== "string")
79
+ throw Error(ERRORS.INVALID_OPTIONS);
80
+ if (["__proto__", "constructor", "prototype"].includes(className))
81
+ throw Error(ERRORS.INVALID_OPTIONS);
82
+ const descriptor = Object.getOwnPropertyDescriptor(rawOrder, className);
83
+ if (!("value" in descriptor) || !descriptor.enumerable)
84
+ throw Error(ERRORS.INVALID_OPTIONS);
85
+ const rank = descriptor.value;
86
+ if (className.length === 0 ||
66
87
  className.length > 64 ||
67
88
  !Number.isSafeInteger(rank) ||
68
89
  rank < 0)
69
90
  throw Error(ERRORS.INVALID_OPTIONS);
91
+ order[className] = rank;
92
+ }
70
93
  this.scheduler = Object.freeze({
71
- queueClassOrder: Object.freeze({ ...order }),
72
- queueAgingMs: scheduler.queueAgingMs,
73
- longRunningMs: scheduler.longRunningMs,
94
+ queueClassOrder: Object.freeze(order),
95
+ queueAgingMs: queueAgingMs,
96
+ longRunningMs: longRunningMs,
74
97
  });
75
98
  const allowed = ["sessionQueueLimit", "maxQueueWaitMs"];
76
99
  if (!admission ||
@@ -114,10 +137,14 @@ export class ExecutionBus {
114
137
  throw Error(ERRORS.DUPLICATE_HANDLER);
115
138
  if (!handler ||
116
139
  typeof handler !== "object" ||
140
+ typeof handler.validate !== "function" ||
141
+ typeof handler.run !== "function" ||
142
+ (handler.idempotent !== undefined && typeof handler.idempotent !== "boolean") ||
117
143
  (handler.queue !== undefined &&
118
144
  (typeof handler.queue !== "string" ||
119
145
  handler.queue.length === 0 ||
120
- handler.queue.length > 64)) ||
146
+ handler.queue.length > 64 ||
147
+ ["__proto__", "constructor", "prototype"].includes(handler.queue))) ||
121
148
  (handler.maxConcurrency !== undefined &&
122
149
  (!Number.isSafeInteger(handler.maxConcurrency) ||
123
150
  handler.maxConcurrency < 1)) ||
@@ -125,7 +152,17 @@ export class ExecutionBus {
125
152
  (handler.dedupe === "payload" && handler.idempotent !== true) ||
126
153
  (handler.resources !== undefined && typeof handler.resources !== "function"))
127
154
  throw Error(ERRORS.INVALID_HANDLER_OPTIONS);
128
- const registration = { handler, token: Symbol(name) };
155
+ // 注册时快照并冻结调度/复用选项,运行期修改原对象不再影响已注册行为。
156
+ const fixed = Object.freeze({
157
+ idempotent: handler.idempotent,
158
+ queue: handler.queue,
159
+ maxConcurrency: handler.maxConcurrency,
160
+ dedupe: handler.dedupe,
161
+ resources: handler.resources?.bind(handler),
162
+ validate: handler.validate.bind(handler),
163
+ run: handler.run.bind(handler),
164
+ });
165
+ const registration = { handler: fixed, token: Symbol(name) };
129
166
  this.handlers.set(name, registration);
130
167
  let disposed = false;
131
168
  return () => {
@@ -232,14 +269,14 @@ export class ExecutionBus {
232
269
  if (e.handler.idempotent === false && isTerminal(e.view.state))
233
270
  throw Error(ERRORS.MUTATING_REUSE_DENIED);
234
271
  const previous = e.messages.get(m.messageId);
235
- if (previous && previous !== canonical(m))
272
+ if (previous && previous !== hashMessage(m))
236
273
  throw Error(ERRORS.MESSAGE_CONFLICT);
237
274
  if (!previous && e.messages.size >= LIMITS.messageAccountLimit)
238
275
  throw Error(ERRORS.MESSAGE_LIMIT);
239
276
  // 先取得附着,再提交消息身份:附着被拒(WAITER_LIMIT)时无副作用。
240
277
  const attached = this.attachment(e, onEvent, m.messageId, true);
241
278
  if (!previous)
242
- e.messages.set(m.messageId, canonical(m));
279
+ e.messages.set(m.messageId, hashMessage(m));
243
280
  this.telemetry.record(e.trace, "reused");
244
281
  return attached;
245
282
  }
@@ -249,6 +286,9 @@ export class ExecutionBus {
249
286
  if (!handler)
250
287
  throw Error(ERRORS.UNKNOWN_HANDLER);
251
288
  handler.validate(parts.input);
289
+ // validate 是用户代码:可能在同步执行中触发 close()。
290
+ if (this.closed)
291
+ throw Error(ERRORS.CLOSED);
252
292
  const dedupeKey = handler.dedupe === "payload"
253
293
  ? JSON.stringify([owner.sessionId, parts.handler, fingerprint])
254
294
  : undefined;
@@ -269,6 +309,9 @@ export class ExecutionBus {
269
309
  }
270
310
  }
271
311
  const resources = this.resourcesFor(handler, parts.input);
312
+ // resources 也是用户代码,返回后重新确认总线未在同步段关闭。
313
+ if (this.closed)
314
+ throw Error(ERRORS.CLOSED);
272
315
  if (this.entries.size >= this.options.recordLimit ||
273
316
  this.queue.length >= this.options.queueLimit)
274
317
  throw Error(ERRORS.BACKPRESSURE);
@@ -306,7 +349,7 @@ export class ExecutionBus {
306
349
  dispatching: false,
307
350
  sourceSequence: 0,
308
351
  created: Date.now(),
309
- messages: new Map([[m.messageId, canonical(m)]]),
352
+ messages: new Map([[m.messageId, hashMessage(m)]]),
310
353
  };
311
354
  this.entries.set(key, e);
312
355
  if (dedupeKey !== undefined)
@@ -375,11 +418,22 @@ export class ExecutionBus {
375
418
  delivery: "pending",
376
419
  result: target.view.result,
377
420
  }));
421
+ let resultBytes = 0;
422
+ try {
423
+ if (snapshot.result !== undefined)
424
+ resultBytes = Buffer.byteLength(JSON.stringify(snapshot.result));
425
+ }
426
+ catch {
427
+ resultBytes = target.resultBytes;
428
+ }
378
429
  let resolve;
379
430
  const done = new Promise((r) => (resolve = r));
431
+ const trace = this.telemetry.begin();
380
432
  const e = {
381
- trace: this.telemetry.begin(),
382
- resultBytes: 0,
433
+ trace,
434
+ resultBytes,
435
+ startedAt: trace.at,
436
+ finishedAt: trace.at,
383
437
  invoke: undefined,
384
438
  correlationId: m.messageId,
385
439
  owner: owner.sessionId,
@@ -399,9 +453,10 @@ export class ExecutionBus {
399
453
  dispatching: false,
400
454
  sourceSequence: 0,
401
455
  created: Date.now(),
402
- messages: new Map([[m.messageId, canonical(m)]]),
456
+ messages: new Map([[m.messageId, hashMessage(m)]]),
403
457
  };
404
458
  this.entries.set(key, e);
459
+ this.retainedResultBytes += resultBytes;
405
460
  this.telemetry.record(e.trace, "accepted");
406
461
  this.telemetry.record(e.trace, "unique_reused");
407
462
  e.resolve(snapshot);
@@ -412,24 +467,31 @@ export class ExecutionBus {
412
467
  if (this.resourceLocks.get(resource) === e)
413
468
  this.resourceLocks.delete(resource);
414
469
  }
415
- /** 是否有空闲槽位供此 Entry 开始执行(Handler 舱壁 + 资源互斥)。 */
416
- eligible(e, now) {
417
- void now;
470
+ /** 优先类别表按自有属性查;未知类别回退 default 权重,避免原型链 NaN。 */
471
+ classRank(queueClass) {
472
+ const order = this.scheduler.queueClassOrder;
473
+ return Object.hasOwn(order, queueClass) ? order[queueClass] : 3;
474
+ }
475
+ /** 当前阻塞来源:Handler 舱壁优先于资源锁;undefined 表示可运行。 */
476
+ blockedBy(e) {
418
477
  const limit = e.handler.maxConcurrency;
419
478
  if (limit !== undefined &&
420
479
  (this.runningByHandler.get(e.handler) ?? 0) >= limit)
421
- return false;
480
+ return "bulkhead";
422
481
  for (const resource of e.resources)
423
482
  if (this.resourceLocks.has(resource))
424
- return false;
425
- return true;
483
+ return "resource";
484
+ return undefined;
485
+ }
486
+ /** 是否有空闲槽位供此 Entry 开始执行(Handler 舱壁 + 资源互斥)。 */
487
+ eligible(e) {
488
+ return this.blockedBy(e) === undefined;
426
489
  }
427
- /** 类别优先级 + 等待老化:分数越低越先出队,长时间等待会逐步压过类别差异。 */
490
+ /** 类别优先级 + 等待老化:等待越久提升越多(不封顶),最低到 0;
491
+ * 同分时按队列先后(FIFO)决出,避免低优先类别饥饿。 */
428
492
  effectivePriority(e, now) {
429
- const rank = this.scheduler.queueClassOrder[e.queueClass] ?? 3;
430
- const boost = Math.min(2, Math.floor(Math.max(0, now - e.trace.at) / this.scheduler.queueAgingMs));
431
- // 老化只削平类别差距,不把优先级推到负值;同分时按队列先后(FIFO)决出。
432
- return Math.max(0, rank - boost);
493
+ const boost = Math.floor(Math.max(0, now - e.trace.at) / this.scheduler.queueAgingMs);
494
+ return Math.max(0, this.classRank(e.queueClass) - boost);
433
495
  }
434
496
  /** 对已到期的排队任务结算 QUEUE_WAIT_TIMEOUT,再从队列移除。 */
435
497
  expireQueued(now) {
@@ -488,7 +550,7 @@ export class ExecutionBus {
488
550
  throw Error(ERRORS.INVALID_CONTROL);
489
551
  this.sweep();
490
552
  const e = this.get(owner, m.requestId);
491
- const previous = e.messages.get(m.messageId), fingerprint = canonical(m);
553
+ const previous = e.messages.get(m.messageId), fingerprint = hashMessage(m);
492
554
  if (previous && previous !== fingerprint)
493
555
  throw Error(ERRORS.MESSAGE_CONFLICT);
494
556
  if (!previous) {
@@ -531,9 +593,7 @@ export class ExecutionBus {
531
593
  waitReason: e.view.state === "queued"
532
594
  ? this.running >= this.options.concurrency
533
595
  ? "concurrency"
534
- : this.eligible(e, now)
535
- ? "dispatch"
536
- : "resource"
596
+ : (this.blockedBy(e) ?? "dispatch")
537
597
  : null,
538
598
  longRunning: e.view.state === "running" &&
539
599
  e.startedAt !== undefined &&
@@ -637,7 +697,7 @@ export class ExecutionBus {
637
697
  let chosenScore = Infinity;
638
698
  for (let i = 0; i < this.queue.length; i++) {
639
699
  const e = this.queue[i];
640
- if (e.owner !== owner || !this.eligible(e, now))
700
+ if (e.owner !== owner || !this.eligible(e))
641
701
  continue;
642
702
  const score = this.effectivePriority(e, now);
643
703
  if (score < chosenScore) {
@@ -11,7 +11,9 @@ export declare function hostToolHandler(toolName: string, opts: {
11
11
  dedupe?: "payload";
12
12
  }): Handler;
13
13
  export declare const testHandler: Handler;
14
- /** 读取:无副作用,幂等,终态结果可复用;可按 payload 指纹做唯一任务。 */
14
+ /** 读取:无副作用,幂等,终态结果可复用。0.12.1 起内置 host.* 不再声明
15
+ * payload 去重:文件/权限/工作目录会变化,缓存旧结果会静默返回过期内容。
16
+ * 扩展 Handler 可自行声明 dedupe 并承担失效责任。 */
15
17
  export declare const readHandler: Handler;
16
18
  /** 文件名搜索:只读,幂等,终态结果可复用;搜索基数由宿主工具限制。 */
17
19
  export declare const globHandler: Handler;
package/dist/handlers.js CHANGED
@@ -66,15 +66,17 @@ function validateEdit(input) {
66
66
  (input.replace_all !== undefined && typeof input.replace_all !== "boolean"))
67
67
  throw Error(ERRORS.INVALID_EDIT_INPUT);
68
68
  }
69
- /** 同一路径的文件写入/编辑互斥;路径按调用原文作为资源键(不做跨平台归一化)。 */
69
+ /** 同一路径的文件写入/编辑互斥;路径按调用原文作为资源键(不做跨平台归一化,
70
+ * 因此别名写法可能绕过互斥;见 docs/validation.md)。 */
70
71
  function fileResource(input) {
71
72
  return ["file:" + String(input.file_path)];
72
73
  }
73
- /** 读取:无副作用,幂等,终态结果可复用;可按 payload 指纹做唯一任务。 */
74
+ /** 读取:无副作用,幂等,终态结果可复用。0.12.1 起内置 host.* 不再声明
75
+ * payload 去重:文件/权限/工作目录会变化,缓存旧结果会静默返回过期内容。
76
+ * 扩展 Handler 可自行声明 dedupe 并承担失效责任。 */
74
77
  export const readHandler = hostToolHandler("read", {
75
78
  idempotent: true,
76
79
  queue: "fs",
77
- dedupe: "payload",
78
80
  validate: validateRead,
79
81
  prepare: (input) => ({
80
82
  ...input,
@@ -93,7 +95,6 @@ function validateGlob(input) {
93
95
  export const globHandler = hostToolHandler("glob", {
94
96
  idempotent: true,
95
97
  queue: "fs",
96
- dedupe: "payload",
97
98
  validate: validateGlob,
98
99
  });
99
100
  function validateGrep(input) {
@@ -125,7 +126,6 @@ function validateGrep(input) {
125
126
  export const grepHandler = hostToolHandler("grep", {
126
127
  idempotent: true,
127
128
  queue: "fs",
128
- dedupe: "payload",
129
129
  validate: validateGrep,
130
130
  });
131
131
  function validateWrite(input) {
@@ -1,4 +1,4 @@
1
- # 同类项目源码参考与整合(0.5.0)
1
+ # 同类项目源码参考与整合(0.5.0–0.12.1)
2
2
 
3
3
  ## 本轮交付
4
4
 
@@ -38,7 +38,7 @@
38
38
  本轮实现(0.12.0):
39
39
 
40
40
  1. Handler 调度声明:queue、maxConcurrency、resources(input)、dedupe:"payload";register 时校验。
41
- 2. 调度:同会话内按类别优先级(fs < diagnostic < command < default)+ 等待老化(每 30 秒削平一级,最多两级)选择;跨会话仍按会话轮转;Handler.maxConcurrency 作为舱壁。
41
+ 2. 调度:同会话内按类别优先级(fs < diagnostic < command < default)+ 等待老化(每 30 秒提升一级,不封顶,最低到 0)选择;跨会话仍按会话轮转;Handler.maxConcurrency 作为舱壁。0.12.1 修正老化封顶导致的 default 类饥饿。
42
42
  3. 资源互斥:Handler.resources 返回稳定字符串键(host.edit/host.write 使用 file:<file_path>);同一总线实例内同键串行,失败/取消/关闭释放;排队期限仍然有效。
43
43
  4. 唯一任务:幂等 Handler 声明 dedupe:"payload"(host.read/glob/grep);同会话同 handler+payload 已有 succeeded 终态时,新 requestId 直接复用快照并带 reused:true;失败结果不缓存。
44
44
  5. 诊断:execution_bus_state 任务增加 longRunning(30 秒阈值);waitReason 增加 resource。
@@ -1,5 +1,7 @@
1
1
  # 引擎经验优化整合与验收
2
2
 
3
+ > 历史文档:本页记录 0.4.0 的优化。其中“按资源串行”当时暂缓,已在 0.12.0 以 Handler.resources + 资源锁实现(见 [整合说明](<INTEGRATION.md>) 与 [0.12.1 发布说明](<releases/0.12.1.md>));冲突时以 README/protocol 与当前发布说明为准。
4
+
3
5
  本轮统一在 0.4.0 交付,不将普通 BUG 修复冒充整体架构优化,不声称模型性能已提高。运行逻辑仅作用于萧何总线及其 Job 关联;保留 0.3.0 的 PTC 派生方式、权限和原工具能力。
4
6
 
5
7
  ## 优先级与交付
package/docs/PRODUCT.md CHANGED
@@ -12,7 +12,9 @@
12
12
 
13
13
  0.11.0 根据 pilot 收紧为收益导向触发:重复提交/重试去重、预算结束后继续/跨步骤取回、多等待者同一执行才用总线,时长本身不是理由;新增简化调用(requestId+handler+input,messageId 自动生成)与控制(requestId+type),完整 v1 消息仍兼容;快照在复用时带 reused:true,并新增只读的会话采用率统计脚本。
14
14
 
15
- 0.12.0 补充调度隔离:Handler 可声明调度类别、舱壁、资源键与 payload 唯一任务;内置写 Handler 按文件路径互斥,只读 Handler 的相同输入可在不同 requestId 间复用成功结果;状态快照增加 longRunning 与 resource 等待原因。参考来源与许可见 [整合说明](<INTEGRATION.md>),未引入第三方运行依赖;fast-check 仅用于开发属性测试。
15
+ 0.12.0 补充调度隔离:Handler 可声明调度类别、舱壁、资源键与 payload 唯一任务;内置写 Handler 按文件路径互斥;状态快照增加 longRunning 与等待原因。参考来源与许可见 [整合说明](<INTEGRATION.md>),未引入第三方运行依赖;fast-check 仅用于开发属性测试。
16
+
17
+ 0.12.1 修正 0.12.0 的默认行为风险:内置 host.* 关闭 payload 去重(文件/权限/工作目录变化后旧结果会静默过期),只保留核心 Handler 能力供扩展自担失效责任;等待老化不封顶,避免 default 类别饥饿;唯一复用副本计入保留字节并写入时间戳;waitReason 区分 bulkhead 与 resource;注册时校验并冻结调度选项、拒绝 accessor/原型污染键;validate/resources 内同步 close 后不再插入不可结算记录;消息身份改存摘要,降低同 requestId 多条消息的内存占用。
16
18
 
17
19
  ## 入口及配置层次
18
20
 
package/docs/protocol.md CHANGED
@@ -28,8 +28,8 @@
28
28
 
29
29
  ### 去重与消息额度
30
30
 
31
- - 逻辑键是 **当前存活总线实例 + 宿主会话身份 + requestId**。不同会话不能查询/取消彼此记录;同预设仍共享容量。
32
- - 对参数规范排序后的摘要仅用于同 requestId 的冲突比较,不用于永久按参数去重。相同参数的新 requestId 仍是新操作。
31
+ - 逻辑键是 **当前存活总线实例 + 宿主会话身份 + requestId**。不同会话不能查询/取消彼此记录;同一预设修订(generation)的会话共享总线容量。
32
+ - 对参数规范排序后的摘要仅用于同 requestId 的冲突比较,不用于永久按参数去重。相同参数的新 requestId 默认仍是新操作;仅显式声明 `dedupe:"payload"` 的幂等 Handler 可复用同会话已成功的 payload 结果(0.12.1 起内置 host.* 不声明)。
33
33
  - 普通消息最多记账 128 条,含首次请求、重复请求及查询。满后拒绝普通新消息;首次尚未确认的有效取消可使用一个额外控制槽,最多 129 条。已记账消息的冲突检查不因控制槽而放松。
34
34
  - 活跃观察者附着最多 32 个;每个附着独立占名额,即使共用回调。附着被拒绝时不占用新消息编号/额度。
35
35
  - 终态超期清理后旧请求键失效,不应把此机制用于长期避免外部副作用重复。
@@ -110,7 +110,7 @@ core 不传配置时用默认值;显式配置必须完整包含 concurrency、
110
110
 
111
111
  期限使用单调时钟,计时器及出队前均检查,超期只将 queued 结算 failed / QUEUE_WAIT_TIMEOUT,不调用 Handler、不设置 cancelRequested、不重试,沿原 done/Job 返回。开始运行、取消或关闭时清除计时器。事件循环繁忙会令通知晚于期限,但不会让超期任务出队执行。已运行任务继续遵守原执行/取消规则。
112
112
 
113
- execution_bus_state 工具仅接受 {},从宿主 Agent 取 sessionId,拒绝匿名或自报身份,关闭后拒绝查询。它读取 core inspect 的不可变快照,不创建 Job、不改变消息额度;查询会做正常终态 TTL 清理。不包含输入/输出,不访问其他会话。最多 50 条、活跃优先,其后按受理次序倒序列出终态;总数未截断,truncated 提示列表截断。元数据含 requestId、state、delivery、cancelRequested、固定 error/progress、waitReason(concurrency/dispatch/resource)、longRunning(运行超过 30 秒)、queueMs/runMs/remainingQueueMs。phase terminal 只表示执行终态且尚未标记返回,不代表可推断宿主 Job 清理进度。
113
+ execution_bus_state 工具仅接受 {},从宿主 Agent 取 sessionId,拒绝匿名或自报身份,关闭后拒绝查询。它读取 core inspect 的不可变快照,不创建 Job、不改变消息额度;查询会做正常终态 TTL 清理。不包含输入/输出,不访问其他会话。最多 50 条、活跃优先,其后按受理次序倒序列出终态;总数未截断,truncated 提示列表截断。元数据含 requestId、state、delivery、cancelRequested、固定 error/progress、waitReason(concurrency/bulkhead/resource/dispatch)、longRunning(运行超过 30 秒)、queueMs/runMs/remainingQueueMs。phase terminal 只表示执行终态且尚未标记返回,不代表可推断宿主 Job 清理进度。
114
114
 
115
115
  core inspect(trustedOwner) 要求非空 sessionId,但不提供鉴权,可信身份和访问控制属于嵌入宿主。勿把 core 的任意 owner 参数直接暴露给远端。状态不是持久审计/模型消费回执,不应循环调用。
116
116
 
@@ -128,6 +128,21 @@ core 实例的 `diagnostics()` 返回只读用途的独立快照:阶段计数
128
128
 
129
129
  0.11.0 起,模型可传简化输入:请求 `{requestId, handler, input, messageId?}`、控制 `{requestId, type, messageId?}`;插件在进入总线前补齐完整 v1 消息,完整 v1 消息仍被接受。返回快照在附着已有执行时追加可选 `reused:true`,只表示复用了逻辑执行,不代表外部副作用对账。
130
130
 
131
- 0.12.0 扩展 Handler 可选声明:`queue`(调度类别,内置 fs/diagnostic/command)、`maxConcurrency`(同一 Handler 舱壁)、`resources(input)`(资源键串行)、`dedupe:"payload"`(幂等 Handler 的唯一任务复用)。调度和资源锁只在单个总线实例内生效;资源键是调用方提供的字符串,不是 OS 文件锁或跨进程保证。唯一任务只复用 succeeded 终态,失败/取消/unknown 不缓存;命中时返回快照带 `reused:true`,且不会再次执行 Handler。
131
+ 0.12.0 扩展 Handler 可选声明:`queue`(调度类别,内置 fs/diagnostic/command)、`maxConcurrency`(同一 Handler 舱壁)、`resources(input)`(资源键串行)、`dedupe:"payload"`(幂等 Handler 的唯一任务复用)。调度和资源锁按总线实例生效;插件实例由同一预设修订(generation)的会话共享,跨进程/跨代际不保证。资源键是调用方提供的字符串,不是 OS 文件锁。唯一任务只复用 succeeded 终态,失败/取消/unknown 不缓存;命中时返回快照带 `reused:true`,且不会再次执行 Handler。
132
+
133
+ 0.12.1 起内置 host.read/host.glob/host.grep **不再声明** `dedupe:"payload"`:文件、权限或工作目录变化后缓存结果可能过期,扩展 Handler 如声明该能力需自行承担失效责任。同时 `waitReason` 区分 `bulkhead`(Handler 舱壁)与 `resource`(资源锁)。
132
134
 
133
135
  内置 test、host.read、host.glob 与 host.grep 没有副作用;host.edit、host.write 与 host.pwsh 是非幂等写/命令 Handler,通过 `Handler.idempotent=false` 显式声明,其终态结果复用被 `MUTATING_REUSE_DENIED` 拒绝,运行中的重复附着仍只等待同一执行。0.8.0 新增宿主工具输入错误码 INVALID_GLOB_INPUT、INVALID_GREP_INPUT 与 INVALID_PWSH_INPUT;协议既有错误码不改名。host.pwsh 的后台结果只是宿主 Job ID,不是完成回执。当前没有 ctx.executionBus 服务、远端传输、跨插件动态注册或外部副作用对账契约。扩展有副作用 Handler 前必须显式声明幂等并另行设计授权与崩溃对账,不能仅依赖本版请求键。
136
+
137
+
138
+ ## 9. 错误词汇(0.12.1 补录)
139
+
140
+ 错误码值即协议 v1 稳定词汇,不得重命名;完整定义见 `src/error-codes.ts`。
141
+
142
+ - 协议解析:`INVALID_OBJECT`、`UNKNOWN_FIELD`、`UNSUPPORTED_VERSION`、`INVALID_ID`、`UNSUPPORTED_TYPE`、`MESSAGE_TOO_LARGE`。
143
+ - 严格 JSON:`JSON_LIMIT`、`INVALID_JSON`、`CYCLIC_JSON`、`INVALID_JSON_ARRAY`、`INVALID_JSON_OBJECT`、`INVALID_JSON_KEY`、`INVALID_JSON_PROPERTY`。
144
+ - 总线调度与准入:`CLOSED`、`EXPECTED_REQUEST`、`INVALID_REQUEST`、`REQUEST_CONFLICT`、`MESSAGE_CONFLICT`、`MESSAGE_LIMIT`、`UNKNOWN_HANDLER`、`BACKPRESSURE`、`SESSION_BACKPRESSURE`、`WAITER_LIMIT`、`EXPECTED_CONTROL`、`INVALID_CONTROL`、`NOT_FOUND`、`UNTRUSTED_CONTEXT`、`OWNING_SESSION_REQUIRED`、`QUEUE_WAIT_TIMEOUT`、`RESULT_NOT_RETAINED`、`CANCELLED`、`CANCELLATION_UNCONFIRMED`、`HANDLER_FAILED`、`INVALID_ADMISSION_POLICY`、`INVALID_OPTIONS`、`DUPLICATE_HANDLER`、`MUTATING_REUSE_DENIED`、`INVALID_HANDLER_OPTIONS`、`INVALID_RESOURCES`。
145
+ - 宿主投影:`DUPLICATE_EXECUTION_HANDLE`、`INVALID_STATE_QUERY`、`HOST_READ_FAILED`、`HOST_TOOL_FAILED`、`HOST_JOB_UNAVAILABLE`。
146
+ - 预设派生:`PTC_PRESET_TOO_LARGE`、`PTC_PRESET_ALREADY_CONTAINS_BUS`、`PTC_PRESET_RELATIVE_BASE_UNSUPPORTED`、`PTC_PRESET_UNAVAILABLE`。
147
+ - 内置 Handler:`INVALID_TEST_INPUT`、`INVALID_READ_INPUT`、`INVALID_EDIT_INPUT`、`INVALID_WRITE_INPUT`、`INVALID_GLOB_INPUT`、`INVALID_GREP_INPUT`、`INVALID_PWSH_INPUT`、`READ_LIMIT`、`CONTROLLED_FAILURE`、`HOST_CAPABILITY_REQUIRED`。
148
+
@@ -1,5 +1,7 @@
1
1
  # 萧何 / Xiao He 0.11.1
2
2
 
3
+ > **未发布版本**:本标签 CI 在 windows-latest + Node 22 上因真实 PTC 预算用例时序失败,未发布到 npm/GitHub;其提示与文档修正并入 [0.11.2](<0.11.2.md>)。保留本文仅用于历史追溯。
4
+
3
5
  ## 修正跨步骤取回说明(pilot 发现)
4
6
 
5
7
  本版只修正提示与文档,不改变执行机制。
@@ -0,0 +1,30 @@
1
+ # 萧何 / Xiao He 0.12.1
2
+
3
+ ## 0.12.0 行为风险热修
4
+
5
+ 本版修正 0.12.0 默认 payload 去重导致的数据正确性问题,并处理同轮审查发现的高优先级调度/生命周期/文档缺陷。**wire 协议与工具调用形状不变。**
6
+
7
+ ## 修正
8
+
9
+ - **关闭内置 host.* 的 payload 去重**:0.12.0 给 `host.read`/`host.glob`/`host.grep` 默认声明了 `dedupe:"payload"`。文件被编辑、会话切换工作目录或权限变化后,同输入的新 requestId 会静默返回旧内容(现场已复现:read 记 v1 → write 改 v2 → 再 read 仍返回 v1)。0.12.1 起内置 Handler 不再声明该能力,恢复“相同参数的新 requestId 是新操作”;核心 `Handler.dedupe` 能力保留,扩展 Handler 如声明须自行承担失效责任。
10
+ - **等待老化不封顶**:原 `boost=min(2,...)` 削不平 default(3) 与 fs(0) 的差距,持续 fs 负载下 default 类可饥饿/必超时;改为按等待时间持续提升、最低到 0,同分按 FIFO。
11
+ - **唯一复用记录计入保留字节与时间戳**:`syntheticUnique` 深拷贝结果但 `resultBytes=0`,且无 startedAt/finishedAt 导致 `queueMs` 随保留期增长;现按结果字节计入、写入时间戳,sweep/close 正常递减。
12
+ - **waitReason 区分舱壁与资源**:新增 `bulkhead`,不再把 Handler.maxConcurrency 等待误报为 `resource`。
13
+ - **注册时校验并快照 Handler 选项**:校验 run/validate 为函数、idempotent 严格布尔、queue/dedupe 合法;冻结调度字段并拒绝 `__proto__` 等类名,避免运行期篡改、NaN 优先级和永不调度;scheduler 策略拒绝 accessor 与原型污染键,数值只读取一次后校验。
14
+ - **关闭重入**:validate/resources 同步执行中触发 `close()` 后不再向已关闭总线插入永不结算的记录,改为拒绝准入(CLOSED)。
15
+ - **消息身份改存 SHA-256 摘要**:同一 requestId 多条消息不再保存完整 canonical 文本,避免消息额度内无字节上限的内存放大;冲突检测语义等价。
16
+ - **文档修正**:protocol 恢复“新 requestId 新操作”并补错误词汇表;validation 更新 PTC 预算参数(8s/12s)、资源互斥跨会话范围(同一预设修订共享,跨进程/跨代际不保证)、修正 0.12.0 发布说明中的范围表述;0.11.1 标注“未发布”;OPTIMIZATION 标注历史并链接当前实现;INTEGRATION 标题与老化说明同步。
17
+
18
+ ## 验证
19
+
20
+ - 新增 6 项回归:default 类老化不饥饿、bulkhead/resource 等待原因、注册负例、scheduler accessor/原型污染、关闭重入、唯一复用字节/时间戳、资源锁失败/取消释放。
21
+ - 本地全量测试通过(122 + 6 = 128 项;以 `npm run check` 实际输出为准)。
22
+ - 未复制第三方源码、未新增运行依赖;fast-check 仍仅为 devDependency。
23
+
24
+ ## 升级说明
25
+
26
+ 需要 DSH 0.2.1-rc.2。工具名、wire 协议与既有错误码不变;行为变更仅影响内置 host.* 的 payload 去重(默认关闭)。旧版创建的总线实例/记录不迁移;重载或升级会创建新的预设修订,旧 requestId 不可取回。
27
+
28
+ ## 验证与限制
29
+
30
+ 调度与资源锁按预设修订共享总线实例,跨进程、跨代际、OS 级文件锁均不保证;资源键别名(相对/绝对、大小写)可能绕过互斥。真实模型、桌面 UI 与实际沙箱仍未完整验收。
@@ -1,6 +1,6 @@
1
1
  # 验证范围与使用限制
2
2
 
3
- 本文说明当前版本的证据边界,不是生产环境认证。当前能力基线为 **v0.12.0**,在 0.5.0 的会话准入/排队、0.7.0 的宿主工具转发、0.8.0 的搜索/命令 Handler、0.9.0 的预算分离等待与 0.10.0 的采用面改造上,收紧为收益导向触发并加入简化调用、复用标记与本地采用率统计;0.11.1 补充跨步骤取回姿势,0.11.3 补记指引级 A/B 证据,0.12.0 加入调度类别/舱壁/资源互斥/唯一任务与属性测试;预设沿用 v0.3.0 的宿主 PTC 声明派生方式。验证环境为 Windows、Node.js 24.21.0,宿主组件锁定 DSH 0.2.1-rc.2、Cordis 4.0.4。
3
+ 本文说明当前版本的证据边界,不是生产环境认证。当前能力基线为 **v0.12.1**,在 0.5.0 的会话准入/排队、0.7.0 的宿主工具转发、0.8.0 的搜索/命令 Handler、0.9.0 的预算分离等待与 0.10.0 的采用面改造上,收紧为收益导向触发并加入简化调用、复用标记与本地采用率统计;0.11.1 补充跨步骤取回姿势,0.11.3 补记指引级 A/B 证据,0.12.0 加入调度类别/舱壁/资源互斥/唯一任务与属性测试,0.12.1 关闭内置 host.* 的 payload 去重并修正老化/字节计数/等待原因/注册校验/关闭重入问题;预设沿用 v0.3.0 的宿主 PTC 声明派生方式。验证环境为 Windows、Node.js 24.21.0,宿主组件锁定 DSH 0.2.1-rc.2、Cordis 4.0.4。
4
4
 
5
5
  ## 已验证什么?
6
6
 
@@ -9,10 +9,11 @@
9
9
  | 总线核心 | 参数、队列、并发、重复请求、冲突、取消竞争、结果保存、TTL、监听与注销 | 程序内部约束及已覆盖异常路径可重复检查 |
10
10
  | 真实宿主组件 | Cordis、ToolRuntime、LocalJobs、LocalFS、文件工具;host.glob/host.grep/host.pwsh 的转发测试使用宿主工具桩 | 请求经过真实工具管线;权限拒绝、Job 进度、共享任务、参数校验、幂等分流与清理逻辑有效;Agent 身份与工具实现为夹具,桩不证明真实搜索/命令工具实现 |
11
11
  | 真实会话循环 | AgentLoop、AgentRegistry、SessionStore;脚本模型适配器 | 内存会话中原工具结果只提交一次,完成后再进入下一次适配器调用 |
12
- | PTC 预算分离等待 | 真实 Node PTC `run_code` 以 2 秒预算等待 5 秒总线执行:预算到期后执行继续、宿主 Job 保留、同 requestId 取回结果;外部中断仍取消执行 | 在 DSH 0.2.1-rc.2 实际中断原因下成立;固定字符串不是跨版本契约 |
12
+ | PTC 预算分离等待 | 真实 Node PTC `run_code` 以 8 秒预算等待 12 秒总线执行:预算到期后执行继续、宿主 Job 保留、同 requestId 取回结果;外部中断仍取消执行 | 在 DSH 0.2.1-rc.2 实际中断原因下成立;固定字符串不是跨版本契约 |
13
13
  | 简化调用与复用标记 | 简化请求/控制、完整 v1 消息兼容、终态与运行中复用返回 `reused:true`、非法形状拒绝 | 组件级测试确认;不改变 wire 语义与副作用边界 |
14
14
  | 采用策略 A/B(指引级) | 同一版本 0.11.2、同一模型、5 类任务各 5 个全新子会话:可选触发 vs 总线优先指引,统计总线调用/复用/耗时 | 指出可选触发更优(总线优先把一次性长命令也包装、并有约 19s 排队延迟);非预设级、并发单机小样本,非统计显著 |
15
- | 调度隔离与资源互斥 | Handler queue/maxConcurrency/resources、类别优先级 + 等待老化、资源等待期限、waitReason/longRunning、唯一任务只复用 succeeded | 组件级回归确认;仅在单个总线实例内成立,不是 OS 锁或跨进程保证 |
15
+ | 调度隔离与资源互斥 | Handler queue/maxConcurrency/resources、类别优先级 + 等待老化(不封顶)、资源等待期限、waitReason(含 bulkhead)/longRunning、唯一任务只复用 succeeded | 组件级回归确认;按预设修订共享总线实例,跨进程/跨代际不保证,不是 OS 锁 |
16
+ | 0.12.1 热修 | 内置 host.* 关闭 payload 去重、老化不封顶、唯一复用字节/时间戳、注册选项快照校验、validate/resources 内 close 重入、消息身份改摘要 | 组件级回归确认;不改变 wire 协议与工具调用形状 |
16
17
  | 属性测试 | fast-check 随机提交/重放与容量压力不变量(固定种子) | 开发依赖(MIT);随机覆盖不是穷尽验证,种子固定保证可复现 |
17
18
  | 预设隔离 | AgentPresetRegistry 与 Loader | 派生预设可激活;源 ptc 声明不变、普通 Agent 不可见总线;原 write/edit/Job 工具保留,宿主前置策略拒绝仍有效 |
18
19
  | 故障注入 | 克隆失败、观察者拒绝、Job 等待/清理异常、先行移除与卸载交错 | 指定故障下的回归约束;不证明所有故障组合都已覆盖 |
@@ -53,7 +54,7 @@ eb95b4e51675f5f1821275e0812ef4dd4984b1aba45a52fbc6bab3518bbf6f38
53
54
  | 从真实 tgz 经宿主插件管理器加载预设、卸载与回滚 | 未完成完整桌面流程;源码工作区的 Loader 测试不能替代 |
54
55
  | 真实 glob/grep/pwsh 工具实现 | 未在测试环境启用:真实搜索工具依赖打包 ripgrep 与 subprocess 服务,真实 pwsh 依赖 shell/sandbox/审批组合;本版只验证按 handler 名转发、输入校验、幂等分流与失败固定码 |
55
56
  | 跨版本 PTC 中断原因 | 分离等待只识别 DSH 0.2.1-rc.2 实际使用的固定中断原因字符串;宿主升级后未知原因保守按取消处理,需复核 |
56
- | 跨会话/跨进程资源互斥 | 未实现:资源键互斥只在单个总线实例内;不提供 OS 级文件锁,宿主插件总线作用域需按实际部署核对 |
57
+ | 跨会话/跨进程资源互斥 | 同一预设修订(generation)的会话共享总线实例,资源锁/舱壁/容量跨会话生效;不提供 OS 级文件锁,跨进程、跨代际不保证;同一文件的相对/绝对、大小写等别名写法可能绕过文件互斥 |
57
58
  | 模式采用率与效果 | 已完成指引级 A/B(可选触发 vs 总线优先),但非预设级、并发单机小样本,不具统计显著性;轮次/耗时/成功率的大样本对照仍未完成,不能由 schema 或单元测试推断 |
58
59
  | 磁盘会话结果提交及重启恢复原调用 | 未验收磁盘提交;本插件不支持跨重启恢复 |
59
60
  | 减少真实模型轮次、状态查询或总耗时的对照 | 未验证,不给提升比例 |
@@ -62,7 +63,7 @@ eb95b4e51675f5f1821275e0812ef4dd4984b1aba45a52fbc6bab3518bbf6f38
62
63
  ## 需要知道的运行约束
63
64
 
64
65
  1. 受理、队列、结果及交付标记均在内存。进程关闭会丢失,宿主会话日志不等于插件恢复执行权。
65
- 2. 同一预设的会话共享总线容量,已受理任务会话轮转、会话内 FIFO;结果访问按会话隔离,不是独立进程或资源安全隔离,也不保证入队名额公平。
66
+ 2. 同一预设修订(generation)的会话共享总线容量,已受理任务会话轮转、会话内 FIFO;结果访问按会话隔离,不是独立进程或资源安全隔离,也不保证入队名额公平。重载/升级会创建新的预设修订,旧实例状态、容量与资源锁不延续。
66
67
  3. 取消任一正在等待的重复调用会请求取消整个逻辑执行,不是仅停止该等待者。
67
68
  4. 已终态且未过期的请求可直接复用结果,不申请新 Job;仍须通过身份、冲突及消息额度检查。
68
69
  5. 等待服务异常后先排空原执行,再有界检查宿主记录;服务持续不可用时记录可能留到所属生命周期清理。不合作 Handler 仍可能阻碍关闭。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feng0531/dsh-execution-bus",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "license": "SEE LICENSE IN LICENSE",