@bolloon/bolloon-agent 0.4.5 → 0.4.7

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 (208) hide show
  1. package/README.md +135 -135
  2. package/bin/bolloon-cli.cjs +183 -183
  3. package/bin/bolloon.cjs +0 -0
  4. package/bin/bolloon.js +157 -0
  5. package/dist/agents/constraint-layer.js +19 -19
  6. package/dist/agents/judgment-protocol.js +14 -14
  7. package/dist/agents/pi-sdk-tools.js +128 -46
  8. package/dist/agents/pi-sdk-types.js +12 -12
  9. package/dist/agents/pi-sdk.js +103 -90
  10. package/dist/agents/process-runner.js +105 -0
  11. package/dist/agents/skill-organizer.js +19 -19
  12. package/dist/agents/skill-writer.js +15 -3
  13. package/dist/agents/workflow-pivot-loop.js +13 -3
  14. package/dist/agents/write-staging.js +85 -0
  15. package/dist/bollharness/src/scripts/deploy-guard.js +8 -8
  16. package/dist/bollharness-integration/context-router-judgment.js +4 -4
  17. package/dist/bollharness-integration/context-router.js +292 -292
  18. package/dist/bollharness-integration/gate-state-machine.js +13 -13
  19. package/dist/bollharness-integration/llm-judgment-engine.js +134 -134
  20. package/dist/bollharness-integration/skill-adapter.js +21 -21
  21. package/dist/bootstrap/chat-archiver.js +14 -14
  22. package/dist/bootstrap/context-os.js +24 -24
  23. package/dist/bootstrap/memory-compressor.js +14 -14
  24. package/dist/cli/ink-app.js +18 -0
  25. package/dist/cli-entry.js +36 -36
  26. package/dist/context-compaction/auto-compact.js +7 -7
  27. package/dist/electron/first-run.js +54 -54
  28. package/dist/electron-build/electron/first-run.js +54 -54
  29. package/dist/electron-preload.js.map +1 -1
  30. package/dist/electron.js.map +1 -1
  31. package/dist/index.js +230 -137
  32. package/dist/llm/llm-judgment-client.js +102 -102
  33. package/dist/llm/pi-ai.js +48 -48
  34. package/dist/llm/system-prompt/layers/channel/human-async.md +41 -41
  35. package/dist/llm/system-prompt/layers/channel/local.md +14 -14
  36. package/dist/llm/system-prompt/layers/channel/p2p-agent.md +18 -18
  37. package/dist/llm/system-prompt/layers/channel/p2p-peer-sync.md +51 -51
  38. package/dist/llm/system-prompt/layers/channel/p2p-proactive.md +43 -43
  39. package/dist/llm/system-prompt/layers/channel/p2p-visitor.md +19 -19
  40. package/dist/llm/system-prompt/layers/channel/session-handoff.md +61 -61
  41. package/dist/llm/system-prompt/layers/core/artifacts_storage.md +89 -89
  42. package/dist/llm/system-prompt/layers/core/evenhandedness.md +21 -21
  43. package/dist/llm/system-prompt/layers/core/external-engagement.md +73 -73
  44. package/dist/llm/system-prompt/layers/core/hibs_reminders.md +15 -15
  45. package/dist/llm/system-prompt/layers/core/identity.md +48 -48
  46. package/dist/llm/system-prompt/layers/core/knowledge.md +17 -17
  47. package/dist/llm/system-prompt/layers/core/memory_system.md +12 -12
  48. package/dist/llm/system-prompt/layers/core/network_filesystem.md +28 -28
  49. package/dist/llm/system-prompt/layers/core/refusal.md +37 -37
  50. package/dist/llm/system-prompt/layers/core/tone.md +31 -31
  51. package/dist/llm/system-prompt/layers/core/tools.thin.md +13 -13
  52. package/dist/llm/system-prompt/layers/core/wellbeing.md +41 -41
  53. package/dist/llm/system-prompt/layers/role/architect.md +20 -20
  54. package/dist/llm/system-prompt/layers/role/expert.md +19 -19
  55. package/dist/llm/system-prompt/layers/role/implementer.md +15 -15
  56. package/dist/llm/system-prompt/layers/role/security.md +15 -15
  57. package/dist/llm/system-prompt/layers/tool/artifacts.md +72 -72
  58. package/dist/llm/system-prompt/layers/tool/bash.md +25 -25
  59. package/dist/llm/system-prompt/layers/tool/goal_handoff.md +77 -77
  60. package/dist/llm/system-prompt/layers/tool/hibs_api.md +171 -171
  61. package/dist/llm/system-prompt/layers/tool/image_search.md +70 -70
  62. package/dist/llm/system-prompt/layers/tool/manifest.md +89 -89
  63. package/dist/llm/system-prompt/layers/tool/mcp_apps.md +53 -53
  64. package/dist/llm/system-prompt/layers/tool/p2p_request.md +61 -61
  65. package/dist/llm/system-prompt/layers/tool/web_search.md +83 -83
  66. package/dist/llm/tool-manifest/ask_user_input.js +1 -1
  67. package/dist/llm/tool-manifest/bash.js +3 -3
  68. package/dist/llm/tool-manifest/create_file.js +4 -4
  69. package/dist/llm/tool-manifest/fetch_sports_data.js +3 -3
  70. package/dist/llm/tool-manifest/image_search.js +2 -2
  71. package/dist/llm/tool-manifest/index.js +0 -59
  72. package/dist/llm/tool-manifest/mcp.js +2 -2
  73. package/dist/llm/tool-manifest/message_compose.js +3 -3
  74. package/dist/llm/tool-manifest/places.js +2 -2
  75. package/dist/llm/tool-manifest/present_files.js +1 -1
  76. package/dist/llm/tool-manifest/recipe.js +2 -2
  77. package/dist/llm/tool-manifest/str_replace.js +5 -5
  78. package/dist/llm/tool-manifest/view.js +3 -3
  79. package/dist/llm/tool-manifest/weather.js +4 -4
  80. package/dist/llm/tool-manifest/web.js +4 -4
  81. package/dist/orbitdb/agent-tools.js +139 -0
  82. package/dist/orbitdb/kanban-store.js +259 -0
  83. package/dist/orbitdb/task-store.js +112 -0
  84. package/dist/pi-ecosystem-colony/index.js +365 -0
  85. package/dist/pi-ecosystem-judgment/causal-judge.js +51 -51
  86. package/dist/pi-ecosystem-judgment/distill-prompt.js +42 -42
  87. package/dist/pi-ecosystem-judgment/distillation.js +14 -14
  88. package/dist/pi-ecosystem-judgment/monitor-gate.js +28 -28
  89. package/dist/pi-ecosystem-judgment/value-injection.js +102 -102
  90. package/dist/security/context-router-tool.js +15 -15
  91. package/dist/security/tool-gate.js +2 -0
  92. package/dist/social/ant-colony/AdaptiveHeartbeat.js +101 -0
  93. package/dist/social/ant-colony/PheromoneEngine.js +227 -0
  94. package/dist/social/ant-colony/index.js +6 -0
  95. package/dist/social/ant-colony/types.js +24 -0
  96. package/dist/storage/trajectory.js +101 -0
  97. package/dist/test/ai-judgment-test.js +80 -0
  98. package/dist/test/bollharness-integration.test.js +318 -0
  99. package/dist/test/channel-agent-multi-dialogue.js +205 -0
  100. package/dist/test/channel-heartbeat-agent-test.js +201 -0
  101. package/dist/test/constraint-layer.test.js +164 -0
  102. package/dist/test/diap-identity-test.js +172 -0
  103. package/dist/test/diap-quick-test.js +62 -0
  104. package/dist/test/global-shared-context.test.js +315 -0
  105. package/dist/test/harness-judgment-injection.test.js +246 -0
  106. package/dist/test/harness-workflow-integrator-test.js +228 -0
  107. package/dist/test/human-value-store.test.js +243 -0
  108. package/dist/test/hybrid-integration-test.js +118 -0
  109. package/dist/test/hybrid-messenger-verify.js +55 -0
  110. package/dist/test/iroh-bistream-debug.js +38 -0
  111. package/dist/test/iroh-communication.test.js +66 -0
  112. package/dist/test/iroh-debug-test.js +57 -0
  113. package/dist/test/iroh-diap-test.js +71 -0
  114. package/dist/test/iroh-direct-connect.js +55 -0
  115. package/dist/test/iroh-e2e-fixed.js +89 -0
  116. package/dist/test/iroh-e2e-same-process.js +63 -0
  117. package/dist/test/iroh-e2e.js +66 -0
  118. package/dist/test/iroh-final-e2e.js +72 -0
  119. package/dist/test/iroh-relay-test.js +37 -0
  120. package/dist/test/iroh-simple-test.js +41 -0
  121. package/dist/test/iroh-transport-verify.js +54 -0
  122. package/dist/test/iroh-transport.test.js +37 -0
  123. package/dist/test/iroh-two-nodes.js +70 -0
  124. package/dist/test/iroh-verify.js +44 -0
  125. package/dist/test/judgment-decision.test.js +219 -0
  126. package/dist/test/llm-judgment-integration.test.js +220 -0
  127. package/dist/test/p2p-agent-complex-dialogue.js +385 -0
  128. package/dist/test/p2p-agent-dialogue.js +341 -0
  129. package/dist/test/p2p-agent-full-bidirectional.js +510 -0
  130. package/dist/test/p2p-agent-harness-flow.js +437 -0
  131. package/dist/test/p2p-agent-harness-single.js +143 -0
  132. package/dist/test/p2p-ai-dialogue-test.js +318 -0
  133. package/dist/test/p2p-cid-connect-test.js +195 -0
  134. package/dist/test/p2p-connect-receiver.js +69 -0
  135. package/dist/test/p2p-doc-transfer.js +110 -0
  136. package/dist/test/p2p-identity-page-test.js +77 -0
  137. package/dist/test/p2p-iroh-test.js +171 -0
  138. package/dist/test/p2p-minimal-test.js +241 -0
  139. package/dist/test/p2p-node-1.js +148 -0
  140. package/dist/test/p2p-node-2.js +148 -0
  141. package/dist/test/p2p-server.js +281 -0
  142. package/dist/test/p2p-two-nodes-test.js +438 -0
  143. package/dist/test/pi-sdk.test.js +44 -0
  144. package/dist/test/set-persona.js +40 -0
  145. package/dist/test/simple.test.js +9 -0
  146. package/dist/test/storage-integration.test.js +150 -0
  147. package/dist/test/subagent-manager.test.js +276 -0
  148. package/dist/test/test-gate-flow.test.js +81 -0
  149. package/dist/test/workflow-engine.test.js +87 -0
  150. package/dist/test/workflow-pivot-loop.test.js +246 -0
  151. package/dist/web/api-config.html +779 -779
  152. package/dist/web/client-hearth.js +10 -10
  153. package/dist/web/components/wallet-viem.mjs +118 -118
  154. package/dist/web/index.html +472 -472
  155. package/dist/web/manifest.json +20 -20
  156. package/dist/web/server-storage.js +29 -0
  157. package/dist/web/server.js +30 -14
  158. package/dist/web/style.css +4946 -4946
  159. package/package.json +195 -195
  160. package/scripts/build-cli.js +215 -215
  161. package/scripts/build-web.ts +130 -130
  162. package/scripts/postinstall.js +152 -152
  163. package/bin/bolloon-daemon.sh +0 -207
  164. package/dist/constraint-runtime/reference_data/archive_surface_snapshot.json +0 -63
  165. package/dist/constraint-runtime/reference_data/commands_snapshot.json +0 -1037
  166. package/dist/constraint-runtime/reference_data/subsystems/assistant.json +0 -8
  167. package/dist/constraint-runtime/reference_data/subsystems/bootstrap.json +0 -8
  168. package/dist/constraint-runtime/reference_data/subsystems/bridge.json +0 -32
  169. package/dist/constraint-runtime/reference_data/subsystems/buddy.json +0 -13
  170. package/dist/constraint-runtime/reference_data/subsystems/cli.json +0 -26
  171. package/dist/constraint-runtime/reference_data/subsystems/components.json +0 -32
  172. package/dist/constraint-runtime/reference_data/subsystems/constants.json +0 -28
  173. package/dist/constraint-runtime/reference_data/subsystems/coordinator.json +0 -8
  174. package/dist/constraint-runtime/reference_data/subsystems/entrypoints.json +0 -15
  175. package/dist/constraint-runtime/reference_data/subsystems/hooks.json +0 -32
  176. package/dist/constraint-runtime/reference_data/subsystems/keybindings.json +0 -21
  177. package/dist/constraint-runtime/reference_data/subsystems/memdir.json +0 -15
  178. package/dist/constraint-runtime/reference_data/subsystems/migrations.json +0 -18
  179. package/dist/constraint-runtime/reference_data/subsystems/moreright.json +0 -8
  180. package/dist/constraint-runtime/reference_data/subsystems/native_ts.json +0 -11
  181. package/dist/constraint-runtime/reference_data/subsystems/outputStyles.json +0 -8
  182. package/dist/constraint-runtime/reference_data/subsystems/plugins.json +0 -9
  183. package/dist/constraint-runtime/reference_data/subsystems/remote.json +0 -11
  184. package/dist/constraint-runtime/reference_data/subsystems/schemas.json +0 -8
  185. package/dist/constraint-runtime/reference_data/subsystems/screens.json +0 -10
  186. package/dist/constraint-runtime/reference_data/subsystems/server.json +0 -10
  187. package/dist/constraint-runtime/reference_data/subsystems/services.json +0 -32
  188. package/dist/constraint-runtime/reference_data/subsystems/skills.json +0 -27
  189. package/dist/constraint-runtime/reference_data/subsystems/state.json +0 -13
  190. package/dist/constraint-runtime/reference_data/subsystems/types.json +0 -18
  191. package/dist/constraint-runtime/reference_data/subsystems/upstreamproxy.json +0 -9
  192. package/dist/constraint-runtime/reference_data/subsystems/utils.json +0 -32
  193. package/dist/constraint-runtime/reference_data/subsystems/vim.json +0 -12
  194. package/dist/constraint-runtime/reference_data/subsystems/voice.json +0 -8
  195. package/dist/constraint-runtime/reference_data/tools_snapshot.json +0 -922
  196. package/dist/electron/config.js.map +0 -1
  197. package/dist/electron/dialogs.js.map +0 -1
  198. package/dist/electron/first-run.js.map +0 -1
  199. package/dist/electron/ipc.js.map +0 -1
  200. package/dist/electron/logger.js.map +0 -1
  201. package/dist/electron/main.js.map +0 -1
  202. package/dist/electron/menu.js.map +0 -1
  203. package/dist/electron/paths.js.map +0 -1
  204. package/dist/electron/server.js.map +0 -1
  205. package/dist/electron/tray.js.map +0 -1
  206. package/dist/electron/window.js.map +0 -1
  207. package/dist/utils/auto-update.js.map +0 -1
  208. package/dist/web/client.js.map +0 -7
