@bolloon/bolloon-agent 0.5.4 → 0.6.0

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 (222) hide show
  1. package/dist/agents/agent-identity-store.js +41 -5
  2. package/dist/agents/background-notices.js +124 -0
  3. package/dist/agents/channel-identity.js +54 -0
  4. package/dist/agents/code-write-gate.js +47 -0
  5. package/dist/agents/deny-pipeline.js +11 -2
  6. package/dist/agents/egress-sanitize.js +69 -0
  7. package/dist/agents/error-classifier.js +67 -5
  8. package/dist/agents/experience-review.js +226 -0
  9. package/dist/agents/gateway-group.js +153 -18
  10. package/dist/agents/iteration-budget.js +64 -0
  11. package/dist/agents/lesson-to-skill.js +211 -0
  12. package/dist/agents/managed-services.js +64 -0
  13. package/dist/agents/parse-tool-call.js +43 -32
  14. package/dist/agents/pi-sdk-session-factory.js +36 -3
  15. package/dist/agents/pi-sdk-session-manager.js +72 -3
  16. package/dist/agents/pi-sdk-tools.js +201 -32
  17. package/dist/agents/pi-sdk.js +798 -206
  18. package/dist/agents/plan-store.js +36 -0
  19. package/dist/agents/process-runner.js +64 -2
  20. package/dist/agents/run-context.js +20 -0
  21. package/dist/agents/runner-resolver.js +2 -1
  22. package/dist/agents/shell-guard.js +109 -13
  23. package/dist/agents/skill-health.js +90 -0
  24. package/dist/agents/skill-ledger.js +129 -0
  25. package/dist/agents/tool-loop-guard.js +303 -0
  26. package/dist/agents/tool-path-args.js +53 -0
  27. package/dist/agents/tool-result-gate.js +50 -0
  28. package/dist/agents/tool-subset.js +109 -0
  29. package/dist/agents/tool-telemetry.js +97 -0
  30. package/dist/bootstrap/context-collector.js +37 -7
  31. package/dist/bootstrap/lifecycle-hooks.js +12 -3
  32. package/dist/bootstrap/persona-init.js +174 -0
  33. package/dist/bootstrap/persona-loader.js +5 -1
  34. package/dist/cli/ink-app.js +153 -6
  35. package/dist/cli/input-paste.js +218 -0
  36. package/dist/cli/loading-tui.js +3 -1
  37. package/dist/cli/log-gate.js +7 -0
  38. package/dist/cli/mention-data.js +30 -0
  39. package/dist/cli/reasoning-view.js +103 -0
  40. package/dist/cli/startup-notice.js +25 -0
  41. package/dist/cli/status-segments.js +87 -2
  42. package/dist/cli/trace-line.js +54 -0
  43. package/dist/context-compaction/auto-compact.js +37 -2
  44. package/dist/electron-build/agents/agent-identity-store.js +43 -5
  45. package/dist/electron-build/agents/agent-identity-store.js.map +1 -1
  46. package/dist/electron-build/agents/background-notices.js +138 -0
  47. package/dist/electron-build/agents/background-notices.js.map +1 -0
  48. package/dist/electron-build/agents/channel-identity.js +64 -0
  49. package/dist/electron-build/agents/channel-identity.js.map +1 -0
  50. package/dist/electron-build/agents/code-write-gate.js +55 -0
  51. package/dist/electron-build/agents/code-write-gate.js.map +1 -0
  52. package/dist/electron-build/agents/deny-pipeline.js +11 -2
  53. package/dist/electron-build/agents/deny-pipeline.js.map +1 -1
  54. package/dist/electron-build/agents/egress-sanitize.js +76 -0
  55. package/dist/electron-build/agents/egress-sanitize.js.map +1 -0
  56. package/dist/electron-build/agents/error-classifier.js +68 -5
  57. package/dist/electron-build/agents/error-classifier.js.map +1 -1
  58. package/dist/electron-build/agents/experience-review.js +243 -0
  59. package/dist/electron-build/agents/experience-review.js.map +1 -0
  60. package/dist/electron-build/agents/iteration-budget.js +73 -0
  61. package/dist/electron-build/agents/iteration-budget.js.map +1 -0
  62. package/dist/electron-build/agents/lesson-to-skill.js +228 -0
  63. package/dist/electron-build/agents/lesson-to-skill.js.map +1 -0
  64. package/dist/electron-build/agents/managed-services.js +69 -0
  65. package/dist/electron-build/agents/managed-services.js.map +1 -0
  66. package/dist/electron-build/agents/parse-tool-call.js +43 -32
  67. package/dist/electron-build/agents/parse-tool-call.js.map +1 -1
  68. package/dist/electron-build/agents/pi-sdk-session-factory.js +36 -3
  69. package/dist/electron-build/agents/pi-sdk-session-factory.js.map +1 -1
  70. package/dist/electron-build/agents/pi-sdk-session-manager.js +75 -3
  71. package/dist/electron-build/agents/pi-sdk-session-manager.js.map +1 -1
  72. package/dist/electron-build/agents/pi-sdk-tools.js +200 -31
  73. package/dist/electron-build/agents/pi-sdk-tools.js.map +1 -1
  74. package/dist/electron-build/agents/pi-sdk-types.js.map +1 -1
  75. package/dist/electron-build/agents/pi-sdk.js +799 -206
  76. package/dist/electron-build/agents/pi-sdk.js.map +1 -1
  77. package/dist/electron-build/agents/plan-store.js +37 -0
  78. package/dist/electron-build/agents/plan-store.js.map +1 -1
  79. package/dist/electron-build/agents/process-runner.js +71 -2
  80. package/dist/electron-build/agents/process-runner.js.map +1 -1
  81. package/dist/electron-build/agents/run-context.js +24 -0
  82. package/dist/electron-build/agents/run-context.js.map +1 -0
  83. package/dist/electron-build/agents/runner-resolver.js +2 -1
  84. package/dist/electron-build/agents/runner-resolver.js.map +1 -1
  85. package/dist/electron-build/agents/shell-guard.js +110 -14
  86. package/dist/electron-build/agents/shell-guard.js.map +1 -1
  87. package/dist/electron-build/agents/skill-health.js +103 -0
  88. package/dist/electron-build/agents/skill-health.js.map +1 -0
  89. package/dist/electron-build/agents/skill-ledger.js +146 -0
  90. package/dist/electron-build/agents/skill-ledger.js.map +1 -0
  91. package/dist/electron-build/agents/tool-loop-guard.js +319 -0
  92. package/dist/electron-build/agents/tool-loop-guard.js.map +1 -0
  93. package/dist/electron-build/agents/tool-path-args.js +63 -0
  94. package/dist/electron-build/agents/tool-path-args.js.map +1 -0
  95. package/dist/electron-build/agents/tool-result-gate.js +59 -0
  96. package/dist/electron-build/agents/tool-result-gate.js.map +1 -0
  97. package/dist/electron-build/agents/tool-subset.js +116 -0
  98. package/dist/electron-build/agents/tool-subset.js.map +1 -0
  99. package/dist/electron-build/agents/tool-telemetry.js +108 -0
  100. package/dist/electron-build/agents/tool-telemetry.js.map +1 -0
  101. package/dist/electron-build/bootstrap/context-collector.js +38 -7
  102. package/dist/electron-build/bootstrap/context-collector.js.map +1 -1
  103. package/dist/electron-build/bootstrap/lifecycle-hooks.js +12 -3
  104. package/dist/electron-build/bootstrap/lifecycle-hooks.js.map +1 -1
  105. package/dist/electron-build/bootstrap/persona-init.js +216 -0
  106. package/dist/electron-build/bootstrap/persona-init.js.map +1 -0
  107. package/dist/electron-build/bootstrap/persona-loader.js +5 -1
  108. package/dist/electron-build/bootstrap/persona-loader.js.map +1 -1
  109. package/dist/electron-build/context-compaction/auto-compact.js +38 -2
  110. package/dist/electron-build/context-compaction/auto-compact.js.map +1 -1
  111. package/dist/electron-build/external-engines/delegate.js +22 -0
  112. package/dist/electron-build/external-engines/delegate.js.map +1 -1
  113. package/dist/electron-build/kernel/channel-actor.js +314 -0
  114. package/dist/electron-build/kernel/channel-actor.js.map +1 -0
  115. package/dist/electron-build/orbitdb/cid-database.js +55 -0
  116. package/dist/electron-build/orbitdb/cid-database.js.map +1 -1
  117. package/dist/electron-build/orbitdb/ipfs-node.js +36 -11
  118. package/dist/electron-build/orbitdb/ipfs-node.js.map +1 -1
  119. package/dist/electron-build/web/server-storage.js +26 -5
  120. package/dist/electron-build/web/server-storage.js.map +1 -1
  121. package/dist/external-engines/delegate.js +21 -0
  122. package/dist/index.js +526 -95
  123. package/dist/kernel/channel-actor.js +297 -0
  124. package/dist/kernel/control.js +95 -0
  125. package/dist/kernel/gate-scan.js +1036 -0
  126. package/dist/kernel/model-runtime.js +472 -0
  127. package/dist/kernel/plan-channel-actor.js +298 -0
  128. package/dist/kernel/plan-constraint.js +165 -0
  129. package/dist/kernel/plan-deletion.js +32 -0
  130. package/dist/kernel/plan-modelruntime.js +60 -0
  131. package/dist/kernel/plan-runcontext.js +163 -0
  132. package/dist/kernel/plan.js +153 -0
  133. package/dist/kernel/roster.js +242 -0
  134. package/dist/orbitdb/cid-database.js +55 -0
  135. package/dist/orbitdb/event-index.js +370 -0
  136. package/dist/orbitdb/event-query.js +131 -0
  137. package/dist/orbitdb/event-shell.js +510 -0
  138. package/dist/orbitdb/group-access.js +381 -0
  139. package/dist/orbitdb/group-shards.js +140 -0
  140. package/dist/orbitdb/ipfs-node.js +36 -11
  141. package/dist/web/bolloon-web.json +3 -3
  142. package/dist/web/client.js +4 -2
  143. package/dist/web/mobile-core.js +7762 -6289
  144. package/dist/web/routes-mobile-tasks.js +2 -1
  145. package/dist/web/routes-tasks.js +5 -1
  146. package/dist/web/server-storage.js +26 -5
  147. package/dist/web/server.js +60 -18
  148. package/dist/web/sw.js +1 -1
  149. package/dist/web/ui/message-renderer.js +473 -29
  150. package/dist/web/ui/step-timeline.js +13 -12
  151. package/package.json +2 -1
  152. package/scripts/build-web.ts +74 -1
  153. package/dist/constraint-runtime/_archive_helper.d.ts +0 -1
  154. package/dist/constraint-runtime/_archive_helper.js +0 -9
  155. package/dist/constraint-runtime/assistant/index.d.ts +0 -11
  156. package/dist/constraint-runtime/assistant/index.js +0 -12
  157. package/dist/constraint-runtime/bootstrap/index.d.ts +0 -11
  158. package/dist/constraint-runtime/bootstrap/index.js +0 -12
  159. package/dist/constraint-runtime/bridge/index.d.ts +0 -11
  160. package/dist/constraint-runtime/bridge/index.js +0 -12
  161. package/dist/constraint-runtime/buddy/index.d.ts +0 -11
  162. package/dist/constraint-runtime/buddy/index.js +0 -12
  163. package/dist/constraint-runtime/cli/index.d.ts +0 -11
  164. package/dist/constraint-runtime/cli/index.js +0 -12
  165. package/dist/constraint-runtime/components/index.d.ts +0 -11
  166. package/dist/constraint-runtime/components/index.js +0 -12
  167. package/dist/constraint-runtime/constants/index.d.ts +0 -11
  168. package/dist/constraint-runtime/constants/index.js +0 -12
  169. package/dist/constraint-runtime/coordinator/index.d.ts +0 -11
  170. package/dist/constraint-runtime/coordinator/index.js +0 -12
  171. package/dist/constraint-runtime/cost_hook.d.ts +0 -2
  172. package/dist/constraint-runtime/cost_hook.js +0 -4
  173. package/dist/constraint-runtime/entrypoints/index.d.ts +0 -11
  174. package/dist/constraint-runtime/entrypoints/index.js +0 -12
  175. package/dist/constraint-runtime/execution_registry.d.ts +0 -20
  176. package/dist/constraint-runtime/execution_registry.js +0 -44
  177. package/dist/constraint-runtime/hooks/index.d.ts +0 -11
  178. package/dist/constraint-runtime/hooks/index.js +0 -12
  179. package/dist/constraint-runtime/ink.d.ts +0 -1
  180. package/dist/constraint-runtime/ink.js +0 -4
  181. package/dist/constraint-runtime/keybindings/index.d.ts +0 -11
  182. package/dist/constraint-runtime/keybindings/index.js +0 -12
  183. package/dist/constraint-runtime/memdir/index.d.ts +0 -11
  184. package/dist/constraint-runtime/memdir/index.js +0 -12
  185. package/dist/constraint-runtime/migrations/index.d.ts +0 -11
  186. package/dist/constraint-runtime/migrations/index.js +0 -12
  187. package/dist/constraint-runtime/moreright/index.d.ts +0 -11
  188. package/dist/constraint-runtime/moreright/index.js +0 -12
  189. package/dist/constraint-runtime/native_ts/index.d.ts +0 -11
  190. package/dist/constraint-runtime/native_ts/index.js +0 -12
  191. package/dist/constraint-runtime/output_styles/index.d.ts +0 -11
  192. package/dist/constraint-runtime/output_styles/index.js +0 -12
  193. package/dist/constraint-runtime/plugins/index.d.ts +0 -11
  194. package/dist/constraint-runtime/plugins/index.js +0 -12
  195. package/dist/constraint-runtime/port_manifest.d.ts +0 -7
  196. package/dist/constraint-runtime/port_manifest.js +0 -11
  197. package/dist/constraint-runtime/query.d.ts +0 -6
  198. package/dist/constraint-runtime/query.js +0 -1
  199. package/dist/constraint-runtime/remote/index.d.ts +0 -11
  200. package/dist/constraint-runtime/remote/index.js +0 -12
  201. package/dist/constraint-runtime/schemas/index.d.ts +0 -11
  202. package/dist/constraint-runtime/schemas/index.js +0 -12
  203. package/dist/constraint-runtime/screens/index.d.ts +0 -11
  204. package/dist/constraint-runtime/screens/index.js +0 -12
  205. package/dist/constraint-runtime/server/index.d.ts +0 -11
  206. package/dist/constraint-runtime/server/index.js +0 -12
  207. package/dist/constraint-runtime/services/index.d.ts +0 -11
  208. package/dist/constraint-runtime/services/index.js +0 -12
  209. package/dist/constraint-runtime/state/index.d.ts +0 -11
  210. package/dist/constraint-runtime/state/index.js +0 -12
  211. package/dist/constraint-runtime/system_init.d.ts +0 -1
  212. package/dist/constraint-runtime/system_init.js +0 -20
  213. package/dist/constraint-runtime/types/index.d.ts +0 -11
  214. package/dist/constraint-runtime/types/index.js +0 -12
  215. package/dist/constraint-runtime/upstream_proxy/index.d.ts +0 -11
  216. package/dist/constraint-runtime/upstream_proxy/index.js +0 -12
  217. package/dist/constraint-runtime/utils/index.d.ts +0 -11
  218. package/dist/constraint-runtime/utils/index.js +0 -12
  219. package/dist/constraint-runtime/vim/index.d.ts +0 -11
  220. package/dist/constraint-runtime/vim/index.js +0 -12
  221. package/dist/constraint-runtime/voice/index.d.ts +0 -11
  222. package/dist/constraint-runtime/voice/index.js +0 -12
