@bolloon/bolloon-agent 0.1.34 → 0.1.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/.auto-evolve-calls +1 -0
  2. package/.last-auto-evolve-baseline +1 -0
  3. package/.playwright-mcp/page-2026-06-16T07-51-45-706Z.yml +130 -0
  4. package/.playwright-mcp/page-2026-06-16T07-56-44-116Z.yml +131 -0
  5. package/Bolloon.md +144 -0
  6. package/Dive-into/CITATION.cff +17 -0
  7. package/Dive-into/LICENSE +25 -0
  8. package/Dive-into/README.md +598 -0
  9. package/Dive-into/README_zh.md +599 -0
  10. package/Dive-into/assets/context.png +0 -0
  11. package/Dive-into/assets/extensibility.png +0 -0
  12. package/Dive-into/assets/iteration.png +0 -0
  13. package/Dive-into/assets/layered_architecture.png +0 -0
  14. package/Dive-into/assets/main_structure.png +0 -0
  15. package/Dive-into/assets/permission.png +0 -0
  16. package/Dive-into/assets/session_compact.png +0 -0
  17. package/Dive-into/assets/subagent.png +0 -0
  18. package/Dive-into/paper/Dive_into_Claude_Code.pdf +0 -0
  19. package/README.md +1 -1
  20. package/dist/agents/p2p-chat-tools.js +6 -6
  21. package/dist/agents/permission-mode.js +115 -0
  22. package/dist/agents/pi-sdk.js +687 -25
  23. package/dist/agents/pre-tool-validator.js +194 -0
  24. package/dist/agents/workflow-pivot-loop.js +113 -12
  25. package/dist/bollharness/src/scripts/checks/check_doc_freshness.js +1 -1
  26. package/dist/bollharness/src/scripts/checks/check_doc_links.js +1 -1
  27. package/dist/bollharness/src/scripts/context_router.js +1 -1
  28. package/dist/bollharness/src/scripts/deploy-guard.js +1 -1
  29. package/dist/bollharness/src/scripts/guard_router.js +1 -1
  30. package/dist/bollharness/src/scripts/hooks/_hook_output.js +1 -1
  31. package/dist/bollharness/src/scripts/hooks/risk-tracker.js +1 -1
  32. package/dist/bollharness-integration/context-router.js +2 -2
  33. package/dist/bollharness-integration/guard-checker.js +1 -1
  34. package/dist/bootstrap/bootstrap.js +117 -0
  35. package/dist/bootstrap/context-collector.js +312 -0
  36. package/dist/bootstrap/context-hierarchy.js +218 -0
  37. package/dist/bootstrap/lifecycle-hooks.js +171 -0
  38. package/dist/bootstrap/project-context.js +158 -0
  39. package/dist/context-compaction/auto-compact.js +144 -0
  40. package/dist/context-compaction/budget-gate.js +28 -0
  41. package/dist/context-compaction/budget-reduce.js +35 -0
  42. package/dist/context-compaction/context-collapse.js +66 -0
  43. package/dist/context-compaction/index.js +21 -0
  44. package/dist/context-compaction/microcompact.js +51 -0
  45. package/dist/context-compaction/pipeline.js +123 -0
  46. package/dist/context-compaction/snip.js +45 -0
  47. package/dist/context-compaction/token-estimator.js +35 -0
  48. package/dist/context-compaction/types.js +19 -0
  49. package/dist/heartbeat/HealthMonitor.js +3 -2
  50. package/dist/index.js +49 -2
  51. package/dist/llm/llm-judgment-client.js +32 -30
  52. package/dist/llm/pi-ai.js +125 -28
  53. package/dist/llm/system-prompt/health.js +129 -0
  54. package/dist/llm/system-prompt/registry.js +246 -0
  55. package/dist/llm/system-prompt/strip-hibsml.js +51 -0
  56. package/dist/llm/tool-manifest/ask_user_input.js +35 -0
  57. package/dist/llm/tool-manifest/bash.js +23 -0
  58. package/dist/llm/tool-manifest/create_file.js +24 -0
  59. package/dist/llm/tool-manifest/fetch_sports_data.js +26 -0
  60. package/dist/llm/tool-manifest/image_search.js +24 -0
  61. package/dist/llm/tool-manifest/index.js +69 -0
  62. package/dist/llm/tool-manifest/mcp.js +43 -0
  63. package/dist/llm/tool-manifest/message_compose.js +31 -0
  64. package/dist/llm/tool-manifest/places.js +83 -0
  65. package/dist/llm/tool-manifest/present_files.js +21 -0
  66. package/dist/llm/tool-manifest/recipe.js +40 -0
  67. package/dist/llm/tool-manifest/recommend_apps.js +23 -0
  68. package/dist/llm/tool-manifest/str_replace.js +27 -0
  69. package/dist/llm/tool-manifest/types.js +7 -0
  70. package/dist/llm/tool-manifest/view.js +24 -0
  71. package/dist/llm/tool-manifest/weather.js +27 -0
  72. package/dist/llm/tool-manifest/web.js +51 -0
  73. package/dist/network/p2p-direct.js +23 -0
  74. package/dist/network/source-intent-broadcaster.js +203 -0
  75. package/dist/network/source-intent.js +100 -0
  76. package/dist/pi-ecosystem-judgment/adaptive-scan.js +279 -0
  77. package/dist/pi-ecosystem-judgment/causal-judge.js +449 -0
  78. package/dist/pi-ecosystem-judgment/detect-hook.js +168 -0
  79. package/dist/pi-ecosystem-judgment/distill-prompt.js +226 -0
  80. package/dist/pi-ecosystem-judgment/evolve-judgment.js +170 -0
  81. package/dist/pi-ecosystem-judgment/human-value-pipeline.js +21 -0
  82. package/dist/pi-ecosystem-judgment/human-value-store.js +283 -22
  83. package/dist/pi-ecosystem-judgment/injection-gate.js +208 -0
  84. package/dist/pi-ecosystem-judgment/monitor-gate.js +188 -0
  85. package/dist/pi-ecosystem-judgment/value-injection.js +8 -2
  86. package/dist/security/builtin-guards.js +124 -0
  87. package/dist/security/context-router-tool.js +106 -0
  88. package/dist/security/input-scanner.js +223 -0
  89. package/dist/security/react-harness.js +143 -0
  90. package/dist/security/tool-gate.js +235 -0
  91. package/dist/utils/auto-evolve-policy.js +117 -0
  92. package/dist/utils/clamp.js +7 -0
  93. package/dist/utils/double.js +6 -0
  94. package/dist/web/client.js +3810 -3830
  95. package/dist/web/components/p2p/P2PModal.js +188 -0
  96. package/dist/web/components/p2p/index.js +264 -226
  97. package/dist/web/components/p2p/p2p-modal.js +657 -0
  98. package/dist/web/components/p2p/p2p-tools.js +248 -0
  99. package/dist/web/index.html +60 -49
  100. package/dist/web/server.js +827 -124
  101. package/dist/web/style.css +531 -249
  102. package/dist/web/ui/message-renderer.js +463 -0
  103. package/dist/web/ui/step-timeline.js +375 -0
  104. package/lefthook.yml +33 -0
  105. package/package.json +3 -2
  106. package/scripts/auto-evolve-loop.ts +481 -0
  107. package/scripts/auto-evolve-oneshot.sh +155 -0
  108. package/scripts/auto-evolve-snapshot.sh +136 -0
  109. package/scripts/build-web.ts +35 -1
  110. package/scripts/detect-schema-changes.sh +48 -0
  111. package/scripts/diff-reviewer.ts +159 -0
  112. package/scripts/validate-system-prompt.ts +142 -0
  113. package/scripts/weekly-report.ts +364 -0
  114. package/src/agents/p2p-chat-tools.ts +6 -6
  115. package/src/agents/permission-mode.ts +127 -0
  116. package/src/agents/pi-sdk.ts +741 -30
  117. package/src/agents/pre-tool-validator.ts +213 -0
  118. package/src/agents/workflow-pivot-loop.ts +110 -19
  119. package/src/bollharness/CLAUDE.md +1 -1
  120. package/src/bollharness/README.md +2 -2
  121. package/src/bollharness/README.zh-CN.md +2 -2
  122. package/src/bollharness/reference/boll-reference/scripts/hooks/find-boll-root.sh +2 -2
  123. package/src/bollharness/scripts/context-fragments/truth-source-hierarchy.md +2 -2
  124. package/src/bollharness/scripts/context-fragments/version-sources.md +1 -1
  125. package/src/bollharness/scripts/hooks/find-project-root.sh +4 -4
  126. package/src/bollharness/src/scripts/checks/check_doc_freshness.ts +1 -1
  127. package/src/bollharness/src/scripts/checks/check_doc_links.ts +1 -1
  128. package/src/bollharness/src/scripts/context_router.ts +1 -1
  129. package/src/bollharness/src/scripts/deploy-guard.ts +1 -1
  130. package/src/bollharness/src/scripts/guard_router.ts +1 -1
  131. package/src/bollharness/src/scripts/hooks/_hook_output.ts +1 -1
  132. package/src/bollharness/src/scripts/hooks/risk-tracker.ts +1 -1
  133. package/src/bollharness/templates/scaffold/.gitignore.append +1 -1
  134. package/src/bollharness/templates/scaffold/CLAUDE.md +2 -2
  135. package/src/bollharness-integration/context-router.ts +2 -2
  136. package/src/bollharness-integration/guard-checker.ts +1 -1
  137. package/src/bootstrap/bootstrap.ts +135 -0
  138. package/src/bootstrap/context-collector.ts +371 -0
  139. package/src/bootstrap/context-hierarchy.ts +283 -0
  140. package/src/bootstrap/lifecycle-hooks.ts +289 -0
  141. package/src/bootstrap/project-context.ts +171 -0
  142. package/src/context-compaction/auto-compact.ts +153 -0
  143. package/src/context-compaction/budget-gate.ts +32 -0
  144. package/src/context-compaction/budget-reduce.ts +37 -0
  145. package/src/context-compaction/context-collapse.ts +72 -0
  146. package/src/context-compaction/index.ts +24 -0
  147. package/src/context-compaction/microcompact.ts +54 -0
  148. package/src/context-compaction/pipeline.ts +133 -0
  149. package/src/context-compaction/snip.ts +51 -0
  150. package/src/context-compaction/token-estimator.ts +36 -0
  151. package/src/context-compaction/types.ts +99 -0
  152. package/src/heartbeat/HealthMonitor.ts +3 -2
  153. package/src/index.ts +47 -2
  154. package/src/llm/llm-judgment-client.ts +36 -35
  155. package/src/llm/pi-ai.ts +135 -29
  156. package/src/llm/system-prompt/health.ts +159 -0
  157. package/src/llm/system-prompt/layers/channel/local.md +14 -0
  158. package/src/llm/system-prompt/layers/channel/p2p-agent.md +18 -0
  159. package/src/llm/system-prompt/layers/channel/p2p-visitor.md +19 -0
  160. package/src/llm/system-prompt/layers/core/artifacts_storage.md +89 -0
  161. package/src/llm/system-prompt/layers/core/evenhandedness.md +21 -0
  162. package/src/llm/system-prompt/layers/core/hibs_reminders.md +15 -0
  163. package/src/llm/system-prompt/layers/core/identity.md +37 -0
  164. package/src/llm/system-prompt/layers/core/knowledge.md +17 -0
  165. package/src/llm/system-prompt/layers/core/memory_system.md +12 -0
  166. package/src/llm/system-prompt/layers/core/network_filesystem.md +28 -0
  167. package/src/llm/system-prompt/layers/core/refusal.md +37 -0
  168. package/src/llm/system-prompt/layers/core/tone.md +31 -0
  169. package/src/llm/system-prompt/layers/core/tools.thin.md +13 -0
  170. package/src/llm/system-prompt/layers/core/wellbeing.md +41 -0
  171. package/src/llm/system-prompt/layers/role/architect.md +20 -0
  172. package/src/llm/system-prompt/layers/role/expert.md +19 -0
  173. package/src/llm/system-prompt/layers/role/implementer.md +15 -0
  174. package/src/llm/system-prompt/layers/role/security.md +15 -0
  175. package/src/llm/system-prompt/layers/tool/artifacts.md +72 -0
  176. package/src/llm/system-prompt/layers/tool/bash.md +25 -0
  177. package/src/llm/system-prompt/layers/tool/hibs_api.md +171 -0
  178. package/src/llm/system-prompt/layers/tool/image_search.md +70 -0
  179. package/src/llm/system-prompt/layers/tool/manifest.md +89 -0
  180. package/src/llm/system-prompt/layers/tool/mcp_apps.md +53 -0
  181. package/src/llm/system-prompt/layers/tool/web_search.md +83 -0
  182. package/src/llm/system-prompt/registry.ts +325 -0
  183. package/src/llm/system-prompt/strip-hibsml.ts +52 -0
  184. package/src/llm/tool-manifest/ask_user_input.ts +37 -0
  185. package/src/llm/tool-manifest/bash.ts +25 -0
  186. package/src/llm/tool-manifest/create_file.ts +26 -0
  187. package/src/llm/tool-manifest/fetch_sports_data.ts +28 -0
  188. package/src/llm/tool-manifest/image_search.ts +26 -0
  189. package/src/llm/tool-manifest/index.ts +88 -0
  190. package/src/llm/tool-manifest/mcp.ts +46 -0
  191. package/src/llm/tool-manifest/message_compose.ts +33 -0
  192. package/src/llm/tool-manifest/places.ts +86 -0
  193. package/src/llm/tool-manifest/present_files.ts +23 -0
  194. package/src/llm/tool-manifest/recipe.ts +42 -0
  195. package/src/llm/tool-manifest/recommend_apps.ts +25 -0
  196. package/src/llm/tool-manifest/str_replace.ts +29 -0
  197. package/src/llm/tool-manifest/types.ts +52 -0
  198. package/src/llm/tool-manifest/view.ts +26 -0
  199. package/src/llm/tool-manifest/weather.ts +29 -0
  200. package/src/llm/tool-manifest/web.ts +54 -0
  201. package/src/network/p2p-direct.ts +22 -0
  202. package/src/network/source-intent-broadcaster.ts +242 -0
  203. package/src/network/source-intent.ts +167 -0
  204. package/src/security/builtin-guards.ts +162 -0
  205. package/src/security/context-router-tool.ts +122 -0
  206. package/src/security/input-scanner.ts +287 -0
  207. package/src/security/react-harness.ts +177 -0
  208. package/src/security/tool-gate.ts +294 -0
  209. package/src/utils/auto-evolve-policy.ts +138 -0
  210. package/src/utils/clamp.ts +5 -0
  211. package/src/web/client.js +105 -2187
  212. package/src/web/client.ts +4326 -0
  213. package/src/web/index.html +60 -49
  214. package/src/web/server.ts +894 -148
  215. package/src/web/style.css +531 -249
  216. package/src/web/ui/message-renderer.ts +540 -0
  217. package/src/web/ui/step-timeline.ts +394 -0
  218. package/staging/auto-evolve/clean-001/.review-verdict +9 -0
  219. package/staging/auto-evolve/clean-001/clean-001.patch +14 -0
  220. package/staging/auto-evolve/e2e-001/.patch-id +1 -0
  221. package/staging/auto-evolve/e2e-001/.review-verdict +12 -0
  222. package/staging/auto-evolve/e2e-001/e2e-001.patch +11 -0
  223. package/staging/auto-evolve/test-bad/.review-verdict +12 -0
  224. package/staging/auto-evolve/test-bad/test-bad.patch +11 -0
  225. package/test-results/.last-run.json +6 -0
  226. package/test-results/src-test-web-loop-status-b-5734c-atus-bar-/346/230/276/347/244/272-spinner-/344/270/215/346/230/276/347/244/272/345/276/252/347/216/257/350/256/241/346/225/260/error-context.md +285 -0
