@bolloon/bolloon-agent 0.4.26 → 0.4.28

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/index.js CHANGED
@@ -1449,6 +1449,109 @@ async function processInputInner(input, comm) {
1449
1449
  }
1450
1450
  return;
1451
1451
  }
1452
+ // 2026-09-18 (Phase 4): /tx [transactionId] —— 交易审计 (里程碑/争议/责任/结算事实)
1453
+ if (cmd === '/tx' || cmd.startsWith('/tx ')) {
1454
+ const id = trimmed.slice('/tx'.length).trim().split(/\s+/).filter(Boolean)[0];
1455
+ try {
1456
+ const { listTransactions, readTransaction, replayTransaction } = await import('./agents/x402/transaction-store.js');
1457
+ const MILE = await import('./agents/x402/milestone-settlement.js');
1458
+ if (!id) {
1459
+ const txs = await listTransactions();
1460
+ if (!txs.length) {
1461
+ appendLine(`${C_DIM}还没有交易记录 (~/.bolloon/transactions/)${RESET}`);
1462
+ return;
1463
+ }
1464
+ appendLine(`${C_DIM}最近 ${txs.length} 笔交易 (生命周期 + 结算事实 两层):${RESET}`);
1465
+ for (const t of txs.slice(-12)) {
1466
+ const agg = t.milestones?.length ? MILE.aggregateMilestones(t.milestones) : null;
1467
+ appendLine(` ${C_DIM}${t.transactionId} ${RESET}${String(t.status).padEnd(18)} 结算=${String(t.settlementFact || '?').padEnd(18)} ${t.amount || '?'} ${t.currency || ''}${agg ? ` · 里程碑 ${agg.verified}/${agg.total}` : ''}${t.dispute ? ' · ⚠争议' : ''}`);
1468
+ }
1469
+ appendLine(`${C_DIM}/tx <transactionId> 看完整审计 (含证据链回放)${RESET}`);
1470
+ return;
1471
+ }
1472
+ const rec = await readTransaction(id);
1473
+ if (!rec) {
1474
+ appendLine(`${C_ERROR}没有这笔交易: ${id}${RESET}`);
1475
+ return;
1476
+ }
1477
+ appendLine(` ${C_DIM}交易 ${rec.transactionId} · ${rec.status} · 结算 ${rec.settlementFact} · 链上=${rec.chainSettled === true} txHash=${rec.txHash || '(无)'}${RESET}`);
1478
+ appendLine(` ${C_DIM}资源 ${rec.itemId} · ${rec.amount} ${rec.currency} · ${rec.network} · 付款方式 ${rec.paymentMode}${RESET}`);
1479
+ if (rec.milestones?.length) {
1480
+ const agg = MILE.aggregateMilestones(rec.milestones);
1481
+ appendLine(` ${C_DIM}里程碑 ${agg.verified}/${agg.total} 完成 → ${agg.settlementFact}: ${agg.reason}${RESET}`);
1482
+ for (const m of rec.milestones)
1483
+ appendLine(` ${C_DIM}${m.milestoneId} ${m.title} ${m.amount} [付:${m.paymentStatus} 交:${m.deliveryStatus} 验:${m.verificationStatus}]${RESET}`);
1484
+ }
1485
+ if (rec.dispute)
1486
+ appendLine(` ${C_ERROR}争议: ${rec.dispute.reason} (缺证据 ${rec.dispute.missingEvidence.length} 项, ${rec.dispute.resolution ? `已收尾: ${rec.dispute.resolution.decision}` : '未收尾'})${RESET}`);
1487
+ if (rec.responsibility)
1488
+ appendLine(` ${C_DIM}责任候选: ${rec.responsibility.type} — ${rec.responsibility.reason}${RESET}`);
1489
+ const elig = MILE.milestoneGoalEligibility(rec, { executionOk: rec.execution?.ok === true, goalCriteriaHit: rec.goalCriteriaMet === true });
1490
+ appendLine(` ${C_DIM}Goal 成功证据资格: ${elig.eligible ? '✓' : '✗'} ${elig.reason}${RESET}`);
1491
+ const lines = await replayTransaction(id);
1492
+ appendLine(` ${C_DIM}证据链 (${lines.length} 条):${RESET}`);
1493
+ for (const l of lines)
1494
+ appendLine(` ${C_DIM}${l}${RESET}`);
1495
+ }
1496
+ catch (e) {
1497
+ appendLine(`${C_ERROR}/tx 失败: ${String(e?.message || e).slice(0, 200)}${RESET}`);
1498
+ }
1499
+ return;
1500
+ }
1501
+ // 2026-09-18: /trace [runId] [--json] —— 智能体工具执行轨迹 (真跑过什么工具/结果/耗时), 可复制交换
1502
+ if (cmd === '/trace' || cmd.startsWith('/trace ')) {
1503
+ const rest = trimmed.slice('/trace'.length).trim();
1504
+ try {
1505
+ const { listRuns, readRun } = await import('./agents/run-store.js');
1506
+ const { runToTraceText, runToTraceJson, summarizeTrace } = await import('./agents/trace-export.js');
1507
+ const wantJson = rest.includes('--json');
1508
+ const runId = rest.split(/\s+/).find((a) => a && !a.startsWith('--'));
1509
+ if (!runId) {
1510
+ const runs = await listRuns({ limit: 12 });
1511
+ if (!runs.length) {
1512
+ appendLine(`${C_DIM}还没有运行记录 (每次智能体运行都落盘 ~/.bolloon/runs/)${RESET}`);
1513
+ return;
1514
+ }
1515
+ appendLine(`${C_DIM}最近 ${runs.length} 次运行的工具执行轨迹:${RESET}`);
1516
+ for (const r of runs)
1517
+ appendLine(` ${C_DIM}${r.runId} [${r.status}] ${RESET}${summarizeTrace(r)}`);
1518
+ appendLine(`${C_DIM}/trace <runId> 看完整轨迹 (文本可复制交换) · /trace <runId> --json 机器可读${RESET}`);
1519
+ return;
1520
+ }
1521
+ const run = await readRun(runId);
1522
+ if (!run) {
1523
+ appendLine(`${C_ERROR}没有这个运行: ${runId}${RESET}`);
1524
+ return;
1525
+ }
1526
+ if (wantJson) {
1527
+ appendLine(JSON.stringify(runToTraceJson(run), null, 2));
1528
+ return;
1529
+ }
1530
+ for (const line of runToTraceText(run).split('\n'))
1531
+ appendLine(` ${C_DIM}${line}${RESET}`);
1532
+ }
1533
+ catch (e) {
1534
+ appendLine(`${C_ERROR}/trace 失败: ${String(e?.message || e).slice(0, 200)}${RESET}`);
1535
+ }
1536
+ return;
1537
+ }
1538
+ // 2026-09-18: /p2p [--json] —— 本机 P2P 连接信息 (peerId + 可拨入地址), 抄进名片/小工具递给对方
1539
+ if (cmd === '/p2p' || cmd.startsWith('/p2p ')) {
1540
+ try {
1541
+ const { getLocalP2pInfo, formatP2pInfoText, formatP2pInfoJson } = await import('./agents/p2p-info.js');
1542
+ const info = await getLocalP2pInfo();
1543
+ if (trimmed.includes('--json')) {
1544
+ appendLine(formatP2pInfoJson(info));
1545
+ return;
1546
+ }
1547
+ for (const line of formatP2pInfoText(info).split('\n'))
1548
+ appendLine(` ${C_DIM}${line}${RESET}`);
1549
+ }
1550
+ catch (e) {
1551
+ appendLine(`${C_ERROR}/p2p 失败: ${String(e?.message || e).slice(0, 200)}${RESET}`);
1552
+ }
1553
+ return;
1554
+ }
1452
1555
  // 2026-09-16 (2-F): /criteria <goalId> [confirm|propose <text...>] —— 判据:看/确认/改/让 agent 提候选
