@seasonkoh/webaz 0.1.18 → 0.1.20
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/dist/layer0-foundation/L0-2-state-machine/engine.js +171 -56
- package/dist/layer0-foundation/L0-2-state-machine/order-chain.js +23 -0
- package/dist/layer0-foundation/L0-2-state-machine/transitions.js +65 -2
- package/dist/layer1-agent/L1-1-mcp-server/server.js +46 -22
- package/dist/layer2-business/L2-8-feedback/build-feedback-engine.js +64 -5
- package/dist/layer2-business/L2-9-contribution/build-reputation-engine.js +4 -0
- package/dist/pwa/contract-fingerprint.js +46 -0
- package/dist/pwa/economic-participation.js +122 -0
- package/dist/pwa/endpoint-actions.js +112 -0
- package/dist/pwa/entity-dictionary.js +125 -0
- package/dist/pwa/goal-index.js +60 -0
- package/dist/pwa/integration-contract.js +64 -0
- package/dist/pwa/limits.js +30 -0
- package/dist/pwa/negative-space.js +64 -0
- package/dist/pwa/public/app.js +5 -0
- package/dist/pwa/public/docs/ECONOMIC-MODEL.md +287 -0
- package/dist/pwa/public/docs/INTEGRATOR.md +67 -0
- package/dist/pwa/public/docs/META-RULES-FULL.md +543 -0
- package/dist/pwa/public/i18n.js +4 -0
- package/dist/pwa/routes/build-feedback.js +3 -2
- package/dist/pwa/routes/disputes-write.js +68 -0
- package/dist/pwa/routes/orders-action.js +93 -1
- package/dist/pwa/routes/orders-create.js +7 -2
- package/dist/pwa/routes/orders-read.js +18 -0
- package/dist/pwa/routes/public-utils.js +131 -1
- package/dist/pwa/routes/webauthn.js +9 -1
- package/dist/pwa/server.js +69 -123
- package/dist/pwa/verifiability-index.js +63 -0
- package/dist/version.js +32 -0
- package/package.json +2 -1
|
@@ -133,6 +133,28 @@ export function checkTimeouts(db) {
|
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
|
+
// RFC-007 stage 3:终结【临时判责】的客观拒单 —— 举证窗口逾期仍无人仲裁 → 落定为违约结算。
|
|
137
|
+
// (被仲裁接手的订单 stage 5 会清 pending / 改 disputed,不会命中此扫描;settled_fault_at 防重入。)
|
|
138
|
+
// 注:deadline 存为 SQLite datetime(空格)、now 为 JS ISO(带 T/Z),字符串直比会错(空格<T) → 两侧都用 datetime() 归一化。
|
|
139
|
+
const staleProvisional = db.prepare(`
|
|
140
|
+
SELECT id FROM orders
|
|
141
|
+
WHERE status = 'fault_seller' AND COALESCE(decline_objective_pending, 0) = 1
|
|
142
|
+
AND COALESCE(decline_contested, 0) = 0
|
|
143
|
+
AND settled_fault_at IS NULL
|
|
144
|
+
AND decline_contest_deadline IS NOT NULL AND datetime(decline_contest_deadline) < datetime(?)
|
|
145
|
+
`).all(now);
|
|
146
|
+
for (const o of staleProvisional) {
|
|
147
|
+
try {
|
|
148
|
+
const sys = getSystemUser(db);
|
|
149
|
+
settleFault(db, o.id, 'fault_seller');
|
|
150
|
+
transition(db, o.id, 'completed', sys.id, [], 'RFC-007:客观拒单举证窗口逾期未仲裁 → 终结为违约');
|
|
151
|
+
db.prepare('UPDATE orders SET decline_objective_pending = 0 WHERE id = ?').run(o.id);
|
|
152
|
+
details.push({ orderId: o.id, action: '临时判责 → fault_seller(举证逾期终结)' });
|
|
153
|
+
}
|
|
154
|
+
catch (e) {
|
|
155
|
+
console.error(`[decline finalize] order=${o.id}`, e);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
136
158
|
return { processed: details.length, details };
|
|
137
159
|
}
|
|
138
160
|
/**
|
|
@@ -186,7 +208,9 @@ export function getOrderStatus(db, orderId) {
|
|
|
186
208
|
// - fault_logistics→ buyer 全额退款 + seller stake 全额返还(卖家无责,物流坏账协议吸收)
|
|
187
209
|
// - fault_buyer → 仅库存回退(escrow 未锁,无资金动作)
|
|
188
210
|
// 不发放 commission / PV / 基金池入金 — 无真实成交
|
|
189
|
-
|
|
211
|
+
// export: 供 tests/test-fault-forfeit-conservation.ts 直接验证守恒(真实代码,非复刻)。
|
|
212
|
+
// 生产仅由本模块 checkTimeouts 内部调用;导出不改变其调用语义。
|
|
213
|
+
export function settleFault(db, orderId, faultState) {
|
|
190
214
|
const sysUserId = 'sys_protocol';
|
|
191
215
|
// 幂等检查 + 资金处置全部包在 transaction 内(防未来迁 PG 时并发 race)
|
|
192
216
|
db.transaction(() => {
|
|
@@ -199,13 +223,98 @@ function settleFault(db, orderId, faultState) {
|
|
|
199
223
|
const buyerId = order.buyer_id;
|
|
200
224
|
const sellerId = order.seller_id;
|
|
201
225
|
const isSecondhand = order.source === 'secondhand';
|
|
202
|
-
//
|
|
203
|
-
//
|
|
204
|
-
//
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
//
|
|
208
|
-
|
|
226
|
+
// RFC-008 stage 1(印钱 bug 修复)+ stage 2(罚没解耦):
|
|
227
|
+
// stage 1:违约没收按订单 stake_backing 快照,绝不假设已锁、绝不超背书 → 根治"staked 转负+印钱"bug。
|
|
228
|
+
// stage 2:罚没率【与质押率解耦】—— penalty = fault_penalty_rate(默认 30%) × total,独立于 stake_rate。
|
|
229
|
+
// · 背书订单(stake_backing>0):先扣 staked(封顶背书额),不足再扣卖家【自由 balance】(责任自负,
|
|
230
|
+
// 罚没真可执行,不被薄质押架构性封顶)。
|
|
231
|
+
// · 起步免赔付(require_seller_stake=0 → backing=0):仍 0 没收,【绝不碰新商家自由余额】(否则重新引入
|
|
232
|
+
// 我们刚移除的门槛),买家已全额退款,卖家仅掉信誉。
|
|
233
|
+
// 守恒不变:实扣 F(staked+balance) === 分出去的 F(协议/买家/推广/公池),永不印钱、永不转负。
|
|
234
|
+
const faultPenaltyRate = () => {
|
|
235
|
+
const row = db.prepare("SELECT value FROM protocol_params WHERE key = 'fault_penalty_rate'").get();
|
|
236
|
+
const v = Number(row?.value);
|
|
237
|
+
return Number.isFinite(v) && v >= 0 ? v : 0.30;
|
|
238
|
+
};
|
|
239
|
+
const penaltyAmount = isSecondhand ? 0 : Math.round(total * faultPenaltyRate() * 100) / 100;
|
|
240
|
+
const orderStakeBacking = Math.max(0, Math.round(Number(order.stake_backing || 0) * 100) / 100);
|
|
241
|
+
// RFC-007 stage 4:没收后的【守恒 + 不牟利】再分配(取代旧的 buyer 50% / protocol 50%)。
|
|
242
|
+
// 旧分配漏掉推广人(违反 §谁责任谁承担:推广人承担了真实推广成本却零补偿)。
|
|
243
|
+
// 新规则(全部用订单快照,可复算,绝不印钱):
|
|
244
|
+
// 1. 协议只回收【原本该收的平台费】protocolTake = min(F, total × protocol_fee_rate)
|
|
245
|
+
// —— 协议不从违约牟利;fund_base(1%) 排除(无成交=无 GMV,社区基金不应从罚没获利)。
|
|
246
|
+
// 2. R = F − protocolTake;买家补偿 = R × 50%(受损对手方固定份额)。
|
|
247
|
+
// 3. 推广人 = R 的另一半,按 l1/l2/l3 原始佣金比例分,【封顶各自原始佣金】—— 永不超过其真实损失。
|
|
248
|
+
// 4. 推广半残值(超封顶 / 无推广人)→ commission_reserve 三级公池(与正常单未归属佣金同去向,不给买家=不过补)。
|
|
249
|
+
// 守恒:protocolTake + buyerComp + promotersPaid + reserveResidual ≡ F(按构造,残值兜底)。
|
|
250
|
+
const FORFEIT_LEVEL_RATES = { 1: 0.70, 2: 0.20, 3: 0.10 };
|
|
251
|
+
const round2 = (n) => Math.round(n * 100) / 100;
|
|
252
|
+
const reserveResidual = (amount, note) => {
|
|
253
|
+
const a = round2(amount);
|
|
254
|
+
if (a <= 0)
|
|
255
|
+
return;
|
|
256
|
+
// 复制 redirectToCommissionReserve 的两笔写入(L0 不依赖 server.ts;kind 用既有 orphan_sponsor 桶)
|
|
257
|
+
db.prepare(`UPDATE commission_reserve SET balance = balance + ?, total_orphan_sponsor = total_orphan_sponsor + ?, updated_at = datetime('now') WHERE id = 'main'`).run(a, a);
|
|
258
|
+
db.prepare(`INSERT INTO commission_reserve_txns (id, kind, from_user_id, amount, related_order_id, note) VALUES (?,?,?,?,?,?)`)
|
|
259
|
+
.run(generateId('crt'), 'redirect_orphan_sponsor', sellerId, a, orderId, note);
|
|
260
|
+
};
|
|
261
|
+
const protocolFeeRate = () => {
|
|
262
|
+
const key = isSecondhand ? 'protocol_fee_rate_secondhand' : 'protocol_fee_rate_shop';
|
|
263
|
+
const row = db.prepare('SELECT value FROM protocol_params WHERE key = ?').get(key);
|
|
264
|
+
const v = Number(row?.value);
|
|
265
|
+
return Number.isFinite(v) && v >= 0 ? v : (isSecondhand ? 0.01 : 0.02);
|
|
266
|
+
};
|
|
267
|
+
// 罚没【收取 + RFC-007 守恒分配】,返回实扣额(= 0 表示起步免赔付,无没收无印钱)
|
|
268
|
+
const forfeitAndDistribute = (penalty) => {
|
|
269
|
+
// 起步免赔付:无背书订单(stake_backing=0)绝不没收、绝不碰新商家自由余额
|
|
270
|
+
if (orderStakeBacking <= 0)
|
|
271
|
+
return 0;
|
|
272
|
+
// stage 2 收取:先扣 staked(封顶背书额),不足部分再扣卖家自由 balance(责任自负;封顶其真实余额→不转负)
|
|
273
|
+
const fromStaked = Math.min(penalty, orderStakeBacking);
|
|
274
|
+
const remainder = round2(penalty - fromStaked);
|
|
275
|
+
const sellerBal = Math.max(0, Number(db.prepare('SELECT COALESCE(balance,0) AS b FROM wallets WHERE user_id = ?').get(sellerId)?.b ?? 0));
|
|
276
|
+
const fromBalance = round2(Math.min(remainder, sellerBal));
|
|
277
|
+
const F = round2(fromStaked + fromBalance);
|
|
278
|
+
if (F <= 0)
|
|
279
|
+
return 0;
|
|
280
|
+
if (fromStaked > 0)
|
|
281
|
+
db.prepare('UPDATE wallets SET staked = staked - ? WHERE user_id = ?').run(fromStaked, sellerId);
|
|
282
|
+
if (fromBalance > 0)
|
|
283
|
+
db.prepare('UPDATE wallets SET balance = balance - ? WHERE user_id = ?').run(fromBalance, sellerId);
|
|
284
|
+
// 1. 协议回收原始平台费(封顶 F,不牟利)
|
|
285
|
+
const protocolTake = round2(Math.min(F, total * protocolFeeRate()));
|
|
286
|
+
if (protocolTake > 0)
|
|
287
|
+
db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(protocolTake, sysUserId);
|
|
288
|
+
// 2. R 与买家补偿
|
|
289
|
+
const R = round2(F - protocolTake);
|
|
290
|
+
const buyerComp = round2(R * 0.5);
|
|
291
|
+
if (buyerComp > 0)
|
|
292
|
+
db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(buyerComp, buyerId);
|
|
293
|
+
// 3. 推广半 → l1/l2/l3 按原始佣金比例,封顶各自原始佣金
|
|
294
|
+
const promoterHalf = round2(R - buyerComp);
|
|
295
|
+
const commissionRate = Number(order.snapshot_commission_rate ?? 0);
|
|
296
|
+
const pool = round2(total * (Number.isFinite(commissionRate) && commissionRate > 0 ? commissionRate : 0));
|
|
297
|
+
const levels = [
|
|
298
|
+
{ uid: order.l1_uid, orig: round2(pool * FORFEIT_LEVEL_RATES[1]) },
|
|
299
|
+
{ uid: order.l2_uid, orig: round2(pool * FORFEIT_LEVEL_RATES[2]) },
|
|
300
|
+
{ uid: order.l3_uid, orig: round2(pool * FORFEIT_LEVEL_RATES[3]) },
|
|
301
|
+
].filter(l => l.uid); // 仅【真实存在的推广人】参与
|
|
302
|
+
const originalCommissionTotal = round2(levels.reduce((s, l) => s + l.orig, 0));
|
|
303
|
+
let promotersPaid = 0;
|
|
304
|
+
if (promoterHalf > 0 && originalCommissionTotal > 0) {
|
|
305
|
+
const payable = Math.min(promoterHalf, originalCommissionTotal); // 封顶原始佣金总额
|
|
306
|
+
for (const l of levels) {
|
|
307
|
+
const share = round2(payable * (l.orig / originalCommissionTotal));
|
|
308
|
+
if (share <= 0)
|
|
309
|
+
continue;
|
|
310
|
+
db.prepare('UPDATE wallets SET balance = balance + ?, earned = earned + ? WHERE user_id = ?').run(share, share, l.uid);
|
|
311
|
+
promotersPaid = round2(promotersPaid + share);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
// 4. 推广半残值(超封顶 / 无推广人 / 取整余数)→ 三级公池;绝不给买家、绝不印钱
|
|
315
|
+
reserveResidual(promoterHalf - promotersPaid, `RFC-007 fault forfeit residual order=${orderId}`);
|
|
316
|
+
return F;
|
|
317
|
+
};
|
|
209
318
|
// P0.1:RFQ 路径的 bid_stake_held — fault 时由各分支按规则处理
|
|
210
319
|
const bidStakeHeld = Number(order.bid_stake_held || 0);
|
|
211
320
|
if (faultState === 'fault_seller') {
|
|
@@ -222,31 +331,9 @@ function settleFault(db, orderId, faultState) {
|
|
|
222
331
|
if (compToSys > 0)
|
|
223
332
|
db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(compToSys, sysUserId);
|
|
224
333
|
}
|
|
225
|
-
// 2.
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
-
let actualDeduct = 0;
|
|
229
|
-
if (stakeLocked) {
|
|
230
|
-
// 已锁 → stake 全额扣(必然可扣,因 stake 已 lock 不可挪用)
|
|
231
|
-
db.prepare('UPDATE wallets SET staked = staked - ? WHERE user_id = ?').run(stakeAmount, sellerId);
|
|
232
|
-
actualDeduct = stakeAmount;
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
// 未锁 → 从 seller.balance 扣(不足则只扣到 0,差额不发放,不印钱)
|
|
236
|
-
const w = db.prepare('SELECT balance FROM wallets WHERE user_id = ?').get(sellerId);
|
|
237
|
-
actualDeduct = Math.min(stakeAmount, Number(w?.balance ?? 0));
|
|
238
|
-
if (actualDeduct > 0) {
|
|
239
|
-
db.prepare('UPDATE wallets SET balance = balance - ? WHERE user_id = ?').run(actualDeduct, sellerId);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
// 按 actualDeduct 比例分配(buyer 50% + sys_protocol 50%),保证 入 = 出
|
|
243
|
-
const compensation = Math.round(actualDeduct * 0.5 * 100) / 100;
|
|
244
|
-
const protocolShare = Math.round((actualDeduct - compensation) * 100) / 100;
|
|
245
|
-
if (compensation > 0)
|
|
246
|
-
db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(compensation, buyerId);
|
|
247
|
-
if (protocolShare > 0)
|
|
248
|
-
db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(protocolShare, sysUserId);
|
|
249
|
-
}
|
|
334
|
+
// 2. 罚没(fault_penalty_rate×total,staked 不足扣自由 balance,绝不印钱)→ RFC-007 守恒分配
|
|
335
|
+
if (penaltyAmount > 0)
|
|
336
|
+
forfeitAndDistribute(penaltyAmount);
|
|
250
337
|
// 3. 库存回退(非二手)
|
|
251
338
|
if (!isSecondhand)
|
|
252
339
|
db.prepare('UPDATE products SET stock = stock + 1 WHERE id = ?').run(order.product_id);
|
|
@@ -278,27 +365,9 @@ function settleFault(db, orderId, faultState) {
|
|
|
278
365
|
if (compToSys > 0)
|
|
279
366
|
db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(compToSys, sysUserId);
|
|
280
367
|
}
|
|
281
|
-
// 3.
|
|
282
|
-
if (
|
|
283
|
-
|
|
284
|
-
if (stakeLocked) {
|
|
285
|
-
db.prepare('UPDATE wallets SET staked = staked - ? WHERE user_id = ?').run(stakeAmount, sellerId);
|
|
286
|
-
actualDeduct = stakeAmount;
|
|
287
|
-
}
|
|
288
|
-
else {
|
|
289
|
-
const w = db.prepare('SELECT balance FROM wallets WHERE user_id = ?').get(sellerId);
|
|
290
|
-
actualDeduct = Math.min(stakeAmount, Number(w?.balance ?? 0));
|
|
291
|
-
if (actualDeduct > 0) {
|
|
292
|
-
db.prepare('UPDATE wallets SET balance = balance - ? WHERE user_id = ?').run(actualDeduct, sellerId);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
const compensation = Math.round(actualDeduct * 0.5 * 100) / 100;
|
|
296
|
-
const protocolShare = Math.round((actualDeduct - compensation) * 100) / 100;
|
|
297
|
-
if (compensation > 0)
|
|
298
|
-
db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(compensation, buyerId);
|
|
299
|
-
if (protocolShare > 0)
|
|
300
|
-
db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(protocolShare, sysUserId);
|
|
301
|
-
}
|
|
368
|
+
// 3. 罚没(self-fulfill seller 违约;fault_penalty_rate×total,staked 不足扣自由 balance)→ RFC-007 守恒分配
|
|
369
|
+
if (penaltyAmount > 0)
|
|
370
|
+
forfeitAndDistribute(penaltyAmount);
|
|
302
371
|
// 4. 库存回退
|
|
303
372
|
if (!isSecondhand)
|
|
304
373
|
db.prepare('UPDATE products SET stock = stock + 1 WHERE id = ?').run(order.product_id);
|
|
@@ -318,9 +387,10 @@ function settleFault(db, orderId, faultState) {
|
|
|
318
387
|
if (bidStakeHeld > 0) {
|
|
319
388
|
db.prepare('UPDATE wallets SET balance = balance + ?, staked = staked - ? WHERE user_id = ?').run(bidStakeHeld, bidStakeHeld, sellerId);
|
|
320
389
|
}
|
|
321
|
-
|
|
390
|
+
// seller 无责 → 退还其【该单实际背书的 stake】(= stake_backing;起步阶段=0,无可退)
|
|
391
|
+
if (orderStakeBacking > 0) {
|
|
322
392
|
db.prepare('UPDATE wallets SET staked = staked - ?, balance = balance + ? WHERE user_id = ?')
|
|
323
|
-
.run(
|
|
393
|
+
.run(orderStakeBacking, orderStakeBacking, sellerId);
|
|
324
394
|
}
|
|
325
395
|
// 3. 库存回退
|
|
326
396
|
if (!isSecondhand)
|
|
@@ -353,6 +423,51 @@ function settleFault(db, orderId, faultState) {
|
|
|
353
423
|
db.prepare("UPDATE orders SET settled_fault_at = datetime('now') WHERE id = ?").run(orderId);
|
|
354
424
|
})();
|
|
355
425
|
}
|
|
426
|
+
// ─── RFC-007 stage 5:无责拒单结算(仲裁认定客观无责后调用)──────────────────
|
|
427
|
+
// §无责零成本:买家全额退款 + 卖家质押全退,零罚没、零佣金、零基金入金(无真实成交)。
|
|
428
|
+
// + 中性 no_fault_decline 信誉事件(points=0,不降分,仅作 rate-observable 信号防滥用)。
|
|
429
|
+
// 守恒:仅做"escrow→买家 balance"和"staked→卖家 balance"两笔内部移动,系统总额不变,绝不印钱。
|
|
430
|
+
export function settleDeclinedNoFault(db, orderId) {
|
|
431
|
+
db.transaction(() => {
|
|
432
|
+
const order = db.prepare('SELECT * FROM orders WHERE id = ?').get(orderId);
|
|
433
|
+
if (!order)
|
|
434
|
+
return;
|
|
435
|
+
if (order.settled_fault_at)
|
|
436
|
+
return; // 幂等(复用 settled_fault_at 标记)
|
|
437
|
+
const total = Number(order.total_amount);
|
|
438
|
+
const buyerId = order.buyer_id;
|
|
439
|
+
const sellerId = order.seller_id;
|
|
440
|
+
const isSecondhand = order.source === 'secondhand';
|
|
441
|
+
const orderStakeBacking = Math.max(0, Math.round(Number(order.stake_backing || 0) * 100) / 100);
|
|
442
|
+
const bidStakeHeld = Number(order.bid_stake_held || 0);
|
|
443
|
+
// 1. 买家 escrow 全额退回
|
|
444
|
+
db.prepare('UPDATE wallets SET escrowed = escrowed - ?, balance = balance + ? WHERE user_id = ?').run(total, total, buyerId);
|
|
445
|
+
// 2. 卖家质押全退(封顶其实际 staked,绝不转负)—— 无责零成本
|
|
446
|
+
const sellerStaked = Math.max(0, Number(db.prepare('SELECT COALESCE(staked,0) AS s FROM wallets WHERE user_id = ?').get(sellerId)?.s ?? 0));
|
|
447
|
+
const returnStake = Math.min(orderStakeBacking + bidStakeHeld, sellerStaked);
|
|
448
|
+
if (returnStake > 0)
|
|
449
|
+
db.prepare('UPDATE wallets SET staked = staked - ?, balance = balance + ? WHERE user_id = ?').run(returnStake, returnStake, sellerId);
|
|
450
|
+
// 3. 库存 / 二手状态恢复
|
|
451
|
+
if (!isSecondhand)
|
|
452
|
+
db.prepare('UPDATE products SET stock = stock + 1 WHERE id = ?').run(order.product_id);
|
|
453
|
+
else {
|
|
454
|
+
try {
|
|
455
|
+
db.prepare("UPDATE secondhand_items SET status = 'available', updated_at = datetime('now') WHERE id = ?").run(order.product_id);
|
|
456
|
+
}
|
|
457
|
+
catch { }
|
|
458
|
+
}
|
|
459
|
+
// 4. 中性 no_fault_decline 信誉事件(points=0 → 不降分;rate-observable 防"假客观"滥用)
|
|
460
|
+
try {
|
|
461
|
+
db.prepare('INSERT INTO reputation_events (id, user_id, order_id, event_type, points, reason) VALUES (?,?,?,?,?,?)')
|
|
462
|
+
.run(generateId('rep'), sellerId, orderId, 'no_fault_decline', 0, `客观无责拒单(仲裁认定) reason=${order.decline_reason_code || ''}`);
|
|
463
|
+
}
|
|
464
|
+
catch (e) {
|
|
465
|
+
console.warn('[no_fault_decline rep event]', e.message);
|
|
466
|
+
}
|
|
467
|
+
// 5. 结算标记 + 清临时判责 flag
|
|
468
|
+
db.prepare("UPDATE orders SET settled_fault_at = datetime('now'), decline_objective_pending = 0 WHERE id = ?").run(orderId);
|
|
469
|
+
})();
|
|
470
|
+
}
|
|
356
471
|
// ─── 内部工具函数 ─────────────────────────────────────────────
|
|
357
472
|
/** 找出当前订单超时的转移(如果有) */
|
|
358
473
|
function findActiveDeadlineTransition(order, now) {
|
|
@@ -42,6 +42,29 @@ export function initOrderChainSchema(db) {
|
|
|
42
42
|
}
|
|
43
43
|
catch { }
|
|
44
44
|
}
|
|
45
|
+
export function listOrderEventsSince(db, userId, since, limit) {
|
|
46
|
+
const lim = Math.min(200, Math.max(1, Math.floor(limit) || 50));
|
|
47
|
+
const sinceRid = since && /^\d+$/.test(since) ? Number(since) : 0;
|
|
48
|
+
const rows = db.prepare(`
|
|
49
|
+
SELECT e.rowid AS rid, e.order_id, e.seq, e.event_type, e.from_status, e.to_status, e.actor_role,
|
|
50
|
+
e.event_hash, e.prev_event_hash, e.signed_at, e.created_at
|
|
51
|
+
FROM order_events e
|
|
52
|
+
JOIN orders o ON o.id = e.order_id
|
|
53
|
+
WHERE (o.buyer_id = ? OR o.seller_id = ? OR o.logistics_id = ?)
|
|
54
|
+
AND e.rowid > ?
|
|
55
|
+
ORDER BY e.rowid ASC
|
|
56
|
+
LIMIT ?
|
|
57
|
+
`).all(userId, userId, userId, sinceRid, lim);
|
|
58
|
+
const events = rows.map(r => ({
|
|
59
|
+
cursor: String(r.rid),
|
|
60
|
+
order_id: r.order_id, seq: r.seq, event_type: r.event_type,
|
|
61
|
+
from_status: r.from_status, to_status: r.to_status, actor_role: r.actor_role,
|
|
62
|
+
event_hash: r.event_hash, prev_event_hash: r.prev_event_hash, signed_at: r.signed_at, created_at: r.created_at,
|
|
63
|
+
}));
|
|
64
|
+
const has_more = events.length === lim;
|
|
65
|
+
const next_cursor = events.length ? events[events.length - 1].cursor : (since ?? null);
|
|
66
|
+
return { events, next_cursor, has_more };
|
|
67
|
+
}
|
|
45
68
|
// canonical_payload — 递归 stringify,每层 key 字母序
|
|
46
69
|
// 修复 ultrareview bug_012:浅排序让嵌套对象用插入序,破坏"独立验证"承诺
|
|
47
70
|
// (已知问题:arbitration_ruling 时 liability_parties 是嵌套数组)
|
|
@@ -177,9 +177,12 @@ export const VALID_TRANSITIONS = {
|
|
|
177
177
|
description: '买家超时未付款,自动取消并标记违约'
|
|
178
178
|
},
|
|
179
179
|
'paid→fault_seller': {
|
|
180
|
-
|
|
180
|
+
// RFC-007 stage 2:除 system 超时判责外,允许 seller 【主动拒单】(decline) 显式触发此转移。
|
|
181
|
+
// 主动拒单 = 卖家明确不接此单(vs 沉默超时),记 decline_reason_code + declined_at。
|
|
182
|
+
// stage 2 一律走违约路径(与超时同结算);stage 3 auto-verify 上线后,客观无责拒单将改判 declined_nofault。
|
|
183
|
+
allowedRoles: ['system', 'seller'],
|
|
181
184
|
requiresEvidence: false,
|
|
182
|
-
description: '
|
|
185
|
+
description: '卖家未接单:system 超时判责 或 seller 主动拒单(decline),退款买家并按违约结算'
|
|
183
186
|
},
|
|
184
187
|
'accepted→fault_seller': {
|
|
185
188
|
allowedRoles: ['system'],
|
|
@@ -201,6 +204,18 @@ export const VALID_TRANSITIONS = {
|
|
|
201
204
|
requiresEvidence: false,
|
|
202
205
|
description: '物流超时未投递,标记物流违约'
|
|
203
206
|
},
|
|
207
|
+
// ── RFC-007 stage 5:客观拒单仲裁翻案 ─────────────────────────────
|
|
208
|
+
// 临时判责(fault_seller + decline_objective_pending)经【人工仲裁】认定客观无责 → declined_nofault。
|
|
209
|
+
'fault_seller→declined_nofault': {
|
|
210
|
+
allowedRoles: ['arbitrator', 'system'],
|
|
211
|
+
requiresEvidence: false,
|
|
212
|
+
description: 'RFC-007:客观拒单经人工仲裁认定无责 → 翻案为无责拒单(全退买家+退卖家质押,零罚没)'
|
|
213
|
+
},
|
|
214
|
+
'declined_nofault→completed': {
|
|
215
|
+
allowedRoles: ['system'],
|
|
216
|
+
requiresEvidence: false,
|
|
217
|
+
description: 'RFC-007:无责拒单结算完成(买家全额退款,卖家质押全退,无罚没无佣金)'
|
|
218
|
+
},
|
|
204
219
|
// ── 判责后的处置结算 ─────────────────────────────────────────
|
|
205
220
|
'fault_seller→completed': {
|
|
206
221
|
allowedRoles: ['system'],
|
|
@@ -236,3 +251,51 @@ export const CURRENT_RESPONSIBLE_SELF_FULFILL = {
|
|
|
236
251
|
picked_up: 'seller',
|
|
237
252
|
in_transit: 'seller',
|
|
238
253
|
};
|
|
254
|
+
// ─── RFC-011 §① 实体语义:订单状态机契约(doc=code,从 VALID_TRANSITIONS 生成)───────────
|
|
255
|
+
// 状态【含义】是 authored(枚举注释无法运行时取);转移由 VALID_TRANSITIONS 生成(零漂移)。
|
|
256
|
+
// 覆盖锁:tests/test-order-lifecycle-contract.ts 断言每个 OrderStatus 有含义 + 每条转移被序列化。
|
|
257
|
+
export const ORDER_STATE_MEANINGS = {
|
|
258
|
+
created: { zh: '已下单,等待买家付款', en: 'placed, awaiting buyer payment' },
|
|
259
|
+
paid: { zh: '资金已托管(escrow),等待卖家接单', en: 'funds in escrow, awaiting seller acceptance' },
|
|
260
|
+
accepted: { zh: '卖家已接单,承诺履约', en: 'seller accepted, committed to fulfil' },
|
|
261
|
+
shipped: { zh: '卖家已交物流/自履行发出', en: 'handed to logistics / self-fulfil dispatched' },
|
|
262
|
+
picked_up: { zh: '物流已揽收', en: 'picked up by logistics' },
|
|
263
|
+
in_transit: { zh: '运输中', en: 'in transit' },
|
|
264
|
+
delivered: { zh: '已投递,等待买家确认', en: 'delivered, awaiting buyer confirmation' },
|
|
265
|
+
confirmed: { zh: '买家确认收货 → 触发结算', en: 'buyer confirmed → triggers settlement' },
|
|
266
|
+
disputed: { zh: '争议中,等待人工仲裁', en: 'in dispute, awaiting human arbitration' },
|
|
267
|
+
completed: { zh: '交易完成,资金已分配(终态)', en: 'completed, funds settled (terminal)' },
|
|
268
|
+
cancelled: { zh: '已取消(终态)', en: 'cancelled (terminal)' },
|
|
269
|
+
fault_buyer: { zh: '买家违约(超时未付,终态)', en: 'buyer fault (payment timeout, terminal)' },
|
|
270
|
+
fault_seller: { zh: '卖家违约(超时未接/发 或 主动拒单)', en: 'seller fault (accept/ship timeout or active decline)' },
|
|
271
|
+
fault_logistics: { zh: '物流违约', en: 'logistics fault' },
|
|
272
|
+
declined_nofault: { zh: '卖家无责拒单(仲裁认定客观)→ 全退买家+退卖家质押,零罚没(终态)', en: 'seller no-fault decline (arbitration-cleared) → full refund + stake returned, no forfeit (terminal)' },
|
|
273
|
+
resolved_for_seller: { zh: '仲裁裁卖家胜诉,资金释放(终态)', en: 'arbitration ruled for seller, funds released (terminal)' },
|
|
274
|
+
refunded_partial: { zh: '仲裁裁部分退款(终态)', en: 'arbitration partial refund (terminal)' },
|
|
275
|
+
refunded_full: { zh: '仲裁裁全额退款,订单作废(终态)', en: 'arbitration full refund, order voided (terminal)' },
|
|
276
|
+
dispute_dismissed: { zh: '争议被驳回(无效,终态)', en: 'dispute dismissed (terminal)' },
|
|
277
|
+
expired: { zh: '订单超时自动失败(通用兜底,终态)', en: 'order expired (generic timeout, terminal)' },
|
|
278
|
+
};
|
|
279
|
+
/** 订单/争议生命周期契约 —— 集成方 agent 读它即懂"订单怎么流转 + 每步谁驱动 + 何时 + 含义"。 */
|
|
280
|
+
export function orderLifecycleContract() {
|
|
281
|
+
const keys = Object.keys(VALID_TRANSITIONS);
|
|
282
|
+
const states = Object.keys(ORDER_STATE_MEANINGS).map(s => ({
|
|
283
|
+
state: s, zh: ORDER_STATE_MEANINGS[s].zh, en: ORDER_STATE_MEANINGS[s].en,
|
|
284
|
+
responsible: CURRENT_RESPONSIBLE[s] ?? null,
|
|
285
|
+
terminal: !keys.some(k => k.startsWith(s + '→')), // 无出边 = 终态
|
|
286
|
+
}));
|
|
287
|
+
const transitions = Object.entries(VALID_TRANSITIONS).map(([key, t]) => {
|
|
288
|
+
const arrow = key.indexOf('→');
|
|
289
|
+
return {
|
|
290
|
+
from: key.slice(0, arrow), to: key.slice(arrow + 1),
|
|
291
|
+
allowed_roles: t.allowedRoles, deadline_field: t.deadlineField ?? null,
|
|
292
|
+
requires_evidence: !!t.requiresEvidence, auto_fault_state: t.autoFaultState ?? null,
|
|
293
|
+
description: t.description,
|
|
294
|
+
};
|
|
295
|
+
});
|
|
296
|
+
return {
|
|
297
|
+
entity: 'order',
|
|
298
|
+
note: 'Order/dispute lifecycle, generated from the protocol state machine (VALID_TRANSITIONS) — doc=code. State changes are observable via the event stream (§⑥ GET /api/agent/events) and integrity-verifiable via the signed chain (§⑤ GET /api/orders/:id/chain).',
|
|
299
|
+
states, transitions,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
@@ -30,7 +30,9 @@ import { initReputationSchema, recordOrderReputation, getReputation, getSearchBo
|
|
|
30
30
|
import { generateManifest, getManifestSummary, MANIFEST_URI, } from '../../layer0-foundation/L0-5-manifest/manifest.js';
|
|
31
31
|
import { requireAuth } from './auth.js';
|
|
32
32
|
import { createHash, randomBytes } from 'node:crypto';
|
|
33
|
-
|
|
33
|
+
import { SOFTWARE_VERSION } from '../../version.js';
|
|
34
|
+
// RFC-011 §④:版本单一来源 = package.json(经 src/version.ts)。不再硬编码(旧 '0.1.8' 早漂移到 0.1.19)。
|
|
35
|
+
const SERVER_VERSION = SOFTWARE_VERSION;
|
|
34
36
|
const TELEMETRY_URL = process.env.WEBAZ_TELEMETRY_URL ?? 'https://webaz.xyz/api/mcp-telemetry';
|
|
35
37
|
// 2026-06-01: phase A pre-launch 默认 OFF(opt-in)— W8 public launch 时翻回 default ON + 加 README 披露段
|
|
36
38
|
// Phase A pre-launch: telemetry default OFF (opt-in). Flip to default ON at W8 launch + add README disclosure section.
|
|
@@ -239,7 +241,7 @@ const TOOLS = [
|
|
|
239
241
|
{
|
|
240
242
|
name: 'webaz_info',
|
|
241
243
|
description: `Get WebAZ documentation and usage guide. Call this FIRST when onboarding a new agent.
|
|
242
|
-
Returns: protocol overview, available tools, role responsibilities, operation flows, **network_state (pre-launch disclaimer)**, **commission_model
|
|
244
|
+
Returns: protocol overview, available tools, role responsibilities, operation flows, **network_state (pre-launch disclaimer)**, **commission_model (3-tier share, jurisdiction-graded, explicit attribution, opt-in)**.
|
|
243
245
|
No auth required, no parameters needed.
|
|
244
246
|
|
|
245
247
|
⚠️ Important: WebAZ is currently **pre-launch** with ~0 real users on the canonical endpoint. All stats / counts returned by this and other tools come from the **local MCP SQLite DB**, not protocol-wide prod state. Read network_state field BEFORE you treat any number as real-economy data.`,
|
|
@@ -253,7 +255,7 @@ No auth required, no parameters needed.
|
|
|
253
255
|
// was ~1732 chars, now ~780 chars
|
|
254
256
|
description: `Register a new WebAZ account. Returns: api_key (36-char 128-bit credential, store securely) + permanent_code (6-char recovery code, pair with handle in webaz_mykey to recover lost api_key) + handle (URL-safe ID; if taken, system appends numeric suffix — check handle_modified flag) + created_at.
|
|
255
257
|
|
|
256
|
-
⚠️ **Consent required
|
|
258
|
+
⚠️ **Consent required**: creating an account on a human user's behalf registers an economic-relationship account (can participate in commission). Agent acting for a human user **MUST get explicit informed consent BEFORE creating account**. Do NOT auto-register from generic shopping questions.
|
|
257
259
|
|
|
258
260
|
Roles: buyer (browse/order/confirm) | seller (list/accept/ship) | logistics (pickup/transit/deliver) | reviewer (reviews) | arbitrator (disputes/rulings).
|
|
259
261
|
|
|
@@ -478,10 +480,15 @@ Missing deadline → protocol auto-marks party in default.`,
|
|
|
478
480
|
order_id: { type: 'string', description: 'Order ID' },
|
|
479
481
|
action: {
|
|
480
482
|
type: 'string',
|
|
481
|
-
enum: ['accept', 'ship', 'pickup', 'transit', 'deliver', 'confirm', 'dispute'],
|
|
482
|
-
description: 'Action to execute',
|
|
483
|
+
enum: ['accept', 'ship', 'pickup', 'transit', 'deliver', 'confirm', 'dispute', 'decline', 'contest_decline'],
|
|
484
|
+
description: 'Action to execute. decline = seller actively refuses a paid order (vs silent timeout); requires decline_reason_code. contest_decline = seller opens human arbitration on an objective-claimed provisional fault (within the contest window) to be cleared to no-fault; pass evidence_description.',
|
|
483
485
|
},
|
|
484
486
|
notes: { type: 'string', description: 'Action note (e.g. tracking number, dispute reason)' },
|
|
487
|
+
decline_reason_code: {
|
|
488
|
+
type: 'string',
|
|
489
|
+
enum: ['stock_consumed_concurrent', 'stale_price_snapshot', 'force_majeure', 'price_regret', 'cherry_pick', 'other'],
|
|
490
|
+
description: 'Required for action=decline. Why the seller refuses. Subjective (price_regret / cherry_pick / other) → settles immediately as seller-fault (buyer fully refunded). Objective-claimed (stock_consumed_concurrent / stale_price_snapshot / force_majeure) → PROVISIONAL fault: not settled yet, opens a contest window — seller must open arbitration (webaz_dispute) with evidence to be cleared (these off-chain facts have no on-protocol auto-verification); uncontested by the deadline → auto-finalizes as fault.',
|
|
491
|
+
},
|
|
485
492
|
evidence_description: {
|
|
486
493
|
type: 'string',
|
|
487
494
|
description: 'Evidence description (recommended for ship/pickup/deliver; required for dispute)',
|
|
@@ -795,7 +802,7 @@ Safer than \`webaz_revoke_key\` — atomic swap, no access gap.`,
|
|
|
795
802
|
|
|
796
803
|
⚠️ **Opt-in required (RFC-002)**: rewards default = off. \`rewards_status\` field returns 4-state {opted_in | never_activated | auto_downgraded | deactivated} + pending_escrow tally. Opted-out users still see attribution + tree, but commission held in escrow until activation via PWA #me.
|
|
797
804
|
|
|
798
|
-
⚠️ **
|
|
805
|
+
⚠️ **Consent required**: generating referral links / promoting on a human user's behalf needs the user's explicit authorization. Agent **MUST get explicit consent** before generating referral links / promoting. Do NOT auto-recruit.`,
|
|
799
806
|
inputSchema: {
|
|
800
807
|
type: 'object',
|
|
801
808
|
properties: {
|
|
@@ -810,7 +817,7 @@ Safer than \`webaz_revoke_key\` — atomic swap, no access gap.`,
|
|
|
810
817
|
|
|
811
818
|
⚠️ **Opt-in required (RFC-002 §3.5)**: this is a valuation-layer action. Caller must have \`rewards_opted_in=1\` (builder-identity opt-in). Opted-out users get \`{error: 'rewards_opt_in_required', missing_requirements, next_steps}\` — direct user to PWA #me to apply.
|
|
812
819
|
|
|
813
|
-
⚠️ **
|
|
820
|
+
⚠️ **Consent required**: this builds a referral chain on the user's behalf. Agent acting for a human user **MUST get explicit consent**. Do NOT auto-generate. See webaz_info.commission_model.`,
|
|
814
821
|
inputSchema: {
|
|
815
822
|
type: 'object',
|
|
816
823
|
properties: {
|
|
@@ -1359,7 +1366,7 @@ Gate by type: ux_issue/bug (reporting = using) → login only, NO Passkey, anyon
|
|
|
1359
1366
|
|
|
1360
1367
|
Actions:
|
|
1361
1368
|
- list_open (default): open tasks (opt. area filter)
|
|
1362
|
-
- claim: take an open task; provenance=human|ai_assisted|ai_authored (self-declared, not detected); auto-expires ~7d if not submitted
|
|
1369
|
+
- claim: take an open task; provenance=human|ai_assisted|ai_authored (self-declared, not detected); auto-expires ~7d if not submitted. Returns a **handoff** (repo + AGENTS.md + PR flow) — point a coding agent at it to actually do the work; the human needn't know git.
|
|
1363
1370
|
- submit: mark in_review with pr_ref
|
|
1364
1371
|
- status: tasks you hold (claimed/in_review)
|
|
1365
1372
|
- profile: your build dashboard — KPI/tier/restrictions+appeal, self-view (private, no public leaderboard)
|
|
@@ -1439,9 +1446,22 @@ async function handleContribute(args) {
|
|
|
1439
1446
|
const tid = args.task_id;
|
|
1440
1447
|
if (!tid)
|
|
1441
1448
|
return { error: 'task_id required for action=claim' };
|
|
1442
|
-
|
|
1449
|
+
const r = await apiCall('/api/build-tasks/' + encodeURIComponent(tid) + '/claim', {
|
|
1443
1450
|
method: 'POST', apiKey, body: { provenance: args.provenance },
|
|
1444
1451
|
});
|
|
1452
|
+
// RFC-006 断点1(b)交接:认领成功后直接下发"怎么真正动手",让贡献者的【编码 agent】接手 git/PR。
|
|
1453
|
+
// 关键:人不必会 git——人的编码 agent(如 Claude Code)做;人(Passkey 真人)担责。
|
|
1454
|
+
if (!r.error) {
|
|
1455
|
+
r.handoff = {
|
|
1456
|
+
repo: 'https://github.com/seasonsagents-art/webaz',
|
|
1457
|
+
start_here: 'Read AGENTS.md (project map + before-you-code + PR flow), then CONTRIBUTING.md.',
|
|
1458
|
+
do_the_work: 'Point a coding agent (e.g. Claude Code) at the repo; work on a single-topic branch. The buyer/shopping agent is not the coding agent — hand off to one.',
|
|
1459
|
+
pr_flow: 'Commit with DCO sign-off (git commit -s). If AI-authored, add 🤖🤖🤖 to the PR title + a meta-rule trace. Humans merge — no auto-merge.',
|
|
1460
|
+
then: `When the PR is open, report it back: webaz_contribute action=submit task_id=${tid} pr_ref=#<N>.`,
|
|
1461
|
+
human_note: "You don't need to know git — your coding agent does it; you (the Passkey-bound human) stay accountable.",
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
1464
|
+
return r;
|
|
1445
1465
|
}
|
|
1446
1466
|
if (action === 'submit') {
|
|
1447
1467
|
const tid = args.task_id;
|
|
@@ -1492,8 +1512,9 @@ function handleInfo() {
|
|
|
1492
1512
|
return {
|
|
1493
1513
|
...summary,
|
|
1494
1514
|
// 诚实化披露 — 排在 stats / quick_start 之前,让 agent 先建立正确预期。
|
|
1495
|
-
// 第三方尽调 agent 抓到过的破绽:live_stats 是本地库数字、引导词推 agent
|
|
1496
|
-
//
|
|
1515
|
+
// 第三方尽调 agent 抓到过的破绽:live_stats 是本地库数字、引导词推 agent 拉新。
|
|
1516
|
+
// 此处主动披露真实状态(pre-launch / 数据来源),不抗辩;agent 自己判断如何处理。
|
|
1517
|
+
// 佣金按【功能】中性描述(commission_model),不做"自证不是X"的辩护——正常机制无需自证。
|
|
1497
1518
|
network_state: {
|
|
1498
1519
|
// RFC-003 P3:显式声明当前客户端模式,让 agent 一眼分清"真网络 vs 本机沙盒"
|
|
1499
1520
|
mode: MODE, // 'network' | 'sandbox'
|
|
@@ -1517,15 +1538,15 @@ function handleInfo() {
|
|
|
1517
1538
|
},
|
|
1518
1539
|
live_stats: stats,
|
|
1519
1540
|
economics,
|
|
1520
|
-
//
|
|
1541
|
+
// 佣金机制 —— 纯功能性描述(怎么运作),不做"自证清白"式辩护。
|
|
1521
1542
|
commission_model: {
|
|
1522
|
-
split: '7:2:1 — L1 70% / L2 20% / L3 10% of commission_pool',
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1543
|
+
split: '7:2:1 — L1 70% / L2 20% / L3 10% of an order\'s commission_pool',
|
|
1544
|
+
jurisdiction_tiers: 'Tiers are graded by the order region\'s max_levels — NOT a uniform 3 tiers everywhere. e.g. global region max_levels=1 → L1 only; singapore (etc.) max_levels=3 → up to L3. A region may also be 0 (no commission tiers; pool → community fund).',
|
|
1545
|
+
attribution: 'EXPLICIT per-order — commission goes to the promoter attributed at purchase time, not derived from the buyer\'s sponsor chain.',
|
|
1546
|
+
how_to_attribute: 'L1: webaz_place_order(promoter_api_key) records the direct promoter. Full L2/L3 chain requires the buyer to arrive via a webaz_share_link ?ref= URL clicked in a browser (builds product_share_attribution).',
|
|
1547
|
+
redirect_rules: 'chain_gap (no L / invalid sponsor) → charity_fund; level beyond the region cap → global_fund.',
|
|
1548
|
+
l1_gate: 'the promoter must be a verified buyer (≥1 completed order) to receive commission, otherwise that share redirects.',
|
|
1549
|
+
opt_in: 'Participation is opt-in (RFC-002): default = off. A user applies (Passkey + ≥1 completed order); attribution is always recorded, but commission settlement is gated until opt-in (pending held in pending_commission_escrow, 30d grace). See docs/rfcs/RFC-002-rewards-opt-in.md.',
|
|
1529
1550
|
},
|
|
1530
1551
|
// QA 轮 3 FAIL:roles 漏 reviewer。register 工具支持 5 个角色,info 必须列全。
|
|
1531
1552
|
roles: {
|
|
@@ -2328,6 +2349,7 @@ async function handleUpdateOrder(args) {
|
|
|
2328
2349
|
action,
|
|
2329
2350
|
notes: args.notes ?? '',
|
|
2330
2351
|
evidence_description: args.evidence_description ?? '',
|
|
2352
|
+
...(args.decline_reason_code ? { decline_reason_code: args.decline_reason_code } : {}),
|
|
2331
2353
|
...(args.logistics_company_id ? { logistics_company_id: args.logistics_company_id } : {}),
|
|
2332
2354
|
},
|
|
2333
2355
|
});
|
|
@@ -4215,7 +4237,9 @@ function settleOrder(db, orderId) {
|
|
|
4215
4237
|
}
|
|
4216
4238
|
// ─── MCP Server 主体 ──────────────────────────────────────────
|
|
4217
4239
|
export async function startMCPServer() {
|
|
4218
|
-
const server = new Server(
|
|
4240
|
+
const server = new Server(
|
|
4241
|
+
// name 是客户端配置引用的 server 标识(勿改);version 走单一来源(旧硬编码 '0.1.0' 已漂移)。
|
|
4242
|
+
{ name: 'dcp-protocol', version: SOFTWARE_VERSION }, { capabilities: { tools: {}, resources: {}, prompts: {} } });
|
|
4219
4243
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
4220
4244
|
// ── MCP Resources:协议 Manifest ─────────────────────────────
|
|
4221
4245
|
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
@@ -4264,7 +4288,7 @@ export async function startMCPServer() {
|
|
|
4264
4288
|
},
|
|
4265
4289
|
{
|
|
4266
4290
|
name: 'webaz-onboard',
|
|
4267
|
-
description: 'Onboarding for a new agent first connecting to webaz — explains protocol nature / pre-launch state /
|
|
4291
|
+
description: 'Onboarding for a new agent first connecting to webaz — explains protocol nature / pre-launch state / commission model / registration path / user-authorization boundaries. Read webaz_info first, then run this prompt.',
|
|
4268
4292
|
arguments: [],
|
|
4269
4293
|
},
|
|
4270
4294
|
{
|
|
@@ -4325,7 +4349,7 @@ export async function startMCPServer() {
|
|
|
4325
4349
|
type: 'text',
|
|
4326
4350
|
text: `请先调用 webaz_info 拿到完整协议状态,然后跟我确认以下几点理解:\n\n` +
|
|
4327
4351
|
`1. **网络阶段(network_state.phase)**:webaz 目前是什么状态?真实用户数多少?\n` +
|
|
4328
|
-
`2. **经济模型(commission_model
|
|
4352
|
+
`2. **经济模型(commission_model)**:三级佣金 7:2:1,按辖区 max_levels 分级(非各地无差别三级),显式 per-order 归因,opt-in 结算(默认 off)。` +
|
|
4329
4353
|
`代理 AI 不应默认替终端用户生成推广链接或拉新,需用户显式授权。\n` +
|
|
4330
4354
|
`3. **数据来源**:工具返回的所有 stats 是本机 MCP 数据库的本地计数,不代表协议全网真实状态。\n` +
|
|
4331
4355
|
`4. **铁律(Iron Rule)**:vote / arbitrate / agent_revoke / delete_passkey / 大额提现需要用户在 PWA 完成 WebAuthn ceremony,` +
|