@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
@@ -0,0 +1,325 @@
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
+
14
+ import * as fs from 'fs/promises';
15
+ import * as path from 'path';
16
+ import { fileURLToPath } from 'url';
17
+ import { stripHibsml } from './strip-hibsml.js';
18
+
19
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
20
+ const LAYERS_DIR = path.join(__dirname, 'layers');
21
+
22
+ export type AppliesTo = (
23
+ | 'local' | 'p2p-visitor' | 'p2p-agent'
24
+ | 'role:expert' | 'role:architect' | 'role:implementer' | 'role:security'
25
+ | 'tool:bash' | 'tool:str_replace' | 'tool:view' | 'tool:web_search' | 'tool:web_fetch'
26
+ | 'tool:mcp_apps' | 'tool:hibs_api' | 'tool:image_search' | 'tool:artifacts' | 'tool:manifest'
27
+ | 'all'
28
+ | 'never' // P-Action 4: 停用标记, .md 保留可回滚, runtime 永远不装配
29
+ );
30
+
31
+ export type LayerSource = 'static-md' | 'function';
32
+
33
+ export interface PromptLayer {
34
+ /** 唯一 ID, 路径式: core.refusal / channel.local / tool.bash */
35
+ id: string;
36
+ /** semver */
37
+ version: string;
38
+ /** 数字小 = 排前, 同 priority 按字母序 */
39
+ priority: number;
40
+ /** 该 layer 适用场景. 'all' 永远嵌入. 其他按 channel/role/tool 过滤 */
41
+ appliesTo: AppliesTo[];
42
+ /** 来源: 静态 .md 或函数(可异步,可被 P2P 覆盖) */
43
+ source: LayerSource;
44
+ /** 单层字符上限 (腐烂防护) */
45
+ maxChars: number;
46
+ /** 静态内容 (source='static-md' 时) */
47
+ content?: string;
48
+ /** 动态内容 (source='function' 时) */
49
+ resolver?: () => string | Promise<string>;
50
+ /**
51
+ * Provenance + Lifecycle metadata (P-Action 2)
52
+ * 安全/identity 类: ttl_days=90 (季度审)
53
+ * channel/role persona: ttl_days=180
54
+ * tool/manual: ttl_days=270
55
+ * knowledge/context: ttl_days=365
56
+ * author ∈ {yuanjie | community | maintainer-name}; 拒绝 llm-judge
57
+ */
58
+ meta?: SectionMeta;
59
+ }
60
+
61
+ export interface SectionMeta {
62
+ /** 初始填入时间 (ISO 8601) */
63
+ addedAt: string;
64
+ /** 上次审视时间 (ISO 8601) */
65
+ lastReviewedAt: string;
66
+ /** 过期天数 (从 lastReviewedAt 算起) */
67
+ ttlDays: number;
68
+ /** 维护者 */
69
+ author: string;
70
+ /** 自由 notes (例如 '需要 domain expert 复审', '依赖 external lib X') */
71
+ notes?: string;
72
+ }
73
+
74
+ /** 解析 .md frontmatter (手写 3 行 regex, 不引 yaml 依赖) */
75
+ function parseFrontmatter(raw: string): { meta: SectionMeta | null; body: string } {
76
+ const m = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
77
+ if (!m) return { meta: null, body: raw };
78
+ const block = m[1];
79
+ const body = m[2];
80
+ const get = (key: string): string | undefined => {
81
+ const re = new RegExp(`^${key}:\\s*(.+)$`, 'm');
82
+ const found = block.match(re);
83
+ return found ? found[1].trim() : undefined;
84
+ };
85
+ const addedAt = get('added_at');
86
+ const lastReviewedAt = get('last_reviewed_at');
87
+ const ttlDaysStr = get('ttl_days');
88
+ const author = get('author');
89
+ const notes = get('notes');
90
+ if (!addedAt || !lastReviewedAt || !ttlDaysStr || !author) {
91
+ // frontmatter 不完整 → 视为无 metadata (health 会标 missing-frontmatter)
92
+ return { meta: null, body: raw };
93
+ }
94
+ const ttlDays = parseInt(ttlDaysStr, 10);
95
+ if (isNaN(ttlDays) || ttlDays <= 0) {
96
+ return { meta: null, body: raw };
97
+ }
98
+ return {
99
+ meta: { addedAt, lastReviewedAt, ttlDays, author, notes },
100
+ body,
101
+ };
102
+ }
103
+
104
+ /**
105
+ * 静态 layer 注册表 — 一个 layer 一个文件
106
+ * 这里只声明 metadata, 内容从 .md 读
107
+ *
108
+ * P-Action 2: meta 默认值, 跟分层 TTL 决策一致
109
+ * safety (refusal / hibs_reminders / tools.thin) = 90
110
+ * channel (local / p2p-* persona) = 180
111
+ * role (expert / architect / implementer / security) = 180
112
+ * tool (bash / search / artifacts / etc) = 270
113
+ * knowledge (knowledge / tone / wellbeing / etc) = 365
114
+ * author = yuanjie (可被 .md frontmatter 覆盖)
115
+ */
116
+ const DEFAULT_META_AUTHOR = 'yuanjie';
117
+ const DEFAULT_ADDED_AT = '2026-06-15';
118
+ const DEFAULT_REVIEWED_AT = '2026-06-15';
119
+ const TTL_SAFETY = 90;
120
+ const TTL_CHANNEL = 180;
121
+ const TTL_ROLE = 180;
122
+ const TTL_TOOL = 270;
123
+ const TTL_KNOWLEDGE = 365;
124
+ const DEFAULT_META = (ttlDays: number): SectionMeta => ({
125
+ addedAt: DEFAULT_ADDED_AT,
126
+ lastReviewedAt: DEFAULT_REVIEWED_AT,
127
+ ttlDays,
128
+ author: DEFAULT_META_AUTHOR,
129
+ });
130
+
131
+ /**
132
+ * P-Action 4 (2026-06-15): 单 layer maxChars 全面收紧, 6 个 layer 停用.
133
+ * 阶段 0 不再对齐 Claude.ai 完整版 (hibs_api / artifacts / mcp_apps / image_search /
134
+ * hibs_reminders / network_filesystem 全是 Claude.ai 平台 runtime, 本地 bolloon 用不到).
135
+ * 停用方法: appliesTo 加 'never' (matchesContext 永远 false), 文件保留可回滚.
136
+ */
137
+ const STATIC_LAYERS: Omit<PromptLayer, 'content'>[] = [
138
+ // ── core/ ──
139
+ { id: 'core.identity', version: '1.0.0', priority: 50, appliesTo: ['all'], source: 'static-md', maxChars: 800 , meta: DEFAULT_META(TTL_KNOWLEDGE) },
140
+ { id: 'core.knowledge', version: '1.0.0', priority: 60, appliesTo: ['all'], source: 'static-md', maxChars: 600 , meta: DEFAULT_META(TTL_KNOWLEDGE) },
141
+ { id: 'core.tools.thin', version: '1.0.0', priority: 70, appliesTo: ['all'], source: 'static-md', maxChars: 400 , meta: DEFAULT_META(TTL_SAFETY) },
142
+ { id: 'core.hibs_reminders', version: '1.0.0', priority: 80, appliesTo: ['never'], source: 'static-md', maxChars: 0 , meta: DEFAULT_META(TTL_SAFETY) },
143
+ { id: 'core.refusal', version: '1.0.0', priority: 100, appliesTo: ['all'], source: 'static-md', maxChars: 800 , meta: DEFAULT_META(TTL_SAFETY) },
144
+ { id: 'core.tone', version: '1.0.0', priority: 110, appliesTo: ['all'], source: 'static-md', maxChars: 500 , meta: DEFAULT_META(TTL_KNOWLEDGE) },
145
+ { id: 'core.wellbeing', version: '1.0.0', priority: 120, appliesTo: ['all'], source: 'static-md', maxChars: 600 , meta: DEFAULT_META(TTL_KNOWLEDGE) },
146
+ { id: 'core.evenhandedness', version: '1.0.0', priority: 130, appliesTo: ['all'], source: 'static-md', maxChars: 300 , meta: DEFAULT_META(TTL_KNOWLEDGE) },
147
+ { id: 'core.memory_system', version: '1.0.0', priority: 140, appliesTo: ['all'], source: 'static-md', maxChars: 200 , meta: DEFAULT_META(TTL_KNOWLEDGE) },
148
+ { id: 'core.artifacts_storage',version: '1.0.0', priority: 145, appliesTo: ['never'], source: 'static-md', maxChars: 0 , meta: DEFAULT_META(TTL_KNOWLEDGE) },
149
+ { id: 'core.network_filesystem',version: '1.0.0', priority: 148, appliesTo: ['never'], source: 'static-md', maxChars: 0 , meta: DEFAULT_META(TTL_KNOWLEDGE) },
150
+
151
+ // ── role/ ── 阶段 0 只用 expert, 其他 3 个停用 (节省 + 阶段 0 不分 role)
152
+ { id: 'role.expert', version: '1.0.0', priority: 200, appliesTo: ['all', 'role:expert'], source: 'static-md', maxChars: 500 , meta: DEFAULT_META(TTL_ROLE) },
153
+ { id: 'role.architect', version: '1.0.0', priority: 200, appliesTo: ['never'], source: 'static-md', maxChars: 0 , meta: DEFAULT_META(TTL_ROLE) },
154
+ { id: 'role.implementer', version: '1.0.0', priority: 200, appliesTo: ['never'], source: 'static-md', maxChars: 0 , meta: DEFAULT_META(TTL_ROLE) },
155
+ { id: 'role.security', version: '1.0.0', priority: 200, appliesTo: ['never'], source: 'static-md', maxChars: 0 , meta: DEFAULT_META(TTL_ROLE) },
156
+
157
+ // ── channel/ ──
158
+ { id: 'channel.local', version: '1.0.0', priority: 150, appliesTo: ['local'], source: 'static-md', maxChars: 500 , meta: DEFAULT_META(TTL_CHANNEL) },
159
+ { id: 'channel.p2p-visitor', version: '1.0.0', priority: 150, appliesTo: ['p2p-visitor'], source: 'static-md', maxChars: 700 },
160
+ { id: 'channel.p2p-agent', version: '1.0.0', priority: 150, appliesTo: ['p2p-agent'], source: 'static-md', maxChars: 700 },
161
+
162
+ // ── tool/ (按工具调用嵌对应 layer) ──
163
+ { id: 'tool.bash', version: '1.0.0', priority: 250, appliesTo: ['tool:bash'], source: 'static-md', maxChars: 600 , meta: DEFAULT_META(TTL_TOOL) },
164
+ { id: 'tool.web_search', version: '1.0.0', priority: 250, appliesTo: ['tool:web_search'], source: 'static-md', maxChars: 600 , meta: DEFAULT_META(TTL_TOOL) },
165
+ { id: 'tool.mcp_apps', version: '1.0.0', priority: 250, appliesTo: ['never'], source: 'static-md', maxChars: 0 , meta: DEFAULT_META(TTL_TOOL) },
166
+ { id: 'tool.hibs_api', version: '1.0.0', priority: 250, appliesTo: ['never'], source: 'static-md', maxChars: 0 , meta: DEFAULT_META(TTL_TOOL) },
167
+ { id: 'tool.image_search', version: '1.0.0', priority: 250, appliesTo: ['never'], source: 'static-md', maxChars: 0 , meta: DEFAULT_META(TTL_TOOL) },
168
+ { id: 'tool.artifacts', version: '1.0.0', priority: 250, appliesTo: ['never'], source: 'static-md', maxChars: 0 , meta: DEFAULT_META(TTL_TOOL) },
169
+ { id: 'tool.manifest', version: '1.0.0', priority: 250, appliesTo: ['tool:manifest'], source: 'static-md', maxChars: 500 , meta: DEFAULT_META(TTL_TOOL) },
170
+ ];
171
+
172
+ /**
173
+ * 动态 layer (运行时计算, 例如 project context, judgment 注入)
174
+ * 接入现有 project-context.ts + value-injection.ts, 不重复实现
175
+ */
176
+ const DYNAMIC_LAYERS: PromptLayer[] = [
177
+ {
178
+ id: 'dynamic.project-context',
179
+ version: '1.0.0',
180
+ priority: 300,
181
+ appliesTo: ['all'],
182
+ source: 'function',
183
+ maxChars: 2000, // 4000 → 2000 (P-Action 4), 跟 4 级层次合并上限对齐
184
+ resolver: async () => {
185
+ try {
186
+ const { getCachedBolloonContext } = await import('../../pi-ecosystem-judgment/human-value-pipeline.js');
187
+ const { formatContextForSystemPrompt } = await import('../../bootstrap/project-context.js');
188
+ const ctx = await getCachedBolloonContext({ cwd: process.cwd() });
189
+ return formatContextForSystemPrompt(ctx, { maxChars: 4000 });
190
+ } catch {
191
+ return '';
192
+ }
193
+ },
194
+ },
195
+ ];
196
+
197
+ /**
198
+ * 装配 system prompt — 按 context 过滤 + 排序 + 截断
199
+ */
200
+ export interface AssembleContext {
201
+ channel: 'local' | 'p2p-visitor' | 'p2p-agent';
202
+ role?: 'expert' | 'architect' | 'implementer' | 'security';
203
+ tool?: 'bash' | 'str_replace' | 'view' | 'web_search' | 'web_fetch';
204
+ }
205
+
206
+ /**
207
+ * P-Action 4 (2026-06-15): 总字符上限 15000 → 4500.
208
+ * 阶段 0 单次 system prompt 控制在 ≤ 4.5KB (≈ 1125 tokens).
209
+ * 配合单 layer maxChars 收紧 + 6 layer 停用, 每轮 chat 节省 ≈ 2625 tokens.
210
+ */
211
+ const TOTAL_BUDGET = 4500;
212
+
213
+ export async function assembleSystemPrompt(ctx: AssembleContext): Promise<{
214
+ text: string;
215
+ layerIds: string[];
216
+ totalChars: number;
217
+ truncated: string[];
218
+ /** P-Action 2: 每层 provenance + lifecycle metadata, 供 health 端点用 */
219
+ layers: PromptLayer[];
220
+ }> {
221
+ // 1. 收集所有 layer
222
+ const allLayers: PromptLayer[] = [];
223
+ for (const meta of STATIC_LAYERS) {
224
+ const { content, meta: sectionMeta } = await loadStaticLayer(meta.id);
225
+ allLayers.push({ ...meta, content, meta: sectionMeta ?? meta.meta });
226
+ }
227
+ for (const dyn of DYNAMIC_LAYERS) {
228
+ allLayers.push({ ...dyn, meta: dyn.meta });
229
+ }
230
+
231
+ // 2. 过滤
232
+ const matched = allLayers.filter((l) => matchesContext(l, ctx));
233
+
234
+ // 3. 排序 (priority 低 → 高, 同 priority 按 id 字母)
235
+ matched.sort((a, b) => a.priority - b.priority || a.id.localeCompare(b.id));
236
+
237
+ // 4. 装配 + 截断
238
+ const parts: string[] = [];
239
+ const used: string[] = [];
240
+ const truncated: string[] = [];
241
+ let usedChars = 0;
242
+
243
+ for (const layer of matched) {
244
+ if (usedChars >= TOTAL_BUDGET) {
245
+ truncated.push(layer.id);
246
+ continue;
247
+ }
248
+ let content = '';
249
+ if (layer.source === 'function' && layer.resolver) {
250
+ try {
251
+ content = await layer.resolver();
252
+ } catch {
253
+ content = '';
254
+ }
255
+ } else {
256
+ content = layer.content || '';
257
+ }
258
+ if (content.length > layer.maxChars) {
259
+ content = content.slice(0, layer.maxChars) + '\n[… 已截断]';
260
+ truncated.push(layer.id);
261
+ }
262
+ if (usedChars + content.length > TOTAL_BUDGET) {
263
+ const remain = TOTAL_BUDGET - usedChars;
264
+ if (remain > 100) {
265
+ content = content.slice(0, remain) + '\n[… 预算截断]';
266
+ } else {
267
+ truncated.push(layer.id);
268
+ continue;
269
+ }
270
+ }
271
+ parts.push(`<!-- ${layer.id}@${layer.version} -->\n${content}`);
272
+ used.push(layer.id);
273
+ usedChars += content.length;
274
+ }
275
+
276
+ return {
277
+ text: stripHibsml(parts.join('\n\n')),
278
+ layerIds: used,
279
+ totalChars: usedChars,
280
+ truncated,
281
+ layers: matched, // P-Action 2: 把 meta 也带回, 供 health 端点
282
+ };
283
+ }
284
+
285
+ function matchesContext(layer: PromptLayer, ctx: AssembleContext): boolean {
286
+ if (layer.appliesTo.includes('all')) return true;
287
+ for (const a of layer.appliesTo) {
288
+ if (a === ctx.channel) return true;
289
+ if (a.startsWith('role:') && a.slice(5) === ctx.role) return true;
290
+ if (a.startsWith('tool:') && a.slice(5) === ctx.tool) return true;
291
+ }
292
+ return false;
293
+ }
294
+
295
+ async function loadStaticLayer(id: string): Promise<{ content: string; meta: SectionMeta | null }> {
296
+ const path2md = idToPath(id);
297
+ try {
298
+ const raw = await fs.readFile(path2md, 'utf-8');
299
+ const { meta, body } = parseFrontmatter(raw);
300
+ return { content: body, meta };
301
+ } catch (err: any) {
302
+ console.warn(`[system-prompt] layer ${id} 读失败: ${err.message?.slice(0, 100)}`);
303
+ return { content: `<!-- ${id}: 文件丢失 -->`, meta: null };
304
+ }
305
+ }
306
+
307
+ function idToPath(id: string): string {
308
+ // core.refusal → layers/core/refusal.md
309
+ // tool.bash → layers/tool/bash.md
310
+ // channel.p2p-visitor → layers/channel/p2p-visitor.md
311
+ // core.tools.thin → layers/core/tools.thin.md (注意: 第一段是 group, 后面整段是文件名)
312
+ const [group, ...rest] = id.split('.');
313
+ return path.join(LAYERS_DIR, group, `${rest.join('.')}.md`);
314
+ }
315
+
316
+ /** 列所有 layer, 供调试 / reviewer 看 */
317
+ export function listLayers(): Array<Omit<PromptLayer, 'content' | 'resolver'>> {
318
+ return [...STATIC_LAYERS, ...DYNAMIC_LAYERS.map((l) => {
319
+ const { resolver, ...rest } = l;
320
+ return rest;
321
+ })];
322
+ }
323
+
324
+ /** 整体版本号 — 跟 hibs 1 对齐 */
325
+ export const SYSTEM_PROMPT_VERSION = 'hibs-1.v1.0.0';
@@ -0,0 +1,52 @@
1
+ /**
2
+ * strip-hibsml.ts — 把 hibsml 协议标签转成 bolloon 标记
3
+ *
4
+ * hibsml 是 hibs 的协议: {hibsml:cite} {hibsml:invoke} {hibsml:thinking_mode}
5
+ * bolloon 不解析这些, 但传给 LLM 之前要"金脱除":
6
+ * - cite → [CITE:index]
7
+ * - invoke → 简化
8
+ * - thinking_mode → 注释形式保留
9
+ * - 其他标签 → 整段脱除
10
+ *
11
+ * 不脱除 hibsml 标签会让 LLM 看到原文标记 (raw XML) 误以为是工具调用.
12
+ */
13
+ const HIBML_REPLACEMENTS: [RegExp, string | ((m: string, ...g: string[]) => string)][] = [
14
+ // {hibsml:cite index="0-1,3-5"}...{/hibsml:cite} → [CITE:0-1,3-5] ... [ENDCITE]
15
+ [
16
+ /\{hibsml:cite\s+index="([^"]+)"\}/g,
17
+ (_m, idx) => `[CITE:${idx}]`,
18
+ ],
19
+ [/\{\/hibsml:cite\}/g, '[ENDCITE]'],
20
+ // {hibsml:invoke name="X"} ... {/hibsml:invoke} → [TOOL:X] ... [ENDTOOL]
21
+ [
22
+ /\{hibsml:invoke\s+name="([^"]+)"\}/g,
23
+ (_m, name) => `[TOOL:${name}]`,
24
+ ],
25
+ [/\{\/hibsml:invoke\}/g, '[ENDTOOL]'],
26
+ // {hibsml:parameter name="X"}val{/hibsml:parameter} → [P:X]val[/P]
27
+ [
28
+ /\{hibsml:parameter\s+name="([^"]+)"\}/g,
29
+ (_m, name) => `[P:${name}]`,
30
+ ],
31
+ [/\{\/hibsml:parameter\}/g, '[/P]'],
32
+ // {hibsml:thinking_mode}auto{/hibsml:thinking_mode} → [THINKING:auto] (保留)
33
+ [
34
+ /\{hibsml:thinking_mode\}(\w+)\{\/hibsml:thinking_mode\}/g,
35
+ (_m, mode) => `[THINKING:${mode}]`,
36
+ ],
37
+ // 脱除 voice_note (hibs 1 明确说永远不用)
38
+ [/<details><summary>.*?<\/summary>.*?<\/details>/gs, ''],
39
+ // 任何其他 hibsml:* 标签
40
+ [/\{hibsml:[a-z_]+\}/g, ''],
41
+ [/\{\/hibsml:[a-z_]+\}/g, ''],
42
+ ];
43
+
44
+ export function stripHibsml(text: string): string {
45
+ let out = text;
46
+ for (const [re, rep] of HIBML_REPLACEMENTS) {
47
+ out = out.replace(re, rep as any);
48
+ }
49
+ // 收尾: 多个空行合并
50
+ out = out.replace(/\n{3,}/g, '\n\n');
51
+ return out.trim();
52
+ }
@@ -0,0 +1,37 @@
1
+ import type { ToolManifest } from './types.js';
2
+
3
+ export const ask_user_input_v0: ToolManifest = {
4
+ id: 'ask_user_input_v0',
5
+ name: 'ask_user_input_v0',
6
+ oneLine: '在提供建议前展示可点击的选项以收集用户偏好 (移动端友好).',
7
+ description: '此工具显示交互式按钮, 用户可以点击按钮来回答, 在移动端比键入要容易得多. 用于引出信息 (ELICITATION) — 当你需要了解用户的偏好、约束或目标以提供有用建议时.',
8
+ whenToUse: [
9
+ '需要了解用户的偏好/约束/目标以提供有用建议',
10
+ '示例: 帮我制定锻炼计划 → 询问目标(力量/有氧/减重)、可用时间、设备访问',
11
+ '示例: 帮我为朋友挑礼物 → 询问场合、预算、朋友的兴趣',
12
+ ],
13
+ whenNotToUse: [
14
+ '用户问 A 还是 B (想要分析, 不是按钮)',
15
+ '用户在发泄或处理情绪',
16
+ '用户询问观点',
17
+ '事实问题 (直接回答)',
18
+ '用户需要散文形式反馈',
19
+ ],
20
+ parameters: [
21
+ { name: 'questions', type: 'array', required: true, description: '1-3 个问题', items: {
22
+ name: 'question', type: 'object', required: true, description: '单个问题', properties: [
23
+ { name: 'question', type: 'string', required: true, description: '向用户显示的问题' },
24
+ { name: 'type', type: 'enum', required: false, description: 'single_select | multi-select | rank_priorities', enumValues: ['single_select', 'multi_select', 'rank_priorities'], default: 'single_select' },
25
+ { name: 'options', type: 'array', required: true, description: '2-4 个带短标签的选项', minItems: 2, maxItems: 4, items: {
26
+ name: 'option', type: 'object', required: true, description: '选项', properties: [
27
+ { name: 'label', type: 'string', required: true, description: '短标签' },
28
+ { name: 'description', type: 'string', required: false, description: '选项描述' },
29
+ ],
30
+ } },
31
+ ],
32
+ } },
33
+ ],
34
+ callExample: `[TOOL:ask_user_input_v0]
35
+ [P:questions]${'${'}JSON.stringify([{"question":"你的预算?","options":[{"label":"<500"},{"label":"500-2000"}]}])[ENDTOOL]`,
36
+ layerId: 'tool.manifest',
37
+ };
@@ -0,0 +1,25 @@
1
+ import type { ToolManifest } from './types.js';
2
+
3
+ export const bash_tool: ToolManifest = {
4
+ id: 'bash_tool',
5
+ name: 'bash_tool',
6
+ oneLine: '在容器中运行 bash 命令.',
7
+ description: '在容器中运行 bash 命令. 必须用 description 参数说明为什么跑.',
8
+ whenToUse: [
9
+ '需要执行 shell 命令 (ls, mkdir, npm, git 等)',
10
+ '需要跑构建/测试/脚本',
11
+ ],
12
+ whenNotToUse: [
13
+ '读取文件 (用 view)',
14
+ '编辑文件 (用 str_replace 或 create_file)',
15
+ ],
16
+ parameters: [
17
+ { name: 'command', type: 'string', required: true, description: '要运行的 bash 命令' },
18
+ { name: 'description', type: 'string', required: true, description: '为什么我要运行此命令' },
19
+ ],
20
+ callExample: `[TOOL:bash_tool]
21
+ [P:command]ls -la /tmp
22
+ [P:description]列出 /tmp 目录内容
23
+ [ENDTOOL]`,
24
+ layerId: 'tool.bash',
25
+ };
@@ -0,0 +1,26 @@
1
+ import type { ToolManifest } from './types.js';
2
+
3
+ export const create_file: ToolManifest = {
4
+ id: 'create_file',
5
+ name: 'create_file',
6
+ oneLine: '在容器中创建带有内容的新文件.',
7
+ description: '如果路径已存在则失败 — 使用 str_replace 编辑现有文件, 或使用 bash_tool (cat > path << EOF) 覆盖.',
8
+ whenToUse: [
9
+ '创建新文件',
10
+ '需要写完整的文件内容',
11
+ ],
12
+ whenNotToUse: [
13
+ '编辑已有文件 (用 str_replace)',
14
+ ],
15
+ parameters: [
16
+ { name: 'description', type: 'string', required: true, description: '为什么我要创建此文件' },
17
+ { name: 'file_text', type: 'string', required: true, description: '要写入的文件内容' },
18
+ { name: 'path', type: 'string', required: true, description: '要创建的文件路径' },
19
+ ],
20
+ callExample: `[TOOL:create_file]
21
+ [P:description]新建 README
22
+ [P:file_text]# Project\n\nHello world
23
+ [P:path]/home/bolloon/README.md
24
+ [ENDTOOL]`,
25
+ layerId: 'tool.manifest',
26
+ };
@@ -0,0 +1,28 @@
1
+ import type { ToolManifest } from './types.js';
2
+
3
+ export const fetch_sports_data: ToolManifest = {
4
+ id: 'fetch_sports_data',
5
+ name: 'fetch_sports_data',
6
+ oneLine: '获取当前/即将进行/最近的体育数据 (比分/积分榜/比赛统计).',
7
+ description: '对于最近和即将进行的比赛的数据、比分、统计, 优先使用此工具而非 Web 搜索. 工作流: 1) 获取比分 2) 根据比赛 ID 获取统计 3) 然后才回复用户.',
8
+ whenToUse: [
9
+ '用户对某场比赛的比分感兴趣',
10
+ '广泛查询 (最近的 NBA 结果等)',
11
+ '需要详细比赛统计',
12
+ ],
13
+ whenNotToUse: [
14
+ '历史比赛 (很久以前)',
15
+ '非体育数据',
16
+ ],
17
+ parameters: [
18
+ { name: 'data_type', type: 'enum', required: true, description: 'scores | standings | game_stats', enumValues: ['scores', 'standings', 'game_stats'] },
19
+ { name: 'game_id', type: 'string', required: false, description: 'SportRadar 比赛 ID (data_type=game_stats 时必需)' },
20
+ { name: 'league', type: 'enum', required: true, description: 'nfl/nba/nhl/mlb/wnba/ncaafb/ncaamb/ncaawb/epl/la_liga/serie_a/bundesliga/ligue_1/mls/champions_league/tennis/golf/nascar/cricket/mma', enumValues: ['nfl','nba','nhl','mlb','wnba','ncaafb','ncaamb','ncaawb','epl','la_liga','serie_a','bundesliga','ligue_1','mls','champions_league','tennis','golf','nascar','cricket','mma'] },
21
+ { name: 'team', type: 'string', required: false, description: '可选的球队名称' },
22
+ ],
23
+ callExample: `[TOOL:fetch_sports_data]
24
+ [P:data_type]scores
25
+ [P:league]nba
26
+ [ENDTOOL]`,
27
+ layerId: 'tool.manifest',
28
+ };
@@ -0,0 +1,26 @@
1
+ import type { ToolManifest } from './types.js';
2
+
3
+ export const image_search: ToolManifest = {
4
+ id: 'image_search',
5
+ name: 'image_search',
6
+ oneLine: '在 Web 上查找图像并连同尺寸返回 (3-4 张/次).',
7
+ description: '对于视觉内容可增强用户理解的任何查询, 默认使用图像搜索. 当交付物主要是文本时跳过.',
8
+ whenToUse: [
9
+ '地点、动物、食物、人物、产品、风格、图表、历史照片、练习',
10
+ '关于视觉事物的简单事实 ("埃菲尔铁塔是哪一年造的?" → 展示它)',
11
+ '多项目内容 (指南、列表、比较、时间线、步骤) — 交错图像',
12
+ ],
13
+ whenNotToUse: [
14
+ '文本输出 (撰写邮件、代码、文章)',
15
+ '数字/数据、编码查询、技术支持、分步说明',
16
+ '关于非视觉主题的分析',
17
+ ],
18
+ parameters: [
19
+ { name: 'max_results', type: 'integer', required: false, description: '返回图片数 (默认 3, 最少 3, 最多 5)', minimum: 3, maximum: 5, default: 3 },
20
+ { name: 'query', type: 'string', required: true, description: '搜索查询 (3-6 词具体)' },
21
+ ],
22
+ callExample: `[TOOL:image_search]
23
+ [P:query]Eiffel Tower Paris
24
+ [ENDTOOL]`,
25
+ layerId: 'tool.image_search',
26
+ };
@@ -0,0 +1,88 @@
1
+ /**
2
+ * tool-manifest/index.ts — 工具清单总入口
3
+ *
4
+ * 16 个工具的定义在 ./<tool>.ts 各文件 (1 个文件 1 个工具, 易于维护).
5
+ * 这个文件提供:
6
+ * - listTools(): 返回所有 16 个
7
+ * - getToolManifest(id): 按 id 拿
8
+ * - formatForPrompt(): 把清单缩到 1-2KB 进 system prompt (远小于 24KB)
9
+ *
10
+ * 设计: prompt 只看到 oneLine + whenToUse + whenNotToUse + callExample (1-3 行).
11
+ * 详细 schema (parameters 嵌套) 在代码侧 — bolloon 真实调用工具时由 PiAI 客户端读.
12
+ */
13
+ import type { ToolManifest } from './types.js';
14
+
15
+ import { ask_user_input_v0 } from './ask_user_input.js';
16
+ import { bash_tool } from './bash.js';
17
+ import { create_file } from './create_file.js';
18
+ import { fetch_sports_data } from './fetch_sports_data.js';
19
+ import { image_search } from './image_search.js';
20
+ import { message_compose_v1 } from './message_compose.js';
21
+ import { places_search, places_map_display_v0 } from './places.js';
22
+ import { present_files } from './present_files.js';
23
+ import { recipe_display_v0 } from './recipe.js';
24
+ import { recommend_bolloon_apps } from './recommend_apps.js';
25
+ import { search_mcp_registry, suggest_connectors } from './mcp.js';
26
+ import { str_replace } from './str_replace.js';
27
+ import { view } from './view.js';
28
+ import { web_search, web_fetch } from './web.js';
29
+ import { weather_fetch } from './weather.js';
30
+
31
+ const ALL: ToolManifest[] = [
32
+ ask_user_input_v0,
33
+ bash_tool,
34
+ create_file,
35
+ fetch_sports_data,
36
+ image_search,
37
+ message_compose_v1,
38
+ places_search,
39
+ places_map_display_v0,
40
+ present_files,
41
+ recipe_display_v0,
42
+ recommend_bolloon_apps,
43
+ search_mcp_registry,
44
+ suggest_connectors,
45
+ str_replace,
46
+ view,
47
+ web_search,
48
+ web_fetch,
49
+ weather_fetch,
50
+ ];
51
+
52
+ export function listTools(): ToolManifest[] {
53
+ return ALL;
54
+ }
55
+
56
+ export function getToolManifest(id: string): ToolManifest | undefined {
57
+ return ALL.find((t) => t.id === id);
58
+ }
59
+
60
+ export function getToolsByLayer(layerId: string): ToolManifest[] {
61
+ return ALL.filter((t) => t.layerId === layerId);
62
+ }
63
+
64
+ /**
65
+ * 缩到 1-3KB 装进 system prompt
66
+ * 包含: 名字 + 一句话 + whenToUse(2) + whenNotToUse(1) + 1 行示例
67
+ *
68
+ * 不包含: 完整 parameters schema (那是 PiAI 客户端在调用时读)
69
+ */
70
+ export function formatForPrompt(tools?: ToolManifest[]): string {
71
+ const list = tools ?? ALL;
72
+ const lines: string[] = [
73
+ `## 工具清单 (${list.length} 个, 详细 schema 在 src/llm/tool-manifest/, 调用时由 PiAI 客户端解析)`,
74
+ '',
75
+ ];
76
+ for (const t of list) {
77
+ lines.push(`### ${t.id}`);
78
+ lines.push(`- **用途**: ${t.oneLine}`);
79
+ lines.push(`- **何时用**: ${t.whenToUse[0] ?? ''}`);
80
+ if (t.whenToUse.length > 1) {
81
+ lines.push(` - 也: ${t.whenToUse.slice(1, 3).join(' / ')}`);
82
+ }
83
+ lines.push(`- **何时不用**: ${t.whenNotToUse[0] ?? ''}`);
84
+ lines.push(`- **示例**: ${t.callExample.replace(/\n/g, ' ')}`);
85
+ lines.push('');
86
+ }
87
+ return lines.join('\n');
88
+ }
@@ -0,0 +1,46 @@
1
+ import type { ToolManifest } from './types.js';
2
+
3
+ export const search_mcp_registry: ToolManifest = {
4
+ id: 'search_mcp_registry',
5
+ name: 'search_mcp_registry',
6
+ oneLine: '在 MCP 注册表中搜索可用连接器.',
7
+ description: '当连接新的 MCP 可能有助于解决用户查询时, 无论他们是否指名了特定产品, 请调用此工具. 如果结果相关, 调用 suggest_connectors 呈现选项. 如果没有任何匹配任务的内容, 不要调用 suggest_connectors.',
8
+ whenToUse: [
9
+ '用户隐含读取个人数据 (邮件、日历、任务、文件、工单)',
10
+ '需要连接外部服务 (Asana/Jira/Slack/Calendar)',
11
+ '用户指名了特定连接器 (即使未连接)',
12
+ ],
13
+ whenNotToUse: [
14
+ '知识问答/购物建议/一般性建议',
15
+ '用户已指名了已连接的特定服务 (直接用)',
16
+ ],
17
+ parameters: [
18
+ { name: 'keywords', type: 'array', required: true, description: '搜索关键词', minItems: 1, items: { name: 'kw', type: 'string', required: true, description: '关键词' } }
19
+ ],
20
+ callExample: `[TOOL:search_mcp_registry]
21
+ [P:keywords]${'${'}JSON.stringify(["calendar","schedule"])[ENDTOOL]`,
22
+ layerId: 'tool.mcp_apps',
23
+ };
24
+
25
+ export const suggest_connectors: ToolManifest = {
26
+ id: 'suggest_connectors',
27
+ name: 'suggest_connectors',
28
+ oneLine: '向用户展示连接器选项, 让用户选择.',
29
+ description: '每个选项都会渲染一个连接或使用按钮, 以及一个"以上都不是"选项. 调用后用一句简短的引导语结束你的回合.',
30
+ whenToUse: [
31
+ 'search_mcp_registry 找到了相关选项',
32
+ '用户未明确指名连接器',
33
+ '工具调用因身份验证/凭据错误失败 (重认证)',
34
+ ],
35
+ whenNotToUse: [
36
+ 'search_mcp_registry 未返回相关内容',
37
+ '用户已指名了特定的已连接服务 (直接用)',
38
+ '没有 search_mcp_registry 上下文',
39
+ ],
40
+ parameters: [
41
+ { name: 'uuids', type: 'array', required: true, description: '从 search_mcp_registry 结果的 directoryUuid 复制', minItems: 1, items: { name: 'u', type: 'string', required: true, description: 'uuid' } }
42
+ ],
43
+ callExample: `[TOOL:suggest_connectors]
44
+ [P:uuids]${'${'}JSON.stringify(["uuid-from-search-result"])[ENDTOOL]`,
45
+ layerId: 'tool.mcp_apps',
46
+ };