@bolloon/bolloon-agent 0.1.34 → 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 (226) hide show
  1. package/.auto-evolve-calls +1 -0
  2. package/.last-auto-evolve-baseline +1 -0
  3. package/.playwright-mcp/page-2026-06-16T07-51-45-706Z.yml +130 -0
  4. package/.playwright-mcp/page-2026-06-16T07-56-44-116Z.yml +131 -0
  5. package/Bolloon.md +144 -0
  6. package/Dive-into/CITATION.cff +17 -0
  7. package/Dive-into/LICENSE +25 -0
  8. package/Dive-into/README.md +598 -0
  9. package/Dive-into/README_zh.md +599 -0
  10. package/Dive-into/assets/context.png +0 -0
  11. package/Dive-into/assets/extensibility.png +0 -0
  12. package/Dive-into/assets/iteration.png +0 -0
  13. package/Dive-into/assets/layered_architecture.png +0 -0
  14. package/Dive-into/assets/main_structure.png +0 -0
  15. package/Dive-into/assets/permission.png +0 -0
  16. package/Dive-into/assets/session_compact.png +0 -0
  17. package/Dive-into/assets/subagent.png +0 -0
  18. package/Dive-into/paper/Dive_into_Claude_Code.pdf +0 -0
  19. package/README.md +1 -1
  20. package/dist/agents/p2p-chat-tools.js +6 -6
  21. package/dist/agents/permission-mode.js +115 -0
  22. package/dist/agents/pi-sdk.js +687 -25
  23. package/dist/agents/pre-tool-validator.js +194 -0
  24. package/dist/agents/workflow-pivot-loop.js +113 -12
  25. package/dist/bollharness/src/scripts/checks/check_doc_freshness.js +1 -1
  26. package/dist/bollharness/src/scripts/checks/check_doc_links.js +1 -1
  27. package/dist/bollharness/src/scripts/context_router.js +1 -1
  28. package/dist/bollharness/src/scripts/deploy-guard.js +1 -1
  29. package/dist/bollharness/src/scripts/guard_router.js +1 -1
  30. package/dist/bollharness/src/scripts/hooks/_hook_output.js +1 -1
  31. package/dist/bollharness/src/scripts/hooks/risk-tracker.js +1 -1
  32. package/dist/bollharness-integration/context-router.js +2 -2
  33. package/dist/bollharness-integration/guard-checker.js +1 -1
  34. package/dist/bootstrap/bootstrap.js +117 -0
  35. package/dist/bootstrap/context-collector.js +312 -0
  36. package/dist/bootstrap/context-hierarchy.js +218 -0
  37. package/dist/bootstrap/lifecycle-hooks.js +171 -0
  38. package/dist/bootstrap/project-context.js +158 -0
  39. package/dist/context-compaction/auto-compact.js +144 -0
  40. package/dist/context-compaction/budget-gate.js +28 -0
  41. package/dist/context-compaction/budget-reduce.js +35 -0
  42. package/dist/context-compaction/context-collapse.js +66 -0
  43. package/dist/context-compaction/index.js +21 -0
  44. package/dist/context-compaction/microcompact.js +51 -0
  45. package/dist/context-compaction/pipeline.js +123 -0
  46. package/dist/context-compaction/snip.js +45 -0
  47. package/dist/context-compaction/token-estimator.js +35 -0
  48. package/dist/context-compaction/types.js +19 -0
  49. package/dist/heartbeat/HealthMonitor.js +3 -2
  50. package/dist/index.js +49 -2
  51. package/dist/llm/llm-judgment-client.js +32 -30
  52. package/dist/llm/pi-ai.js +125 -28
  53. package/dist/llm/system-prompt/health.js +129 -0
  54. package/dist/llm/system-prompt/registry.js +246 -0
  55. package/dist/llm/system-prompt/strip-hibsml.js +51 -0
  56. package/dist/llm/tool-manifest/ask_user_input.js +35 -0
  57. package/dist/llm/tool-manifest/bash.js +23 -0
  58. package/dist/llm/tool-manifest/create_file.js +24 -0
  59. package/dist/llm/tool-manifest/fetch_sports_data.js +26 -0
  60. package/dist/llm/tool-manifest/image_search.js +24 -0
  61. package/dist/llm/tool-manifest/index.js +69 -0
  62. package/dist/llm/tool-manifest/mcp.js +43 -0
  63. package/dist/llm/tool-manifest/message_compose.js +31 -0
  64. package/dist/llm/tool-manifest/places.js +83 -0
  65. package/dist/llm/tool-manifest/present_files.js +21 -0
  66. package/dist/llm/tool-manifest/recipe.js +40 -0
  67. package/dist/llm/tool-manifest/recommend_apps.js +23 -0
  68. package/dist/llm/tool-manifest/str_replace.js +27 -0
  69. package/dist/llm/tool-manifest/types.js +7 -0
  70. package/dist/llm/tool-manifest/view.js +24 -0
  71. package/dist/llm/tool-manifest/weather.js +27 -0
  72. package/dist/llm/tool-manifest/web.js +51 -0
  73. package/dist/network/p2p-direct.js +23 -0
  74. package/dist/network/source-intent-broadcaster.js +203 -0
  75. package/dist/network/source-intent.js +100 -0
  76. package/dist/pi-ecosystem-judgment/adaptive-scan.js +279 -0
  77. package/dist/pi-ecosystem-judgment/causal-judge.js +449 -0
  78. package/dist/pi-ecosystem-judgment/detect-hook.js +168 -0
  79. package/dist/pi-ecosystem-judgment/distill-prompt.js +226 -0
  80. package/dist/pi-ecosystem-judgment/evolve-judgment.js +170 -0
  81. package/dist/pi-ecosystem-judgment/human-value-pipeline.js +21 -0
  82. package/dist/pi-ecosystem-judgment/human-value-store.js +283 -22
  83. package/dist/pi-ecosystem-judgment/injection-gate.js +208 -0
  84. package/dist/pi-ecosystem-judgment/monitor-gate.js +188 -0
  85. package/dist/pi-ecosystem-judgment/value-injection.js +8 -2
  86. package/dist/security/builtin-guards.js +124 -0
  87. package/dist/security/context-router-tool.js +106 -0
  88. package/dist/security/input-scanner.js +223 -0
  89. package/dist/security/react-harness.js +143 -0
  90. package/dist/security/tool-gate.js +235 -0
  91. package/dist/utils/auto-evolve-policy.js +117 -0
  92. package/dist/utils/clamp.js +7 -0
  93. package/dist/utils/double.js +6 -0
  94. package/dist/web/client.js +3810 -3830
  95. package/dist/web/components/p2p/P2PModal.js +188 -0
  96. package/dist/web/components/p2p/index.js +264 -226
  97. package/dist/web/components/p2p/p2p-modal.js +657 -0
  98. package/dist/web/components/p2p/p2p-tools.js +248 -0
  99. package/dist/web/index.html +60 -49
  100. package/dist/web/server.js +827 -124
  101. package/dist/web/style.css +531 -249
  102. package/dist/web/ui/message-renderer.js +463 -0
  103. package/dist/web/ui/step-timeline.js +375 -0
  104. package/lefthook.yml +33 -0
  105. package/package.json +3 -2
  106. package/scripts/auto-evolve-loop.ts +481 -0
  107. package/scripts/auto-evolve-oneshot.sh +155 -0
  108. package/scripts/auto-evolve-snapshot.sh +136 -0
  109. package/scripts/build-web.ts +35 -1
  110. package/scripts/detect-schema-changes.sh +48 -0
  111. package/scripts/diff-reviewer.ts +159 -0
  112. package/scripts/validate-system-prompt.ts +142 -0
  113. package/scripts/weekly-report.ts +364 -0
  114. package/src/agents/p2p-chat-tools.ts +6 -6
  115. package/src/agents/permission-mode.ts +127 -0
  116. package/src/agents/pi-sdk.ts +741 -30
  117. package/src/agents/pre-tool-validator.ts +213 -0
  118. package/src/agents/workflow-pivot-loop.ts +110 -19
  119. package/src/bollharness/CLAUDE.md +1 -1
  120. package/src/bollharness/README.md +2 -2
  121. package/src/bollharness/README.zh-CN.md +2 -2
  122. package/src/bollharness/reference/boll-reference/scripts/hooks/find-boll-root.sh +2 -2
  123. package/src/bollharness/scripts/context-fragments/truth-source-hierarchy.md +2 -2
  124. package/src/bollharness/scripts/context-fragments/version-sources.md +1 -1
  125. package/src/bollharness/scripts/hooks/find-project-root.sh +4 -4
  126. package/src/bollharness/src/scripts/checks/check_doc_freshness.ts +1 -1
  127. package/src/bollharness/src/scripts/checks/check_doc_links.ts +1 -1
  128. package/src/bollharness/src/scripts/context_router.ts +1 -1
  129. package/src/bollharness/src/scripts/deploy-guard.ts +1 -1
  130. package/src/bollharness/src/scripts/guard_router.ts +1 -1
  131. package/src/bollharness/src/scripts/hooks/_hook_output.ts +1 -1
  132. package/src/bollharness/src/scripts/hooks/risk-tracker.ts +1 -1
  133. package/src/bollharness/templates/scaffold/.gitignore.append +1 -1
  134. package/src/bollharness/templates/scaffold/CLAUDE.md +2 -2
  135. package/src/bollharness-integration/context-router.ts +2 -2
  136. package/src/bollharness-integration/guard-checker.ts +1 -1
  137. package/src/bootstrap/bootstrap.ts +135 -0
  138. package/src/bootstrap/context-collector.ts +371 -0
  139. package/src/bootstrap/context-hierarchy.ts +283 -0
  140. package/src/bootstrap/lifecycle-hooks.ts +289 -0
  141. package/src/bootstrap/project-context.ts +171 -0
  142. package/src/context-compaction/auto-compact.ts +153 -0
  143. package/src/context-compaction/budget-gate.ts +32 -0
  144. package/src/context-compaction/budget-reduce.ts +37 -0
  145. package/src/context-compaction/context-collapse.ts +72 -0
  146. package/src/context-compaction/index.ts +24 -0
  147. package/src/context-compaction/microcompact.ts +54 -0
  148. package/src/context-compaction/pipeline.ts +133 -0
  149. package/src/context-compaction/snip.ts +51 -0
  150. package/src/context-compaction/token-estimator.ts +36 -0
  151. package/src/context-compaction/types.ts +99 -0
  152. package/src/heartbeat/HealthMonitor.ts +3 -2
  153. package/src/index.ts +47 -2
  154. package/src/llm/llm-judgment-client.ts +36 -35
  155. package/src/llm/pi-ai.ts +135 -29
  156. package/src/llm/system-prompt/health.ts +159 -0
  157. package/src/llm/system-prompt/layers/channel/local.md +14 -0
  158. package/src/llm/system-prompt/layers/channel/p2p-agent.md +18 -0
  159. package/src/llm/system-prompt/layers/channel/p2p-visitor.md +19 -0
  160. package/src/llm/system-prompt/layers/core/artifacts_storage.md +89 -0
  161. package/src/llm/system-prompt/layers/core/evenhandedness.md +21 -0
  162. package/src/llm/system-prompt/layers/core/hibs_reminders.md +15 -0
  163. package/src/llm/system-prompt/layers/core/identity.md +37 -0
  164. package/src/llm/system-prompt/layers/core/knowledge.md +17 -0
  165. package/src/llm/system-prompt/layers/core/memory_system.md +12 -0
  166. package/src/llm/system-prompt/layers/core/network_filesystem.md +28 -0
  167. package/src/llm/system-prompt/layers/core/refusal.md +37 -0
  168. package/src/llm/system-prompt/layers/core/tone.md +31 -0
  169. package/src/llm/system-prompt/layers/core/tools.thin.md +13 -0
  170. package/src/llm/system-prompt/layers/core/wellbeing.md +41 -0
  171. package/src/llm/system-prompt/layers/role/architect.md +20 -0
  172. package/src/llm/system-prompt/layers/role/expert.md +19 -0
  173. package/src/llm/system-prompt/layers/role/implementer.md +15 -0
  174. package/src/llm/system-prompt/layers/role/security.md +15 -0
  175. package/src/llm/system-prompt/layers/tool/artifacts.md +72 -0
  176. package/src/llm/system-prompt/layers/tool/bash.md +25 -0
  177. package/src/llm/system-prompt/layers/tool/hibs_api.md +171 -0
  178. package/src/llm/system-prompt/layers/tool/image_search.md +70 -0
  179. package/src/llm/system-prompt/layers/tool/manifest.md +89 -0
  180. package/src/llm/system-prompt/layers/tool/mcp_apps.md +53 -0
  181. package/src/llm/system-prompt/layers/tool/web_search.md +83 -0
  182. package/src/llm/system-prompt/registry.ts +325 -0
  183. package/src/llm/system-prompt/strip-hibsml.ts +52 -0
  184. package/src/llm/tool-manifest/ask_user_input.ts +37 -0
  185. package/src/llm/tool-manifest/bash.ts +25 -0
  186. package/src/llm/tool-manifest/create_file.ts +26 -0
  187. package/src/llm/tool-manifest/fetch_sports_data.ts +28 -0
  188. package/src/llm/tool-manifest/image_search.ts +26 -0
  189. package/src/llm/tool-manifest/index.ts +88 -0
  190. package/src/llm/tool-manifest/mcp.ts +46 -0
  191. package/src/llm/tool-manifest/message_compose.ts +33 -0
  192. package/src/llm/tool-manifest/places.ts +86 -0
  193. package/src/llm/tool-manifest/present_files.ts +23 -0
  194. package/src/llm/tool-manifest/recipe.ts +42 -0
  195. package/src/llm/tool-manifest/recommend_apps.ts +25 -0
  196. package/src/llm/tool-manifest/str_replace.ts +29 -0
  197. package/src/llm/tool-manifest/types.ts +52 -0
  198. package/src/llm/tool-manifest/view.ts +26 -0
  199. package/src/llm/tool-manifest/weather.ts +29 -0
  200. package/src/llm/tool-manifest/web.ts +54 -0
  201. package/src/network/p2p-direct.ts +22 -0
  202. package/src/network/source-intent-broadcaster.ts +242 -0
  203. package/src/network/source-intent.ts +167 -0
  204. package/src/security/builtin-guards.ts +162 -0
  205. package/src/security/context-router-tool.ts +122 -0
  206. package/src/security/input-scanner.ts +287 -0
  207. package/src/security/react-harness.ts +177 -0
  208. package/src/security/tool-gate.ts +294 -0
  209. package/src/utils/auto-evolve-policy.ts +138 -0
  210. package/src/utils/clamp.ts +5 -0
  211. package/src/web/client.js +105 -2187
  212. package/src/web/client.ts +4326 -0
  213. package/src/web/index.html +60 -49
  214. package/src/web/server.ts +894 -148
  215. package/src/web/style.css +531 -249
  216. package/src/web/ui/message-renderer.ts +540 -0
  217. package/src/web/ui/step-timeline.ts +394 -0
  218. package/staging/auto-evolve/clean-001/.review-verdict +9 -0
  219. package/staging/auto-evolve/clean-001/clean-001.patch +14 -0
  220. package/staging/auto-evolve/e2e-001/.patch-id +1 -0
  221. package/staging/auto-evolve/e2e-001/.review-verdict +12 -0
  222. package/staging/auto-evolve/e2e-001/e2e-001.patch +11 -0
  223. package/staging/auto-evolve/test-bad/.review-verdict +12 -0
  224. package/staging/auto-evolve/test-bad/test-bad.patch +11 -0
  225. package/test-results/.last-run.json +6 -0
  226. 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'],
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Bolloon Bootstrap — 启动入口
3
+ *
4
+ * web server 启动时 (或 CLI 模式) 调一次, 完成 3 件事:
5
+ * 1. 跑类 B 自适应扫描 (暖缓存 + 写 evolution.jsonl 启动事件)
6
+ * 2. 收集项目 Context (Bolloon.md / git / persona / judgments / skills)
7
+ * 3. 挂每天 0:00 定时任务
8
+ *
9
+ * 失败静默: 任意步骤失败 console.warn, 不抛错 (主流程不被阻塞)
10
+ */
11
+ import { runAdaptiveScan, logEvolution } from '../pi-ecosystem-judgment/adaptive-scan.js';
12
+ import { collectBolloonContext } from './context-collector.js';
13
+ /**
14
+ * 入口: web server / CLI 启动时调一次
15
+ */
16
+ export async function bootstrapBolloon(opts = {}) {
17
+ const start = Date.now();
18
+ const errors = [];
19
+ // 1. 类 B 启动扫描
20
+ let scanResult = {
21
+ scannedAt: new Date().toISOString(),
22
+ judgmentsTotal: 0,
23
+ usageEntriesScanned: 0,
24
+ suggestions: [],
25
+ };
26
+ try {
27
+ scanResult = await runAdaptiveScan();
28
+ const { suggestionHint } = await import('../pi-ecosystem-judgment/adaptive-scan.js');
29
+ await logEvolution({
30
+ ts: new Date().toISOString(),
31
+ action: 'accept', // 用 accept 表示"系统记录" (跟 reject 区分)
32
+ suggestion: {
33
+ key: 'bootstrap-startup',
34
+ kind: 'unused', // 占位
35
+ judgmentId: '__bootstrap__',
36
+ decision: 'Bolloon 启动扫描',
37
+ reason: `本次启动扫描了 ${scanResult.judgmentsTotal} 条原则, ${scanResult.usageEntriesScanned} 条使用记录, 生成 ${scanResult.suggestions.length} 条建议`,
38
+ action: 'review',
39
+ hint: suggestionHint('unused', 'review', { usage7d: 0, usage30d: 0, daysSinceLastUse: 0, totalUsage: 0 }),
40
+ metrics: { usage7d: 0, usage30d: 0, daysSinceLastUse: 0, totalUsage: 0 },
41
+ scannedAt: scanResult.scannedAt,
42
+ },
43
+ });
44
+ console.log(`[bootstrap] 类 B 启动扫描完成: ${scanResult.suggestions.length} 条建议`);
45
+ }
46
+ catch (err) {
47
+ errors.push(`scan: ${err.message}`);
48
+ console.warn('[bootstrap] 启动扫描失败 (非致命):', err);
49
+ }
50
+ // 2. 收集项目 Context
51
+ let context = {
52
+ projectRoot: opts.cwd ?? process.cwd(),
53
+ projectName: 'unknown',
54
+ bolloonMd: null,
55
+ hierarchy: { managed: null, user: null, project: null, local: null, merged: '' },
56
+ git: null,
57
+ persona: null,
58
+ judgmentsSummary: { total: 0, active: 0, superseded: 0, rejected: 0, topValues: [] },
59
+ skills: [],
60
+ env: { os: 'unknown', nodeVersion: 'unknown', llmProvider: 'unknown' },
61
+ pending: { goals: [], todos: [] },
62
+ collectedAt: new Date().toISOString(),
63
+ };
64
+ try {
65
+ context = await collectBolloonContext({ cwd: opts.cwd ?? process.cwd() });
66
+ console.log(`[bootstrap] context 收集完成: ${context.judgmentsSummary.total} judgments, ${context.skills.length} skills`);
67
+ }
68
+ catch (err) {
69
+ errors.push(`context: ${err.message}`);
70
+ console.warn('[bootstrap] context 收集失败 (非致命):', err);
71
+ }
72
+ // 3. 挂定时任务 (每天 0:00 跑扫描, server 重启时丢失可接受)
73
+ try {
74
+ scheduleAdaptiveScanDaily();
75
+ console.log('[bootstrap] 定时任务已挂: 每天 0:00 跑类 B 扫描');
76
+ }
77
+ catch (err) {
78
+ errors.push(`schedule: ${err.message}`);
79
+ console.warn('[bootstrap] 定时任务挂载失败 (非致命):', err);
80
+ }
81
+ const durationMs = Date.now() - start;
82
+ console.log(`[bootstrap] 完成 (${durationMs}ms, ${errors.length} 个错误)`);
83
+ return { context, scanResult, durationMs, errors };
84
+ }
85
+ // ============================================================
86
+ // 定时任务: 每天 0:00 跑类 B 自适应扫描
87
+ // ============================================================
88
+ let scheduled = false;
89
+ function scheduleAdaptiveScanDaily() {
90
+ if (scheduled)
91
+ return;
92
+ scheduled = true;
93
+ const now = new Date();
94
+ const next = new Date(now);
95
+ next.setHours(24, 0, 0, 0);
96
+ const msUntilMidnight = next.getTime() - now.getTime();
97
+ // 第一次: 等到明天 0:00
98
+ setTimeout(() => {
99
+ runAdaptiveScan().then((result) => {
100
+ console.log(`[bootstrap] 定时扫描完成: ${result.suggestions.length} 条建议`);
101
+ }).catch((err) => {
102
+ console.warn('[bootstrap] 定时扫描失败:', err);
103
+ });
104
+ // 之后: 每 24h
105
+ setInterval(() => {
106
+ runAdaptiveScan().then((result) => {
107
+ console.log(`[bootstrap] 定时扫描完成: ${result.suggestions.length} 条建议`);
108
+ }).catch((err) => {
109
+ console.warn('[bootstrap] 定时扫描失败:', err);
110
+ });
111
+ }, 24 * 60 * 60 * 1000);
112
+ }, msUntilMidnight);
113
+ }
114
+ /** 测试辅助: 重置 scheduled 标志 */
115
+ export function _resetScheduleForTest() {
116
+ scheduled = false;
117
+ }