@twinsearth/dsh-master 3.3.1 → 3.3.2
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 +1 -0
- package/client.js +1 -1
- package/dist-host/contract/constants.js +1 -1
- package/dist-host/contract/relay.js +12 -7
- package/dist-host/kernel.js +1 -1
- package/dist-host/plugins/cost-optimizer.js +3 -3
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/CHANGELOG.md +15 -0
- package/docs/DESIGN-v3.3.0-cost-optimization.md +2 -1
- package/docs/EXPERIMENT-M5-fang-relay.md +68 -5
- package/package.json +2 -2
- package/src/contract/constants.js +1 -1
- package/src/contract/relay.js +12 -6
- package/src/kernel.ts +1 -1
- package/src/plugins/cost-optimizer.ts +3 -3
package/README.md
CHANGED
|
@@ -92,6 +92,7 @@ dsh plugin add <package> # 加载 dist-host/dist-worker
|
|
|
92
92
|
|
|
93
93
|
## 状态
|
|
94
94
|
|
|
95
|
+
- **3.3.2 发布线**:与真实瓮城(dsh-Fang 3.1.35)完成回流实测——探针 `--peer fang` 用瓮城发布物当对端(取其 `VENDOR.json` 校验、`peerLabel` 取其 orchestrator),四项 M5 预期全部成立;同批修掉 contract 1.1.0 的 `rejected` 负数计数并同步 1.1.1。详见 [M5 回流实测](docs/EXPERIMENT-M5-fang-relay.md)。
|
|
95
96
|
- **3.3.1 发布线**:M5 收口 —— 瓮城回流接口升级到 `@twinsearth/dsh-fang-contract` **1.1.0**(`fang.relay.publish` / `fang.relay.status`),房主侧 `src/cost/fang-relay.ts` 做真实探测与投递(只有对端回执才算送达,不可用给具名原因);里程碑 **M1–M5 全部 done**。详见 [M5 回流实测](docs/EXPERIMENT-M5-fang-relay.md)。
|
|
96
97
|
- **3.3.0 发布线**:成本优化系统层(ledger / budget / policy / audit)+ Worker 四个成本切点 + A1 紧凑化(默认 shadow)+ A2–A3 / B1–B4;真实 A/B 实测 tokens/task 6263 → 3906.75(−37.6%),成功率 0pp 差,详见 [3.3.0 架构设计](docs/DESIGN-v3.3.0-cost-optimization.md)。
|
|
97
98
|
- 3.2.10 发布线:角色矩阵放宽——Router 进入母体(essence/mother),Worker 与 Worker Agent Loop 进入原身(fork/essence);母体仍不接纳执行体,fork 仍不注册 Router。
|
package/client.js
CHANGED
|
@@ -275,7 +275,7 @@ window.__ModuleLoader__.load({
|
|
|
275
275
|
return h('div', { style: S.card, 'data-dsh-master': 'cost-block' },
|
|
276
276
|
h('div', { style: S.row },
|
|
277
277
|
h('span', { style: S.name }, '成本(只读)'),
|
|
278
|
-
tag(block ? '3.3.
|
|
278
|
+
tag(block ? '3.3.2' : '未启用', block ? undefined : '#B45309')),
|
|
279
279
|
failed ? h('div', { style: S.error }, '成本接口不可达') : null,
|
|
280
280
|
!block && !failed ? h('div', { style: S.desc }, '成本系统未装配(cost-optimizer 未启用或角色不匹配)') : null,
|
|
281
281
|
columns ? h('div', { style: S.desc },
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* * 删除/改义必须升 MAJOR,并保留至少一个 MINOR 周期的双写(method@1 与 method@2)。
|
|
10
10
|
*/
|
|
11
11
|
export const CONTRACT_PACKAGE = '@twinsearth/dsh-fang-contract';
|
|
12
|
-
export const CONTRACT_VERSION = '1.1.
|
|
12
|
+
export const CONTRACT_VERSION = '1.1.1';
|
|
13
13
|
export const PROTOCOL = 'fang-master';
|
|
14
14
|
export const PROTOCOL_VERSION = 1;
|
|
15
15
|
export const MIN_COMPATIBLE_MAJOR = 1;
|
|
@@ -135,10 +135,12 @@ export function createRelayStore(options = {}) {
|
|
|
135
135
|
}
|
|
136
136
|
counters.batches += 1;
|
|
137
137
|
const acceptedIds = [];
|
|
138
|
-
let
|
|
138
|
+
let alreadySeen = 0;
|
|
139
|
+
let inBatchDuplicates = 0;
|
|
140
|
+
let rejectedCount = 0;
|
|
139
141
|
for (const event of validated.accepted) {
|
|
140
142
|
if (seen.has(event.id)) {
|
|
141
|
-
|
|
143
|
+
alreadySeen += 1;
|
|
142
144
|
continue;
|
|
143
145
|
}
|
|
144
146
|
seen.set(event.id, { at: nowMs(now), cursor: cursor + 1 });
|
|
@@ -148,18 +150,21 @@ export function createRelayStore(options = {}) {
|
|
|
148
150
|
remember({ id: event.id, type: event.type, at: event.at, cursor });
|
|
149
151
|
lastAt = nowMs(now);
|
|
150
152
|
}
|
|
151
|
-
counters.duplicates += duplicates;
|
|
152
153
|
for (const item of validated.rejected) {
|
|
153
154
|
// 批内重复计入 duplicates;其余非法条目计入 rejected 并留原因(有界)。
|
|
154
155
|
if (item.reason === 'duplicate-in-batch') {
|
|
155
|
-
|
|
156
|
-
counters.duplicates += 1;
|
|
156
|
+
inBatchDuplicates += 1;
|
|
157
157
|
continue;
|
|
158
158
|
}
|
|
159
|
-
|
|
159
|
+
rejectedCount += 1;
|
|
160
160
|
if (lastErrors.length < 50)
|
|
161
161
|
lastErrors.push({ id: item.id, reason: item.reason });
|
|
162
162
|
}
|
|
163
|
+
// 两个计数分开累计:duplicates = 已存在 + 批内重复;rejected 只数真正非法的条目。
|
|
164
|
+
// (1.1.0 曾用 `validated.rejected.length - duplicates` 计算 rejected,重投时会得到负数。)
|
|
165
|
+
const duplicates = alreadySeen + inBatchDuplicates;
|
|
166
|
+
counters.duplicates += duplicates;
|
|
167
|
+
counters.rejected += rejectedCount;
|
|
163
168
|
const batchId = String(params?.batchId ?? `batch-${counters.batches}`);
|
|
164
169
|
const receipt = Object.freeze({
|
|
165
170
|
ok: true,
|
|
@@ -167,7 +172,7 @@ export function createRelayStore(options = {}) {
|
|
|
167
172
|
taskId: params?.taskId === undefined ? null : String(params.taskId),
|
|
168
173
|
accepted: acceptedIds.length,
|
|
169
174
|
duplicates,
|
|
170
|
-
rejected:
|
|
175
|
+
rejected: rejectedCount,
|
|
171
176
|
cursor,
|
|
172
177
|
acceptedIds,
|
|
173
178
|
peer: peerLabel,
|
package/dist-host/kernel.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* 与瓮城 dsh-Fang 的协作只走 DSH 服务键,不 import fang 代码。
|
|
8
8
|
*/
|
|
9
9
|
import crypto from 'node:crypto';
|
|
10
|
-
export const MASTER_VERSION = '3.3.
|
|
10
|
+
export const MASTER_VERSION = '3.3.2';
|
|
11
11
|
export const FANG_CONTRACT_MIN = '3.1.14';
|
|
12
12
|
export const SERVICE = Object.freeze({
|
|
13
13
|
master: 'dshMaster',
|
|
@@ -108,7 +108,7 @@ export function apply(ctx, config = {}) {
|
|
|
108
108
|
};
|
|
109
109
|
const service = {
|
|
110
110
|
id: SERVICE.cost,
|
|
111
|
-
version: '3.3.
|
|
111
|
+
version: '3.3.2',
|
|
112
112
|
role,
|
|
113
113
|
get mode() {
|
|
114
114
|
return system.policy.describe().mode;
|
|
@@ -354,7 +354,7 @@ export function apply(ctx, config = {}) {
|
|
|
354
354
|
return {
|
|
355
355
|
ok: true,
|
|
356
356
|
id: 'cost-optimizer',
|
|
357
|
-
version: '3.3.
|
|
357
|
+
version: '3.3.2',
|
|
358
358
|
role,
|
|
359
359
|
mode: current.mode,
|
|
360
360
|
requestedMode: current.requestedMode,
|
|
@@ -396,7 +396,7 @@ export function apply(ctx, config = {}) {
|
|
|
396
396
|
doubleCountGuard: report.doubleCountGuard,
|
|
397
397
|
};
|
|
398
398
|
})(),
|
|
399
|
-
note: '3.3.
|
|
399
|
+
note: '3.3.2:系统层记账/预算/灰度/审计 + A1 工具输出紧凑化(默认 shadow)+ 瓮城回流出口(contract 1.1.1 真实投递,回执计数修复);工具面 A2–A3 与决策复用 B1–B4 已接入(shadow 默认)。',
|
|
400
400
|
};
|
|
401
401
|
},
|
|
402
402
|
};
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -132,7 +132,7 @@ master-cost-optimizer(行,仅 fork)= 编排器 + 服务 dshMasterCost
|
|
|
132
132
|
Agent Loop **不认识具体模块**;模块异常 fail-open 并留痕,因此新增模块无需改 `agent-loop.ts`。
|
|
133
133
|
- 四切点:`worker/cost-hooks.ts`,事件经 `sink.cost` 到宿主(HTTP sink 的 `cost` 事件 / Cordis `superagent:cost`)。
|
|
134
134
|
- A1 默认 **shadow**:计算「本可节省」但返回原文;只有 `enforce` 且紧凑结果更短才替换,且归因单独入账(不重复计消耗)。
|
|
135
|
-
- 瓮城回流(3.3.1
|
|
135
|
+
- 瓮城回流(3.3.1 起):`src/cost/fang-relay.ts` 走 `@twinsearth/dsh-fang-contract` 1.1.x 的
|
|
136
136
|
`fang.relay.publish` / `fang.relay.status`;只有对端回执才算送达,不可用给具名原因。
|
|
137
137
|
- 详见 `docs/DESIGN-v3.3.0-cost-optimization.md`(§0 总体架构、§17 实施进度)、
|
|
138
138
|
`docs/EXPERIMENT-M5-real-ab.md`(真实 A/B)、`docs/EXPERIMENT-M5-fang-relay.md`(回流实测)。
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## 3.3.2(2026-11-13 已发布)
|
|
2
|
+
|
|
3
|
+
> 三方发布:annotated tag `v3.3.2` + GitHub Release + npm `@twinsearth/dsh-master@3.3.2`。
|
|
4
|
+
> 同批发布接口包:`@twinsearth/dsh-fang-contract@1.1.1`(修回执计数缺陷)。
|
|
5
|
+
|
|
6
|
+
**主题:与真实瓮城(dsh-Fang 3.1.35)完成回流实测,并修掉回执计数缺陷。**
|
|
7
|
+
|
|
8
|
+
- **瓮城侧状态(只读核对)**:dsh-Fang **3.1.35** 已内嵌 contract **1.1.0**,`src/contract/` 8 个文件与发布包逐字节相同,并附 `VENDOR.json`(`version` / `fileCount` / 逐文件 sha256);`src/plugins/orchestrator.mjs` 声明 `peerLabel: 'dsh-fang-bundle'` 且注册 `...relayHandlers(relayStore)`。
|
|
9
|
+
- **探针新增 `--peer fang` 模式**:把**瓮城自己的发布物**当对端 —— 用它 vendored 的契约服务端,`peerLabel` 从它自己的 orchestrator 源码读取(不硬编码),并用它的 `VENDOR.json` 逐文件校验 sha256(8/8 通过)。实测:`peer=dsh-fang-bundle`、`events=6`、`sameProcess=false`、`failed=0`、`transport=http`、`contractVersion=1.1.0`;幂等重投 `accepted:0 / duplicates:3 / cursor` 不变。四项 M5 预期全部成立。
|
|
10
|
+
- **实测发现的缺陷(contract 1.1.0)**:relay 回执把 `rejected` 算成 `validated.rejected.length - duplicates`,重投时会得到**负数**(实测 `-3`)。影响仅限回执字段(事件去重、`accepted`、`cursor` 一直正确)。contract **1.1.1** 已修正为「`duplicates`(已存在 + 批内)与 `rejected`(仅非法条目)分开累计」,并补回归测试(契约侧 16/16)。
|
|
11
|
+
- **房主内嵌副本同步 1.1.1**:`src/contract/` 与已发布包逐字节相同(conformance 测试守卫);新增「重投回执非负且 cursor 不倒退」回归测试。
|
|
12
|
+
- **里程碑**:M5 证据由 `contract-server` 升级为 `dsh-fang-bundle · 6 条`,结果行 `notes` 里的残余缺口(“待 bundle 采用 1.1”)自动清空;**M1–M5 全部 done**。
|
|
13
|
+
- **仍未做**:宿主级 co-load(守则 §二 禁止把瓮城装进任何 profile);瓮城 UI/信息栏是否消费回流事件属瓮城产品面。
|
|
14
|
+
- 测试:全量 **449 条**(`npm test` 447 通过 + 2 条 Windows 并行跳过;`npm run test:clean` 449/449)。
|
|
15
|
+
|
|
1
16
|
## 3.3.1(2026-11-13 已发布)
|
|
2
17
|
|
|
3
18
|
> 三方发布:annotated tag `v3.3.1` + GitHub Release + npm `@twinsearth/dsh-master@3.3.1`。
|
|
@@ -1054,5 +1054,6 @@ node scripts/milestones.mjs --input evidence.json # 追加 M3 守卫准确率 /
|
|
|
1054
1054
|
| **M2 收口** 系统层 attribution / bus + 模块 SPI | ✅ 已实施(3.3.0-dev,未发布) | `src/cost/attribution.ts`(按模块/任务聚合,enforce 才入账)、`src/cost/bus.ts`(事件总线 + 回流出口,订阅者异常隔离)、`worker/cost-spi.ts`(ToolModule / DecisionModule 契约)、`worker/cost-modules.ts`(注册表 + A1 适配,异常 fail-open)、`test/worker-cost-spi.test.ts`;Agent Loop 已改为只依赖注册表 |
|
|
1055
1055
|
| **M2 余项 / M3–M4** | ✅ 已发布(3.3.0) | A2 渐进纰漏、A3 批量、B1–B4、真实 fork 对照实验、面板成本块(见 §14) |
|
|
1056
1056
|
| **M5 回流收口** | ✅ 已发布(3.3.1) | contract 1.1.0(`fang.relay.publish`/`status`)+ `src/cost/fang-relay.ts` + `scripts/fang-relay-probe.mjs`;M5 由 `fang-relay-out-of-scope` 改为对端回执判定 |
|
|
1057
|
+
| **M5 真实瓮城实测** | ✅ 已发布(3.3.2) | 瓮城 3.1.35 内嵌 1.1.0 并自报 `dsh-fang-bundle`;探针 `--peer fang` 用瓮城发布物当对端(VENDOR.json 校验 8/8、peerLabel 取其 orchestrator);实测 6 条回执、幂等重投通过;同批修 contract 1.1.1 回执计数缺陷并同步 |
|
|
1057
1058
|
|
|
1058
|
-
*3.3.0 / 3.3.1 均已三方发布(annotated tag + GitHub Release + npm
|
|
1059
|
+
*3.3.0 / 3.3.1 / 3.3.2 均已三方发布(annotated tag + GitHub Release + npm);contract 1.0.0 / 1.1.0 / 1.1.1 已发 npm。*
|
|
@@ -103,15 +103,78 @@ M5 │ 实现 ✔ │ 依赖 ✔ │ 效果 verified │ 状态 done │ 回流
|
|
|
103
103
|
|
|
104
104
|
**不能证明**
|
|
105
105
|
|
|
106
|
-
-
|
|
107
|
-
|
|
108
|
-
M5 的 `notes` 会自动消失(代码里写死的是白名单,不需要改里程碑表);
|
|
106
|
+
- 不能说“两个插件在同一个 DSH 宿主里同时加载跑通了”:按本会话守则 §二,**不得把瓮城安装/升级到任何 profile**,
|
|
107
|
+
所以本轮没有做宿主级 co-load;做的是「瓮城**已发布包**的 vendored 契约服务端 × 房主内嵌副本客户端」的跨进程实测;
|
|
109
108
|
- 不能证明生产环境的网络/鉴权拓扑:本轮是 127.0.0.1 + Bearer token 的本机双进程。
|
|
110
109
|
|
|
111
110
|
## 6. 复现注意事项
|
|
112
111
|
|
|
113
112
|
- 必须 `npm run build` 后跑:脚本 import 的是 `dist-host/`;
|
|
114
|
-
-
|
|
115
|
-
|
|
113
|
+
- 两种对端:默认 `--peer contract` 用已发布的契约包(自报 `contract-server`,仅协议自测);
|
|
114
|
+
`--peer fang --fang-dir <解包目录>` 用**瓮城自己的包**当对端(自报 `dsh-fang-bundle`);
|
|
116
115
|
- 证据文件不要提交进仓库(G6 零残留门禁会检查 `rsi-report.json` 一类产物);
|
|
117
116
|
- Windows 上子进程退出由 `SIGTERM` 处理,若端口被占用会直接报 ready 超时。
|
|
117
|
+
|
|
118
|
+
## 7. 瓮城 3.1.35 复核(2026-11-13 晚)
|
|
119
|
+
|
|
120
|
+
瓮城已发 **3.1.35**(commit `feat(v3.1.35): 适配 contract 1.1.0 回流接口 (#100)`),房主侧按下面的方式复核:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
npm run build
|
|
124
|
+
node scripts/fang-relay-probe.mjs --peer fang --fang-dir <解包后的 @twinsearth/dsh-fang> --events 6
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**静态核对(对端是瓮城发布物本身)**
|
|
128
|
+
|
|
129
|
+
| 项 | 结果 |
|
|
130
|
+
|---|---|
|
|
131
|
+
| 瓮城 `src/contract/` 8 个文件 vs contract 1.1.0 | **8/8 逐字节相同** |
|
|
132
|
+
| 瓮城 `src/contract/VENDOR.json` | `package=@twinsearth/dsh-fang-contract, version=1.1.0, fileCount=8`,逐文件 sha256 与实物一致 |
|
|
133
|
+
| `src/plugins/orchestrator.mjs` | `createRelayStore({ peerLabel: 'dsh-fang-bundle' })` + handlers 里 `...relayHandlers(relayStore)` |
|
|
134
|
+
|
|
135
|
+
探针不硬编码 `peerLabel`:它从**瓮城自己的 orchestrator 源码**里读出该值,并用瓮城的 `VENDOR.json` 校验文件哈希,
|
|
136
|
+
所以 `peer` 是瓮城自报的,不是我们假设的。
|
|
137
|
+
|
|
138
|
+
**实测结果(`--peer fang`)**
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"ok": true,
|
|
143
|
+
"peerMode": "fang",
|
|
144
|
+
"expectation": { "peer": "dsh-fang-bundle", "matched": true },
|
|
145
|
+
"fangRelay": { "available": true, "peer": "dsh-fang-bundle", "events": 6,
|
|
146
|
+
"sameProcess": false, "transport": "http", "contractVersion": "1.1.0",
|
|
147
|
+
"cursor": 6, "failed": 0, "dropped": 0 },
|
|
148
|
+
"provenance": { "fangVersion": "3.1.35", "vendorVersion": "1.1.0",
|
|
149
|
+
"vendoredFilesChecked": 8, "vendoredFilesOk": true,
|
|
150
|
+
"peerLabelSource": "src/plugins/orchestrator.mjs",
|
|
151
|
+
"relayHandlersRegistered": true },
|
|
152
|
+
"detail": {
|
|
153
|
+
"probePid": 10632, "peerPid": 12972, "sameProcess": false,
|
|
154
|
+
"idempotency": { "ok": true, "events": 3,
|
|
155
|
+
"first": { "accepted": 3, "duplicates": 0, "cursor": 9 },
|
|
156
|
+
"second": { "accepted": 0, "duplicates": 3, "cursor": 9 } }
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
四项预期全部成立:`peer === 'dsh-fang-bundle'`、`events ≥ 3`、`sameProcess === false`、`failed === 0`;
|
|
162
|
+
额外核对幂等重投:同批事件第二次 `accepted:0 / duplicates:3 / cursor` 不倒退。
|
|
163
|
+
|
|
164
|
+
**这次实测发现 contract 1.1.0 的一个回执缺陷(已在 1.1.1 修复并发布)**
|
|
165
|
+
|
|
166
|
+
`createRelayStore().publish()` 用 `validated.rejected.length - duplicates` 计算 `rejected`,
|
|
167
|
+
而 `duplicates` 同时包含「已存在重复」和「批内重复」,重投时会得到**负数**(实测 `-3`)。
|
|
168
|
+
影响面:只影响回执里的 `rejected` 字段(事件去重、`cursor`、`accepted` 都正确),房主侧不读该字段;
|
|
169
|
+
但负数回执是错的数据。已加回归测试并修正为「`rejected` 只数真正非法条目,`duplicates` 单独累计」,
|
|
170
|
+
契约仓库测试 16/16 通过;**contract 1.1.1 已发布**(npm `latest=1.1.1`,字节核验通过),
|
|
171
|
+
房主 **3.3.2** 已同步 1.1.1 并重新三方发布。
|
|
172
|
+
|
|
173
|
+
> 注意:瓮城 3.1.35 内嵌的仍是 1.1.0(含该回执缺陷)。这是 PATCH 级差异、只影响 `rejected` 显示,
|
|
174
|
+
> 不影响去重与投递;瓮城下次 vendor 同步时带上 1.1.1 即可,无需协议改动。
|
|
175
|
+
|
|
176
|
+
**仍未证明**
|
|
177
|
+
|
|
178
|
+
- 宿主级 co-load(守则 §二禁止往 profile 装瓮城);
|
|
179
|
+
- 瓮城侧 UI/信息栏是否消费了回流事件(属瓮城产品面,房主只需协议可用)。
|
|
180
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twinsearth/dsh-master",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"build": "npm run build:host && npm run build:worker"
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
|
-
"@twinsearth/dsh-fang-contract": "^1.1.
|
|
78
|
+
"@twinsearth/dsh-fang-contract": "^1.1.1",
|
|
79
79
|
"@types/node": "^24.0.0",
|
|
80
80
|
"typescript": "^5.9.0"
|
|
81
81
|
},
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
export const CONTRACT_PACKAGE = '@twinsearth/dsh-fang-contract'
|
|
14
|
-
export const CONTRACT_VERSION = '1.1.
|
|
14
|
+
export const CONTRACT_VERSION = '1.1.1'
|
|
15
15
|
export const PROTOCOL = 'fang-master'
|
|
16
16
|
export const PROTOCOL_VERSION = 1
|
|
17
17
|
export const MIN_COMPATIBLE_MAJOR = 1
|
package/src/contract/relay.js
CHANGED
|
@@ -123,9 +123,11 @@ export function createRelayStore(options = {}) {
|
|
|
123
123
|
}
|
|
124
124
|
counters.batches += 1
|
|
125
125
|
const acceptedIds = []
|
|
126
|
-
let
|
|
126
|
+
let alreadySeen = 0
|
|
127
|
+
let inBatchDuplicates = 0
|
|
128
|
+
let rejectedCount = 0
|
|
127
129
|
for (const event of validated.accepted) {
|
|
128
|
-
if (seen.has(event.id)) {
|
|
130
|
+
if (seen.has(event.id)) { alreadySeen += 1; continue }
|
|
129
131
|
seen.set(event.id, { at: nowMs(now), cursor: cursor + 1 })
|
|
130
132
|
cursor += 1
|
|
131
133
|
counters.accepted += 1
|
|
@@ -133,13 +135,17 @@ export function createRelayStore(options = {}) {
|
|
|
133
135
|
remember({ id: event.id, type: event.type, at: event.at, cursor })
|
|
134
136
|
lastAt = nowMs(now)
|
|
135
137
|
}
|
|
136
|
-
counters.duplicates += duplicates
|
|
137
138
|
for (const item of validated.rejected) {
|
|
138
139
|
// 批内重复计入 duplicates;其余非法条目计入 rejected 并留原因(有界)。
|
|
139
|
-
if (item.reason === 'duplicate-in-batch') {
|
|
140
|
-
|
|
140
|
+
if (item.reason === 'duplicate-in-batch') { inBatchDuplicates += 1; continue }
|
|
141
|
+
rejectedCount += 1
|
|
141
142
|
if (lastErrors.length < 50) lastErrors.push({ id: item.id, reason: item.reason })
|
|
142
143
|
}
|
|
144
|
+
// 两个计数分开累计:duplicates = 已存在 + 批内重复;rejected 只数真正非法的条目。
|
|
145
|
+
// (1.1.0 曾用 `validated.rejected.length - duplicates` 计算 rejected,重投时会得到负数。)
|
|
146
|
+
const duplicates = alreadySeen + inBatchDuplicates
|
|
147
|
+
counters.duplicates += duplicates
|
|
148
|
+
counters.rejected += rejectedCount
|
|
143
149
|
const batchId = String(params?.batchId ?? `batch-${counters.batches}`)
|
|
144
150
|
const receipt = Object.freeze({
|
|
145
151
|
ok: true,
|
|
@@ -147,7 +153,7 @@ export function createRelayStore(options = {}) {
|
|
|
147
153
|
taskId: params?.taskId === undefined ? null : String(params.taskId),
|
|
148
154
|
accepted: acceptedIds.length,
|
|
149
155
|
duplicates,
|
|
150
|
-
rejected:
|
|
156
|
+
rejected: rejectedCount,
|
|
151
157
|
cursor,
|
|
152
158
|
acceptedIds,
|
|
153
159
|
peer: peerLabel,
|
package/src/kernel.ts
CHANGED
|
@@ -117,7 +117,7 @@ export function apply(ctx, config = {}) {
|
|
|
117
117
|
|
|
118
118
|
const service = {
|
|
119
119
|
id: SERVICE.cost,
|
|
120
|
-
version: '3.3.
|
|
120
|
+
version: '3.3.2',
|
|
121
121
|
role,
|
|
122
122
|
get mode() {
|
|
123
123
|
return system.policy.describe().mode
|
|
@@ -354,7 +354,7 @@ export function apply(ctx, config = {}) {
|
|
|
354
354
|
return {
|
|
355
355
|
ok: true,
|
|
356
356
|
id: 'cost-optimizer',
|
|
357
|
-
version: '3.3.
|
|
357
|
+
version: '3.3.2',
|
|
358
358
|
role,
|
|
359
359
|
mode: current.mode,
|
|
360
360
|
requestedMode: current.requestedMode,
|
|
@@ -396,7 +396,7 @@ export function apply(ctx, config = {}) {
|
|
|
396
396
|
doubleCountGuard: report.doubleCountGuard,
|
|
397
397
|
}
|
|
398
398
|
})(),
|
|
399
|
-
note: '3.3.
|
|
399
|
+
note: '3.3.2:系统层记账/预算/灰度/审计 + A1 工具输出紧凑化(默认 shadow)+ 瓮城回流出口(contract 1.1.1 真实投递,回执计数修复);工具面 A2–A3 与决策复用 B1–B4 已接入(shadow 默认)。',
|
|
400
400
|
}
|
|
401
401
|
},
|
|
402
402
|
}
|