@bolloon/bolloon-agent 0.5.3 → 0.5.5

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 (144) 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 +38 -32
  14. package/dist/agents/pi-sdk-session-manager.js +72 -3
  15. package/dist/agents/pi-sdk-tools.js +268 -47
  16. package/dist/agents/pi-sdk.js +470 -22
  17. package/dist/agents/plan-store.js +36 -0
  18. package/dist/agents/process-runner.js +64 -2
  19. package/dist/agents/session-store.js +144 -3
  20. package/dist/agents/shell-guard.js +109 -13
  21. package/dist/agents/skill-health.js +90 -0
  22. package/dist/agents/skill-ledger.js +129 -0
  23. package/dist/agents/tool-loop-guard.js +303 -0
  24. package/dist/agents/tool-path-args.js +53 -0
  25. package/dist/agents/tool-result-gate.js +50 -0
  26. package/dist/agents/tool-subset.js +109 -0
  27. package/dist/agents/tool-telemetry.js +97 -0
  28. package/dist/agents/wallet-tools.js +63 -0
  29. package/dist/bootstrap/context-collector.js +37 -7
  30. package/dist/bootstrap/lifecycle-hooks.js +12 -3
  31. package/dist/bootstrap/persona-init.js +174 -0
  32. package/dist/bootstrap/persona-loader.js +5 -1
  33. package/dist/cli/ink-app.js +231 -32
  34. package/dist/cli/input-history.js +119 -0
  35. package/dist/cli/input-paste.js +218 -0
  36. package/dist/cli/loading-tui.js +69 -7
  37. package/dist/cli/log-gate.js +46 -1
  38. package/dist/cli/mention-data.js +30 -0
  39. package/dist/cli/reasoning-view.js +103 -0
  40. package/dist/cli/session-summary.js +129 -0
  41. package/dist/cli/session-tree.js +141 -0
  42. package/dist/cli/startup-notice.js +25 -0
  43. package/dist/cli/status-segments.js +155 -0
  44. package/dist/cli/stores.js +16 -0
  45. package/dist/cli/trace-line.js +54 -0
  46. package/dist/cli/wallet-store.js +159 -0
  47. package/dist/cli/wallet-table.js +46 -0
  48. package/dist/cli-entry.js +22 -0
  49. package/dist/context-compaction/auto-compact.js +37 -2
  50. package/dist/electron-build/agents/agent-identity-store.js +43 -5
  51. package/dist/electron-build/agents/agent-identity-store.js.map +1 -1
  52. package/dist/electron-build/agents/background-notices.js +138 -0
  53. package/dist/electron-build/agents/background-notices.js.map +1 -0
  54. package/dist/electron-build/agents/channel-identity.js +64 -0
  55. package/dist/electron-build/agents/channel-identity.js.map +1 -0
  56. package/dist/electron-build/agents/code-write-gate.js +55 -0
  57. package/dist/electron-build/agents/code-write-gate.js.map +1 -0
  58. package/dist/electron-build/agents/deny-pipeline.js +11 -2
  59. package/dist/electron-build/agents/deny-pipeline.js.map +1 -1
  60. package/dist/electron-build/agents/egress-sanitize.js +76 -0
  61. package/dist/electron-build/agents/egress-sanitize.js.map +1 -0
  62. package/dist/electron-build/agents/error-classifier.js +68 -5
  63. package/dist/electron-build/agents/error-classifier.js.map +1 -1
  64. package/dist/electron-build/agents/experience-review.js +243 -0
  65. package/dist/electron-build/agents/experience-review.js.map +1 -0
  66. package/dist/electron-build/agents/iteration-budget.js +73 -0
  67. package/dist/electron-build/agents/iteration-budget.js.map +1 -0
  68. package/dist/electron-build/agents/lesson-to-skill.js +228 -0
  69. package/dist/electron-build/agents/lesson-to-skill.js.map +1 -0
  70. package/dist/electron-build/agents/managed-services.js +69 -0
  71. package/dist/electron-build/agents/managed-services.js.map +1 -0
  72. package/dist/electron-build/agents/parse-tool-call.js +38 -32
  73. package/dist/electron-build/agents/parse-tool-call.js.map +1 -1
  74. package/dist/electron-build/agents/pi-sdk-session-manager.js +75 -3
  75. package/dist/electron-build/agents/pi-sdk-session-manager.js.map +1 -1
  76. package/dist/electron-build/agents/pi-sdk-tools.js +268 -46
  77. package/dist/electron-build/agents/pi-sdk-tools.js.map +1 -1
  78. package/dist/electron-build/agents/pi-sdk-types.js.map +1 -1
  79. package/dist/electron-build/agents/pi-sdk.js +471 -22
  80. package/dist/electron-build/agents/pi-sdk.js.map +1 -1
  81. package/dist/electron-build/agents/plan-store.js +37 -0
  82. package/dist/electron-build/agents/plan-store.js.map +1 -1
  83. package/dist/electron-build/agents/process-runner.js +71 -2
  84. package/dist/electron-build/agents/process-runner.js.map +1 -1
  85. package/dist/electron-build/agents/session-store.js +144 -3
  86. package/dist/electron-build/agents/session-store.js.map +1 -1
  87. package/dist/electron-build/agents/shell-guard.js +110 -14
  88. package/dist/electron-build/agents/shell-guard.js.map +1 -1
  89. package/dist/electron-build/agents/skill-health.js +103 -0
  90. package/dist/electron-build/agents/skill-health.js.map +1 -0
  91. package/dist/electron-build/agents/skill-ledger.js +146 -0
  92. package/dist/electron-build/agents/skill-ledger.js.map +1 -0
  93. package/dist/electron-build/agents/tool-loop-guard.js +319 -0
  94. package/dist/electron-build/agents/tool-loop-guard.js.map +1 -0
  95. package/dist/electron-build/agents/tool-path-args.js +63 -0
  96. package/dist/electron-build/agents/tool-path-args.js.map +1 -0
  97. package/dist/electron-build/agents/tool-result-gate.js +59 -0
  98. package/dist/electron-build/agents/tool-result-gate.js.map +1 -0
  99. package/dist/electron-build/agents/tool-subset.js +116 -0
  100. package/dist/electron-build/agents/tool-subset.js.map +1 -0
  101. package/dist/electron-build/agents/tool-telemetry.js +108 -0
  102. package/dist/electron-build/agents/tool-telemetry.js.map +1 -0
  103. package/dist/electron-build/agents/wallet-tools.js +102 -0
  104. package/dist/electron-build/agents/wallet-tools.js.map +1 -0
  105. package/dist/electron-build/bootstrap/context-collector.js +38 -7
  106. package/dist/electron-build/bootstrap/context-collector.js.map +1 -1
  107. package/dist/electron-build/bootstrap/lifecycle-hooks.js +12 -3
  108. package/dist/electron-build/bootstrap/lifecycle-hooks.js.map +1 -1
  109. package/dist/electron-build/bootstrap/persona-init.js +216 -0
  110. package/dist/electron-build/bootstrap/persona-init.js.map +1 -0
  111. package/dist/electron-build/bootstrap/persona-loader.js +5 -1
  112. package/dist/electron-build/bootstrap/persona-loader.js.map +1 -1
  113. package/dist/electron-build/cli/wallet-store.js +200 -0
  114. package/dist/electron-build/cli/wallet-store.js.map +1 -0
  115. package/dist/electron-build/context-compaction/auto-compact.js +38 -2
  116. package/dist/electron-build/context-compaction/auto-compact.js.map +1 -1
  117. package/dist/electron-build/external-engines/delegate.js +22 -0
  118. package/dist/electron-build/external-engines/delegate.js.map +1 -1
  119. package/dist/electron-build/llm/pi-ai.js +25 -0
  120. package/dist/electron-build/llm/pi-ai.js.map +1 -1
  121. package/dist/electron-build/orbitdb/cid-database.js +55 -0
  122. package/dist/electron-build/orbitdb/cid-database.js.map +1 -1
  123. package/dist/electron-build/orbitdb/ipfs-node.js +36 -11
  124. package/dist/electron-build/orbitdb/ipfs-node.js.map +1 -1
  125. package/dist/electron-build/web/server-storage.js +26 -5
  126. package/dist/electron-build/web/server-storage.js.map +1 -1
  127. package/dist/external-engines/delegate.js +21 -0
  128. package/dist/index.js +1324 -173
  129. package/dist/llm/pi-ai.js +23 -0
  130. package/dist/orbitdb/cid-database.js +55 -0
  131. package/dist/orbitdb/event-index.js +370 -0
  132. package/dist/orbitdb/event-query.js +131 -0
  133. package/dist/orbitdb/event-shell.js +510 -0
  134. package/dist/orbitdb/group-access.js +381 -0
  135. package/dist/orbitdb/group-shards.js +140 -0
  136. package/dist/orbitdb/ipfs-node.js +36 -11
  137. package/dist/web/bolloon-web.json +3 -3
  138. package/dist/web/client.js +3 -2
  139. package/dist/web/mobile-core.js +7762 -6289
  140. package/dist/web/routes-mobile-tasks.js +2 -1
  141. package/dist/web/server-storage.js +26 -5
  142. package/dist/web/server.js +2 -0
  143. package/dist/web/sw.js +1 -1
  144. package/package.json +2 -1
