@bolloon/bolloon-agent 0.3.44 → 0.3.46

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 (217) hide show
  1. package/README.md +135 -135
  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/dist/agents/agent-identity.js +17 -0
  6. package/dist/agents/constraint-layer.js +19 -19
  7. package/dist/agents/judgment-protocol.js +14 -14
  8. package/dist/agents/loop-review.js +21 -5
  9. package/dist/agents/pi-sdk-types.js +12 -12
  10. package/dist/agents/pi-sdk.js +197 -102
  11. package/dist/agents/skill-writer.js +19 -0
  12. package/dist/agents/workflow-pivot-loop.js +3 -3
  13. package/dist/bollharness/src/scripts/deploy-guard.js +8 -8
  14. package/dist/bollharness-integration/context-router-judgment.js +4 -4
  15. package/dist/bollharness-integration/context-router.js +292 -292
  16. package/dist/bollharness-integration/gate-state-machine.js +13 -13
  17. package/dist/bollharness-integration/llm-judgment-engine.js +134 -134
  18. package/dist/bollharness-integration/skill-adapter.js +21 -21
  19. package/dist/bootstrap/chat-archiver.js +14 -14
  20. package/dist/bootstrap/context-os.js +21 -21
  21. package/dist/bootstrap/memory-compressor.js +29 -13
  22. package/dist/cli-entry.js +47 -37
  23. package/dist/constraint-runtime/reference_data/archive_surface_snapshot.json +63 -0
  24. package/dist/constraint-runtime/reference_data/commands_snapshot.json +1037 -0
  25. package/dist/constraint-runtime/reference_data/subsystems/assistant.json +8 -0
  26. package/dist/constraint-runtime/reference_data/subsystems/bootstrap.json +8 -0
  27. package/dist/constraint-runtime/reference_data/subsystems/bridge.json +32 -0
  28. package/dist/constraint-runtime/reference_data/subsystems/buddy.json +13 -0
  29. package/dist/constraint-runtime/reference_data/subsystems/cli.json +26 -0
  30. package/dist/constraint-runtime/reference_data/subsystems/components.json +32 -0
  31. package/dist/constraint-runtime/reference_data/subsystems/constants.json +28 -0
  32. package/dist/constraint-runtime/reference_data/subsystems/coordinator.json +8 -0
  33. package/dist/constraint-runtime/reference_data/subsystems/entrypoints.json +15 -0
  34. package/dist/constraint-runtime/reference_data/subsystems/hooks.json +32 -0
  35. package/dist/constraint-runtime/reference_data/subsystems/keybindings.json +21 -0
  36. package/dist/constraint-runtime/reference_data/subsystems/memdir.json +15 -0
  37. package/dist/constraint-runtime/reference_data/subsystems/migrations.json +18 -0
  38. package/dist/constraint-runtime/reference_data/subsystems/moreright.json +8 -0
  39. package/dist/constraint-runtime/reference_data/subsystems/native_ts.json +11 -0
  40. package/dist/constraint-runtime/reference_data/subsystems/outputStyles.json +8 -0
  41. package/dist/constraint-runtime/reference_data/subsystems/plugins.json +9 -0
  42. package/dist/constraint-runtime/reference_data/subsystems/remote.json +11 -0
  43. package/dist/constraint-runtime/reference_data/subsystems/schemas.json +8 -0
  44. package/dist/constraint-runtime/reference_data/subsystems/screens.json +10 -0
  45. package/dist/constraint-runtime/reference_data/subsystems/server.json +10 -0
  46. package/dist/constraint-runtime/reference_data/subsystems/services.json +32 -0
  47. package/dist/constraint-runtime/reference_data/subsystems/skills.json +27 -0
  48. package/dist/constraint-runtime/reference_data/subsystems/state.json +13 -0
  49. package/dist/constraint-runtime/reference_data/subsystems/types.json +18 -0
  50. package/dist/constraint-runtime/reference_data/subsystems/upstreamproxy.json +9 -0
  51. package/dist/constraint-runtime/reference_data/subsystems/utils.json +32 -0
  52. package/dist/constraint-runtime/reference_data/subsystems/vim.json +12 -0
  53. package/dist/constraint-runtime/reference_data/subsystems/voice.json +8 -0
  54. package/dist/constraint-runtime/reference_data/tools_snapshot.json +922 -0
  55. package/dist/constraint-runtime/setup.js +1 -1
  56. package/dist/constraint-runtime/tools/PolymarketSDK/cancelOrder.d.ts +13 -3
  57. package/dist/constraint-runtime/tools/PolymarketSDK/cancelOrder.js +16 -4
  58. package/dist/constraint-runtime/tools/PolymarketSDK/clobShared.d.ts +50 -0
  59. package/dist/constraint-runtime/tools/PolymarketSDK/clobShared.js +78 -0
  60. package/dist/constraint-runtime/tools/PolymarketSDK/createOrder.d.ts +24 -3
  61. package/dist/constraint-runtime/tools/PolymarketSDK/createOrder.js +31 -4
  62. package/dist/constraint-runtime/tools/PolymarketSDK/getMarket.d.ts +1 -0
  63. package/dist/constraint-runtime/tools/PolymarketSDK/getMarket.js +14 -3
  64. package/dist/constraint-runtime/tools/PolymarketSDK/getOrders.d.ts +11 -2
  65. package/dist/constraint-runtime/tools/PolymarketSDK/getOrders.js +23 -4
  66. package/dist/constraint-runtime/tools/PolymarketSDK/listMarkets.d.ts +4 -0
  67. package/dist/constraint-runtime/tools/PolymarketSDK/listMarkets.js +22 -2
  68. package/dist/context-compaction/auto-compact.js +7 -7
  69. package/dist/electron/config.js.map +1 -0
  70. package/dist/electron/dialogs.js.map +1 -0
  71. package/dist/electron/first-run.js +54 -54
  72. package/dist/electron/first-run.js.map +1 -0
  73. package/dist/electron/ipc.js.map +1 -0
  74. package/dist/electron/logger.js.map +1 -0
  75. package/dist/electron/main.js.map +1 -0
  76. package/dist/electron/menu.js.map +1 -0
  77. package/dist/electron/paths.js.map +1 -0
  78. package/dist/electron/server.js.map +1 -0
  79. package/dist/electron/tray.js.map +1 -0
  80. package/dist/electron/window.js.map +1 -0
  81. package/dist/electron-build/electron/first-run.js +54 -54
  82. package/dist/electron-preload.js.map +1 -1
  83. package/dist/electron.js.map +1 -1
  84. package/dist/index.js +113 -113
  85. package/dist/llm/llm-judgment-client.js +102 -102
  86. package/dist/llm/pi-ai.js +48 -48
  87. package/dist/llm/system-prompt/layers/channel/human-async.md +41 -41
  88. package/dist/llm/system-prompt/layers/channel/local.md +14 -14
  89. package/dist/llm/system-prompt/layers/channel/p2p-agent.md +18 -18
  90. package/dist/llm/system-prompt/layers/channel/p2p-peer-sync.md +51 -51
  91. package/dist/llm/system-prompt/layers/channel/p2p-proactive.md +43 -43
  92. package/dist/llm/system-prompt/layers/channel/p2p-visitor.md +19 -19
  93. package/dist/llm/system-prompt/layers/channel/session-handoff.md +61 -61
  94. package/dist/llm/system-prompt/layers/core/artifacts_storage.md +89 -89
  95. package/dist/llm/system-prompt/layers/core/evenhandedness.md +21 -21
  96. package/dist/llm/system-prompt/layers/core/external-engagement.md +73 -73
  97. package/dist/llm/system-prompt/layers/core/hibs_reminders.md +15 -15
  98. package/dist/llm/system-prompt/layers/core/identity.md +48 -48
  99. package/dist/llm/system-prompt/layers/core/knowledge.md +17 -17
  100. package/dist/llm/system-prompt/layers/core/memory_system.md +12 -12
  101. package/dist/llm/system-prompt/layers/core/network_filesystem.md +28 -28
  102. package/dist/llm/system-prompt/layers/core/refusal.md +37 -37
  103. package/dist/llm/system-prompt/layers/core/tone.md +31 -31
  104. package/dist/llm/system-prompt/layers/core/tools.thin.md +13 -13
  105. package/dist/llm/system-prompt/layers/core/wellbeing.md +41 -41
  106. package/dist/llm/system-prompt/layers/role/architect.md +20 -20
  107. package/dist/llm/system-prompt/layers/role/expert.md +19 -19
  108. package/dist/llm/system-prompt/layers/role/implementer.md +15 -15
  109. package/dist/llm/system-prompt/layers/role/security.md +15 -15
  110. package/dist/llm/system-prompt/layers/tool/artifacts.md +72 -72
  111. package/dist/llm/system-prompt/layers/tool/bash.md +25 -25
  112. package/dist/llm/system-prompt/layers/tool/goal_handoff.md +77 -77
  113. package/dist/llm/system-prompt/layers/tool/hibs_api.md +171 -171
  114. package/dist/llm/system-prompt/layers/tool/image_search.md +70 -70
  115. package/dist/llm/system-prompt/layers/tool/manifest.md +89 -89
  116. package/dist/llm/system-prompt/layers/tool/mcp_apps.md +53 -53
  117. package/dist/llm/system-prompt/layers/tool/p2p_request.md +61 -61
  118. package/dist/llm/system-prompt/layers/tool/web_search.md +83 -83
  119. package/dist/llm/tool-manifest/ask_user_input.js +1 -1
  120. package/dist/llm/tool-manifest/bash.js +3 -3
  121. package/dist/llm/tool-manifest/create_file.js +4 -4
  122. package/dist/llm/tool-manifest/fetch_sports_data.js +3 -3
  123. package/dist/llm/tool-manifest/image_search.js +2 -2
  124. package/dist/llm/tool-manifest/index.js +59 -0
  125. package/dist/llm/tool-manifest/mcp.js +2 -2
  126. package/dist/llm/tool-manifest/message_compose.js +3 -3
  127. package/dist/llm/tool-manifest/places.js +2 -2
  128. package/dist/llm/tool-manifest/present_files.js +1 -1
  129. package/dist/llm/tool-manifest/recipe.js +2 -2
  130. package/dist/llm/tool-manifest/str_replace.js +5 -5
  131. package/dist/llm/tool-manifest/view.js +3 -3
  132. package/dist/llm/tool-manifest/weather.js +4 -4
  133. package/dist/llm/tool-manifest/web.js +4 -4
  134. package/dist/orbitdb/cid-database.js +41 -0
  135. package/dist/orbitdb/did-catalog-replication.js +199 -0
  136. package/dist/orbitdb/trajectory-store.js +150 -0
  137. package/dist/pi-ecosystem-judgment/causal-judge.js +51 -51
  138. package/dist/pi-ecosystem-judgment/distill-prompt.js +42 -42
  139. package/dist/pi-ecosystem-judgment/distillation.js +14 -14
  140. package/dist/pi-ecosystem-judgment/monitor-gate.js +28 -28
  141. package/dist/pi-ecosystem-judgment/value-injection.js +102 -102
  142. package/dist/security/context-router-tool.js +15 -15
  143. package/dist/storage/did-catalog-bridge.js +271 -0
  144. package/dist/utils/auto-update.js.map +1 -0
  145. package/dist/web/api-config.html +779 -779
  146. package/dist/web/client-hearth.js +10 -10
  147. package/dist/web/client.js +141 -0
  148. package/dist/web/client.js.map +7 -0
  149. package/dist/web/components/wallet-viem.mjs +118 -118
  150. package/dist/web/index.html +472 -424
  151. package/dist/web/manifest.json +20 -20
  152. package/dist/web/server.js +290 -13
  153. package/dist/web/style.css +4946 -4914
  154. package/package.json +193 -193
  155. package/scripts/build-cli.js +215 -215
  156. package/scripts/build-web.ts +130 -130
  157. package/scripts/postinstall.js +152 -152
  158. package/bin/bolloon.js +0 -157
  159. package/dist/pi-ecosystem-colony/index.js +0 -365
  160. package/dist/social/ant-colony/AdaptiveHeartbeat.js +0 -101
  161. package/dist/social/ant-colony/PheromoneEngine.js +0 -227
  162. package/dist/social/ant-colony/index.js +0 -6
  163. package/dist/social/ant-colony/types.js +0 -24
  164. package/dist/test/ai-judgment-test.js +0 -80
  165. package/dist/test/bollharness-integration.test.js +0 -318
  166. package/dist/test/channel-agent-multi-dialogue.js +0 -205
  167. package/dist/test/channel-heartbeat-agent-test.js +0 -201
  168. package/dist/test/constraint-layer.test.js +0 -164
  169. package/dist/test/diap-identity-test.js +0 -172
  170. package/dist/test/diap-quick-test.js +0 -62
  171. package/dist/test/global-shared-context.test.js +0 -315
  172. package/dist/test/harness-judgment-injection.test.js +0 -246
  173. package/dist/test/harness-workflow-integrator-test.js +0 -228
  174. package/dist/test/human-value-store.test.js +0 -243
  175. package/dist/test/hybrid-integration-test.js +0 -118
  176. package/dist/test/hybrid-messenger-verify.js +0 -55
  177. package/dist/test/iroh-bistream-debug.js +0 -38
  178. package/dist/test/iroh-communication.test.js +0 -66
  179. package/dist/test/iroh-debug-test.js +0 -57
  180. package/dist/test/iroh-diap-test.js +0 -71
  181. package/dist/test/iroh-direct-connect.js +0 -55
  182. package/dist/test/iroh-e2e-fixed.js +0 -89
  183. package/dist/test/iroh-e2e-same-process.js +0 -63
  184. package/dist/test/iroh-e2e.js +0 -66
  185. package/dist/test/iroh-final-e2e.js +0 -72
  186. package/dist/test/iroh-relay-test.js +0 -37
  187. package/dist/test/iroh-simple-test.js +0 -41
  188. package/dist/test/iroh-transport-verify.js +0 -54
  189. package/dist/test/iroh-transport.test.js +0 -37
  190. package/dist/test/iroh-two-nodes.js +0 -70
  191. package/dist/test/iroh-verify.js +0 -44
  192. package/dist/test/judgment-decision.test.js +0 -219
  193. package/dist/test/llm-judgment-integration.test.js +0 -220
  194. package/dist/test/p2p-agent-complex-dialogue.js +0 -385
  195. package/dist/test/p2p-agent-dialogue.js +0 -341
  196. package/dist/test/p2p-agent-full-bidirectional.js +0 -510
  197. package/dist/test/p2p-agent-harness-flow.js +0 -437
  198. package/dist/test/p2p-agent-harness-single.js +0 -143
  199. package/dist/test/p2p-ai-dialogue-test.js +0 -318
  200. package/dist/test/p2p-cid-connect-test.js +0 -195
  201. package/dist/test/p2p-connect-receiver.js +0 -69
  202. package/dist/test/p2p-doc-transfer.js +0 -110
  203. package/dist/test/p2p-identity-page-test.js +0 -77
  204. package/dist/test/p2p-iroh-test.js +0 -171
  205. package/dist/test/p2p-minimal-test.js +0 -241
  206. package/dist/test/p2p-node-1.js +0 -148
  207. package/dist/test/p2p-node-2.js +0 -148
  208. package/dist/test/p2p-server.js +0 -281
  209. package/dist/test/p2p-two-nodes-test.js +0 -438
  210. package/dist/test/pi-sdk.test.js +0 -44
  211. package/dist/test/set-persona.js +0 -40
  212. package/dist/test/simple.test.js +0 -9
  213. package/dist/test/storage-integration.test.js +0 -150
  214. package/dist/test/subagent-manager.test.js +0 -276
  215. package/dist/test/test-gate-flow.test.js +0 -81
  216. package/dist/test/workflow-engine.test.js +0 -87
  217. package/dist/test/workflow-pivot-loop.test.js +0 -246
