@bolloon/bolloon-agent 0.2.12 → 0.2.14

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 (284) hide show
  1. package/README.md +495 -467
  2. package/bin/bolloon-cli.cjs +183 -183
  3. package/bin/bolloon-daemon.sh +207 -0
  4. package/bin/bolloon.cjs +0 -0
  5. package/bin/ipfs +0 -0
  6. package/dist/agents/agent-manifest-protocol.js +52 -0
  7. package/dist/agents/chat-segmenter.js +298 -0
  8. package/dist/agents/constraint-layer.js +19 -19
  9. package/dist/agents/judgment-protocol.js +479 -0
  10. package/dist/agents/parse-tool-call.js +304 -0
  11. package/dist/agents/peer-manifest-loader.js +210 -0
  12. package/dist/agents/pi-sdk-session-factory.js +110 -0
  13. package/dist/agents/pi-sdk-session-manager.js +297 -0
  14. package/dist/agents/pi-sdk-tools.js +1288 -0
  15. package/dist/agents/pi-sdk-types.js +13 -0
  16. package/dist/agents/pi-sdk.js +553 -1306
  17. package/dist/agents/react-loop.js +120 -0
  18. package/dist/agents/session-store.js +171 -0
  19. package/dist/agents/shell-guard.js +2 -2
  20. package/dist/agents/tool-registry.js +136 -0
  21. package/dist/agents/workflow-pivot-loop.js +200 -22
  22. package/dist/bollharness/src/scripts/context_router.js +7 -0
  23. package/dist/bollharness/src/scripts/deploy-guard.js +8 -8
  24. package/dist/bollharness-integration/context-router-judgment.js +4 -4
  25. package/dist/bollharness-integration/context-router.js +292 -292
  26. package/dist/bollharness-integration/gate-state-machine.js +26 -13
  27. package/dist/bollharness-integration/integration.js +71 -0
  28. package/dist/bollharness-integration/llm-judgment-engine.js +134 -134
  29. package/dist/bollharness-integration/skill-adapter.js +57 -127
  30. package/dist/bootstrap/chat-archiver.js +276 -0
  31. package/dist/bootstrap/context-collector.js +6 -3
  32. package/dist/bootstrap/event-log.js +160 -0
  33. package/dist/bootstrap/lifecycle-hooks.js +15 -1
  34. package/dist/bootstrap/memory-compressor.js +170 -0
  35. package/dist/bootstrap/persona-loader.js +94 -0
  36. package/dist/bootstrap/project-state.js +159 -0
  37. package/dist/bootstrap/remote-mirror.js +97 -0
  38. package/dist/bootstrap/session-window.js +121 -0
  39. package/dist/bootstrap/vector-index.js +248 -0
  40. package/dist/cli/loading-tui.js +40 -0
  41. package/dist/cli-entry.js +41 -37
  42. package/dist/context-compaction/auto-compact.js +7 -7
  43. package/dist/electron/config.js +21 -0
  44. package/dist/electron/config.js.map +1 -0
  45. package/dist/electron/dialogs.js +108 -0
  46. package/dist/electron/dialogs.js.map +1 -0
  47. package/dist/electron/first-run.js +170 -0
  48. package/dist/electron/first-run.js.map +1 -0
  49. package/dist/electron/ipc.js +20 -0
  50. package/dist/electron/ipc.js.map +1 -0
  51. package/dist/electron/logger.js +114 -0
  52. package/dist/electron/logger.js.map +1 -0
  53. package/dist/electron/main.js +63 -0
  54. package/dist/electron/main.js.map +1 -0
  55. package/dist/electron/menu.js +145 -0
  56. package/dist/electron/menu.js.map +1 -0
  57. package/dist/electron/paths.js +75 -0
  58. package/dist/electron/paths.js.map +1 -0
  59. package/dist/electron/server.js +119 -0
  60. package/dist/electron/server.js.map +1 -0
  61. package/dist/electron/tray.js +111 -0
  62. package/dist/electron/tray.js.map +1 -0
  63. package/dist/electron/window.js +111 -0
  64. package/dist/electron/window.js.map +1 -0
  65. package/dist/electron-preload.js +26 -6
  66. package/dist/electron-preload.js.map +1 -1
  67. package/dist/electron.js +6 -168
  68. package/dist/electron.js.map +1 -1
  69. package/dist/index.js +323 -266
  70. package/dist/llm/config-store.js +29 -8
  71. package/dist/llm/llm-judgment-client.js +102 -102
  72. package/dist/llm/pi-ai.js +139 -85
  73. package/dist/llm/system-prompt/layers/channel/local.md +14 -14
  74. package/dist/llm/system-prompt/layers/channel/p2p-agent.md +18 -18
  75. package/dist/llm/system-prompt/layers/channel/p2p-visitor.md +19 -19
  76. package/dist/llm/system-prompt/layers/core/artifacts_storage.md +89 -89
  77. package/dist/llm/system-prompt/layers/core/evenhandedness.md +21 -21
  78. package/dist/llm/system-prompt/layers/core/hibs_reminders.md +15 -15
  79. package/dist/llm/system-prompt/layers/core/identity.md +37 -37
  80. package/dist/llm/system-prompt/layers/core/knowledge.md +17 -17
  81. package/dist/llm/system-prompt/layers/core/memory_system.md +12 -12
  82. package/dist/llm/system-prompt/layers/core/network_filesystem.md +28 -28
  83. package/dist/llm/system-prompt/layers/core/refusal.md +37 -37
  84. package/dist/llm/system-prompt/layers/core/tone.md +31 -31
  85. package/dist/llm/system-prompt/layers/core/tools.thin.md +13 -13
  86. package/dist/llm/system-prompt/layers/core/wellbeing.md +41 -41
  87. package/dist/llm/system-prompt/layers/role/architect.md +20 -20
  88. package/dist/llm/system-prompt/layers/role/expert.md +19 -19
  89. package/dist/llm/system-prompt/layers/role/implementer.md +15 -15
  90. package/dist/llm/system-prompt/layers/role/security.md +15 -15
  91. package/dist/llm/system-prompt/layers/tool/artifacts.md +72 -72
  92. package/dist/llm/system-prompt/layers/tool/bash.md +25 -25
  93. package/dist/llm/system-prompt/layers/tool/hibs_api.md +171 -171
  94. package/dist/llm/system-prompt/layers/tool/image_search.md +70 -70
  95. package/dist/llm/system-prompt/layers/tool/manifest.md +89 -89
  96. package/dist/llm/system-prompt/layers/tool/mcp_apps.md +53 -53
  97. package/dist/llm/system-prompt/layers/tool/web_search.md +83 -83
  98. package/dist/llm/tool-manifest/ask_user_input.js +1 -1
  99. package/dist/llm/tool-manifest/bash.js +3 -3
  100. package/dist/llm/tool-manifest/create_file.js +4 -4
  101. package/dist/llm/tool-manifest/fetch_sports_data.js +3 -3
  102. package/dist/llm/tool-manifest/image_search.js +2 -2
  103. package/dist/llm/tool-manifest/mcp.js +2 -2
  104. package/dist/llm/tool-manifest/message_compose.js +3 -3
  105. package/dist/llm/tool-manifest/places.js +2 -2
  106. package/dist/llm/tool-manifest/present_files.js +1 -1
  107. package/dist/llm/tool-manifest/recipe.js +2 -2
  108. package/dist/llm/tool-manifest/str_replace.js +5 -5
  109. package/dist/llm/tool-manifest/view.js +3 -3
  110. package/dist/llm/tool-manifest/weather.js +4 -4
  111. package/dist/llm/tool-manifest/web.js +4 -4
  112. package/dist/network/local-inbox-bus.js +73 -0
  113. package/dist/network/p2p-outbox.js +161 -0
  114. package/dist/network/peer-fs.js +420 -0
  115. package/dist/network/peer-resource-bridge.js +216 -0
  116. package/dist/pi-ecosystem-judgment/causal-judge.js +51 -51
  117. package/dist/pi-ecosystem-judgment/cleanup.js +126 -0
  118. package/dist/pi-ecosystem-judgment/distill-prompt.js +42 -42
  119. package/dist/pi-ecosystem-judgment/distillation.js +14 -14
  120. package/dist/pi-ecosystem-judgment/human-value-store.js +45 -11
  121. package/dist/pi-ecosystem-judgment/monitor-gate.js +28 -28
  122. package/dist/pi-ecosystem-judgment/value-injection.js +102 -102
  123. package/dist/security/context-router-tool.js +15 -15
  124. package/dist/security/tool-gate.js +11 -0
  125. package/dist/web/api-config.html +520 -520
  126. package/dist/web/client-loop-status.js +220 -0
  127. package/dist/web/client.js +22 -5
  128. package/dist/web/client.js.map +7 -0
  129. package/dist/web/components/wallet-viem.mjs +118 -118
  130. package/dist/web/index.html +375 -375
  131. package/dist/web/input-validator.js +103 -0
  132. package/dist/web/manifest.json +20 -20
  133. package/dist/web/routes-judgments.js +738 -0
  134. package/dist/web/routes-llm-config.js +279 -0
  135. package/dist/web/routes-tasks.js +222 -0
  136. package/dist/web/server-sse.js +121 -0
  137. package/dist/web/server-storage.js +154 -0
  138. package/dist/web/server-types.js +15 -0
  139. package/dist/web/server-v3-p2p.js +224 -0
  140. package/dist/web/server.js +1070 -1307
  141. package/dist/web/style.css +4742 -4742
  142. package/dist/web/util/safe-name.js +32 -0
  143. package/package.json +162 -160
  144. package/scripts/build-cli.js +215 -215
  145. package/scripts/build-web.ts +130 -130
  146. package/scripts/postinstall.js +152 -152
  147. package/bin/bolloon.js +0 -157
  148. package/dist/constraint-runtime/src/_archive_helper.js +0 -9
  149. package/dist/constraint-runtime/src/agent/coordinator.js +0 -48
  150. package/dist/constraint-runtime/src/agent/index.js +0 -1
  151. package/dist/constraint-runtime/src/assistant/index.js +0 -12
  152. package/dist/constraint-runtime/src/bootstrap/index.js +0 -12
  153. package/dist/constraint-runtime/src/bootstrap_graph.js +0 -13
  154. package/dist/constraint-runtime/src/bridge/index.js +0 -12
  155. package/dist/constraint-runtime/src/buddy/index.js +0 -12
  156. package/dist/constraint-runtime/src/cli/index.js +0 -12
  157. package/dist/constraint-runtime/src/command_graph.js +0 -10
  158. package/dist/constraint-runtime/src/commands.js +0 -60
  159. package/dist/constraint-runtime/src/components/index.js +0 -12
  160. package/dist/constraint-runtime/src/constants/index.js +0 -12
  161. package/dist/constraint-runtime/src/constraint/budget.js +0 -22
  162. package/dist/constraint-runtime/src/constraint/index.js +0 -2
  163. package/dist/constraint-runtime/src/constraint/permission.js +0 -20
  164. package/dist/constraint-runtime/src/context.js +0 -30
  165. package/dist/constraint-runtime/src/coordinator/index.js +0 -12
  166. package/dist/constraint-runtime/src/cost_hook.js +0 -4
  167. package/dist/constraint-runtime/src/cost_tracker.js +0 -8
  168. package/dist/constraint-runtime/src/deferred_init.js +0 -10
  169. package/dist/constraint-runtime/src/direct_modes.js +0 -6
  170. package/dist/constraint-runtime/src/dynamic-tool-loader.js +0 -85
  171. package/dist/constraint-runtime/src/entrypoints/index.js +0 -12
  172. package/dist/constraint-runtime/src/execution_registry.js +0 -44
  173. package/dist/constraint-runtime/src/history.js +0 -9
  174. package/dist/constraint-runtime/src/hooks/index.js +0 -12
  175. package/dist/constraint-runtime/src/index.js +0 -26
  176. package/dist/constraint-runtime/src/ink.js +0 -4
  177. package/dist/constraint-runtime/src/keybindings/index.js +0 -12
  178. package/dist/constraint-runtime/src/memdir/index.js +0 -12
  179. package/dist/constraint-runtime/src/migrations/index.js +0 -12
  180. package/dist/constraint-runtime/src/models.js +0 -1
  181. package/dist/constraint-runtime/src/moreright/index.js +0 -12
  182. package/dist/constraint-runtime/src/native_ts/index.js +0 -12
  183. package/dist/constraint-runtime/src/output_styles/index.js +0 -12
  184. package/dist/constraint-runtime/src/parity_audit.js +0 -12
  185. package/dist/constraint-runtime/src/plugins/index.js +0 -12
  186. package/dist/constraint-runtime/src/port_manifest.js +0 -11
  187. package/dist/constraint-runtime/src/prefetch.js +0 -9
  188. package/dist/constraint-runtime/src/query.js +0 -1
  189. package/dist/constraint-runtime/src/remote/index.js +0 -12
  190. package/dist/constraint-runtime/src/remote_runtime.js +0 -9
  191. package/dist/constraint-runtime/src/runtime/index.js +0 -1
  192. package/dist/constraint-runtime/src/runtime/session.js +0 -35
  193. package/dist/constraint-runtime/src/schemas/index.js +0 -12
  194. package/dist/constraint-runtime/src/screens/index.js +0 -12
  195. package/dist/constraint-runtime/src/server/index.js +0 -12
  196. package/dist/constraint-runtime/src/services/index.js +0 -12
  197. package/dist/constraint-runtime/src/session_store.js +0 -22
  198. package/dist/constraint-runtime/src/setup.js +0 -30
  199. package/dist/constraint-runtime/src/skills/index.js +0 -1
  200. package/dist/constraint-runtime/src/skills/skill-registry.js +0 -28
  201. package/dist/constraint-runtime/src/state/index.js +0 -12
  202. package/dist/constraint-runtime/src/system_init.js +0 -20
  203. package/dist/constraint-runtime/src/thinking/engine.js +0 -42
  204. package/dist/constraint-runtime/src/thinking/index.js +0 -1
  205. package/dist/constraint-runtime/src/tool_pool.js +0 -8
  206. package/dist/constraint-runtime/src/tools/OpenCLI/execAdapter.js +0 -7
  207. package/dist/constraint-runtime/src/tools/OpenCLI/listAdapters.js +0 -7
  208. package/dist/constraint-runtime/src/tools/OpenCLI/runCommand.js +0 -7
  209. package/dist/constraint-runtime/src/tools/SafeSDK/confirmTransaction.js +0 -6
  210. package/dist/constraint-runtime/src/tools/SafeSDK/createTransaction.js +0 -8
  211. package/dist/constraint-runtime/src/tools/SafeSDK/executeTransaction.js +0 -6
  212. package/dist/constraint-runtime/src/tools/SafeSDK/getBalance.js +0 -6
  213. package/dist/constraint-runtime/src/tools/SafeSDK/getPendingTransactions.js +0 -6
  214. package/dist/constraint-runtime/src/tools/SafeSDK/proposeTransaction.js +0 -6
  215. package/dist/constraint-runtime/src/tools.js +0 -80
  216. package/dist/constraint-runtime/src/transcript.js +0 -19
  217. package/dist/constraint-runtime/src/types/index.js +0 -12
  218. package/dist/constraint-runtime/src/upstream_proxy/index.js +0 -12
  219. package/dist/constraint-runtime/src/utils/index.js +0 -12
  220. package/dist/constraint-runtime/src/vim/index.js +0 -12
  221. package/dist/constraint-runtime/src/voice/index.js +0 -12
  222. package/dist/constraint-runtime/tests/agent.test.js +0 -16
  223. package/dist/constraint-runtime/tests/constraint.test.js +0 -41
  224. package/dist/constraint-runtime/tests/skill.test.js +0 -19
  225. package/dist/constraint-runtime/tests/thinking.test.js +0 -22
  226. package/dist/pi-ecosystem-colony/index.js +0 -365
  227. package/dist/social/ant-colony/AdaptiveHeartbeat.js +0 -101
  228. package/dist/social/ant-colony/PheromoneEngine.js +0 -227
  229. package/dist/social/ant-colony/index.js +0 -6
  230. package/dist/social/ant-colony/types.js +0 -24
  231. package/dist/test/ai-judgment-test.js +0 -80
  232. package/dist/test/bollharness-integration.test.js +0 -318
  233. package/dist/test/channel-agent-multi-dialogue.js +0 -205
  234. package/dist/test/channel-heartbeat-agent-test.js +0 -201
  235. package/dist/test/constraint-layer.test.js +0 -164
  236. package/dist/test/diap-identity-test.js +0 -172
  237. package/dist/test/diap-quick-test.js +0 -62
  238. package/dist/test/global-shared-context.test.js +0 -315
  239. package/dist/test/harness-judgment-injection.test.js +0 -246
  240. package/dist/test/harness-workflow-integrator-test.js +0 -228
  241. package/dist/test/human-value-store.test.js +0 -243
  242. package/dist/test/hybrid-integration-test.js +0 -118
  243. package/dist/test/hybrid-messenger-verify.js +0 -55
  244. package/dist/test/iroh-bistream-debug.js +0 -38
  245. package/dist/test/iroh-communication.test.js +0 -66
  246. package/dist/test/iroh-debug-test.js +0 -57
  247. package/dist/test/iroh-diap-test.js +0 -71
  248. package/dist/test/iroh-direct-connect.js +0 -55
  249. package/dist/test/iroh-e2e-fixed.js +0 -89
  250. package/dist/test/iroh-e2e-same-process.js +0 -63
  251. package/dist/test/iroh-e2e.js +0 -66
  252. package/dist/test/iroh-final-e2e.js +0 -72
  253. package/dist/test/iroh-relay-test.js +0 -37
  254. package/dist/test/iroh-simple-test.js +0 -41
  255. package/dist/test/iroh-transport-verify.js +0 -54
  256. package/dist/test/iroh-transport.test.js +0 -37
  257. package/dist/test/iroh-two-nodes.js +0 -70
  258. package/dist/test/iroh-verify.js +0 -44
  259. package/dist/test/judgment-decision.test.js +0 -219
  260. package/dist/test/llm-judgment-integration.test.js +0 -220
  261. package/dist/test/p2p-agent-complex-dialogue.js +0 -385
  262. package/dist/test/p2p-agent-dialogue.js +0 -341
  263. package/dist/test/p2p-agent-full-bidirectional.js +0 -510
  264. package/dist/test/p2p-agent-harness-flow.js +0 -437
  265. package/dist/test/p2p-agent-harness-single.js +0 -143
  266. package/dist/test/p2p-ai-dialogue-test.js +0 -318
  267. package/dist/test/p2p-cid-connect-test.js +0 -195
  268. package/dist/test/p2p-connect-receiver.js +0 -69
  269. package/dist/test/p2p-doc-transfer.js +0 -110
  270. package/dist/test/p2p-identity-page-test.js +0 -77
  271. package/dist/test/p2p-iroh-test.js +0 -171
  272. package/dist/test/p2p-minimal-test.js +0 -241
  273. package/dist/test/p2p-node-1.js +0 -148
  274. package/dist/test/p2p-node-2.js +0 -148
  275. package/dist/test/p2p-server.js +0 -281
  276. package/dist/test/p2p-two-nodes-test.js +0 -438
  277. package/dist/test/pi-sdk.test.js +0 -44
  278. package/dist/test/set-persona.js +0 -40
  279. package/dist/test/simple.test.js +0 -9
  280. package/dist/test/storage-integration.test.js +0 -150
  281. package/dist/test/subagent-manager.test.js +0 -276
  282. package/dist/test/test-gate-flow.test.js +0 -81
  283. package/dist/test/workflow-engine.test.js +0 -87
  284. package/dist/test/workflow-pivot-loop.test.js +0 -246
