@twinsearth/agent-universe 2.5.7 → 2.5.9

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
@@ -273,7 +273,7 @@ npm config set @twinsearth:registry https://npm.pkg.github.com
273
273
  npm install @twinsearth/agent-universe
274
274
  ```
275
275
 
276
- 已发布版本:1.0.0 / 2.0.0 / 2.2.0 / 2.3.0 / 2.3.1 / 2.3.4 / 2.3.5 / 2.3.6 / 2.4.0 ~ 2.5.7,详见 [Releases](https://github.com/TwinsEarth/agent-universe/releases)。
276
+ 已发布版本:1.0.0 / 2.0.0 / 2.2.0 / 2.3.0 / 2.3.1 / 2.3.4 / 2.3.5 / 2.3.6 / 2.4.0 ~ 2.5.9,详见 [Releases](https://github.com/TwinsEarth/agent-universe/releases)。
277
277
 
278
278
  ## 版本谱系
279
279
 
@@ -308,6 +308,8 @@ npm install @twinsearth/agent-universe
308
308
  | v2.5.5 | **Relay Pool** | **Relay 节点池管理 + 多通道智能切换(默认 3 条,掉线自动补)** |
309
309
  | v2.5.6 | **Regression** | **历史 Bug 回归套件:test/ 14 条 + Rust 网络回归,CI 强制全量回归,修新 bug 先加用例** |
310
310
  | v2.5.7 | **Identity** | **跨实现身份一致性:统一 DID 派生口径 + conformance 签名向量逐字节命中上游 + 客户端构建链路根治(file: 依赖)** |
311
+ | v2.5.8 | **Ledger** | **精确整数账本 Money(i64):守恒精确相等无容差 + 发布即托管 __escrow__ 防铸币 + 三端共享金额向量逐值一致** |
312
+ | v2.5.9 | **Auth QA** | **认证式 BFT(固定委员集 Ed25519 签名票,替代调用方合成)+ 可失败独立审计(只信任流水独立重放)+ nonce/时间窗重放保护 + 只追加结算流水** |
311
313
 
312
314
  详见 [RELEASES.md](RELEASES.md) 和 [releases/](releases/) 目录。
313
315
 
package/js/index.js CHANGED
@@ -9,7 +9,7 @@ const { AgentMarket, Bid, Reputation, MIN_STAKE } = require('./lib/market');
9
9
  const { McpHttpClient, McpError, MCP_PROTOCOL_VERSION } = require('./lib/mcp');
10
10
  const aca = require('./lib/aca');
11
11
 
12
- const version = '2.5.7';
12
+ const version = '2.5.9';
13
13
 
14
14
  /**
15
15
  * AgentUniverse 门面:聚合身份、网络、市场能力
package/js/lib/aca.js CHANGED
@@ -129,7 +129,7 @@ function buildManifest(identity, name, capabilities, opts = {}) {
129
129
  const manifest = {
130
130
  did: identity.did,
131
131
  name,
132
- version: opts.version || '2.5.7',
132
+ version: opts.version || '2.5.9',
133
133
  capabilities: [...capabilities],
134
134
  endpoints: opts.endpoints || [],
135
135
  hardware: {
package/js/lib/market.js CHANGED
@@ -38,14 +38,33 @@ class AgentMarket {
38
38
  this.bids = new Map(); // taskId → Bid[]
39
39
  this.balances = new Map(); // 账户 → 余额
40
40
  this.totalSlashed = 0;
41
+ this.totalDeposits = 0;
41
42
  this.paidTasks = new Set(); // 已结算任务(防重复支付)
43
+ this.records = []; // 只追加结算流水(独立审计的唯一信任源)
44
+ this._seenNonces = new Set(); // 已用投票 nonce(防重放)
45
+ }
46
+
47
+ /** 追加一条结算流水(与 Rust SettlementRecord 对齐) */
48
+ _record(from, to, amount, reason) {
49
+ this.records.push({ from, to, amount, reason });
50
+ }
51
+
52
+ /** 金额必须是安全整数(与 Rust Money(i64) 对齐,杜绝浮点导致的跨语言守恒失效) */
53
+ _assertMoney(v, label) {
54
+ if (typeof v !== 'number' || !Number.isInteger(v) || !Number.isSafeInteger(v)) {
55
+ throw new Error(`${label || '金额'}必须是整数`);
56
+ }
57
+ return v;
42
58
  }
43
59
 
44
60
  /** 充值 / 质押金进入账户 */
45
61
  deposit(account, amount) {
62
+ this._assertMoney(amount, '金额');
46
63
  if (amount < 0) throw new Error('金额不能为负');
47
64
  const cur = this.balances.get(account) || 0;
48
65
  this.balances.set(account, cur + amount);
66
+ this.totalDeposits += amount;
67
+ this._record('', account, amount, 'Deposited');
49
68
  }
50
69
 
51
70
  balance(account) {
@@ -57,11 +76,17 @@ class AgentMarket {
57
76
  return `__stake__:${agentId}`;
58
77
  }
59
78
 
79
+ /** 任务预算托管账户(发布即锁定,结算时从此支付 / 退款),与 Rust 命名一致 */
80
+ _escrowAccount(taskId) {
81
+ return `__escrow__:${taskId}`;
82
+ }
83
+
60
84
  /** 注册 Agent(需质押 ≥ MIN_STAKE) */
61
85
  registerAgent(card, stake) {
62
86
  if (!card || !card.did) throw new Error('agent_id 不能为空');
63
87
  if (!card.name) throw new Error('name 不能为空');
64
88
  if (this.agents.has(card.did)) throw new Error('Agent 已存在');
89
+ this._assertMoney(stake, '质押');
65
90
  if (stake < MIN_STAKE) {
66
91
  throw new Error(`质押不足,最低 ${MIN_STAKE}`);
67
92
  }
@@ -73,6 +98,7 @@ class AgentMarket {
73
98
  const stakeAcc = this._stakeAccount(owner);
74
99
  this.balances.set(owner, this.balance(owner) - stake);
75
100
  this.balances.set(stakeAcc, this.balance(stakeAcc) + stake);
101
+ this._record(owner, stakeAcc, stake, 'Staked');
76
102
 
77
103
  this.agents.set(card.did, {
78
104
  card,
@@ -86,12 +112,16 @@ class AgentMarket {
86
112
  /** 发布任务 */
87
113
  publishTask(taskId, goal, budget, requester) {
88
114
  if (!goal) throw new Error('goal 不能为空');
115
+ this._assertMoney(budget, '预算');
89
116
  if (budget <= 0) throw new Error('预算必须为正');
90
117
  if (this.balance(requester) < budget) {
91
118
  throw new Error('需求方余额不足以覆盖预算');
92
119
  }
93
- // 预算锁定
120
+ // 发布即托管:预算从需求方转入任务托管账户(钱仍在系统内,任意时刻守恒)
94
121
  this.balances.set(requester, this.balance(requester) - budget);
122
+ const escrowAcc = this._escrowAccount(taskId);
123
+ this.balances.set(escrowAcc, this.balance(escrowAcc) + budget);
124
+ this._record(requester, escrowAcc, budget, 'Escrowed');
95
125
 
96
126
  const task = new Task(taskId, goal);
97
127
  task.transition(TaskStatus.OPEN);
@@ -105,6 +135,8 @@ class AgentMarket {
105
135
  /** 提交投标 */
106
136
  submitBid(taskId, agentId, price) {
107
137
  if (!this.agents.has(agentId)) throw new Error('Agent 未注册');
138
+ this._assertMoney(price, '出价');
139
+ if (price <= 0) throw new Error('出价必须为正');
108
140
  const task = this.tasks.get(taskId);
109
141
  if (!task || task.status !== TaskStatus.OPEN) {
110
142
  throw new Error('任务不在开放状态');
@@ -166,13 +198,18 @@ class AgentMarket {
166
198
  const price = task.winnerPrice || task.budget;
167
199
  const pay = Math.min(price, task.budget);
168
200
 
169
- // 从任务锁定预算中支付给执行者(账户间转账,系统总余额不变)
201
+ // 从托管账户支付给执行者、余款退还需求方(账户间转账,总余额不变)
202
+ const escrowAcc = this._escrowAccount(taskId);
170
203
  const assignee = task.assignee;
204
+ this.balances.set(escrowAcc, this.balance(escrowAcc) - pay);
171
205
  this.balances.set(assignee, this.balance(assignee) + pay);
206
+ this._record(escrowAcc, assignee, pay, 'Completed');
172
207
  // 未用完的预算退还需求方
173
208
  const refund = task.budget - pay;
174
209
  if (refund > 0) {
210
+ this.balances.set(escrowAcc, this.balance(escrowAcc) - refund);
175
211
  this.balances.set(task.requester, this.balance(task.requester) + refund);
212
+ this._record(escrowAcc, task.requester, refund, 'Refunded');
176
213
  }
177
214
 
178
215
  task.settle();
@@ -184,12 +221,15 @@ class AgentMarket {
184
221
  slash(agentId, amount, reason) {
185
222
  const entry = this.agents.get(agentId);
186
223
  if (!entry) throw new Error('Agent 不存在');
224
+ this._assertMoney(amount, '罚没金额');
225
+ if (amount <= 0) throw new Error('罚没金额必须为正');
187
226
  const actual = Math.min(amount, entry.stake);
188
227
  entry.stake -= actual;
189
228
  // 从锁定账户实际扣除(退出系统)
190
229
  const stakeAcc = this._stakeAccount(agentId);
191
230
  this.balances.set(stakeAcc, this.balance(stakeAcc) - actual);
192
231
  this.totalSlashed += actual;
232
+ this._record(stakeAcc, '', actual, 'Slashed');
193
233
  entry.status = entry.stake < MIN_STAKE ? 'suspended' : entry.status;
194
234
  return { slashed: actual, reason, remainingStake: entry.stake };
195
235
  }
@@ -229,12 +269,170 @@ class AgentMarket {
229
269
  for (const v of this.balances.values()) balanceSum += v;
230
270
  const expected = totalDeposits - this.totalSlashed;
231
271
  return {
232
- conserved: Math.abs(balanceSum - expected) < 1e-9,
272
+ conserved: balanceSum === expected,
233
273
  balanceSum,
234
274
  totalSlashed: this.totalSlashed,
235
275
  expected,
236
276
  };
237
277
  }
278
+
279
+ /**
280
+ * 认证式 QA 验证(v2.5.9,与 Rust verify_result_authenticated 对齐)
281
+ * members: [{did, public_key(hex32)}] 固定委员集
282
+ * signedVotes: [SignedQaVote],vote 为 Stop/Continue
283
+ */
284
+ verifyResultAuthenticated(
285
+ taskId,
286
+ members,
287
+ signedVotes,
288
+ round = 0,
289
+ now = Math.floor(Date.now() / 1000),
290
+ ) {
291
+ if (!Array.isArray(members) || members.length === 0) {
292
+ throw new Error('members 必须为非空固定委员集');
293
+ }
294
+ const n = members.length;
295
+ const f = Math.floor((n - 1) / 3);
296
+ const pubkeys = new Map();
297
+ for (const m of members) {
298
+ const pk = m.public_key || m.publicKey;
299
+ if (!m.did || !pk) throw new Error('委员需含 did 与 public_key');
300
+ if (!/^[0-9a-fA-F]{64}$/.test(pk)) throw new Error('委员公钥须为 32 字节 hex');
301
+ pubkeys.set(m.did, pk.toLowerCase());
302
+ }
303
+
304
+ // 逐票校验;voterVotes 记录每个委员最终立场(equivocated 票作废)
305
+ const voterVotes = new Map();
306
+ for (const sv of signedVotes || []) {
307
+ const pk = pubkeys.get(sv.voter);
308
+ if (!pk) throw new Error(`投票者 ${sv.voter} 不在固定委员集`);
309
+ if (sv.task_id !== taskId) throw new Error('投票任务不匹配');
310
+ if (Number(sv.round) !== round) throw new Error('投票轮次不匹配');
311
+ const issued = Number(sv.issued_at);
312
+ const expires = Number(sv.expires_at);
313
+ if (!(expires > issued) || now < issued || now > expires) {
314
+ throw new Error(`投票 ${sv.nonce} 不在有效时间窗`);
315
+ }
316
+ const voteTag = String(sv.vote).toLowerCase();
317
+ if (voteTag === 'silent') throw new Error('Silent 票不计入');
318
+ // 先验签,避免无效票污染 nonce
319
+ if (!AgentMarket._verifyEd25519(pk, AgentMarket._voteSigningBytes(sv), sv.signature)) {
320
+ throw new Error(`投票 ${sv.nonce} 签名无效`);
321
+ }
322
+ if (!sv.nonce) throw new Error('nonce 不能为空');
323
+ if (this._seenNonces.has(sv.nonce)) throw new Error(`投票 ${sv.nonce} 重放`);
324
+ this._seenNonces.add(sv.nonce);
325
+ const prev = voterVotes.get(sv.voter);
326
+ if (prev && prev !== voteTag) voterVotes.set(sv.voter, 'equivocated');
327
+ else if (!prev) voterVotes.set(sv.voter, voteTag);
328
+ }
329
+
330
+ let stops = 0;
331
+ let continues = 0;
332
+ for (const v of voterVotes.values()) {
333
+ if (v === 'stop') stops += 1;
334
+ else if (v === 'continue') continues += 1;
335
+ }
336
+ const quorum = 2 * f + 1;
337
+ let decision;
338
+ if (stops >= quorum) decision = 'stop';
339
+ else if (continues >= quorum) decision = 'continue';
340
+ else decision = 'no_quorum';
341
+ const accepted = decision === 'stop';
342
+ const task = this.tasks.get(taskId);
343
+ if (task && decision === 'stop') task.verify(true);
344
+ return { decision, accepted, stops, continues, quorum, n, f };
345
+ }
346
+
347
+ /**
348
+ * 独立审计(v2.5.9,与 Rust independent_audit 对齐)
349
+ * 只信任 records,逐笔重放出期望余额,再与当前 balances 逐户比对;
350
+ * 能发现守恒检查无法察觉的账实不符(如拆账、幽灵账户)。
351
+ */
352
+ independentAudit() {
353
+ const expected = new Map();
354
+ const add = (acct, delta) => {
355
+ const cur = expected.get(acct) || 0;
356
+ const v = cur + delta;
357
+ if (!Number.isSafeInteger(v)) throw new Error('重放溢出');
358
+ expected.set(acct, v);
359
+ };
360
+ let replayedDeposits = 0;
361
+ let replayedSlashed = 0;
362
+ for (const r of this.records) {
363
+ switch (r.reason) {
364
+ case 'Deposited':
365
+ add(r.to, r.amount);
366
+ replayedDeposits += r.amount;
367
+ break;
368
+ case 'Slashed':
369
+ add(r.from, -r.amount);
370
+ replayedSlashed += r.amount;
371
+ break;
372
+ default:
373
+ if (r.amount > 0) {
374
+ add(r.from, -r.amount);
375
+ add(r.to, r.amount);
376
+ }
377
+ }
378
+ }
379
+
380
+ const accounts = new Set([...expected.keys(), ...this.balances.keys()]);
381
+ const mismatches = [];
382
+ for (const a of accounts) {
383
+ const exp = expected.get(a) || 0;
384
+ const act = this.balances.get(a) || 0;
385
+ if (exp !== act) mismatches.push({ account: a, expected: exp, actual: act });
386
+ }
387
+ let balanceSum = 0;
388
+ for (const v of this.balances.values()) balanceSum += v;
389
+ const aggregateMatches =
390
+ replayedDeposits === this.totalDeposits &&
391
+ replayedSlashed === this.totalSlashed;
392
+ const expectedTotal = replayedDeposits - replayedSlashed;
393
+ return {
394
+ passed:
395
+ mismatches.length === 0 &&
396
+ aggregateMatches &&
397
+ expectedTotal === balanceSum,
398
+ replayedRecords: this.records.length,
399
+ replayedDeposits,
400
+ replayedSlashed,
401
+ expectedTotal,
402
+ actualTotal: balanceSum,
403
+ aggregateMatches,
404
+ mismatches,
405
+ };
406
+ }
407
+
408
+ /** 投票待签名规范字节(与 Rust SignedQaVote::signing_bytes 逐字一致) */
409
+ static _voteSigningBytes(sv) {
410
+ const tag = String(sv.vote).toUpperCase();
411
+ return (
412
+ `AU-QA-VOTE\ntask_id=${sv.task_id}\nround=${sv.round}\nvoter=${sv.voter}` +
413
+ `\nvote=${tag}\nnonce=${sv.nonce}\nissued_at=${sv.issued_at}` +
414
+ `\nexpires_at=${sv.expires_at}`
415
+ );
416
+ }
417
+
418
+ /** Ed25519 验签;无 crypto 的环境(WKWebView)抛明确错误而非静默通过 */
419
+ static _verifyEd25519(pubkeyHex, message, signatureHex) {
420
+ let crypto;
421
+ try {
422
+ crypto = require('crypto');
423
+ } catch (e) {
424
+ throw new Error('当前环境无 Ed25519 验签能力;签名验证须在 Node/Rust 侧进行');
425
+ }
426
+ const SPKI = '302a300506032b6570032100';
427
+ const der = Buffer.from(SPKI + pubkeyHex, 'hex');
428
+ const key = crypto.createPublicKey({ key: der, format: 'der', type: 'spki' });
429
+ return crypto.verify(
430
+ null,
431
+ Buffer.from(message, 'utf8'),
432
+ key,
433
+ Buffer.from(signatureHex, 'hex'),
434
+ );
435
+ }
238
436
  }
239
437
 
240
438
  module.exports = { AgentMarket, Bid, Reputation, MIN_STAKE };
package/js/lib/mcp.js CHANGED
@@ -60,7 +60,7 @@ class McpHttpClient {
60
60
  method: 'initialize',
61
61
  params: {
62
62
  protocolVersion: MCP_PROTOCOL_VERSION,
63
- clientInfo: { name: 'agent-universe-js', version: '2.5.7' },
63
+ clientInfo: { name: 'agent-universe-js', version: '2.5.9' },
64
64
  capabilities: {},
65
65
  },
66
66
  });
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@twinsearth/agent-universe",
3
- "version": "2.5.7",
3
+ "version": "2.5.9",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@twinsearth/agent-universe",
9
- "version": "2.5.7",
9
+ "version": "2.5.9",
10
10
  "license": "MIT",
11
11
  "engines": {
12
12
  "node": ">=18"
package/js/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twinsearth/agent-universe",
3
- "version": "2.5.7",
3
+ "version": "2.5.9",
4
4
  "description": "Agent Universe - 群众化的 AGI 路线,去中心化的智能体共享 & 开源网络。使命:让科技造福全人类!",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
package/js/test/test.js CHANGED
@@ -1,7 +1,9 @@
1
1
  // test/test.js
2
- // Agent Universe JS SDK 测试(12 项)
2
+ // Agent Universe JS SDK 测试(14 项)
3
3
 
4
4
  const assert = require('node:assert');
5
+ const fs = require('node:fs');
6
+ const path = require('node:path');
5
7
  const {
6
8
  version,
7
9
  AgentUniverse,
@@ -41,8 +43,8 @@ function test(name, fn) {
41
43
  console.log('Agent Universe JS SDK 测试\n');
42
44
 
43
45
  // 1. 版本号
44
- test('版本号为 2.5.7', () => {
45
- assert.strictEqual(version, '2.5.7');
46
+ test('版本号为 2.5.9', () => {
47
+ assert.strictEqual(version, '2.5.9');
46
48
  });
47
49
 
48
50
  // 2. 密钥对 + DID + 签名验证
@@ -154,6 +156,140 @@ test('罚没后守恒:总余额 = 总充值 − 罚没', () => {
154
156
  assert.strictEqual(check.balanceSum, 140);
155
157
  });
156
158
 
159
+ // 8b. 整数账本:拒绝浮点金额与 0/负出价(与 Rust Money(i64) 对齐)
160
+ test('整数账本:拒绝浮点金额与非正出价', () => {
161
+ const market = new AgentMarket();
162
+ const owner = 'did:au:int';
163
+ assert.throws(() => market.deposit(owner, 10.5), /整数/);
164
+ market.deposit(owner, 200);
165
+ const card = AgentCard.new({ did: owner, name: 'int' });
166
+ market.registerAgent(card, MIN_STAKE);
167
+ market.publishTask('t-int', 'goal', 50, owner);
168
+ assert.throws(() => market.submitBid('t-int', owner, 0), /正/);
169
+ assert.throws(() => market.submitBid('t-int', owner, -5), /正/);
170
+ assert.throws(() => market.submitBid('t-int', owner, 5.5), /整数/);
171
+ });
172
+
173
+ // 8c. 跨语言金额向量:逐账户余额命中 conformance/money-vectors.json(与 Rust 一致)
174
+ test('跨语言金额向量:逐账户余额命中 conformance 向量', () => {
175
+ const vector = JSON.parse(
176
+ fs.readFileSync(path.join(__dirname, '../../conformance/money-vectors.json'), 'utf8')
177
+ );
178
+ const market = new AgentMarket();
179
+ market.deposit('agent-1', 100);
180
+ const card = AgentCard.new({ did: 'agent-1', name: 'worker' }).withSkill('writing');
181
+ market.registerAgent(card, MIN_STAKE);
182
+ market.deposit('requester-1', 50);
183
+ market.publishTask('task-1', 'translate', 50, 'requester-1');
184
+ market.submitBid('task-1', 'agent-1', 10);
185
+ assert.strictEqual(market.matchTask('task-1').agentId, 'agent-1');
186
+ market.completeTask('task-1', true);
187
+ const paid = market.settle('task-1');
188
+ assert.strictEqual(paid.paid, vector.expected_report.total_paid);
189
+ for (const [key, want] of Object.entries(vector.expected_balances)) {
190
+ assert.strictEqual(market.balance(key), want, `账户 ${key} 跨语言不一致`);
191
+ }
192
+ const report = market.conservationCheck(vector.expected_report.total_deposits);
193
+ assert.strictEqual(report.totalSlashed, vector.expected_report.total_slashed);
194
+ assert.strictEqual(report.balanceSum, vector.expected_report.balance_sum);
195
+ assert.strictEqual(report.conserved, vector.expected_report.conserved);
196
+ });
197
+
198
+ // 8d. 认证式 QA:固定委员集 + 签名票(v2.5.9,与 Rust 认证式 BFT 对齐)
199
+ test('认证式 QA:固定委员签名票通过、伪造与重放拒绝', () => {
200
+ const market = new AgentMarket();
201
+ const owner = 'did:au:qa-owner';
202
+ const requester = 'did:au:qa-req';
203
+ market.deposit(owner, 100);
204
+ market.deposit(requester, 50);
205
+ const card = AgentCard.new({ did: owner, name: 'worker' }).withSkill('writing');
206
+ market.registerAgent(card, MIN_STAKE);
207
+ market.publishTask('task-qa', 'goal', 50, requester);
208
+ market.submitBid('task-qa', owner, 40);
209
+ market.matchTask('task-qa');
210
+ // 执行完成,停在 COMPLETED 等待认证式验收(不走本地 verify)
211
+ const t = market.tasks.get('task-qa');
212
+ t.start();
213
+ t.complete();
214
+
215
+ // 固定 4 委员(n=4, f=1, quorum=3)
216
+ const now = Math.floor(Date.now() / 1000);
217
+ const kps = [];
218
+ const members = [];
219
+ for (let i = 0; i < 4; i++) {
220
+ const kp = Keypair.generate();
221
+ kps.push(kp);
222
+ members.push({ did: kp.did, public_key: kp.rawPublicKey().toString('hex') });
223
+ }
224
+ const makeVote = (kp, vote, nonce, round = 0) => {
225
+ const sv = {
226
+ task_id: 'task-qa', round, voter: kp.did, vote, nonce,
227
+ issued_at: now - 60, expires_at: now + 3600, signature: '',
228
+ };
229
+ sv.signature = kp.sign(AgentMarket._voteSigningBytes(sv));
230
+ return sv;
231
+ };
232
+ // 前 3 委员签 Stop
233
+ const votes = [
234
+ makeVote(kps[0], 'Stop', 'n1'),
235
+ makeVote(kps[1], 'Stop', 'n2'),
236
+ makeVote(kps[2], 'Stop', 'n3'),
237
+ ];
238
+ const r = market.verifyResultAuthenticated('task-qa', members, votes, 0, now);
239
+ assert.strictEqual(r.decision, 'stop');
240
+ assert.strictEqual(r.accepted, true);
241
+
242
+ // 伪造签名:委员3 签的票冒充委员0(round 1)→ 验签失败
243
+ const forged = makeVote(kps[3], 'Stop', 'nX', 1);
244
+ forged.voter = kps[0].did;
245
+ assert.throws(
246
+ () => market.verifyResultAuthenticated('task-qa', members, [forged], 1, now),
247
+ /签名无效/,
248
+ );
249
+
250
+ // 重放:委员3 在 round1 复用已用 nonce n1 → 拒绝
251
+ const replay = makeVote(kps[3], 'Stop', 'n1', 1);
252
+ assert.throws(
253
+ () => market.verifyResultAuthenticated('task-qa', members, [replay], 1, now),
254
+ /重放/,
255
+ );
256
+ });
257
+
258
+ // 8e. 独立审计:完整生命周期通过、守恒无法发现的拆账篡改被捕获(v2.5.9)
259
+ test('独立审计:完整生命周期通过并捕获账实篡改', () => {
260
+ const market = new AgentMarket();
261
+ const owner = 'did:au:au-owner';
262
+ const requester = 'did:au:au-req';
263
+ market.deposit(owner, 100);
264
+ market.deposit(requester, 50);
265
+ const card = AgentCard.new({ did: owner, name: 'w' }).withSkill('writing');
266
+ market.registerAgent(card, MIN_STAKE);
267
+ market.publishTask('task-au', 'goal', 50, requester);
268
+ market.submitBid('task-au', owner, 40);
269
+ market.matchTask('task-au');
270
+ market.completeTask('task-au', true);
271
+ market.settle('task-au');
272
+
273
+ const good = market.independentAudit();
274
+ assert.ok(good.passed, `审计应通过: ${JSON.stringify(good.mismatches)}`);
275
+
276
+ // 拆账篡改:质押账户 100 → 50,另开幽灵账户 50(总额不变)
277
+ const stakeAcc = `__stake__:${owner}`;
278
+ const before = market.balance(stakeAcc);
279
+ market.balances.set(stakeAcc, before - 50);
280
+ market.balances.set('__ghost__:split', 50);
281
+ // 守恒检查被蒙蔽(余额总和不变)
282
+ const cons = market.conservationCheck(150);
283
+ assert.ok(cons.conserved, '拆账不改总额,守恒被蒙蔽');
284
+ // 独立审计捕获
285
+ const bad = market.independentAudit();
286
+ assert.ok(!bad.passed, '独立审计应发现账实不符');
287
+ assert.ok(
288
+ bad.mismatches.some((m) => m.account === '__ghost__:split'),
289
+ '应列出幽灵拆账账户',
290
+ );
291
+ });
292
+
157
293
  // 9. AipIdentity DID 与签名往返
158
294
  test('AipIdentity 生成 did:aip 且签名可验证', () => {
159
295
  const id = AipIdentity.generate();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twinsearth/agent-universe",
3
- "version": "2.5.7",
3
+ "version": "2.5.9",
4
4
  "description": "Agent Universe - 群众化的 AGI 路线,去中心化的智能体共享 & 开源网络。使命:让科技造福全人类!",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",