@bolloon/bolloon-agent 0.4.5 → 0.4.7

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.
Files changed (208) hide show
  1. package/README.md +135 -135
  2. package/bin/bolloon-cli.cjs +183 -183
  3. package/bin/bolloon.cjs +0 -0
  4. package/bin/bolloon.js +157 -0
  5. package/dist/agents/constraint-layer.js +19 -19
  6. package/dist/agents/judgment-protocol.js +14 -14
  7. package/dist/agents/pi-sdk-tools.js +128 -46
  8. package/dist/agents/pi-sdk-types.js +12 -12
  9. package/dist/agents/pi-sdk.js +103 -90
  10. package/dist/agents/process-runner.js +105 -0
  11. package/dist/agents/skill-organizer.js +19 -19
  12. package/dist/agents/skill-writer.js +15 -3
  13. package/dist/agents/workflow-pivot-loop.js +13 -3
  14. package/dist/agents/write-staging.js +85 -0
  15. package/dist/bollharness/src/scripts/deploy-guard.js +8 -8
  16. package/dist/bollharness-integration/context-router-judgment.js +4 -4
  17. package/dist/bollharness-integration/context-router.js +292 -292
  18. package/dist/bollharness-integration/gate-state-machine.js +13 -13
  19. package/dist/bollharness-integration/llm-judgment-engine.js +134 -134
  20. package/dist/bollharness-integration/skill-adapter.js +21 -21
  21. package/dist/bootstrap/chat-archiver.js +14 -14
  22. package/dist/bootstrap/context-os.js +24 -24
  23. package/dist/bootstrap/memory-compressor.js +14 -14
  24. package/dist/cli/ink-app.js +18 -0
  25. package/dist/cli-entry.js +36 -36
  26. package/dist/context-compaction/auto-compact.js +7 -7
  27. package/dist/electron/first-run.js +54 -54
  28. package/dist/electron-build/electron/first-run.js +54 -54
  29. package/dist/electron-preload.js.map +1 -1
  30. package/dist/electron.js.map +1 -1
  31. package/dist/index.js +230 -137
  32. package/dist/llm/llm-judgment-client.js +102 -102
  33. package/dist/llm/pi-ai.js +48 -48
  34. package/dist/llm/system-prompt/layers/channel/human-async.md +41 -41
  35. package/dist/llm/system-prompt/layers/channel/local.md +14 -14
  36. package/dist/llm/system-prompt/layers/channel/p2p-agent.md +18 -18
  37. package/dist/llm/system-prompt/layers/channel/p2p-peer-sync.md +51 -51
  38. package/dist/llm/system-prompt/layers/channel/p2p-proactive.md +43 -43
  39. package/dist/llm/system-prompt/layers/channel/p2p-visitor.md +19 -19
  40. package/dist/llm/system-prompt/layers/channel/session-handoff.md +61 -61
  41. package/dist/llm/system-prompt/layers/core/artifacts_storage.md +89 -89
  42. package/dist/llm/system-prompt/layers/core/evenhandedness.md +21 -21
  43. package/dist/llm/system-prompt/layers/core/external-engagement.md +73 -73
  44. package/dist/llm/system-prompt/layers/core/hibs_reminders.md +15 -15
  45. package/dist/llm/system-prompt/layers/core/identity.md +48 -48
  46. package/dist/llm/system-prompt/layers/core/knowledge.md +17 -17
  47. package/dist/llm/system-prompt/layers/core/memory_system.md +12 -12
  48. package/dist/llm/system-prompt/layers/core/network_filesystem.md +28 -28
  49. package/dist/llm/system-prompt/layers/core/refusal.md +37 -37
  50. package/dist/llm/system-prompt/layers/core/tone.md +31 -31
  51. package/dist/llm/system-prompt/layers/core/tools.thin.md +13 -13
  52. package/dist/llm/system-prompt/layers/core/wellbeing.md +41 -41
  53. package/dist/llm/system-prompt/layers/role/architect.md +20 -20
  54. package/dist/llm/system-prompt/layers/role/expert.md +19 -19
  55. package/dist/llm/system-prompt/layers/role/implementer.md +15 -15
  56. package/dist/llm/system-prompt/layers/role/security.md +15 -15
  57. package/dist/llm/system-prompt/layers/tool/artifacts.md +72 -72
  58. package/dist/llm/system-prompt/layers/tool/bash.md +25 -25
  59. package/dist/llm/system-prompt/layers/tool/goal_handoff.md +77 -77
  60. package/dist/llm/system-prompt/layers/tool/hibs_api.md +171 -171
  61. package/dist/llm/system-prompt/layers/tool/image_search.md +70 -70
  62. package/dist/llm/system-prompt/layers/tool/manifest.md +89 -89
  63. package/dist/llm/system-prompt/layers/tool/mcp_apps.md +53 -53
  64. package/dist/llm/system-prompt/layers/tool/p2p_request.md +61 -61
  65. package/dist/llm/system-prompt/layers/tool/web_search.md +83 -83
  66. package/dist/llm/tool-manifest/ask_user_input.js +1 -1
  67. package/dist/llm/tool-manifest/bash.js +3 -3
  68. package/dist/llm/tool-manifest/create_file.js +4 -4
  69. package/dist/llm/tool-manifest/fetch_sports_data.js +3 -3
  70. package/dist/llm/tool-manifest/image_search.js +2 -2
  71. package/dist/llm/tool-manifest/index.js +0 -59
  72. package/dist/llm/tool-manifest/mcp.js +2 -2
  73. package/dist/llm/tool-manifest/message_compose.js +3 -3
  74. package/dist/llm/tool-manifest/places.js +2 -2
  75. package/dist/llm/tool-manifest/present_files.js +1 -1
  76. package/dist/llm/tool-manifest/recipe.js +2 -2
  77. package/dist/llm/tool-manifest/str_replace.js +5 -5
  78. package/dist/llm/tool-manifest/view.js +3 -3
  79. package/dist/llm/tool-manifest/weather.js +4 -4
  80. package/dist/llm/tool-manifest/web.js +4 -4
  81. package/dist/orbitdb/agent-tools.js +139 -0
  82. package/dist/orbitdb/kanban-store.js +259 -0
  83. package/dist/orbitdb/task-store.js +112 -0
  84. package/dist/pi-ecosystem-colony/index.js +365 -0
  85. package/dist/pi-ecosystem-judgment/causal-judge.js +51 -51
  86. package/dist/pi-ecosystem-judgment/distill-prompt.js +42 -42
  87. package/dist/pi-ecosystem-judgment/distillation.js +14 -14
  88. package/dist/pi-ecosystem-judgment/monitor-gate.js +28 -28
  89. package/dist/pi-ecosystem-judgment/value-injection.js +102 -102
  90. package/dist/security/context-router-tool.js +15 -15
  91. package/dist/security/tool-gate.js +2 -0
  92. package/dist/social/ant-colony/AdaptiveHeartbeat.js +101 -0
  93. package/dist/social/ant-colony/PheromoneEngine.js +227 -0
  94. package/dist/social/ant-colony/index.js +6 -0
  95. package/dist/social/ant-colony/types.js +24 -0
  96. package/dist/storage/trajectory.js +101 -0
  97. package/dist/test/ai-judgment-test.js +80 -0
  98. package/dist/test/bollharness-integration.test.js +318 -0
  99. package/dist/test/channel-agent-multi-dialogue.js +205 -0
  100. package/dist/test/channel-heartbeat-agent-test.js +201 -0
  101. package/dist/test/constraint-layer.test.js +164 -0
  102. package/dist/test/diap-identity-test.js +172 -0
  103. package/dist/test/diap-quick-test.js +62 -0
  104. package/dist/test/global-shared-context.test.js +315 -0
  105. package/dist/test/harness-judgment-injection.test.js +246 -0
  106. package/dist/test/harness-workflow-integrator-test.js +228 -0
  107. package/dist/test/human-value-store.test.js +243 -0
  108. package/dist/test/hybrid-integration-test.js +118 -0
  109. package/dist/test/hybrid-messenger-verify.js +55 -0
  110. package/dist/test/iroh-bistream-debug.js +38 -0
  111. package/dist/test/iroh-communication.test.js +66 -0
  112. package/dist/test/iroh-debug-test.js +57 -0
  113. package/dist/test/iroh-diap-test.js +71 -0
  114. package/dist/test/iroh-direct-connect.js +55 -0
  115. package/dist/test/iroh-e2e-fixed.js +89 -0
  116. package/dist/test/iroh-e2e-same-process.js +63 -0
  117. package/dist/test/iroh-e2e.js +66 -0
  118. package/dist/test/iroh-final-e2e.js +72 -0
  119. package/dist/test/iroh-relay-test.js +37 -0
  120. package/dist/test/iroh-simple-test.js +41 -0
  121. package/dist/test/iroh-transport-verify.js +54 -0
  122. package/dist/test/iroh-transport.test.js +37 -0
  123. package/dist/test/iroh-two-nodes.js +70 -0
  124. package/dist/test/iroh-verify.js +44 -0
  125. package/dist/test/judgment-decision.test.js +219 -0
  126. package/dist/test/llm-judgment-integration.test.js +220 -0
  127. package/dist/test/p2p-agent-complex-dialogue.js +385 -0
  128. package/dist/test/p2p-agent-dialogue.js +341 -0
  129. package/dist/test/p2p-agent-full-bidirectional.js +510 -0
  130. package/dist/test/p2p-agent-harness-flow.js +437 -0
  131. package/dist/test/p2p-agent-harness-single.js +143 -0
  132. package/dist/test/p2p-ai-dialogue-test.js +318 -0
  133. package/dist/test/p2p-cid-connect-test.js +195 -0
  134. package/dist/test/p2p-connect-receiver.js +69 -0
  135. package/dist/test/p2p-doc-transfer.js +110 -0
  136. package/dist/test/p2p-identity-page-test.js +77 -0
  137. package/dist/test/p2p-iroh-test.js +171 -0
  138. package/dist/test/p2p-minimal-test.js +241 -0
  139. package/dist/test/p2p-node-1.js +148 -0
  140. package/dist/test/p2p-node-2.js +148 -0
  141. package/dist/test/p2p-server.js +281 -0
  142. package/dist/test/p2p-two-nodes-test.js +438 -0
  143. package/dist/test/pi-sdk.test.js +44 -0
  144. package/dist/test/set-persona.js +40 -0
  145. package/dist/test/simple.test.js +9 -0
  146. package/dist/test/storage-integration.test.js +150 -0
  147. package/dist/test/subagent-manager.test.js +276 -0
  148. package/dist/test/test-gate-flow.test.js +81 -0
  149. package/dist/test/workflow-engine.test.js +87 -0
  150. package/dist/test/workflow-pivot-loop.test.js +246 -0
  151. package/dist/web/api-config.html +779 -779
  152. package/dist/web/client-hearth.js +10 -10
  153. package/dist/web/components/wallet-viem.mjs +118 -118
  154. package/dist/web/index.html +472 -472
  155. package/dist/web/manifest.json +20 -20
  156. package/dist/web/server-storage.js +29 -0
  157. package/dist/web/server.js +30 -14
  158. package/dist/web/style.css +4946 -4946
  159. package/package.json +195 -195
  160. package/scripts/build-cli.js +215 -215
  161. package/scripts/build-web.ts +130 -130
  162. package/scripts/postinstall.js +152 -152
  163. package/bin/bolloon-daemon.sh +0 -207
  164. package/dist/constraint-runtime/reference_data/archive_surface_snapshot.json +0 -63
  165. package/dist/constraint-runtime/reference_data/commands_snapshot.json +0 -1037
  166. package/dist/constraint-runtime/reference_data/subsystems/assistant.json +0 -8
  167. package/dist/constraint-runtime/reference_data/subsystems/bootstrap.json +0 -8
  168. package/dist/constraint-runtime/reference_data/subsystems/bridge.json +0 -32
  169. package/dist/constraint-runtime/reference_data/subsystems/buddy.json +0 -13
  170. package/dist/constraint-runtime/reference_data/subsystems/cli.json +0 -26
  171. package/dist/constraint-runtime/reference_data/subsystems/components.json +0 -32
  172. package/dist/constraint-runtime/reference_data/subsystems/constants.json +0 -28
  173. package/dist/constraint-runtime/reference_data/subsystems/coordinator.json +0 -8
  174. package/dist/constraint-runtime/reference_data/subsystems/entrypoints.json +0 -15
  175. package/dist/constraint-runtime/reference_data/subsystems/hooks.json +0 -32
  176. package/dist/constraint-runtime/reference_data/subsystems/keybindings.json +0 -21
  177. package/dist/constraint-runtime/reference_data/subsystems/memdir.json +0 -15
  178. package/dist/constraint-runtime/reference_data/subsystems/migrations.json +0 -18
  179. package/dist/constraint-runtime/reference_data/subsystems/moreright.json +0 -8
  180. package/dist/constraint-runtime/reference_data/subsystems/native_ts.json +0 -11
  181. package/dist/constraint-runtime/reference_data/subsystems/outputStyles.json +0 -8
  182. package/dist/constraint-runtime/reference_data/subsystems/plugins.json +0 -9
  183. package/dist/constraint-runtime/reference_data/subsystems/remote.json +0 -11
  184. package/dist/constraint-runtime/reference_data/subsystems/schemas.json +0 -8
  185. package/dist/constraint-runtime/reference_data/subsystems/screens.json +0 -10
  186. package/dist/constraint-runtime/reference_data/subsystems/server.json +0 -10
  187. package/dist/constraint-runtime/reference_data/subsystems/services.json +0 -32
  188. package/dist/constraint-runtime/reference_data/subsystems/skills.json +0 -27
  189. package/dist/constraint-runtime/reference_data/subsystems/state.json +0 -13
  190. package/dist/constraint-runtime/reference_data/subsystems/types.json +0 -18
  191. package/dist/constraint-runtime/reference_data/subsystems/upstreamproxy.json +0 -9
  192. package/dist/constraint-runtime/reference_data/subsystems/utils.json +0 -32
  193. package/dist/constraint-runtime/reference_data/subsystems/vim.json +0 -12
  194. package/dist/constraint-runtime/reference_data/subsystems/voice.json +0 -8
  195. package/dist/constraint-runtime/reference_data/tools_snapshot.json +0 -922
  196. package/dist/electron/config.js.map +0 -1
  197. package/dist/electron/dialogs.js.map +0 -1
  198. package/dist/electron/first-run.js.map +0 -1
  199. package/dist/electron/ipc.js.map +0 -1
  200. package/dist/electron/logger.js.map +0 -1
  201. package/dist/electron/main.js.map +0 -1
  202. package/dist/electron/menu.js.map +0 -1
  203. package/dist/electron/paths.js.map +0 -1
  204. package/dist/electron/server.js.map +0 -1
  205. package/dist/electron/tray.js.map +0 -1
  206. package/dist/electron/window.js.map +0 -1
  207. package/dist/utils/auto-update.js.map +0 -1
  208. package/dist/web/client.js.map +0 -7
