@bolloon/bolloon-agent 0.1.1 → 0.1.2

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 (200) hide show
  1. package/bin/bolloon-cli.cjs +157 -0
  2. package/bin/bolloon-daemon.sh +207 -0
  3. package/bin/bolloon.cmd +11 -0
  4. package/dist/agents/constraint-layer.js +10 -15
  5. package/dist/agents/pi-sdk.js +433 -106
  6. package/dist/agents/protocol.js +82 -1
  7. package/dist/agents/subagent-manager.js +2 -2
  8. package/dist/agents/workflow-engine.js +15 -20
  9. package/dist/agents/workflow-pivot-loop.js +541 -0
  10. package/dist/bollharness/src/index.js +5 -0
  11. package/dist/bollharness/src/scripts/checks/check_adr_plan_numbering.js +6 -0
  12. package/dist/bollharness/src/scripts/checks/check_api_types.js +45 -0
  13. package/dist/bollharness/src/scripts/checks/check_artifact_link.js +146 -0
  14. package/dist/bollharness/src/scripts/checks/check_bridge_deps.js +6 -0
  15. package/dist/bollharness/src/scripts/checks/check_bugfix_binding.js +6 -0
  16. package/dist/bollharness/src/scripts/checks/check_bugfix_binding_ci.js +6 -0
  17. package/dist/bollharness/src/scripts/checks/check_doc_file_references.js +6 -0
  18. package/dist/bollharness/src/scripts/checks/check_doc_freshness.js +135 -0
  19. package/dist/bollharness/src/scripts/checks/check_doc_links.js +31 -0
  20. package/dist/bollharness/src/scripts/checks/check_file_existence_claims.js +6 -0
  21. package/dist/bollharness/src/scripts/checks/check_fragment_integrity.js +34 -0
  22. package/dist/bollharness/src/scripts/checks/check_hook_installed.js +63 -0
  23. package/dist/bollharness/src/scripts/checks/check_issue_closure.js +41 -0
  24. package/dist/bollharness/src/scripts/checks/check_mcp_parity.js +6 -0
  25. package/dist/bollharness/src/scripts/checks/check_security.js +48 -0
  26. package/dist/bollharness/src/scripts/checks/check_skill_parity.js +6 -0
  27. package/dist/bollharness/src/scripts/checks/check_versions.js +6 -0
  28. package/dist/bollharness/src/scripts/checks/finding.js +13 -0
  29. package/dist/bollharness/src/scripts/checks/next_decision_number.js +20 -0
  30. package/dist/bollharness/src/scripts/checks/regenerate_magic_docs.js +6 -0
  31. package/dist/bollharness/src/scripts/ci/detect_rebaseline_triggers.js +8 -0
  32. package/dist/bollharness/src/scripts/ci/scan_subprocess_cfg.js +8 -0
  33. package/dist/bollharness/src/scripts/ci/scan_verify_artifacts.js +8 -0
  34. package/dist/bollharness/src/scripts/ci/scan_yaml_schema.js +8 -0
  35. package/dist/bollharness/src/scripts/context_router.js +67 -0
  36. package/dist/bollharness/src/scripts/deploy-guard.js +157 -0
  37. package/dist/bollharness/src/scripts/guard-feedback.js +192 -0
  38. package/dist/bollharness/src/scripts/guard_router.js +158 -0
  39. package/dist/bollharness/src/scripts/hooks/_hook_output.js +6 -0
  40. package/dist/bollharness/src/scripts/hooks/auto-python3.js +6 -0
  41. package/dist/bollharness/src/scripts/hooks/deploy-progress-on-session-end.js +6 -0
  42. package/dist/bollharness/src/scripts/hooks/failure-analyzer.js +6 -0
  43. package/dist/bollharness/src/scripts/hooks/gate-judgment-inject.js +92 -0
  44. package/dist/bollharness/src/scripts/hooks/gate-transition-judgment.js +63 -0
  45. package/dist/bollharness/src/scripts/hooks/inbox-ack.js +6 -0
  46. package/dist/bollharness/src/scripts/hooks/inbox-inject-on-start.js +6 -0
  47. package/dist/bollharness/src/scripts/hooks/inbox-validate.js +6 -0
  48. package/dist/bollharness/src/scripts/hooks/inbox-write-ledger.js +6 -0
  49. package/dist/bollharness/src/scripts/hooks/initializer-agent.js +6 -0
  50. package/dist/bollharness/src/scripts/hooks/loop-detection.js +73 -0
  51. package/dist/bollharness/src/scripts/hooks/owner-guard.js +6 -0
  52. package/dist/bollharness/src/scripts/hooks/precompact.js +6 -0
  53. package/dist/bollharness/src/scripts/hooks/review-agent-gatekeeper.js +6 -0
  54. package/dist/bollharness/src/scripts/hooks/risk-tracker.js +108 -0
  55. package/dist/bollharness/src/scripts/hooks/sanitize-on-read.js +6 -0
  56. package/dist/bollharness/src/scripts/hooks/session-reflection.js +7 -0
  57. package/dist/bollharness/src/scripts/hooks/session-start-magic-docs.js +7 -0
  58. package/dist/bollharness/src/scripts/hooks/session-start-reset-risk.js +7 -0
  59. package/dist/bollharness/src/scripts/hooks/session-start-toolkit-reminder.js +7 -0
  60. package/dist/bollharness/src/scripts/hooks/stop-evaluator.js +157 -0
  61. package/dist/bollharness/src/scripts/hooks/tool-call-counter.js +6 -0
  62. package/dist/bollharness/src/scripts/hooks/trace-analyzer.js +10 -0
  63. package/dist/bollharness/src/scripts/install/install-trust-token.js +7 -0
  64. package/dist/bollharness/src/scripts/install/multi_project_registry.js +9 -0
  65. package/dist/bollharness/src/scripts/install/phase2_auto.js +21 -0
  66. package/dist/bollharness/src/scripts/install/pre_commit_installer.js +6 -0
  67. package/dist/bollharness/src/scripts/install/tier_selector.js +7 -0
  68. package/dist/bollharness/src/scripts/install/transcript_miner.js +7 -0
  69. package/dist/bollharness/src/scripts/lib/claim_patterns.js +10 -0
  70. package/dist/bollharness/src/scripts/lib/sanitize_patterns.js +12 -0
  71. package/dist/bollharness/src/scripts/sanitize.js +6 -0
  72. package/dist/bollharness-integration/channel-judgment-engine.js +530 -0
  73. package/dist/bollharness-integration/context-chain-router.js +383 -0
  74. package/dist/bollharness-integration/context-router-judgment.js +13 -21
  75. package/dist/bollharness-integration/context-router.js +22 -64
  76. package/dist/bollharness-integration/gate-state-machine.js +14 -19
  77. package/dist/bollharness-integration/gate-transition-hooks.js +16 -61
  78. package/dist/bollharness-integration/guard-checker.js +21 -68
  79. package/dist/bollharness-integration/index.js +14 -124
  80. package/dist/bollharness-integration/integration.js +13 -20
  81. package/dist/bollharness-integration/llm-judgment-engine.js +569 -0
  82. package/dist/bollharness-integration/skill-adapter.js +18 -64
  83. package/dist/cli-entry.js +261 -0
  84. package/dist/constraint-runtime/src/commands.js +17 -7
  85. package/dist/constraint-runtime/src/constraint/budget.js +1 -6
  86. package/dist/constraint-runtime/src/constraint/permission.js +1 -6
  87. package/dist/constraint-runtime/src/models.js +1 -3
  88. package/dist/constraint-runtime/src/tools.js +17 -7
  89. package/dist/constraints/index.js +1 -7
  90. package/dist/documents/reader.js +8 -49
  91. package/dist/heartbeat/DaemonManager.js +242 -0
  92. package/dist/heartbeat/HealthMonitor.js +285 -0
  93. package/dist/heartbeat/StartupVerifier.js +205 -0
  94. package/dist/heartbeat/Watchdog.js +168 -0
  95. package/dist/heartbeat/index.js +84 -0
  96. package/dist/heartbeat/types.js +5 -0
  97. package/dist/index.js +381 -28
  98. package/dist/llm/config-store.js +31 -57
  99. package/dist/llm/llm-judgment-client.js +389 -0
  100. package/dist/llm/pi-ai.js +9 -52
  101. package/dist/network/agent-network.js +46 -90
  102. package/dist/network/hybrid-messenger.js +125 -0
  103. package/dist/network/iroh-bootstrap.js +38 -0
  104. package/dist/network/iroh-discovery.js +145 -0
  105. package/dist/network/iroh-integration.js +9 -16
  106. package/dist/network/iroh-transport.js +10 -48
  107. package/dist/network/p2p.js +23 -62
  108. package/dist/network/storage/adapters/json-adapter.js +4 -42
  109. package/dist/network/storage/index.js +147 -0
  110. package/dist/network/storage/types.js +14 -0
  111. package/dist/pi-ecosystem/index.js +233 -0
  112. package/dist/pi-ecosystem-colony/index.js +29 -90
  113. package/dist/pi-ecosystem-goals/index.js +20 -74
  114. package/dist/pi-ecosystem-judgment/decision.js +29 -47
  115. package/dist/pi-ecosystem-judgment/distillation.js +16 -29
  116. package/dist/pi-ecosystem-judgment/human-value-store.js +13 -60
  117. package/dist/pi-ecosystem-judgment/index.js +21 -74
  118. package/dist/pi-ecosystem-judgment/value-injection.js +26 -72
  119. package/dist/pi-ecosystem-mcp/index.js +24 -78
  120. package/dist/pi-ecosystem-subagents/index.js +20 -69
  121. package/dist/social/ant-colony/AdaptiveHeartbeat.js +3 -8
  122. package/dist/social/ant-colony/PheromoneEngine.js +11 -49
  123. package/dist/social/ant-colony/index.js +6 -0
  124. package/dist/social/ant-colony/types.js +4 -8
  125. package/dist/social/channels/ChannelManager.js +8 -46
  126. package/dist/social/channels/DiapChannelBridge.js +9 -47
  127. package/dist/social/channels/InterestMatcher.js +2 -7
  128. package/dist/social/channels/channel-agent-session.js +309 -0
  129. package/dist/social/channels/channel-heartbeat-agent.js +494 -0
  130. package/dist/social/channels/diap-doc-parser.js +204 -0
  131. package/dist/social/channels/harness-workflow-integrator.js +446 -0
  132. package/dist/social/channels/index.js +9 -0
  133. package/dist/social/channels/types.js +3 -7
  134. package/dist/social/global-shared-context.js +6 -47
  135. package/dist/social/heartbeat.js +29 -72
  136. package/dist/social/persona/enhanced-persona.js +299 -0
  137. package/dist/web/client.js +302 -136
  138. package/dist/web/components/p2p/index.js +159 -9
  139. package/dist/web/components/p2p/p2p-connection.js +136 -0
  140. package/dist/web/components/p2p/p2p-manager.js +24 -0
  141. package/dist/web/components/p2p/p2p-store-memory.js +1 -1
  142. package/dist/web/components/p2p/types.js +7 -0
  143. package/dist/web/index.html +5 -0
  144. package/dist/web/style.css +118 -0
  145. package/package.json +12 -6
  146. package/scripts/build-cli.js +206 -0
  147. package/scripts/postinstall.js +153 -0
  148. package/src/agents/pi-sdk.ts +347 -28
  149. package/src/agents/protocol.ts +95 -1
  150. package/src/agents/workflow-pivot-loop.ts +674 -0
  151. package/src/bollharness/CLAUDE.md +73 -0
  152. package/src/bollharness/README.md +143 -0
  153. package/src/bollharness/README.zh-CN.md +131 -0
  154. package/src/bollharness/reference/boll-reference/scripts/hooks/stop-evaluator.md +57 -0
  155. package/src/bollharness/scripts/context-fragments/artifact-linkage.md +14 -0
  156. package/src/bollharness/scripts/context-fragments/auth-consumers.md +17 -0
  157. package/src/bollharness/scripts/context-fragments/bridge-constitution.md +13 -0
  158. package/src/bollharness/scripts/context-fragments/catalyst-distributed.md +18 -0
  159. package/src/bollharness/scripts/context-fragments/closure-checklist.md +13 -0
  160. package/src/bollharness/scripts/context-fragments/contract-consumers.md +15 -0
  161. package/src/bollharness/scripts/context-fragments/db-shared-structures.md +15 -0
  162. package/src/bollharness/scripts/context-fragments/fixed-three-layers.md +19 -0
  163. package/src/bollharness/scripts/context-fragments/general-dev-principles.md +11 -0
  164. package/src/bollharness/scripts/context-fragments/issue-first.md +8 -0
  165. package/src/bollharness/scripts/context-fragments/mcp-parity.md +16 -0
  166. package/src/bollharness/scripts/context-fragments/pi-agent-operations.md +108 -0
  167. package/src/bollharness/scripts/context-fragments/protocol-consumers.md +15 -0
  168. package/src/bollharness/scripts/context-fragments/run-events-consumers.md +15 -0
  169. package/src/bollharness/scripts/context-fragments/scene-fidelity.md +13 -0
  170. package/src/bollharness/scripts/context-fragments/truth-source-hierarchy.md +15 -0
  171. package/src/bollharness/scripts/context-fragments/two-language.md +15 -0
  172. package/src/bollharness/scripts/context-fragments/version-sources.md +14 -0
  173. package/src/bollharness/scripts/hooks/stop-evaluator.md +83 -0
  174. package/src/bollharness/templates/scaffold/CLAUDE.md +89 -0
  175. package/src/cli-entry.ts +304 -0
  176. package/src/heartbeat/DaemonManager.ts +283 -0
  177. package/src/heartbeat/HealthMonitor.ts +316 -0
  178. package/src/heartbeat/StartupVerifier.ts +223 -0
  179. package/src/heartbeat/Watchdog.ts +198 -0
  180. package/src/heartbeat/index.ts +108 -0
  181. package/src/heartbeat/types.ts +82 -0
  182. package/src/llm/config-store.ts +23 -5
  183. package/src/network/iroh-transport.ts +3 -3
  184. package/src/web/client.js +302 -136
  185. package/src/web/components/p2p/P2PModal.tsx +91 -3
  186. package/src/web/components/p2p/index.ts +171 -9
  187. package/src/web/components/p2p/p2p-connection.ts +153 -1
  188. package/src/web/components/p2p/p2p-manager.ts +39 -1
  189. package/src/web/components/p2p/p2p-store-memory.ts +1 -1
  190. package/src/web/components/p2p/p2p-tools.ts +315 -0
  191. package/src/web/components/p2p/types.ts +58 -0
  192. package/src/web/design.md +99 -0
  193. package/src/web/index.html +5 -0
  194. package/src/web/server.ts +353 -36
  195. package/src/web/style.css +118 -0
  196. package/tsconfig.cli.json +16 -0
  197. package/tsconfig.electron.json +1 -1
  198. package/tsconfig.json +1 -2
  199. package/dist/web/server.js +0 -1647
  200. package/dist/web/server.js.map +0 -1