@@ -0,0 +1,319 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_THRESHOLDS = exports.LoopStallState = exports.STUB_ARGS_PREVIEW_CHARS = exports.REPEAT_STUB_MIN_CHARS = exports.STUB_MIN_CHARS = exports.CYCLE_HISTORY = exports.MAX_CYCLE_PERIOD = exports.IDENTICAL_CALL_THRESHOLD = exports.FAILURE_TOLERANT_TOOLS = exports.REPEATABLE_SUFFIXES = exports.REPEATABLE_TOOLS = exports.MUTATING_TOOLS = exports.IDEMPOTENT_TOOLS = exports.CONSTANT_RESULT_TOOLS = void 0;
4
+ exports.canonicalToolArgs = canonicalToolArgs;
5
+ exports.toolCallSignature = toolCallSignature;
6
+ exports.resultFingerprint = resultFingerprint;
7
+ exports.isRepeatableTool = isRepeatableTool;
8
+ exports.observeToolCall = observeToolCall;
9
+ exports.detectCycle = detectCycle;
10
+ exports.toolMayHaveSideEffect = toolMayHaveSideEffect;
11
+ exports.defaultHardStopFor = defaultHardStopFor;
12
+ exports.newProgressBookkeeping = newProgressBookkeeping;
13
+ exports.trackProgress = trackProgress;
14
+ exports.batchHint = batchHint;
15
+ /**
16
+ * tool-loop-guard.ts — 工具调用的**停滞观测与温和引导** (2026-10-01)
17
+ *
18
+ * 为什么需要它 (用户原话: 「一个简单的任务居然花这么多次工具调用」):
19
+ * 原先的防重只有一条「同工具连续 5 次 ⇒ 注入一条提示, 然后重置计数」。实测模型无视提示后
20
+ * 完全没刹车 —— 同一个 get_identity 连调 10 次 ✗; 而且**只看工具名**, 连"同工具换参数的正常
21
+ * 迭代"(逐文件读)也会被误判。
22
+ * 反过来"硬停"也不行 (用户明确否决): 那是在症状上下手, 而且会堵掉正当的重复轮询。
23
+ *
24
+ * 所以这里采用**分类 + 温和合成**的口径:
25
+ * ① 工具先分类: 幂等读 / 有副作用 / 可重复轮询 (含命名约定 `_poll` / `_get_result`);
26
+ * ② 判据看**签名** (工具名 + 参数的规范 JSON 的哈希), 不只看工具名 ⇒ 换参数不误伤;
27
+ * ③ 命中停滞**不拒绝执行**, 而是产出一条**引导文本**追加在真结果后面 (保留模型的选择权);
28
+ * ④ 完全相同的**结果**从第 2 次起用**引用 stub** 替代 (省上下文; 且明确告诉模型"与上次相同");
29
+ * ⑤ 抓**循环** (A→B→A→B…, 周期 ≤4): 相邻重复计数会被交替调用清零, 只抓相邻的会漏;
30
+ * ⑥ 失败宽容类工具 (跑测试红了 / grep 空 / 页面超时) 的"失败"是正常工作输出, 不参与停滞判定。
31
+ *
32
+ * 纯逻辑, 不碰 IO / 不发请求 —— 便于单测与变异验证。
33
+ */
34
+ const node_crypto_1 = require("node:crypto");
35
+ /** 幂等只读类: 同参数重复调用不会产生新信息 (但允许换参数继续用) */
36
+ /**
37
+ * **结果恒定**的工具 (2026-10-01): 同一会话内反复调, 返回**永远一样**, 且答案本身很短。
38
+ * 用户实测: `get_identity` 连调 3–4 次 ✗ —— 原有"引用 stub"有字符数门槛, 而它的输出才 ~90 字符
39
+ * ⇒ 门槛拦不住 ⇒ 模型看不出"再问也一样"。
40
+ * 规矩 (工具设置层, 不做硬刹车): 从**第 2 次**完全相同的调用起, 直接把结果换成一句标准答复,
41
+ * 明确告诉模型"答案就在上面那次返回里, 再调不会变" ⇒ 去掉它重复调的理由。
42
+ */
43
+ exports.CONSTANT_RESULT_TOOLS = new Set([
44
+ 'get_identity', 'bolloon_config_get', 'get_agent_info', 'whoami',
45
+ ]);
46
+ exports.IDEMPOTENT_TOOLS = new Set([
47
+ 'read_file', 'list_files', 'search_files', 'get_identity', 'get_operation_logs',
48
+ 'list_remote_channels', 'list_context_layers', 'read_context_assets', 'get_balance', 'wallet_list',
49
+ 'web_search', 'web_extract', 'check_inbox',
50
+ ]);
51
+ /** 有副作用类: 重复调用可能是正当的 (例如再跑一条命令), 由"同签名同结果"判定停滞 */
52
+ exports.MUTATING_TOOLS = new Set([
53
+ 'terminal', 'shell_exec', 'execute_code', 'write_file', 'patch', 'set_persona', 'memory',
54
+ 'todo_list', 'send_message', 'send_to_remote_channel', 'delegate_task', 'create_group', 'set_wallet',
55
+ ]);
56
+ /** 轮询类: 同参数重复**本来就正当** (等异步任务), 永不触发相同调用提示 */
57
+ exports.REPEATABLE_TOOLS = new Set(['process_manage', 'check_task_status']);
58
+ /** 生成/MCP 轮询工具的命名约定 (与上面同理) */
59
+ exports.REPEATABLE_SUFFIXES = ['_poll', '_get_result'];
60
+ /** 失败的"正常输出"类: 红了/空结果/超时属于工作产出, 不参与停滞判定 */
61
+ exports.FAILURE_TOLERANT_TOOLS = new Set(['terminal', 'shell_exec', 'execute_code', 'web_search', 'web_extract']);
62
+ /** 第几次**连续相同 (工具+参数+结果)** 触发引导 (3 = 容忍一次复核) */
63
+ exports.IDENTICAL_CALL_THRESHOLD = 3;
64
+ /** 抓循环时最长的周期 (A→B→A→B 的周期是 2) */
65
+ exports.MAX_CYCLE_PERIOD = 4;
66
+ /** 历史窗口: 够放最长的周期若干圈 + 余量 */
67
+ exports.CYCLE_HISTORY = 64;
68
+ /** 结果达到这个字符数才值得用 stub 替代 (太短本来就省不了多少) */
69
+ exports.STUB_MIN_CHARS = 512;
70
+ /**
71
+ * **同签名 + 同结果**的重复调用: 门槛降到这个值 (2026-10-01, 优化 #1 的收尾)。
72
+ * 为什么单独一条: 512 是"值不值得压缩"的门槛 ✗, 而"同一个调用又跑了一遍、结果一字不差"**本身就是浪费** ✓
73
+ * —— 实测 `get_identity` 输出才 ~90 字符 ⇒ 卡在 512 门外, 于是被连调 3–4 次 ✓。
74
+ * 现在: 同签名重复 且 结果一致 且 ≥64 字符 ⇒ 从第 2 次起换引用 stub ✓ (少于 64 字符的本来也占不了多少上下文, 放行 ✓)。
75
+ */
76
+ exports.REPEAT_STUB_MIN_CHARS = 64;
77
+ /** stub 里保留的参数预览长度 (万一压缩把原结果挤掉, 还能看出当初调了什么) */
78
+ exports.STUB_ARGS_PREVIEW_CHARS = 120;
79
+ /** 参数的规范 JSON (键排序 + 紧凑分隔符) —— 让 {a,b} 与 {b,a} 同签名 */
80
+ function canonicalToolArgs(args) {
81
+ if (args === null || typeof args !== 'object' || Array.isArray(args))
82
+ return '';
83
+ const obj = args;
84
+ const keys = Object.keys(obj).sort();
85
+ const parts = keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`);
86
+ return `{${parts.join(',')}}`;
87
+ }
88
+ function stableStringify(v) {
89
+ if (v === null || typeof v !== 'object')
90
+ return JSON.stringify(v) ?? 'null';
91
+ if (Array.isArray(v))
92
+ return `[${v.map(stableStringify).join(',')}]`;
93
+ const o = v;
94
+ return `{${Object.keys(o).sort().map((k) => `${JSON.stringify(k)}:${stableStringify(o[k])}`).join(',')}}`;
95
+ }
96
+ /** 调用签名: 不可逆 (只留哈希), 不泄漏参数原文 */
97
+ function toolCallSignature(toolName, args) {
98
+ return (0, node_crypto_1.createHash)('sha256').update(`${toolName}\u0000${canonicalToolArgs(args)}`).digest('hex').slice(0, 16);
99
+ }
100
+ /** 结果指纹 (判"完全相同的返回") */
101
+ function resultFingerprint(resultText) {
102
+ return (0, node_crypto_1.createHash)('sha256').update(String(resultText ?? '')).digest('hex').slice(0, 16);
103
+ }
104
+ function isRepeatableTool(toolName) {
105
+ if (exports.REPEATABLE_TOOLS.has(toolName))
106
+ return true;
107
+ return exports.REPEATABLE_SUFFIXES.some((s) => toolName.endsWith(s));
108
+ }
109
+ /**
110
+ * 记录一次调用并给出观测。**纯函数式**: 调用方每次都要把上一次的 streak 传进来
111
+ * (见 `LoopStallState`), 这样状态归属清晰、也便于单测。
112
+ */
113
+ function observeToolCall(state, input) {
114
+ const ok = input.ok !== false;
115
+ const tolerant = exports.FAILURE_TOLERANT_TOOLS.has(input.toolName);
116
+ const repeatable = isRepeatableTool(input.toolName);
117
+ const text = String(input.resultText ?? '');
118
+ const chars = input.resultChars ?? text.length;
119
+ // ① 轮询类: 同参数重复本就正当 ⇒ 只记录, 不出声
120
+ if (repeatable) {
121
+ state.push(input.toolName, input.args, text);
122
+ return { action: 'allow', code: 'allow', count: 0 };
123
+ }
124
+ const sig = toolCallSignature(input.toolName, input.args);
125
+ const fp = resultFingerprint(text);
126
+ const prev = state.last;
127
+ const sameAsPrev = !!prev && prev.sig === sig && prev.fp === fp;
128
+ // ② 连续相同 (工具 + 参数 + 结果): 每次 +1, 否则归 1
129
+ state.streak = sameAsPrev ? state.streak + 1 : 1;
130
+ state.push(input.toolName, input.args, text);
131
+ // ③ 失败宽容类工具的失败不参与停滞判定 (红测试/空 grep 是产出, 不是卡住)
132
+ if (!ok && tolerant) {
133
+ return { action: 'allow', code: 'allow', count: state.streak };
134
+ }
135
+ // ③.5 恒定结果工具 (get_identity 这类): 从第 2 次起换标准答复 —— 不管结果长短
136
+ if (sameAsPrev && exports.CONSTANT_RESULT_TOOLS.has(input.toolName) && ok) {
137
+ return {
138
+ action: 'allow',
139
+ code: 'allow',
140
+ count: state.streak,
141
+ stub: `[已答] ${input.toolName} 的结果在本次会话内**恒定不变**(它读的是固定身份/配置, 不是活数据)。` +
142
+ `上面那一次返回就是答案, 直接用它作答即可; 再调一次只会得到同样的内容。` +
143
+ `若确实需要变化, 请改用写类工具(如 set_persona / 相应 update 工具)。`,
144
+ };
145
+ }
146
+ // ④ 结果引用 stub: 从**第 2 次**完全相同的返回起 (够长才替代; 失败永不替代)
147
+ let stub;
148
+ // 2026-10-01 (优化 #1 收尾): 同签名重复 + 结果一字不差 ⇒ 用更低门槛 (重复本身就是浪费, 不管长短)
149
+ const repeatFloor = input.sameSignatureBefore ? exports.REPEAT_STUB_MIN_CHARS : exports.STUB_MIN_CHARS;
150
+ if (ok && input.seenResultBefore && chars >= repeatFloor) {
151
+ stub = `[结果引用] 与本次会话中此前一次 ${input.toolName} 的返回**逐字相同** (${chars} 字符, 已折叠)。`
152
+ + `参数预览: ${canonicalToolArgs(input.args).slice(0, exports.STUB_ARGS_PREVIEW_CHARS)}`;
153
+ }
154
+ if (state.streak >= exports.IDENTICAL_CALL_THRESHOLD) {
155
+ // ⑤ 连续相同达阈值 ⇒ 温和引导 (不拒绝执行)
156
+ return {
157
+ action: 'warn',
158
+ code: 'identical_call_streak',
159
+ count: state.streak,
160
+ stub,
161
+ notice: `[系统提示] 这是第 ${state.streak} 次**用完全相同的参数**调用 ${input.toolName}, 且返回与上次完全相同。`
162
+ + `重复调用不会得到新信息 —— 请换参数、换工具, 或基于已有结果直接回答。`,
163
+ };
164
+ }
165
+ // ⑥ 循环检测: 最近 N 次里出现了周期 ≤4 的重复串 (相邻计数会被交替调用清零, 只抓相邻必漏)
166
+ const cycle = detectCycle(state.history);
167
+ if (cycle) {
168
+ return {
169
+ action: 'warn',
170
+ code: 'identical_cycle',
171
+ count: cycle.laps,
172
+ stub,
173
+ notice: `[系统提示] 检测到最近 ${cycle.laps} 圈重复同一组 ${cycle.period} 次调用`
174
+ + `(以 ${input.toolName} 结尾), 参数与返回都逐字相同。请不要重复这一组 —— 换参数、换工具, 或基于已有结果直接回答。`,
175
+ };
176
+ }
177
+ return { action: 'allow', code: 'allow', count: state.streak, stub };
178
+ }
179
+ /** 每轮 ReAct 循环一份的停滞状态 */
180
+ class LoopStallState {
181
+ streak = 0;
182
+ last = null;
183
+ history = [];
184
+ /** 已见过的结果指纹 (用于"完全相同返回"的 stub 判定) */
185
+ seenResults = new Set();
186
+ push(toolName, args, resultText) {
187
+ const step = { sig: toolCallSignature(toolName, args), fp: resultFingerprint(resultText) };
188
+ this.last = step;
189
+ this.history.push(step);
190
+ if (this.history.length > exports.CYCLE_HISTORY)
191
+ this.history.shift();
192
+ this.seenResults.add(step.fp);
193
+ }
194
+ /** 这个结果指纹此前是否出现过 (供 observeToolCall 的 seenResultBefore) */
195
+ hasSeenResult(text) {
196
+ return this.seenResults.has(resultFingerprint(text));
197
+ }
198
+ reset() {
199
+ this.streak = 0;
200
+ this.last = null;
201
+ this.history.length = 0;
202
+ this.seenResults.clear();
203
+ }
204
+ }
205
+ exports.LoopStallState = LoopStallState;
206
+ /**
207
+ * 找"最近若干圈完全重复"的串: 对每个候选周期 p ∈ [1, MAX_CYCLE_PERIOD],
208
+ * 看末尾是否由同一个 p 长度的块**重复 ≥ THRESHOLD 次**组成 (块内每个 (签名,结果指纹) 都相同)。
209
+ * 返回圈数与周期, 没有则 null。
210
+ */
211
+ function detectCycle(history) {
212
+ const h = history;
213
+ for (let p = 1; p <= exports.MAX_CYCLE_PERIOD; p++) {
214
+ const need = p * exports.IDENTICAL_CALL_THRESHOLD;
215
+ if (h.length < need)
216
+ continue;
217
+ const tail = h.slice(-need);
218
+ let same = true;
219
+ for (let i = p; i < need && same; i++) {
220
+ if (tail[i].sig !== tail[i % p].sig || tail[i].fp !== tail[i % p].fp)
221
+ same = false;
222
+ }
223
+ if (same)
224
+ return { period: p, laps: exports.IDENTICAL_CALL_THRESHOLD };
225
+ }
226
+ return null;
227
+ }
228
+ // ───────────────────────────────────────────────────────────────────────────
229
+ // 2026-10-01 第二轮 (继续学): 三处补上
230
+ // ① **无进展**是独立一轴: 幂等工具即使**换了参数**, 只要结果与上次相同 ⇒ 也是无进展
231
+ // (原先只抓"同参数" ⇒ 换个措辞再问一遍就绕过去了)
232
+ // ② **进展的定义 = 有副作用的调用成功过** (纯读再多都不算) ⇒ 用它清零失败/无进展计数
233
+ // ③ 升级策略**按在场与否分流**: 有人在 ⇒ 只提醒(从不阻止执行); 无人值守 ⇒ 才允许拦/停
234
+ // (人在场时不该替用户做决定; 但 cron/supervisor 这种无人值守的场景, 该果断收)
235
+ // ───────────────────────────────────────────────────────────────────────────
236
+ /** 有副作用 = 会改变世界 (文件/进程/网络/资金/记忆). 唯一判据, 供"进展"与"无进展"用 */
237
+ function toolMayHaveSideEffect(toolName) {
238
+ if (exports.MUTATING_TOOLS.has(toolName))
239
+ return true;
240
+ if (exports.IDEMPOTENT_TOOLS.has(toolName))
241
+ return false;
242
+ if (isRepeatableTool(toolName))
243
+ return false;
244
+ // 未登记的工具按"可能有副作用"处理 (保守: 不轻易判定"它没改变世界")
245
+ return true;
246
+ }
247
+ exports.DEFAULT_THRESHOLDS = {
248
+ exactFailureWarnAfter: 2,
249
+ exactFailureBlockAfter: 5,
250
+ sameToolFailureWarnAfter: 3,
251
+ sameToolFailureHaltAfter: 8,
252
+ noProgressWarnAfter: 2,
253
+ noProgressBlockAfter: 5,
254
+ hardStopEnabled: false,
255
+ };
256
+ /** 无人值守场景 (cron / supervisor / 非交互) —— 那里才默认允许拦与停 */
257
+ function defaultHardStopFor(context = {}) {
258
+ if (typeof context.unattended === 'boolean')
259
+ return context.unattended;
260
+ const env = context.env || process.env;
261
+ return env.BOLLOON_UNATTENDED === '1' || env.BOLLOON_CRON === '1' || env.BOLLOON_SUPERVISOR === '1';
262
+ }
263
+ function newProgressBookkeeping() {
264
+ return { progressClearedSignatures: new Set(), noProgress: new Map() };
265
+ }
266
+ /**
267
+ * 记一次调用结果, 维护"进展 / 无进展"两本账。
268
+ * - 有副作用的调用**成功** ⇒ 视为进展: 清掉该签名的失败标记 + 清掉**所有**无进展记录
269
+ * (世界变了 ⇒ 之前"读了没变化"的判定作废)
270
+ * - 幂等工具: 结果指纹与上次相同 ⇒ 无进展 +1 (**参数变没变不影响**), 不同 ⇒ 归 1
271
+ */
272
+ function trackProgress(book, input, thresholds = exports.DEFAULT_THRESHOLDS) {
273
+ const ok = input.ok !== false;
274
+ const sig = toolCallSignature(input.toolName, input.args);
275
+ const fp = resultFingerprint(String(input.resultText ?? ''));
276
+ if (ok && toolMayHaveSideEffect(input.toolName)) {
277
+ book.progressClearedSignatures.add(sig);
278
+ if (book.noProgress.size > 0)
279
+ book.noProgress.clear();
280
+ return { action: 'allow', code: 'allow', count: 0 };
281
+ }
282
+ if (!toolMayHaveSideEffect(input.toolName)) {
283
+ const prev = book.noProgress.get(input.toolName);
284
+ const count = prev && prev.lastResultFp === fp ? prev.count + 1 : 1;
285
+ book.noProgress.set(input.toolName, { lastResultFp: fp, count });
286
+ if (count >= thresholds.noProgressWarnAfter) {
287
+ return {
288
+ action: 'warn',
289
+ code: 'idempotent_no_progress',
290
+ count,
291
+ notice: `[系统提示] 连续 ${count} 次读取类调用 (${input.toolName}) 返回的都是**同一份内容**`
292
+ + `(参数不同也一样) —— 世界没有变化, 再读还是它。请基于已有内容作答, 或改去查别的来源。`,
293
+ };
294
+ }
295
+ return { action: 'allow', code: 'allow', count };
296
+ }
297
+ return { action: 'allow', code: 'allow', count: 0 };
298
+ }
299
+ /**
300
+ * 2026-10-01 (用户: 「为什么这么慢」): 量到的事实 —— 每次 LLM 往返平均 **3338ms**, 而
301
+ * `toolCalls>1`(一轮发多个工具)**只占 12/63** ✗ ⇒ 回合时长 ≈ 往返次数 × 3.3s ✓;
302
+ * 也就是说"慢"主要不是工具慢(工具合计仅占 4% ✗), 而是**同一轮只干一件事**、来回跑 ✓。
303
+ * 做法: 一轮只调了一个**只读**工具时, 在结果后附一句**带代价**的提醒(不是口号 ✓)。
304
+ * 只读工具才提醒(写类工具串行更安全 ✓ 不催)。
305
+ */
306
+ const BATCHABLE_HINT_TOOLS = new Set([
307
+ 'read_file', 'grep_files', 'glob_files', 'list_files', 'read_document',
308
+ 'git_status', 'git_log', 'git_diff', 'get_identity', 'list_tools', 'list_skills',
309
+ 'process', 'list_local_channels', 'get_balance',
310
+ ]);
311
+ function batchHint(toolName, toolCallsThisRound) {
312
+ if (toolCallsThisRound !== 1)
313
+ return '';
314
+ if (!BATCHABLE_HINT_TOOLS.has(String(toolName || '')))
315
+ return '';
316
+ return '\n⚡ 若你接下来还要查别的(或还要读别的文件), **同一轮可以一次发多个工具** —— '
317
+ + '每次来回约 3.3 秒, 分成多轮就是纯等待; 独立的读操作请并成一轮发。';
318
+ }
319
+ //# sourceMappingURL=tool-loop-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-loop-guard.js","sourceRoot":"","sources":["../../../src/agents/tool-loop-guard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PATH_ARG_KEYS = void 0;
7
+ exports.expandTilde = expandTilde;
8
+ exports.expandKnownAliases = expandKnownAliases;
9
+ exports.expandHomeArgs = expandHomeArgs;
10
+ /**
11
+ * 工具参数里的 `~` 展开 (2026-10-01)。
12
+ *
13
+ * 用户实测: 智能体调 `list_files { path: "~/.bolloon" }` ⇒ `ENOENT: scandir '~/.bolloon'` ✗。
14
+ * 根因: 全仓**没有任何一处**做 `~` 展开 ⇒ 模型写的 `~/x` 原样进 fs, 变成字面目录名。
15
+ * 修法: 在**唯一分发点** (pi-sdk.ts 的 `tool.execute(...)`) 统一展开 —— 工具设置层修一次,
16
+ * 而不是每个工具各打一个补丁 (那样必然漏)。只碰已知的路径类参数, 其它参数一律不动。
17
+ */
18
+ const node_os_1 = __importDefault(require("node:os"));
19
+ const node_path_1 = __importDefault(require("node:path"));
20
+ /** 会被当成路径的参数名 (只展开这些, 别的一律不碰) */
21
+ exports.PATH_ARG_KEYS = [
22
+ 'path', 'paths', 'dir', 'directory', 'folder', 'file', 'file_path', 'filePath',
23
+ 'from', 'to', 'target', 'source', 'src', 'dest', 'cwd', 'workdir', 'root', 'glob_dir',
24
+ ];
25
+ /** 只展开**开头**的 `~` (单独的 `~` 或 `~/...`); `a~b` / `~user` 不碰 */
26
+ function expandTilde(p, home = node_os_1.default.homedir()) {
27
+ const s = String(p ?? '');
28
+ if (s === '~')
29
+ return home;
30
+ if (s.startsWith('~/') || s.startsWith('~' + node_path_1.default.sep))
31
+ return node_path_1.default.join(home, s.slice(2));
32
+ return s;
33
+ }
34
+ /**
35
+ * 已知别名: `.bolloon` 指的就是数据目录 (~/.bolloon)。
36
+ * 2026-10-01 用户实测: 智能体写 `list_files { path: ".bolloon" }` ⇒ `ENOENT: scandir '.bolloon'` ✗
37
+ * (它连 `~` 都没写, 是相对路径 ⇒ 按 cwd 解析必然不存在)。只映射**恰好的**那个别名:
38
+ * `.bolloon` / `.bolloon/...`; `foo/.bolloon` 之类一律不碰 (避免误伤真的同名目录)。
39
+ */
40
+ function expandKnownAliases(p, home = node_os_1.default.homedir()) {
41
+ const s = String(p ?? '');
42
+ if (s === '.bolloon')
43
+ return node_path_1.default.join(home, '.bolloon');
44
+ if (s.startsWith('.bolloon/') || s.startsWith('.bolloon' + node_path_1.default.sep))
45
+ return node_path_1.default.join(home, '.bolloon', s.slice('.bolloon/'.length));
46
+ return s;
47
+ }
48
+ function expandHomeArgs(args, home) {
49
+ if (!args || typeof args !== 'object')
50
+ return args;
51
+ const out = { ...args };
52
+ for (const key of Object.keys(out)) {
53
+ if (!exports.PATH_ARG_KEYS.includes(key))
54
+ continue;
55
+ const v = out[key];
56
+ if (typeof v === 'string')
57
+ out[key] = expandKnownAliases(expandTilde(v, home), home);
58
+ else if (Array.isArray(v))
59
+ out[key] = v.map((x) => (typeof x === 'string' ? expandKnownAliases(expandTilde(x, home), home) : x));
60
+ }
61
+ return out;
62
+ }
63
+ //# sourceMappingURL=tool-path-args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-path-args.js","sourceRoot":"","sources":["../../../src/agents/tool-path-args.ts"],"names":[],"mappings":";;;;;;;;;AAAA;;;;;;;GAOG;AACH,sDAAyB;AACzB,0DAA6B;AAE7B,iCAAiC;AACpB,QAAA,aAAa,GAA0B;IAClD,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU;IAC9E,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU;CACtF,CAAC;AAEF,6DAA6D;AAC7D,qBAA4B,CAAS,EAAE,IAAI,GAAW,iBAAE,CAAC,OAAO,EAAE;IAChE,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1B,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAC3B,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,GAAG,mBAAI,CAAC,GAAG,CAAC;QAAE,OAAO,mBAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3F,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;;;GAKG;AACH,4BAAmC,CAAS,EAAE,IAAI,GAAW,iBAAE,CAAC,OAAO,EAAE;IACvE,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1B,IAAI,CAAC,KAAK,UAAU;QAAE,OAAO,mBAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACzD,IAAI,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,UAAU,GAAG,mBAAI,CAAC,GAAG,CAAC;QAAE,OAAO,mBAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;IACtI,OAAO,CAAC,CAAC;AACX,CAAC;AAED,wBAA+B,IAAyB,EAAE,IAAa;IACrE,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,GAAG,GAAwB,EAAE,GAAG,IAAI,EAAE,CAAC;IAC7C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACnC,IAAI,CAAC,QAAA,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,SAAS;QAC3C,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QACnB,IAAI,OAAO,CAAC,KAAK,QAAQ;YAAE,GAAG,CAAC,GAAG,CAAC,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;aAChF,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;YAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnI,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DEFAULT_RESULT_MAX_CHARS = void 0;
7
+ exports.resultsDir = resultsDir;
8
+ exports.capToolResult = capToolResult;
9
+ /**
10
+ * 工具结果的"进上下文"闸 (2026-10-01, 优化方向 #1)。
11
+ *
12
+ * 问题: 工具结果**没有上限** ✗ 就直接进 history, 而 history **每一轮都重放** ⇒ 一条大输出会持续吃上下文。
13
+ * 规矩: 超上限 ⇒ 保留**头 + 尾**(中段信息密度最低) + 把**完整结果落到文件**并把路径给模型
14
+ * (要看细节就 read_file ⇒ 既省上下文, 又不丢信息 ✓)。
15
+ * 纯函数 + 一个落盘动作; 落盘失败就退化成"只截断"(绝不因此让工具失败 ✓)。
16
+ */
17
+ const node_fs_1 = __importDefault(require("node:fs"));
18
+ const node_path_1 = __importDefault(require("node:path"));
19
+ const node_os_1 = __importDefault(require("node:os"));
20
+ /** 默认上限: 8000 字符 (够放下绝大多数结果; 超了才走落文件) */
21
+ exports.DEFAULT_RESULT_MAX_CHARS = 8000;
22
+ function resultsDir(home = node_os_1.default.homedir()) {
23
+ return node_path_1.default.join(home, '.bolloon', 'logs', 'tool-results');
24
+ }
25
+ /**
26
+ * 头尾保留: head 60% / tail 25%, 留 15% 给提示语 (与出口净化的省略口径一致, 免得两套规矩)。
27
+ */
28
+ function capToolResult(text, opts = { tool: '?' }) {
29
+ const s = String(text ?? '');
30
+ const max = opts.maxChars ?? exports.DEFAULT_RESULT_MAX_CHARS;
31
+ const originalChars = s.length;
32
+ if (max <= 0 || s.length <= max)
33
+ return { text: s, capped: false, originalChars };
34
+ const head = Math.floor(max * 0.6);
35
+ const tail = Math.floor(max * 0.25);
36
+ let spilledTo;
37
+ if (opts.spill !== false) {
38
+ try {
39
+ const home = opts.home || node_os_1.default.homedir();
40
+ const dir = resultsDir(home);
41
+ node_fs_1.default.mkdirSync(dir, { recursive: true });
42
+ const safeTool = String(opts.tool || 'tool').replace(/[^a-zA-Z0-9_.-]/g, '_').slice(0, 40);
43
+ const file = node_path_1.default.join(dir, `${Date.now()}-${safeTool}.txt`);
44
+ node_fs_1.default.writeFileSync(file, s, 'utf-8');
45
+ spilledTo = file;
46
+ }
47
+ catch { /* 落盘失败就只截断 */ }
48
+ }
49
+ const notice = spilledTo
50
+ ? `\n…[结果过长已截断: 原文 ${originalChars} 字符, 完整内容已存到 ${spilledTo} —— 要看细节用 read_file 读它]…\n`
51
+ : `\n…[结果过长已截断: 原文 ${originalChars} 字符]…\n`;
52
+ return {
53
+ text: `${s.slice(0, head)}${notice}${s.slice(-tail)}`,
54
+ capped: true,
55
+ spilledTo,
56
+ originalChars,
57
+ };
58
+ }
59
+ //# sourceMappingURL=tool-result-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-result-gate.js","sourceRoot":"","sources":["../../../src/agents/tool-result-gate.ts"],"names":[],"mappings":";;;;;;;;AAAA;;;;;;;GAOG;AACH,sDAAyB;AACzB,0DAA6B;AAC7B,sDAAyB;AAEzB,yCAAyC;AAC5B,QAAA,wBAAwB,GAAG,IAAI,CAAC;AAS7C,oBAA2B,IAAI,GAAG,iBAAE,CAAC,OAAO,EAAE;IAC5C,OAAO,mBAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC;AAC7D,CAAC;AAED;;GAEG;AACH,uBACE,IAAY,EACZ,IAAI,GAAwE,EAAE,IAAI,EAAE,GAAG,EAAE;IAEzF,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,IAAI,QAAA,wBAAwB,CAAC;IACtD,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;IAElF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IACpC,IAAI,SAA6B,CAAC;IAClC,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,iBAAE,CAAC,OAAO,EAAE,CAAC;YACvC,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;YAC7B,iBAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACvC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC3F,MAAM,IAAI,GAAG,mBAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ,MAAM,CAAC,CAAC;YAC7D,iBAAE,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;YACnC,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC;QAAC,MAAM,CAAC,CAAC,cAAc,CAAC,CAAC;IAC5B,CAAC;IACD,MAAM,MAAM,GAAG,SAAS;QACtB,CAAC,CAAC,mBAAmB,aAAa,gBAAgB,SAAS,4BAA4B;QACvF,CAAC,CAAC,mBAAmB,aAAa,SAAS,CAAC;IAC9C,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE;QACrD,MAAM,EAAE,IAAI;QACZ,SAAS;QACT,aAAa;KACd,CAAC;AACJ,CAAC"}
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ /**
3
+ * 工具集合的"注入降噪" (2026-10-01, 优化 #2 v2)。
4
+ *
5
+ * 背景: 125 个工具一次性列出 ⇒ **选择过载**(实测两次错工具: terminal 拿去造文件 ✗ / list_files 当搜索 ✗)。
6
+ * 但注意两个约束(实测过):
7
+ * ① 清单**本来就是紧凑的**(只列 `name(params)`, 不列描述) ⇒ 省上下文的价值有限, 主要价值在**选得准**;
8
+ * ② 这份清单**被缓存**(每轮重建会顶大 prompt, 还有踩 max_tokens 的历史教训) ⇒ 默认**不做**每轮动态子集。
9
+ * 因此两档:
10
+ * · 默认档: **分组列 + 末尾一句"未列出的工具也能直接按名字调用"** ⇒ 不丢能力, 且不破坏缓存;
11
+ * · 开关档 (`BOLLOON_TOOL_SUBSET=on`): 按 intent 只列相关桶 + 永远带 `list_tools` ⇒ 最省认知。
12
+ * 纪律: **执行/校验永远用完整注册表** ⇒ 子集只影响"列给模型看的那份", 不影响能不能调 ✓。
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.CORE_BUCKETS = void 0;
16
+ exports.bucketOf = bucketOf;
17
+ exports.renderToolList = renderToolList;
18
+ exports.renderToolListWithParams = renderToolListWithParams;
19
+ /** 分类: 名字 → 桶 (机械规则, 不猜) */
20
+ function bucketOf(name) {
21
+ const n = name.toLowerCase();
22
+ // 注意顺序: `list_tools` 会被 ^list 抢成"读/找文件" ⇒ 元工具先判 (2026-10-01 门抓到)
23
+ if (/^(get_identity|set_persona|persona|identity|list_tools|bolloon_config)/.test(n))
24
+ return '身份/元';
25
+ if (/wallet|chain|token|balance|^tx|polymarket|safe_|x402|erc20|escrow|treasury/.test(n))
26
+ return '链上/钱包';
27
+ if (/^(read|list|glob|grep|search|find)/.test(n) || /file|dir|document|doc$/.test(n))
28
+ return '读/找文件';
29
+ if (/^(write|edit|mkdir|move|delete|copy|patch|truncate)/.test(n))
30
+ return '写/改文件';
31
+ if (/task|plan|todo|goal|^run|queue|operation_log/.test(n))
32
+ return '任务/计划';
33
+ if (/peer|channel|friend|send|broadcast|group|inbox|p2p|contact|message/.test(n))
34
+ return 'P2P/沟通';
35
+ if (/web|fetch|http|url|search_web/.test(n))
36
+ return '网络';
37
+ if (/agent|delegate|registry|spawn|call_/.test(n))
38
+ return '智能体';
39
+ return '其它';
40
+ }
41
+ /** 核心桶: 任何任务都可能用到 ⇒ 默认档全部展开 */
42
+ exports.CORE_BUCKETS = ['读/找文件', '写/改文件', '任务/计划', '身份/元'];
43
+ /** intent 关键词 → 额外展开的桶 */
44
+ const INTENT_BUCKETS = [
45
+ [/钱包|链上|转账|余额|代币|合约|多签|gas|wallet|token|chain|tx/i, '链上/钱包'],
46
+ [/群|频道|好友|发消息|广播|节点|点对点|channel|group|friend|peer|p2p|broadcast/i, 'P2P/沟通'],
47
+ [/网页|抓取|搜索|下载|链接|url|web|fetch|http/i, '网络'],
48
+ [/智能体|子智能体|委派|agent|delegate/i, '智能体'],
49
+ ];
50
+ /**
51
+ * 生成"工具清单"文本。**只影响列出来的那份**; 调用方必须保证执行侧仍用完整注册表。
52
+ */
53
+ function renderToolList(allNames, intentText = '', opts = {}) {
54
+ const total = allNames.length;
55
+ const byBucket = new Map();
56
+ for (const n of allNames) {
57
+ const b = bucketOf(n);
58
+ if (!byBucket.has(b))
59
+ byBucket.set(b, []);
60
+ byBucket.get(b).push(n);
61
+ }
62
+ const per = Math.max(1, opts.perBucket ?? 14);
63
+ // 哪些桶要"展开列出", 哪些只给名字(折叠)
64
+ const expanded = new Set();
65
+ if (!opts.subset) {
66
+ for (const b of byBucket.keys())
67
+ expanded.add(b);
68
+ }
69
+ else {
70
+ for (const b of exports.CORE_BUCKETS)
71
+ expanded.add(b);
72
+ for (const [re, b] of INTENT_BUCKETS)
73
+ if (re.test(intentText))
74
+ expanded.add(b);
75
+ }
76
+ const lines = [];
77
+ let shown = 0;
78
+ const foldedBuckets = [];
79
+ for (const [b, names] of byBucket) {
80
+ if (!expanded.has(b)) {
81
+ foldedBuckets.push(`${b}(${names.length})`);
82
+ continue;
83
+ }
84
+ const head = names.slice(0, per).sort();
85
+ shown += head.length;
86
+ const rest = names.length - head.length;
87
+ lines.push(`【${b}】${head.join(' ')}${rest > 0 ? ` …还有${rest}个` : ''}`);
88
+ }
89
+ const folded = foldedBuckets.length ? `未列出的分类: ${foldedBuckets.join(' ')}` : '';
90
+ const footer = [
91
+ '**没找到合适的工具 ⇒ 用 `list_tools {keyword:"…"}` 查**; 未列出的工具也**可以直接按名字调用**(名字对了就能跑)。',
92
+ folded,
93
+ ].filter(Boolean).join('\n');
94
+ return { text: [lines.join('\n'), footer].filter(Boolean).join('\n'), shown, total };
95
+ }
96
+ /**
97
+ * 带参数名的完整清单 (= `getToolDefinitions` 真正注入的那份)。
98
+ * 单独抽出来是为了**可测**: 之前这个拼装写在 pi-sdk 里(私有方法 ✗ 测不到), 于是"渲染得对不对"只能靠眼看。
99
+ */
100
+ function renderToolListWithParams(tools, intentText = '', opts = {}) {
101
+ const paramOf = new Map(tools.map((t) => [t.name, Object.keys(t.parameters || {}).join(',')]));
102
+ const grouped = renderToolList(tools.map((t) => t.name), intentText, opts);
103
+ const body = grouped.text.split('\n').map((line) => {
104
+ if (!line.startsWith('【'))
105
+ return line;
106
+ const m = /^【([^】]+)】(.*)$/.exec(line);
107
+ if (!m)
108
+ return line;
109
+ const [, bucket, rest] = m;
110
+ const more = rest.includes('…') ? rest.slice(rest.indexOf('…')) : '';
111
+ const names = rest.replace(/….*$/, '').trim().split(/\s+/).filter(Boolean);
112
+ return `【${bucket}】` + names.map((n) => `${n}(${paramOf.get(n) ?? ''})`).join(' ') + (more ? ` ${more}` : '');
113
+ }).join('\n');
114
+ return { text: body, shown: grouped.shown, total: grouped.total };
115
+ }
116
+ //# sourceMappingURL=tool-subset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-subset.js","sourceRoot":"","sources":["../../../src/agents/tool-subset.ts"],"names":[],"mappings":"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"}