@bolloon/bolloon-agent 0.1.35 → 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.
- package/.playwright-mcp/page-2026-06-16T07-51-45-706Z.yml +130 -0
- package/.playwright-mcp/page-2026-06-16T07-56-44-116Z.yml +131 -0
- package/Bolloon.md +45 -4
- package/Dive-into/CITATION.cff +17 -0
- package/Dive-into/LICENSE +25 -0
- package/Dive-into/README.md +598 -0
- package/Dive-into/README_zh.md +599 -0
- package/Dive-into/assets/context.png +0 -0
- package/Dive-into/assets/extensibility.png +0 -0
- package/Dive-into/assets/iteration.png +0 -0
- package/Dive-into/assets/layered_architecture.png +0 -0
- package/Dive-into/assets/main_structure.png +0 -0
- package/Dive-into/assets/permission.png +0 -0
- package/Dive-into/assets/session_compact.png +0 -0
- package/Dive-into/assets/subagent.png +0 -0
- package/Dive-into/paper/Dive_into_Claude_Code.pdf +0 -0
- package/README.md +1 -1
- package/dist/agents/p2p-chat-tools.js +6 -6
- package/dist/agents/permission-mode.js +115 -0
- package/dist/agents/pi-sdk.js +435 -25
- package/dist/agents/pre-tool-validator.js +194 -0
- package/dist/agents/workflow-pivot-loop.js +113 -12
- package/dist/bollharness/src/scripts/checks/check_doc_freshness.js +1 -1
- package/dist/bollharness/src/scripts/checks/check_doc_links.js +1 -1
- package/dist/bollharness/src/scripts/context_router.js +1 -1
- package/dist/bollharness/src/scripts/deploy-guard.js +1 -1
- package/dist/bollharness/src/scripts/guard_router.js +1 -1
- package/dist/bollharness/src/scripts/hooks/_hook_output.js +1 -1
- package/dist/bollharness/src/scripts/hooks/risk-tracker.js +1 -1
- package/dist/bollharness-integration/context-router.js +2 -2
- package/dist/bollharness-integration/guard-checker.js +1 -1
- package/dist/bootstrap/bootstrap.js +3 -0
- package/dist/bootstrap/context-collector.js +17 -1
- package/dist/bootstrap/context-hierarchy.js +218 -0
- package/dist/bootstrap/lifecycle-hooks.js +86 -24
- package/dist/bootstrap/project-context.js +10 -3
- package/dist/context-compaction/auto-compact.js +144 -0
- package/dist/context-compaction/budget-gate.js +28 -0
- package/dist/context-compaction/budget-reduce.js +35 -0
- package/dist/context-compaction/context-collapse.js +66 -0
- package/dist/context-compaction/index.js +21 -0
- package/dist/context-compaction/microcompact.js +51 -0
- package/dist/context-compaction/pipeline.js +123 -0
- package/dist/context-compaction/snip.js +45 -0
- package/dist/context-compaction/token-estimator.js +35 -0
- package/dist/context-compaction/types.js +19 -0
- package/dist/heartbeat/HealthMonitor.js +3 -2
- package/dist/index.js +38 -2
- package/dist/llm/llm-judgment-client.js +32 -30
- package/dist/llm/pi-ai.js +103 -16
- package/dist/llm/system-prompt/health.js +129 -0
- package/dist/llm/system-prompt/registry.js +246 -0
- package/dist/llm/system-prompt/strip-hibsml.js +51 -0
- package/dist/llm/tool-manifest/ask_user_input.js +35 -0
- package/dist/llm/tool-manifest/bash.js +23 -0
- package/dist/llm/tool-manifest/create_file.js +24 -0
- package/dist/llm/tool-manifest/fetch_sports_data.js +26 -0
- package/dist/llm/tool-manifest/image_search.js +24 -0
- package/dist/llm/tool-manifest/index.js +69 -0
- package/dist/llm/tool-manifest/mcp.js +43 -0
- package/dist/llm/tool-manifest/message_compose.js +31 -0
- package/dist/llm/tool-manifest/places.js +83 -0
- package/dist/llm/tool-manifest/present_files.js +21 -0
- package/dist/llm/tool-manifest/recipe.js +40 -0
- package/dist/llm/tool-manifest/recommend_apps.js +23 -0
- package/dist/llm/tool-manifest/str_replace.js +27 -0
- package/dist/llm/tool-manifest/types.js +7 -0
- package/dist/llm/tool-manifest/view.js +24 -0
- package/dist/llm/tool-manifest/weather.js +27 -0
- package/dist/llm/tool-manifest/web.js +51 -0
- package/dist/network/p2p-direct.js +23 -0
- package/dist/network/source-intent-broadcaster.js +203 -0
- package/dist/network/source-intent.js +100 -0
- package/dist/pi-ecosystem-judgment/adaptive-scan.js +48 -0
- package/dist/pi-ecosystem-judgment/injection-gate.js +54 -12
- package/dist/pi-ecosystem-judgment/value-injection.js +8 -2
- package/dist/security/context-router-tool.js +1 -1
- package/dist/security/input-scanner.js +223 -0
- package/dist/web/client.js +3677 -4161
- package/dist/web/components/p2p/P2PModal.js +188 -0
- package/dist/web/components/p2p/index.js +264 -226
- package/dist/web/components/p2p/p2p-modal.js +657 -0
- package/dist/web/components/p2p/p2p-tools.js +248 -0
- package/dist/web/index.html +58 -67
- package/dist/web/server.js +299 -117
- package/dist/web/style.css +531 -249
- package/dist/web/ui/message-renderer.js +463 -0
- package/dist/web/ui/step-timeline.js +375 -0
- package/lefthook.yml +4 -0
- package/package.json +1 -1
- package/scripts/auto-evolve-loop.ts +105 -0
- package/scripts/build-web.ts +35 -1
- package/scripts/validate-system-prompt.ts +142 -0
- package/src/agents/p2p-chat-tools.ts +6 -6
- package/src/agents/permission-mode.ts +127 -0
- package/src/agents/pi-sdk.ts +461 -28
- package/src/agents/pre-tool-validator.ts +213 -0
- package/src/agents/workflow-pivot-loop.ts +110 -19
- package/src/bollharness/CLAUDE.md +1 -1
- package/src/bollharness/README.md +2 -2
- package/src/bollharness/README.zh-CN.md +2 -2
- package/src/bollharness/reference/boll-reference/scripts/hooks/find-boll-root.sh +2 -2
- package/src/bollharness/scripts/context-fragments/truth-source-hierarchy.md +2 -2
- package/src/bollharness/scripts/context-fragments/version-sources.md +1 -1
- package/src/bollharness/scripts/hooks/find-project-root.sh +4 -4
- package/src/bollharness/src/scripts/checks/check_doc_freshness.ts +1 -1
- package/src/bollharness/src/scripts/checks/check_doc_links.ts +1 -1
- package/src/bollharness/src/scripts/context_router.ts +1 -1
- package/src/bollharness/src/scripts/deploy-guard.ts +1 -1
- package/src/bollharness/src/scripts/guard_router.ts +1 -1
- package/src/bollharness/src/scripts/hooks/_hook_output.ts +1 -1
- package/src/bollharness/src/scripts/hooks/risk-tracker.ts +1 -1
- package/src/bollharness/templates/scaffold/.gitignore.append +1 -1
- package/src/bollharness/templates/scaffold/CLAUDE.md +2 -2
- package/src/bollharness-integration/context-router.ts +2 -2
- package/src/bollharness-integration/guard-checker.ts +1 -1
- package/src/bootstrap/bootstrap.ts +3 -0
- package/src/bootstrap/context-collector.ts +30 -1
- package/src/bootstrap/context-hierarchy.ts +283 -0
- package/src/bootstrap/lifecycle-hooks.ts +139 -26
- package/src/bootstrap/project-context.ts +11 -3
- package/src/context-compaction/auto-compact.ts +153 -0
- package/src/context-compaction/budget-gate.ts +32 -0
- package/src/context-compaction/budget-reduce.ts +37 -0
- package/src/context-compaction/context-collapse.ts +72 -0
- package/src/context-compaction/index.ts +24 -0
- package/src/context-compaction/microcompact.ts +54 -0
- package/src/context-compaction/pipeline.ts +133 -0
- package/src/context-compaction/snip.ts +51 -0
- package/src/context-compaction/token-estimator.ts +36 -0
- package/src/context-compaction/types.ts +99 -0
- package/src/heartbeat/HealthMonitor.ts +3 -2
- package/src/index.ts +36 -2
- package/src/llm/llm-judgment-client.ts +36 -35
- package/src/llm/pi-ai.ts +111 -16
- package/src/llm/system-prompt/health.ts +159 -0
- package/src/llm/system-prompt/layers/channel/local.md +14 -0
- package/src/llm/system-prompt/layers/channel/p2p-agent.md +18 -0
- package/src/llm/system-prompt/layers/channel/p2p-visitor.md +19 -0
- package/src/llm/system-prompt/layers/core/artifacts_storage.md +89 -0
- package/src/llm/system-prompt/layers/core/evenhandedness.md +21 -0
- package/src/llm/system-prompt/layers/core/hibs_reminders.md +15 -0
- package/src/llm/system-prompt/layers/core/identity.md +37 -0
- package/src/llm/system-prompt/layers/core/knowledge.md +17 -0
- package/src/llm/system-prompt/layers/core/memory_system.md +12 -0
- package/src/llm/system-prompt/layers/core/network_filesystem.md +28 -0
- package/src/llm/system-prompt/layers/core/refusal.md +37 -0
- package/src/llm/system-prompt/layers/core/tone.md +31 -0
- package/src/llm/system-prompt/layers/core/tools.thin.md +13 -0
- package/src/llm/system-prompt/layers/core/wellbeing.md +41 -0
- package/src/llm/system-prompt/layers/role/architect.md +20 -0
- package/src/llm/system-prompt/layers/role/expert.md +19 -0
- package/src/llm/system-prompt/layers/role/implementer.md +15 -0
- package/src/llm/system-prompt/layers/role/security.md +15 -0
- package/src/llm/system-prompt/layers/tool/artifacts.md +72 -0
- package/src/llm/system-prompt/layers/tool/bash.md +25 -0
- package/src/llm/system-prompt/layers/tool/hibs_api.md +171 -0
- package/src/llm/system-prompt/layers/tool/image_search.md +70 -0
- package/src/llm/system-prompt/layers/tool/manifest.md +89 -0
- package/src/llm/system-prompt/layers/tool/mcp_apps.md +53 -0
- package/src/llm/system-prompt/layers/tool/web_search.md +83 -0
- package/src/llm/system-prompt/registry.ts +325 -0
- package/src/llm/system-prompt/strip-hibsml.ts +52 -0
- package/src/llm/tool-manifest/ask_user_input.ts +37 -0
- package/src/llm/tool-manifest/bash.ts +25 -0
- package/src/llm/tool-manifest/create_file.ts +26 -0
- package/src/llm/tool-manifest/fetch_sports_data.ts +28 -0
- package/src/llm/tool-manifest/image_search.ts +26 -0
- package/src/llm/tool-manifest/index.ts +88 -0
- package/src/llm/tool-manifest/mcp.ts +46 -0
- package/src/llm/tool-manifest/message_compose.ts +33 -0
- package/src/llm/tool-manifest/places.ts +86 -0
- package/src/llm/tool-manifest/present_files.ts +23 -0
- package/src/llm/tool-manifest/recipe.ts +42 -0
- package/src/llm/tool-manifest/recommend_apps.ts +25 -0
- package/src/llm/tool-manifest/str_replace.ts +29 -0
- package/src/llm/tool-manifest/types.ts +52 -0
- package/src/llm/tool-manifest/view.ts +26 -0
- package/src/llm/tool-manifest/weather.ts +29 -0
- package/src/llm/tool-manifest/web.ts +54 -0
- package/src/network/p2p-direct.ts +22 -0
- package/src/network/source-intent-broadcaster.ts +242 -0
- package/src/network/source-intent.ts +167 -0
- package/src/security/context-router-tool.ts +1 -1
- package/src/security/input-scanner.ts +287 -0
- package/src/web/client.js +271 -2817
- package/src/web/client.ts +4326 -0
- package/src/web/index.html +58 -67
- package/src/web/server.ts +301 -141
- package/src/web/style.css +531 -249
- package/src/web/ui/message-renderer.ts +540 -0
- package/src/web/ui/step-timeline.ts +394 -0
- package/test-results/.last-run.json +6 -0
- 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,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Compaction — 类型定义
|
|
3
|
+
*
|
|
4
|
+
* 严格对齐 Claude Code 论文 5 层压缩流水线:
|
|
5
|
+
* 1. Budget Reduction (单条消息大小限制)
|
|
6
|
+
* 2. Snip (裁掉老历史, feature flag 关闭)
|
|
7
|
+
* 3. Microcompact (cache-aware 细粒度压缩, 老 tool_result 折叠)
|
|
8
|
+
* 4. Context Collapse (读时虚拟投影, 非破坏)
|
|
9
|
+
* 5. Auto-Compact (LLM 摘要, 兜底)
|
|
10
|
+
*
|
|
11
|
+
* 关键不变量:
|
|
12
|
+
* - 第 1-3 层不破坏 messageHistory 内存结构, 只改 buildContext 输出
|
|
13
|
+
* - 第 4 层读时投影, 绝不改 messageHistory
|
|
14
|
+
* - 第 5 层破坏 messageHistory, 把 N 对折叠成 1 条 summary
|
|
15
|
+
*
|
|
16
|
+
* 类型设计: Message 在本模块内独立定义, 靠 structural typing 与 pi-sdk.ts 的 Message 兼容.
|
|
17
|
+
* 不 import pi-sdk 避免循环依赖.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export type Role = 'user' | 'assistant' | 'tool' | 'system';
|
|
21
|
+
|
|
22
|
+
export interface Message {
|
|
23
|
+
role: Role;
|
|
24
|
+
content: string;
|
|
25
|
+
toolCall?: { name: string; args: Record<string, unknown> };
|
|
26
|
+
toolResult?: unknown;
|
|
27
|
+
toolName?: string;
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ============================================================
|
|
32
|
+
// 5 层 stage 共享的输入输出
|
|
33
|
+
// ============================================================
|
|
34
|
+
|
|
35
|
+
export interface StageOptions {
|
|
36
|
+
/** Token 预算 (默认 8000) */
|
|
37
|
+
maxTokens?: number;
|
|
38
|
+
/** 跳过本层 (per-stage 开关) */
|
|
39
|
+
skip?: boolean;
|
|
40
|
+
/** Snip 专用: 保留最近多少对 (默认 20) */
|
|
41
|
+
snipKeepPairs?: number;
|
|
42
|
+
/** Budget Reduction 专用: 单条消息上限 (默认 4000 字符) */
|
|
43
|
+
budgetReduceMaxChars?: number;
|
|
44
|
+
/** Microcompact 专用: 保留最近多少条完整 tool_result (默认 3) */
|
|
45
|
+
microcompactKeepRecent?: number;
|
|
46
|
+
/** Context Collapse 专用: 折叠前多少对 (默认 5) */
|
|
47
|
+
contextCollapseCollapsePairs?: number;
|
|
48
|
+
/** Auto-Compact 专用: 摘要前多少对 (默认 5) */
|
|
49
|
+
autoCompactCollapsePairs?: number;
|
|
50
|
+
/** Auto-Compact 专用: 注入 LLM 调用 (用于生成摘要) */
|
|
51
|
+
llmChat?: (systemPrompt: string, userPrompt: string, signal?: AbortSignal) => Promise<string>;
|
|
52
|
+
/** Auto-Compact 专用: 缓存 key 后缀 (通常 = channelId) */
|
|
53
|
+
cacheScope?: string;
|
|
54
|
+
/** Context Collapse 专用: 注入 LLM (虚拟投影需要 summary 文本) */
|
|
55
|
+
collapseLlmChat?: (systemPrompt: string, userPrompt: string) => Promise<string>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface CompactionContext {
|
|
59
|
+
/** 输入: 原始 history */
|
|
60
|
+
inputHistory: Message[];
|
|
61
|
+
/** 当前已压缩的 history (经过前面 stage) */
|
|
62
|
+
history: Message[];
|
|
63
|
+
/** 估算的 token 数 */
|
|
64
|
+
estimatedTokens: number;
|
|
65
|
+
/** 选项 */
|
|
66
|
+
opts: StageOptions;
|
|
67
|
+
/** Pipeline 元数据, 记录每层做了什么 */
|
|
68
|
+
stages: Array<{ stage: string; applied: boolean; before: number; after: number; detail?: string }>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface StageResult {
|
|
72
|
+
/** 压缩后的 history */
|
|
73
|
+
history: Message[];
|
|
74
|
+
/** 该 stage 是否做了改动 */
|
|
75
|
+
applied: boolean;
|
|
76
|
+
/** 详情 (供 audit / debug) */
|
|
77
|
+
detail?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ============================================================
|
|
81
|
+
// Budget Gate
|
|
82
|
+
// ============================================================
|
|
83
|
+
|
|
84
|
+
export interface BudgetReport {
|
|
85
|
+
fit: boolean;
|
|
86
|
+
estimatedTokens: number;
|
|
87
|
+
/** 占用比例 0-1 */
|
|
88
|
+
ratio: number;
|
|
89
|
+
/** 触发了哪一层 (>=0.8 = next layer, >1 = all layers) */
|
|
90
|
+
triggerNextLayer: boolean;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface PipelineResult {
|
|
94
|
+
history: Message[];
|
|
95
|
+
estimatedTokens: number;
|
|
96
|
+
stages: CompactionContext['stages'];
|
|
97
|
+
/** 是否至少跑了一层 */
|
|
98
|
+
compacted: boolean;
|
|
99
|
+
}
|
|
@@ -257,9 +257,10 @@ export class HealthMonitor implements HealthCheckProvider {
|
|
|
257
257
|
private async pingLLM(minimax: any): Promise<number> {
|
|
258
258
|
const start = Date.now();
|
|
259
259
|
try {
|
|
260
|
-
//
|
|
260
|
+
// 2026-06-15: signal 位置正确传 undefined (chat signature 是 (message, context?, signal?))
|
|
261
|
+
// 之前传 { maxTokens: 1 } 被当 signal, Node 22+ undici 强类型 AbortSignal 校验 throw
|
|
261
262
|
if (minimax.chat) {
|
|
262
|
-
await minimax.chat('ping', 'test',
|
|
263
|
+
await minimax.chat('ping', 'test', undefined);
|
|
263
264
|
} else if (minimax.ping) {
|
|
264
265
|
await minimax.ping();
|
|
265
266
|
}
|
package/src/index.ts
CHANGED
|
@@ -1132,6 +1132,32 @@ async function runNonInteractive(
|
|
|
1132
1132
|
return;
|
|
1133
1133
|
}
|
|
1134
1134
|
|
|
1135
|
+
if (tool === 'prompt' && prompt) {
|
|
1136
|
+
// 2026-06-15: --prompt "text" 走直接调 a.prompt(prompt) 路径, 避开 runToolCommand
|
|
1137
|
+
// (case 'prompt' 内的 [text] = args 从 toolArgs 取值是空数组, 永远报"缺少 prompt 文本"是 CLI bug)
|
|
1138
|
+
const startTime = Date.now();
|
|
1139
|
+
const a = await getAgent();
|
|
1140
|
+
try {
|
|
1141
|
+
const response = await a.prompt(prompt);
|
|
1142
|
+
const elapsed = Date.now() - startTime;
|
|
1143
|
+
if (json) {
|
|
1144
|
+
console.log(JSON.stringify({ success: true, response, elapsedMs: elapsed }, null, 2));
|
|
1145
|
+
} else {
|
|
1146
|
+
console.log(response);
|
|
1147
|
+
console.log(`\n耗时: ${elapsed}ms`);
|
|
1148
|
+
}
|
|
1149
|
+
} catch (e: any) {
|
|
1150
|
+
const error = e?.message || String(e);
|
|
1151
|
+
if (json) {
|
|
1152
|
+
console.log(JSON.stringify({ success: false, error }, null, 2));
|
|
1153
|
+
} else {
|
|
1154
|
+
console.log(`\n错误: ${error}`);
|
|
1155
|
+
}
|
|
1156
|
+
process.exit(1);
|
|
1157
|
+
}
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1135
1161
|
if (tool) {
|
|
1136
1162
|
await runToolCommand(tool, toolArgs, !!json, comm);
|
|
1137
1163
|
} else if (prompt) {
|
|
@@ -1565,7 +1591,9 @@ async function main() {
|
|
|
1565
1591
|
s.section('系统初始化');
|
|
1566
1592
|
|
|
1567
1593
|
const hasOpenAI = !!process.env.OPENAI_API_KEY;
|
|
1568
|
-
|
|
1594
|
+
// 2026-06-15: 修复 — 之前 anthropic 401 是因为 shell env 残留的旧 ANTHROPIC_API_KEY 抢了 provider 选择
|
|
1595
|
+
// 用 BOLLOON_LLM_PROVIDER env 显式覆盖, 否则还是按 env hasXxx 顺序自动选
|
|
1596
|
+
const hasAnthropic = !!process.env.ANTHROPIC_API_KEY && !process.env.BOLLOON_LLM_PROVIDER;
|
|
1569
1597
|
const hasMinimax = !!process.env.MINIMAX_API_KEY;
|
|
1570
1598
|
const hasDeepSeek = !!process.env.DEEPSEEK_API_KEY;
|
|
1571
1599
|
const hasKimi = !!(process.env.KIMI_API_KEY || process.env.MOONSHOT_API_KEY);
|
|
@@ -1649,10 +1677,16 @@ async function main() {
|
|
|
1649
1677
|
|
|
1650
1678
|
if (mode === 'web') {
|
|
1651
1679
|
const port = parseInt(process.env.PORT || '54188');
|
|
1680
|
+
// 2026-06-16: BOLLOON_DEV_MODE=1 或 selfImprove=true 启动项 → 开发者模式, 启用自迭代 (健康监控+自改总线)
|
|
1681
|
+
// 默认用户模式: 不自迭代, 自改卡片不自动出现, 仍可 POST /api/self-improve/trigger 手动触发
|
|
1682
|
+
const selfImprove = process.env.BOLLOON_DEV_MODE === '1' || process.env.BOLLOON_DEV_MODE === 'true';
|
|
1683
|
+
if (selfImprove) {
|
|
1684
|
+
console.log('[startup] BOLLOON_DEV_MODE=1, 开发者模式: 自迭代已启用');
|
|
1685
|
+
}
|
|
1652
1686
|
const { createWebServer, openBrowser } = await import('./web/server.js');
|
|
1653
1687
|
|
|
1654
1688
|
s.info(`启动 Web 服务端口 ${port}...`);
|
|
1655
|
-
const { port: actualPort } = await createWebServer(port);
|
|
1689
|
+
const { port: actualPort } = await createWebServer(port, { selfImprove });
|
|
1656
1690
|
|
|
1657
1691
|
s.success(`浏览器已打开 → http://localhost:${actualPort}`);
|
|
1658
1692
|
openBrowser(`http://localhost:${actualPort}`);
|
|
@@ -7,10 +7,32 @@
|
|
|
7
7
|
|
|
8
8
|
import { llmConfigStore, type ModelProvider } from './config-store.js';
|
|
9
9
|
import { PiAIModel, type ChatMessage } from './pi-ai.js';
|
|
10
|
+
import * as fs from 'fs/promises';
|
|
11
|
+
import * as path from 'path';
|
|
12
|
+
import { fileURLToPath } from 'url';
|
|
13
|
+
|
|
14
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const LAYERS_DIR = path.join(__dirname, 'system-prompt', 'layers');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 从 layer 读 .md 文件作为系统 prompt
|
|
19
|
+
* 失败时降级到兜底字符串 (不挂)
|
|
20
|
+
*/
|
|
21
|
+
async function loadRoleLayer(layerId: string, fallback: string): Promise<string> {
|
|
22
|
+
const [group, ...rest] = layerId.split('.');
|
|
23
|
+
const path2md = path.join(LAYERS_DIR, group, `${rest.join('.')}.md`);
|
|
24
|
+
try {
|
|
25
|
+
return await fs.readFile(path2md, 'utf-8');
|
|
26
|
+
} catch {
|
|
27
|
+
return fallback;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
10
30
|
|
|
11
31
|
export interface JudgmentPromptConfig {
|
|
12
|
-
// 系统 Prompt
|
|
32
|
+
// 系统 Prompt (静态字符串, 向后兼容)
|
|
13
33
|
systemPrompt: string;
|
|
34
|
+
// 系统 Prompt resolver (优先于 systemPrompt; 异步从 layer 读)
|
|
35
|
+
systemPromptResolver?: () => Promise<string>;
|
|
14
36
|
|
|
15
37
|
// 用户 Prompt 模板
|
|
16
38
|
userPromptTemplate: string;
|
|
@@ -29,16 +51,8 @@ export interface JudgmentPromptConfig {
|
|
|
29
51
|
export const JUDGMENT_PROMPTS: Record<string, JudgmentPromptConfig> = {
|
|
30
52
|
// 默认判断 Prompt
|
|
31
53
|
default: {
|
|
32
|
-
systemPrompt:
|
|
33
|
-
|
|
34
|
-
核心原则:
|
|
35
|
-
1. 第一性原理:追问问题的本质,而非表面现象
|
|
36
|
-
2. 多角度思考:考虑不同视角和影响因素
|
|
37
|
-
3. 上下文感知:理解对话历史和隐含意图
|
|
38
|
-
4. 动态评估:根据实际情况判断复杂性
|
|
39
|
-
|
|
40
|
-
你的判断会影响后续的智能体协作方式,请认真分析。`,
|
|
41
|
-
|
|
54
|
+
systemPrompt: '',
|
|
55
|
+
systemPromptResolver: () => loadRoleLayer('role.expert', '你是一个专业的 AI 任务分析专家. '),
|
|
42
56
|
userPromptTemplate: `【当前输入】
|
|
43
57
|
{user_input}
|
|
44
58
|
|
|
@@ -95,17 +109,8 @@ export const JUDGMENT_PROMPTS: Record<string, JudgmentPromptConfig> = {
|
|
|
95
109
|
|
|
96
110
|
// 架构相关问题
|
|
97
111
|
architecture: {
|
|
98
|
-
systemPrompt:
|
|
99
|
-
|
|
100
|
-
- 设计可扩展的系统架构
|
|
101
|
-
- 权衡技术方案
|
|
102
|
-
- 识别架构风险
|
|
103
|
-
|
|
104
|
-
你相信:
|
|
105
|
-
- 本质和实现必须分离
|
|
106
|
-
- 好的架构从简单规则中生长
|
|
107
|
-
- 复杂性应该被控制而非消除`,
|
|
108
|
-
|
|
112
|
+
systemPrompt: '',
|
|
113
|
+
systemPromptResolver: () => loadRoleLayer('role.architect', '你是一个经验丰富的系统架构师. '),
|
|
109
114
|
userPromptTemplate: `【架构设计问题】
|
|
110
115
|
{user_input}
|
|
111
116
|
|
|
@@ -133,12 +138,8 @@ export const JUDGMENT_PROMPTS: Record<string, JudgmentPromptConfig> = {
|
|
|
133
138
|
|
|
134
139
|
// 代码相关问题
|
|
135
140
|
code: {
|
|
136
|
-
systemPrompt:
|
|
137
|
-
|
|
138
|
-
- 编写清晰、可维护的代码
|
|
139
|
-
- 遵循最佳实践
|
|
140
|
-
- 处理边界情况`,
|
|
141
|
-
|
|
141
|
+
systemPrompt: '',
|
|
142
|
+
systemPromptResolver: () => loadRoleLayer('role.implementer', '你是一个高效的代码实现专家. '),
|
|
142
143
|
userPromptTemplate: `【代码问题】
|
|
143
144
|
{user_input}
|
|
144
145
|
|
|
@@ -162,12 +163,8 @@ export const JUDGMENT_PROMPTS: Record<string, JudgmentPromptConfig> = {
|
|
|
162
163
|
|
|
163
164
|
// 安全相关问题
|
|
164
165
|
security: {
|
|
165
|
-
systemPrompt:
|
|
166
|
-
|
|
167
|
-
- 评估威胁模型
|
|
168
|
-
- 设计安全方案
|
|
169
|
-
- 遵循安全最佳实践`,
|
|
170
|
-
|
|
166
|
+
systemPrompt: '',
|
|
167
|
+
systemPromptResolver: () => loadRoleLayer('role.security', '你是一个严格的安全专家. '),
|
|
171
168
|
userPromptTemplate: `【安全问题】
|
|
172
169
|
{user_input}
|
|
173
170
|
|
|
@@ -300,8 +297,12 @@ export class LLMJudgmentClient {
|
|
|
300
297
|
|
|
301
298
|
try {
|
|
302
299
|
// 调用 LLM
|
|
300
|
+
// 优先用 systemPromptResolver (从 layer 读), fallback 静态 systemPrompt
|
|
301
|
+
const systemPrompt = this.promptConfig.systemPromptResolver
|
|
302
|
+
? await this.promptConfig.systemPromptResolver()
|
|
303
|
+
: this.promptConfig.systemPrompt;
|
|
303
304
|
const messages: ChatMessage[] = [
|
|
304
|
-
{ role: 'system', content:
|
|
305
|
+
{ role: 'system', content: systemPrompt },
|
|
305
306
|
{ role: 'user', content: userPrompt }
|
|
306
307
|
];
|
|
307
308
|
|
package/src/llm/pi-ai.ts
CHANGED
|
@@ -29,6 +29,28 @@ export interface GenerateOptions {
|
|
|
29
29
|
temperature?: number;
|
|
30
30
|
maxTokens?: number;
|
|
31
31
|
signal?: AbortSignal;
|
|
32
|
+
/** 工具 id 列表 — 代码侧 (src/llm/tool-manifest/) 查 schema, prompt 里只嵌 oneLine + callExample */
|
|
33
|
+
tools?: string[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 外部 system 注入钩子.
|
|
38
|
+
* 调用方 (e.g. auto-evolve-loop) 用 setSystemPrependProvider() 注册一个返回字符串的函数,
|
|
39
|
+
* generateText 在拼 finalMessages 时, 把返回的字符串作为最前一个 system message.
|
|
40
|
+
*
|
|
41
|
+
* 用途: P2P 协作时把"行级 reserve 状态"实时塞给 LLM,
|
|
42
|
+
* 让 LLM 主动避开对方正在改的代码行.
|
|
43
|
+
*
|
|
44
|
+
* 返回 '' / null / undefined → 不注入.
|
|
45
|
+
*/
|
|
46
|
+
let _prependProvider: (() => string | null | undefined | Promise<string | null | undefined>) | null = null;
|
|
47
|
+
export function setSystemPrependProvider(
|
|
48
|
+
p: (() => string | null | undefined | Promise<string | null | undefined>) | null
|
|
49
|
+
): void {
|
|
50
|
+
_prependProvider = p;
|
|
51
|
+
}
|
|
52
|
+
export function getSystemPrependProvider(): typeof _prependProvider {
|
|
53
|
+
return _prependProvider;
|
|
32
54
|
}
|
|
33
55
|
|
|
34
56
|
export class PiAIModel {
|
|
@@ -41,7 +63,7 @@ export class PiAIModel {
|
|
|
41
63
|
}
|
|
42
64
|
|
|
43
65
|
async chat(message: string, context?: string, signal?: AbortSignal): Promise<ChatResult> {
|
|
44
|
-
const systemPrompt = this.
|
|
66
|
+
const systemPrompt = await this.buildSystemPromptAsync(context);
|
|
45
67
|
const messages: ChatMessage[] = [
|
|
46
68
|
{ role: 'system', content: systemPrompt },
|
|
47
69
|
{ role: 'user', content: message }
|
|
@@ -51,6 +73,7 @@ export class PiAIModel {
|
|
|
51
73
|
const response = await this.generateText({
|
|
52
74
|
messages,
|
|
53
75
|
temperature: 0.8,
|
|
76
|
+
maxTokens: 8192, // 2026-06-15: 提到 8192, 避免长回答 (含表格/代码块) 被截断
|
|
54
77
|
signal,
|
|
55
78
|
});
|
|
56
79
|
return { reply: response };
|
|
@@ -60,7 +83,14 @@ export class PiAIModel {
|
|
|
60
83
|
throw error; // 上层 try/catch 处理
|
|
61
84
|
}
|
|
62
85
|
console.error('PiAI chat error:', error);
|
|
63
|
-
|
|
86
|
+
// 2026-06-15: 真实 error 信息 + 明确告诉 LLM "这是 API 错, 不要 retry"
|
|
87
|
+
// 旧版: "抱歉,AI服务暂时不可用。" → LLM 看到 isTooShort=false(< 50 但 > 0),
|
|
88
|
+
// needsMoreWork 不会触发, 但 hasError 模式 (含 "error" / "失败") 会判定要继续修
|
|
89
|
+
// 新版: 让 LLM 立即停止循环, 直接展示给用户
|
|
90
|
+
const errMsg = (error?.message || '').slice(0, 300);
|
|
91
|
+
return {
|
|
92
|
+
reply: `[AI 服务调用失败] ${errMsg}\n\n这是一个**底层 API 错误**(401 / 鉴权失败 / 网络中断 / 配额耗尽等),不是你的任务有问题。**请直接把这个错误消息回复给用户,不要再循环尝试。**`,
|
|
93
|
+
};
|
|
64
94
|
}
|
|
65
95
|
}
|
|
66
96
|
|
|
@@ -106,7 +136,40 @@ export class PiAIModel {
|
|
|
106
136
|
}
|
|
107
137
|
|
|
108
138
|
private async generateText(options: GenerateOptions): Promise<string> {
|
|
109
|
-
const { messages, temperature = 0.7, maxTokens = 4096, signal } = options;
|
|
139
|
+
const { messages, temperature = 0.7, maxTokens = 4096, signal, tools } = options;
|
|
140
|
+
|
|
141
|
+
// 工具清单: 代码侧 schema → 进 system prompt (作为额外 system message)
|
|
142
|
+
let finalMessages = messages;
|
|
143
|
+
|
|
144
|
+
// 1) 外部 prepend (e.g. P2P 行级 reserve 状态), 拼到最前
|
|
145
|
+
if (_prependProvider) {
|
|
146
|
+
try {
|
|
147
|
+
const pre = await _prependProvider();
|
|
148
|
+
if (pre && typeof pre === 'string' && pre.trim()) {
|
|
149
|
+
finalMessages = [{ role: 'system', content: pre }, ...finalMessages];
|
|
150
|
+
}
|
|
151
|
+
} catch (err: any) {
|
|
152
|
+
console.warn('[pi-ai] systemPrepend 失败:', err?.message?.slice(0, 100));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (tools && tools.length > 0) {
|
|
157
|
+
try {
|
|
158
|
+
const { getToolManifest, formatForPrompt } = await import('./tool-manifest/index.js');
|
|
159
|
+
const manifests = tools
|
|
160
|
+
.map((id) => getToolManifest(id))
|
|
161
|
+
.filter((m): m is NonNullable<typeof m> => m !== undefined);
|
|
162
|
+
if (manifests.length > 0) {
|
|
163
|
+
const toolPrompt = formatForPrompt(manifests);
|
|
164
|
+
finalMessages = [
|
|
165
|
+
{ role: 'system', content: toolPrompt },
|
|
166
|
+
...messages,
|
|
167
|
+
];
|
|
168
|
+
}
|
|
169
|
+
} catch (err: any) {
|
|
170
|
+
console.warn('[pi-ai] tool-manifest 加载失败:', err.message?.slice(0, 100));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
110
173
|
|
|
111
174
|
switch (this.provider) {
|
|
112
175
|
case 'openai':
|
|
@@ -115,17 +178,17 @@ export class PiAIModel {
|
|
|
115
178
|
case 'kimi':
|
|
116
179
|
case 'glm':
|
|
117
180
|
case 'qwen':
|
|
118
|
-
return this.callOpenAI(
|
|
181
|
+
return this.callOpenAI(finalMessages, temperature, maxTokens, signal);
|
|
119
182
|
case 'anthropic':
|
|
120
|
-
return this.callAnthropic(
|
|
183
|
+
return this.callAnthropic(finalMessages, temperature, maxTokens, signal);
|
|
121
184
|
case 'ollama':
|
|
122
|
-
return this.callOllama(
|
|
185
|
+
return this.callOllama(finalMessages, temperature, signal);
|
|
123
186
|
case 'openrouter':
|
|
124
|
-
return this.callOpenRouter(
|
|
187
|
+
return this.callOpenRouter(finalMessages, temperature, maxTokens, signal);
|
|
125
188
|
case 'gemini':
|
|
126
|
-
return this.callGemini(
|
|
189
|
+
return this.callGemini(finalMessages, temperature, maxTokens, signal);
|
|
127
190
|
case 'local':
|
|
128
|
-
return this.callLocal(
|
|
191
|
+
return this.callLocal(finalMessages, temperature, signal);
|
|
129
192
|
default:
|
|
130
193
|
throw new Error(`Unsupported provider: ${this.provider}`);
|
|
131
194
|
}
|
|
@@ -198,19 +261,23 @@ export class PiAIModel {
|
|
|
198
261
|
throw new Error('OPENAI_API_KEY not set');
|
|
199
262
|
}
|
|
200
263
|
|
|
264
|
+
const requestBody = {
|
|
265
|
+
model: this.mapModel(),
|
|
266
|
+
messages,
|
|
267
|
+
temperature,
|
|
268
|
+
max_tokens: maxTokens
|
|
269
|
+
};
|
|
201
270
|
const response = await fetch(`${this.getBaseUrl()}/chat/completions`, {
|
|
202
271
|
method: 'POST',
|
|
203
272
|
headers: {
|
|
204
273
|
'Content-Type': 'application/json',
|
|
205
274
|
'Authorization': `Bearer ${apiKey}`
|
|
206
275
|
},
|
|
207
|
-
body: JSON.stringify(
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}),
|
|
213
|
-
signal,
|
|
276
|
+
body: JSON.stringify(requestBody),
|
|
277
|
+
// 2026-06-15: signal 字段防御 — Node 22+ undici 强类型 AbortSignal, 非 AbortSignal 实例
|
|
278
|
+
// (e.g. 误传的 { maxTokens: 1 } 对象) 会 throw "Expected signal to be AbortSignal".
|
|
279
|
+
// 若不是 AbortSignal, 退到无 signal 调用, fetch 自然支持 timeout 由外层控制.
|
|
280
|
+
signal: signal instanceof AbortSignal ? signal : undefined,
|
|
214
281
|
});
|
|
215
282
|
|
|
216
283
|
if (!response.ok) {
|
|
@@ -355,7 +422,35 @@ export class PiAIModel {
|
|
|
355
422
|
private async callLocal(messages: ChatMessage[], temperature: number, signal?: AbortSignal): Promise<string> {
|
|
356
423
|
return this.callOllama(messages, temperature, signal);
|
|
357
424
|
}
|
|
425
|
+
// 注: callLocal 直接代理 ollama. 工具清单由 generateText 在外层拼到 messages, 这里 messages 已是 finalMessages.
|
|
426
|
+
|
|
427
|
+
private async buildSystemPromptAsync(context?: string): Promise<string> {
|
|
428
|
+
// 走 layer registry: 装配所有相关 layer (身份/行为/工具/角色/渠道)
|
|
429
|
+
try {
|
|
430
|
+
const { assembleSystemPrompt, SYSTEM_PROMPT_VERSION } = await import(
|
|
431
|
+
'./system-prompt/registry.js' as any
|
|
432
|
+
).catch(() => import('./system-prompt/registry.js'));
|
|
433
|
+
// channel = 本机 (PI SDK 直接调就是 local)
|
|
434
|
+
// role = 由 prompt context 决定 (默认 expert)
|
|
435
|
+
const ctx = context ? { channel: 'local' as const, role: 'expert' as const } : { channel: 'local' as const, role: 'expert' as const };
|
|
436
|
+
const result = await assembleSystemPrompt(ctx);
|
|
437
|
+
return `${result.text}\n\n## User Working Directory\n${context || process.cwd()}\n\n## bolloon-runtime\n${SYSTEM_PROMPT_VERSION} · layers: ${result.layerIds.join(',')}`;
|
|
438
|
+
} catch (err: any) {
|
|
439
|
+
// 降级: 旧硬编码 (layer registry 不可用时不挂)
|
|
440
|
+
console.warn('[pi-ai] layer registry 不可用, 降级:', err.message?.slice(0, 100));
|
|
441
|
+
const envDetails = this.getEnvironmentDetails();
|
|
442
|
+
return `You are a friendly AI assistant in a P2P document collaboration network.
|
|
443
|
+
|
|
444
|
+
## User Working Directory
|
|
445
|
+
${context || process.cwd()}
|
|
446
|
+
|
|
447
|
+
## Environment
|
|
448
|
+
${envDetails}`;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
358
451
|
|
|
452
|
+
// 同步版: 旧调用点 (buildSystemPrompt 是同步)
|
|
453
|
+
// 保留但内部用 sync fallback; 后续可改成 async
|
|
359
454
|
private buildSystemPrompt(context?: string): string {
|
|
360
455
|
const envDetails = this.getEnvironmentDetails();
|
|
361
456
|
return `You are a friendly AI assistant in a P2P document collaboration network.
|
|
@@ -0,0 +1,159 @@
|
|
|
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
|
+
|
|
17
|
+
import type { PromptLayer, SectionMeta, AppliesTo } from './registry.js';
|
|
18
|
+
|
|
19
|
+
export type LayerHealth = 'ok' | 'stale' | 'overdue-review' | 'missing-frontmatter' | 'dynamic';
|
|
20
|
+
|
|
21
|
+
export interface LayerHealthEntry {
|
|
22
|
+
id: string;
|
|
23
|
+
health: LayerHealth;
|
|
24
|
+
ageDays: number;
|
|
25
|
+
ttlDays: number;
|
|
26
|
+
/** 距离过期还有多少天 (负数 = 已过期) */
|
|
27
|
+
remainingDays: number;
|
|
28
|
+
author: string | null;
|
|
29
|
+
lastReviewedAt: string | null;
|
|
30
|
+
notes?: string;
|
|
31
|
+
/** 该层是否在当前 context 下被装配 (assembleSystemPrompt 调用过) */
|
|
32
|
+
active: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface HealthReport {
|
|
36
|
+
scannedAt: string;
|
|
37
|
+
total: number;
|
|
38
|
+
okCount: number;
|
|
39
|
+
staleCount: number;
|
|
40
|
+
overdueCount: number;
|
|
41
|
+
missingCount: number;
|
|
42
|
+
dynamicCount: number;
|
|
43
|
+
entries: LayerHealthEntry[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
47
|
+
|
|
48
|
+
/** 兼容运行时: dynamic layer 没有 meta, 视作 'dynamic' (last_reviewed = now) */
|
|
49
|
+
function isDynamic(l: PromptLayer): boolean {
|
|
50
|
+
return l.source === 'function';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** 评估单层 health */
|
|
54
|
+
export function evaluateLayer(l: PromptLayer, now: Date = new Date()): LayerHealthEntry {
|
|
55
|
+
if (isDynamic(l)) {
|
|
56
|
+
return {
|
|
57
|
+
id: l.id,
|
|
58
|
+
health: 'dynamic',
|
|
59
|
+
ageDays: 0,
|
|
60
|
+
ttlDays: -1,
|
|
61
|
+
remainingDays: -1,
|
|
62
|
+
author: l.meta?.author ?? 'runtime',
|
|
63
|
+
lastReviewedAt: now.toISOString(),
|
|
64
|
+
active: false, // 由 caller (assembleSystemPrompt) 决定
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (!l.meta) {
|
|
68
|
+
// frontmatter 解析失败或缺失
|
|
69
|
+
return {
|
|
70
|
+
id: l.id,
|
|
71
|
+
health: 'missing-frontmatter',
|
|
72
|
+
ageDays: -1,
|
|
73
|
+
ttlDays: -1,
|
|
74
|
+
remainingDays: -1,
|
|
75
|
+
author: null,
|
|
76
|
+
lastReviewedAt: null,
|
|
77
|
+
active: false,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const reviewedAt = new Date(l.meta.lastReviewedAt).getTime();
|
|
81
|
+
if (isNaN(reviewedAt)) {
|
|
82
|
+
return {
|
|
83
|
+
id: l.id,
|
|
84
|
+
health: 'missing-frontmatter',
|
|
85
|
+
ageDays: -1,
|
|
86
|
+
ttlDays: l.meta.ttlDays,
|
|
87
|
+
remainingDays: -1,
|
|
88
|
+
author: l.meta.author,
|
|
89
|
+
lastReviewedAt: l.meta.lastReviewedAt,
|
|
90
|
+
active: false,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const ageMs = now.getTime() - reviewedAt;
|
|
94
|
+
const ageDays = Math.floor(ageMs / DAY_MS);
|
|
95
|
+
const remainingDays = l.meta.ttlDays - ageDays;
|
|
96
|
+
let health: LayerHealth;
|
|
97
|
+
if (remainingDays < 0) {
|
|
98
|
+
health = 'overdue-review';
|
|
99
|
+
} else if (remainingDays < l.meta.ttlDays * 0.2) {
|
|
100
|
+
health = 'stale'; // 剩余 < 20% ttl 触发告警
|
|
101
|
+
} else {
|
|
102
|
+
health = 'ok';
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
id: l.id,
|
|
106
|
+
health,
|
|
107
|
+
ageDays,
|
|
108
|
+
ttlDays: l.meta.ttlDays,
|
|
109
|
+
remainingDays,
|
|
110
|
+
author: l.meta.author,
|
|
111
|
+
lastReviewedAt: l.meta.lastReviewedAt,
|
|
112
|
+
notes: l.meta.notes,
|
|
113
|
+
active: false, // caller 决定
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** 评估整组 layer (输入: assembleSystemPrompt 返回的 layers[]) */
|
|
118
|
+
export function evaluateLayers(layers: PromptLayer[], now: Date = new Date()): HealthReport {
|
|
119
|
+
const entries = layers.map((l) => evaluateLayer(l, now));
|
|
120
|
+
return summarize(entries, now);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** 汇总: 给一批 entries 算 counts */
|
|
124
|
+
function summarize(entries: LayerHealthEntry[], now: Date): HealthReport {
|
|
125
|
+
const counts = { ok: 0, stale: 0, overdue: 0, missing: 0, dynamic: 0 };
|
|
126
|
+
for (const e of entries) {
|
|
127
|
+
if (e.health === 'ok') counts.ok++;
|
|
128
|
+
else if (e.health === 'stale') counts.stale++;
|
|
129
|
+
else if (e.health === 'overdue-review') counts.overdue++;
|
|
130
|
+
else if (e.health === 'missing-frontmatter') counts.missing++;
|
|
131
|
+
else if (e.health === 'dynamic') counts.dynamic++;
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
scannedAt: now.toISOString(),
|
|
135
|
+
total: entries.length,
|
|
136
|
+
okCount: counts.ok,
|
|
137
|
+
staleCount: counts.stale,
|
|
138
|
+
overdueCount: counts.overdue,
|
|
139
|
+
missingCount: counts.missing,
|
|
140
|
+
dynamicCount: counts.dynamic,
|
|
141
|
+
entries,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** 标记当前 context 下激活的 layer (在 assembleSystemPrompt 调用后用) */
|
|
146
|
+
export function markActive(report: HealthReport, activeIds: Set<string>): HealthReport {
|
|
147
|
+
return {
|
|
148
|
+
...report,
|
|
149
|
+
entries: report.entries.map((e) => ({ ...e, active: activeIds.has(e.id) })),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ============================================================
|
|
154
|
+
// 测试钩子
|
|
155
|
+
// ============================================================
|
|
156
|
+
|
|
157
|
+
export function _resetHealthForTest(): void {
|
|
158
|
+
// 无内部状态, 保留 API 一致
|
|
159
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
added_at: 2026-06-15
|
|
3
|
+
last_reviewed_at: 2026-06-15
|
|
4
|
+
ttl_days: 180
|
|
5
|
+
author: yuanjie
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<!-- channel.local@1.0.0 -->
|
|
9
|
+
# 本地渠道 (默认, bolloon 跑在用户本机)
|
|
10
|
+
|
|
11
|
+
你跑在用户的本地设备上, 可直接读写用户文件系统. 信任度高.
|
|
12
|
+
|
|
13
|
+
允许: 读写文件、执行命令、调用所有工具.
|
|
14
|
+
边界: 仍遵守 core.refusal / core.wellbeing / 拒绝处理.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
added_at: 2026-06-15
|
|
3
|
+
last_reviewed_at: 2026-06-15
|
|
4
|
+
ttl_days: 180
|
|
5
|
+
author: yuanjie
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<!-- channel.p2p-agent@1.0.0 -->
|
|
9
|
+
# P2P 远程智能体 (另一个 bolloon 经 P2P 路由)
|
|
10
|
+
|
|
11
|
+
你是远端 bolloon, 收到的是**另一个 bolloon 实例**的协作请求. 信任度高 (基于 P2P 节点身份验证, 不是基于人类).
|
|
12
|
+
|
|
13
|
+
允许: 读 / 写, 协作推理, 共享上下文.
|
|
14
|
+
|
|
15
|
+
边界:
|
|
16
|
+
- 身份验证优先: 先确认对方节点 ID + 公钥签名
|
|
17
|
+
- 共享上下文时避免把人类隐私数据 (judgment / persona) 外泄给未知节点
|
|
18
|
+
- 协作冲突时回退到 snapshot 重新协商, 不强推修改
|