package/dist/llm/pi-ai.js CHANGED
@@ -1,5 +1,22 @@
1
1
  import * as path from 'path';
2
2
  import * as fs from 'fs';
3
+ /**
4
+ * 外部 system 注入钩子.
5
+ * 调用方 (e.g. auto-evolve-loop) 用 setSystemPrependProvider() 注册一个返回字符串的函数,
6
+ * generateText 在拼 finalMessages 时, 把返回的字符串作为最前一个 system message.
7
+ *
8
+ * 用途: P2P 协作时把"行级 reserve 状态"实时塞给 LLM,
9
+ * 让 LLM 主动避开对方正在改的代码行.
10
+ *
11
+ * 返回 '' / null / undefined → 不注入.
12
+ */
13
+ let _prependProvider = null;
14
+ export function setSystemPrependProvider(p) {
15
+ _prependProvider = p;
16
+ }
17
+ export function getSystemPrependProvider() {
18
+ return _prependProvider;
19
+ }
3
20
  export class PiAIModel {
4
21
  config;
5
22
  provider;
@@ -7,8 +24,8 @@ export class PiAIModel {
7
24
  this.config = config;
8
25
  this.provider = config.provider;
9
26
  }
10
- async chat(message, context) {
11
- const systemPrompt = this.buildSystemPrompt(context);
27
+ async chat(message, context, signal) {
28
+ const systemPrompt = await this.buildSystemPromptAsync(context);
12
29
  const messages = [
13
30
  { role: 'system', content: systemPrompt },
14
31
  { role: 'user', content: message }
@@ -16,13 +33,26 @@ export class PiAIModel {
16
33
  try {
17
34
  const response = await this.generateText({
18
35
  messages,
19
- temperature: 0.8
36
+ temperature: 0.8,
37
+ maxTokens: 8192, // 2026-06-15: 提到 8192, 避免长回答 (含表格/代码块) 被截断
38
+ signal,
20
39
  });
21
40
  return { reply: response };
22
41
  }
23
42
  catch (error) {
43
+ // abort 不当作错误, 透传一个 sentinel 让上层能识别
44
+ if (signal?.aborted || error?.name === 'AbortError') {
45
+ throw error; // 上层 try/catch 处理
46
+ }
24
47
  console.error('PiAI chat error:', error);
25
- return { reply: '抱歉,AI服务暂时不可用。' };
48
+ // 2026-06-15: 真实 error 信息 + 明确告诉 LLM "这是 API 错, 不要 retry"
49
+ // 旧版: "抱歉,AI服务暂时不可用。" → LLM 看到 isTooShort=false(< 50 但 > 0),
50
+ // needsMoreWork 不会触发, 但 hasError 模式 (含 "error" / "失败") 会判定要继续修
51
+ // 新版: 让 LLM 立即停止循环, 直接展示给用户
52
+ const errMsg = (error?.message || '').slice(0, 300);
53
+ return {
54
+ reply: `[AI 服务调用失败] ${errMsg}\n\n这是一个**底层 API 错误**(401 / 鉴权失败 / 网络中断 / 配额耗尽等),不是你的任务有问题。**请直接把这个错误消息回复给用户,不要再循环尝试。**`,
55
+ };
26
56
  }
27
57
  }
28
58
  async summarize(text, context) {
@@ -64,7 +94,39 @@ export class PiAIModel {
64
94
  }
65
95
  }
66
96
  async generateText(options) {
67
- const { messages, temperature = 0.7, maxTokens = 4096 } = options;
97
+ const { messages, temperature = 0.7, maxTokens = 4096, signal, tools } = options;
98
+ // 工具清单: 代码侧 schema → 进 system prompt (作为额外 system message)
99
+ let finalMessages = messages;
100
+ // 1) 外部 prepend (e.g. P2P 行级 reserve 状态), 拼到最前
101
+ if (_prependProvider) {
102
+ try {
103
+ const pre = await _prependProvider();
104
+ if (pre && typeof pre === 'string' && pre.trim()) {
105
+ finalMessages = [{ role: 'system', content: pre }, ...finalMessages];
106
+ }
107
+ }
108
+ catch (err) {
109
+ console.warn('[pi-ai] systemPrepend 失败:', err?.message?.slice(0, 100));
110
+ }
111
+ }
112
+ if (tools && tools.length > 0) {
113
+ try {
114
+ const { getToolManifest, formatForPrompt } = await import('./tool-manifest/index.js');
115
+ const manifests = tools
116
+ .map((id) => getToolManifest(id))
117
+ .filter((m) => m !== undefined);
118
+ if (manifests.length > 0) {
119
+ const toolPrompt = formatForPrompt(manifests);
120
+ finalMessages = [
121
+ { role: 'system', content: toolPrompt },
122
+ ...messages,
123
+ ];
124
+ }
125
+ }
126
+ catch (err) {
127
+ console.warn('[pi-ai] tool-manifest 加载失败:', err.message?.slice(0, 100));
128
+ }
129
+ }
68
130
  switch (this.provider) {
69
131
  case 'openai':
70
132
  case 'minimax':
@@ -72,17 +134,17 @@ export class PiAIModel {
72
134
  case 'kimi':
73
135
  case 'glm':
74
136
  case 'qwen':
75
- return this.callOpenAI(messages, temperature, maxTokens);
137
+ return this.callOpenAI(finalMessages, temperature, maxTokens, signal);
76
138
  case 'anthropic':
77
- return this.callAnthropic(messages, temperature, maxTokens);
139
+ return this.callAnthropic(finalMessages, temperature, maxTokens, signal);
78
140
  case 'ollama':
79
- return this.callOllama(messages, temperature);
141
+ return this.callOllama(finalMessages, temperature, signal);
80
142
  case 'openrouter':
81
- return this.callOpenRouter(messages, temperature, maxTokens);
143
+ return this.callOpenRouter(finalMessages, temperature, maxTokens, signal);
82
144
  case 'gemini':
83
- return this.callGemini(messages, temperature, maxTokens);
145
+ return this.callGemini(finalMessages, temperature, maxTokens, signal);
84
146
  case 'local':
85
- return this.callLocal(messages, temperature);
147
+ return this.callLocal(finalMessages, temperature, signal);
86
148
  default:
87
149
  throw new Error(`Unsupported provider: ${this.provider}`);
88
150
  }
@@ -142,23 +204,28 @@ export class PiAIModel {
142
204
  };
143
205
  return modelMap[this.provider];
144
206
  }
145
- async callOpenAI(messages, temperature, maxTokens) {
207
+ async callOpenAI(messages, temperature, maxTokens, signal) {
146
208
  const apiKey = this.getApiKey();
147
209
  if (!apiKey) {
148
210
  throw new Error('OPENAI_API_KEY not set');
149
211
  }
212
+ const requestBody = {
213
+ model: this.mapModel(),
214
+ messages,
215
+ temperature,
216
+ max_tokens: maxTokens
217
+ };
150
218
  const response = await fetch(`${this.getBaseUrl()}/chat/completions`, {
151
219
  method: 'POST',
152
220
  headers: {
153
221
  'Content-Type': 'application/json',
154
222
  'Authorization': `Bearer ${apiKey}`
155
223
  },
156
- body: JSON.stringify({
157
- model: this.mapModel(),
158
- messages,
159
- temperature,
160
- max_tokens: maxTokens
161
- })
224
+ body: JSON.stringify(requestBody),
225
+ // 2026-06-15: signal 字段防御 — Node 22+ undici 强类型 AbortSignal, 非 AbortSignal 实例
226
+ // (e.g. 误传的 { maxTokens: 1 } 对象) 会 throw "Expected signal to be AbortSignal".
227
+ // 若不是 AbortSignal, 退到无 signal 调用, fetch 自然支持 timeout 由外层控制.
228
+ signal: signal instanceof AbortSignal ? signal : undefined,
162
229
  });
163
230
  if (!response.ok) {
164
231
  throw new Error(`OpenAI API error: ${response.status}`);
@@ -166,7 +233,7 @@ export class PiAIModel {
166
233
  const data = await response.json();
167
234
  return data.choices?.[0]?.message?.content || '';
168
235
  }
169
- async callAnthropic(messages, temperature, maxTokens) {
236
+ async callAnthropic(messages, temperature, maxTokens, signal) {
170
237
  const apiKey = this.getApiKey();
171
238
  if (!apiKey) {
172
239
  throw new Error('ANTHROPIC_API_KEY not set');
@@ -187,7 +254,8 @@ export class PiAIModel {
187
254
  system: systemMessage,
188
255
  temperature,
189
256
  max_tokens: maxTokens
190
- })
257
+ }),
258
+ signal,
191
259
  });
192
260
  if (!response.ok) {
193
261
  throw new Error(`Anthropic API error: ${response.status}`);
@@ -195,7 +263,7 @@ export class PiAIModel {
195
263
  const data = await response.json();
196
264
  return data.content?.[0]?.text || '';
197
265
  }
198
- async callOllama(messages, temperature) {
266
+ async callOllama(messages, temperature, signal) {
199
267
  const response = await fetch(`${this.getBaseUrl()}/api/chat`, {
200
268
  method: 'POST',
201
269
  headers: {
@@ -206,7 +274,8 @@ export class PiAIModel {
206
274
  messages,
207
275
  temperature,
208
276
  stream: false
209
- })
277
+ }),
278
+ signal,
210
279
  });
211
280
  if (!response.ok) {
212
281
  throw new Error(`Ollama API error: ${response.status}`);
@@ -214,7 +283,7 @@ export class PiAIModel {
214
283
  const data = await response.json();
215
284
  return data.message?.content || '';
216
285
  }
217
- async callOpenRouter(messages, temperature, maxTokens) {
286
+ async callOpenRouter(messages, temperature, maxTokens, signal) {
218
287
  const apiKey = this.getApiKey();
219
288
  if (!apiKey) {
220
289
  throw new Error('OPENROUTER_API_KEY not set');
@@ -232,7 +301,8 @@ export class PiAIModel {
232
301
  messages,
233
302
  temperature,
234
303
  max_tokens: maxTokens
235
- })
304
+ }),
305
+ signal,
236
306
  });
237
307
  if (!response.ok) {
238
308
  throw new Error(`OpenRouter API error: ${response.status}`);
@@ -240,7 +310,7 @@ export class PiAIModel {
240
310
  const data = await response.json();
241
311
  return data.choices?.[0]?.message?.content || '';
242
312
  }
243
- async callGemini(messages, temperature, maxTokens) {
313
+ async callGemini(messages, temperature, maxTokens, signal) {
244
314
  const apiKey = this.getApiKey();
245
315
  if (!apiKey) {
246
316
  throw new Error('GEMINI_API_KEY not set');
@@ -264,7 +334,8 @@ export class PiAIModel {
264
334
  temperature,
265
335
  maxOutputTokens: maxTokens
266
336
  }
267
- })
337
+ }),
338
+ signal,
268
339
  });
269
340
  if (!response.ok) {
270
341
  throw new Error(`Gemini API error: ${response.status}`);
@@ -272,9 +343,35 @@ export class PiAIModel {
272
343
  const data = await response.json();
273
344
  return data.candidates?.[0]?.content?.parts?.[0]?.text || '';
274
345
  }
275
- async callLocal(messages, temperature) {
276
- return this.callOllama(messages, temperature);
346
+ async callLocal(messages, temperature, signal) {
347
+ return this.callOllama(messages, temperature, signal);
348
+ }
349
+ // 注: callLocal 直接代理 ollama. 工具清单由 generateText 在外层拼到 messages, 这里 messages 已是 finalMessages.
350
+ async buildSystemPromptAsync(context) {
351
+ // 走 layer registry: 装配所有相关 layer (身份/行为/工具/角色/渠道)
352
+ try {
353
+ const { assembleSystemPrompt, SYSTEM_PROMPT_VERSION } = await import('./system-prompt/registry.js').catch(() => import('./system-prompt/registry.js'));
354
+ // channel = 本机 (PI SDK 直接调就是 local)
355
+ // role = 由 prompt context 决定 (默认 expert)
356
+ const ctx = context ? { channel: 'local', role: 'expert' } : { channel: 'local', role: 'expert' };
357
+ const result = await assembleSystemPrompt(ctx);
358
+ return `${result.text}\n\n## User Working Directory\n${context || process.cwd()}\n\n## bolloon-runtime\n${SYSTEM_PROMPT_VERSION} · layers: ${result.layerIds.join(',')}`;
359
+ }
360
+ catch (err) {
361
+ // 降级: 旧硬编码 (layer registry 不可用时不挂)
362
+ console.warn('[pi-ai] layer registry 不可用, 降级:', err.message?.slice(0, 100));
363
+ const envDetails = this.getEnvironmentDetails();
364
+ return `You are a friendly AI assistant in a P2P document collaboration network.
365
+
366
+ ## User Working Directory
367
+ ${context || process.cwd()}
368
+
369
+ ## Environment
370
+ ${envDetails}`;
371
+ }
277
372
  }
373
+ // 同步版: 旧调用点 (buildSystemPrompt 是同步)
374
+ // 保留但内部用 sync fallback; 后续可改成 async
278
375
  buildSystemPrompt(context) {
279
376
  const envDetails = this.getEnvironmentDetails();
280
377
  return `You are a friendly AI assistant in a P2P document collaboration network.
@@ -0,0 +1,129 @@
1
+ /**
2
+ * system-prompt health — 每层 lifecycle 状态扫描
3
+ *
4
+ * 严格对齐 deusyu 论文 "Harness Gardening 3-6 月" + walkinglabs Lifecycle 4-stage
5
+ * + 马书 ch05+ch13 systemPromptSection + 89 FF 的版本/owner 治理思想.
6
+ *
7
+ * 设计:
8
+ * - 输入: 完整 layer list (assembleSystemPrompt 返回的 layers[])
9
+ * - 输出: 每层 health 状态 (ok | stale | overdue-review | missing-frontmatter)
10
+ * - 失败静默: 任何 layer 读失败 → 标 'missing-frontmatter' 而非抛错
11
+ *
12
+ * 决策 (cross-repo_caveats 选边):
13
+ * - dynamic layers (function source) → 标 'ok' (last_reviewed_at = now), 视作 runtime-managed
14
+ * - frontmatter author 拒绝 'llm-judge' 之外的不合法值 → 标 'missing-frontmatter'
15
+ */
16
+ const DAY_MS = 24 * 60 * 60 * 1000;
17
+ /** 兼容运行时: dynamic layer 没有 meta, 视作 'dynamic' (last_reviewed = now) */
18
+ function isDynamic(l) {
19
+ return l.source === 'function';
20
+ }
21
+ /** 评估单层 health */
22
+ export function evaluateLayer(l, now = new Date()) {
23
+ if (isDynamic(l)) {
24
+ return {
25
+ id: l.id,
26
+ health: 'dynamic',
27
+ ageDays: 0,
28
+ ttlDays: -1,
29
+ remainingDays: -1,
30
+ author: l.meta?.author ?? 'runtime',
31
+ lastReviewedAt: now.toISOString(),
32
+ active: false, // 由 caller (assembleSystemPrompt) 决定
33
+ };
34
+ }
35
+ if (!l.meta) {
36
+ // frontmatter 解析失败或缺失
37
+ return {
38
+ id: l.id,
39
+ health: 'missing-frontmatter',
40
+ ageDays: -1,
41
+ ttlDays: -1,
42
+ remainingDays: -1,
43
+ author: null,
44
+ lastReviewedAt: null,
45
+ active: false,
46
+ };
47
+ }
48
+ const reviewedAt = new Date(l.meta.lastReviewedAt).getTime();
49
+ if (isNaN(reviewedAt)) {
50
+ return {
51
+ id: l.id,
52
+ health: 'missing-frontmatter',
53
+ ageDays: -1,
54
+ ttlDays: l.meta.ttlDays,
55
+ remainingDays: -1,
56
+ author: l.meta.author,
57
+ lastReviewedAt: l.meta.lastReviewedAt,
58
+ active: false,
59
+ };
60
+ }
61
+ const ageMs = now.getTime() - reviewedAt;
62
+ const ageDays = Math.floor(ageMs / DAY_MS);
63
+ const remainingDays = l.meta.ttlDays - ageDays;
64
+ let health;
65
+ if (remainingDays < 0) {
66
+ health = 'overdue-review';
67
+ }
68
+ else if (remainingDays < l.meta.ttlDays * 0.2) {
69
+ health = 'stale'; // 剩余 < 20% ttl 触发告警
70
+ }
71
+ else {
72
+ health = 'ok';
73
+ }
74
+ return {
75
+ id: l.id,
76
+ health,
77
+ ageDays,
78
+ ttlDays: l.meta.ttlDays,
79
+ remainingDays,
80
+ author: l.meta.author,
81
+ lastReviewedAt: l.meta.lastReviewedAt,
82
+ notes: l.meta.notes,
83
+ active: false, // caller 决定
84
+ };
85
+ }
86
+ /** 评估整组 layer (输入: assembleSystemPrompt 返回的 layers[]) */
87
+ export function evaluateLayers(layers, now = new Date()) {
88
+ const entries = layers.map((l) => evaluateLayer(l, now));
89
+ return summarize(entries, now);
90
+ }
91
+ /** 汇总: 给一批 entries 算 counts */
92
+ function summarize(entries, now) {
93
+ const counts = { ok: 0, stale: 0, overdue: 0, missing: 0, dynamic: 0 };
94
+ for (const e of entries) {
95
+ if (e.health === 'ok')
96
+ counts.ok++;
97
+ else if (e.health === 'stale')
98
+ counts.stale++;
99
+ else if (e.health === 'overdue-review')
100
+ counts.overdue++;
101
+ else if (e.health === 'missing-frontmatter')
102
+ counts.missing++;
103
+ else if (e.health === 'dynamic')
104
+ counts.dynamic++;
105
+ }
106
+ return {
107
+ scannedAt: now.toISOString(),
108
+ total: entries.length,
109
+ okCount: counts.ok,
110
+ staleCount: counts.stale,
111
+ overdueCount: counts.overdue,
112
+ missingCount: counts.missing,
113
+ dynamicCount: counts.dynamic,
114
+ entries,
115
+ };
116
+ }
117
+ /** 标记当前 context 下激活的 layer (在 assembleSystemPrompt 调用后用) */
118
+ export function markActive(report, activeIds) {
119
+ return {
120
+ ...report,
121
+ entries: report.entries.map((e) => ({ ...e, active: activeIds.has(e.id) })),
122
+ };
123
+ }
124
+ // ============================================================
125
+ // 测试钩子
126
+ // ============================================================
127
+ export function _resetHealthForTest() {
128
+ // 无内部状态, 保留 API 一致
129
+ }
@@ -0,0 +1,246 @@
1
+ /**
2
+ * registry.ts — system prompt 层注册 + 装配器
3
+ *
4
+ * 设计原则:
5
+ * - 每个 layer 独立文件, 可单独更新/审查/回滚
6
+ * - 装配器按 channel + role + tool 过滤, 输出最终 system prompt
7
+ * - 字符预算按"层级"硬限, 避免腐烂
8
+ * - 远程 P2P layer (function source) 可被远程智能体覆盖本地 layer
9
+ *
10
+ * 装配顺序 (低 priority 先, 高 priority 后):
11
+ * core/identity < core/knowledge < core/tools < channel/* < role/* < tool/* < dynamic/*
12
+ */
13
+ import * as fs from 'fs/promises';
14
+ import * as path from 'path';
15
+ import { fileURLToPath } from 'url';
16
+ import { stripHibsml } from './strip-hibsml.js';
17
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
18
+ const LAYERS_DIR = path.join(__dirname, 'layers');
19
+ /** 解析 .md frontmatter (手写 3 行 regex, 不引 yaml 依赖) */
20
+ function parseFrontmatter(raw) {
21
+ const m = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
22
+ if (!m)
23
+ return { meta: null, body: raw };
24
+ const block = m[1];
25
+ const body = m[2];
26
+ const get = (key) => {
27
+ const re = new RegExp(`^${key}:\\s*(.+)$`, 'm');
28
+ const found = block.match(re);
29
+ return found ? found[1].trim() : undefined;
30
+ };
31
+ const addedAt = get('added_at');
32
+ const lastReviewedAt = get('last_reviewed_at');
33
+ const ttlDaysStr = get('ttl_days');
34
+ const author = get('author');
35
+ const notes = get('notes');
36
+ if (!addedAt || !lastReviewedAt || !ttlDaysStr || !author) {
37
+ // frontmatter 不完整 → 视为无 metadata (health 会标 missing-frontmatter)
38
+ return { meta: null, body: raw };
39
+ }
40
+ const ttlDays = parseInt(ttlDaysStr, 10);
41
+ if (isNaN(ttlDays) || ttlDays <= 0) {
42
+ return { meta: null, body: raw };
43
+ }
44
+ return {
45
+ meta: { addedAt, lastReviewedAt, ttlDays, author, notes },
46
+ body,
47
+ };
48
+ }
49
+ /**
50
+ * 静态 layer 注册表 — 一个 layer 一个文件
51
+ * 这里只声明 metadata, 内容从 .md 读
52
+ *
53
+ * P-Action 2: meta 默认值, 跟分层 TTL 决策一致
54
+ * safety (refusal / hibs_reminders / tools.thin) = 90
55
+ * channel (local / p2p-* persona) = 180
56
+ * role (expert / architect / implementer / security) = 180
57
+ * tool (bash / search / artifacts / etc) = 270
58
+ * knowledge (knowledge / tone / wellbeing / etc) = 365
59
+ * author = yuanjie (可被 .md frontmatter 覆盖)
60
+ */
61
+ const DEFAULT_META_AUTHOR = 'yuanjie';
62
+ const DEFAULT_ADDED_AT = '2026-06-15';
63
+ const DEFAULT_REVIEWED_AT = '2026-06-15';
64
+ const TTL_SAFETY = 90;
65
+ const TTL_CHANNEL = 180;
66
+ const TTL_ROLE = 180;
67
+ const TTL_TOOL = 270;
68
+ const TTL_KNOWLEDGE = 365;
69
+ const DEFAULT_META = (ttlDays) => ({
70
+ addedAt: DEFAULT_ADDED_AT,
71
+ lastReviewedAt: DEFAULT_REVIEWED_AT,
72
+ ttlDays,
73
+ author: DEFAULT_META_AUTHOR,
74
+ });
75
+ /**
76
+ * P-Action 4 (2026-06-15): 单 layer maxChars 全面收紧, 6 个 layer 停用.
77
+ * 阶段 0 不再对齐 Claude.ai 完整版 (hibs_api / artifacts / mcp_apps / image_search /
78
+ * hibs_reminders / network_filesystem 全是 Claude.ai 平台 runtime, 本地 bolloon 用不到).
79
+ * 停用方法: appliesTo 加 'never' (matchesContext 永远 false), 文件保留可回滚.
80
+ */
81
+ const STATIC_LAYERS = [
82
+ // ── core/ ──
83
+ { id: 'core.identity', version: '1.0.0', priority: 50, appliesTo: ['all'], source: 'static-md', maxChars: 800, meta: DEFAULT_META(TTL_KNOWLEDGE) },
84
+ { id: 'core.knowledge', version: '1.0.0', priority: 60, appliesTo: ['all'], source: 'static-md', maxChars: 600, meta: DEFAULT_META(TTL_KNOWLEDGE) },
85
+ { id: 'core.tools.thin', version: '1.0.0', priority: 70, appliesTo: ['all'], source: 'static-md', maxChars: 400, meta: DEFAULT_META(TTL_SAFETY) },
86
+ { id: 'core.hibs_reminders', version: '1.0.0', priority: 80, appliesTo: ['never'], source: 'static-md', maxChars: 0, meta: DEFAULT_META(TTL_SAFETY) },
87
+ { id: 'core.refusal', version: '1.0.0', priority: 100, appliesTo: ['all'], source: 'static-md', maxChars: 800, meta: DEFAULT_META(TTL_SAFETY) },
88
+ { id: 'core.tone', version: '1.0.0', priority: 110, appliesTo: ['all'], source: 'static-md', maxChars: 500, meta: DEFAULT_META(TTL_KNOWLEDGE) },
89
+ { id: 'core.wellbeing', version: '1.0.0', priority: 120, appliesTo: ['all'], source: 'static-md', maxChars: 600, meta: DEFAULT_META(TTL_KNOWLEDGE) },
90
+ { id: 'core.evenhandedness', version: '1.0.0', priority: 130, appliesTo: ['all'], source: 'static-md', maxChars: 300, meta: DEFAULT_META(TTL_KNOWLEDGE) },
91
+ { id: 'core.memory_system', version: '1.0.0', priority: 140, appliesTo: ['all'], source: 'static-md', maxChars: 200, meta: DEFAULT_META(TTL_KNOWLEDGE) },
92
+ { id: 'core.artifacts_storage', version: '1.0.0', priority: 145, appliesTo: ['never'], source: 'static-md', maxChars: 0, meta: DEFAULT_META(TTL_KNOWLEDGE) },
93
+ { id: 'core.network_filesystem', version: '1.0.0', priority: 148, appliesTo: ['never'], source: 'static-md', maxChars: 0, meta: DEFAULT_META(TTL_KNOWLEDGE) },
94
+ // ── role/ ── 阶段 0 只用 expert, 其他 3 个停用 (节省 + 阶段 0 不分 role)
95
+ { id: 'role.expert', version: '1.0.0', priority: 200, appliesTo: ['all', 'role:expert'], source: 'static-md', maxChars: 500, meta: DEFAULT_META(TTL_ROLE) },
96
+ { id: 'role.architect', version: '1.0.0', priority: 200, appliesTo: ['never'], source: 'static-md', maxChars: 0, meta: DEFAULT_META(TTL_ROLE) },
97
+ { id: 'role.implementer', version: '1.0.0', priority: 200, appliesTo: ['never'], source: 'static-md', maxChars: 0, meta: DEFAULT_META(TTL_ROLE) },
98
+ { id: 'role.security', version: '1.0.0', priority: 200, appliesTo: ['never'], source: 'static-md', maxChars: 0, meta: DEFAULT_META(TTL_ROLE) },
99
+ // ── channel/ ──
100
+ { id: 'channel.local', version: '1.0.0', priority: 150, appliesTo: ['local'], source: 'static-md', maxChars: 500, meta: DEFAULT_META(TTL_CHANNEL) },
101
+ { id: 'channel.p2p-visitor', version: '1.0.0', priority: 150, appliesTo: ['p2p-visitor'], source: 'static-md', maxChars: 700 },
102
+ { id: 'channel.p2p-agent', version: '1.0.0', priority: 150, appliesTo: ['p2p-agent'], source: 'static-md', maxChars: 700 },
103
+ // ── tool/ (按工具调用嵌对应 layer) ──
104
+ { id: 'tool.bash', version: '1.0.0', priority: 250, appliesTo: ['tool:bash'], source: 'static-md', maxChars: 600, meta: DEFAULT_META(TTL_TOOL) },
105
+ { id: 'tool.web_search', version: '1.0.0', priority: 250, appliesTo: ['tool:web_search'], source: 'static-md', maxChars: 600, meta: DEFAULT_META(TTL_TOOL) },
106
+ { id: 'tool.mcp_apps', version: '1.0.0', priority: 250, appliesTo: ['never'], source: 'static-md', maxChars: 0, meta: DEFAULT_META(TTL_TOOL) },
107
+ { id: 'tool.hibs_api', version: '1.0.0', priority: 250, appliesTo: ['never'], source: 'static-md', maxChars: 0, meta: DEFAULT_META(TTL_TOOL) },
108
+ { id: 'tool.image_search', version: '1.0.0', priority: 250, appliesTo: ['never'], source: 'static-md', maxChars: 0, meta: DEFAULT_META(TTL_TOOL) },
109
+ { id: 'tool.artifacts', version: '1.0.0', priority: 250, appliesTo: ['never'], source: 'static-md', maxChars: 0, meta: DEFAULT_META(TTL_TOOL) },
110
+ { id: 'tool.manifest', version: '1.0.0', priority: 250, appliesTo: ['tool:manifest'], source: 'static-md', maxChars: 500, meta: DEFAULT_META(TTL_TOOL) },
111
+ ];
112
+ /**
113
+ * 动态 layer (运行时计算, 例如 project context, judgment 注入)
114
+ * 接入现有 project-context.ts + value-injection.ts, 不重复实现
115
+ */
116
+ const DYNAMIC_LAYERS = [
117
+ {
118
+ id: 'dynamic.project-context',
119
+ version: '1.0.0',
120
+ priority: 300,
121
+ appliesTo: ['all'],
122
+ source: 'function',
123
+ maxChars: 2000, // 4000 → 2000 (P-Action 4), 跟 4 级层次合并上限对齐
124
+ resolver: async () => {
125
+ try {
126
+ const { getCachedBolloonContext } = await import('../../pi-ecosystem-judgment/human-value-pipeline.js');
127
+ const { formatContextForSystemPrompt } = await import('../../bootstrap/project-context.js');
128
+ const ctx = await getCachedBolloonContext({ cwd: process.cwd() });
129
+ return formatContextForSystemPrompt(ctx, { maxChars: 4000 });
130
+ }
131
+ catch {
132
+ return '';
133
+ }
134
+ },
135
+ },
136
+ ];
137
+ /**
138
+ * P-Action 4 (2026-06-15): 总字符上限 15000 → 4500.
139
+ * 阶段 0 单次 system prompt 控制在 ≤ 4.5KB (≈ 1125 tokens).
140
+ * 配合单 layer maxChars 收紧 + 6 layer 停用, 每轮 chat 节省 ≈ 2625 tokens.
141
+ */
142
+ const TOTAL_BUDGET = 4500;
143
+ export async function assembleSystemPrompt(ctx) {
144
+ // 1. 收集所有 layer
145
+ const allLayers = [];
146
+ for (const meta of STATIC_LAYERS) {
147
+ const { content, meta: sectionMeta } = await loadStaticLayer(meta.id);
148
+ allLayers.push({ ...meta, content, meta: sectionMeta ?? meta.meta });
149
+ }
150
+ for (const dyn of DYNAMIC_LAYERS) {
151
+ allLayers.push({ ...dyn, meta: dyn.meta });
152
+ }
153
+ // 2. 过滤
154
+ const matched = allLayers.filter((l) => matchesContext(l, ctx));
155
+ // 3. 排序 (priority 低 → 高, 同 priority 按 id 字母)
156
+ matched.sort((a, b) => a.priority - b.priority || a.id.localeCompare(b.id));
157
+ // 4. 装配 + 截断
158
+ const parts = [];
159
+ const used = [];
160
+ const truncated = [];
161
+ let usedChars = 0;
162
+ for (const layer of matched) {
163
+ if (usedChars >= TOTAL_BUDGET) {
164
+ truncated.push(layer.id);
165
+ continue;
166
+ }
167
+ let content = '';
168
+ if (layer.source === 'function' && layer.resolver) {
169
+ try {
170
+ content = await layer.resolver();
171
+ }
172
+ catch {
173
+ content = '';
174
+ }
175
+ }
176
+ else {
177
+ content = layer.content || '';
178
+ }
179
+ if (content.length > layer.maxChars) {
180
+ content = content.slice(0, layer.maxChars) + '\n[… 已截断]';
181
+ truncated.push(layer.id);
182
+ }
183
+ if (usedChars + content.length > TOTAL_BUDGET) {
184
+ const remain = TOTAL_BUDGET - usedChars;
185
+ if (remain > 100) {
186
+ content = content.slice(0, remain) + '\n[… 预算截断]';
187
+ }
188
+ else {
189
+ truncated.push(layer.id);
190
+ continue;
191
+ }
192
+ }
193
+ parts.push(`<!-- ${layer.id}@${layer.version} -->\n${content}`);
194
+ used.push(layer.id);
195
+ usedChars += content.length;
196
+ }
197
+ return {
198
+ text: stripHibsml(parts.join('\n\n')),
199
+ layerIds: used,
200
+ totalChars: usedChars,
201
+ truncated,
202
+ layers: matched, // P-Action 2: 把 meta 也带回, 供 health 端点
203
+ };
204
+ }
205
+ function matchesContext(layer, ctx) {
206
+ if (layer.appliesTo.includes('all'))
207
+ return true;
208
+ for (const a of layer.appliesTo) {
209
+ if (a === ctx.channel)
210
+ return true;
211
+ if (a.startsWith('role:') && a.slice(5) === ctx.role)
212
+ return true;
213
+ if (a.startsWith('tool:') && a.slice(5) === ctx.tool)
214
+ return true;
215
+ }
216
+ return false;
217
+ }
218
+ async function loadStaticLayer(id) {
219
+ const path2md = idToPath(id);
220
+ try {
221
+ const raw = await fs.readFile(path2md, 'utf-8');
222
+ const { meta, body } = parseFrontmatter(raw);
223
+ return { content: body, meta };
224
+ }
225
+ catch (err) {
226
+ console.warn(`[system-prompt] layer ${id} 读失败: ${err.message?.slice(0, 100)}`);
227
+ return { content: `<!-- ${id}: 文件丢失 -->`, meta: null };
228
+ }
229
+ }
230
+ function idToPath(id) {
231
+ // core.refusal → layers/core/refusal.md
232
+ // tool.bash → layers/tool/bash.md
233
+ // channel.p2p-visitor → layers/channel/p2p-visitor.md
234
+ // core.tools.thin → layers/core/tools.thin.md (注意: 第一段是 group, 后面整段是文件名)
235
+ const [group, ...rest] = id.split('.');
236
+ return path.join(LAYERS_DIR, group, `${rest.join('.')}.md`);
237
+ }
238
+ /** 列所有 layer, 供调试 / reviewer 看 */
239
+ export function listLayers() {
240
+ return [...STATIC_LAYERS, ...DYNAMIC_LAYERS.map((l) => {
241
+ const { resolver, ...rest } = l;
242
+ return rest;
243
+ })];
244
+ }
245
+ /** 整体版本号 — 跟 hibs 1 对齐 */
246
+ export const SYSTEM_PROMPT_VERSION = 'hibs-1.v1.0.0';