@@ -596,9 +596,57 @@ export class PiAgentSession {
596
596
  }
597
597
  catch { /* 非致命 */ }
598
598
  }
599
+ // ============================================================
600
+ // 2026-08-08: 运行轨迹采集 (trajectory) — 每轮运行 → 落盘 + OrbitDB, 失败静默
601
+ // ============================================================
602
+ async createTrajectoryRecorder(input, channelId) {
603
+ try {
604
+ const { TrajectoryRecorder } = await import('../orbitdb/trajectory-store.js');
605
+ const { resolveUserDid } = await import('../storage/did-catalog-bridge.js');
606
+ const did = await resolveUserDid();
607
+ const model = this.llmConfig?.model || this.model || '';
608
+ return new TrajectoryRecorder({
609
+ agentId: this.currentAgentId || 'default',
610
+ input,
611
+ channelId: channelId || this.currentChannelId,
612
+ did: did || undefined,
613
+ model: typeof model === 'string' && model ? model : undefined,
614
+ });
615
+ }
616
+ catch {
617
+ return null; // 轨迹采集失败静默 (增强层)
618
+ }
619
+ }
620
+ wrapTrajectoryStream(onStream, rec) {
621
+ return (ev) => {
622
+ try {
623
+ rec?.recordStep?.(ev);
624
+ }
625
+ catch { /* 记录失败不影响转发 */ }
626
+ onStream(ev);
627
+ };
628
+ }
629
+ finishTrajectory(rec, reply, status = 'ok') {
630
+ if (!rec)
631
+ return;
632
+ // fire-and-forget: 不阻塞回复返回
633
+ (async () => {
634
+ try {
635
+ const { recordTrajectory } = await import('../orbitdb/trajectory-store.js');
636
+ const run = rec.endRun(reply, status);
637
+ await recordTrajectory(run, {});
638
+ }
639
+ catch { /* 静默 */ }
640
+ })();
641
+ }
599
642
  async prompt(input, options) {
600
643
  this.minimaxAvailable = this.checkMinimax();
601
644
  this.currentChannelId = options?.channelId ?? this.currentChannelId;
645
+ // 2026-08-08: 运行轨迹采集 (落盘 + OrbitDB, 失败静默) — 包裹 onStream 收集步骤事件
646
+ const trajRec = await this.createTrajectoryRecorder(input, options?.channelId);
647
+ if (trajRec && options?.onStream) {
648
+ options = { ...options, onStream: this.wrapTrajectoryStream(options.onStream, trajRec) };
649
+ }
602
650
  this.messageHistory.push({
603
651
  role: 'user',
604
652
  content: input
@@ -607,6 +655,7 @@ export class PiAgentSession {
607
655
  const response = await this.handleFallback(input);
608
656
  this.messageHistory.push({ role: 'assistant', content: response });
609
657
  this.reportUsageToContextManager();
658
+ this.finishTrajectory(trajRec, response);
610
659
  return response;
611
660
  }
612
661
  // P0 注入门
@@ -638,6 +687,7 @@ export class PiAgentSession {
638
687
  if (this.usePivotLoop) {
639
688
  try {
640
689
  const lr = await this.promptWithPivotLoop(input, undefined, options?.channelId);
690
+ this.finishTrajectory(trajRec, lr.response || '');
641
691
  return lr.response || '';
642
692
  }
643
693
  finally {
@@ -653,6 +703,7 @@ export class PiAgentSession {
653
703
  try {
654
704
  // 2026-06-16: runReActLoop 现在返回 { reply, aiFailed, aiFailureReason } — 这里只需 reply 字符串
655
705
  const loopResult = await this.runReActLoop(this.currentOnStream ?? undefined, options?.signal);
706
+ this.finishTrajectory(trajRec, loopResult.reply, loopResult.aiFailed ? 'error' : 'ok');
656
707
  return loopResult.reply;
657
708
  }
658
709
  finally {
@@ -673,6 +724,10 @@ export class PiAgentSession {
673
724
  this.minimaxAvailable = this.checkMinimax();
674
725
  console.log(`[PiAgent.promptStream] minimaxAvailable=${this.minimaxAvailable}`);
675
726
  this.currentChannelId = channelId ?? this.currentChannelId;
727
+ // 2026-08-08: 运行轨迹采集 (落盘 + OrbitDB, 失败静默) — 包裹 onStream 收集步骤事件
728
+ const trajRec = await this.createTrajectoryRecorder(input, channelId);
729
+ if (trajRec)
730
+ onStream = this.wrapTrajectoryStream(onStream, trajRec);
676
731
  // 2026-06-18 (supervisor): web server 把 46K markedPrompt 喂过来
677
732
  // (【本轮用户请求】\n<text>\n【请求结束】\n\n<contextHint>).
678
733
  // 整个 input 走下游, pivot loop 之前拿 47K buildContext 当 user message 发出去,
@@ -693,6 +748,7 @@ export class PiAgentSession {
693
748
  this.messageHistory.push({ role: 'assistant', content: response });
694
749
  onStream({ type: 'done', content: '' });
695
750
  this.reportUsageToContextManager();
751
+ this.finishTrajectory(trajRec, response);
696
752
  return response;
697
753
  }
698
754
  // P0 注入门: 缓存 onStream + signal, computeJudgmentGate 用 currentOnStream 广播 phase
@@ -840,6 +896,7 @@ export class PiAgentSession {
840
896
  this.promptStartTime = 0;
841
897
  this.reportUsageToContextManager();
842
898
  }
899
+ this.finishTrajectory(trajRec, pivotResult);
843
900
  return pivotResult;
844
901
  }
845
902
  // 2026-06-16: loop 自动重试 — runReActLoop 内部遇到 [AI 服务调用失败] sentinel 时,
@@ -929,6 +986,8 @@ export class PiAgentSession {
929
986
  this.currentSignal = null;
930
987
  this.bootstrapAddition = '';
931
988
  this.promptStartTime = 0;
989
+ // 2026-08-08: 轨迹收尾 — 落盘 + OrbitDB (失败静默, 不阻塞回复)
990
+ this.finishTrajectory(trajRec, result);
932
991
  return result;
933
992
  }
934
993
  async promptWithPivotLoop(input, config, channelId) {
@@ -972,38 +1031,38 @@ export class PiAgentSession {
972
1031
  }
973
1032
  // M2.4: persona 缓存
974
1033
  if (!this.cachedPersonaSection && this.persona) {
975
- this.cachedPersonaSection = `
976
- 角色描述: ${this.persona.description || '无'}
977
- 性格特点: ${this.persona.personality || '无'}
978
- 问候语: ${this.persona.greeting || '无'}
1034
+ this.cachedPersonaSection = `
1035
+ 角色描述: ${this.persona.description || '无'}
1036
+ 性格特点: ${this.persona.personality || '无'}
1037
+ 问候语: ${this.persona.greeting || '无'}
979
1038
  `;
980
1039
  }
981
- const systemPrompt = `${this.bootstrapAddition}你是 ${this.identity.name},基于ReAct (Reasoning + Acting)模式工作。${this.cachedPersonaSection}
982
- 当前工作目录: ${this.cwd}
983
- 当前身份: ${this.identity.name} (${this.identity.did})
984
- ${this.currentIntentHint}
985
-
986
- ${this.getToolDefinitions()}
987
-
988
- 工作模式:
989
- 1. 理解用户自然语言请求
990
- 2. 分析需要哪些工具来完成
991
- 3. 按顺序调用工具并观察结果
992
- 4. 根据观察结果决定下一步
993
- 5. 最终给出完整回答
994
-
995
- 重要 (一次命中要求):
996
- - 每次只调用一个工具
997
- - 仔细分析工具返回结果
998
- - 当任务完成时,必须在回答末尾添加 <final gen> 标记表示结束
999
- - 如果需要更多信息,继续调用工具
1000
-
1001
- 【工具调用格式 (严格遵守, 否则系统无法解析)】
1002
- - 你只能输出**一个**工具调用, 不要堆叠多个 invoke
1003
- - 工具调用格式: {"name":"<tool_name>","input":{"arg1":"value1"}}
1004
- - 用 markdown json code block 包裹: \`\`\`json\n{"name":"X","input":{...}}\n\`\`\`
1005
- - 工具调用前可以简短思考 (1-2 句话), 但**不要写长篇 thinking** (会撞 max_tokens)
1006
- - 工具调用后必须等结果, 不要在同一个回复里继续输出
1040
+ const systemPrompt = `${this.bootstrapAddition}你是 ${this.identity.name},基于ReAct (Reasoning + Acting)模式工作。${this.cachedPersonaSection}
1041
+ 当前工作目录: ${this.cwd}
1042
+ 当前身份: ${this.identity.name} (${this.identity.did})
1043
+ ${this.currentIntentHint}
1044
+
1045
+ ${this.getToolDefinitions()}
1046
+
1047
+ 工作模式:
1048
+ 1. 理解用户自然语言请求
1049
+ 2. 分析需要哪些工具来完成
1050
+ 3. 按顺序调用工具并观察结果
1051
+ 4. 根据观察结果决定下一步
1052
+ 5. 最终给出完整回答
1053
+
1054
+ 重要 (一次命中要求):
1055
+ - 每次只调用一个工具
1056
+ - 仔细分析工具返回结果
1057
+ - 当任务完成时,必须在回答末尾添加 <final gen> 标记表示结束
1058
+ - 如果需要更多信息,继续调用工具
1059
+
1060
+ 【工具调用格式 (严格遵守, 否则系统无法解析)】
1061
+ - 你只能输出**一个**工具调用, 不要堆叠多个 invoke
1062
+ - 工具调用格式: {"name":"<tool_name>","input":{"arg1":"value1"}}
1063
+ - 用 markdown json code block 包裹: \`\`\`json\n{"name":"X","input":{...}}\n\`\`\`
1064
+ - 工具调用前可以简短思考 (1-2 句话), 但**不要写长篇 thinking** (会撞 max_tokens)
1065
+ - 工具调用后必须等结果, 不要在同一个回复里继续输出
1007
1066
  - <final gen> 只在**真完成所有任务**时输出, 不要在工具调用前/中输出${this.judgmentGateAddition}${this.contextHintAddition}`;
1008
1067
  // 2026-06-15: 把 currentOnStream 传给 loop, 让 step-timeline 在 pivot 循环里也能 emit step_start/done
1009
1068
  // 之前 loop.execute() 不接 streamCallback, 导致 step-timeline 只能看到老 runReActLoop 路径
@@ -1070,6 +1129,10 @@ ${this.getToolDefinitions()}
1070
1129
  // 上限=2 次 (用户要求"运行一两次"), 结束后按用户需求为准.
1071
1130
  let loopReviewCount = 0;
1072
1131
  const loopReviewCompletedTools = new Set();
1132
+ // 2026-08-09: 本轮行动日志 — 每轮工具执行都记录 (args + 结果摘要),
1133
+ // final 前 review 用逐条核查目标; 也注入 system prompt 让 LLM 看到连续进度
1134
+ // (防"每轮都像重启" — 之前 LLM 看不到自己已完成什么, 容易重复 react)
1135
+ const loopActionLog = [];
1073
1136
  // 发送循环开始的事件
1074
1137
  if (onStream) {
1075
1138
  onStream({ type: 'status', content: '🔄 开始 ReAct 循环...', tool: 'system' });
@@ -1186,32 +1249,47 @@ ${this.getToolDefinitions()}
1186
1249
  }
1187
1250
  // M2.4: persona section 缓存 — persona 在 loadPersona() 时一次设定, 此后不变
1188
1251
  if (!this.cachedPersonaSection && this.persona) {
1189
- this.cachedPersonaSection = `
1190
- 角色描述: ${this.persona.description || '无'}
1191
- 性格特点: ${this.persona.personality || '无'}
1192
- 问候语: ${this.persona.greeting || '无'}
1252
+ this.cachedPersonaSection = `
1253
+ 角色描述: ${this.persona.description || '无'}
1254
+ 性格特点: ${this.persona.personality || '无'}
1255
+ 问候语: ${this.persona.greeting || '无'}
1193
1256
  `;
1194
1257
  }
1195
1258
  const personaSection = this.cachedPersonaSection;
1196
- const systemPrompt = `${this.bootstrapAddition}你是 ${this.identity.name},基于ReAct (Reasoning + Acting)模式工作。${personaSection}
1197
- 当前工作目录: ${this.cwd}
1198
- 当前身份: ${this.identity.name} (${this.identity.did})
1199
- ${refineContext}
1200
- ${this.currentIntentHint}
1201
-
1202
- ${toolDefs}
1203
-
1204
- 工作模式:
1205
- 1. 理解用户自然语言请求
1206
- 2. 分析需要哪些工具来完成
1207
- 3. 按顺序调用工具并观察结果
1208
- 4. 根据观察结果决定下一步
1209
- 5. 最终给出完整回答
1210
-
1211
- 重要:
1212
- - 每次只调用一个工具
1213
- - 仔细分析工具返回结果
1214
- - 当任务完成时,必须在回答末尾添加 <final gen> 标记表示结束
1259
+ // 2026-08-09: 循环进度段 LLM 看到本轮已完成的动作 (连续进度, 不重启)
1260
+ // Hermes 式 Agent Runtime: 循环是状态机, LLM 每次看到的是"第 N 步 + 已完成 X"
1261
+ // (之前每轮都是全新上下文, LLM 不知道做过什么 → 重复 react / 衔接差)
1262
+ let loopProgressSection = '';
1263
+ if (loopActionLog.length > 0) {
1264
+ const actionLines = loopActionLog
1265
+ .map((a, i) => {
1266
+ const args = a.argsPreview ? `(${a.argsPreview.slice(0, 60)})` : '';
1267
+ const res = a.success ? '✓' : '✗';
1268
+ return ` ${i + 1}. ${res} ${a.tool}${args}`;
1269
+ })
1270
+ .join('\n');
1271
+ loopProgressSection = `\n【本轮循环进度】你已完成以下 ${loopActionLog.length} 个动作, 这是连续执行的同一轮任务:\n${actionLines}\n请基于已有结果继续推进, 不要重复执行上面已成功的动作. 全部完成后用 <final gen> 结束.\n`;
1272
+ }
1273
+ const systemPrompt = `${this.bootstrapAddition}你是 ${this.identity.name},基于ReAct (Reasoning + Acting)模式工作。${personaSection}
1274
+ 当前工作目录: ${this.cwd}
1275
+ 当前身份: ${this.identity.name} (${this.identity.did})
1276
+ ${refineContext}
1277
+ ${this.currentIntentHint}
1278
+ ${loopProgressSection}
1279
+
1280
+ ${toolDefs}
1281
+
1282
+ 工作模式:
1283
+ 1. 理解用户自然语言请求
1284
+ 2. 分析需要哪些工具来完成
1285
+ 3. 按顺序调用工具并观察结果
1286
+ 4. 根据观察结果决定下一步
1287
+ 5. 最终给出完整回答
1288
+
1289
+ 重要:
1290
+ - 每次只调用一个工具
1291
+ - 仔细分析工具返回结果
1292
+ - 当任务完成时,必须在回答末尾添加 <final gen> 标记表示结束
1215
1293
  - 如果需要更多信息,继续调用工具${this.judgmentGateAddition}${this.contextHintAddition}`;
1216
1294
  // 3 个恢复机制 (Claude Code 论文 9-step pipeline 内部):
1217
1295
  // 1. max output token 升级 (最多 3 次, 每次 maxOutputTokens 翻倍)
@@ -1302,9 +1380,10 @@ ${toolDefs}
1302
1380
  }
1303
1381
  console.log(`[PiAgent] LLM 回复长度: ${reply.length}, 内容预览: "${reply.substring(0, 80)}..."`);
1304
1382
  console.log(`[PiAgent] LLM 完整回复:\n${reply}`);
1305
- // 通知前端:收到 LLM 回复
1383
+ // 通知前端:收到 LLM 回复 (2026-08-09: 不再截断 100 字符 — 前端流式渲染完整内容,
1384
+ // 配合 Hermes 式回复框: 加载中显示完整文本, 完成后封闭底框)
1306
1385
  if (onStream) {
1307
- onStream({ type: 'token', content: reply.substring(0, 100) });
1386
+ onStream({ type: 'token', content: reply });
1308
1387
  }
1309
1388
  // 2026-06-19 架构 fix: parseToolCall 优先于 isFinalResponse
1310
1389
  // 之前: 思考块里的 "<final gen>" 触发 isFinalResponse 提前 break, 工具从未真正执行
@@ -1356,9 +1435,10 @@ ${toolDefs}
1356
1435
  content: reply,
1357
1436
  toolCalls: toolCalls.length > 1 ? toolCalls : [toolCalls[0]],
1358
1437
  });
1359
- // 顺序执行每个工具
1360
- for (let ti = 0; ti < toolCalls.length; ti++) {
1361
- const toolCall = toolCalls[ti];
1438
+ // 2026-08-09: 并发执行本轮所有工具 (Hermes 式 Agent Runtime: 一轮内多工具并行,
1439
+ // 一轮没跑完之前不中断 工具执行不检查 abort, 全部完成才 continue 下一轮)
1440
+ // 旧实现顺序 for 循环, 一个工具等一个, 慢; 且多工具时 LLM 要等全部串完才能看到结果.
1441
+ await Promise.all(toolCalls.map(async (toolCall, ti) => {
1362
1442
  const isMulti = toolCalls.length > 1;
1363
1443
  // 通知前端
1364
1444
  if (onStream) {
@@ -1391,7 +1471,7 @@ ${toolDefs}
1391
1471
  const denyResultMsg = { success: false, error: `拒绝: [${denyResult.source}] ${denyResult.reason}` };
1392
1472
  this.messageHistory.push({ role: 'tool', content: JSON.stringify(denyResultMsg), toolResult: denyResultMsg });
1393
1473
  this.logToHarness(toolCall.name, toolCall.args, denyResultMsg);
1394
- continue;
1474
+ return;
1395
1475
  }
1396
1476
  if (denyResult.systemAddition) {
1397
1477
  this.contextHintAddition += '\n' + denyResult.systemAddition;
@@ -1410,7 +1490,7 @@ ${toolDefs}
1410
1490
  if (onStream)
1411
1491
  onStream({ type: 'status', content: `💡 Reflection: ${obs.summary}`, tool: 'system' });
1412
1492
  console.warn(`[PiAgent] 未知工具: ${toolCall.name} (累计 ${totalErrors}/${this.MAX_TOTAL_ERRORS}),跳过并继续`);
1413
- continue;
1493
+ return;
1414
1494
  }
1415
1495
  // Bootstrap PreToolUse hook: 调工具前校验 (危险命令拦截)
1416
1496
  // 失败静默 — hook 自身挂掉 = 放行
@@ -1454,7 +1534,7 @@ ${toolDefs}
1454
1534
  this.messageHistory.push({ role: 'system', content: `[注意] 连续 ${consecutiveErrors} 次工具调用被系统拒绝. 请换其他工具或直接回答用户, 末尾加 <final gen>.` });
1455
1535
  consecutiveErrors = 0;
1456
1536
  }
1457
- continue;
1537
+ return;
1458
1538
  }
1459
1539
  }
1460
1540
  catch (err) {
@@ -1491,7 +1571,7 @@ ${toolDefs}
1491
1571
  this.messageHistory.push({ role: 'system', content: `[注意] 连续 ${consecutiveErrors} 次工具调用被 Harness 拒绝. 请换其他工具或直接回答.` });
1492
1572
  consecutiveErrors = 0;
1493
1573
  }
1494
- continue;
1574
+ return;
1495
1575
  }
1496
1576
  }
1497
1577
  catch (err) {
@@ -1528,6 +1608,20 @@ ${toolDefs}
1528
1608
  }
1529
1609
  this.messageHistory.push({ role: 'tool', content: JSON.stringify(result), toolResult: result, toolCallId: toolCall.id || `call_${Date.now()}_${Math.random().toString(36).slice(2, 8)}` });
1530
1610
  this.logToHarness(toolCall.name, toolCall.args, result);
1611
+ // 2026-08-09: 记录到本轮行动日志 (循环进度 + final 前目标核查用)
1612
+ // 去重: 同一工具同 args 连续成功只记一次 (防 LLM 重复 react 刷屏)
1613
+ const argsPreview = JSON.stringify(toolCall.args || {}).slice(0, 120);
1614
+ const isDup = loopActionLog.some((a) => a.tool === toolCall.name && a.argsPreview === argsPreview && a.success === !!result.success);
1615
+ if (!isDup) {
1616
+ loopActionLog.push({
1617
+ tool: toolCall.name,
1618
+ argsPreview,
1619
+ resultPreview: result.success
1620
+ ? String(result.output || '(无输出)').slice(0, 200)
1621
+ : String(result.error || 'failed').slice(0, 200),
1622
+ success: !!result.success,
1623
+ });
1624
+ }
1531
1625
  if (onStream) {
1532
1626
  if (result.success) {
1533
1627
  onStream({ type: 'status', content: `✅ ${toolCall.name} 执行成功`, tool: toolCall.name });
@@ -1585,7 +1679,7 @@ ${toolDefs}
1585
1679
  lastFailedTool = '';
1586
1680
  lastFailedToolCount = 0;
1587
1681
  consecutiveErrors = 0;
1588
- continue;
1682
+ return;
1589
1683
  }
1590
1684
  if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
1591
1685
  this.messageHistory.push({ role: 'system', content: `[注意] 前面的工具调用连续失败。请尝试其他工具或换一种方式完成用户请求, 或用 <final gen> 给出最终回答.` });
@@ -1606,7 +1700,7 @@ ${toolDefs}
1606
1700
  onStream({ type: 'status', content: `💡 Reflection: ${obs.summary}`, tool: 'system' });
1607
1701
  console.error(`[PiAgent] 工具执行异常 (累计 ${totalErrors}/${this.MAX_TOTAL_ERRORS}): ${execError}`);
1608
1702
  }
1609
- } // end for (ti)
1703
+ })); // end Promise.all(toolCalls.map(async ...))
1610
1704
  // 所有工具执行完毕后, continue while 循环, 让 LLM 看到结果
1611
1705
  continue;
1612
1706
  }
@@ -1616,9 +1710,9 @@ ${toolDefs}
1616
1710
  role: 'assistant',
1617
1711
  content: reply
1618
1712
  });