package/dist/index.js CHANGED
@@ -423,6 +423,9 @@ let cliStartTime = 0;
423
423
  let cliModelName = '…';
424
424
  let cliAgentName = '…';
425
425
  let cliActiveChannelId = null;
426
+ // 2026-08-12: 当前 active channel 的 agentId (如 agent-alice). memory 落盘按 agentId 存,
427
+ // /memory /resume /did 读路径必须用 agentId 而非 display name (cliAgentName), 否则路径不一致读不到.
428
+ let cliAgentId = null;
426
429
  // 2026-08-10: CLI 自动整理心跳 (与社交心跳并列, 独立于 server) — 退出时 stop
427
430
  let cliOrganizeHeartbeat = null;
428
431
  function fmtDuration(ms) {
@@ -435,6 +438,13 @@ function fmtDuration(ms) {
435
438
  const h = Math.floor(m / 60);
436
439
  return `${h}h ${m % 60}m`;
437
440
  }
441
+ /** 2026-08-12: 返回当前 agentId (memory 路径用). 优先 cliAgentId, fallback cliAgentName. */
442
+ function getCliAgentId() {
443
+ if (cliAgentId)
444
+ return cliAgentId;
445
+ // 无显式 agentId 时退到 cliAgentName (harness 默认 'agent', 其余用名字)
446
+ return cliAgentName === 'bolloon' ? 'agent' : (cliAgentName || 'agent');
447
+ }
438
448
  /** 2026-08-06: 从 ContextManager 读上下文用量 (CLI 状态栏数据源, 失败退化 0/1M) */
439
449
  // 2026-08-07: 不能用 require 加载 ESM 模块 (ERR_REQUIRE_ESM) → startCLI 里 await import 一次缓存引用,
440
450
  // 同步函数 getCliCtxUsage/getStatus 复用 — 之前裸 require 与 _require 都抛错被 catch → 状态栏恒 0/1M
@@ -563,6 +573,9 @@ async function startCLI(comm) {
563
573
  if (active) {
564
574
  cliAgentName = active.name;
565
575
  cliActiveChannelId = active.channelId ?? null;
576
+ // 2026-08-12: 同步 agentId (memory 路径一致) — 从 rawChannels 按 channelId 取 agentId
577
+ const raw = active.channelId ? store.rawChannels.find((c) => c.id === active.channelId) : undefined;
578
+ cliAgentId = raw?.agentId || null;
566
579
  }
567
580
  }
568
581
  catch {
@@ -692,23 +705,36 @@ async function processInput(input, comm) {
692
705
  const runEndOkSteps = [];
693
706
  // each iteration
694
707
  let lastToolEvent = null;
695
- // !command — 直接执行终端命令
708
+ // !command — 直接执行终端命令. 2026-08-12 (Task4): 支持多命令 (&& / ;) 逐段顺序执行 + 加载显示.
696
709
  if (trimmed.startsWith('!')) {
697
710
  const cmd = trimmed.slice(1).trim();
698
711
  if (!cmd) {
699
- appendLine(`${C_DIM}!<命令> 执行终端命令, 如 !ls -la${RESET}`);
712
+ appendLine(`${C_DIM}!<命令> 执行终端命令 (支持 && 串联多命令), 如 !ls -la${RESET}`);
700
713
  return;
701
714
  }
702
- appendLine(`${C_DIM}── $ ${cmd}${RESET}`);
715
+ const { execSync } = await import('child_process');
716
+ // 拆成多段: && 逻辑与 (前失败则停) / ; 无条件顺序. 保留每段顺序执行, 显示加载态.
717
+ const segments = cmd.split(/\s*;\s*/).filter(Boolean);
703
718
  try {
704
- const { execSync } = await import('child_process');
705
- const out = execSync(cmd, { timeout: 30000, encoding: 'utf-8', cwd: process.cwd() });
706
- appendLine(`${C_DIM}${out || '(无输出)'}${RESET}`);
719
+ for (const seg of segments) {
720
+ const segCmds = seg.split(/\s*&&\s*/).filter(Boolean);
721
+ for (const c of segCmds) {
722
+ appendLine(`${C_DIM}── $ ${c}${RESET}`);
723
+ try {
724
+ const out = execSync(c, { timeout: 30000, encoding: 'utf-8', cwd: process.cwd() });
725
+ appendLine(`${C_DIM}${out || '(无输出)'}${RESET}`);
726
+ }
727
+ catch (e) {
728
+ appendLine(`${C_ERROR}${e.stderr || e.message}${RESET}`);
729
+ // && 逻辑与: 某段失败则中止后续 && 段
730
+ break;
731
+ }
732
+ }
733
+ }
707
734
  }
708
- catch (e) {
709
- appendLine(`${C_ERROR}${e.stderr || e.message}${RESET}`);
735
+ finally {
736
+ appendLine(`${C_DIM}──${RESET}`);
710
737
  }
711
- appendLine(`${C_DIM}──${RESET}`);
712
738
  return;
713
739
  }
714
740
  // /channel — 切换当前智能体 (agent channel), 参数 name/id/number 自动解析
@@ -743,6 +769,7 @@ async function processInput(input, comm) {
743
769
  await store.setActive(r.channel.id);
744
770
  cliAgentName = r.identity.name;
745
771
  cliActiveChannelId = r.channel.id;
772
+ cliAgentId = r.channel.agentId || null; // 2026-08-12: memory 路径一致
746
773
  // 2026-08-09: 切 channel 必须重建 agent session — 否则身份/记忆停留在旧 channel (bug 修复)
747
774
  invalidateAgent();
748
775
  // 立即重建 (提前建好, 避免下次输入才卡顿; 失败不阻塞切换)
@@ -809,6 +836,7 @@ async function processInput(input, comm) {
809
836
  await store.setActive(id);
810
837
  cliAgentName = name.trim();
811
838
  cliActiveChannelId = id;
839
+ cliAgentId = agentId; // 2026-08-12: memory 路径一致
812
840
  // 2026-08-09: 新建 agent 后立即重建 session — 否则新 agent 身份不加载 (bug 修复)
813
841
  invalidateAgent();
814
842
  try {
@@ -1107,7 +1135,7 @@ async function processInput(input, comm) {
1107
1135
  const { getMemoryDir } = await import('./bootstrap/memory-compressor.js');
1108
1136
  const { readdir, readFile } = await import('fs/promises');
1109
1137
  const { join } = await import('path');
1110
- const dir = getMemoryDir(cliAgentName === 'bolloon' ? 'agent' : cliAgentName);
1138
+ const dir = getMemoryDir(getCliAgentId());
1111
1139
  const files = (await readdir(join(dir, 'sessions')).catch(() => [])).filter((f) => f.endsWith('.summary.md'));
1112
1140
  appendLine(`${C_ACCENT}记忆摘要 (${files.length} 个 session):${RESET}`);
1113
1141
  for (const f of files.slice(-5)) {
@@ -1126,10 +1154,10 @@ async function processInput(input, comm) {
1126
1154
  // /resume — 恢复: 最近记忆摘要 + 进行中计划
1127
1155
  if (cmd === '/resume' || cmd.startsWith('/resume ')) {
1128
1156
  try {
1129
- const { getMemoryDir, getSessionSummaryPath } = await import('./bootstrap/memory-compressor.js');
1157
+ const { getMemoryDir } = await import('./bootstrap/memory-compressor.js');
1130
1158
  const { readFile, readdir } = await import('fs/promises');
1131
1159
  const { join } = await import('path');
1132
- const dir = getMemoryDir(cliAgentName === 'bolloon' ? 'agent' : cliAgentName);
1160
+ const dir = getMemoryDir(getCliAgentId());
1133
1161
  const files = (await readdir(join(dir, 'sessions')).catch(() => [])).filter((f) => f.endsWith('.summary.md'));
1134
1162
  appendLine(`${C_ACCENT}↻ 恢复上下文:${RESET}`);
1135
1163
  if (files.length > 0) {
@@ -1286,6 +1314,50 @@ async function processInput(input, comm) {
1286
1314
  catch { /* 静默 */ }
1287
1315
  return;
1288
1316
  }
1317
+ // /skills [名] — 查看正式技能 (2026-08-12 Task5): 无参列全部, 带名看详情. 运行时开始前的技能 view.
1318
+ if (cmd === '/skills' || cmd.startsWith('/skills ')) {
1319
+ try {
1320
+ const { defaultSkillPaths, loadSkillsDir } = await import('./agents/skill-loader.js');
1321
+ const dirs = defaultSkillPaths();
1322
+ const metas = [];
1323
+ for (const d of dirs) {
1324
+ const m = await loadSkillsDir(d);
1325
+ for (const s of m)
1326
+ if (s.status === 'active' && !metas.some(x => x.name === s.name))
1327
+ metas.push(s);
1328
+ }
1329
+ const q = cmd.startsWith('/skills ') ? cmd.slice('/skills '.length).trim().toLowerCase() : '';
1330
+ if (!q) {
1331
+ appendLine(`${C_ACCENT}技能 (${metas.length}):${RESET}`);
1332
+ if (metas.length === 0)
1333
+ appendLine(` ${C_DIM}暂无正式技能 — run-end 经验可沉淀为 skill${RESET}`);
1334
+ for (const s of metas.slice(0, 20)) {
1335
+ const desc = (s.description || '').slice(0, 60);
1336
+ appendLine(` ${C_DIM}·${RESET} ${C_ACCENT}${s.name}${RESET}${desc ? ` ${C_DIM}${desc}${RESET}` : ''}`);
1337
+ }
1338
+ appendLine(`${C_DIM}用法: /skills <名> 查看详情${RESET}`);
1339
+ }
1340
+ else {
1341
+ const hit = metas.find(s => s.name.toLowerCase() === q || s.name.toLowerCase().includes(q));
1342
+ if (!hit) {
1343
+ appendLine(`${C_WARN}未找到技能: '${q}'${RESET}`);
1344
+ return;
1345
+ }
1346
+ appendLine(`${C_ACCENT}═ ${hit.name} ═${RESET}`);
1347
+ if (hit.description)
1348
+ appendLine(` ${C_DIM}描述:${RESET} ${hit.description}`);
1349
+ if (hit.triggers && hit.triggers.length > 0)
1350
+ appendLine(` ${C_DIM}触发:${RESET} ${hit.triggers.join(', ')}`);
1351
+ const body = (hit.body || '').trim().slice(0, 1200);
1352
+ if (body)
1353
+ appendLine(` ${C_DIM}---${RESET}\n${body}`);
1354
+ }
1355
+ }
1356
+ catch (e) {
1357
+ appendLine(`${C_ERROR}/skills 失败: ${String(e?.message || e).slice(0, 120)}${RESET}`);
1358
+ }
1359
+ return;
1360
+ }
1289
1361
  // /mcp — MCP 插件/工具列表
1290
1362
  if (cmd === '/mcp') {
1291
1363
  try {
@@ -1324,7 +1396,7 @@ async function processInput(input, comm) {
1324
1396
  if (cmd === '/did') {
1325
1397
  try {
1326
1398
  const { loadOrCreateAgentIdentity } = await import('./agents/agent-identity.js');
1327
- const identity = loadOrCreateAgentIdentity(cliAgentName === 'bolloon' ? 'default-agent' : cliAgentName);
1399
+ const identity = loadOrCreateAgentIdentity(getCliAgentId());
1328
1400
  appendLine(`${C_ACCENT}DID 身份:${RESET}`);
1329
1401
  appendLine(` ${C_DIM}did:${RESET} ${identity.did}`);
1330
1402
  appendLine(` ${C_DIM}publicKey:${RESET} ${identity.publicKey?.slice(0, 32) || '—'}...`);
@@ -1649,6 +1721,7 @@ async function processInput(input, comm) {
1649
1721
  appendLine(` ${C_ACCENT}/todo${RESET} 查看/勾选循环步骤 ${C_DIM}/todo <planId> <序号>${RESET}`);
1650
1722
  appendLine(` ${C_ACCENT}/tools${RESET} 可用工具列表 (名/参数/简介)`);
1651
1723
  appendLine(` ${C_ACCENT}/skill${RESET} 技能候选 ${C_DIM}skill-writer 沉淀候选${RESET}`);
1724
+ appendLine(` ${C_ACCENT}/skills${RESET} 查看正式技能 ${C_DIM}/skills <名> 看详情 (运行时开始前 view)${RESET}`);
1652
1725
  appendLine(` ${C_ACCENT}/mcp${RESET} MCP 服务器列表`);
1653
1726
  appendLine(` ${C_ACCENT}/agent${RESET} 当前智能体身份`);
1654
1727
  appendLine(` ${C_ACCENT}/did${RESET} DID 身份`);
@@ -1753,15 +1826,8 @@ async function processInput(input, comm) {
1753
1826
  // 真正思考内容在 status 的 Reflection/💡 事件 → 下方框渲染
1754
1827
  }
1755
1828
  else if (e.phase && !e.type) {
1756
- const ph = String(e.phase);
1757
- const detail = e.detail ? ` (${String(e.detail).slice(0, 60)})` : '';
1758
- const phLabel = {
1759
- intent_classified: '意图识别',
1760
- tool_selected: '工具选择',
1761
- reflection: '反思',
1762
- planning: '规划',
1763
- };
1764
- appendLine(`${C_WARN}◈ ${phLabel[ph] || ph}${detail}${RESET}`);
1829
+ // 2026-08-12 (Task4): phase (意图识别/工具选择/规划) 是模型内部规划过程,
1830
+ // 用户偏好"中间过程不显示" 静默丢弃, 不污染终端. (仅 Reflection 框保留, status 分支)
1765
1831
  }
1766
1832
  else if (e.type === 'status' && e.content) {
1767
1833
  const content = String(e.content);
@@ -1771,13 +1837,26 @@ async function processInput(input, comm) {
1771
1837
  if (body.trim())
1772
1838
  appendLine(renderMessageBox({ title: '💡 反思', body, color: C_WARN }));
1773
1839
  }
1774
- else if (!content.includes('🔄 循环') && !content.includes('📋 参数')) {
1840
+ else if (!content.includes('🔄 循环') && !content.includes('📋 参数')
1841
+ && !content.includes('🔍 任务复杂度') && !content.includes('⚙️ 动态配置')
1842
+ && !content.includes('⏹️ pivot loop')
1843
+ // 2026-08-12 (Task4): 循环过渡噪音 — "工具执行完成继续循环"/"继续总结" 是内部推进过程,
1844
+ // 不是给用户看的内容, 一律静默丢弃 (用户抱怨"每次显示触发下一轮循环"的真凶).
1845
+ && !content.includes('继续循环') && !content.includes('继续总结')) {
1775
1846
  appendLine(`${C_DIM}${content}${RESET}`);
1776
1847
  }
1777
1848
  }
1778
1849
  else if (e.type === 'step_start') {
1779
1850
  tuiToolCounter++;
1780
- tuiToolCalls.push({ tool: e.tool || '?', args: e.args, _t: Date.now() });
1851
+ const toolName = e.tool || '?';
1852
+ tuiToolCalls.push({ tool: toolName, args: e.args, _t: Date.now() });
1853
+ // 2026-08-12 (TaskA): 工具命中要干净 — step_start 不 appendLine 到消息流 (避免重复),
1854
+ // 改用 transient 行显示"正在执行"(消息流只在 done 时出现一次完成行).
1855
+ if (toolName !== 'system' && toolName !== 'loop' && toolName !== '?') {
1856
+ const activeNames = tuiToolCalls.map(c => c.tool).filter(t => t !== 'system' && t !== 'loop' && t !== '?');
1857
+ const label = activeNames.length > 1 ? `执行 ${activeNames.length} 个工具: ${activeNames.join(', ')}` : `🔧 ${toolName}`;
1858
+ inkSetTransient(`${C_DIM}${label} 运行中...${RESET}`);
1859
+ }
1781
1860
  }
1782
1861
  else if (e.type === 'step_done' || e.type === 'step_error') {
1783
1862
  const p = tuiToolCalls.shift();
@@ -1795,7 +1874,21 @@ async function processInput(input, comm) {
1795
1874
  runEndOkSteps.push({ status: 'ok', name: t, output: e.output });
1796
1875
  }
1797
1876
  }
1798
- appendLine(renderToolCallListItem(doneItem, tuiToolCalls.length + 1, tuiToolCounter));
1877
+ // 2026-08-12 (TaskA): 每个工具只在消息流出现一次 (done appendLine 完成行).
1878
+ // 不 replaceLastLine (并行/thinking 交错会替换错行); 完成行固定追加.
1879
+ const doneTool = e.tool ?? p?.tool;
1880
+ if (doneTool !== 'system' && doneTool !== 'loop' && doneTool !== '?') {
1881
+ appendLine(renderToolCallListItem(doneItem, tuiToolCalls.length + 1, tuiToolCounter));
1882
+ }
1883
+ // 更新 transient: 还有进行中的工具 → 显示下一个; 否则清空 (交回 thinking 动画)
1884
+ const remaining = tuiToolCalls.filter(c => c.tool !== 'system' && c.tool !== 'loop' && c.tool !== '?');
1885
+ if (remaining.length > 0) {
1886
+ const label = remaining.length > 1 ? `执行 ${remaining.length} 个工具: ${remaining.map(c => c.tool).join(', ')}` : `🔧 ${remaining[0].tool}`;
1887
+ inkSetTransient(`${C_DIM}${label} 运行中...${RESET}`);
1888
+ }
1889
+ else {
1890
+ inkSetTransient(null);
1891
+ }
1799
1892
  }
1800
1893
  }
1801
1894
  });
@@ -1981,10 +2074,10 @@ async function runToolCommand(tool, args, outputJson, comm, model, prompt) {
1981
2074
  const running = irohTransport.isRunning();
1982
2075
  const irohPeers = irohTransport.getPeers();
1983
2076
  const messenger = hybridMessenger ? 'HybridMessenger 就绪' : 'HybridMessenger 未初始化';
1984
- response = `iroh P2P 状态:
1985
- 运行中: ${running ? '是' : '否'}
1986
- Node ID: ${nodeId ? nodeId.substring(0, 32) + '...' : 'N/A'}
1987
- 已知节点: ${irohPeers.length}
2077
+ response = `iroh P2P 状态:
2078
+ 运行中: ${running ? '是' : '否'}
2079
+ Node ID: ${nodeId ? nodeId.substring(0, 32) + '...' : 'N/A'}
2080
+ 已知节点: ${irohPeers.length}
1988
2081
  ${messenger}`;
1989
2082
  break;
1990
2083
  }
@@ -3118,114 +3211,114 @@ function parseArgs() {
3118
3211
  return result;
3119
3212
  }
3120
3213
  function printHelp() {
3121
- console.log(`
3122
- 🤖 Bolloon Agent - AI 可调用文档处理智能体
3123
-
3124
- 用法:
3125
- npx tsx src/index.ts [选项] [参数]
3126
-
3127
- 选项:
3128
- # 文档处理
3129
- --read <file> 读取文档 (txt, md, pdf, docx)
3130
- --summarize <file> [ctx] 总结文档,可选上下文
3131
- --improve <file> <req> 改进文档,req 为改进要求
3132
-
3133
- # P2P 网络
3134
- --peers 列出已连接的对等节点
3135
- --broadcast <msg> 广播消息到所有节点
3136
- --send <peerId> <msg> 向指定节点发送消息
3137
-
3138
- # 智能体
3139
- --identity 显示当前智能体身份
3140
- --logs 显示操作日志
3141
- --search <keyword> 搜索文件
3142
- --tools 显示所有可用工具
3143
-
3144
- # SubAgent 管理
3145
- --agents 列出所有 SubAgent
3146
- --register-agent <name> [cap1] [cap2]... 注册新 SubAgent
3147
- --delegate <任务描述> [能力要求...] 委派任务给最佳 Agent
3148
-
3149
- # 全局共享上下文
3150
- --context 显示全局共享上下文摘要
3151
- --global-agents 显示全局 Agent 注册表
3152
- --add-action <内容> [重要性] 添加用户行动到共享上下文
3153
-
3154
- # Bollharness 治理框架
3155
- --harness-init 初始化 Bollharness 治理框架
3156
- --harness-gate 显示当前 Gate 状态
3157
- --harness-transition [PASS|BLOCK] 执行 Gate 转移
3158
- --harness-skill <name> [action] 执行 Harness Skill
3159
- --harness-classify <描述> 分类变更类型
3160
- --harness-context <file> 获取文件上下文
3161
- --harness-check <file> 执行 Guard 检查
3162
- --harness-archive 归档当前 Session 到 Harness
3163
- --harness-sessions 列出 Session 归档记录
3164
- --harness-session-context [id] 获取 Session 上下文
3165
-
3166
- # 自动更新
3167
- --update-check 检查 npm 包更新
3168
- --update-now [pkg] 更新到最新版本
3169
-
3170
- # 跨机聊天 (commits-as-messages, 共享 GitHub 仓库)
3171
- --chat-init 初始化 .comm/ 目录 (一次性)
3172
- --chat-send "消息正文" 把消息写到 .comm/<role>/, commit + push
3173
- --chat-pull 拉取远端 .comm/ 的新消息并显示
3174
- --chat-list 列出本地所有已同步消息
3175
- --chat-watch [--interval 15s] 后台定时拉取, 有新消息时输出
3176
- --chat-status 一屏查看: role / publicKey / remote / ahead-behind
3177
-
3178
- # 纯 P2P 私聊 (不走 GitHub, 不写 git, 不持久化)
3179
- --chat-p2p-send <peer|publicKey> "消息正文" 通过 P2P 直接发一条
3180
- --chat-p2p-listen 后台监听对方 P2P 私聊消息 (Ctrl-C 退出)
3181
-
3182
- # 跨机 agent 协作 (对方 bolloon --web 起着才能处理, 走 P2P 不经 GitHub)
3183
- --collab <peer|publicKey> "<任务描述>" 派任务给对端 LLM 干活, 等回结果 (90s 超时)
3184
-
3185
- # 外部编码智能体委派
3186
- --engine, -e <id> [--model <m>] <prompt> 委派任务给外部引擎,如 opencode/codex
3187
- --model <name> 指定委派时使用的模型
3188
-
3189
- # 输出控制
3190
- --json, -j 输出 JSON 格式
3191
- --output, -o <file> 结果保存到文件
3192
- --web 启动 Web UI 模式
3193
- --help, -h 显示帮助信息
3194
-
3195
- 示例:
3196
- # 文档处理
3197
- npx tsx src/index.ts --read 想法.md
3198
- npx tsx src/index.ts --summarize docs/想法.md
3199
- npx tsx src/index.ts --improve docs/README.md "让内容更简洁"
3200
- npx tsx src/index.ts --read 想法.md -o summary.txt
3201
-
3202
- # P2P 网络
3203
- npx tsx src/index.ts --peers
3204
- npx tsx src/index.ts --broadcast "Hello everyone"
3205
- npx tsx src/index.ts --send QmABC... "私信内容"
3206
-
3207
- # AI 对话
3208
- npx tsx src/index.ts --prompt "总结 README.md"
3209
- npx tsx src/index.ts -p "分析这个项目" -j
3210
-
3211
- # 交互模式
3212
- npx tsx src/index.ts
3213
-
3214
- # 外部引擎委派
3215
- npx tsx src/index.ts --engine opencode --model opencode/deepseek-v4-flash-free "说你好"
3216
-
3217
- # Web 模式
3218
- npx tsx src/index.ts --web
3219
-
3220
- 环境变量:
3221
- MINIMAX_API_KEY MiniMax API 密钥
3222
- DEEPSEEK_API_KEY DeepSeek API 密钥
3223
- KIMI_API_KEY / MOONSHOT_API_KEY Kimi/Moonshot API 密钥
3224
- GLM_API_KEY / ZHIPU_API_KEY 智谱 GLM API 密钥
3225
- QWEN_API_KEY / DASHSCOPE_API_KEY 通义千问 API 密钥
3226
- OPENAI_API_KEY OpenAI API 密钥(Pi SDK)
3227
- ANTHROPIC_API_KEY Anthropic API 密钥(Pi SDK)
3228
- PORT Web 服务端口(默认 54188)
3214
+ console.log(`
3215
+ 🤖 Bolloon Agent - AI 可调用文档处理智能体
3216
+
3217
+ 用法:
3218
+ npx tsx src/index.ts [选项] [参数]
3219
+
3220
+ 选项:
3221
+ # 文档处理
3222
+ --read <file> 读取文档 (txt, md, pdf, docx)
3223
+ --summarize <file> [ctx] 总结文档,可选上下文
3224
+ --improve <file> <req> 改进文档,req 为改进要求
3225
+
3226
+ # P2P 网络
3227
+ --peers 列出已连接的对等节点
3228
+ --broadcast <msg> 广播消息到所有节点
3229
+ --send <peerId> <msg> 向指定节点发送消息
3230
+
3231
+ # 智能体
3232
+ --identity 显示当前智能体身份
3233
+ --logs 显示操作日志
3234
+ --search <keyword> 搜索文件
3235
+ --tools 显示所有可用工具
3236
+
3237
+ # SubAgent 管理
3238
+ --agents 列出所有 SubAgent
3239
+ --register-agent <name> [cap1] [cap2]... 注册新 SubAgent
3240
+ --delegate <任务描述> [能力要求...] 委派任务给最佳 Agent
3241
+
3242
+ # 全局共享上下文
3243
+ --context 显示全局共享上下文摘要
3244
+ --global-agents 显示全局 Agent 注册表
3245
+ --add-action <内容> [重要性] 添加用户行动到共享上下文
3246
+
3247
+ # Bollharness 治理框架
3248
+ --harness-init 初始化 Bollharness 治理框架
3249
+ --harness-gate 显示当前 Gate 状态
3250
+ --harness-transition [PASS|BLOCK] 执行 Gate 转移
3251
+ --harness-skill <name> [action] 执行 Harness Skill
3252
+ --harness-classify <描述> 分类变更类型
3253
+ --harness-context <file> 获取文件上下文
3254
+ --harness-check <file> 执行 Guard 检查
3255
+ --harness-archive 归档当前 Session 到 Harness
3256
+ --harness-sessions 列出 Session 归档记录
3257
+ --harness-session-context [id] 获取 Session 上下文
3258
+
3259
+ # 自动更新
3260
+ --update-check 检查 npm 包更新
3261
+ --update-now [pkg] 更新到最新版本
3262
+
3263
+ # 跨机聊天 (commits-as-messages, 共享 GitHub 仓库)
3264
+ --chat-init 初始化 .comm/ 目录 (一次性)
3265
+ --chat-send "消息正文" 把消息写到 .comm/<role>/, commit + push
3266
+ --chat-pull 拉取远端 .comm/ 的新消息并显示
3267
+ --chat-list 列出本地所有已同步消息
3268
+ --chat-watch [--interval 15s] 后台定时拉取, 有新消息时输出
3269
+ --chat-status 一屏查看: role / publicKey / remote / ahead-behind
3270
+
3271
+ # 纯 P2P 私聊 (不走 GitHub, 不写 git, 不持久化)
3272
+ --chat-p2p-send <peer|publicKey> "消息正文" 通过 P2P 直接发一条
3273
+ --chat-p2p-listen 后台监听对方 P2P 私聊消息 (Ctrl-C 退出)
3274
+
3275
+ # 跨机 agent 协作 (对方 bolloon --web 起着才能处理, 走 P2P 不经 GitHub)
3276
+ --collab <peer|publicKey> "<任务描述>" 派任务给对端 LLM 干活, 等回结果 (90s 超时)
3277
+
3278
+ # 外部编码智能体委派
3279
+ --engine, -e <id> [--model <m>] <prompt> 委派任务给外部引擎,如 opencode/codex
3280
+ --model <name> 指定委派时使用的模型
3281
+
3282
+ # 输出控制
3283
+ --json, -j 输出 JSON 格式
3284
+ --output, -o <file> 结果保存到文件
3285
+ --web 启动 Web UI 模式
3286
+ --help, -h 显示帮助信息
3287
+
3288
+ 示例:
3289
+ # 文档处理
3290
+ npx tsx src/index.ts --read 想法.md
3291
+ npx tsx src/index.ts --summarize docs/想法.md
3292
+ npx tsx src/index.ts --improve docs/README.md "让内容更简洁"
3293
+ npx tsx src/index.ts --read 想法.md -o summary.txt
3294
+
3295
+ # P2P 网络
3296
+ npx tsx src/index.ts --peers
3297
+ npx tsx src/index.ts --broadcast "Hello everyone"
3298
+ npx tsx src/index.ts --send QmABC... "私信内容"
3299
+
3300
+ # AI 对话
3301
+ npx tsx src/index.ts --prompt "总结 README.md"
3302
+ npx tsx src/index.ts -p "分析这个项目" -j
3303
+
3304
+ # 交互模式
3305
+ npx tsx src/index.ts
3306
+
3307
+ # 外部引擎委派
3308
+ npx tsx src/index.ts --engine opencode --model opencode/deepseek-v4-flash-free "说你好"
3309
+
3310
+ # Web 模式
3311
+ npx tsx src/index.ts --web
3312
+
3313
+ 环境变量:
3314
+ MINIMAX_API_KEY MiniMax API 密钥
3315
+ DEEPSEEK_API_KEY DeepSeek API 密钥
3316
+ KIMI_API_KEY / MOONSHOT_API_KEY Kimi/Moonshot API 密钥
3317
+ GLM_API_KEY / ZHIPU_API_KEY 智谱 GLM API 密钥
3318
+ QWEN_API_KEY / DASHSCOPE_API_KEY 通义千问 API 密钥
3319
+ OPENAI_API_KEY OpenAI API 密钥(Pi SDK)
3320
+ ANTHROPIC_API_KEY Anthropic API 密钥(Pi SDK)
3321
+ PORT Web 服务端口(默认 54188)
3229
3322
  `);
3230
3323
  }
3231
3324
  // ---------------------------------------------------------------------------