@@ -0,0 +1,318 @@
1
+ /**
2
+ * Bollharness Integration Tests - Standalone Component Tests
3
+ *
4
+ * These tests verify the core bollharness integration components
5
+ * without requiring full constraint-runtime dependency chain.
6
+ */
7
+ import { describe, it, expect } from 'vitest';
8
+ const GATE_CONFIGS = {
9
+ 0: { entryCondition: '用户提出需求', requiredArtifact: '问题陈述', requiredNextSkill: 'arch', isReviewGate: false },
10
+ 1: { entryCondition: 'Gate 0 产物存在', requiredArtifact: 'ADR草稿', requiredNextSkill: 'arch', isReviewGate: false },
11
+ 2: { entryCondition: 'ADR草稿完成', requiredArtifact: '审查报告', requiredNextSkill: 'review', requiredReviewSubstrate: 'ref-review-sop.md', isReviewGate: true },
12
+ 3: { entryCondition: 'Gate 2 PASS', requiredArtifact: 'PLAN文档', requiredNextSkill: 'harness-eng', isReviewGate: false },
13
+ 4: { entryCondition: 'PLAN vN-final冻结', requiredArtifact: '审查报告', requiredNextSkill: 'review', requiredReviewSubstrate: 'ref-review-sop.md', isReviewGate: true },
14
+ 5: { entryCondition: 'Gate 4 PASS', requiredArtifact: 'WP拆分+TASK.md', requiredNextSkill: 'task-arch', isReviewGate: false },
15
+ 6: { entryCondition: '全部TASK.md完成', requiredArtifact: '审查报告', requiredNextSkill: 'review', requiredReviewSubstrate: 'ref-review-sop.md', isReviewGate: true },
16
+ 7: { entryCondition: 'Gate 6 PASS', requiredArtifact: '代码+LOG.md', requiredNextSkill: 'harness-eng', isReviewGate: false },
17
+ 8: { entryCondition: '全部WP代码+LOG.md存在', requiredArtifact: '审查报告', requiredNextSkill: 'harness-eng-test', requiredReviewSubstrate: 'ref-review-sop.md', isReviewGate: true },
18
+ };
19
+ describe('GateStateMachine', () => {
20
+ let currentGate = 0;
21
+ let artifacts = new Map();
22
+ describe('initialization', () => {
23
+ it('should start at gate 0', () => {
24
+ currentGate = 0;
25
+ expect(currentGate).toBe(0);
26
+ });
27
+ it('should have correct gate 0 config', () => {
28
+ const config = GATE_CONFIGS[0];
29
+ expect(config.requiredArtifact).toBeDefined();
30
+ expect(config.isReviewGate).toBe(false);
31
+ });
32
+ });
33
+ describe('gate configs', () => {
34
+ it('should have 9 gates defined', () => {
35
+ expect(Object.keys(GATE_CONFIGS).length).toBe(9);
36
+ });
37
+ it('should have 4 review gates (2,4,6,8)', () => {
38
+ expect(GATE_CONFIGS[2].isReviewGate).toBe(true);
39
+ expect(GATE_CONFIGS[4].isReviewGate).toBe(true);
40
+ expect(GATE_CONFIGS[6].isReviewGate).toBe(true);
41
+ expect(GATE_CONFIGS[8].isReviewGate).toBe(true);
42
+ });
43
+ it('should have correct next skill for each gate', () => {
44
+ expect(GATE_CONFIGS[0].requiredNextSkill).toBe('arch');
45
+ expect(GATE_CONFIGS[5].requiredNextSkill).toBe('task-arch');
46
+ });
47
+ });
48
+ describe('artifacts', () => {
49
+ it('should be able to submit artifacts', () => {
50
+ artifacts.set('test', { data: 'test' });
51
+ expect(artifacts.has('test')).toBe(true);
52
+ });
53
+ it('should retrieve submitted artifacts', () => {
54
+ artifacts.set('adr', { content: 'ADR draft' });
55
+ const artifact = artifacts.get('adr');
56
+ expect(artifact).toBeDefined();
57
+ });
58
+ });
59
+ });
60
+ // ==================== Guard Router Tests ====================
61
+ const GUARD_MAP = {
62
+ 'src/agents/': ['check_api_types', 'check_skill_parity'],
63
+ 'src/documents/': ['check_doc_freshness', 'check_api_types'],
64
+ 'src/network/': ['check_api_types', 'check_versions'],
65
+ 'docs/': ['check_doc_freshness', 'check_doc_links'],
66
+ 'CLAUDE.md': ['check_doc_freshness', 'check_artifact_link'],
67
+ 'docs/decisions/': ['check_artifact_link', 'check_versions'],
68
+ 'src/test/': ['check_api_types', 'check_versions'],
69
+ };
70
+ function route(filePath) {
71
+ const matched = [];
72
+ const sortedPatterns = Object.keys(GUARD_MAP).sort((a, b) => b.length - a.length);
73
+ for (const pattern of sortedPatterns) {
74
+ if (filePath.startsWith(pattern) || filePath === pattern.replace(/\/$/, '')) {
75
+ matched.push(...GUARD_MAP[pattern]);
76
+ }
77
+ }
78
+ return [...new Set(matched)];
79
+ }
80
+ describe('GuardRouter', () => {
81
+ describe('routing', () => {
82
+ it('should route agents path to check_api_types', () => {
83
+ const guards = route('src/agents/pi-sdk.ts');
84
+ expect(guards).toContain('check_api_types');
85
+ });
86
+ it('should route docs path to doc freshness', () => {
87
+ const guards = route('docs/guide.md');
88
+ expect(guards).toContain('check_doc_freshness');
89
+ });
90
+ it('should route decisions to artifact link check', () => {
91
+ const guards = route('docs/decisions/adr-001.md');
92
+ expect(guards).toContain('check_artifact_link');
93
+ });
94
+ it('should route test files to version check', () => {
95
+ const guards = route('src/test/pi-sdk.test.ts');
96
+ expect(guards).toContain('check_versions');
97
+ });
98
+ it('should return empty array for unknown paths', () => {
99
+ const guards = route('src/unknown/file.ts');
100
+ expect(guards.length).toBe(0);
101
+ });
102
+ it('should handle CLAUDE.md special case', () => {
103
+ const guards = route('CLAUDE.md');
104
+ expect(guards).toContain('check_doc_freshness');
105
+ expect(guards).toContain('check_artifact_link');
106
+ });
107
+ });
108
+ describe('deduplication', () => {
109
+ it('should deduplicate guard names', () => {
110
+ const guards = route('src/agents/pi-sdk.ts');
111
+ const uniqueGuards = [...new Set(guards)];
112
+ expect(guards.length).toBe(uniqueGuards.length);
113
+ });
114
+ });
115
+ });
116
+ // ==================== Context Router Tests ====================
117
+ const CONTEXT_MAP = {
118
+ 'src/agents/': ['agent-architecture', 'multi-agent-patterns'],
119
+ 'src/documents/': ['document-processing', 'parser-patterns'],
120
+ 'src/network/': ['p2p-protocols', 'connection-patterns'],
121
+ 'docs/': ['documentation-standards'],
122
+ 'docs/decisions/': ['decision-tracking', 'adr-patterns'],
123
+ 'src/test/': ['testing-patterns', 'quality-standards'],
124
+ 'CLAUDE.md': ['project-governance', 'truth-source-hierarchy'],
125
+ };
126
+ const FALLBACK_FRAGMENTS = ['general-dev-principles', 'code-quality'];
127
+ function match(filePath) {
128
+ if (!filePath)
129
+ return [];
130
+ const normalized = filePath.replace(/\\/g, '/');
131
+ const matched = [];
132
+ const sortedPatterns = Object.keys(CONTEXT_MAP).sort((a, b) => b.length - a.length);
133
+ for (const pattern of sortedPatterns) {
134
+ if (normalized.startsWith(pattern) || normalized.endsWith(pattern)) {
135
+ matched.push(...CONTEXT_MAP[pattern]);
136
+ }
137
+ }
138
+ return [...new Set(matched)];
139
+ }
140
+ describe('ContextRouter', () => {
141
+ describe('matching', () => {
142
+ it('should match agents path', () => {
143
+ const fragments = match('src/agents/protocol.ts');
144
+ expect(fragments).toContain('agent-architecture');
145
+ expect(fragments).toContain('multi-agent-patterns');
146
+ });
147
+ it('should match docs path', () => {
148
+ const fragments = match('docs/api.md');
149
+ expect(fragments).toContain('documentation-standards');
150
+ });
151
+ it('should match decisions path', () => {
152
+ const fragments = match('docs/decisions/adr-001.md');
153
+ expect(fragments).toContain('decision-tracking');
154
+ expect(fragments).toContain('adr-patterns');
155
+ });
156
+ it('should match CLAUDE.md', () => {
157
+ const fragments = match('CLAUDE.md');
158
+ expect(fragments).toContain('project-governance');
159
+ });
160
+ it('should return empty for invalid paths', () => {
161
+ const fragments = match('');
162
+ expect(fragments.length).toBe(0);
163
+ });
164
+ });
165
+ describe('fallback fragments', () => {
166
+ it('should have fallback fragments defined', () => {
167
+ expect(FALLBACK_FRAGMENTS).toContain('general-dev-principles');
168
+ expect(FALLBACK_FRAGMENTS).toContain('code-quality');
169
+ });
170
+ });
171
+ });
172
+ // Mock skills for testing
173
+ const skills = {
174
+ arch: {
175
+ name: 'arch',
176
+ description: 'Project architect. Architecture decisions and boundary freezing.',
177
+ async execute(params) {
178
+ const essence = `Core challenge: ${params.task || 'unspecified'}`;
179
+ const tensions = ['Simplicity vs Flexibility', 'Performance vs Maintainability'];
180
+ const alternatives = [
181
+ { name: 'Option A', tradeoffs: ['Fast', 'Limited'], recommendation: 'MVP' },
182
+ { name: 'Option B', tradeoffs: ['More work', 'Extensible'], recommendation: 'Long-term' }
183
+ ];
184
+ return JSON.stringify({ essence, tensions, alternatives }, null, 2);
185
+ }
186
+ },
187
+ lead: {
188
+ name: 'lead',
189
+ description: 'Development workflow commander. Fail-closed state machine.',
190
+ async execute(params) {
191
+ const action = params.action;
192
+ if (action === 'get_gate') {
193
+ return JSON.stringify({ current_gate: 0, gate_name: 'Problem Lock', required_artifact: '问题陈述' }, null, 2);
194
+ }
195
+ if (action === 'classify') {
196
+ const desc = params.description || '';
197
+ const isPolicy = desc.includes('policy');
198
+ const isContract = desc.includes('API') || desc.includes('contract');
199
+ return JSON.stringify({
200
+ classification: isPolicy ? 'policy' : isContract ? 'contract' : 'implementation',
201
+ fast_track: !isPolicy && !isContract
202
+ }, null, 2);
203
+ }
204
+ return JSON.stringify({ current_gate: 0 });
205
+ }
206
+ },
207
+ task_arch: {
208
+ name: 'task-arch',
209
+ description: 'Task decomposition. Breaks down PLAN into work packages.',
210
+ async execute(params) {
211
+ const workPackages = [
212
+ { id: 'WP-1', description: 'Core implementation', files: ['src/agents/*.ts'] },
213
+ { id: 'WP-2', description: 'Network layer', files: ['src/network/*.ts'] }
214
+ ];
215
+ return JSON.stringify({ workPackages }, null, 2);
216
+ }
217
+ }
218
+ };
219
+ describe('Skills', () => {
220
+ describe('arch skill', () => {
221
+ it('should have correct name and description', () => {
222
+ expect(skills.arch.name).toBe('arch');
223
+ expect(skills.arch.description).toContain('architect');
224
+ });
225
+ it('should execute and return analysis', async () => {
226
+ const result = await skills.arch.execute({ task: 'design new feature' });
227
+ const parsed = JSON.parse(result);
228
+ expect(parsed.essence).toBeDefined();
229
+ expect(parsed.tensions).toHaveLength(2);
230
+ expect(parsed.alternatives).toHaveLength(2);
231
+ });
232
+ });
233
+ describe('lead skill', () => {
234
+ it('should have correct name', () => {
235
+ expect(skills.lead.name).toBe('lead');
236
+ });
237
+ it('should get gate pack', async () => {
238
+ const result = await skills.lead.execute({ action: 'get_gate' });
239
+ const parsed = JSON.parse(result);
240
+ expect(parsed.current_gate).toBe(0);
241
+ });
242
+ it('should classify policy changes', async () => {
243
+ const result = await skills.lead.execute({ action: 'classify', description: 'change policy boundary' });
244
+ const parsed = JSON.parse(result);
245
+ expect(parsed.classification).toBe('policy');
246
+ });
247
+ it('should classify contract changes', async () => {
248
+ const result = await skills.lead.execute({ action: 'classify', description: 'change API contract' });
249
+ const parsed = JSON.parse(result);
250
+ expect(parsed.classification).toBe('contract');
251
+ });
252
+ it('should classify implementation changes as fast-track eligible', async () => {
253
+ const result = await skills.lead.execute({ action: 'classify', description: 'fix bug' });
254
+ const parsed = JSON.parse(result);
255
+ expect(parsed.classification).toBe('implementation');
256
+ expect(parsed.fast_track).toBe(true);
257
+ });
258
+ });
259
+ describe('task-arch skill', () => {
260
+ it('should decompose plan into work packages', async () => {
261
+ const result = await skills.task_arch.execute({ plan: 'implement feature' });
262
+ const parsed = JSON.parse(result);
263
+ expect(parsed.workPackages).toHaveLength(2);
264
+ expect(parsed.workPackages[0].id).toBe('WP-1');
265
+ });
266
+ });
267
+ });
268
+ // ==================== Change Classification Tests ====================
269
+ describe('Change Classification', () => {
270
+ function classifyChange(description) {
271
+ const isPolicy = description.includes('policy') || description.includes('boundary');
272
+ const isContract = description.includes('API') || description.includes('contract') || description.includes('schema');
273
+ const isImplementation = !isPolicy && !isContract;
274
+ return {
275
+ classification: isPolicy ? 'policy' : isContract ? 'contract' : 'implementation',
276
+ fast_track: isImplementation
277
+ };
278
+ }
279
+ it('should classify policy changes', () => {
280
+ const result = classifyChange('change policy boundary');
281
+ expect(result.classification).toBe('policy');
282
+ expect(result.fast_track).toBe(false);
283
+ });
284
+ it('should classify contract changes', () => {
285
+ const result = classifyChange('change API contract');
286
+ expect(result.classification).toBe('contract');
287
+ expect(result.fast_track).toBe(false);
288
+ });
289
+ it('should classify implementation changes', () => {
290
+ const result = classifyChange('fix bug in code');
291
+ expect(result.classification).toBe('implementation');
292
+ expect(result.fast_track).toBe(true);
293
+ });
294
+ it('should classify schema changes as contract', () => {
295
+ const result = classifyChange('update database schema');
296
+ expect(result.classification).toBe('contract');
297
+ });
298
+ });
299
+ // ==================== Integration Configuration Tests ====================
300
+ describe('BollharnessConfig', () => {
301
+ function createDefaultConfig() {
302
+ return {
303
+ enabled: true,
304
+ guardsEnabled: true,
305
+ contextEnabled: true,
306
+ skillsEnabled: true,
307
+ gatesEnabled: true,
308
+ };
309
+ }
310
+ it('should have default config with all features enabled', () => {
311
+ const config = createDefaultConfig();
312
+ expect(config.enabled).toBe(true);
313
+ expect(config.guardsEnabled).toBe(true);
314
+ expect(config.contextEnabled).toBe(true);
315
+ expect(config.skillsEnabled).toBe(true);
316
+ expect(config.gatesEnabled).toBe(true);
317
+ });
318
+ });
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Channel Agent Multi-Turn Dialogue Test
3
+ *
4
+ * 测试场景:
5
+ * 1. 两个 Channel Agent (Alice/Bob) 之间的多轮对话
6
+ * 2. 基于判断力的 Harness 调用 (Gate 1-8)
7
+ * 3. 上下文感知的 Gate 选择
8
+ * 4. Persona 设计能力
9
+ *
10
+ * 运行: npx tsx src/test/channel-agent-multi-dialogue.ts
11
+ */
12
+ import { config } from 'dotenv';
13
+ import { createChannelAgent, ChannelAgentRegistry } from '../social/channels/channel-agent-session.js';
14
+ config();
15
+ const PORT_ALICE = 8001;
16
+ const PORT_BOB = 8002;
17
+ async function testMultiTurnDialogue() {
18
+ console.log('\n========================================');
19
+ console.log(' Channel Agent 多轮对话测试');
20
+ console.log(' 场景: 完成一个用户认证系统的需求讨论');
21
+ console.log('========================================\n');
22
+ // 创建 Agent 注册表
23
+ const registry = new ChannelAgentRegistry();
24
+ // 创建 Alice
25
+ const alice = createChannelAgent({
26
+ name: 'Alice',
27
+ port: PORT_ALICE,
28
+ domain: '架构',
29
+ capabilities: ['架构设计', '代码审查', '任务分解']
30
+ });
31
+ // 创建 Bob
32
+ const bob = createChannelAgent({
33
+ name: 'Bob',
34
+ port: PORT_BOB,
35
+ domain: '开发',
36
+ capabilities: ['代码编写', '调试', '测试']
37
+ });
38
+ // 注册
39
+ registry.register(alice);
40
+ registry.register(bob);
41
+ // 设计 persona
42
+ alice.designPersona({
43
+ name: 'Alice',
44
+ type: 'developer',
45
+ domain: '架构',
46
+ tone: 'professional'
47
+ });
48
+ bob.designPersona({
49
+ name: 'Bob',
50
+ type: 'developer',
51
+ domain: '开发',
52
+ tone: 'technical'
53
+ });
54
+ // 启动
55
+ await alice.start();
56
+ await bob.start();
57
+ // 对话脚本
58
+ const script = [
59
+ // 第1轮 - 架构讨论
60
+ { from: 'Alice', to: 'Bob', content: 'Bob,我们需要设计一个新的用户认证系统。', expectHarness: false },
61
+ { from: 'Bob', to: 'Alice', content: '好的,具体需要哪些功能?', expectHarness: false },
62
+ // 第2轮 - 需求讨论
63
+ { from: 'Alice', to: 'Bob', content: '需要登录、注册、找回密码,还要支持社交登录。', expectHarness: false },
64
+ { from: 'Bob', to: 'Alice', content: '明白了。这个系统需要什么级别的安全性?', expectHarness: false },
65
+ // 第3轮 - 安全讨论 → Gate 4
66
+ { from: 'Alice', to: 'Bob', content: '需要处理金融数据,所以安全性很重要。请检查一下架构设计。', expectHarness: true, expectGate: 4 },
67
+ { from: 'Bob', to: 'Alice', content: '好的,我来分析一下架构设计,看看有没有安全风险。', expectHarness: true, expectGate: 1 },
68
+ // 第4轮 - 代码审查 → Gate 2
69
+ { from: 'Alice', to: 'Bob', content: '架构看起来不错。帮我 review 一下登录模块的实现。', expectHarness: true, expectGate: 2 },
70
+ { from: 'Bob', to: 'Alice', content: '好的,让我检查登录模块的代码实现。', expectHarness: true, expectGate: 2 },
71
+ // 第5轮 - 优化讨论
72
+ { from: 'Alice', to: 'Bob', content: '代码审查发现了一些问题,需要优化错误处理。', expectHarness: false },
73
+ { from: 'Bob', to: 'Alice', content: '同意,错误处理需要改进。', expectHarness: false },
74
+ // 第6轮 - 任务分解 → Gate 5
75
+ { from: 'Alice', to: 'Bob', content: '好的,现在我们来分解任务,制定开发计划。', expectHarness: true, expectGate: 5 },
76
+ { from: 'Bob', to: 'Alice', content: '我来帮你把任务分解成可执行的小块。', expectHarness: true, expectGate: 5 },
77
+ // 第7轮 - 开始实现
78
+ { from: 'Alice', to: 'Bob', content: '任务分解得很好。我们开始实现第一个任务吧。', expectHarness: false },
79
+ { from: 'Bob', to: 'Alice', content: '好的,开始实现登录模块。', expectHarness: false },
80
+ // 第8轮 - 代码实现 → Gate 7
81
+ { from: 'Alice', to: 'Bob', content: '实现完成,帮我优化一下代码质量。', expectHarness: true, expectGate: 7 },
82
+ { from: 'Bob', to: 'Alice', content: '好的,我来进行代码优化。', expectHarness: true, expectGate: 7 },
83
+ // 第9轮 - 测试验证 → Gate 8
84
+ { from: 'Alice', to: 'Bob', content: '代码优化完成,帮我验证一下质量。', expectHarness: true, expectGate: 8 },
85
+ { from: 'Bob', to: 'Alice', content: '好的,我来运行测试验证。', expectHarness: true, expectGate: 8 },
86
+ // 第10轮 - 完成
87
+ { from: 'Alice', to: 'Bob', content: '测试通过了!继续下一个任务。', expectHarness: false },
88
+ { from: 'Bob', to: 'Alice', content: '太好了!继续加油。', expectHarness: false },
89
+ ];
90
+ console.log('━━━ 开始多轮对话 ━━━\n');
91
+ let harnessCallCount = 0;
92
+ const gateStats = new Map();
93
+ for (let i = 0; i < script.length; i++) {
94
+ const { from, to, content, expectHarness, expectGate } = script[i];
95
+ const targetPort = from === 'Alice' ? PORT_BOB : PORT_ALICE;
96
+ console.log(`━━━ 第 ${Math.floor(i / 2) + 1} 轮 ━━━`);
97
+ console.log(`[${from}] >>> ${content}`);
98
+ try {
99
+ const resp = await fetch(`http://localhost:${targetPort}/receive`, {
100
+ method: 'POST',
101
+ headers: { 'Content-Type': 'application/json' },
102
+ body: JSON.stringify({ fromName: from, content })
103
+ });
104
+ const result = await resp.json();
105
+ console.log(`\n[${to}] 处理结果:`);
106
+ if (result.harnessCalled) {
107
+ console.log(` 🧠 调用 Harness: Gate ${result.gate}`);
108
+ console.log(` 📋 Skills: ${result.skills?.join(', ') || '无'}`);
109
+ harnessCallCount++;
110
+ gateStats.set(result.gate, (gateStats.get(result.gate) || 0) + 1);
111
+ if (expectHarness && result.gate !== expectGate) {
112
+ console.log(` ⚠️ 期望 Gate ${expectGate},实际 ${result.gate}`);
113
+ }
114
+ }
115
+ else {
116
+ console.log(` 🧠 普通对话 (无需 Harness)`);
117
+ }
118
+ console.log(` 📤 回复: ${result.response?.substring(0, 80)}...`);
119
+ // 等待一下再继续
120
+ await new Promise(r => setTimeout(r, 300));
121
+ }
122
+ catch (err) {
123
+ console.log(` ❌ 发送失败:`, err);
124
+ }
125
+ console.log('');
126
+ }
127
+ // 结果汇总
128
+ console.log('━━━ 结果汇总 ━━━\n');
129
+ console.log(`[Alice] 对话数: ${alice.getDialogHistory().length}`);
130
+ console.log(`[Bob] 对话数: ${bob.getDialogHistory().length}`);
131
+ console.log(`\nHarness 调用次数: ${harnessCallCount}`);
132
+ console.log('\nGate 统计:');
133
+ for (const [gate, count] of Array.from(gateStats.entries()).sort((a, b) => a[0] - b[0])) {
134
+ const gateNames = {
135
+ 1: '架构设计',
136
+ 2: '代码审查',
137
+ 4: '安全检查',
138
+ 5: '任务分解',
139
+ 7: '代码实现',
140
+ 8: '测试验证'
141
+ };
142
+ console.log(` Gate ${gate} (${gateNames[gate] || '未知'}): ${count} 次`);
143
+ }
144
+ // 健康检查
145
+ console.log('\n━━━ 最终状态 ━━━\n');
146
+ const aliceHealth = await fetch(`http://localhost:${PORT_ALICE}/health`).then(r => r.json());
147
+ const bobHealth = await fetch(`http://localhost:${PORT_BOB}/health`).then(r => r.json());
148
+ console.log(`[Alice] DID: ${aliceHealth.id.substring(0, 16)}...`);
149
+ console.log(`[Bob] DID: ${bobHealth.id.substring(0, 16)}...`);
150
+ console.log(`\n对话总数: ${aliceHealth.dialogCount + bobHealth.dialogCount}`);
151
+ console.log(`Harness 调用: ${harnessCallCount} 次`);
152
+ const success = harnessCallCount >= 6;
153
+ console.log(`\n${success ? '✅' : '⚠️'} ${success ? '测试成功' : '测试部分成功'}`);
154
+ // 清理
155
+ registry.clear();
156
+ console.log('\n========================================\n');
157
+ return success;
158
+ }
159
+ async function testPersonaDesign() {
160
+ console.log('\n========================================');
161
+ console.log(' Persona 设计测试');
162
+ console.log('========================================\n');
163
+ const agent = createChannelAgent({
164
+ name: 'TestAgent',
165
+ port: 8010,
166
+ domain: '通用'
167
+ });
168
+ await agent.start();
169
+ // 设计不同类型的 persona
170
+ const personaTypes = [
171
+ { name: 'ArchMaster', type: 'developer', domain: '架构', tone: 'professional' },
172
+ { name: 'CodeReviewer', type: 'reviewer', domain: '安全', tone: 'formal' },
173
+ { name: 'TaskManager', type: 'manager', domain: '项目管理', tone: 'professional' },
174
+ { name: 'CreativeDesigner', type: 'designer', domain: '设计', tone: 'casual' }
175
+ ];
176
+ console.log('━━━ 设计 Persona ━━━\n');
177
+ for (const request of personaTypes) {
178
+ const persona = agent.designPersona(request);
179
+ console.log(`设计 ${request.type} persona:`);
180
+ console.log(` 名称: ${persona.name}`);
181
+ console.log(` 描述: ${persona.description}`);
182
+ console.log(` 性格: ${persona.personality}`);
183
+ console.log(` 能力: ${persona.capabilities.join(', ')}`);
184
+ console.log(` 兴趣: ${persona.interests.join(', ')}`);
185
+ console.log('');
186
+ }
187
+ // 获取当前 persona
188
+ const currentPersona = agent.getPersona();
189
+ console.log('当前 Persona:');
190
+ console.log(JSON.stringify(currentPersona, null, 2));
191
+ agent.shutdown();
192
+ console.log('\n========================================\n');
193
+ }
194
+ async function main() {
195
+ try {
196
+ await testMultiTurnDialogue();
197
+ await testPersonaDesign();
198
+ process.exit(0);
199
+ }
200
+ catch (err) {
201
+ console.error('测试失败:', err);
202
+ process.exit(1);
203
+ }
204
+ }
205
+ main();