@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.
- package/.auto-evolve-calls +1 -0
- package/.last-auto-evolve-baseline +1 -0
- 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 +144 -0
- 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 +687 -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 +117 -0
- package/dist/bootstrap/context-collector.js +312 -0
- package/dist/bootstrap/context-hierarchy.js +218 -0
- package/dist/bootstrap/lifecycle-hooks.js +171 -0
- package/dist/bootstrap/project-context.js +158 -0
- 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 +49 -2
- package/dist/llm/llm-judgment-client.js +32 -30
- package/dist/llm/pi-ai.js +125 -28
- 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 +279 -0
- package/dist/pi-ecosystem-judgment/causal-judge.js +449 -0
- package/dist/pi-ecosystem-judgment/detect-hook.js +168 -0
- package/dist/pi-ecosystem-judgment/distill-prompt.js +226 -0
- package/dist/pi-ecosystem-judgment/evolve-judgment.js +170 -0
- package/dist/pi-ecosystem-judgment/human-value-pipeline.js +21 -0
- package/dist/pi-ecosystem-judgment/human-value-store.js +283 -22
- package/dist/pi-ecosystem-judgment/injection-gate.js +208 -0
- package/dist/pi-ecosystem-judgment/monitor-gate.js +188 -0
- package/dist/pi-ecosystem-judgment/value-injection.js +8 -2
- package/dist/security/builtin-guards.js +124 -0
- package/dist/security/context-router-tool.js +106 -0
- package/dist/security/input-scanner.js +223 -0
- package/dist/security/react-harness.js +143 -0
- package/dist/security/tool-gate.js +235 -0
- package/dist/utils/auto-evolve-policy.js +117 -0
- package/dist/utils/clamp.js +7 -0
- package/dist/utils/double.js +6 -0
- package/dist/web/client.js +3810 -3830
- 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 +60 -49
- package/dist/web/server.js +827 -124
- 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 +33 -0
- package/package.json +3 -2
- package/scripts/auto-evolve-loop.ts +481 -0
- package/scripts/auto-evolve-oneshot.sh +155 -0
- package/scripts/auto-evolve-snapshot.sh +136 -0
- package/scripts/build-web.ts +35 -1
- package/scripts/detect-schema-changes.sh +48 -0
- package/scripts/diff-reviewer.ts +159 -0
- package/scripts/validate-system-prompt.ts +142 -0
- package/scripts/weekly-report.ts +364 -0
- package/src/agents/p2p-chat-tools.ts +6 -6
- package/src/agents/permission-mode.ts +127 -0
- package/src/agents/pi-sdk.ts +741 -30
- 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 +135 -0
- package/src/bootstrap/context-collector.ts +371 -0
- package/src/bootstrap/context-hierarchy.ts +283 -0
- package/src/bootstrap/lifecycle-hooks.ts +289 -0
- package/src/bootstrap/project-context.ts +171 -0
- 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 +47 -2
- package/src/llm/llm-judgment-client.ts +36 -35
- package/src/llm/pi-ai.ts +135 -29
- 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/builtin-guards.ts +162 -0
- package/src/security/context-router-tool.ts +122 -0
- package/src/security/input-scanner.ts +287 -0
- package/src/security/react-harness.ts +177 -0
- package/src/security/tool-gate.ts +294 -0
- package/src/utils/auto-evolve-policy.ts +138 -0
- package/src/utils/clamp.ts +5 -0
- package/src/web/client.js +105 -2187
- package/src/web/client.ts +4326 -0
- package/src/web/index.html +60 -49
- package/src/web/server.ts +894 -148
- 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/staging/auto-evolve/clean-001/.review-verdict +9 -0
- package/staging/auto-evolve/clean-001/clean-001.patch +14 -0
- package/staging/auto-evolve/e2e-001/.patch-id +1 -0
- package/staging/auto-evolve/e2e-001/.review-verdict +12 -0
- package/staging/auto-evolve/e2e-001/e2e-001.patch +11 -0
- package/staging/auto-evolve/test-bad/.review-verdict +12 -0
- package/staging/auto-evolve/test-bad/test-bad.patch +11 -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,364 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* weekly-report.ts — Bolloon 每周表现报告 (阶段 B)
|
|
4
|
+
*
|
|
5
|
+
* 输入:
|
|
6
|
+
* ~/.bolloon/human-values/evolution.jsonl (接受/拒绝/回滚事件)
|
|
7
|
+
* ~/.bolloon/human-values/usage.jsonl (judgment 使用记录)
|
|
8
|
+
* ~/.bolloon/human-values/counterfactual-audit.jsonl (反事实审计)
|
|
9
|
+
* ~/.bolloon/human-values/judgments.json (当前 judgment 库)
|
|
10
|
+
* ~/.bolloon/self-improve-audit.log (改源码尝试的审计, 即使被拒)
|
|
11
|
+
*
|
|
12
|
+
* 输出:
|
|
13
|
+
* ~/.bolloon/reports/2026-W24.md (markdown 报告)
|
|
14
|
+
*
|
|
15
|
+
* 设计原则:
|
|
16
|
+
* - 纯本地计算 + 纯函数式分析 (不调 LLM, 避免幻觉)
|
|
17
|
+
* - 周范围默认 ISO 周(周一为起点), 可 --week 2026-W24 指定
|
|
18
|
+
* - 不写 judgments.json, 不动 persona, 仅追加 reports/*.md
|
|
19
|
+
* - 失败静默 + 退出码 != 0 让 cron 知道坏了
|
|
20
|
+
*
|
|
21
|
+
* 用法:
|
|
22
|
+
* tsx scripts/weekly-report.ts # 生成上周
|
|
23
|
+
* tsx scripts/weekly-report.ts --week 2026-W24 # 指定周
|
|
24
|
+
* tsx scripts/weekly-report.ts --week 2026-W24 --dry-run
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import * as fs from 'fs/promises';
|
|
28
|
+
import * as os from 'os';
|
|
29
|
+
import * as path from 'path';
|
|
30
|
+
|
|
31
|
+
const HOME = () => os.homedir() || process.env.HOME || '/tmp';
|
|
32
|
+
const ROOT = () => HOME() + '/.bolloon';
|
|
33
|
+
const REPORTS_DIR = () => ROOT() + '/reports';
|
|
34
|
+
|
|
35
|
+
const FILES = {
|
|
36
|
+
evolution: () => ROOT() + '/human-values/evolution.jsonl',
|
|
37
|
+
usage: () => ROOT() + '/human-values/usage.jsonl',
|
|
38
|
+
counterfactual: () => ROOT() + '/human-values/counterfactual-audit.jsonl',
|
|
39
|
+
judgments: () => ROOT() + '/human-values/judgments.json',
|
|
40
|
+
selfImproveAudit: () => ROOT() + '/self-improve-audit.log',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
interface EvolutionEntry {
|
|
44
|
+
ts: string;
|
|
45
|
+
action: 'accept' | 'reject' | 'revert';
|
|
46
|
+
suggestion: { kind: string; judgmentId: string; action: string };
|
|
47
|
+
appliedPatch?: Record<string, unknown>;
|
|
48
|
+
}
|
|
49
|
+
interface UsageEntry {
|
|
50
|
+
ts: string;
|
|
51
|
+
channelId: string | null;
|
|
52
|
+
userInputPreview: string;
|
|
53
|
+
usedIds: string[];
|
|
54
|
+
}
|
|
55
|
+
interface CounterfactualEntry {
|
|
56
|
+
ts: string;
|
|
57
|
+
trigger: { userInput: string; aiReply: string; violatedPrinciples: unknown[] };
|
|
58
|
+
verdict: string;
|
|
59
|
+
recomendaciones?: string[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function readJsonl<T>(p: string): Promise<T[]> {
|
|
63
|
+
try {
|
|
64
|
+
const content = await fs.readFile(p, 'utf-8');
|
|
65
|
+
return content
|
|
66
|
+
.trim()
|
|
67
|
+
.split('\n')
|
|
68
|
+
.filter(Boolean)
|
|
69
|
+
.map((l) => {
|
|
70
|
+
try {
|
|
71
|
+
return JSON.parse(l) as T;
|
|
72
|
+
} catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
.filter((e): e is T => Boolean(e));
|
|
77
|
+
} catch {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function readJson<T>(p: string): Promise<T | null> {
|
|
83
|
+
try {
|
|
84
|
+
return JSON.parse(await fs.readFile(p, 'utf-8')) as T;
|
|
85
|
+
} catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function isoWeekString(d: Date): string {
|
|
91
|
+
// ISO week: 1 = 包含 1 月 4 日的那周
|
|
92
|
+
const target = new Date(d.valueOf());
|
|
93
|
+
const dayNr = (d.getUTCDay() + 6) % 7; // 周一=0
|
|
94
|
+
target.setUTCDate(target.getUTCDate() - dayNr + 3);
|
|
95
|
+
const firstThursday = new Date(Date.UTC(target.getUTCFullYear(), 0, 4));
|
|
96
|
+
const week =
|
|
97
|
+
1 +
|
|
98
|
+
Math.round(
|
|
99
|
+
((target.valueOf() - firstThursday.valueOf()) / 86400000 - 3 + ((firstThursday.getUTCDay() + 6) % 7)) / 7
|
|
100
|
+
);
|
|
101
|
+
return `${target.getUTCFullYear()}-W${String(week).padStart(2, '0')}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function weekRange(iso: string): { start: Date; end: Date } {
|
|
105
|
+
const m = /^(\d{4})-W(\d{1,2})$/.exec(iso);
|
|
106
|
+
if (!m) throw new Error(`bad ISO week: ${iso}`);
|
|
107
|
+
const year = Number(m[1]);
|
|
108
|
+
const week = Number(m[2]);
|
|
109
|
+
// ISO 周 1 是包含 1 月 4 日的那周
|
|
110
|
+
const jan4 = new Date(Date.UTC(year, 0, 4));
|
|
111
|
+
const jan4Day = (jan4.getUTCDay() + 6) % 7; // 周一=0
|
|
112
|
+
const week1Mon = new Date(jan4);
|
|
113
|
+
week1Mon.setUTCDate(jan4.getUTCDate() - jan4Day);
|
|
114
|
+
const start = new Date(week1Mon);
|
|
115
|
+
start.setUTCDate(week1Mon.getUTCDate() + (week - 1) * 7);
|
|
116
|
+
const end = new Date(start);
|
|
117
|
+
end.setUTCDate(start.getUTCDate() + 7);
|
|
118
|
+
return { start, end };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function inWeek(ts: string, start: Date, end: Date): boolean {
|
|
122
|
+
const t = new Date(ts).getTime();
|
|
123
|
+
return t >= start.getTime() && t < end.getTime();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function pct(n: number, d: number): string {
|
|
127
|
+
if (d === 0) return 'n/a';
|
|
128
|
+
return `${((n / d) * 100).toFixed(1)}%`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function topN<T>(arr: T[], key: (x: T) => string, n: number): Array<[string, number]> {
|
|
132
|
+
const m = new Map<string, number>();
|
|
133
|
+
for (const x of arr) {
|
|
134
|
+
const k = key(x);
|
|
135
|
+
m.set(k, (m.get(k) || 0) + 1);
|
|
136
|
+
}
|
|
137
|
+
return Array.from(m.entries())
|
|
138
|
+
.sort((a, b) => b[1] - a[1])
|
|
139
|
+
.slice(0, n);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
interface Report {
|
|
143
|
+
week: string;
|
|
144
|
+
range: { start: string; end: string };
|
|
145
|
+
generatedAt: string;
|
|
146
|
+
summary: {
|
|
147
|
+
totalUsage: number;
|
|
148
|
+
uniqueChannels: number;
|
|
149
|
+
uniqueJudgments: number;
|
|
150
|
+
evolutionEvents: number;
|
|
151
|
+
acceptRate: number;
|
|
152
|
+
rejected: number;
|
|
153
|
+
reverted: number;
|
|
154
|
+
counterfactualScans: number;
|
|
155
|
+
};
|
|
156
|
+
topJudgments: Array<[string, number]>;
|
|
157
|
+
topKinds: Array<[string, number]>;
|
|
158
|
+
policyAudit: {
|
|
159
|
+
selfImproveAttempts: number;
|
|
160
|
+
blockedByPolicy: number;
|
|
161
|
+
note: string;
|
|
162
|
+
};
|
|
163
|
+
openQuestions: string[];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function buildReport(weekIso: string): Promise<Report> {
|
|
167
|
+
const { start, end } = weekRange(weekIso);
|
|
168
|
+
|
|
169
|
+
const [evolution, usage, counterfactual, judgments, audit] = await Promise.all([
|
|
170
|
+
readJsonl<EvolutionEntry>(FILES.evolution()),
|
|
171
|
+
readJsonl<UsageEntry>(FILES.usage()),
|
|
172
|
+
readJsonl<CounterfactualEntry>(FILES.counterfactual()),
|
|
173
|
+
readJson<unknown[]>(FILES.judgments()),
|
|
174
|
+
(async () => {
|
|
175
|
+
try {
|
|
176
|
+
const txt = await fs.readFile(FILES.selfImproveAudit(), 'utf-8');
|
|
177
|
+
return txt.split('\n').filter(Boolean);
|
|
178
|
+
} catch {
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
181
|
+
})(),
|
|
182
|
+
]);
|
|
183
|
+
|
|
184
|
+
const evoInWeek = evolution.filter((e) => inWeek(e.ts, start, end));
|
|
185
|
+
const useInWeek = usage.filter((u) => inWeek(u.ts, start, end));
|
|
186
|
+
const cfInWeek = counterfactual.filter((c) => inWeek(c.ts, start, end));
|
|
187
|
+
const auditInWeek = audit.filter((line) => {
|
|
188
|
+
// audit 是日志行, 格式不一定, 简单按日期前缀过滤
|
|
189
|
+
const d = line.match(/^(\d{4}-\d{2}-\d{2})/);
|
|
190
|
+
if (!d) return false;
|
|
191
|
+
const ts = new Date(d[1] + 'T00:00:00Z').getTime();
|
|
192
|
+
return ts >= start.getTime() && ts < end.getTime();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const totalUsage = useInWeek.length;
|
|
196
|
+
const channels = new Set(useInWeek.map((u) => u.channelId || 'null'));
|
|
197
|
+
const ids = new Set<string>();
|
|
198
|
+
for (const u of useInWeek) for (const id of u.usedIds) ids.add(id);
|
|
199
|
+
|
|
200
|
+
const accepts = evoInWeek.filter((e) => e.action === 'accept').length;
|
|
201
|
+
const rejects = evoInWeek.filter((e) => e.action === 'reject').length;
|
|
202
|
+
const reverts = evoInWeek.filter((e) => e.action === 'revert').length;
|
|
203
|
+
const acceptRate = accepts + rejects === 0 ? 0 : accepts / (accepts + rejects);
|
|
204
|
+
|
|
205
|
+
// 自改审计 (路径白/黑名单拦截)
|
|
206
|
+
const selfImproveAttempts = auditInWeek.filter((l) => /attempt|尝试/i.test(l)).length;
|
|
207
|
+
const blockedByPolicy = auditInWeek.filter((l) => /block|deny|拒绝|denylist/i.test(l)).length;
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
week: weekIso,
|
|
211
|
+
range: { start: start.toISOString().slice(0, 10), end: end.toISOString().slice(0, 10) },
|
|
212
|
+
generatedAt: new Date().toISOString(),
|
|
213
|
+
summary: {
|
|
214
|
+
totalUsage,
|
|
215
|
+
uniqueChannels: channels.size,
|
|
216
|
+
uniqueJudgments: ids.size,
|
|
217
|
+
evolutionEvents: evoInWeek.length,
|
|
218
|
+
acceptRate,
|
|
219
|
+
rejected: rejects,
|
|
220
|
+
reverted: reverts,
|
|
221
|
+
counterfactualScans: cfInWeek.length,
|
|
222
|
+
},
|
|
223
|
+
topJudgments: topN(useInWeek.flatMap((u) => u.usedIds.map((id) => ({ id }))), (x) => x.id, 10),
|
|
224
|
+
topKinds: topN(evoInWeek, (e) => e.suggestion.kind, 5),
|
|
225
|
+
policyAudit: {
|
|
226
|
+
selfImproveAttempts,
|
|
227
|
+
blockedByPolicy,
|
|
228
|
+
note: selfImproveAttempts === 0 ? '本周无源码自改尝试(护栏未触发)' : '见 self-improve-audit.log',
|
|
229
|
+
},
|
|
230
|
+
openQuestions: openQuestions(evoInWeek, cfInWeek, useInWeek),
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function openQuestions(evo: EvolutionEntry[], cf: CounterfactualEntry[], use: UsageEntry[]): string[] {
|
|
235
|
+
const out: string[] = [];
|
|
236
|
+
if (evo.length === 0 && use.length > 0) {
|
|
237
|
+
out.push('本周有使用但无自适应建议 → 可能 judgment 库太稳定, 或扫描器未触发');
|
|
238
|
+
}
|
|
239
|
+
if (use.length === 0) {
|
|
240
|
+
out.push('本周无 judgment 使用记录 → 检查 usage.jsonl 是否在写, 或渠道是否活跃');
|
|
241
|
+
}
|
|
242
|
+
if (cf.length > 0) {
|
|
243
|
+
const conflictCount = cf.filter((c) => /冲突|conflict|不合理/i.test(c.verdict)).length;
|
|
244
|
+
if (conflictCount > 0) {
|
|
245
|
+
out.push(`反事实审计发现 ${conflictCount} 条潜在冲突 → 看 counterfactual-audit.jsonl`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
const reverts = evo.filter((e) => e.action === 'revert').length;
|
|
249
|
+
if (reverts >= 2) {
|
|
250
|
+
out.push(`本周回滚 ${reverts} 次 → 类 B 建议可能过激, 考虑收紧阈值`);
|
|
251
|
+
}
|
|
252
|
+
return out;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function toMarkdown(r: Report, totalJudgments: number): string {
|
|
256
|
+
const lines: string[] = [];
|
|
257
|
+
lines.push(`# 📊 Bolloon 周报 — ${r.week}`);
|
|
258
|
+
lines.push('');
|
|
259
|
+
lines.push(`> 范围: ${r.range.start} → ${r.range.end} · 生成于 ${r.generatedAt}`);
|
|
260
|
+
lines.push('');
|
|
261
|
+
lines.push('## 核心数字');
|
|
262
|
+
lines.push('');
|
|
263
|
+
lines.push('| 指标 | 本周 |');
|
|
264
|
+
lines.push('|------|------|');
|
|
265
|
+
lines.push(`| judgment 调用次数 | ${r.summary.totalUsage} |`);
|
|
266
|
+
lines.push(`| 触达渠道数 | ${r.summary.uniqueChannels} |`);
|
|
267
|
+
lines.push(`| 用到的不同 judgment 数 | ${r.summary.uniqueJudgments} |`);
|
|
268
|
+
lines.push(`| 自适应事件数 | ${r.summary.evolutionEvents} |`);
|
|
269
|
+
lines.push(`| 接受率 | ${pct(r.summary.acceptRate * 100, 100)} (${Math.round(r.summary.acceptRate * 100)}%) |`);
|
|
270
|
+
lines.push(`| 拒绝数 | ${r.summary.rejected} |`);
|
|
271
|
+
lines.push(`| 回滚数 | ${r.summary.reverted} |`);
|
|
272
|
+
lines.push(`| 反事实审计次数 | ${r.summary.counterfactualScans} |`);
|
|
273
|
+
lines.push(`| 当前 judgment 库总条数 | ${totalJudgments} |`);
|
|
274
|
+
lines.push('');
|
|
275
|
+
lines.push('## 最常被引用的 judgment');
|
|
276
|
+
lines.push('');
|
|
277
|
+
if (r.topJudgments.length === 0) {
|
|
278
|
+
lines.push('_(本周无引用)_');
|
|
279
|
+
} else {
|
|
280
|
+
for (const [id, n] of r.topJudgments) {
|
|
281
|
+
lines.push(`- \`${id}\` × ${n}`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
lines.push('');
|
|
285
|
+
lines.push('## 自适应建议类型分布');
|
|
286
|
+
lines.push('');
|
|
287
|
+
if (r.topKinds.length === 0) {
|
|
288
|
+
lines.push('_(本周无自适应事件)_');
|
|
289
|
+
} else {
|
|
290
|
+
for (const [kind, n] of r.topKinds) {
|
|
291
|
+
lines.push(`- **${kind}** × ${n}`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
lines.push('');
|
|
295
|
+
lines.push('## 护栏审计');
|
|
296
|
+
lines.push('');
|
|
297
|
+
lines.push(`- 源码自改尝试: **${r.policyAudit.selfImproveAttempts}**`);
|
|
298
|
+
lines.push(`- 被策略拦截: **${r.policyAudit.blockedByPolicy}**`);
|
|
299
|
+
lines.push(`- ${r.policyAudit.note}`);
|
|
300
|
+
lines.push('');
|
|
301
|
+
lines.push('## 关注事项');
|
|
302
|
+
lines.push('');
|
|
303
|
+
if (r.openQuestions.length === 0) {
|
|
304
|
+
lines.push('_(本周一切正常, 无特别关注)_');
|
|
305
|
+
} else {
|
|
306
|
+
for (const q of r.openQuestions) {
|
|
307
|
+
lines.push(`- ${q}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
lines.push('');
|
|
311
|
+
lines.push('---');
|
|
312
|
+
lines.push('');
|
|
313
|
+
lines.push('> 本报告由 `scripts/weekly-report.ts` 纯本地生成, 不调 LLM. 数据源见 ~/.bolloon/human-values/');
|
|
314
|
+
lines.push('');
|
|
315
|
+
return lines.join('\n');
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function parseArgs(argv: string[]): { week?: string; dryRun: boolean } {
|
|
319
|
+
const out: { week?: string; dryRun: boolean } = { dryRun: false };
|
|
320
|
+
for (let i = 0; i < argv.length; i++) {
|
|
321
|
+
if (argv[i] === '--week' && argv[i + 1]) {
|
|
322
|
+
out.week = argv[++i];
|
|
323
|
+
} else if (argv[i] === '--dry-run') {
|
|
324
|
+
out.dryRun = true;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return out;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
async function main() {
|
|
331
|
+
const args = parseArgs(process.argv.slice(2));
|
|
332
|
+
// 默认: 上周(更合理 — 周末才回顾)
|
|
333
|
+
const now = new Date();
|
|
334
|
+
const lastWeek = new Date(now);
|
|
335
|
+
lastWeek.setUTCDate(now.getUTCDate() - 7);
|
|
336
|
+
const week = args.week || isoWeekString(lastWeek);
|
|
337
|
+
const { start, end } = weekRange(week);
|
|
338
|
+
|
|
339
|
+
console.log(`[weekly-report] 生成 ${week} (${start.toISOString().slice(0, 10)} → ${end.toISOString().slice(0, 10)})`);
|
|
340
|
+
|
|
341
|
+
const r = await buildReport(week);
|
|
342
|
+
const judgments = (await readJson<unknown[]>(FILES.judgments())) || [];
|
|
343
|
+
const md = toMarkdown(r, judgments.length);
|
|
344
|
+
|
|
345
|
+
const outDir = REPORTS_DIR();
|
|
346
|
+
const outPath = path.join(outDir, `${week}.md`);
|
|
347
|
+
|
|
348
|
+
if (args.dryRun) {
|
|
349
|
+
console.log(`[weekly-report] DRY-RUN, 不会写盘. 输出预览:`);
|
|
350
|
+
console.log('---');
|
|
351
|
+
console.log(md);
|
|
352
|
+
console.log('---');
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
await fs.mkdir(outDir, { recursive: true });
|
|
357
|
+
await fs.writeFile(outPath, md, 'utf-8');
|
|
358
|
+
console.log(`[weekly-report] ✅ 写入 ${outPath} (${md.length} bytes)`);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
main().catch((err) => {
|
|
362
|
+
console.error('[weekly-report] ❌ 失败:', err);
|
|
363
|
+
process.exit(1);
|
|
364
|
+
});
|
|
@@ -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
|
+
}
|