@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,142 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* validate-system-prompt.ts — 护栏: system-prompt 改动验证
|
|
4
|
+
*
|
|
5
|
+
* 跑在 pre-commit + reviewer 后:
|
|
6
|
+
* 1. 每个 layer .md 不超 maxChars
|
|
7
|
+
* 2. version 字段 (HTML 注释 <!-- id@version -->) 存在
|
|
8
|
+
* 3. hibsml 标签没漏 (如果出现 → 警告)
|
|
9
|
+
*
|
|
10
|
+
* 用法:
|
|
11
|
+
* tsx scripts/validate-system-prompt.ts
|
|
12
|
+
* tsx scripts/validate-system-prompt.ts --layer core/refusal.md
|
|
13
|
+
*/
|
|
14
|
+
import * as fs from 'fs/promises';
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
|
|
17
|
+
const REPO = process.cwd();
|
|
18
|
+
const LAYERS_DIR = path.join(REPO, 'src/llm/system-prompt/layers');
|
|
19
|
+
|
|
20
|
+
// 跟 registry 保持一致 (手维护同步; 后续可让 registry 暴露)
|
|
21
|
+
// P-Action 4 (2026-06-15): 跟 registry.ts STATIC_LAYERS maxChars 同步, 停用 layer 设 0
|
|
22
|
+
const MAX_CHARS: Record<string, number> = {
|
|
23
|
+
'core/identity': 800,
|
|
24
|
+
'core/knowledge': 600,
|
|
25
|
+
'core/tools.thin': 400,
|
|
26
|
+
'core/hibs_reminders': 0, // 停用 (was 800)
|
|
27
|
+
'core/refusal': 800,
|
|
28
|
+
'core/tone': 500,
|
|
29
|
+
'core/wellbeing': 600,
|
|
30
|
+
'core/evenhandedness': 300,
|
|
31
|
+
'core/memory_system': 200,
|
|
32
|
+
'core/artifacts_storage': 0, // 停用 (was 2500)
|
|
33
|
+
'core/network_filesystem': 0, // 停用 (was 900)
|
|
34
|
+
'role/expert': 500,
|
|
35
|
+
'role/architect': 0, // 停用 (was 500)
|
|
36
|
+
'role/implementer': 0, // 停用 (was 500)
|
|
37
|
+
'role/security': 0, // 停用 (was 500)
|
|
38
|
+
'channel/local': 500,
|
|
39
|
+
'channel/p2p-visitor': 700,
|
|
40
|
+
'channel/p2p-agent': 700,
|
|
41
|
+
'tool/bash': 600,
|
|
42
|
+
'tool/web_search': 600,
|
|
43
|
+
'tool/mcp_apps': 0, // 停用 (was 1800)
|
|
44
|
+
'tool/hibs_api': 0, // 停用 (was 4500)
|
|
45
|
+
'tool/image_search': 0, // 停用 (was 2500)
|
|
46
|
+
'tool/artifacts': 0, // 停用 (was 2500)
|
|
47
|
+
'tool/manifest': 500,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
type IssueType = 'oversize' | 'no-version' | 'leftover-hibsml';
|
|
51
|
+
type IssueSeverity = 'error' | 'warning';
|
|
52
|
+
|
|
53
|
+
interface Issue {
|
|
54
|
+
layer: string;
|
|
55
|
+
type: IssueType;
|
|
56
|
+
severity: IssueSeverity;
|
|
57
|
+
detail: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* P-Action 4 (2026-06-15): oversize 降级为 warning.
|
|
62
|
+
* 原因: P-Action 4 收紧 registry maxChars 后, 多个历史 layer .md 实际长度超过新上限.
|
|
63
|
+
* 这些 .md 是"原样"对齐 Claude.ai 完整版的, 内容不动 (用户约束: 不改 prompt).
|
|
64
|
+
* runtime 时 assembleSystemPrompt 会按 maxChars 截断, 行为已受控.
|
|
65
|
+
* 因此 validate 阶段不再 fail build, 只 log warning.
|
|
66
|
+
* 保留为 error 的: no-version (可追溯性), leftover-hibsml (安全).
|
|
67
|
+
*/
|
|
68
|
+
const SEVERITY: Record<IssueType, IssueSeverity> = {
|
|
69
|
+
oversize: 'warning',
|
|
70
|
+
'no-version': 'error',
|
|
71
|
+
'leftover-hibsml': 'error',
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
async function check(file: string): Promise<Issue[]> {
|
|
75
|
+
const issues: Issue[] = [];
|
|
76
|
+
const rel = path.relative(LAYERS_DIR, file);
|
|
77
|
+
const max = MAX_CHARS[rel.replace(/\.md$/, '')];
|
|
78
|
+
const content = await fs.readFile(file, 'utf-8');
|
|
79
|
+
if (max && content.length > max) {
|
|
80
|
+
issues.push({ layer: rel, type: 'oversize', severity: SEVERITY.oversize, detail: `${content.length} > ${max}` });
|
|
81
|
+
}
|
|
82
|
+
if (!/<!--\s*[\w.-]+@[\d.]+\s*-->/.test(content)) {
|
|
83
|
+
issues.push({ layer: rel, type: 'no-version', severity: SEVERITY['no-version'], detail: '缺 <!-- id@version --> 标记' });
|
|
84
|
+
}
|
|
85
|
+
if (/\{hibsml:(?!thinking_mode)[\w_]+\}/.test(content) || /\{\/hibsml:/.test(content)) {
|
|
86
|
+
issues.push({ layer: rel, type: 'leftover-hibsml', severity: SEVERITY['leftover-hibsml'], detail: '检测到未脱除的 hibsml 标签' });
|
|
87
|
+
}
|
|
88
|
+
return issues;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function walk(dir: string): Promise<string[]> {
|
|
92
|
+
const out: string[] = [];
|
|
93
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
94
|
+
for (const e of entries) {
|
|
95
|
+
const full = path.join(dir, e.name);
|
|
96
|
+
if (e.isDirectory()) out.push(...await walk(full));
|
|
97
|
+
else if (e.name.endsWith('.md')) out.push(full);
|
|
98
|
+
}
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function main() {
|
|
103
|
+
const args = process.argv.slice(2);
|
|
104
|
+
let files: string[];
|
|
105
|
+
if (args.includes('--layer')) {
|
|
106
|
+
const idx = args.indexOf('--layer');
|
|
107
|
+
const target = path.join(LAYERS_DIR, args[idx + 1]);
|
|
108
|
+
files = [target];
|
|
109
|
+
} else {
|
|
110
|
+
files = await walk(LAYERS_DIR);
|
|
111
|
+
}
|
|
112
|
+
const allIssues: Issue[] = [];
|
|
113
|
+
for (const f of files) {
|
|
114
|
+
const issues = await check(f);
|
|
115
|
+
allIssues.push(...issues);
|
|
116
|
+
}
|
|
117
|
+
if (allIssues.length === 0) {
|
|
118
|
+
console.log(`✅ ${files.length} 个 layer 全部通过 (字符上限, 版本标记, hibsml 脱除)`);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const errors = allIssues.filter((i) => i.severity === 'error');
|
|
122
|
+
const warnings = allIssues.filter((i) => i.severity === 'warning');
|
|
123
|
+
|
|
124
|
+
if (warnings.length > 0) {
|
|
125
|
+
console.warn(`⚠️ ${warnings.length} 个 warning (P-Action 4 后 oversize 不再 fail build):`);
|
|
126
|
+
for (const i of warnings) {
|
|
127
|
+
console.warn(` [${i.type}] ${i.layer}: ${i.detail}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (errors.length === 0) {
|
|
132
|
+
console.log(`✅ ${files.length} 个 layer 通过 (${warnings.length} 个 warning, 0 个 error)`);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
console.error(`❌ ${errors.length} 个 error:`);
|
|
136
|
+
for (const i of errors) {
|
|
137
|
+
console.error(` [${i.type}] ${i.layer}: ${i.detail}`);
|
|
138
|
+
}
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
main().catch((e) => { console.error(e); process.exit(1); });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* p2p-chat-tools — 异步 chat 通道 + 持久化 inbox + 判断力外包 (draft)
|
|
3
3
|
*
|
|
4
|
-
* 解决痛点:
|
|
4
|
+
* 解决痛点: 两个节点的智能体代替各自人类用户做异步判断
|
|
5
5
|
*
|
|
6
6
|
* 流程:
|
|
7
7
|
* 1. A 节点 (人类在线) 通过 sendChat(peerDID, text) 发消息 → iroh 'agent_chat'
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - onMessage('agent_chat') → 落 ~/.bolloon/inbox/<peerDID>.jsonl
|
|
10
10
|
* - 状态 = 'received' (未处理)
|
|
11
11
|
* 3. B 节点 wake-up (processPendingInbox) → 扫描 status='received' 的消息
|
|
12
|
-
* - 调 LLM 生成 draft (
|
|
12
|
+
* - 调 LLM 生成 draft (注入用户历史判断 + ValueProfile)
|
|
13
13
|
* - 写 status='drafted', draft 落盘
|
|
14
14
|
* - 通过 'agent_chat' 消息类型回送 draft (前缀 [DRAFT] 表明是代回)
|
|
15
15
|
* 4. A 节点收到 draft → 写到 A 的 outbox (inbox 中 from=B 的那条)
|
|
@@ -258,7 +258,7 @@ async function buildValueHint(text: string): Promise<string> {
|
|
|
258
258
|
.slice(-10)
|
|
259
259
|
.flatMap((j) => j.reasons || [])
|
|
260
260
|
.slice(0, 20);
|
|
261
|
-
return `\n[
|
|
261
|
+
return `\n[用户历史判断 (style 参考, 不可外泄)]\n关注维度: ${profileHint}\n关键理由示例: ${reasons.join(' | ').slice(0, 400) || '(无)'}\n`;
|
|
262
262
|
} catch (e) {
|
|
263
263
|
return '';
|
|
264
264
|
}
|
|
@@ -275,7 +275,7 @@ export async function generateDraft(
|
|
|
275
275
|
if (entry.status !== 'received') return entry;
|
|
276
276
|
|
|
277
277
|
const valueHint = await buildValueHint(entry.text);
|
|
278
|
-
const promptForDraft =
|
|
278
|
+
const promptForDraft = `你是用户的代理. 对方发来这条消息: "${entry.text.slice(0, 1500)}"\n${valueHint}\n请基于用户的历史判断, 用 1-2 句话代用户拟一个回复草案. 草案要保留用户的语气和立场, 开头标注 [DRAFT]. 直接给草案文本, 不要解释.`;
|
|
279
279
|
|
|
280
280
|
let draftText = '';
|
|
281
281
|
let confidence = 0.5;
|
|
@@ -290,7 +290,7 @@ export async function generateDraft(
|
|
|
290
290
|
body: JSON.stringify({
|
|
291
291
|
model: openaiModel,
|
|
292
292
|
messages: [
|
|
293
|
-
{ role: 'system', content: '
|
|
293
|
+
{ role: 'system', content: '你是用户的代理. 你的输出会被用户审阅后才发出. 请谨慎.' },
|
|
294
294
|
{ role: 'user', content: promptForDraft },
|
|
295
295
|
],
|
|
296
296
|
temperature: 0.4,
|
|
@@ -310,7 +310,7 @@ export async function generateDraft(
|
|
|
310
310
|
}
|
|
311
311
|
|
|
312
312
|
if (!draftText) {
|
|
313
|
-
draftText = `[DRAFT] 已收到. (本地 draft 引擎未配置 LLM,
|
|
313
|
+
draftText = `[DRAFT] 已收到. (本地 draft 引擎未配置 LLM, 用户上线后请手写回复)`;
|
|
314
314
|
confidence = 0.1;
|
|
315
315
|
}
|
|
316
316
|
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission Mode — 3 种模式枚举 + 解析
|
|
3
|
+
*
|
|
4
|
+
* 设计: 严格对齐 Claude Code 论文 3 种 mode (default / acceptEdits / bypassPermissions)
|
|
5
|
+
* 论文第 7 种 (plan/dontAsk/bubble) bolloon 暂不引入, 减少 API surface.
|
|
6
|
+
*
|
|
7
|
+
* 关键设计: shell-guard **不受** mode 影响
|
|
8
|
+
* - 路径黑名单 (.bolloon/, pi-sdk.ts, shell-guard.ts, .env, .git/ 等) 永远生效
|
|
9
|
+
* - 命令 allowlist 默认: git/node/npm/npx/tsx/tsc/vitest/cat/head/tail/wc/ls/echo/pwd/date/mkdir/touch 永远生效
|
|
10
|
+
* - 即使用户设 bypassPermissions, shell 类工具仍走 shell-guard
|
|
11
|
+
*
|
|
12
|
+
* 失败静默: 任何异常 → 返回 'default' (向后兼容)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export type PermissionMode = 'default' | 'acceptEdits' | 'bypassPermissions';
|
|
16
|
+
|
|
17
|
+
export const ALL_PERMISSION_MODES: readonly PermissionMode[] = [
|
|
18
|
+
'default',
|
|
19
|
+
'acceptEdits',
|
|
20
|
+
'bypassPermissions',
|
|
21
|
+
] as const;
|
|
22
|
+
|
|
23
|
+
export interface ResolveOptions {
|
|
24
|
+
/** BootstrapOptions / onPreToolUse 调用处透传 */
|
|
25
|
+
permissionMode?: PermissionMode;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 解析当前 permission mode.
|
|
30
|
+
* 优先级 (由高到低):
|
|
31
|
+
* 1. opts.permissionMode (BootstrapOptions 显式传入)
|
|
32
|
+
* 2. runtime override (~/.bolloon/sessions/permission-mode.json, UI 设置)
|
|
33
|
+
* 3. env BOLLOON_PERM_MODE (环境变量)
|
|
34
|
+
* 4. 'default' (兜底)
|
|
35
|
+
*
|
|
36
|
+
* 非法值 → fallback 'default', console.warn
|
|
37
|
+
* 失败静默: 任何异常 → 'default' (不阻塞主对话)
|
|
38
|
+
*/
|
|
39
|
+
export function resolvePermissionMode(opts?: ResolveOptions): PermissionMode {
|
|
40
|
+
try {
|
|
41
|
+
if (opts?.permissionMode && ALL_PERMISSION_MODES.includes(opts.permissionMode)) {
|
|
42
|
+
return opts.permissionMode;
|
|
43
|
+
}
|
|
44
|
+
// runtime override (UI 设置, 存 ~/.bolloon/sessions/permission-mode.json)
|
|
45
|
+
const override = readRuntimeOverride();
|
|
46
|
+
if (override) {
|
|
47
|
+
return override;
|
|
48
|
+
}
|
|
49
|
+
const env = process.env.BOLLOON_PERM_MODE;
|
|
50
|
+
if (env === 'default' || env === 'acceptEdits' || env === 'bypassPermissions') {
|
|
51
|
+
return env;
|
|
52
|
+
}
|
|
53
|
+
if (env && env.length > 0) {
|
|
54
|
+
console.warn(`[permission-mode] unknown BOLLOON_PERM_MODE="${env}", fallback to "default"`);
|
|
55
|
+
}
|
|
56
|
+
return 'default';
|
|
57
|
+
} catch (err) {
|
|
58
|
+
console.warn('[permission-mode] resolvePermissionMode failed (silent, using default):', err);
|
|
59
|
+
return 'default';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 读 runtime override 文件 (UI 设的 mode).
|
|
65
|
+
* 失败静默: 任何异常 → null (回到 env/default)
|
|
66
|
+
*/
|
|
67
|
+
function readRuntimeOverride(): PermissionMode | null {
|
|
68
|
+
try {
|
|
69
|
+
const fs = require('fs') as typeof import('fs');
|
|
70
|
+
const os = require('os') as typeof import('os');
|
|
71
|
+
const path = require('path') as typeof import('path');
|
|
72
|
+
const file = path.join(
|
|
73
|
+
process.env.HOME || os.homedir() || '/tmp',
|
|
74
|
+
'.bolloon', 'sessions', 'permission-mode.json'
|
|
75
|
+
);
|
|
76
|
+
if (!fs.existsSync(file)) return null;
|
|
77
|
+
const raw = fs.readFileSync(file, 'utf-8');
|
|
78
|
+
const obj = JSON.parse(raw);
|
|
79
|
+
if (obj && typeof obj.mode === 'string' && ALL_PERMISSION_MODES.includes(obj.mode as any)) {
|
|
80
|
+
return obj.mode as PermissionMode;
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
} catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 工具名分类: 是不是"编辑"类 (受 acceptEdits 模式影响)
|
|
90
|
+
* 简单按前缀, 不做完整注册表
|
|
91
|
+
*/
|
|
92
|
+
export function isEditTool(toolName: string): boolean {
|
|
93
|
+
const lc = toolName.toLowerCase();
|
|
94
|
+
return lc.startsWith('edit_') || lc.startsWith('write_') || lc === 'str_replace' || lc === 'create_file' || lc === 'present_files';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 工具名分类: 是不是"shell"类 (永远走 shell-guard, 不受 mode 影响)
|
|
99
|
+
*/
|
|
100
|
+
export function isShellTool(toolName: string): boolean {
|
|
101
|
+
const lc = toolName.toLowerCase();
|
|
102
|
+
return lc === 'shell' || lc === 'shell_exec' || lc === 'bash';
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 判断当前 mode + tool 的最终是否需要走 blacklist
|
|
107
|
+
*
|
|
108
|
+
* 逻辑表:
|
|
109
|
+
* - shell 工具: 永远走 blacklist (shell-guard 接管)
|
|
110
|
+
* - bypassPermissions 模式: 非 shell 全部放行
|
|
111
|
+
* - acceptEdits 模式: edit_* / write_* 类跳过 blacklist
|
|
112
|
+
* - default 模式: 全部走 blacklist
|
|
113
|
+
*/
|
|
114
|
+
export function shouldRunBlacklist(toolName: string, mode: PermissionMode): boolean {
|
|
115
|
+
if (isShellTool(toolName)) return true; // shell-guard 永远跑
|
|
116
|
+
if (mode === 'bypassPermissions') return false;
|
|
117
|
+
if (mode === 'acceptEdits' && isEditTool(toolName)) return false;
|
|
118
|
+
return true; // default
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ============================================================
|
|
122
|
+
// 测试钩子
|
|
123
|
+
// ============================================================
|
|
124
|
+
|
|
125
|
+
export function _resetPermissionModeForTest(): void {
|
|
126
|
+
// 解析只读 env, 无内部状态, 保留 API 一致
|
|
127
|
+
}
|