@@ -77,14 +77,25 @@ export class WorkflowPivotLoop {
77
77
  tools;
78
78
  messageHistory;
79
79
  streamCallback;
80
+ // 2026-07-06: pivot 想看内部明细设 BOLLOON_VERBOSE=1 — 默认只保留 status 事件给 UI
81
+ verbose = typeof process !== 'undefined' && process.env?.BOLLOON_VERBOSE === '1';
82
+ vlog(msg) { if (this.verbose)
83
+ console.log(msg); }
84
+ onApproachingTokenBudget;
85
+ compactedThisRun = false; // 防止 pivot 单次 execute 反复触发 compact
86
+ // 2026-07-06: iter ≥ 2 时用简短 systemHeader 替代完整 systemPrompt
87
+ // pivot 默认 moderate profile 是 30 iter, 每次调 llm 都重复装 11K persona+tools 装 + 25 layer
88
+ // (pi-ai.ts buildSystemPromptAsync 重复读 .md) — 真没必要. 第一轮装全, 后续用锚句占位.
89
+ // compactor 触发后 (旧 systemHeader 代表性弱化) 再重新装一次全量.
90
+ continuationSystemHeader = null;
80
91
  constructor(config) {
81
92
  this.tools = new Map();
82
93
  // Default configuration based on task complexity if not provided
83
94
  const defaults = {
84
- maxIterations: config.maxIterations || 50,
95
+ maxIterations: config.maxIterations || 1000, // 2026-07-06: 持久循环 (旧 50)
85
96
  minIterations: config.minIterations || 2,
86
97
  qualityThreshold: config.qualityThreshold || 0.7,
87
- maxConsecutiveNoProgress: config.maxConsecutiveNoProgress || 5,
98
+ maxConsecutiveNoProgress: config.maxConsecutiveNoProgress || 8,
88
99
  maxTokenBudget: config.maxTokenBudget || 50000,
89
100
  complexity: config.complexity || 'moderate'
90
101
  };
@@ -120,13 +131,33 @@ export class WorkflowPivotLoop {
120
131
  /**
121
132
  * Execute the pivot loop
122
133
  */
123
- async execute(input, llm, systemPrompt, streamCallback) {
134
+ async execute(input, llm, systemPrompt, streamCallback, signal,
135
+ /**
136
+ * 2026-07-06: token 预算接近上限时回调 — 让上层 (PiAgentSession) 跑 compactor
137
+ * 否者单纯报 "Token 预算超支" 直接 break, 用户消息丢失.
138
+ * 回调同步返回值 (newMessageHistory) 时替换 this.messageHistory 后继续.
139
+ * 异步: PiAgent 触发 compactPipeline, 这里 await 等折叠完再继续.
140
+ */
141
+ onApproachingTokenBudget) {
124
142
  this.streamCallback = streamCallback;
125
143
  this.state = this.createInitialState();
144
+ this.onApproachingTokenBudget = onApproachingTokenBudget;
145
+ // 2026-07-06: 准备短 systemHeader. iter ≥ 2 用它替代完整 systemPrompt
146
+ // LLM 已经在 messageHistory 里看到全部历史, 不需要每次重装. compact 触发后下次再装全量.
147
+ this.continuationSystemHeader = this.buildContinuationHeader(systemPrompt);
148
+ // 重置 compact 状态 — 这次 execute 第一次 iter 仍用全量
149
+ this.compactedThisRun = false;
150
+ this.vlog(`[pivot] execute: input chars=${input.length}, systemPrompt chars=${systemPrompt.length}, signal=${!!signal}, continuationHeader chars=${this.continuationSystemHeader?.length ?? 0}`);
126
151
  this.messageHistory = [{ role: 'user', content: input }];
127
152
  // Analyze task complexity and adapt config
128
153
  const taskProfile = analyzeTaskComplexity(input);
129
154
  const effectiveConfig = this.adaptConfigForTask(taskProfile);
155
+ // 2026-06-18 (supervisor): taskProfile 算的 tokenBudget 只看 input 长度, 但 systemPrompt 53K 时不够
156
+ // 把 effectiveConfig.maxTokenBudget 提到 systemPrompt * 1.2 留余量, 简单问题也走完
157
+ // 2026-07-06: 但累计 totalTokens = systemPrompt + sum(replies), 每 iter LLM 反 ~3-10K
158
+ // 旧公式 budget=11K*1.2=13K, 5 iter 累计 5 × 11K + replies = ~70K → 12-13K 处直接 break.
159
+ // 修法: budget = systemPrompt × 1.2 × maxIterations, 容纳 N 次 prompt + N-1 次 reply.
160
+ effectiveConfig.maxTokenBudget = Math.max(effectiveConfig.maxTokenBudget, Math.ceil(systemPrompt.length * 1.2 * effectiveConfig.maxIterations));
130
161
  this.emit({
131
162
  type: 'status',
132
163
  content: `🔍 任务复杂度: ${taskProfile.complexity} (预估 ${taskProfile.estimatedSteps} 步)`,
@@ -139,6 +170,15 @@ export class WorkflowPivotLoop {
139
170
  });
140
171
  let response = '';
141
172
  while (this.shouldContinue(effectiveConfig)) {
173
+ // 2026-07-04: signal abort 让 pivot 提前退出 (防止 LLM hang)
174
+ if (signal?.aborted) {
175
+ this.emit({
176
+ type: 'status',
177
+ content: `⏹️ pivot loop 被 abort (iter=${this.state.iteration})`,
178
+ tool: 'loop'
179
+ });
180
+ break;
181
+ }
142
182
  this.state.iteration++;
143
183
  this.emit({
144
184
  type: 'status',
@@ -148,13 +188,57 @@ export class WorkflowPivotLoop {
148
188
  // Build context for LLM
149
189
  const context = this.buildContext();
150
190
  const fullPrompt = `${systemPrompt}\n\n${context}`;
191
+ // 2026-07-06: iter ≥ 2 改用 continuationHeader — messageHistory 已经载过全 persona/tools,
192
+ // 重装 11K + 25 layer 是浪费, 同时让 pi-ai.chat 走 < 2000 分支 (不重新装 system prompt).
193
+ // compact 触发 → 这次 iter 之后下一 iter 恢复用 full.
194
+ let headerForThisIter = systemPrompt;
195
+ let usingContinuation = false;
196
+ const shouldUseContinuation = this.state.iteration >= 2 && !this.compactedThisRun && this.continuationSystemHeader;
197
+ if (shouldUseContinuation) {
198
+ headerForThisIter = this.continuationSystemHeader;
199
+ usingContinuation = true;
200
+ }
201
+ this.vlog(`[pivot] iter=${this.state.iteration} ctx=${context.length} fullPrompt=${fullPrompt.length} budget=${effectiveConfig.maxTokenBudget} iterHeader=${usingContinuation ? 'short' : 'full'} (${headerForThisIter.length}B)`);
151
202
  try {
152
203
  // Call LLM
153
- const llmResponse = await llm.chat(context, systemPrompt);
204
+ const t0 = Date.now();
205
+ const llmResponse = await llm.chat(context, headerForThisIter, signal);
154
206
  const reply = llmResponse.reply.trim();
207
+ this.vlog(`[pivot] iter=${this.state.iteration} LLM took=${Date.now() - t0}ms reply=${reply.length} head=${reply.substring(0, 80).replace(/\n/g, ' ')}`);
155
208
  this.emit({ type: 'token', content: reply.substring(0, 100) });
209
+ // 2026-07-06: 把完整 reply 推给前端 — 前端按需更新临时气泡
210
+ // 之前只 emit token(100B 截断), 前端拿到 100B 看不清. 现在 emit preview 带完整 content.
211
+ // 折叠 / 清洗交给前端的 message-renderer.addMessage (类型 ai 入口统一 strip).
212
+ this.emit({ type: 'reply-preview', content: reply, iteration: this.state.iteration });
156
213
  // Estimate token usage
157
- this.state.totalTokens += this.estimateTokens(fullPrompt) + this.estimateTokens(reply);
214
+ this.state.totalTokens += this.estimateTokens(headerForThisIter + '\n\n' + context) + this.estimateTokens(reply);
215
+ // 2026-07-06: token 接近预算时先尝试自动压缩, 而不是直接 break — 上层 PiAgentSession
216
+ // 通过 onApproachingTokenBudget 回调触发 compactPipeline (5 层短路)
217
+ const budgetRatio = this.state.totalTokens / effectiveConfig.maxTokenBudget;
218
+ if (budgetRatio > 0.7 && this.onApproachingTokenBudget && !this.compactedThisRun && this.messageHistory.length >= 6) {
219
+ this.compactedThisRun = true;
220
+ this.emit({
221
+ type: 'status',
222
+ content: `🗜️ token ${this.state.totalTokens} 接近预算 (${(budgetRatio * 100).toFixed(0)}%), 尝试自动压缩上下文`,
223
+ tool: 'compactor',
224
+ });
225
+ try {
226
+ await this.onApproachingTokenBudget();
227
+ // 重置 totalTokens 因为 compactor 折叠后本来就不准
228
+ this.state.totalTokens = Math.ceil(systemPrompt.length * 0.5); // 粗略剩余量
229
+ this.emit({
230
+ type: 'status',
231
+ content: `🗜️ 自动压缩完成, 继续循环 (剩余估算 ${this.state.totalTokens} chars)`,
232
+ tool: 'compactor',
233
+ });
234
+ // 继续 loop, 不 break
235
+ continue;
236
+ }
237
+ catch (err) {
238
+ console.warn('[pivot] onApproachingTokenBudget failed (non-fatal, 继续走 token 阈值):', String(err?.message || err).slice(0, 100));
239
+ // compact 失败 → 回到原 check
240
+ }
241
+ }
158
242
  // Check token budget
159
243
  if (this.state.totalTokens > effectiveConfig.maxTokenBudget) {
160
244
  this.emit({
@@ -166,6 +250,39 @@ export class WorkflowPivotLoop {
166
250
  // Check if this is a final response (no tool calls)
167
251
  const pendingTools = this.extractPendingToolUses(reply);
168
252
  if (pendingTools.length === 0) {
253
+ // 2026-07-06: LLM 显式 <final gen> 标记 — pivot 立即退出, 不再走 quality/iter 流程
254
+ // 这个 marker 之前和 思考/ 同义被忽略, 害得 "你好" 类问题跑 11 iter 还不见停
255
+ // 加上之后 iter=1 收到 <final gen> 就 accept
256
+ if (/<final\s+gen\s*\/?>|<\/final\s+gen>/i.test(reply)) {
257
+ const cleaned = reply.replace(/<final\s+gen\s*\/?>|<\/final\s+gen>/gi, '').trim();
258
+ response = cleaned || reply;
259
+ this.emit({
260
+ type: 'status',
261
+ content: `✅ 检测到 <final gen> 结束标记, 立即退出 (iter=${this.state.iteration})`,
262
+ tool: 'system',
263
+ });
264
+ return this.createResult(true, response, 'final_gen_marker');
265
+ }
266
+ // Check if the reply contains tool call intent but couldn't be parsed
267
+ // 2026-07-06: 排除合法的 思考/<final gen> 这些 tag (它们是 sentinel 不是 tool call)
268
+ const cleanedForIntentCheck = reply
269
+ .replace(/<final\s+gen\s*\/?>|<\/final\s+gen>/gi, '')
270
+ .replace(/<\/?think(?:ing)?>/gi, ''); // 移除
271
+ const containsToolCallIntent = cleanedForIntentCheck.includes('调用工具') || cleanedForIntentCheck.includes('tool(') ||
272
+ cleanedForIntentCheck.includes('使用工具') || cleanedForIntentCheck.includes('需要获取') || cleanedForIntentCheck.includes('需要查看') ||
273
+ cleanedForIntentCheck.includes('tool =>') || cleanedForIntentCheck.includes('[TOOL_CALL]') ||
274
+ // 仅匹配真工具调用 tag (tool_use / function_calls / tool_call / invoke / tool_code)
275
+ /<\s*(tool_use|tool_call|function_calls?|tool_code|invoke)\s*>/i.test(cleanedForIntentCheck);
276
+ // If there's tool call intent but no parsed tools, continue the loop
277
+ if (containsToolCallIntent && this.state.iteration < effectiveConfig.maxIterations) {
278
+ this.emit({
279
+ type: 'status',
280
+ content: `🔄 检测到工具调用意图但格式无法解析,继续循环...`,
281
+ tool: 'system'
282
+ });
283
+ this.state.consecutiveNoProgress++;
284
+ continue;
285
+ }
169
286
  // No pending tool uses - this is a normal completion
170
287
  this.state.pendingToolUses = [];
171
288
  // Evaluate quality before accepting
@@ -377,6 +494,29 @@ export class WorkflowPivotLoop {
377
494
  }
378
495
  }
379
496
  }
497
+ // 2026-06-18 (supervisor): Claude Code 风格 <tool_call><tool_name>...</tool_call>
498
+ // bolloon agent 之前一直被 model 教用这个格式, 但 pivot loop 只认 3 个旧 pattern.
499
+ // 现在加: <tool_call><invoke name="X"><parameter name="k">v</parameter></invoke></tool_call>
500
+ const toolCodeRe = /<tool_code>([\s\S]*?)<\/tool_code>/g;
501
+ while ((match = toolCodeRe.exec(content)) !== null) {
502
+ const block = match[1];
503
+ const invokeRe = /<invoke\s+name="(\w+)"\s*>([\s\S]*?)<\/invoke>/g;
504
+ let im;
505
+ while ((im = invokeRe.exec(block)) !== null) {
506
+ const name = im[1];
507
+ if (!this.tools.has(name))
508
+ continue;
509
+ const args = {};
510
+ const paramRe = /<parameter\s+name="(\w+)"\s*>([\s\S]*?)<\/parameter>/g;
511
+ let pm;
512
+ while ((pm = paramRe.exec(im[2])) !== null) {
513
+ args[pm[1]] = pm[2].trim().replace(/^["']|['"]$/g, '');
514
+ }
515
+ if (!pending.some(p => p.name === name)) {
516
+ pending.push({ name, args, description: '', parameters: {} });
517
+ }
518
+ }
519
+ }
380
520
  // Pattern 1: Chinese format "调用工具: tool_name(args)"
381
521
  const pattern1 = /调用工具[::]\s*(\w+)\s*\(([^)]*)\)/g;
382
522
  while ((match = pattern1.exec(content)) !== null) {
@@ -417,6 +557,31 @@ export class WorkflowPivotLoop {
417
557
  catch {
418
558
  // JSON parsing failed, ignore
419
559
  }
560
+ // Pattern 4: Single JSON tool call format {"name": "tool_name", "arguments": {...}}
561
+ try {
562
+ const singleJsonRe = /\{\s*"name"\s*:\s*"(\w+)"\s*,\s*"arguments"\s*:\s*(\{[\s\S]*?\})\s*\}/g;
563
+ let singleMatch;
564
+ while ((singleMatch = singleJsonRe.exec(content)) !== null) {
565
+ const name = singleMatch[1];
566
+ if (pending.some(p => p.name === name))
567
+ continue;
568
+ if (!this.tools.has(name))
569
+ continue;
570
+ try {
571
+ const args = JSON.parse(singleMatch[2]);
572
+ if (args && typeof args === 'object') {
573
+ const normalizedArgs = this.normalizeArgs(args);
574
+ pending.push({ name, args: normalizedArgs, description: '', parameters: {} });
575
+ }
576
+ }
577
+ catch {
578
+ // JSON parsing failed, skip
579
+ }
580
+ }
581
+ }
582
+ catch {
583
+ // Pattern matching failed, ignore
584
+ }
420
585
  return pending;
421
586
  }
422
587
  /**
@@ -493,25 +658,21 @@ export class WorkflowPivotLoop {
493
658
  * Evaluate response quality
494
659
  */
495
660
  evaluateQuality(response) {
496
- let score = 0.5;
497
- // Length-based scoring
661
+ let score = 0.7; // 2026-07-06: 起点 0.7 — 短回复不应被当成低质量打回去重跑
662
+ // Length bonus
498
663
  if (response.length > 100)
499
- score += 0.1;
664
+ score += 0.05;
500
665
  if (response.length > 500)
501
666
  score += 0.1;
502
- if (response.length < 30)
503
- score -= 0.2;
504
- // Structure indicators
667
+ // Structure
505
668
  if (response.includes('\n'))
506
669
  score += 0.05;
507
- if (response.includes('-') || response.includes('•'))
508
- score += 0.05;
509
670
  if (response.includes('```'))
510
671
  score += 0.1;
511
- // Content quality indicators
512
- const conclusionWords = ['完成', '结果', '总结', '所以', '因此', '答案', '推荐', '建议'];
672
+ // Conclusion language
673
+ const conclusionWords = ['完成', '总结', '所以', '因此', '答案', '推荐', '建议'];
513
674
  if (conclusionWords.some(w => response.includes(w)))
514
- score += 0.1;
675
+ score += 0.05;
515
676
  // Negative indicators
516
677
  if (response.includes('调用工具') || response.includes('tool('))
517
678
  score -= 0.15;
@@ -546,6 +707,20 @@ export class WorkflowPivotLoop {
546
707
  // Rough estimate: ~4 characters per token for Chinese/English mix
547
708
  return Math.ceil(text.length / 4);
548
709
  }
710
+ /**
711
+ * 2026-07-06: build continuation header — iter ≥ 2 的短 system 头部
712
+ * LLM 已在 messageHistory 里, 不需要每次 11K persona+tools 重装.
713
+ * 短头告诉 LLM: "你已经看过首轮; 继续 conversation, 详情见历史".
714
+ */
715
+ buildContinuationHeader(fullSystemPrompt) {
716
+ // 从 fullSystemPrompt 提取关键锚句: identity persona 第 1 行 + 工作模式 head 段
717
+ // 完整重装只在 iter=1 和 compact 之后两次
718
+ const lines = fullSystemPrompt.split('\n');
719
+ const identityLine = lines.find((l) => l.includes('你是') && !l.includes('(续)')) ?? '';
720
+ return `[continuation] 你的 persona + tools + judgments 已在首轮 messageHistory 中.
721
+ ${identityLine ? identityLine + '\n' : ''}当前 step 别再读 persona 全文, 继续推进任务即可.
722
+ > <final gen> 只在真完成时输出.`;
723
+ }
549
724
  /**
550
725
  * Create result object
551
726
  */
@@ -607,26 +782,29 @@ export class WorkflowPivotLoop {
607
782
  * Factory to create a default pivot loop configuration
608
783
  */
609
784
  export function createDefaultPivotConfig(complexity) {
785
+ // 2026-07-06: maxIterations 默认大幅上调 — LLM 自己约束结束时机, 持久循环.
786
+ // 真实停止由 LLM emit <final gen> / 质量达标 / 连续无进展 / user abort 触发.
787
+ // 旧 30/60 iter 在长任务 (eg. 大文档改写) 太短, 现在跑到 N 次也不报超时.
610
788
  const profiles = {
611
789
  simple: {
612
- maxIterations: 15,
790
+ maxIterations: 200,
613
791
  minIterations: 1,
614
792
  qualityThreshold: 0.6,
615
- maxConsecutiveNoProgress: 3,
793
+ maxConsecutiveNoProgress: 5,
616
794
  maxTokenBudget: 10000
617
795
  },
618
796
  moderate: {
619
- maxIterations: 30,
797
+ maxIterations: 1000,
620
798
  minIterations: 2,
621
799
  qualityThreshold: 0.7,
622
- maxConsecutiveNoProgress: 5,
800
+ maxConsecutiveNoProgress: 8,
623
801
  maxTokenBudget: 30000
624
802
  },
625
803
  complex: {
626
- maxIterations: 60,
804
+ maxIterations: 2000,
627
805
  minIterations: 3,
628
806
  qualityThreshold: 0.75,
629
- maxConsecutiveNoProgress: 8,
807
+ maxConsecutiveNoProgress: 12,
630
808
  maxTokenBudget: 60000
631
809
  }
632
810
  };
@@ -1,5 +1,12 @@
1
1
  import * as fs from "fs";
2
2
  import * as path from "path";
3
+ import { fileURLToPath } from "url";
4
+ // ESM compatibility: root package.json has "type": "module" so bare `__dirname`
5
+ // is undefined at module-eval time (this module is re-exported via
6
+ // dist/bollharness-integration/index.js which dist/index.js imports unconditionally,
7
+ // so the crash fires before --web/--cli even reaches parseArgs).
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
3
10
  export const FRAGMENTS_DIR = path.join(__dirname, "..", "context-fragments");
4
11
  export const CONTEXT_MAP = {
5
12
  "bridge_agent/": ["bridge-constitution"],
@@ -104,17 +104,17 @@ function checkRsync(cmd) {
104
104
  function block(reason, host = null) {
105
105
  let guidance;
106
106
  if (host === PROD_IP) {
107
- guidance = `请使用标准部署流程:
108
- 后端: bash scripts/deploy.sh --yes
109
- Demo 正式: bash scripts/deploy-demo.sh <name> --channel prod --yes
110
- Demo 内测: bash scripts/deploy-demo.sh <name> --channel preview --yes
111
- Edge/Nginx: bash scripts/deploy-edge.sh --yes
107
+ guidance = `请使用标准部署流程:
108
+ 后端: bash scripts/deploy.sh --yes
109
+ Demo 正式: bash scripts/deploy-demo.sh <name> --channel prod --yes
110
+ Demo 内测: bash scripts/deploy-demo.sh <name> --channel preview --yes
111
+ Edge/Nginx: bash scripts/deploy-edge.sh --yes
112
112
  详见 Bolloon.md Development Commands。`;
113
113
  }
114
114
  else if (BRIDGE_VPS_HOSTS.includes(host ?? "")) {
115
- guidance = `Bridge VPS 必须走 git pull 更新路径:
116
- ssh root@${host} 'sudo -u boll git -C /opt/boll pull --ff-only'
117
- scp/rsync 直传会重新制造 orphan worktree。
115
+ guidance = `Bridge VPS 必须走 git pull 更新路径:
116
+ ssh root@${host} 'sudo -u boll git -C /opt/boll pull --ff-only'
117
+ scp/rsync 直传会重新制造 orphan worktree。
118
118
  详见 docs/issues/guard-20260408-0445-bridge-vps-orphan-worktree.md。`;
119
119
  }
120
120
  else {
@@ -253,9 +253,9 @@ export async function generateJudgmentInjection(filePath, gate, options = {}) {
253
253
  const header = gate === 3
254
254
  ? '# Plan Freeze - Decision Principles'
255
255
  : `# Gate ${gate} - Active Judgments`;
256
- return `${header}
257
- # Path: ${filePath}
258
- # Confidence: ${(result.confidence * 100).toFixed(0)}%
259
-
256
+ return `${header}
257
+ # Path: ${filePath}
258
+ # Confidence: ${(result.confidence * 100).toFixed(0)}%
259
+
260
260
  ${result.contextYaml}`;
261
261
  }