@@ -12,6 +12,7 @@ import { p2pNetwork } from '../network/p2p.js';
12
12
  import { ConstraintLayer, WorkflowContext } from './constraint-layer.js';
13
13
  import { WorkflowEngine, WorkflowStep, StepResult, Workflow } from './workflow-engine.js';
14
14
  import { DeepThinkingEngine, AgentCoordinator, type ThinkResult, type AgentResult } from '@bolloon/constraint-runtime';
15
+ import { WorkflowPivotLoop, createDefaultPivotConfig, type PivotLoopConfig, type LoopResult } from './workflow-pivot-loop.js';
15
16
  import {
16
17
  DiscoveredAgentsManager,
17
18
  SocialHeartbeat,
@@ -42,6 +43,8 @@ export interface AgentSessionConfig {
42
43
  cwd: string;
43
44
  peerId?: string;
44
45
  identityDoc?: IdentityDoc;
46
+ usePivotLoop?: boolean;
47
+ pivotLoopConfig?: PivotLoopConfig;
45
48
  }
46
49
 
47
50
  export interface IdentityDoc {
@@ -487,6 +490,7 @@ export interface HeartbeatConfig {
487
490
  export interface AgentSession {
488
491
  prompt(input: string): Promise<string>;
489
492
  promptStream(input: string, onStream: StreamCallback): Promise<string>;
493
+ promptWithPivotLoop(input: string, config?: PivotLoopConfig): Promise<LoopResult>;
490
494
  suggestRename(messages: { type: string; content: string }[]): Promise<string | null>;
491
495
  readDocument(filePath: string): Promise<string>;
492
496
  summarizeDocument(filePath: string, context?: string): Promise<{
@@ -546,11 +550,15 @@ class PiAgentSession implements AgentSession {
546
550
  private messageHistory: Message[] = [];
547
551
  private tools: Map<string, Tool> = new Map();
548
552
  private skillRegistry: SkillRegistry = new SkillRegistry();
549
- private readonly MAX_REACT_ITERATIONS = 10;
553
+ private readonly MAX_REACT_ITERATIONS = 100;
554
+ private readonly MAX_REFINE_ATTEMPTS = 3;
555
+ private readonly QUALITY_THRESHOLD = 0.6;
550
556
  private thinkingEngine = new DeepThinkingEngine(3);
551
557
  private coordinator = new AgentCoordinator(3);
552
558
  private harness: any = null;
553
559
  private harnessEnabled = false;
560
+ private usePivotLoop: boolean = false;
561
+ private pivotLoopConfig?: PivotLoopConfig;
554
562
 
555
563
  constructor(config: AgentSessionConfig) {
556
564
  this.cwd = config.cwd;
@@ -561,6 +569,8 @@ class PiAgentSession implements AgentSession {
561
569
  this.workflowEngine = new WorkflowEngine(this.constraintLayer);
562
570
  this.sessionManager = new PiSessionManager(this.identity.did, this.cwd);
563
571
  this.agentsManager = new DiscoveredAgentsManager();
572
+ this.usePivotLoop = config.usePivotLoop ?? false;
573
+ this.pivotLoopConfig = config.pivotLoopConfig;
564
574
  this.initSession();
565
575
  this.registerTools();
566
576
  this.initHarness();
@@ -739,6 +749,58 @@ class PiAgentSession implements AgentSession {
739
749
  };
740
750
  }
741
751
  });
752
+
753
+ // 添加文件列表工具
754
+ this.tools.set('list_files', {
755
+ name: 'list_files',
756
+ description: '列出目录中的文件',
757
+ parameters: { path: '目录路径(可选,默认为当前目录)' },
758
+ execute: async (args) => {
759
+ try {
760
+ const fs = await import('fs');
761
+ const path = args.path || this.cwd;
762
+ const files = fs.readdirSync(path);
763
+ return {
764
+ success: true,
765
+ output: `📁 目录 ${path} 中的文件 (${files.length} 个):\n${files.slice(0, 20).map(f => ` - ${f}`).join('\n')}${files.length > 20 ? '\n ...' : ''}`
766
+ };
767
+ } catch (e) {
768
+ return { success: false, error: String(e) };
769
+ }
770
+ }
771
+ });
772
+
773
+ // 添加目录读取工具(更完整的实现)
774
+ this.tools.set('read_directory', {
775
+ name: 'read_directory',
776
+ description: '读取目录内容,返回文件列表和目录结构',
777
+ parameters: { path: '目录路径(可选,默认为当前目录)' },
778
+ execute: async (args) => {
779
+ try {
780
+ const fs = await import('fs');
781
+ const pathModule = await import('path');
782
+ const targetPath = args.path || this.cwd;
783
+ const items = fs.readdirSync(targetPath);
784
+ const result: string[] = [];
785
+ for (const item of items.slice(0, 30)) {
786
+ const fullPath = pathModule.join(targetPath, item);
787
+ try {
788
+ const stat = fs.statSync(fullPath);
789
+ const type = stat.isDirectory() ? '📁' : '📄';
790
+ result.push(`${type} ${item}${stat.isDirectory() ? '/' : ''}`);
791
+ } catch {
792
+ result.push(`📄 ${item}`);
793
+ }
794
+ }
795
+ return {
796
+ success: true,
797
+ output: `📂 ${targetPath} (${items.length} 项):\n${result.join('\n')}${items.length > 30 ? '\n... 还有更多文件' : ''}`
798
+ };
799
+ } catch (e) {
800
+ return { success: false, error: `无法读取目录: ${String(e)}` };
801
+ }
802
+ }
803
+ });
742
804
  }
743
805
 
744
806
  private getToolDefinitions(): string {
@@ -803,7 +865,7 @@ class PiAgentSession implements AgentSession {
803
865
  content: input
804
866
  });
805
867
 
806
- onStream({ type: 'thinking', content: '🤔 思考中...' });
868
+ onStream({ type: 'thinking', content: '🤔 开始思考...' });
807
869
 
808
870
  if (!this.minimaxAvailable) {
809
871
  const response = await this.handleFallback(input);
@@ -812,22 +874,113 @@ class PiAgentSession implements AgentSession {
812
874
  return response;
813
875
  }
814
876
 
815
- const result = await this.runReActLoop();
877
+ const result = await this.runReActLoop(onStream);
816
878
  onStream({ type: 'done', content: '' });
817
879
  return result;
818
880
  }
819
881
 
820
- private async runReActLoop(): Promise<string> {
882
+ async promptWithPivotLoop(input: string, config?: PivotLoopConfig): Promise<LoopResult> {
883
+ if (!this.minimaxAvailable) {
884
+ const response = await this.handleFallback(input);
885
+ return {
886
+ success: false,
887
+ response,
888
+ iterations: 0,
889
+ toolCalls: 0,
890
+ qualityScore: 0,
891
+ exitReason: 'error',
892
+ state: {
893
+ iteration: 0,
894
+ totalTokens: 0,
895
+ toolCallsCount: 0,
896
+ consecutiveNoProgress: 0,
897
+ qualityScores: [],
898
+ pendingToolUses: [],
899
+ lastMeaningfulWork: 0
900
+ }
901
+ };
902
+ }
903
+
904
+ const llm = getMinimax();
905
+ const loopConfig = config || this.pivotLoopConfig || createDefaultPivotConfig();
906
+ const loop = new WorkflowPivotLoop(loopConfig);
907
+
908
+ for (const tool of this.tools.values()) {
909
+ loop.registerTool(tool);
910
+ }
911
+
912
+ const personaSection = this.persona ? `
913
+ 角色描述: ${this.persona.description || '无'}
914
+ 性格特点: ${this.persona.personality || '无'}
915
+ 问候语: ${this.persona.greeting || '无'}
916
+ ` : '';
917
+
918
+ const systemPrompt = `你是 ${this.identity.name},基于ReAct (Reasoning + Acting)模式工作。${personaSection}
919
+ 当前工作目录: ${this.cwd}
920
+ 当前身份: ${this.identity.name} (${this.identity.did})
921
+
922
+ ${this.getToolDefinitions()}
923
+
924
+ 工作模式:
925
+ 1. 理解用户自然语言请求
926
+ 2. 分析需要哪些工具来完成
927
+ 3. 按顺序调用工具并观察结果
928
+ 4. 根据观察结果决定下一步
929
+ 5. 最终给出完整回答
930
+
931
+ 重要:
932
+ - 每次只调用一个工具
933
+ - 仔细分析工具返回结果
934
+ - 当任务完成时,必须在回答末尾添加 <final gen> 标记表示结束
935
+ - 如果需要更多信息,继续调用工具`;
936
+
937
+ const result = await loop.execute(input, llm, systemPrompt);
938
+
939
+ this.messageHistory.push({ role: 'user', content: input });
940
+ if (result.response) {
941
+ this.messageHistory.push({ role: 'assistant', content: result.response });
942
+ }
943
+
944
+ return result;
945
+ }
946
+
947
+ private async runReActLoop(onStream?: StreamCallback): Promise<string> {
821
948
  const llm = getMinimax();
822
949
  let iteration = 0;
823
950
  let finalResponse = '';
951
+ let lastQualityScore = 0;
952
+ let refineAttempts = 0;
953
+ let consecutiveErrors = 0;
954
+ const MAX_CONSECUTIVE_ERRORS = 3;
955
+
956
+ // 发送循环开始的事件
957
+ if (onStream) {
958
+ onStream({ type: 'status', content: '🔄 开始 ReAct 循环...', tool: 'system' });
959
+ }
824
960
 
825
961
  while (iteration < this.MAX_REACT_ITERATIONS) {
826
962
  iteration++;
827
963
 
964
+ // 调试日志:显示每次循环开始
965
+ console.log(`[PiAgent] 循环 ${iteration}/${this.MAX_REACT_ITERATIONS} 开始`);
966
+ if (onStream) {
967
+ onStream({ type: 'status', content: `🔄 循环 ${iteration}/${this.MAX_REACT_ITERATIONS}`, tool: 'loop' });
968
+ }
969
+
828
970
  const context = this.buildContext();
829
971
  const toolDefs = this.getToolDefinitions();
830
972
 
973
+ // 动态构建 refine 上下文
974
+ let refineContext = '';
975
+ if (refineAttempts > 0 && lastQualityScore < this.QUALITY_THRESHOLD) {
976
+ refineContext = `\n【改进提示】上轮结果质量分 ${(lastQualityScore * 10).toFixed(1)}/10,请改进回答。`;
977
+ }
978
+
979
+ // 连续错误时的额外提示
980
+ if (consecutiveErrors > 0) {
981
+ refineContext += `\n【错误提示】上轮发生 ${consecutiveErrors} 次错误,请重新分析问题或换一种方式处理。`;
982
+ }
983
+
831
984
  const personaSection = this.persona ? `
832
985
  角色描述: ${this.persona.description || '无'}
833
986
  性格特点: ${this.persona.personality || '无'}
@@ -837,6 +990,7 @@ class PiAgentSession implements AgentSession {
837
990
  const systemPrompt = `你是 ${this.identity.name},基于ReAct (Reasoning + Acting)模式工作。${personaSection}
838
991
  当前工作目录: ${this.cwd}
839
992
  当前身份: ${this.identity.name} (${this.identity.did})
993
+ ${refineContext}
840
994
 
841
995
  ${toolDefs}
842
996
 
@@ -850,13 +1004,30 @@ ${toolDefs}
850
1004
  重要:
851
1005
  - 每次只调用一个工具
852
1006
  - 仔细分析工具返回结果
853
- - 如果任务完成,返回完整回答
1007
+ - 当任务完成时,必须在回答末尾添加 <final gen> 标记表示结束
854
1008
  - 如果需要更多信息,继续调用工具`;
855
1009
 
856
1010
  const response = await llm.chat(context, systemPrompt);
857
1011
  const reply = response.reply.trim();
858
1012
 
1013
+ console.log(`[PiAgent] LLM 回复长度: ${reply.length}, 内容预览: "${reply.substring(0, 80)}..."`);
1014
+
1015
+ // 通知前端:收到 LLM 回复
1016
+ if (onStream) {
1017
+ onStream({ type: 'token', content: reply.substring(0, 100) });
1018
+ }
1019
+
859
1020
  if (this.isFinalResponse(reply)) {
1021
+ // 检查质量分数
1022
+ lastQualityScore = this.estimateResponseQuality(reply);
1023
+
1024
+ // 如果质量太低且还有改进机会,进入改进循环
1025
+ if (lastQualityScore < this.QUALITY_THRESHOLD && refineAttempts < this.MAX_REFINE_ATTEMPTS) {
1026
+ refineAttempts++;
1027
+ console.log(`[PiAgent] 质量评分 ${(lastQualityScore * 10).toFixed(1)}/10 < ${(this.QUALITY_THRESHOLD * 10).toFixed(1)}/10,自动改进中 (${refineAttempts}/${this.MAX_REFINE_ATTEMPTS})`);
1028
+ continue;
1029
+ }
1030
+
860
1031
  finalResponse = this.extractFinalAnswer(reply);
861
1032
  break;
862
1033
  }
@@ -869,30 +1040,132 @@ ${toolDefs}
869
1040
  toolCall
870
1041
  });
871
1042
 
1043
+ // 通知前端:检测到工具调用
1044
+ if (onStream) {
1045
+ onStream({ type: 'tool', content: `🔧 调用工具: ${toolCall.name}`, tool: toolCall.name });
1046
+ if (toolCall.args && Object.keys(toolCall.args).length > 0) {
1047
+ onStream({ type: 'status', content: `📋 参数: ${JSON.stringify(toolCall.args)}`, tool: toolCall.name });
1048
+ }
1049
+ }
1050
+
872
1051
  const tool = this.tools.get(toolCall.name);
873
1052
  if (!tool) {
1053
+ consecutiveErrors++;
874
1054
  const errorResult: ToolResult = { success: false, error: `未知工具: ${toolCall.name}` };
875
1055
  this.messageHistory.push({ role: 'tool', content: JSON.stringify(errorResult), toolResult: errorResult });
876
1056
  this.logToHarness(toolCall.name, toolCall.args, errorResult);
1057
+ console.warn(`[PiAgent] 未知工具: ${toolCall.name},跳过并继续`);
877
1058
  continue;
878
1059
  }
879
1060
 
880
- const result = await tool.execute(toolCall.args);
881
- this.messageHistory.push({ role: 'tool', content: JSON.stringify(result), toolResult: result });
882
- this.logToHarness(toolCall.name, toolCall.args, result);
1061
+ try {
1062
+ const result = await tool.execute(toolCall.args);
1063
+ console.log(`[PiAgent] 工具 ${toolCall.name} 执行完成: success=${result.success}`);
1064
+ this.messageHistory.push({ role: 'tool', content: JSON.stringify(result), toolResult: result });
1065
+ this.logToHarness(toolCall.name, toolCall.args, result);
1066
+
1067
+ // 通知前端工具执行结果
1068
+ if (onStream) {
1069
+ if (result.success) {
1070
+ onStream({ type: 'status', content: `✅ ${toolCall.name} 执行成功`, tool: toolCall.name });
1071
+ if (result.output) {
1072
+ const outputPreview = result.output.substring(0, 200);
1073
+ onStream({ type: 'tool', content: `📤 结果: ${outputPreview}${result.output.length > 200 ? '...' : ''}`, tool: toolCall.name });
1074
+ }
1075
+ } else {
1076
+ onStream({ type: 'error', content: `❌ ${toolCall.name} 执行失败: ${result.error}`, tool: toolCall.name });
1077
+ }
1078
+ }
883
1079
 
884
- if (!result.success && result.error) {
885
- console.warn(`Tool ${toolCall.name} error: ${result.error}`);
1080
+ if (result.success) {
1081
+ consecutiveErrors = 0; // 重置连续错误计数
1082
+
1083
+ // 检查工具执行质量
1084
+ lastQualityScore = this.estimateToolResultQuality(result);
1085
+ if (lastQualityScore < this.QUALITY_THRESHOLD && refineAttempts < this.MAX_REFINE_ATTEMPTS) {
1086
+ refineAttempts++;
1087
+ console.log(`[PiAgent] 工具结果质量低,自动重试 (${refineAttempts}/${this.MAX_REFINE_ATTEMPTS})`);
1088
+ } else {
1089
+ console.log(`[PiAgent] 工具执行成功,质量评分: ${(lastQualityScore * 10).toFixed(1)}/10`);
1090
+ }
1091
+
1092
+ // 工具执行成功后,继续循环获取下一个 LLM 响应
1093
+ if (onStream) {
1094
+ onStream({ type: 'status', content: `🔄 工具执行完成,继续循环...`, tool: 'loop' });
1095
+ }
1096
+ // 不 break,继续下一次循环
1097
+ } else {
1098
+ consecutiveErrors++;
1099
+ console.warn(`[PiAgent] 工具执行失败 (${consecutiveErrors}/${MAX_CONSECUTIVE_ERRORS}): ${result.error}`);
1100
+
1101
+ // 连续错误达到上限,尝试换一种方式
1102
+ if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
1103
+ console.log(`[PiAgent] 连续 ${MAX_CONSECUTIVE_ERRORS} 次错误,尝试换一种方式处理`);
1104
+ // 添加错误上下文,让 LLM 换一种方式
1105
+ this.messageHistory.push({
1106
+ role: 'system',
1107
+ content: `[注意] 前面的工具调用连续失败。请尝试其他工具或换一种方式完成用户请求。`
1108
+ });
1109
+ consecutiveErrors = 0; // 重置以继续尝试
1110
+ }
1111
+ }
1112
+ } catch (execError) {
1113
+ consecutiveErrors++;
1114
+ const errorResult: ToolResult = { success: false, error: String(execError) };
1115
+ this.messageHistory.push({ role: 'tool', content: JSON.stringify(errorResult), toolResult: errorResult });
1116
+ this.logToHarness(toolCall.name, toolCall.args, errorResult);
1117
+ console.error(`[PiAgent] 工具执行异常: ${execError}`);
886
1118
  }
887
1119
  } else {
888
- this.messageHistory.push({ role: 'assistant', content: reply });
1120
+ // LLM 返回的不是 tool call 格式
1121
+ this.messageHistory.push({
1122
+ role: 'assistant',
1123
+ content: reply
1124
+ });
1125
+
1126
+ // 通知前端收到非工具调用回复
1127
+ if (onStream) {
1128
+ onStream({ type: 'token', content: reply.substring(0, 150) });
1129
+ }
1130
+
1131
+ // 检查是否需要继续循环处理
1132
+ // 更严格的判断:只有当回复明确表示需要更多信息时才继续
1133
+ const containsToolCallIntent = reply.includes('调用工具') || reply.includes('tool(') ||
1134
+ reply.includes('使用工具') || reply.includes('需要获取') || reply.includes('需要查看');
1135
+ const hasError = ['不存在', '找不到', '无法找到', 'not found', 'does not exist',
1136
+ '错误', 'error', '失败', 'failed'].some(k => reply.includes(k));
1137
+ const isTooShort = reply.length < 50 && reply.length > 0;
1138
+ const hasQuestion = reply.includes('?') && (reply.includes('怎么') || reply.includes('如何') || reply.includes('什么'));
1139
+
1140
+ const needsMoreWork = hasError || containsToolCallIntent || isTooShort || hasQuestion;
1141
+
1142
+ if (needsMoreWork && iteration < this.MAX_REACT_ITERATIONS) {
1143
+ console.log(`[PiAgent] 继续循环处理 (${iteration}/${this.MAX_REACT_ITERATIONS}): needsMoreWork=${needsMoreWork}, hasError=${hasError}, containsToolCallIntent=${containsToolCallIntent}`);
1144
+ if (onStream) {
1145
+ onStream({ type: 'status', content: `🔄 继续处理,循环 ${iteration}...`, tool: 'loop' });
1146
+ }
1147
+ continue;
1148
+ }
1149
+
1150
+ // 否则把这个当作可能的最终回答
889
1151
  finalResponse = reply;
1152
+ if (onStream) {
1153
+ onStream({ type: 'status', content: `📝 提取最终回答,长度 ${reply.length}`, tool: 'system' });
1154
+ }
890
1155
  break;
891
1156
  }
892
1157
  }
893
1158
 
894
1159
  if (!finalResponse) {
895
1160
  finalResponse = '任务处理超时,请尝试更具体的请求。';
1161
+ if (onStream) {
1162
+ onStream({ type: 'error', content: '⚠️ 任务处理超时', tool: 'system' });
1163
+ }
1164
+ }
1165
+
1166
+ // 通知前端循环完成
1167
+ if (onStream) {
1168
+ onStream({ type: 'status', content: `✅ 处理完成,共 ${iteration - 1} 次循环`, tool: 'system' });
896
1169
  }
897
1170
 
898
1171
  const now = new Date().toISOString();
@@ -975,30 +1248,23 @@ Workspace root folder: ${this.cwd}
975
1248
  }
976
1249
 
977
1250
  private isFinalResponse(content: string): boolean {
978
- const finalMarkers = ['最终回答', '完成', '答案如下', '结果是', 'final', 'answer:'];
979
- const lower = content.toLowerCase();
980
- // 更保守的判断:只有明确标记最终回答,且回复较短时才认为是最终回复
981
- if (content.includes('✅') && content.length < 200 && finalMarkers.some(m => lower.includes(m))) {
982
- return true;
983
- }
984
- return finalMarkers.some(m => lower.includes(m));
1251
+ // 只有明确输出 <final gen> 才认为是最终回答
1252
+ return content.includes('<final gen>');
985
1253
  }
986
1254
 
987
1255
  private extractFinalAnswer(content: string): string {
988
- // 移除任何 tool call 标记(保持完整回复)
1256
+ // 提取 <final gen> 后的内容作为最终回答
1257
+ const marker = '<final gen>';
1258
+ const markerIndex = content.indexOf(marker);
1259
+ if (markerIndex !== -1) {
1260
+ content = content.substring(markerIndex + marker.length).trim();
1261
+ }
1262
+ // 移除任何 tool call 标记
989
1263
  let cleaned = content
990
1264
  .replace(/调用工具[::]\s*\w+\s*\([^)]*\)/g, '')
991
1265
  .replace(/使用工具[::]\s*\w+\s*\([^)]*\)/g, '')
992
1266
  .replace(/tool[_\w]*[::]\s*\w+\s*\([^)]*\)/gi, '')
993
1267
  .trim();
994
-
995
- const lines = cleaned.split('\n');
996
- const answerStart = lines.findIndex(l =>
997
- ['最终回答', '完成', '答案如下', '结果是', 'final', 'answer:'].some(m => l.toLowerCase().includes(m))
998
- );
999
- if (answerStart >= 0) {
1000
- return lines.slice(answerStart + 1).join('\n').trim();
1001
- }
1002
1268
  return cleaned;
1003
1269
  }
1004
1270
 
@@ -1033,6 +1299,34 @@ Workspace root folder: ${this.cwd}
1033
1299
  return null;
1034
1300
  }
1035
1301
 
1302
+ private estimateResponseQuality(response: string): number {
1303
+ let score = 0.5;
1304
+ if (response.length > 50) score += 0.1;
1305
+ if (response.length > 200) score += 0.1;
1306
+ if (response.length < 20) score -= 0.3;
1307
+ if (response.includes('\n')) score += 0.1;
1308
+ if (response.includes('-') || response.includes('•')) score += 0.05;
1309
+ if (response.includes('```')) score += 0.1;
1310
+ const conclusionWords = ['完成', '结果', '总结', '所以', '因此', '答案', '推荐'];
1311
+ if (conclusionWords.some(w => response.includes(w))) score += 0.1;
1312
+ if (response.includes('调用工具') || response.includes('tool(')) score -= 0.2;
1313
+ return Math.max(0, Math.min(1, score));
1314
+ }
1315
+
1316
+ private estimateToolResultQuality(result: ToolResult): number {
1317
+ let score = 0.5;
1318
+ if (!result.success) return 0.2;
1319
+ if (result.output) {
1320
+ score += 0.2;
1321
+ if (result.output.length > 50) score += 0.1;
1322
+ if (result.output.length < 10) score -= 0.1;
1323
+ if (result.output.includes('❌') || result.output.includes('error')) score -= 0.2;
1324
+ if (result.output.includes('✅') || result.output.includes('success')) score += 0.1;
1325
+ }
1326
+ if (result.error) score -= 0.3;
1327
+ return Math.max(0, Math.min(1, score));
1328
+ }
1329
+
1036
1330
  private async handleFallback(input: string): Promise<string> {
1037
1331
  const lowerInput = input.toLowerCase();
1038
1332
  const parts = input.trim().split(/\s+/);
@@ -1551,9 +1845,34 @@ ${this.extractOperationsFromRef(operationsRef)}
1551
1845
  let sessionInstance: AgentSession | null = null;
1552
1846
  let lastIdentityDid: string | null = null;
1553
1847
 
1554
- export async function createAgentSession(config: AgentSessionConfig): Promise<AgentSession> {
1848
+ // 独立的 session 实例缓存(用于多 session 支持)
1849
+ const independentSessions: Map<string, AgentSession> = new Map();
1850
+
1851
+ export async function createAgentSession(config: AgentSessionConfig, forceNew?: boolean): Promise<AgentSession> {
1555
1852
  const incomingDid = config.identityDoc?.did;
1556
1853
 
1854
+ // 如果有独立的 peerId (包含 :),使用它作为 key
1855
+ if (config.peerId && config.peerId.includes(':')) {
1856
+ const key = config.peerId;
1857
+ if (!forceNew && independentSessions.has(key)) {
1858
+ console.log(`[createAgentSession] 找到现有独立 session, key=${key}`);
1859
+ return independentSessions.get(key)!;
1860
+ }
1861
+ const session = new PiAgentSession(config);
1862
+ independentSessions.set(key, session);
1863
+ console.log(`[createAgentSession] 创建独立 session, key=${key}, DID=${incomingDid}`);
1864
+ return session;
1865
+ }
1866
+
1867
+ // 如果指定了 forceNew 但没有 peerId,生成带时间戳的 key
1868
+ if (forceNew) {
1869
+ const key = `force:${Date.now()}`;
1870
+ const session = new PiAgentSession(config);
1871
+ independentSessions.set(key, session);
1872
+ console.log(`[createAgentSession] 创建强制新 session, key=${key}`);
1873
+ return session;
1874
+ }
1875
+
1557
1876
  // 如果有新的 DID,强制重建 session
1558
1877
  if (sessionInstance && lastIdentityDid && incomingDid && lastIdentityDid !== incomingDid) {
1559
1878
  console.log(`[createAgentSession] DID 变化 ${lastIdentityDid} -> ${incomingDid},重建 session`);
@@ -268,11 +268,105 @@ export class AgentProtocol {
268
268
  };
269
269
 
270
270
  const peers = p2pNetwork.getPeers();
271
+ const failedPeers: string[] = [];
272
+
271
273
  for (const peer of peers) {
272
274
  if (peer !== fromPeer) {
273
- await p2pNetwork.sendMessage(peer, 'report', JSON.stringify(reportMsg));
275
+ try {
276
+ await p2pNetwork.sendMessage(peer, 'report', JSON.stringify(reportMsg));
277
+ console.log(`[${this.identityName}] 汇报已发送至 ${peer}`);
278
+ } catch (sendError) {
279
+ console.warn(`[${this.identityName}] 发送汇报至 ${peer} 失败: ${sendError}`);
280
+ failedPeers.push(peer);
281
+ }
282
+ }
283
+ }
284
+
285
+ // 重试失败的发送
286
+ if (failedPeers.length > 0) {
287
+ console.log(`[${this.identityName}] 尝试重新发送汇报至 ${failedPeers.length} 个失败节点`);
288
+ await this.retryFailedReports(reportMsg, failedPeers, 2);
289
+ }
290
+ }
291
+
292
+ private async retryFailedReports(
293
+ reportMsg: AgentMessage,
294
+ failedPeers: string[],
295
+ maxRetries: number
296
+ ): Promise<void> {
297
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
298
+ if (failedPeers.length === 0) break;
299
+
300
+ console.log(`[${this.identityName}] 重试第 ${attempt}/${maxRetries} 次`);
301
+ await this.sleep(1000 * attempt); // 指数退避
302
+
303
+ const stillFailed: string[] = [];
304
+ for (const peer of failedPeers) {
305
+ try {
306
+ await p2pNetwork.sendMessage(peer, 'report', JSON.stringify(reportMsg));
307
+ console.log(`[${this.identityName}] 重试成功: ${peer}`);
308
+ } catch {
309
+ stillFailed.push(peer);
310
+ }
311
+ }
312
+ failedPeers = stillFailed;
313
+ }
314
+
315
+ if (failedPeers.length > 0) {
316
+ console.log(`[${this.identityName}] 最终仍有 ${failedPeers.length} 个节点发送失败,将加入待重试队列`);
317
+ this.queueFailedReports(reportMsg, failedPeers);
318
+ }
319
+ }
320
+
321
+ private failedReportsQueue: Array<{ msg: AgentMessage; peers: string[]; timestamp: number }> = [];
322
+
323
+ private queueFailedReports(msg: AgentMessage, peers: string[]): void {
324
+ this.failedReportsQueue.push({
325
+ msg,
326
+ peers,
327
+ timestamp: Date.now()
328
+ });
329
+ // 限制队列大小
330
+ if (this.failedReportsQueue.length > 50) {
331
+ this.failedReportsQueue = this.failedReportsQueue.slice(-50);
332
+ }
333
+ }
334
+
335
+ async processFailedReportsQueue(): Promise<void> {
336
+ if (this.failedReportsQueue.length === 0) return;
337
+
338
+ console.log(`[${this.identityName}] 处理待重试汇报队列 (${this.failedReportsQueue.length} 条)`);
339
+ const processed: number[] = [];
340
+
341
+ for (let i = 0; i < this.failedReportsQueue.length; i++) {
342
+ const item = this.failedReportsQueue[i];
343
+ const stillFailed: string[] = [];
344
+
345
+ for (const peer of item.peers) {
346
+ try {
347
+ await p2pNetwork.sendMessage(peer, 'report', JSON.stringify(item.msg));
348
+ console.log(`[${this.identityName}] 队列重试成功: ${peer}`);
349
+ } catch {
350
+ stillFailed.push(peer);
351
+ }
352
+ }
353
+
354
+ if (stillFailed.length === 0) {
355
+ processed.push(i);
356
+ } else {
357
+ item.peers = stillFailed;
358
+ item.timestamp = Date.now();
274
359
  }
275
360
  }
361
+
362
+ // 移除已成功的
363
+ for (let i = processed.length - 1; i >= 0; i--) {
364
+ this.failedReportsQueue.splice(processed[i], 1);
365
+ }
366
+ }
367
+
368
+ private sleep(ms: number): Promise<void> {
369
+ return new Promise(resolve => setTimeout(resolve, ms));
276
370
  }
277
371
 
278
372
  async submitImprovements(taskId: string, improvements: string): Promise<void> {