/** * 接力分配(2026-10-09 定:平分)。参与分配的人 = 最终提交者 + 所有留下过有效检查点后离开的前任 * (has_valid_checkpoint 且 end_reason ∈ timeout / released / transferred);同一 Agent 多次持有只算一份。 * 赏金按人数平分,除不尽的余数归最终提交者。各笔之和必须等于 reward。 * * @param {number} reward 正整数 * @param {Array<{ agent_id: string, end_reason: 'submitted'|'released'|'timeout'|'transferred'|null, has_valid_checkpoint: boolean }>} holds 该任务的全部持有记录 * @param {string} final_agent_id 最终提交者 * @returns {Array<{ agent_id: string, amount: number, kind: 'winner'|'relay_share' }>} 最终提交者排第一 */ export function splitRelayPayout(reward, holds, final_agent_id) { if (!Number.isSafeInteger(reward) || reward < 1) throw new Error(`splitRelayPayout: reward invalid: ${reward}`) const relays = new Set() for (const hold of holds) { switch (hold.end_reason) { case 'released': case 'timeout': case 'transferred': if (hold.has_valid_checkpoint && hold.agent_id !== final_agent_id) relays.add(hold.agent_id) break case 'submitted': case null: break default: throw new Error(`Unhandled end_reason: ${hold.end_reason}, agent: ${hold.agent_id}`) } } const share = Math.floor(reward / (relays.size + 1)) // 人数多于积分时前任分不到整数份:全部归最终提交者(发放明细金额必须 > 0)。 const paidRelays = share === 0 ? [] : [...relays] const payouts = [ { agent_id: final_agent_id, amount: reward - share * paidRelays.length, kind: /** @type {const} */ ('winner') }, ...paidRelays.map((agent_id) => ({ agent_id, amount: share, kind: /** @type {const} */ ('relay_share') })), ] if (payouts.reduce((sum, payout) => sum + payout.amount, 0) !== reward) throw new Error(`splitRelayPayout: payouts do not sum to reward ${reward}`) return payouts }