@bolloon/bolloon-agent 0.1.35 → 0.1.36

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 (194) hide show
  1. package/.playwright-mcp/page-2026-06-16T07-51-45-706Z.yml +130 -0
  2. package/.playwright-mcp/page-2026-06-16T07-56-44-116Z.yml +131 -0
  3. package/Bolloon.md +45 -4
  4. package/Dive-into/CITATION.cff +17 -0
  5. package/Dive-into/LICENSE +25 -0
  6. package/Dive-into/README.md +598 -0
  7. package/Dive-into/README_zh.md +599 -0
  8. package/Dive-into/assets/context.png +0 -0
  9. package/Dive-into/assets/extensibility.png +0 -0
  10. package/Dive-into/assets/iteration.png +0 -0
  11. package/Dive-into/assets/layered_architecture.png +0 -0
  12. package/Dive-into/assets/main_structure.png +0 -0
  13. package/Dive-into/assets/permission.png +0 -0
  14. package/Dive-into/assets/session_compact.png +0 -0
  15. package/Dive-into/assets/subagent.png +0 -0
  16. package/Dive-into/paper/Dive_into_Claude_Code.pdf +0 -0
  17. package/README.md +1 -1
  18. package/dist/agents/p2p-chat-tools.js +6 -6
  19. package/dist/agents/permission-mode.js +115 -0
  20. package/dist/agents/pi-sdk.js +435 -25
  21. package/dist/agents/pre-tool-validator.js +194 -0
  22. package/dist/agents/workflow-pivot-loop.js +113 -12
  23. package/dist/bollharness/src/scripts/checks/check_doc_freshness.js +1 -1
  24. package/dist/bollharness/src/scripts/checks/check_doc_links.js +1 -1
  25. package/dist/bollharness/src/scripts/context_router.js +1 -1
  26. package/dist/bollharness/src/scripts/deploy-guard.js +1 -1
  27. package/dist/bollharness/src/scripts/guard_router.js +1 -1
  28. package/dist/bollharness/src/scripts/hooks/_hook_output.js +1 -1
  29. package/dist/bollharness/src/scripts/hooks/risk-tracker.js +1 -1
  30. package/dist/bollharness-integration/context-router.js +2 -2
  31. package/dist/bollharness-integration/guard-checker.js +1 -1
  32. package/dist/bootstrap/bootstrap.js +3 -0
  33. package/dist/bootstrap/context-collector.js +17 -1
  34. package/dist/bootstrap/context-hierarchy.js +218 -0
  35. package/dist/bootstrap/lifecycle-hooks.js +86 -24
  36. package/dist/bootstrap/project-context.js +10 -3
  37. package/dist/context-compaction/auto-compact.js +144 -0
  38. package/dist/context-compaction/budget-gate.js +28 -0
  39. package/dist/context-compaction/budget-reduce.js +35 -0
  40. package/dist/context-compaction/context-collapse.js +66 -0
  41. package/dist/context-compaction/index.js +21 -0
  42. package/dist/context-compaction/microcompact.js +51 -0
  43. package/dist/context-compaction/pipeline.js +123 -0
  44. package/dist/context-compaction/snip.js +45 -0
  45. package/dist/context-compaction/token-estimator.js +35 -0
  46. package/dist/context-compaction/types.js +19 -0
  47. package/dist/heartbeat/HealthMonitor.js +3 -2
  48. package/dist/index.js +38 -2
  49. package/dist/llm/llm-judgment-client.js +32 -30
  50. package/dist/llm/pi-ai.js +103 -16
  51. package/dist/llm/system-prompt/health.js +129 -0
  52. package/dist/llm/system-prompt/registry.js +246 -0
  53. package/dist/llm/system-prompt/strip-hibsml.js +51 -0
  54. package/dist/llm/tool-manifest/ask_user_input.js +35 -0
  55. package/dist/llm/tool-manifest/bash.js +23 -0
  56. package/dist/llm/tool-manifest/create_file.js +24 -0
  57. package/dist/llm/tool-manifest/fetch_sports_data.js +26 -0
  58. package/dist/llm/tool-manifest/image_search.js +24 -0
  59. package/dist/llm/tool-manifest/index.js +69 -0
  60. package/dist/llm/tool-manifest/mcp.js +43 -0
  61. package/dist/llm/tool-manifest/message_compose.js +31 -0
  62. package/dist/llm/tool-manifest/places.js +83 -0
  63. package/dist/llm/tool-manifest/present_files.js +21 -0
  64. package/dist/llm/tool-manifest/recipe.js +40 -0
  65. package/dist/llm/tool-manifest/recommend_apps.js +23 -0
  66. package/dist/llm/tool-manifest/str_replace.js +27 -0
  67. package/dist/llm/tool-manifest/types.js +7 -0
  68. package/dist/llm/tool-manifest/view.js +24 -0
  69. package/dist/llm/tool-manifest/weather.js +27 -0
  70. package/dist/llm/tool-manifest/web.js +51 -0
  71. package/dist/network/p2p-direct.js +23 -0
  72. package/dist/network/source-intent-broadcaster.js +203 -0
  73. package/dist/network/source-intent.js +100 -0
  74. package/dist/pi-ecosystem-judgment/adaptive-scan.js +48 -0
  75. package/dist/pi-ecosystem-judgment/injection-gate.js +54 -12
  76. package/dist/pi-ecosystem-judgment/value-injection.js +8 -2
  77. package/dist/security/context-router-tool.js +1 -1
  78. package/dist/security/input-scanner.js +223 -0
  79. package/dist/web/client.js +3677 -4161
  80. package/dist/web/components/p2p/P2PModal.js +188 -0
  81. package/dist/web/components/p2p/index.js +264 -226
  82. package/dist/web/components/p2p/p2p-modal.js +657 -0
  83. package/dist/web/components/p2p/p2p-tools.js +248 -0
  84. package/dist/web/index.html +58 -67
  85. package/dist/web/server.js +299 -117
  86. package/dist/web/style.css +531 -249
  87. package/dist/web/ui/message-renderer.js +463 -0
  88. package/dist/web/ui/step-timeline.js +375 -0
  89. package/lefthook.yml +4 -0
  90. package/package.json +1 -1
  91. package/scripts/auto-evolve-loop.ts +105 -0
  92. package/scripts/build-web.ts +35 -1
  93. package/scripts/validate-system-prompt.ts +142 -0
  94. package/src/agents/p2p-chat-tools.ts +6 -6
  95. package/src/agents/permission-mode.ts +127 -0
  96. package/src/agents/pi-sdk.ts +461 -28
  97. package/src/agents/pre-tool-validator.ts +213 -0
  98. package/src/agents/workflow-pivot-loop.ts +110 -19
  99. package/src/bollharness/CLAUDE.md +1 -1
  100. package/src/bollharness/README.md +2 -2
  101. package/src/bollharness/README.zh-CN.md +2 -2
  102. package/src/bollharness/reference/boll-reference/scripts/hooks/find-boll-root.sh +2 -2
  103. package/src/bollharness/scripts/context-fragments/truth-source-hierarchy.md +2 -2
  104. package/src/bollharness/scripts/context-fragments/version-sources.md +1 -1
  105. package/src/bollharness/scripts/hooks/find-project-root.sh +4 -4
  106. package/src/bollharness/src/scripts/checks/check_doc_freshness.ts +1 -1
  107. package/src/bollharness/src/scripts/checks/check_doc_links.ts +1 -1
  108. package/src/bollharness/src/scripts/context_router.ts +1 -1
  109. package/src/bollharness/src/scripts/deploy-guard.ts +1 -1
  110. package/src/bollharness/src/scripts/guard_router.ts +1 -1
  111. package/src/bollharness/src/scripts/hooks/_hook_output.ts +1 -1
  112. package/src/bollharness/src/scripts/hooks/risk-tracker.ts +1 -1
  113. package/src/bollharness/templates/scaffold/.gitignore.append +1 -1
  114. package/src/bollharness/templates/scaffold/CLAUDE.md +2 -2
  115. package/src/bollharness-integration/context-router.ts +2 -2
  116. package/src/bollharness-integration/guard-checker.ts +1 -1
  117. package/src/bootstrap/bootstrap.ts +3 -0
  118. package/src/bootstrap/context-collector.ts +30 -1
  119. package/src/bootstrap/context-hierarchy.ts +283 -0
  120. package/src/bootstrap/lifecycle-hooks.ts +139 -26
  121. package/src/bootstrap/project-context.ts +11 -3
  122. package/src/context-compaction/auto-compact.ts +153 -0
  123. package/src/context-compaction/budget-gate.ts +32 -0
  124. package/src/context-compaction/budget-reduce.ts +37 -0
  125. package/src/context-compaction/context-collapse.ts +72 -0
  126. package/src/context-compaction/index.ts +24 -0
  127. package/src/context-compaction/microcompact.ts +54 -0
  128. package/src/context-compaction/pipeline.ts +133 -0
  129. package/src/context-compaction/snip.ts +51 -0
  130. package/src/context-compaction/token-estimator.ts +36 -0
  131. package/src/context-compaction/types.ts +99 -0
  132. package/src/heartbeat/HealthMonitor.ts +3 -2
  133. package/src/index.ts +36 -2
  134. package/src/llm/llm-judgment-client.ts +36 -35
  135. package/src/llm/pi-ai.ts +111 -16
  136. package/src/llm/system-prompt/health.ts +159 -0
  137. package/src/llm/system-prompt/layers/channel/local.md +14 -0
  138. package/src/llm/system-prompt/layers/channel/p2p-agent.md +18 -0
  139. package/src/llm/system-prompt/layers/channel/p2p-visitor.md +19 -0
  140. package/src/llm/system-prompt/layers/core/artifacts_storage.md +89 -0
  141. package/src/llm/system-prompt/layers/core/evenhandedness.md +21 -0
  142. package/src/llm/system-prompt/layers/core/hibs_reminders.md +15 -0
  143. package/src/llm/system-prompt/layers/core/identity.md +37 -0
  144. package/src/llm/system-prompt/layers/core/knowledge.md +17 -0
  145. package/src/llm/system-prompt/layers/core/memory_system.md +12 -0
  146. package/src/llm/system-prompt/layers/core/network_filesystem.md +28 -0
  147. package/src/llm/system-prompt/layers/core/refusal.md +37 -0
  148. package/src/llm/system-prompt/layers/core/tone.md +31 -0
  149. package/src/llm/system-prompt/layers/core/tools.thin.md +13 -0
  150. package/src/llm/system-prompt/layers/core/wellbeing.md +41 -0
  151. package/src/llm/system-prompt/layers/role/architect.md +20 -0
  152. package/src/llm/system-prompt/layers/role/expert.md +19 -0
  153. package/src/llm/system-prompt/layers/role/implementer.md +15 -0
  154. package/src/llm/system-prompt/layers/role/security.md +15 -0
  155. package/src/llm/system-prompt/layers/tool/artifacts.md +72 -0
  156. package/src/llm/system-prompt/layers/tool/bash.md +25 -0
  157. package/src/llm/system-prompt/layers/tool/hibs_api.md +171 -0
  158. package/src/llm/system-prompt/layers/tool/image_search.md +70 -0
  159. package/src/llm/system-prompt/layers/tool/manifest.md +89 -0
  160. package/src/llm/system-prompt/layers/tool/mcp_apps.md +53 -0
  161. package/src/llm/system-prompt/layers/tool/web_search.md +83 -0
  162. package/src/llm/system-prompt/registry.ts +325 -0
  163. package/src/llm/system-prompt/strip-hibsml.ts +52 -0
  164. package/src/llm/tool-manifest/ask_user_input.ts +37 -0
  165. package/src/llm/tool-manifest/bash.ts +25 -0
  166. package/src/llm/tool-manifest/create_file.ts +26 -0
  167. package/src/llm/tool-manifest/fetch_sports_data.ts +28 -0
  168. package/src/llm/tool-manifest/image_search.ts +26 -0
  169. package/src/llm/tool-manifest/index.ts +88 -0
  170. package/src/llm/tool-manifest/mcp.ts +46 -0
  171. package/src/llm/tool-manifest/message_compose.ts +33 -0
  172. package/src/llm/tool-manifest/places.ts +86 -0
  173. package/src/llm/tool-manifest/present_files.ts +23 -0
  174. package/src/llm/tool-manifest/recipe.ts +42 -0
  175. package/src/llm/tool-manifest/recommend_apps.ts +25 -0
  176. package/src/llm/tool-manifest/str_replace.ts +29 -0
  177. package/src/llm/tool-manifest/types.ts +52 -0
  178. package/src/llm/tool-manifest/view.ts +26 -0
  179. package/src/llm/tool-manifest/weather.ts +29 -0
  180. package/src/llm/tool-manifest/web.ts +54 -0
  181. package/src/network/p2p-direct.ts +22 -0
  182. package/src/network/source-intent-broadcaster.ts +242 -0
  183. package/src/network/source-intent.ts +167 -0
  184. package/src/security/context-router-tool.ts +1 -1
  185. package/src/security/input-scanner.ts +287 -0
  186. package/src/web/client.js +271 -2817
  187. package/src/web/client.ts +4326 -0
  188. package/src/web/index.html +58 -67
  189. package/src/web/server.ts +301 -141
  190. package/src/web/style.css +531 -249
  191. package/src/web/ui/message-renderer.ts +540 -0
  192. package/src/web/ui/step-timeline.ts +394 -0
  193. package/test-results/.last-run.json +6 -0
  194. package/test-results/src-test-web-loop-status-b-5734c-atus-bar-/346/230/276/347/244/272-spinner-/344/270/215/346/230/276/347/244/272/345/276/252/347/216/257/350/256/241/346/225/260/error-context.md +285 -0
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Pre-Tool Validator — 4 步链式校验
3
+ *
4
+ * 严格对齐 Claude Code 论文 PreToolUse 4 步:
5
+ * 1. modeGate(tool, mode) — bypassPermissions → 直接放行(除 shell)
6
+ * 2. blacklistGate(tool, args) — 6 模式黑名单 (recursive rm, force push, dd 等)
7
+ * 3. shellGuardGate(tool, args) — **完整** shell-guard (checkCommand + checkWritePath), 永远生效
8
+ * 4. schemaGate(tool, args) — 第一版 stub: always allow (后续接入 tool-gate.ts)
9
+ *
10
+ * 每步独立 try/catch, 失败 = 跳过该步, 不阻塞 (与现有 lifecycle-hooks 风格一致)
11
+ *
12
+ * 失败静默: 任何 step 异常 → console.warn + 继续下一 step, 不抛错
13
+ */
14
+ import { isShellTool } from './permission-mode.js';
15
+ import { checkCommand, checkWritePath } from './shell-guard.js';
16
+ const DANGEROUS_PATTERNS = [
17
+ { re: /\brm\s+(-[a-z]*f[a-z]*\s+)?-[a-z]*r[a-z]*\s+\//, reason: '禁止递归删除根目录' },
18
+ { re: /\bgit\s+push\s+.*--force\b/, reason: '禁止 force push' },
19
+ { re: /\brm\s+-rf\s+~\//, reason: '禁止递归删除 home' },
20
+ { re: /\bdd\s+if=.*\s+of=\/dev\//, reason: '禁止 dd 覆盖块设备' },
21
+ { re: /\bcurl\s+.*\|\s*(ba)?sh\b/, reason: '禁止 curl|sh 直执行' },
22
+ { re: />\s*\/dev\/sd[a-z]/, reason: '禁止写裸设备' },
23
+ ];
24
+ /**
25
+ * Step 1: modeGate
26
+ * - bypassPermissions + 非 shell → 直接放行 (但保留 shellGuardRetained 标记)
27
+ * - 其他 → 继续
28
+ */
29
+ function modeGate(tool, mode) {
30
+ try {
31
+ if (mode === 'bypassPermissions' && !isShellTool(tool)) {
32
+ return { allowed: true, mode, rejectedBy: undefined, shellGuardRetained: false };
33
+ }
34
+ return null; // 不在这一步决定
35
+ }
36
+ catch (err) {
37
+ console.warn('[pre-tool] modeGate failed (silent):', err);
38
+ return null;
39
+ }
40
+ }
41
+ /**
42
+ * Step 2: blacklistGate
43
+ * 6 模式黑名单 (现有 lifecycle-hooks.ts 行为)
44
+ */
45
+ function blacklistGate(tool, args, mode) {
46
+ try {
47
+ if (!isShellTool(tool))
48
+ return null;
49
+ const cmd = String(args.command || args.cmd || '');
50
+ if (!cmd)
51
+ return null;
52
+ for (const { re, reason } of DANGEROUS_PATTERNS) {
53
+ if (re.test(cmd)) {
54
+ return { allowed: false, reason, rejectedBy: 'blacklist', mode };
55
+ }
56
+ }
57
+ return null;
58
+ }
59
+ catch (err) {
60
+ console.warn('[pre-tool] blacklistGate failed (silent):', err);
61
+ return null;
62
+ }
63
+ }
64
+ /**
65
+ * Step 3: shellGuardGate
66
+ * 永远跑 (绕过 mode), 是 bolloon 自身的安全底线
67
+ *
68
+ * 接完整 shell-guard.ts (checkCommand + checkWritePath):
69
+ * - checkCommand 走命令白名单 + 参数黑名单 (允许列表默认 git/node/npm/npx/tsx/tsc/vitest 等)
70
+ * - checkWritePath 走路径黑名单 (硬编码: pi-sdk.ts, shell-guard.ts, .env, .git/, .bolloon/, package.json, dist/ 等)
71
+ *
72
+ * 失败静默: 任何异常 → 跳过这步, 不阻塞
73
+ */
74
+ function shellGuardGate(tool, args, mode) {
75
+ try {
76
+ if (!isShellTool(tool))
77
+ return null;
78
+ // 1. 命令检查 (含白名单 + arg 黑名单)
79
+ const cmd = String(args.command || args.cmd || '');
80
+ let bin = '';
81
+ if (cmd) {
82
+ // shell-guard 期望 (cmd, args[]) 形式
83
+ // 简单 split: 第一个 token 是 cmd, 之后是 args
84
+ const parts = cmd.split(/\s+/).filter(Boolean);
85
+ bin = parts[0] || '';
86
+ const rest = parts.slice(1);
87
+ if (bin) {
88
+ const cmdResult = checkCommand(bin, rest);
89
+ if (!cmdResult.allowed) {
90
+ return {
91
+ allowed: false,
92
+ reason: cmdResult.reason,
93
+ rejectedBy: 'shell-guard',
94
+ mode,
95
+ shellGuardRetained: true,
96
+ };
97
+ }
98
+ }
99
+ }
100
+ // 2. 写路径检查 — 从 2 个来源抽:
101
+ // a) 结构化字段 (args.path / args.target / args.file)
102
+ // b) 命令字符串里的 token (老测试传 args.command='cat pi-sdk.ts' 时也能拦)
103
+ const pathCandidates = [];
104
+ const structured = String(args.path || args.target || args.file || args.targetPath || args.destination || '');
105
+ if (structured)
106
+ pathCandidates.push(structured);
107
+ if (cmd) {
108
+ // 简单 tokenize: 按空白切, 取不含 shell 元字符的 token
109
+ const tokens = cmd.split(/\s+/).filter((t) => t && !/^[|>;&`$()<]/.test(t));
110
+ for (const t of tokens) {
111
+ if (t !== bin)
112
+ pathCandidates.push(t); // 跳过命令本身
113
+ }
114
+ }
115
+ // 纯文件名 denylist (兜底 — checkWritePath 的 regex 要求 /src/xxx/ 锚定, 纯文件名拦不到)
116
+ // 只对**写/删/改**类命令生效 (rm, cp, mv, sed -i, echo >); 读类命令 (cat, head, tail, ls) 跳过
117
+ const READ_BINS = new Set(['cat', 'head', 'tail', 'wc', 'ls', 'echo', 'pwd', 'date', 'mkdir', 'touch', 'find', 'grep', 'git', 'node', 'npm', 'npx', 'tsx', 'tsc', 'vitest']);
118
+ const FILENAME_DENYLIST = [
119
+ 'pi-sdk.ts', 'shell-guard.ts', 'shell-tool.ts',
120
+ '.env', 'package.json', 'tsconfig.json',
121
+ ];
122
+ const isReadOperation = READ_BINS.has(bin);
123
+ for (const candidate of pathCandidates) {
124
+ // 2a. checkWritePath 只对写操作调用 (读类命令绕开, 避免 cat pi-sdk.ts 误拦)
125
+ if (!isReadOperation) {
126
+ const pathResult = checkWritePath(candidate);
127
+ if (!pathResult.allowed) {
128
+ return {
129
+ allowed: false,
130
+ reason: pathResult.reason,
131
+ rejectedBy: 'shell-guard',
132
+ mode,
133
+ shellGuardRetained: true,
134
+ };
135
+ }
136
+ }
137
+ // 2b. 纯文件名兜底 — 只对写操作生效
138
+ if (!isReadOperation) {
139
+ const baseName = candidate.split('/').pop() || '';
140
+ if (FILENAME_DENYLIST.includes(baseName)) {
141
+ return {
142
+ allowed: false,
143
+ reason: `shell-guard: 禁止 ${bin} 操作 ${candidate} (命中文件名硬编码黑名单)`,
144
+ rejectedBy: 'shell-guard',
145
+ mode,
146
+ shellGuardRetained: true,
147
+ };
148
+ }
149
+ }
150
+ }
151
+ return null; // 通过
152
+ }
153
+ catch (err) {
154
+ console.warn('[pre-tool] shellGuardGate failed (silent):', err);
155
+ return null;
156
+ }
157
+ }
158
+ /**
159
+ * Step 4: schemaGate
160
+ * 第一版 stub: always allow
161
+ */
162
+ function schemaGate(_tool, _args, mode) {
163
+ return null; // 不在这一步决定
164
+ }
165
+ /**
166
+ * 主入口: 4 步链式, 任何一步拒绝 → 返回, 否则最终放行
167
+ */
168
+ export function validatePreToolUse(tool, args, mode) {
169
+ const steps = [
170
+ () => modeGate(tool, mode),
171
+ () => blacklistGate(tool, args, mode),
172
+ () => shellGuardGate(tool, args, mode),
173
+ () => schemaGate(tool, args, mode),
174
+ ];
175
+ for (const step of steps) {
176
+ const result = step();
177
+ if (result && !result.allowed)
178
+ return result;
179
+ }
180
+ // 全部通过 → 放行
181
+ // bypassPermissions + shell 走到这里 = shellGuardGate 已经放过, 但要标记 shellGuardRetained
182
+ return {
183
+ allowed: true,
184
+ mode,
185
+ rejectedBy: undefined,
186
+ shellGuardRetained: isShellTool(tool),
187
+ };
188
+ }
189
+ // ============================================================
190
+ // 测试钩子
191
+ // ============================================================
192
+ export function _resetValidatorForTest() {
193
+ // validator 是 pure, 保留 API 一致
194
+ }
@@ -208,6 +208,19 @@ export class WorkflowPivotLoop {
208
208
  type: 'error',
209
209
  content: `❌ 未知工具: ${toolCall.name}`
210
210
  });
211
+ // 2026-06-15: step-timeline — 未知工具也开/关一个 step 节点
212
+ this.emit({
213
+ type: 'step_start',
214
+ content: `未知工具 ${toolCall.name}`,
215
+ tool: toolCall.name,
216
+ args: toolCall.args || {},
217
+ });
218
+ this.emit({
219
+ type: 'step_error',
220
+ content: `未知工具 ${toolCall.name}`,
221
+ tool: toolCall.name,
222
+ error: 'Unknown tool',
223
+ });
211
224
  this.messageHistory.push({
212
225
  role: 'tool',
213
226
  content: JSON.stringify({ success: false, error: `Unknown tool: ${toolCall.name}` })
@@ -219,8 +232,26 @@ export class WorkflowPivotLoop {
219
232
  content: `🔧 执行: ${toolCall.name}`,
220
233
  tool: toolCall.name
221
234
  });
235
+ // 2026-06-15: step-timeline — 开节点
236
+ this.emit({
237
+ type: 'step_start',
238
+ content: `调用 ${toolCall.name}`,
239
+ tool: toolCall.name,
240
+ args: toolCall.args || {},
241
+ });
222
242
  try {
223
243
  const result = await tool.execute(toolCall.args ?? {});
244
+ // 2026-06-15: step-timeline — 关闭节点 (success / error)
245
+ this.emit({
246
+ type: result.success ? 'step_done' : 'step_error',
247
+ content: result.success
248
+ ? `${toolCall.name} 成功`
249
+ : `${toolCall.name} 失败: ${result.error}`,
250
+ tool: toolCall.name,
251
+ success: result.success,
252
+ output: result.output,
253
+ error: result.error,
254
+ });
224
255
  this.emit({
225
256
  type: result.success ? 'status' : 'error',
226
257
  content: result.success
@@ -305,9 +336,49 @@ export class WorkflowPivotLoop {
305
336
  */
306
337
  extractPendingToolUses(content) {
307
338
  const pending = [];
339
+ // Pattern 0: <tool_use>{...JSON...}</tool_use> (Anthropic 风格 + minimax 也用)
340
+ // 这次 LLM 输出: <tool_use>\n{"name": "read_document", "arguments": {"path": "/Users/.../README.md"}}\n</tool_use>
341
+ const toolUseRe = /<tool_use>\s*(\{[\s\S]*?\})\s*<\/tool_use>/g;
342
+ let match;
343
+ while ((match = toolUseRe.exec(content)) !== null) {
344
+ try {
345
+ const obj = JSON.parse(match[1]);
346
+ if (obj && obj.name && this.tools.has(obj.name)) {
347
+ const args = this.normalizeArgs(obj.arguments || {});
348
+ pending.push({ name: obj.name, args, description: '', parameters: {} });
349
+ }
350
+ }
351
+ catch (e) {
352
+ // JSON 解析失败, 继续下一 match
353
+ }
354
+ }
355
+ // Pattern 0b: <function_calls><invoke name="X"><parameter name="k">v</parameter>...</invoke></function_calls>
356
+ // 这次 minimax LLM 用这种 Anthropic 风格 XML
357
+ const fnCallsRe = /<function_calls>([\s\S]*?)<\/function_calls>/g;
358
+ while ((match = fnCallsRe.exec(content)) !== null) {
359
+ const block = match[1];
360
+ // 抓 <invoke name="X">...</invoke>
361
+ const invokeRe = /<invoke\s+name="(\w+)"\s*>([\s\S]*?)<\/invoke>/g;
362
+ let im;
363
+ while ((im = invokeRe.exec(block)) !== null) {
364
+ const name = im[1];
365
+ if (!this.tools.has(name))
366
+ continue;
367
+ // 抓 <parameter name="k">v</parameter> 列表
368
+ const args = {};
369
+ const paramRe = /<parameter\s+name="(\w+)"\s*>([\s\S]*?)<\/parameter>/g;
370
+ let pm;
371
+ while ((pm = paramRe.exec(im[2])) !== null) {
372
+ args[pm[1]] = pm[2].trim().replace(/^["']|['"]$/g, '');
373
+ }
374
+ // 避免重复添加
375
+ if (!pending.some(p => p.name === name)) {
376
+ pending.push({ name, args, description: '', parameters: {} });
377
+ }
378
+ }
379
+ }
308
380
  // Pattern 1: Chinese format "调用工具: tool_name(args)"
309
381
  const pattern1 = /调用工具[::]\s*(\w+)\s*\(([^)]*)\)/g;
310
- let match;
311
382
  while ((match = pattern1.exec(content)) !== null) {
312
383
  const name = match[1];
313
384
  const argsStr = match[2];
@@ -357,22 +428,52 @@ export class WorkflowPivotLoop {
357
428
  return args;
358
429
  const pairs = argsStr.split(',').map(s => s.trim()).filter(Boolean);
359
430
  for (const pair of pairs) {
360
- const colonIdx = pair.indexOf(':');
361
- if (colonIdx > 0) {
362
- const key = pair.substring(0, colonIdx).trim();
363
- const value = pair.substring(colonIdx + 1).trim().replace(/^['"]|['"]$/g, '');
364
- args[key] = value;
431
+ // 2026-06-15: LLM 实际输出 3 种格式 — 全支持
432
+ // 1) JSON 风格: {"key":"value"} (服务器日志显示, LLM 不会真输出完整 JSON)
433
+ // 2) key="value" 含双引号 (本次 read_document(path="/Users/..."))
434
+ // 3) key='value' 含单引号
435
+ // 4) key:value (老 Chinese 格式)
436
+ // 5) key value (positional 兜底)
437
+ let m = pair.match(/^["']?([\w-]+)["']?\s*=\s*["']([^"']*)["']$/);
438
+ if (m) {
439
+ args[m[1]] = m[2];
440
+ continue;
365
441
  }
366
- else {
367
- // No colon, try to parse as positional
368
- const parts = pair.split(/\s+/);
369
- if (parts.length >= 2) {
370
- args[parts[0]] = parts.slice(1).join(' ');
371
- }
442
+ m = pair.match(/^["']?([\w-]+)["']?\s*[:=]\s*([^,]+)$/);
443
+ if (m) {
444
+ args[m[1]] = m[2].trim().replace(/^['"]|['"]$/g, '');
445
+ continue;
446
+ }
447
+ m = pair.match(/^["']?([\w-]+)["']?\s*[:]\s*["']?([^"']*)["']?$/);
448
+ if (m) {
449
+ args[m[1]] = m[2].trim();
450
+ continue;
451
+ }
452
+ // positional 兜底
453
+ const parts = pair.split(/\s+/);
454
+ if (parts.length >= 2) {
455
+ args[parts[0]] = parts.slice(1).join(' ');
372
456
  }
373
457
  }
374
458
  return args;
375
459
  }
460
+ /**
461
+ * 把 tool_use JSON 里的 arguments (已经是对象) 转成 Record<string, string>
462
+ * JSON parser 直接给对象, 但 tool.execute 期望 Record<string, string>
463
+ * 非字符串值 JSON.stringify 一下
464
+ */
465
+ normalizeArgs(args) {
466
+ const out = {};
467
+ for (const [k, v] of Object.entries(args || {})) {
468
+ if (v == null)
469
+ continue;
470
+ if (typeof v === 'string')
471
+ out[k] = v;
472
+ else
473
+ out[k] = JSON.stringify(v);
474
+ }
475
+ return out;
476
+ }
376
477
  /**
377
478
  * Build context from message history
378
479
  */
@@ -74,7 +74,7 @@ export function run(repoRoot, mode = "full") {
74
74
  const findings = [];
75
75
  const actualRoutes = countRouteDecorators(repoRoot);
76
76
  const routesDoc = path.join(repoRoot, ".boll", "rules", "backend-routes.md");
77
- const claudeMd = path.join(repoRoot, "CLAUDE.md");
77
+ const claudeMd = path.join(repoRoot, "Bolloon.md");
78
78
  if (fs.existsSync(routesDoc)) {
79
79
  const routesContent = fs.readFileSync(routesDoc, "utf-8");
80
80
  const routeClaims = routesContent.match(/-\s+`(?:GET|POST|PUT|PATCH|DELETE|WS)\s+/g) || [];
@@ -2,7 +2,7 @@ import * as fs from "fs";
2
2
  import * as path from "path";
3
3
  export function run(repoRoot, mode = "full") {
4
4
  const findings = [];
5
- const docFiles = ["CLAUDE.md", "docs/ROADMAP.md", "README.md"];
5
+ const docFiles = ["Bolloon.md", "docs/ROADMAP.md", "README.md"];
6
6
  const maxAgeDays = 7;
7
7
  for (const docFile of docFiles) {
8
8
  const fullPath = path.join(repoRoot, docFile);
@@ -17,7 +17,7 @@ export const CONTEXT_MAP = {
17
17
  "scenes/": ["scene-fidelity", "two-language"],
18
18
  "website/app/[scene]/": ["scene-fidelity", "two-language"],
19
19
  "website/components/scene/": ["scene-fidelity", "two-language"],
20
- "CLAUDE.md": ["truth-source-hierarchy"],
20
+ "Bolloon.md": ["truth-source-hierarchy"],
21
21
  "MEMORY.md": ["truth-source-hierarchy"],
22
22
  "docs/INDEX.md": ["truth-source-hierarchy"],
23
23
  "mcp-server/pyproject.toml": ["version-sources"],
@@ -109,7 +109,7 @@ function block(reason, host = null) {
109
109
  Demo 正式: bash scripts/deploy-demo.sh <name> --channel prod --yes
110
110
  Demo 内测: bash scripts/deploy-demo.sh <name> --channel preview --yes
111
111
  Edge/Nginx: bash scripts/deploy-edge.sh --yes
112
- 详见 CLAUDE.md Development Commands。`;
112
+ 详见 Bolloon.md Development Commands。`;
113
113
  }
114
114
  else if (BRIDGE_VPS_HOSTS.includes(host ?? "")) {
115
115
  guidance = `Bridge VPS 必须走 git pull 更新路径:
@@ -8,7 +8,7 @@ export const GUARD_MAP = {
8
8
  "docs/issues/": ["check_issue_closure"],
9
9
  "backend/product/routes/": ["check_doc_freshness"],
10
10
  "docs/ROADMAP.md": ["check_doc_freshness"],
11
- "CLAUDE.md": ["check_doc_freshness"],
11
+ "Bolloon.md": ["check_doc_freshness"],
12
12
  "docs/magic/": ["check_doc_freshness"],
13
13
  ".boll/rules/backend-routes.md": ["check_doc_freshness"],
14
14
  ".boll/settings.json": ["check_hook_installed"],
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  process.stdout.write(`\n\n## Bollharness Context\n\n` +
3
3
  `bollharness is an AI Agent Session Governance Framework.\n\n` +
4
- `For more information, see CLAUDE.md in the project root.\n`);
4
+ `For more information, see Bolloon.md in the project root.\n`);
5
5
  process.exit(0);
6
6
  export {};
@@ -6,7 +6,7 @@ const RISK_ORDER = { R0: 0, R1: 1, R2: 2, R3: 3, R4: 4 };
6
6
  const RISK_ELEVATORS = [
7
7
  ["scripts/deploy", "R4"],
8
8
  ["backend/product/db/migration", "R4"],
9
- ["CLAUDE.md", "R3"],
9
+ ["Bolloon.md", "R3"],
10
10
  [".boll/settings.json", "R3"],
11
11
  [".boll/skills/", "R3"],
12
12
  [".boll/rules/", "R3"],
@@ -25,7 +25,7 @@ export const CONTEXT_MAP = {
25
25
  'src/bollharness-integration/': ['truth-source-hierarchy', 'artifact-linkage'],
26
26
  'docs/': ['truth-source-hierarchy', 'two-language'],
27
27
  'docs/decisions/': ['truth-source-hierarchy', 'artifact-linkage'],
28
- 'CLAUDE.md': ['truth-source-hierarchy', 'bridge-constitution'],
28
+ 'Bolloon.md': ['truth-source-hierarchy', 'bridge-constitution'],
29
29
  'README.md': ['truth-source-hierarchy', 'general-dev-principles'],
30
30
  };
31
31
  /**
@@ -331,7 +331,7 @@ export const CONTEXT_FRAGMENTS = {
331
331
  'project-governance': `# Project Governance
332
332
 
333
333
  ## Truth Sources
334
- - CLAUDE.md: Project overview and norms
334
+ - Bolloon.md: Project overview and norms
335
335
  - docs/: Architecture and decisions
336
336
  - code/: Implementation
337
337
  - issues/: Tracking and discussions
@@ -36,7 +36,7 @@ export const GUARD_MAP = {
36
36
  'src/constraints/': ['check_api_types', 'check_versions'],
37
37
  'src/social/': ['check_api_types', 'check_versions'],
38
38
  'docs/': ['check_doc_freshness', 'check_doc_links'],
39
- 'CLAUDE.md': ['check_doc_freshness', 'check_artifact_link'],
39
+ 'Bolloon.md': ['check_doc_freshness', 'check_artifact_link'],
40
40
  'README.md': ['check_doc_freshness'],
41
41
  'docs/decisions/': ['check_artifact_link', 'check_versions'],
42
42
  '.boll/': ['check_hook_installed'],
@@ -25,6 +25,7 @@ export async function bootstrapBolloon(opts = {}) {
25
25
  };
26
26
  try {
27
27
  scanResult = await runAdaptiveScan();
28
+ const { suggestionHint } = await import('../pi-ecosystem-judgment/adaptive-scan.js');
28
29
  await logEvolution({
29
30
  ts: new Date().toISOString(),
30
31
  action: 'accept', // 用 accept 表示"系统记录" (跟 reject 区分)
@@ -35,6 +36,7 @@ export async function bootstrapBolloon(opts = {}) {
35
36
  decision: 'Bolloon 启动扫描',
36
37
  reason: `本次启动扫描了 ${scanResult.judgmentsTotal} 条原则, ${scanResult.usageEntriesScanned} 条使用记录, 生成 ${scanResult.suggestions.length} 条建议`,
37
38
  action: 'review',
39
+ hint: suggestionHint('unused', 'review', { usage7d: 0, usage30d: 0, daysSinceLastUse: 0, totalUsage: 0 }),
38
40
  metrics: { usage7d: 0, usage30d: 0, daysSinceLastUse: 0, totalUsage: 0 },
39
41
  scannedAt: scanResult.scannedAt,
40
42
  },
@@ -50,6 +52,7 @@ export async function bootstrapBolloon(opts = {}) {
50
52
  projectRoot: opts.cwd ?? process.cwd(),
51
53
  projectName: 'unknown',
52
54
  bolloonMd: null,
55
+ hierarchy: { managed: null, user: null, project: null, local: null, merged: '' },
53
56
  git: null,
54
57
  persona: null,
55
58
  judgmentsSummary: { total: 0, active: 0, superseded: 0, rejected: 0, topValues: [] },
@@ -57,6 +57,20 @@ async function collectProject(cwd, bolloonMdMaxBytes) {
57
57
  const bolloonMd = await safeReadFile(path.join(cwd, 'Bolloon.md'), bolloonMdMaxBytes);
58
58
  return { projectName, bolloonMd };
59
59
  }
60
+ async function collectHierarchy(cwd) {
61
+ // 动态 import 避免循环依赖
62
+ const { collectHierarchyLayers, mergeHierarchyLayers } = await import('./context-hierarchy.js');
63
+ try {
64
+ const layers = await collectHierarchyLayers({ cwd });
65
+ const merged = mergeHierarchyLayers(layers, { maxChars: DEFAULT_HIERARCHY_MERGED_MAX });
66
+ return { ...layers, merged };
67
+ }
68
+ catch (err) {
69
+ console.warn('[context-collector] collectHierarchy failed (silent):', err);
70
+ return { managed: null, user: null, project: null, local: null, merged: '' };
71
+ }
72
+ }
73
+ const DEFAULT_HIERARCHY_MERGED_MAX = 2000; // 4000 → 2000 (P-Action 4), 跟 context-hierarchy.ts DEFAULT_MERGE_MAX_CHARS 对齐
60
74
  async function collectGit(cwd, limit) {
61
75
  const branchOut = await safeExecFile('git', ['rev-parse', '--abbrev-ref', 'HEAD'], cwd);
62
76
  if (!branchOut)
@@ -255,13 +269,14 @@ async function walkDir(dir, cb) {
255
269
  export async function collectBolloonContext(opts) {
256
270
  const { cwd, bolloonMdMaxBytes = 2000, gitCommitLimit = 5, topValuesLimit = 10, } = opts;
257
271
  // 并行收集 (除 judgments 因为要动态 import + 依赖其它)
258
- const [project, git, persona, skills, env, pending] = await Promise.all([
272
+ const [project, git, persona, skills, env, pending, hierarchy] = await Promise.all([
259
273
  collectProject(cwd, bolloonMdMaxBytes),
260
274
  collectGit(cwd, gitCommitLimit),
261
275
  collectPersona(),
262
276
  collectSkills(),
263
277
  Promise.resolve(collectEnv()),
264
278
  collectPending(opts),
279
+ collectHierarchy(cwd),
265
280
  ]);
266
281
  // judgments 单独调 (内部 import)
267
282
  const judgmentsSummary = await collectJudgmentsSummary(topValuesLimit);
@@ -269,6 +284,7 @@ export async function collectBolloonContext(opts) {
269
284
  projectRoot: cwd,
270
285
  projectName: project.projectName,
271
286
  bolloonMd: project.bolloonMd,
287
+ hierarchy,
272
288
  git,
273
289
  persona,
274
290
  judgmentsSummary,