@@ -2,6 +2,7 @@ import * as fs from 'fs/promises';
2
2
  import { loadWalletTool } from './wallet-tools.js';
3
3
  import * as fsSync from 'fs';
4
4
  import * as path from 'path';
5
+ import * as os from 'os';
5
6
  import { currentPackageRoot } from '../utils/version-info.js';
6
7
  import { documentReader } from '../documents/reader.js';
7
8
  import { p2pNetwork } from '../network/p2p.js';
@@ -10,7 +11,9 @@ import { checkWritePath } from './shell-guard.js';
10
11
  import { p2pDocumentTools } from './p2p-document-tools.js';
11
12
  import { runSelfImproveLoop } from './pi-sdk-session-factory.js';
12
13
  import { getMinimax } from '../constraints/index.js';
13
- import { delegateToEngine } from '../external-engines/delegate.js';
14
+ import { delegateToEngine, buildDelegateCommand } from '../external-engines/delegate.js';
15
+ import { spawnBackground } from './process-runner.js';
16
+ import { DELEGATE_ENV_MARK } from './background-notices.js';
14
17
  // 2026-09-13: 下一代工具集 (人机问答 / 精确补丁 / 桌面操作 / 技能分享)
15
18
  import { userQuestions, DEFAULT_QUESTION_TIMEOUT_MS } from './user-questions.js';