1619
- // 通知前端收到非工具调用回复
1713
+ // 通知前端收到非工具调用回复 (2026-08-09: 完整内容, 不再截断 150)
1620
1714
  if (onStream) {
1621
- onStream({ type: 'token', content: reply.substring(0, 150) });
1715
+ onStream({ type: 'token', content: reply });
1622
1716
  }
1623
1717
  // 2026-06-19 架构 fix: 只有 strip <think> 后才检查 isFinalResponse
1624
1718
  // (parseToolCall 已先尝试, 既然没解析出 tool_call, 现在检查 final gen 是否真的在最终回答区)
@@ -1661,6 +1755,7 @@ ${toolDefs}
1661
1755
  reviewsDone: loopReviewCount,
1662
1756
  userIntent: this.currentIntentHint,
1663
1757
  completedTools: Array.from(loopReviewCompletedTools),
1758
+ actionLog: loopActionLog,
1664
1759
  }, DEFAULT_MAX_REVIEWS);
1665
1760
  if (reviewDecision.kind === 'continue-review') {
1666
1761
  loopReviewCount++;
@@ -2248,37 +2343,37 @@ ${toolDefs}
2248
2343
  getDefaultResponse(input) {
2249
2344
  const operationsRef = PiAgentSession.getOperationsReference();
2250
2345
  if (operationsRef) {
2251
- return `收到了: "${input}"
2252
-
2253
- 我是一个判断力处理智能体,支持自然语言交互。
2254
-
2255
- 可用操作(直接说出即可):
2256
- ${this.extractOperationsFromRef(operationsRef)}
2257
-
2258
- 示例请求:
2259
- - "读取 src/index.ts 文件"
2260
- - "总结一下 README.md"
2261
- - "查看当前连接了哪些节点"
2346
+ return `收到了: "${input}"
2347
+
2348
+ 我是一个判断力处理智能体,支持自然语言交互。
2349
+
2350
+ 可用操作(直接说出即可):
2351
+ ${this.extractOperationsFromRef(operationsRef)}
2352
+
2353
+ 示例请求:
2354
+ - "读取 src/index.ts 文件"
2355
+ - "总结一下 README.md"
2356
+ - "查看当前连接了哪些节点"
2262
2357
  - "向 QmABC... 发送测试消息"`;
2263
2358
  }
2264
- return `收到了: "${input}"
2265
-
2266
- 我是一个判断力处理智能体,支持自然语言交互。
2267
-
2268
- 可用操作(直接说出即可):
2269
- - "读取 README.md" - 读取并分析文档
2270
- - "总结文档" - 总结文档内容
2271
- - "改进文档,按照X要求" - 改进文档
2272
- - "查看节点" - 查看已连接的对等节点
2273
- - "向X发送消息Y" - 向对等节点发送消息
2274
- - "广播消息X" - 广播消息到所有节点
2275
- - "查看身份" - 查看当前智能体身份
2276
- - "查看日志" - 查看最近操作日志
2277
-
2278
- 示例请求:
2279
- - "读取 src/index.ts 文件"
2280
- - "总结一下 README.md"
2281
- - "查看当前连接了哪些节点"
2359
+ return `收到了: "${input}"
2360
+
2361
+ 我是一个判断力处理智能体,支持自然语言交互。
2362
+
2363
+ 可用操作(直接说出即可):
2364
+ - "读取 README.md" - 读取并分析文档
2365
+ - "总结文档" - 总结文档内容
2366
+ - "改进文档,按照X要求" - 改进文档
2367
+ - "查看节点" - 查看已连接的对等节点
2368
+ - "向X发送消息Y" - 向对等节点发送消息
2369
+ - "广播消息X" - 广播消息到所有节点
2370
+ - "查看身份" - 查看当前智能体身份
2371
+ - "查看日志" - 查看最近操作日志
2372
+
2373
+ 示例请求:
2374
+ - "读取 src/index.ts 文件"
2375
+ - "总结一下 README.md"
2376
+ - "查看当前连接了哪些节点"
2282
2377
  - "向 QmABC... 发送测试消息"`;
2283
2378
  }
2284
2379
  extractOperationsFromRef(ref) {
@@ -2303,13 +2398,13 @@ ${this.extractOperationsFromRef(operationsRef)}
2303
2398
  }
2304
2399
  }
2305
2400
  return operationLines.length > 0 ? operationLines.join('\n') :
2306
- ` - "读取 README.md" - 读取并分析文档
2307
- - "总结文档" - 总结文档内容
2308
- - "改进文档,按照X要求" - 改进文档
2309
- - "查看节点" - 查看已连接的对等节点
2310
- - "向X发送消息Y" - 向对等节点发送消息
2311
- - "广播消息X" - 广播消息到所有节点
2312
- - "查看身份" - 查看当前智能体身份
2401
+ ` - "读取 README.md" - 读取并分析文档
2402
+ - "总结文档" - 总结文档内容
2403
+ - "改进文档,按照X要求" - 改进文档
2404
+ - "查看节点" - 查看已连接的对等节点
2405
+ - "向X发送消息Y" - 向对等节点发送消息
2406
+ - "广播消息X" - 广播消息到所有节点
2407
+ - "查看身份" - 查看当前智能体身份
2313
2408
  - "查看日志" - 查看最近操作日志`;
2314
2409
  }
2315
2410
  async suggestRename(messages) {
@@ -61,6 +61,8 @@ export async function createSkill(name, description, body, opts = {}) {
61
61
  try {
62
62
  await fs.mkdir(skillDir, { recursive: true });
63
63
  await fs.writeFile(file, content, 'utf-8');
64
+ // 2026-08-08 (DID 目录写穿): skill 落盘同步进 DID 目录 skills 表 (失败静默)
65
+ catalogSkillUpsert(`skill/${safeName}`, { name: safeName, kind: 'skill', description, file, size: content.length, updatedAt: Date.now() });
64
66
  return { ok: true, path: file };
65
67
  }
66
68
  catch (e) {
@@ -115,6 +117,8 @@ export async function updateSkill(name, opts) {
115
117
  }
116
118
  const content = '---\n' + fmLines.join('\n') + '\n---\n\n' + body + '\n';
117
119
  await fs.writeFile(file, content, 'utf-8');
120
+ // 2026-08-08 (DID 目录写穿): skill 更新同步进 DID 目录 skills 表 (失败静默)
121
+ catalogSkillUpsert(`skill/${safeName}`, { name: safeName, kind: 'skill', description: String(opts.description || ''), file, size: content.length, updatedAt: Date.now() });
118
122
  return { ok: true, path: file };
119
123
  }
120
124
  catch (e) {
@@ -166,8 +170,23 @@ export async function writeSkillCandidate(c) {
166
170
  catch { /* 新文件 */ }
167
171
  const merged = { ...c, runs, body, timestamp: c.timestamp || new Date().toISOString() };
168
172
  await fs.writeFile(file, JSON.stringify(merged, null, 2), 'utf-8');
173
+ // 2026-08-08 (DID 目录写穿): 候选同步进 DID 目录 skills 表 (失败静默)
174
+ catalogSkillUpsert(`candidate/${safeName}`, {
175
+ name: safeName, kind: 'candidate', description: c.description,
176
+ file, runs, signature: c.signature, size: merged.body.length, updatedAt: Date.now(),
177
+ });
169
178
  return file;
170
179
  }
180
+ /** 2026-08-08: skill 写入 → DID 目录 skills 表写穿 (WAL 事件 → 多设备同步/OrbitDB 复制). 失败静默. */
181
+ function catalogSkillUpsert(key, data) {
182
+ (async () => {
183
+ try {
184
+ const { catalogUpsertQuiet } = await import('../storage/did-catalog-bridge.js');
185
+ await catalogUpsertQuiet('skills', key, data);
186
+ }
187
+ catch { /* 静默 */ }
188
+ })();
189
+ }
171
190
  export async function listSkillCandidates(home = os.homedir()) {
172
191
  const dir = getCandidateDir(home);
173
192
  let entries;
@@ -260,7 +260,7 @@ export class WorkflowPivotLoop {
260
260
  const llmResponse = await llm.chat(context, headerForThisIter, signal, openAITools);
261
261
  const reply = (llmResponse.reply || '').trim();
262
262
  this.vlog(`[pivot] iter=${this.state.iteration} LLM took=${Date.now() - t0}ms reply=${reply.length} nativeToolCalls=${llmResponse.toolCalls?.length ?? 0} head=${reply.substring(0, 80).replace(/\n/g, ' ')}`);
263
- this.emit({ type: 'token', content: reply.substring(0, 100) });
263
+ this.emit({ type: 'token', content: reply });
264
264
  // 2026-07-06: 把完整 reply 推给前端 — 前端按需更新临时气泡
265
265
  // 之前只 emit token(100B 截断), 前端拿到 100B 看不清. 现在 emit preview 带完整 content.
266
266
  // 折叠 / 清洗交给前端的 message-renderer.addMessage (类型 ai 入口统一 strip).
@@ -778,8 +778,8 @@ export class WorkflowPivotLoop {
778
778
  // 完整重装只在 iter=1 和 compact 之后两次
779
779
  const lines = fullSystemPrompt.split('\n');
780
780
  const identityLine = lines.find((l) => l.includes('你是') && !l.includes('(续)')) ?? '';
781
- return `[continuation] 你的 persona + tools + judgments 已在首轮 messageHistory 中.
782
- ${identityLine ? identityLine + '\n' : ''}当前 step 别再读 persona 全文, 继续推进任务即可.
781
+ return `[continuation] 你的 persona + tools + judgments 已在首轮 messageHistory 中.
782
+ ${identityLine ? identityLine + '\n' : ''}当前 step 别再读 persona 全文, 继续推进任务即可.
783
783
  > <final gen> 只在真完成时输出.`;
784
784
  }
785
785
  /**
@@ -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
  }