1453
1556
  if (cmd === '/criteria' || cmd.startsWith('/criteria ')) {
1454
1557
  const rest = trimmed.slice('/criteria'.length).trim();
@@ -107,6 +107,7 @@ export function registerX402InfoRoutes(app) {
107
107
  const pay = await checkAndSettlePayment({
108
108
  paymentHeader: String(paymentHeader),
109
109
  requirements,
110
+ expectedItemId: stored.item.id, // 凭据必须绑定这条资源 (防跨资源复用)
110
111
  });
111
112
  if (!pay.ok) {
112
113
  return res.status(402).json({ ...requirements, error: pay.error });
@@ -2949,6 +2949,88 @@ ${goalDesc}
2949
2949
  }
2950
2950
  });
2951
2951
  // 2026-09-16: 运行记录 (持久化 harness) — 当前 + 历史 agent 运行。跨重载可读。
2952
+ // 2026-09-18: 智能体工具执行轨迹 + 本机 P2P 信息 (与小工具/名片交换用, 结构化)
2953
+ // 2026-09-18 (Phase 4): 交易审计 — 里程碑 / 争议 / 责任 / 事件链 一眼可查
2954
+ app.get('/api/x402/transactions', async (_req, res) => {
2955
+ try {
2956
+ const { listTransactions } = await import('../agents/x402/transaction-store.js');
2957
+ const { aggregateMilestones } = await import('../agents/x402/milestone-settlement.js');
2958
+ const txs = await listTransactions();
2959
+ res.json({
2960
+ count: txs.length,
2961
+ transactions: txs.map((t) => ({
2962
+ transactionId: t.transactionId, status: t.status, settlementFact: t.settlementFact,
2963
+ itemId: t.itemId, amount: t.amount, currency: t.currency, network: t.network,
2964
+ paymentMode: t.paymentMode, chainSettled: t.chainSettled === true, txHash: t.txHash,
2965
+ milestones: t.milestones?.length ? aggregateMilestones(t.milestones) : null,
2966
+ disputed: !!t.dispute, disputeResolved: t.dispute?.resolution?.decision || null,
2967
+ responsibility: t.responsibility?.type || null,
2968
+ partial: t.settlementFact === 'partially_settled',
2969
+ })),
2970
+ });
2971
+ }
2972
+ catch (err) {
2973
+ res.status(500).json({ error: String(err?.message || err).slice(0, 200) });
2974
+ }
2975
+ });
2976
+ app.get('/api/x402/transactions/:id', async (req, res) => {
2977
+ try {
2978
+ const { readTransaction, replayTransaction } = await import('../agents/x402/transaction-store.js');
2979
+ const { aggregateMilestones, milestoneGoalEligibility } = await import('../agents/x402/milestone-settlement.js');
2980
+ const rec = await readTransaction(req.params.id);
2981
+ if (!rec)
2982
+ return res.status(404).json({ error: '交易不存在' });
2983
+ res.json({
2984
+ transaction: rec,
2985
+ milestones: rec.milestones?.length ? { list: rec.milestones, aggregate: aggregateMilestones(rec.milestones) } : null,
2986
+ dispute: rec.dispute || null,
2987
+ responsibility: rec.responsibility || null,
2988
+ goalEligibility: milestoneGoalEligibility(rec, { executionOk: rec.execution?.ok === true, goalCriteriaHit: rec.goalCriteriaMet === true }),
2989
+ replay: await replayTransaction(req.params.id),
2990
+ });
2991
+ }
2992
+ catch (err) {
2993
+ res.status(500).json({ error: String(err?.message || err).slice(0, 200) });
2994
+ }
2995
+ });
2996
+ app.get('/api/trace', async (req, res) => {
2997
+ try {
2998
+ const { listRuns } = await import('../agents/run-store.js');
2999
+ const { runToTraceJson } = await import('../agents/trace-export.js');
3000
+ const runs = await listRuns({ limit: Number(req.query.limit) || 20 });
3001
+ res.json({ runs: runs.map((r) => runToTraceJson(r)) });
3002
+ }
3003
+ catch (err) {
3004
+ res.status(500).json({ error: String(err?.message || err).slice(0, 200) });
3005
+ }
3006
+ });
3007
+ app.get('/api/trace/:runId', async (req, res) => {
3008
+ try {
3009
+ const { readRun } = await import('../agents/run-store.js');
3010
+ const { runToTraceText, runToTraceJson } = await import('../agents/trace-export.js');
3011
+ const run = await readRun(req.params.runId);
3012
+ if (!run)
3013
+ return res.status(404).json({ error: 'run 不存在' });
3014
+ if (String(req.query.format || 'json') === 'text') {
3015
+ res.type('text/plain; charset=utf-8').send(runToTraceText(run));
3016
+ return;
3017
+ }
3018
+ res.json(runToTraceJson(run));
3019
+ }
3020
+ catch (err) {
3021
+ res.status(500).json({ error: String(err?.message || err).slice(0, 200) });
3022
+ }
3023
+ });
3024
+ app.get('/api/p2p/info', async (_req, res) => {
3025
+ try {
3026
+ const { getLocalP2pInfo, formatP2pInfoJson } = await import('../agents/p2p-info.js');
3027
+ const info = await getLocalP2pInfo();
3028
+ res.type('application/json').send(formatP2pInfoJson(info));
3029
+ }
3030
+ catch (err) {
3031
+ res.status(500).json({ error: String(err?.message || err).slice(0, 200) });
3032
+ }
3033
+ });
2952
3034
  // 2026-09-16 (2-F/2-H): 判据 (criteria) + 长期执行面板 API —— CLI/Web 读同一份 Goal 事实
2953
3035
  app.get('/api/goals/:id/criteria', async (req, res) => {
2954
3036
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bolloon/bolloon-agent",
3
- "version": "0.4.26",
3
+ "version": "0.4.28",
4
4
  "type": "module",
5
5
  "description": "P2P AI Document Agent - 全局安装后执行 `bolloon` 启动产品",
6
6
  "main": "dist/cli-entry.js",