16
19
  import { registerPatchTools } from './patch-tool.js';
@@ -62,6 +65,13 @@ async function detectRunner(candidates) {
62
65
  }
63
66
  return null;
64
67
  }
68
+ /**
69
+ * 技能文件路径(账本/快照用) —— 与 skill-writer 的落盘规则一致: <root>/<name>/SKILL.md ✓
70
+ */
71
+ function skillFilePath(name, scope) {
72
+ const root = scope === 'project' ? path.join(process.cwd(), '.bolloon', 'skills') : path.join(os.homedir(), '.bolloon', 'skills');
73
+ return path.join(root, name, 'SKILL.md');
74
+ }
65
75
  export async function runCodeSnippet(opts) {
66
76
  const code = String(opts.code ?? '').trim();
67
77
  if (!code)
@@ -196,7 +206,7 @@ export function registerBuiltinTools(ctx) {
196
206
  });
197
207
  ctx.tools.set('summarize_document', {
198
208
  name: 'summarize_document',
199
- description: '总结文档内容,分析并生成摘要',
209
+ description: '读一篇文档并生成摘要. 只想要摘要 ⇒ 这个; 想按你的要求改写 ⇒ improve_document; 想自己读原文 ⇒ read_document.',
200
210
  parameters: { path: '文件路径 (必填)', context: '可选, 总结上下文提示' },
201
211
  execute: async (args) => {
202
212
  try {
@@ -223,7 +233,7 @@ export function registerBuiltinTools(ctx) {
223
233
  });
224
234
  ctx.tools.set('improve_document', {
225
235
  name: 'improve_document',
226
- description: '根据要求改进文档内容',
236
+ description: '按你的要求**改写**已有文档 (保留原意、改表达). 纯改写 ⇒ 用这个; 只想要摘要 ⇒ summarize_document; 全新写一份 ⇒ write_file.',
227
237
  parameters: { path: '文件路径 (必填)', requirements: '改进要求 (必填)' },
228
238
  execute: async (args) => {
229
239
  try {
@@ -251,7 +261,7 @@ export function registerBuiltinTools(ctx) {
251
261
  });
252
262
  ctx.tools.set('list_peers', {
253
263
  name: 'list_peers',
254
- description: '列出已连接的对等节点',
264
+ description: '列**已连接**的 P2P 对等节点. 想找远端可发消息的 channel ⇒ list_remote_channels; 想看好友申请 ⇒ list_pending_friend_requests.',
255
265
  parameters: {},
256
266
  execute: async () => {
257
267
  const peers = p2pNetwork.getPeers();
@@ -263,7 +273,7 @@ export function registerBuiltinTools(ctx) {
263
273
  });
264
274
  ctx.tools.set('send_message', {
265
275
  name: 'send_message',
266
- description: '向指定对等节点发送消息',
276
+ description: '给**一个**指定节点发消息 (需要它的 peerId). 不知道发谁 ⇒ 先 list_peers; 要广播 ⇒ broadcast_message.',
267
277
  parameters: { peer_id: '对等节点ID', message: '消息内容' },
268
278
  execute: async (args) => {
269
279
  try {
@@ -277,7 +287,7 @@ export function registerBuiltinTools(ctx) {
277
287
  });
278
288
  ctx.tools.set('broadcast_message', {
279
289
  name: 'broadcast_message',
280
- description: '向所有对等节点广播消息',
290
+ description: '向**所有**已连接节点广播一条消息. 只发给某一个 ⇒ send_message; 发到某个远程 channel ⇒ send_to_remote_channel. 广播面广, 别拿它当日常通知用.',
281
291
  parameters: { message: '消息内容' },
282
292
  execute: async (args) => {
283
293
  try {
@@ -375,7 +385,7 @@ export function registerBuiltinTools(ctx) {
375
385
  });
376
386
  ctx.tools.set('p2p_broadcast', {
377
387
  name: 'p2p_broadcast',
378
- description: '广播消息到所有连接的 P2P 节点',
388
+ description: '把一个 p2p channel 的地址**广播**给已连接节点. 单点发消息用 send_message, 别广播.',
379
389
  parameters: { message: '消息内容', type: '可选, 消息类型标签 (默认 agent-broadcast)' },
380
390
  execute: async (args) => {
381
391
  const msg = String(args.message || '').trim();
@@ -609,6 +619,8 @@ export function registerBuiltinTools(ctx) {
609
619
  }
610
620
  });
611
621
  // delegate_to_engine — 把编码任务委派给本机已安装的其他 AI 编码智能体 CLI
622
+ // 2026-10-01: **它是同步等待** (默认 120s 超时, 超时杀进程) ⇒ 长活别用它 ✗ —
623
+ // 要"起完就走、以后回来收"的长任务 ⇒ `terminal(background:true)` + `process poll/wait/kill` ✓(今天已给后台进程加了落盘/懒恢复 ✓)。
612
624
  // (codex / claude-code / opencode / openclaw / hermes). 它们必须已安装且可达 PATH.
613
625
  // 实验 API 引擎 (experiment:xxx) 是供应商不是 CLI, 不支持委派, 工具会提示改用 import.
614
626
  ctx.tools.set('delegate_to_engine', {
@@ -618,7 +630,9 @@ export function registerBuiltinTools(ctx) {
618
630
  engine: "引擎 id: codex / claude-code / opencode / openclaw / hermes (实验 API 不支持委派)",
619
631
  prompt: '派发的任务描述 (作为单参数传给该引擎 CLI)',
620
632
  model: '可选, 强制指定模型 (如 deepseek/deepseek-v4-flash), 需引擎支持',
621
- cwd: '可选, 工作目录, 默认当前目录'
633
+ cwd: '可选, 工作目录, 默认当前目录',
634
+ timeoutMs: '可选, 等待上限毫秒(默认 120000=2 分钟, 超时会**终止**该引擎进程). 小活给 20000~30000, 大活别用委派(见描述)',
635
+ background: '可选, **默认 true** —— 起完就走(后台 session), 结果会在完成后的**下一轮自动回灌**给你; 想拿完整输出用 process poll <session>。只有确实要"现在就等它的结果"才传 false'
622
636
  },
623
637
  execute: async (args) => {
624
638
  const engine = String(args.engine || '').trim();
@@ -633,14 +647,44 @@ export function registerBuiltinTools(ctx) {
633
647
  // correlationId 幂等去重 (同一 agent 重复 correlation 只应出现一次)
634
648
  const ownerDid = ctx.identity?.did || 'unknown';
635
649
  const correlationId = `delegate:${Date.now()}:${Math.random().toString(36).slice(2, 10)}`;
650
+ // 2026-10-01 (用户: 「开展子智能体后,bolloon 没有回归」/「还是卡住了,学 hermes」):
651
+ // **默认后台** ✓ —— 起完立刻返回(带 session id), 父智能体继续干别的 ✓;
652
+ // 它跑完的结果由 background-notices 在下一轮**自动回灌** ✓ ⇒ 不再"卡满 120s 然后白干" ✗。
653
+ const wantBackground = String(args.background ?? '') !== 'false'; // 默认 true(起完就走 ✓)
654
+ if (wantBackground) {
655
+ try {
656
+ const cmd = await buildDelegateCommand(engine, prompt, { ...(model ? { model } : {}), ...(cwd ? { cwd } : {}) });
657
+ if (cmd) {
658
+ const shellCmd = `${DELEGATE_ENV_MARK} ${JSON.stringify(cmd.cliPath)} ${cmd.args.map((a) => JSON.stringify(a)).join(' ')}`;
659
+ const sess = spawnBackground(shellCmd, cmd.cwd);
660
+ return {
661
+ success: true,
662
+ output: `🚀 已在**后台**起 ${engine} (session ${sess.id}) —— 你可以继续干别的 ✓。`
663
+ + `\n它跑完的结果会**自动回灌**到下一轮(或随时 \`process poll ${sess.id}\` 取完整输出; \`process kill ${sess.id}\` 可停)。`
664
+ + `\n❗别在原地等它, 也别重复发同一条委派。`
665
+ };
666
+ }
667
+ }
668
+ catch {
669
+ /* 造不出命令/起不来 ⇒ 退回下面的同步委派 ✓ */
670
+ }
671
+ }
672
+ const timeoutMs = Number(args.timeoutMs) || undefined;
636
673
  const result = await delegateToEngine(engine, prompt, {
637
674
  cwd: cwd || undefined,
638
675
  ...(model ? { model } : {}),
676
+ ...(timeoutMs ? { timeoutMs } : {}),
639
677
  ownerDid,
640
678
  correlationId,
641
679
  });
642
680
  if (!result.success) {
643
- return { success: false, error: result.error, output: result.output };
681
+ // 2026-10-01 (用户: 「开展子智能体后,bolloon 没有回归」):
682
+ // 实测体验 = 卡整整 2 分钟(默认超时)然后**白干**(进程被杀、活丢了) ✗ ⇒ 这里把话说清、给出可操作下一步 ✓
683
+ const timedOut = /超时/.test(String(result.error || ''));
684
+ const hint = timedOut
685
+ ? '\n⇒ 这不是"坏了", 是**该换法**: ① 拆成更小的委派(每次 ≤ 1~2 分钟); ② 或改用 `terminal(background:true)` 起长活, 再用 `process poll/wait/kill` 管(后台进程能一直活着, 不会因为超时被丢 ✓); ③ 别在原样重试同一条委派。'
686
+ : '';
687
+ return { success: false, error: `${result.error}${hint}`, output: result.output };
644
688
  }
645
689
  const handleLine = result.handle
646
690
  ? `\n[delegate handle] contractVersion=${result.handle.contractVersion} delegateId=${result.handle.delegateId} ownerDid=${result.handle.ownerDid} correlationId=${result.handle.correlationId ?? '-'} capability=${result.handle.capability.slice(0, 16)}...`
@@ -653,7 +697,7 @@ export function registerBuiltinTools(ctx) {
653
697
  });
654
698
  ctx.tools.set('get_identity', {
655
699
  name: 'get_identity',
656
- description: '获取当前智能体身份信息',
700
+ description: '取当前智能体身份 (名称 + DID). 结果在**同一会话内恒定** —— 问过一次就够了, 重复调用不会变; 要改身份用 set_persona.',
657
701
  parameters: {},
658
702
  execute: async () => {
659
703
  const id = ctx.identity;
@@ -665,7 +709,7 @@ export function registerBuiltinTools(ctx) {
665
709
  });
666
710
  ctx.tools.set('set_persona', {
667
711
  name: 'set_persona',
668
- description: '更新智能体的 persona 信息',
712
+ description: '设置**当前 agent** 的人格(名称/描述/性格). 只在用户明确要求改人格时用; 查身份用 get_identity.',
669
713
  parameters: { persona_json: 'Persona JSON 对象' },
670
714
  execute: async (args) => {
671
715
  try {
@@ -691,7 +735,7 @@ export function registerBuiltinTools(ctx) {
691
735
  });
692
736
  ctx.tools.set('get_operation_logs', {
693
737
  name: 'get_operation_logs',
694
- description: '获取约束层的操作日志',
738
+ description: '取约束层(护栏)的操作日志: 最近哪些动作被拦、按什么规则拦的. 排查"我明明做了却没生效"时先看这里.',
695
739
  parameters: {},
696
740
  execute: async () => {
697
741
  const logs = ctx.constraintLayer.getLogs();
@@ -707,7 +751,7 @@ export function registerBuiltinTools(ctx) {
707
751
  // 文件系统工具
708
752
  ctx.tools.set('list_files', {
709
753
  name: 'list_files',
710
- description: '列出目录中的文件',
754
+ description: '列目录 (看**有哪些**文件). 只需要**找**特定文件 ⇒ glob_files; 要在文件**内容**里搜 ⇒ grep_files; 要读某个文件内容 ⇒ read_file.',
711
755
  parameters: { path: '目录路径(可选,默认为当前目录)' },
712
756
  execute: async (args) => {
713
757
  try {
@@ -726,7 +770,7 @@ export function registerBuiltinTools(ctx) {
726
770
  });
727
771
  ctx.tools.set('read_directory', {
728
772
  name: 'read_directory',
729
- description: '读取目录内容,返回文件列表和目录结构',
773
+ description: '列一个目录下的条目(含子目录). 只要文件名清单 ⇒ list_files; 按通配找 ⇒ glob_files.',
730
774
  parameters: { path: '目录路径(可选,默认为当前目录)' },
731
775
  execute: async (args) => {
732
776
  try {
@@ -803,9 +847,93 @@ export function registerBuiltinTools(ctx) {
803
847
  // 2026-08-10: terminal — 灵活终端写命令 (用户要求: bolloon 自己写命令进 terminal, 少围栏).
804
848
  // 2026-08-12 (Task2): 支持 commands 数组并行执行; 与 shell_exec 统一走 runTerminalCommand.
805
849
  // 与 shell_exec 的区别: 直接接受完整 shell 命令字符串, 更适合模型自主写命令.
850
+ // 2026-10-01 (优化 #2 v1): **工具发现** —— 125 个工具不可能都记住, 给一个"按关键词找工具"的入口。
851
+ // 空关键词只回**分类索引**(名称清单), 有关键词才回"名称+用途摘要" ⇒ 不刷屏。
852
+ // 2026-10-01 (用户: 「skills 教训学习, bolloon 有吗, 从 hermes 触发的方法是什么, 学一下」):
853
+ // **实测缺口**: `skill-loader` 把 1300 个技能**注册**进注册表 ✓, 但 `describeSkill` 只 import 从没调用 ✗
854
+ // ⇒ 技能**描述从不进提示** ⇒ 模型不知道它们存在 ⇒ 技能触发链**断在这里** ✗。
855
+ // 她那边的做法: 启动自动装载 + 按**描述**在提示里露面 + 需要时用 `skill_view` 读全文 ✓。
856
+ // bolloon 有 1300 个(她百来号) ⇒ 全量注入会撑爆提示 ✗ ⇒ 改成**两个发现工具**(和 `list_tools` 同一思路 ✓)。
857
+ ctx.tools.set('list_skills', {
858
+ name: 'list_skills',
859
+ description: '用于**找技能**: 给关键词(中文/英文都行)⇒ 返回已装载技能的名字+说明(≤20 条); 不给关键词 ⇒ 先给总数与前若干条. 找到后用 read_skill 读全文再照做.',
860
+ parameters: { keyword: '可选: 关键词(名字或说明里出现的词); 省略则给概览' },
861
+ async execute(args) {
862
+ const kw = String(args?.keyword ?? '').trim().toLowerCase();
863
+ const all = typeof ctx.listSkills === 'function' ? ctx.listSkills() : [];
864
+ if (!all.length)
865
+ return { success: true, output: '(当前没有装载技能)' };
866
+ if (!kw) {
867
+ return { success: true, output: `已装载 ${all.length} 个技能. 前 20 个:\n` + all.slice(0, 20).map((s) => `- ${s.name}: ${String(s.description || '').slice(0, 80)}`).join('\n') + '\n\n用 list_skills {keyword:"…"} 找具体的.' };
868
+ }
869
+ const hits = all.filter((s) => `${s.name} ${s.description || ''}`.toLowerCase().includes(kw));
870
+ if (!hits.length)
871
+ return { success: true, output: `没有技能匹配 "${kw}"(共 ${all.length} 个). 换个更短的词试试.` };
872
+ return { success: true, output: hits.slice(0, 20).map((s) => `- ${s.name}: ${String(s.description || '').slice(0, 100)}`).join('\n') + (hits.length > 20 ? `\n…还有 ${hits.length - 20} 个` : '') };
873
+ },
874
+ });
875
+ ctx.tools.set('read_skill', {
876
+ name: 'read_skill',
877
+ description: '用于**读技能全文**(先 list_skills 找名字). 按里面的步骤/判据做事, 别只读个大概就动手; 内容长的会截断.',
878
+ parameters: { name: '技能名(必填, 来自 list_skills)' },
879
+ async execute(args) {
880
+ const name = String(args?.name ?? '').trim();
881
+ if (!name)
882
+ return { success: false, error: '缺 name; 先用 list_skills 找' };
883
+ let body = null;
884
+ try {
885
+ body = ctx.getSkillBody ? await ctx.getSkillBody(name) : null;
886
+ }
887
+ catch {
888
+ body = null;
889
+ }
890
+ if (!body)
891
+ return { success: false, error: `没有这个技能: ${name}(用 list_skills 看有哪些)` };
892
+ const clipped = body.length > 8000 ? body.slice(0, 8000) + `\n…[已截断, 原文 ${body.length} 字符]` : body;
893
+ // 2026-10-01: 用量遥测(旁挂文件 ✓ 绝不写进用户 SKILL.md ✗)—— best-effort, 失败不影响本次读取 ✓
894
+ try {
895
+ const { bumpUsage } = await import('./skill-health.js');
896
+ bumpUsage(name);
897
+ }
898
+ catch { /* best-effort */ }
899
+ return { success: true, output: clipped };
900
+ },
901
+ });
902
+ ctx.tools.set('list_tools', {
903
+ name: 'list_tools',
904
+ description: '找不到该用哪个工具时用这个: 给关键词(如 "文件"/"钱包"/"群" / "grep")⇒ 返回匹配工具的名字+用途摘要; 不给关键词 ⇒ 回全部分类索引. 你**先** list_tools 摸清工具, 再动手.',
905
+ parameters: { keyword: '可选: 关键词 (工具名或用途里出现的词); 省略则回分类索引' },
906
+ async execute(args) {
907
+ const kw = String(args?.keyword ?? '').trim().toLowerCase();
908
+ const all = [...ctx.tools.entries()].map(([name, t]) => ({ name, desc: String(t?.description ?? '') }));
909
+ if (!kw) {
910
+ const groups = {};
911
+ for (const { name } of all) {
912
+ const k = /wallet|chain|token|balance|tx|polymarket|safe|x402/.test(name) ? '链上/钱包'
913
+ : /^(read|list|glob|grep|search)/.test(name) || /file|dir|doc/.test(name) ? '读/找文件'
914
+ : /^(write|edit|mkdir|move|delete|copy|patch)/.test(name) ? '写/改文件'
915
+ : /task|plan|todo|goal|run/.test(name) ? '任务/计划'
916
+ : /peer|channel|friend|send|broadcast|group|inbox|p2p/.test(name) ? 'P2P/沟通'
917
+ : /web|fetch|http|url/.test(name) ? '网络'
918
+ : '其它';
919
+ (groups[k] ||= []).push(name);
920
+ }
921
+ const lines = Object.entries(groups).map(([k, v]) => `- ${k} (${v.length}): ${v.slice(0, 14).join(' ')}${v.length > 14 ? ' …' : ''}`);
922
+ return { success: true, output: `共 ${all.length} 个工具, 按类:\n${lines.join('\n')}\n\n用 list_tools {keyword:"…"} 查具体用途.` };
923
+ }
924
+ const hits = all.filter((t) => t.name.toLowerCase().includes(kw) || t.desc.toLowerCase().includes(kw));
925
+ if (!hits.length)
926
+ return { success: true, output: `没有工具匹配 "${kw}". 试试更短的词, 或 list_tools 不带关键词看分类.` };
927
+ return { success: true, output: hits.slice(0, 20).map((t) => `- ${t.name}: ${t.desc.replace(/\s+/g, ' ').slice(0, 90)}`).join('\n') };
928
+ },
929
+ });
806
930
  ctx.tools.set('terminal', {
807
931
  name: 'terminal',
808
- description: '执行完整 shell 命令 (支持管道/重定向/写文件/跑脚本). 护栏只挡高危破坏操作 (sudo/格式化/rm -rf 根目录/写 ~/.bolloon 数据), 其余灵活放行. 适合: 写 HTML 文件、跑 python/node 脚本、查系统状态、装依赖. 多条命令用 commands 数组并行执行. 长命令 (服务器/构建/后台任务) 设 background=true 后台执行不阻塞对话. 也可直接传 code+language 自动写脚本执行 (便捷代码运行: python/js/ts/shell/html).',
932
+ description: '执行 shell 命令 (管道/重定向/跑脚本/装依赖/查系统状态). '
933
+ + '**上手前先看一眼这张对照表 —— 下面这些"终端习惯"都有专用工具, 用它们比敲 shell 更稳(有护栏、结果可核对)**: '
934
+ + 'grep/rg ⇒ grep_files · find/ls ⇒ glob_files / list_files · cat/head/tail ⇒ read_file · '
935
+ + 'sed/awk ⇒ edit_file · echo > 文件 / cat <<EOF ⇒ write_file(要一次性生成大量代码 ⇒ execute_code) · '
936
+ + 'curl 取网页正文 ⇒ fetch_url. 造文件请优先用 write_file / execute_code. 护栏只挡高危破坏操作 (sudo/格式化/rm -rf 根目录/写 ~/.bolloon 数据), 其余灵活放行. 适合: 写 HTML 文件、跑 python/node 脚本、查系统状态、装依赖. 多条命令用 commands 数组并行执行. 长命令 (服务器/构建/后台任务) 设 background=true 后台执行不阻塞对话. 也可直接传 code+language 自动写脚本执行 (便捷代码运行: python/js/ts/shell/html).',
809
937
  parameters: { command: '完整 shell 命令 (可选, 如: echo "<html>" > /tmp/site/index.html && ls /tmp/site)', commands: '可选: 多条命令数组 (并行执行), 每条独立字符串', code: '可选: 一段代码, 传 code+language 时自动写脚本执行 (便捷代码运行)', language: '可选: code 的语言 (python/js/ts/shell/html), 默认自动', timeoutMs: '超时毫秒, 默认 30000', background: '可选: true 后台执行, 立即返回 session_id (用 process 工具 poll/wait/kill)' },
810
938
  execute: async (args) => {
811
939
  const timeoutMs = Number(args.timeoutMs) || 30000;
@@ -826,10 +954,25 @@ export function registerBuiltinTools(ctx) {
826
954
  // 长命令不阻塞对话: terminal(background=true) 启动 → process 工具轮询/等待/终止.
827
955
  ctx.tools.set('process', {
828
956
  name: 'process',
829
- description: '管理后台进程 (terminal background=true 启动的). action: poll(查状态, 不阻塞) / wait(等结束, 最多 timeoutMs) / kill(终止) / list(列全部). 长期运行命令的阻塞问题用它解决.',
957
+ description: '管理后台进程与常驻服务. action: poll(查状态, 不阻塞) / wait(等结束, 最多 timeoutMs) / kill(终止) / list(列全部 shell 后台进程) / **services(列常驻服务: 社交心跳·P2P·OrbitDB 群聊·文档接收)** / **service(启停某个常驻服务, 需 name + op)**. 长期运行命令与去中心化交流进程都用它管.',
830
958
  parameters: { session_id: '后台进程 session_id (必填, poll/wait/kill 用)', action: 'poll | wait | kill | list (默认 poll)', timeoutMs: 'wait 模式等待上限毫秒, 默认 30000' },
831
959
  execute: async (args) => {
832
960
  const action = String(args.action || 'poll').trim().toLowerCase();
961
+ // 2026-10-01 (用户: 「process 也要可以管理群聊和去中心化交流进程」):
962
+ // services ⇒ 看常驻服务(社交心跳/P2P/OrbitDB 群聊/文档接收); service ⇒ 启停(只对真能启停的放行)
963
+ if (action === 'services') {
964
+ return { success: true, output: ctx.managedServices ? ctx.managedServices.describe() : '(没有注册常驻服务)' };
965
+ }
966
+ if (action === 'service') {
967
+ const nm = String(args.name || '').trim();
968
+ const op = String(args.op || '').trim().toLowerCase();
969
+ if (!nm || (op !== 'start' && op !== 'stop'))
970
+ return { success: false, error: 'action=service 需要 name + op(start|stop)' };
971
+ if (!ctx.managedServices)
972
+ return { success: false, error: '本进程没有注册常驻服务' };
973
+ const r = await ctx.managedServices.control(nm, op);
974
+ return r.ok ? { success: true, output: r.output } : { success: false, error: r.output };
975
+ }
833
976
  try {
834
977
  const { pollSession, waitSession, killSession, listSessions, isValidSessionId } = await import('./process-runner.js');
835
978
  if (action === 'list') {
@@ -879,15 +1022,19 @@ export function registerBuiltinTools(ctx) {
879
1022
  // write_file / edit_file / git_*
880
1023
  ctx.tools.set('write_file', {
881
1024
  name: 'write_file',
882
- description: '写入一个文件. 路径必须在白名单. 大文件 (> 100KB) 会被拒. 命中护栏黑名单会拒.',
883
- parameters: { path: '相对路径 (必填, 相对 cwd)', content: '文件内容 (必填)' },
1025
+ description: '写入**一个**文件 (整文件覆盖). 路径须在白名单内, 命中护栏黑名单会拒. '
1026
+ + '**要写大量代码时按这个分工**: 一个文件 ⇒ 就用本工具(单文件上限 2MB) · '
1027
+ + '**多个文件 / 要生成成套代码** ⇒ 用 execute_code 一次写完(一次调用写 N 个文件, 不逐个来回) · '
1028
+ + '别用 terminal 的 heredoc/echo 来造文件(容易转义出错、也看不出写了什么).',
1029
+ parameters: { path: '相对路径 (必填, 相对 cwd)', content: '文件内容 (必填; 单文件上限 2MB)' },
884
1030
  execute: async (args) => {
885
1031
  const relPath = String(args.path || '').trim();
886
1032
  const content = String(args.content ?? '');
887
1033
  if (!relPath)
888
1034
  return { success: false, error: 'path 必填' };
889
- if (content.length > 100_000)
890
- return { success: false, error: `内容过大 (${content.length} > 100000 字节), 请分块写` };
1035
+ // 2026-10-01: 上限 100KB → 2MB —— 原来的上限让"写一个大文件"只能分块, 反而促成了碎片化的多次调用
1036
+ if (content.length > 2_000_000)
1037
+ return { success: false, error: `内容过大 (${content.length} > 2000000 字节): 请用 execute_code 在脚本里分块写, 或拆成多个文件` };
891
1038
  const pathResult = checkWritePath(relPath);
892
1039
  if (!pathResult.allowed) {
893
1040
  return { success: false, error: `路径被护栏拒: ${pathResult.reason}` };
@@ -1089,7 +1236,7 @@ export function registerBuiltinTools(ctx) {
1089
1236
  });
1090
1237
  ctx.tools.set('mkdir', {
1091
1238
  name: 'mkdir',
1092
- description: '创建一个或多个目录. 自动 mkdir -p.',
1239
+ description: '新建一个目录(可带 -p 建多级). 只是**列**目录用 list_files / read_directory, 别用这个.',
1093
1240
  parameters: { path: '目录路径 (必填, 相对 cwd)' },
1094
1241
  execute: async (args) => {
1095
1242
  const relPath = String(args.path || '').trim();
@@ -1151,7 +1298,7 @@ export function registerBuiltinTools(ctx) {
1151
1298
  const { promisify } = await import('util');
1152
1299
  const pExecFile = promisify(execFile);
1153
1300
  const argv = ['-rn', '--include=' + (args.filePattern || '*'), pattern, searchPath];
1154
- const { stdout, stderr } = await pExecFile('grep', argv, { cwd: ctx.cwd, maxBuffer: 1024 * 1024 });
1301
+ const { stdout, stderr } = await pExecFile('grep', argv, { cwd: ctx.cwd, maxBuffer: 32 * 1024 * 1024 });
1155
1302
  const lines = stdout.split('\n').filter(Boolean).slice(0, 50);
1156
1303
  return { success: true, output: `🔍 grep "${pattern}" in ${searchPath} (${args.filePattern || '*'}, 最多 50 行):\n${lines.join('\n')}${lines.length === 50 ? '\n... (truncated)' : ''}` };
1157
1304
  }
@@ -1464,7 +1611,7 @@ export function registerBuiltinTools(ctx) {
1464
1611
  });
1465
1612
  ctx.tools.set('update_task', {
1466
1613
  name: 'update_task',
1467
- description: '更新任务的某一步状态.',
1614
+ description: '更新任务的某一步 (标记 done/blocked + 备注). 改**整条计划**(目标/加步骤) ⇒ 用计划类工具; 只是查进度 ⇒ get_task.',
1468
1615
  parameters: { task_id: '任务 id', step_id: '步骤 id', status: '新状态', result_summary: '可选', error: '可选' },
1469
1616
  execute: async (args) => {
1470
1617
  const taskId = String(args.task_id || '').trim();
@@ -1498,7 +1645,7 @@ export function registerBuiltinTools(ctx) {
1498
1645
  });
1499
1646
  ctx.tools.set('get_task', {
1500
1647
  name: 'get_task',
1501
- description: '查任务的当前状态和步骤进度.',
1648
+ description: '查一个任务的当前状态与步骤进度. 要看**每一步的细节输出** ⇒ get_operation_logs; 想改状态 ⇒ update_task.',
1502
1649
  parameters: { task_id: '任务 id (必填)' },
1503
1650
  execute: async (args) => {
1504
1651
  const taskId = String(args.task_id || '').trim();
@@ -1530,7 +1677,7 @@ export function registerBuiltinTools(ctx) {
1530
1677
  });
1531
1678
  ctx.tools.set('list_tasks', {
1532
1679
  name: 'list_tasks',
1533
- description: '列出最近 N 个任务 (默认 10).',
1680
+ description: '列任务清单(默认只看未完成). 要看某个任务的步骤 ⇒ get_task; 想看执行细节 ⇒ get_operation_logs.',
1534
1681
  parameters: { limit: '可选, 默认 10' },
1535
1682
  execute: async (args) => {
1536
1683
  const limit = Number(args.limit) || 10;
@@ -1682,10 +1829,22 @@ export function registerBuiltinTools(ctx) {
1682
1829
  triggers = t.map(String);
1683
1830
  }
1684
1831
  catch { /* triggers 解析失败忽略 */ }
1832
+ // 2026-10-01: 变更前先留快照(覆盖已有技能时才有内容 ✓)+ 记"谁建的"(决定自审有没有资格治理它 ✓)
1833
+ const { captureSkillBefore, appendLedger, currentWriteOrigin } = await import('./skill-ledger.js');
1834
+ const { markCreatedBy } = await import('./skill-health.js');
1835
+ const before = captureSkillBefore(skillFilePath(name, args.scope === 'project' ? 'project' : 'user'));
1685
1836
  const r = await createSkill(name, String(args.description || ''), body, {
1686
1837
  scope: args.scope === 'project' ? 'project' : 'user',
1687
1838
  triggers,
1688
1839
  });
1840
+ if (r.ok) {
1841
+ const after = captureSkillBefore(r.path);
1842
+ appendLedger({ origin: currentWriteOrigin(), tool: 'create_skill', skill: name, file: r.path, beforeSha: before.beforeSha, beforeBytes: undefined, afterSha: after.beforeSha, bytesBefore: before.bytesBefore, bytesAfter: after.bytesBefore });
1843
+ try {
1844
+ markCreatedBy(name, currentWriteOrigin());
1845
+ }
1846
+ catch { /* best-effort */ }
1847
+ }
1689
1848
  return r.ok
1690
1849
  ? { success: true, output: `✅ skill '${name}' 已写入 ${r.path}` }
1691
1850
  : { success: false, error: r.error };
@@ -1708,9 +1867,15 @@ export function registerBuiltinTools(ctx) {
1708
1867
  execute: async (args) => {
1709
1868
  try {
1710
1869
  const { updateSkill } = await import('./skill-writer.js');
1870
+ const { reviewMayTouch, captureSkillBefore, appendLedger, currentWriteOrigin } = await import('./skill-ledger.js');
1711
1871
  const name = String(args.name || '').trim();
1712
1872
  if (!name)
1713
1873
  return { success: false, error: 'name 必填' };
1874
+ // 2026-10-01 **写来源隔离**: 自审只许治理自己造出来的技能 ✓ —— 用户点名要的归用户 ✓
1875
+ const may = reviewMayTouch(name);
1876
+ if (!may.ok)
1877
+ return { success: false, error: `拒绝(写来源隔离): ${may.reason}` };
1878
+ const before = captureSkillBefore(skillFilePath(name, 'user'));
1714
1879
  let triggers;
1715
1880
  try {
1716
1881
  const t = JSON.parse(String(args.triggers || '[]'));
@@ -1724,6 +1889,10 @@ export function registerBuiltinTools(ctx) {
1724
1889
  body: args.body ? String(args.body) : undefined,
1725
1890
  triggers,
1726
1891
  });
1892
+ if (r.ok) {
1893
+ const after = captureSkillBefore(r.path || skillFilePath(name, 'user'));
1894
+ appendLedger({ origin: currentWriteOrigin(), tool: 'update_skill', skill: name, file: r.path || skillFilePath(name, 'user'), beforeSha: before.beforeSha, afterSha: after.beforeSha, bytesBefore: before.bytesBefore, bytesAfter: after.bytesBefore });
1895
+ }
1727
1896
  return r.ok
1728
1897
  ? { success: true, output: `✅ skill '${name}' 已更新 ${r.path}` }
1729
1898
  : { success: false, error: r.error };
@@ -3364,7 +3533,7 @@ export function registerBuiltinTools(ctx) {
3364
3533
  // execute_code — 独立代码执行工具 (与 terminal 的 code 便捷路径同源, 这里给独立入口)
3365
3534
  ctx.tools.set('execute_code', {
3366
3535
  name: 'execute_code',
3367
- description: '写一段代码并执行, 拿回 stdout/stderr/退出码。适合计算、数据处理、试算法、快速验证想法。支持 python / js / ts / shell / html。需要多步 shell 操作用 terminal; 需要长期进程用 terminal background=true。',
3536
+ description: '写一段代码并执行, 拿回 stdout/stderr/退出码。适合: 计算 · 数据处理 · 试算法 · 快速验证想法 · **一次生成大量代码**(在脚本里循环写多个文件 —— 这是"成套代码/多文件"的**推荐路径**, 一次调用顶几十次 write_file)。支持 python / js / ts / shell / html。需要多步 shell 操作用 terminal; 需要长期进程用 terminal background=true。',
3368
3537
  parameters: {
3369
3538
  code: '要执行的代码 (必填)',
3370
3539
  language: '语言: python | js | ts | shell (默认 python)',
@@ -3532,7 +3701,7 @@ export function registerWalletTools(ctx) {
3532
3701
  });
3533
3702
  ctx.tools.set('wallet_import', {
3534
3703
  name: 'wallet_import',
3535
- description: '导入已有 EVM 钱包 (用助记词或私钥)',
3704
+ description: '从文件导入 EVM 钱包到本机台账(私钥值不会回显). 新建钱包 ⇒ 用创建类工具, 别拿它当通用入口.',
3536
3705
  parameters: { mnemonic: '可选, 12/15/18/21/24 词助记词', privateKey: '可选, 0x 开头的私钥' },
3537
3706
  execute: async (args) => {
3538
3707
  try {
@@ -3561,7 +3730,7 @@ export function registerWalletTools(ctx) {
3561
3730
  });
3562
3731
  ctx.tools.set('wallet_get_balance', {
3563
3732
  name: 'wallet_get_balance',
3564
- description: '查 EVM 钱包 ETH 余额.',
3733
+ description: '查 EVM 钱包 ETH 余额. 想看**全部**资产/代币 ⇒ 用钱包总览类工具; 想转账 ⇒ 转账工具.',
3565
3734
  parameters: { address: '0x 开头的 EVM 地址 (必填)', rpcUrl: '可选 RPC URL (默认 eth.llamarpc.com)' },
3566
3735
  execute: async (args) => {
3567
3736
  try {
@@ -3612,7 +3781,7 @@ export function registerWalletTools(ctx) {
3612
3781
  });
3613
3782
  ctx.tools.set('wallet_transfer_token', {
3614
3783
  name: 'wallet_transfer_token',
3615
- description: '用 EVM 钱包转 ERC20 token.',
3784
+ description: '转 ERC20 代币到指定地址(要合约地址+金额). 转**原生币**(ETH)用另一个工具; 先确认链和余额.',
3616
3785
  parameters: { privateKey: '私钥 (必填)', tokenAddress: 'ERC20 合约地址 (必填)', to: '接收地址 (必填)', amount: 'token 数量', decimals: '可选 token decimals', rpcUrl: '可选 RPC URL' },
3617
3786
  execute: async (args) => {
3618
3787
  try {
@@ -3656,7 +3825,7 @@ export function registerWalletTools(ctx) {
3656
3825
  // Polymarket
3657
3826
  ctx.tools.set('polymarket_list_markets', {
3658
3827
  name: 'polymarket_list_markets',
3659
- description: '列出 Polymarket 预测市场.',
3828
+ description: '列 Polymarket 预测市场(可按分类/状态筛). 查某个市场的**价格/盘口** ⇒ 用对应详情工具.',
3660
3829
  parameters: { limit: '可选 数量 (默认 50)', offset: '可选 偏移', closed: '可选 是否只显示已关闭' },
3661
3830
  execute: async (args) => {
3662
3831
  try {
@@ -3679,7 +3848,7 @@ export function registerWalletTools(ctx) {
3679
3848
  });
3680
3849
  ctx.tools.set('polymarket_get_market', {
3681
3850
  name: 'polymarket_get_market',
3682
- description: '获取单个 Polymarket 市场的详情.',
3851
+ description: '查某个 Polymarket 市场的详情(题目/结算/盘口). 只是**列**市场 ⇒ polymarket_list_markets.',
3683
3852
  parameters: { marketId: '市场 ID (必填)' },
3684
3853
  execute: async (args) => {
3685
3854
  try {
@@ -3950,7 +4119,7 @@ export function registerWalletTools(ctx) {
3950
4119
  // Safe
3951
4120
  ctx.tools.set('safe_deploy', {
3952
4121
  name: 'safe_deploy',
3953
- description: '部署 Safe 多签钱包.',
4122
+ description: '部署 Gnosis Safe 多签钱包 (建钱包/加签名人). 只是**查**多签状态别用这个.',
3954
4123
  parameters: { owners: 'JSON 数组 owner 地址 (必填)', threshold: '需要几个签名 (必填)' },
3955
4124
  execute: async (args) => {
3956
4125
  try {