@bolloon/bolloon-agent 0.5.3 → 0.5.5
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/dist/agents/agent-identity-store.js +41 -5
- package/dist/agents/background-notices.js +124 -0
- package/dist/agents/channel-identity.js +54 -0
- package/dist/agents/code-write-gate.js +47 -0
- package/dist/agents/deny-pipeline.js +11 -2
- package/dist/agents/egress-sanitize.js +69 -0
- package/dist/agents/error-classifier.js +67 -5
- package/dist/agents/experience-review.js +226 -0
- package/dist/agents/gateway-group.js +153 -18
- package/dist/agents/iteration-budget.js +64 -0
- package/dist/agents/lesson-to-skill.js +211 -0
- package/dist/agents/managed-services.js +64 -0
- package/dist/agents/parse-tool-call.js +38 -32
- package/dist/agents/pi-sdk-session-manager.js +72 -3
- package/dist/agents/pi-sdk-tools.js +268 -47
- package/dist/agents/pi-sdk.js +470 -22
- package/dist/agents/plan-store.js +36 -0
- package/dist/agents/process-runner.js +64 -2
- package/dist/agents/session-store.js +144 -3
- package/dist/agents/shell-guard.js +109 -13
- package/dist/agents/skill-health.js +90 -0
- package/dist/agents/skill-ledger.js +129 -0
- package/dist/agents/tool-loop-guard.js +303 -0
- package/dist/agents/tool-path-args.js +53 -0
- package/dist/agents/tool-result-gate.js +50 -0
- package/dist/agents/tool-subset.js +109 -0
- package/dist/agents/tool-telemetry.js +97 -0
- package/dist/agents/wallet-tools.js +63 -0
- package/dist/bootstrap/context-collector.js +37 -7
- package/dist/bootstrap/lifecycle-hooks.js +12 -3
- package/dist/bootstrap/persona-init.js +174 -0
- package/dist/bootstrap/persona-loader.js +5 -1
- package/dist/cli/ink-app.js +231 -32
- package/dist/cli/input-history.js +119 -0
- package/dist/cli/input-paste.js +218 -0
- package/dist/cli/loading-tui.js +69 -7
- package/dist/cli/log-gate.js +46 -1
- package/dist/cli/mention-data.js +30 -0
- package/dist/cli/reasoning-view.js +103 -0
- package/dist/cli/session-summary.js +129 -0
- package/dist/cli/session-tree.js +141 -0
- package/dist/cli/startup-notice.js +25 -0
- package/dist/cli/status-segments.js +155 -0
- package/dist/cli/stores.js +16 -0
- package/dist/cli/trace-line.js +54 -0
- package/dist/cli/wallet-store.js +159 -0
- package/dist/cli/wallet-table.js +46 -0
- package/dist/cli-entry.js +22 -0
- package/dist/context-compaction/auto-compact.js +37 -2
- package/dist/electron-build/agents/agent-identity-store.js +43 -5
- package/dist/electron-build/agents/agent-identity-store.js.map +1 -1
- package/dist/electron-build/agents/background-notices.js +138 -0
- package/dist/electron-build/agents/background-notices.js.map +1 -0
- package/dist/electron-build/agents/channel-identity.js +64 -0
- package/dist/electron-build/agents/channel-identity.js.map +1 -0
- package/dist/electron-build/agents/code-write-gate.js +55 -0
- package/dist/electron-build/agents/code-write-gate.js.map +1 -0
- package/dist/electron-build/agents/deny-pipeline.js +11 -2
- package/dist/electron-build/agents/deny-pipeline.js.map +1 -1
- package/dist/electron-build/agents/egress-sanitize.js +76 -0
- package/dist/electron-build/agents/egress-sanitize.js.map +1 -0
- package/dist/electron-build/agents/error-classifier.js +68 -5
- package/dist/electron-build/agents/error-classifier.js.map +1 -1
- package/dist/electron-build/agents/experience-review.js +243 -0
- package/dist/electron-build/agents/experience-review.js.map +1 -0
- package/dist/electron-build/agents/iteration-budget.js +73 -0
- package/dist/electron-build/agents/iteration-budget.js.map +1 -0
- package/dist/electron-build/agents/lesson-to-skill.js +228 -0
- package/dist/electron-build/agents/lesson-to-skill.js.map +1 -0
- package/dist/electron-build/agents/managed-services.js +69 -0
- package/dist/electron-build/agents/managed-services.js.map +1 -0
- package/dist/electron-build/agents/parse-tool-call.js +38 -32
- package/dist/electron-build/agents/parse-tool-call.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk-session-manager.js +75 -3
- package/dist/electron-build/agents/pi-sdk-session-manager.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk-tools.js +268 -46
- package/dist/electron-build/agents/pi-sdk-tools.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk-types.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk.js +471 -22
- package/dist/electron-build/agents/pi-sdk.js.map +1 -1
- package/dist/electron-build/agents/plan-store.js +37 -0
- package/dist/electron-build/agents/plan-store.js.map +1 -1
- package/dist/electron-build/agents/process-runner.js +71 -2
- package/dist/electron-build/agents/process-runner.js.map +1 -1
- package/dist/electron-build/agents/session-store.js +144 -3
- package/dist/electron-build/agents/session-store.js.map +1 -1
- package/dist/electron-build/agents/shell-guard.js +110 -14
- package/dist/electron-build/agents/shell-guard.js.map +1 -1
- package/dist/electron-build/agents/skill-health.js +103 -0
- package/dist/electron-build/agents/skill-health.js.map +1 -0
- package/dist/electron-build/agents/skill-ledger.js +146 -0
- package/dist/electron-build/agents/skill-ledger.js.map +1 -0
- package/dist/electron-build/agents/tool-loop-guard.js +319 -0
- package/dist/electron-build/agents/tool-loop-guard.js.map +1 -0
- package/dist/electron-build/agents/tool-path-args.js +63 -0
- package/dist/electron-build/agents/tool-path-args.js.map +1 -0
- package/dist/electron-build/agents/tool-result-gate.js +59 -0
- package/dist/electron-build/agents/tool-result-gate.js.map +1 -0
- package/dist/electron-build/agents/tool-subset.js +116 -0
- package/dist/electron-build/agents/tool-subset.js.map +1 -0
- package/dist/electron-build/agents/tool-telemetry.js +108 -0
- package/dist/electron-build/agents/tool-telemetry.js.map +1 -0
- package/dist/electron-build/agents/wallet-tools.js +102 -0
- package/dist/electron-build/agents/wallet-tools.js.map +1 -0
- package/dist/electron-build/bootstrap/context-collector.js +38 -7
- package/dist/electron-build/bootstrap/context-collector.js.map +1 -1
- package/dist/electron-build/bootstrap/lifecycle-hooks.js +12 -3
- package/dist/electron-build/bootstrap/lifecycle-hooks.js.map +1 -1
- package/dist/electron-build/bootstrap/persona-init.js +216 -0
- package/dist/electron-build/bootstrap/persona-init.js.map +1 -0
- package/dist/electron-build/bootstrap/persona-loader.js +5 -1
- package/dist/electron-build/bootstrap/persona-loader.js.map +1 -1
- package/dist/electron-build/cli/wallet-store.js +200 -0
- package/dist/electron-build/cli/wallet-store.js.map +1 -0
- package/dist/electron-build/context-compaction/auto-compact.js +38 -2
- package/dist/electron-build/context-compaction/auto-compact.js.map +1 -1
- package/dist/electron-build/external-engines/delegate.js +22 -0
- package/dist/electron-build/external-engines/delegate.js.map +1 -1
- package/dist/electron-build/llm/pi-ai.js +25 -0
- package/dist/electron-build/llm/pi-ai.js.map +1 -1
- package/dist/electron-build/orbitdb/cid-database.js +55 -0
- package/dist/electron-build/orbitdb/cid-database.js.map +1 -1
- package/dist/electron-build/orbitdb/ipfs-node.js +36 -11
- package/dist/electron-build/orbitdb/ipfs-node.js.map +1 -1
- package/dist/electron-build/web/server-storage.js +26 -5
- package/dist/electron-build/web/server-storage.js.map +1 -1
- package/dist/external-engines/delegate.js +21 -0
- package/dist/index.js +1324 -173
- package/dist/llm/pi-ai.js +23 -0
- package/dist/orbitdb/cid-database.js +55 -0
- package/dist/orbitdb/event-index.js +370 -0
- package/dist/orbitdb/event-query.js +131 -0
- package/dist/orbitdb/event-shell.js +510 -0
- package/dist/orbitdb/group-access.js +381 -0
- package/dist/orbitdb/group-shards.js +140 -0
- package/dist/orbitdb/ipfs-node.js +36 -11
- package/dist/web/bolloon-web.json +3 -3
- package/dist/web/client.js +3 -2
- package/dist/web/mobile-core.js +7762 -6289
- package/dist/web/routes-mobile-tasks.js +2 -1
- package/dist/web/server-storage.js +26 -5
- package/dist/web/server.js +2 -0
- package/dist/web/sw.js +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 代码写改的"类型门" (2026-10-01, 回应「tsc 抓错这个功能 bolloon 有吗」)。
|
|
4
|
+
*
|
|
5
|
+
* 查到的事实: bolloon **早就**有类型检查, 而且有三层 —— pre-commit 的 `tsc-check`(lefthook) ·
|
|
6
|
+
* `build:main = tsc && …` · 以及**给智能体的工具** `tsc_check`(pi-sdk-tools.ts:1450, 60s timeout)。
|
|
7
|
+
* 但**循环里没有"编辑后自动检查"** ✗ ⇒ `tsc_check` 是个"模型想得起来才会调"的工具 ✓
|
|
8
|
+
* ⇒ 和 #4(写操作读回)同一个病: **规矩写了, 没人执行** ✓。
|
|
9
|
+
* 这条把它变成机制: 本回合**改过 .ts/.tsx** ⇒ 回合收尾**自动**跑一次类型检查(一轮只跑一次, 不管改了 1 个还是 8 个文件 ✓),
|
|
10
|
+
* 结果以一行状态进对话流 —— "类型过没过"不再依赖模型自觉 ✓。
|
|
11
|
+
* 纪律: 每个回合最多一次(tsc 是秒级开销, 但也不该每改一个文件就跑) · 只对 TS 源码生效 · 失败绝不影响回合结果 ✓。
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.CODE_WRITE_TOOLS = void 0;
|
|
15
|
+
exports.isTypeScriptFile = isTypeScriptFile;
|
|
16
|
+
exports.codeWriteTarget = codeWriteTarget;
|
|
17
|
+
exports.decideTypecheck = decideTypecheck;
|
|
18
|
+
exports.formatTypecheckResult = formatTypecheckResult;
|
|
19
|
+
/** 写/改类工具里, 会"产出源码"的那些 */
|
|
20
|
+
exports.CODE_WRITE_TOOLS = new Set(['write_file', 'edit_file', 'patch', 'move_file', 'copy_file']);
|
|
21
|
+
function isTypeScriptFile(p) {
|
|
22
|
+
return /\.(ts|tsx|mts|cts)$/i.test(String(p || '').trim());
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 这次写操作是否**产生了/改动了 TS 源码** ⇒ 返回那个路径(否则 null)。
|
|
26
|
+
* 只看路径, 不看内容(便宜且够用 ✓)。
|
|
27
|
+
*/
|
|
28
|
+
function codeWriteTarget(toolName, args) {
|
|
29
|
+
if (!exports.CODE_WRITE_TOOLS.has(String(toolName)))
|
|
30
|
+
return null;
|
|
31
|
+
const rel = String(args?.path ?? args?.to ?? args?.file ?? '').trim();
|
|
32
|
+
if (!rel)
|
|
33
|
+
return null;
|
|
34
|
+
return isTypeScriptFile(rel) ? rel : null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 回合收尾要不要跑类型检查: 改过 TS 且本回合还没跑过 ⇒ 跑。
|
|
38
|
+
* 抽成纯函数是为了**可测**: "一轮只跑一次"这条最容易写错(写成每个文件跑一次就把时间烧光 ✓)。
|
|
39
|
+
*/
|
|
40
|
+
function decideTypecheck(touched, ranThisTurn) {
|
|
41
|
+
return !ranThisTurn && touched.length > 0;
|
|
42
|
+
}
|
|
43
|
+
/** 类型检查结果 ⇒ **一行**状态 (给对话流看; 错误多时只给前几条 + 总数) */
|
|
44
|
+
function formatTypecheckResult(ok, output, maxLines = 6) {
|
|
45
|
+
const body = String(output || '').split('\n').map((l) => l.trim()).filter(Boolean);
|
|
46
|
+
if (ok)
|
|
47
|
+
return '✅ 类型检查通过 (tsc --noEmit, 本回合改过 TS 后自动跑)';
|
|
48
|
+
const errs = body.filter((l) => /error TS\d+/.test(l));
|
|
49
|
+
const head = (errs.length ? errs : body).slice(0, maxLines);
|
|
50
|
+
const more = (errs.length || body.length) - head.length;
|
|
51
|
+
return `❌ 类型检查没过: ${errs.length || '?'} 个错误\n` + head.map((l) => ` ${l.slice(0, 160)}`).join('\n')
|
|
52
|
+
+ (more > 0 ? `\n …还有 ${more} 条` : '')
|
|
53
|
+
+ '\n ⇒ **先修类型再继续**(别在同一轮里叠加更多改动)';
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=code-write-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-write-gate.js","sourceRoot":"","sources":["../../../src/agents/code-write-gate.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;;;;;AAEH,0BAA0B;AACb,QAAA,gBAAgB,GAAwB,IAAI,GAAG,CAAC,CAAC,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC;AAE7H,0BAAiC,CAAS;IACxC,OAAO,sBAAsB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED;;;GAGG;AACH,yBAAgC,QAAgB,EAAE,IAAS;IACzD,IAAI,CAAC,QAAA,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACzD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE,EAAE,IAAI,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACtE,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,OAAO,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACH,yBAAgC,OAAiB,EAAE,WAAoB;IACrE,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;AAC5C,CAAC;AAED,gDAAgD;AAChD,+BAAsC,EAAW,EAAE,MAAc,EAAE,QAAQ,GAAG,CAAC;IAC7E,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACnF,IAAI,EAAE;QAAE,OAAO,wCAAwC,CAAC;IACxD,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC5D,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;IACxD,OAAO,aAAa,IAAI,CAAC,MAAM,IAAI,GAAG,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;UAChG,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;UACtC,kCAAkC,CAAC;AACzC,CAAC"}
|
|
@@ -91,8 +91,17 @@ class DenyPipeline {
|
|
|
91
91
|
* 实测日志 "write_file 被权限拦了" → LLM 只能绕道, 任务无法推进.
|
|
92
92
|
*/
|
|
93
93
|
static permissionChecker() {
|
|
94
|
+
/**
|
|
95
|
+
* 2026-10-01: **default 模式下只禁 `git_push`**(远端/不可逆那一个);
|
|
96
|
+
* `git_commit` / `git_branch` 放行 —— 它们本来是"指定安全路径"(有护栏、只提交已暂存的 ✓)。
|
|
97
|
+
* 为什么改: 用户现场实录里, `git_commit` 被权限拦了 ⇒ 智能体**绕道裸跑 shell**
|
|
98
|
+
* (`shell_exec` + **`git add -A`** ✗ —— 正是项目红线禁止的那条, 会卷走别的智能体在写的文件 ✗✗);
|
|
99
|
+
* 也就是说"拦住它"并没有更安全, 只是把它推去了**更危险**的路 ✗。
|
|
100
|
+
* (本文件上面那段注释早就记过同类教训: "被权限拦了 → LLM 只能绕道, 任务无法推进" ✓。)
|
|
101
|
+
* acceptEdits 模式仍按原样: shell/git 一律受限(那是**显式**收紧, 不是默认 ✗)。
|
|
102
|
+
*/
|
|
94
103
|
const DEFAULT_DENY_TOOLS = new Set([
|
|
95
|
-
'
|
|
104
|
+
'git_push',
|
|
96
105
|
]);
|
|
97
106
|
return (ctx) => {
|
|
98
107
|
if (ctx.permissionMode === 'bypassPermissions') {
|
|
@@ -109,7 +118,7 @@ class DenyPipeline {
|
|
|
109
118
|
}
|
|
110
119
|
return { denied: false, reason: '', source: 'permission' };
|
|
111
120
|
}
|
|
112
|
-
// default:
|
|
121
|
+
// default: 只禁不可逆的远端操作(见上)
|
|
113
122
|
if (DEFAULT_DENY_TOOLS.has(ctx.toolName)) {
|
|
114
123
|
return {
|
|
115
124
|
denied: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deny-pipeline.js","sourceRoot":"","sources":["../../../src/agents/deny-pipeline.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;GAkBG;;;AA+BH;;;;;GAKG;AACH;IACU,QAAQ,GAAkB,EAAE,CAAC;IAErC;QACE,oCAAoC;IACtC,CAAC;IAED,wBAAwB;IACxB,UAAU,CAAC,OAAoB;QAC7B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAED,uBAAuB;IACvB,KAAK;QACH,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;IACrB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK,CAAC,GAAgB;QAC1B,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpC,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;gBAClC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;oBAClB,OAAO,MAAM,CAAC;gBAChB,CAAC;gBACD,kCAAkC;gBAClC,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;oBAC1B,wBAAwB;gBAC1B,CAAC;YACH,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,4CAA4C;gBAC5C,OAAO;oBACL,MAAM,EAAE,IAAI;oBACZ,MAAM,EAAE,kBAAkB,MAAM,CAAC,CAAC,CAAC,EAAE;oBACrC,MAAM,EAAE,WAAW,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;iBACpD,CAAC;YACJ,CAAC;QACH,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACnD,CAAC;IAED,qCAAqC;IAErC;;;OAGG;IACH,MAAM,CAAC,eAAe,CAAC,WAAwB;QAC7C,OAAO,CAAC,GAAgB,EAAE,EAAE,CAAC,CAAC;YAC5B,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC;YACrC,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC;gBACnC,CAAC,CAAC,MAAM,GAAG,CAAC,QAAQ,gBAAgB;gBACpC,CAAC,CAAC,EAAE;YACN,MAAM,EAAE,WAAW;SACpB,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;;OASG;IACH,MAAM,CAAC,iBAAiB;QACtB,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAS;YACzC,
|
|
1
|
+
{"version":3,"file":"deny-pipeline.js","sourceRoot":"","sources":["../../../src/agents/deny-pipeline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.redactSecrets = redactSecrets;
|
|
4
|
+
exports.elideMiddle = elideMiddle;
|
|
5
|
+
exports.sanitizeForLlm = sanitizeForLlm;
|
|
6
|
+
exports.hasSuspiciousSecret = hasSuspiciousSecret;
|
|
7
|
+
/**
|
|
8
|
+
* 出口净化 (2026-10-01 落实③)。
|
|
9
|
+
*
|
|
10
|
+
* 为什么: 记忆/身份文档/历史片段这些"文本资产"最终都会**拼进给模型的提示**。秘密一旦混进去, 就等于
|
|
11
|
+
* 通过 API 送出去了 —— 而且会**反复**送(每一轮)。纪律上我们早就规定"凭证只落 0600 文件、绝不回显",
|
|
12
|
+
* 但那靠的是调用方自觉;出口必须是一道**闸**: 所有要进模型的文本都过同一个函数。
|
|
13
|
+
* 设计对齐 (只搬约束): **一个 choke point** · 先脱敏再截断 · 中间省略保留头尾 · 短文本原样返回(零成本)。
|
|
14
|
+
* 取舍: 宁可误伤(把长得像密钥的东西打码), 不可漏放 —— 误伤只是少几个字符, 漏放是事故。
|
|
15
|
+
*/
|
|
16
|
+
/** 常见凭证形态 (按"长得像"判, 不依赖具体厂商) */
|
|
17
|
+
const SECRET_PATTERNS = [
|
|
18
|
+
// 私钥 hex: 0x + 64 hex, 或裸 64 hex
|
|
19
|
+
{ re: /\b0x[0-9a-fA-F]{64}\b/g, tag: '[REDACTED:hex-key]' },
|
|
20
|
+
{ re: /\b[0-9a-fA-F]{64}\b/g, tag: '[REDACTED:hex64]' },
|
|
21
|
+
// 助记词: 12+ 个连续小写词 (空格分隔)
|
|
22
|
+
{ re: /\b(?:[a-z]{3,8}\s+){11,23}[a-z]{3,8}\b/g, tag: '[REDACTED:mnemonic]' },
|
|
23
|
+
// 常见 token 前缀
|
|
24
|
+
{ re: /\b(?:sk|pk|rk)-[A-Za-z0-9_-]{16,}\b/g, tag: '[REDACTED:api-key]' },
|
|
25
|
+
{ re: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}\b/g, tag: '[REDACTED:github-token]' },
|
|
26
|
+
{ re: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, tag: '[REDACTED:slack-token]' },
|
|
27
|
+
{ re: /\bAKIA[0-9A-Z]{16}\b/g, tag: '[REDACTED:aws-key]' },
|
|
28
|
+
{ re: /\bnpm_[A-Za-z0-9]{30,}\b/g, tag: '[REDACTED:npm-token]' },
|
|
29
|
+
// JWT
|
|
30
|
+
{ re: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{6,}\b/g, tag: '[REDACTED:jwt]' },
|
|
31
|
+
// Authorization: Bearer xxx
|
|
32
|
+
{ re: /\bBearer\s+[A-Za-z0-9._~+/-]{12,}=*/gi, tag: 'Bearer [REDACTED]' },
|
|
33
|
+
// URL 里的用户名:密码
|
|
34
|
+
{ re: /(https?:\/\/)[^\s/@:]+:[^\s/@]+@/gi, tag: '$1[REDACTED]@' },
|
|
35
|
+
// key: value 形态的敏感字段 (私钥/助记词/密钥/token/password)
|
|
36
|
+
{ re: /((?:privateKey|private_key|mnemonic|seedPhrase|seed_phrase|secret|apiKey|api_key|token|password|passwd|passphrase)"?\s*[:=]\s*)"[^"]{8,}"/gi, tag: '$1"[REDACTED]"' },
|
|
37
|
+
{ re: /((?:privateKey|private_key|mnemonic|seedPhrase|seed_phrase|secret|apiKey|api_key|token|password|passwd|passphrase)"?\s*[:=]\s*)'[^']{8,}'/gi, tag: "$1'[REDACTED]'" },
|
|
38
|
+
{ re: /((?:privateKey|private_key|mnemonic|seedPhrase|seed_phrase|secret|apiKey|api_key|token|password|passwd|passphrase)"?\s*[:=]\s*)[^\s,;"'}\]]{8,}/gi, tag: '$1[REDACTED]' },
|
|
39
|
+
];
|
|
40
|
+
/** 把长得像凭证的东西打码 (纯函数, 幂等 — 已打码的文本再跑不变) */
|
|
41
|
+
function redactSecrets(text) {
|
|
42
|
+
let out = String(text ?? '');
|
|
43
|
+
for (const { re, tag } of SECRET_PATTERNS)
|
|
44
|
+
out = out.replace(re, tag);
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
/** 中间省略: 长度 <= maxChars 原样返回; 否则保留头 head、尾 tail, 中间插省略标记 */
|
|
48
|
+
function elideMiddle(text, maxChars, headChars = 0, tailChars = 0) {
|
|
49
|
+
const s = String(text ?? '');
|
|
50
|
+
const max = Number.isFinite(maxChars) && maxChars > 0 ? Math.floor(maxChars) : 0;
|
|
51
|
+
if (!max || s.length <= max)
|
|
52
|
+
return s;
|
|
53
|
+
// 头/尾各留一部分, **给省略标记留出余量** —— 上一版 head+tail 正好等于 max ⇒ 落到"直接截断"分支,
|
|
54
|
+
// 省略标记根本不出现(门抓到: 输出里没有 "省略" ✗)。现在按 60%/25% 留, 剩下给标记。
|
|
55
|
+
const head = headChars > 0 ? Math.min(headChars, Math.floor(max * 0.8)) : Math.floor(max * 0.6);
|
|
56
|
+
const tail = tailChars > 0 ? Math.min(tailChars, Math.floor(max * 0.3)) : Math.floor(max * 0.25);
|
|
57
|
+
const marker = (n) => `\n…[省略 ${n} 字符]…\n`;
|
|
58
|
+
if (head <= 0 && tail <= 0)
|
|
59
|
+
return s.slice(0, max);
|
|
60
|
+
const omitted = Math.max(0, s.length - head - tail);
|
|
61
|
+
const out = `${s.slice(0, head)}${marker(omitted)}${tail > 0 ? s.slice(-tail) : ''}`;
|
|
62
|
+
return out.length <= max + marker(omitted).length ? out : out.slice(0, max + marker(omitted).length);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* **唯一出口**: 任何要拼进模型的文本都过这里。
|
|
66
|
+
* 顺序很重要 —— 先脱敏再截断: 否则截断可能把 key 的中间切掉, 让模式匹配失效(半截 key 照样是泄漏)。
|
|
67
|
+
*/
|
|
68
|
+
function sanitizeForLlm(text, maxChars = 0) {
|
|
69
|
+
const redacted = redactSecrets(text);
|
|
70
|
+
return maxChars > 0 ? elideMiddle(redacted, maxChars) : redacted;
|
|
71
|
+
}
|
|
72
|
+
/** 一行内是否含可疑凭证 (给调用方做审计/告警用, 不改变文本) */
|
|
73
|
+
function hasSuspiciousSecret(text) {
|
|
74
|
+
return redactSecrets(text) !== String(text ?? '');
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=egress-sanitize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"egress-sanitize.js","sourceRoot":"","sources":["../../../src/agents/egress-sanitize.ts"],"names":[],"mappings":";;;;;;AAAA;;;;;;;;GAQG;AACH,gCAAgC;AAChC,MAAM,eAAe,GAAuC;IAC1D,iCAAiC;IACjC,EAAE,EAAE,EAAE,wBAAwB,EAAE,GAAG,EAAE,oBAAoB,EAAE;IAC3D,EAAE,EAAE,EAAE,sBAAsB,EAAE,GAAG,EAAE,kBAAkB,EAAE;IACvD,yBAAyB;IACzB,EAAE,EAAE,EAAE,yCAAyC,EAAE,GAAG,EAAE,qBAAqB,EAAE;IAC7E,cAAc;IACd,EAAE,EAAE,EAAE,sCAAsC,EAAE,GAAG,EAAE,oBAAoB,EAAE;IACzE,EAAE,EAAE,EAAE,+CAA+C,EAAE,GAAG,EAAE,yBAAyB,EAAE;IACvF,EAAE,EAAE,EAAE,mCAAmC,EAAE,GAAG,EAAE,wBAAwB,EAAE;IAC1E,EAAE,EAAE,EAAE,uBAAuB,EAAE,GAAG,EAAE,oBAAoB,EAAE;IAC1D,EAAE,EAAE,EAAE,2BAA2B,EAAE,GAAG,EAAE,sBAAsB,EAAE;IAChE,MAAM;IACN,EAAE,EAAE,EAAE,mEAAmE,EAAE,GAAG,EAAE,gBAAgB,EAAE;IAClG,4BAA4B;IAC5B,EAAE,EAAE,EAAE,uCAAuC,EAAE,GAAG,EAAE,mBAAmB,EAAE;IACzE,eAAe;IACf,EAAE,EAAE,EAAE,oCAAoC,EAAE,GAAG,EAAE,eAAe,EAAE;IAClE,gDAAgD;IAChD,EAAE,EAAE,EAAE,6IAA6I,EAAE,GAAG,EAAE,gBAAgB,EAAE;IAC5K,EAAE,EAAE,EAAE,6IAA6I,EAAE,GAAG,EAAE,gBAAgB,EAAE;IAC5K,EAAE,EAAE,EAAE,mJAAmJ,EAAE,GAAG,EAAE,cAAc,EAAE;CACjL,CAAC;AAEF,yCAAyC;AACzC,uBAA8B,IAAY;IACxC,IAAI,GAAG,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAC7B,KAAK,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,eAAe;QAAE,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,GAAU,CAAC,CAAC;IAC7E,OAAO,GAAG,CAAC;AACb,CAAC;AAED,4DAA4D;AAC5D,qBAA4B,IAAY,EAAE,QAAgB,EAAE,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,CAAC;IACtF,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAC7B,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjF,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,CAAC,CAAC;IACtC,kEAAkE;IAClE,qDAAqD;IACrD,MAAM,IAAI,GAAG,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC;IAChG,MAAM,IAAI,GAAG,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IACjG,MAAM,MAAM,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;IACnD,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;IACpD,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IACrF,OAAO,GAAG,CAAC,MAAM,IAAI,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC;AACvG,CAAC;AAED;;;GAGG;AACH,wBAA+B,IAAY,EAAE,QAAQ,GAAG,CAAC;IACvD,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACrC,OAAO,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AACnE,CAAC;AAED,sCAAsC;AACtC,6BAAoC,IAAY;IAC9C,OAAO,aAAa,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;AACpD,CAAC"}
|
|
@@ -13,17 +13,24 @@ exports.classifyError = classifyError;
|
|
|
13
13
|
exports.buildObservation = buildObservation;
|
|
14
14
|
exports.buildReflection = buildReflection;
|
|
15
15
|
exports.formatObservationWithReflection = formatObservationWithReflection;
|
|
16
|
+
exports.suggestNextAction = suggestNextAction;
|
|
16
17
|
const ERROR_SIGNATURES = [
|
|
17
18
|
{ pattern: /unknown tool|未知工具|tool.*not found|is not a function/i, cls: 'tool_not_found', label: '工具不存在' },
|
|
18
19
|
{ pattern: /PreToolUse 拒绝|Harness.*拒绝|permission|not allowed|denied/i, cls: 'permission_denied', label: '权限拒绝' },
|
|
19
20
|
{ pattern: /ECONNREFUSED|ECONNRESET|ETIMEDOUT|ENOTFOUND|network|connect.*fail|fetch.*fail/i, cls: 'network_error', label: '网络错误' },
|
|
20
21
|
{ pattern: /timeout|timed out/i, cls: 'timeout', label: '执行超时' },
|
|
21
|
-
|
|
22
|
+
// 2026-10-01: ENOENT 单独一条 —— 原先和 bad argument 挤在一起 ⇒ 路径不存在被报成"参数错误" ✗
|
|
23
|
+
// (用户实测: list_files '~/.bolloon' ⇒ ENOENT, 却被标成"参数错误", 真原因被标签盖住)
|
|
24
|
+
{ pattern: /ENOENT|no such file|does not exist|not exist/i, cls: 'bad_input', label: '路径/文件不存在' },
|
|
25
|
+
{ pattern: /invalid path|bad argument|ERR_INVALID|参数/i, cls: 'bad_input', label: '参数错误' },
|
|
26
|
+
// 2026-10-01: **策略拒绝**必须单列且排在前面 —— 否则长文本里的无关关键词会把它误判成鉴权类 ⇒ 熔断 ✗
|
|
27
|
+
// (现场: 「路径被护栏拒…」被当成"鉴权类错误不重试" ⇒ 停在 needs_human ✗)
|
|
28
|
+
{ pattern: /路径被护栏拒|不在白名单|被护栏拒|denied by policy/i, cls: 'policy', label: '策略拒绝(护栏)' },
|
|
22
29
|
{ pattern: /401|403|quota|rate limit|API key|unauthorized|authentication/i, cls: 'api_error', label: 'API 认证错误' },
|
|
23
30
|
];
|
|
24
31
|
function classifyError(errorMsg) {
|
|
25
32
|
if (!errorMsg)
|
|
26
|
-
return { cls: 'unknown', label: '
|
|
33
|
+
return { cls: 'unknown', label: '', severity: 'medium', recoverable: true };
|
|
27
34
|
for (const sig of ERROR_SIGNATURES) {
|
|
28
35
|
if (sig.pattern.test(errorMsg)) {
|
|
29
36
|
const severity = sig.cls === 'api_error' ? 'high'
|
|
@@ -36,21 +43,44 @@ function classifyError(errorMsg) {
|
|
|
36
43
|
return { cls: sig.cls, label: sig.label, severity, recoverable };
|
|
37
44
|
}
|
|
38
45
|
}
|
|
39
|
-
return { cls: 'unknown', label: '
|
|
46
|
+
return { cls: 'unknown', label: '', severity: 'medium', recoverable: true };
|
|
40
47
|
}
|
|
41
48
|
function buildObservation(tool, args, result) {
|
|
42
49
|
const obs = { tool, args, success: result.success, summary: '' };
|
|
43
50
|
if (result.success) {
|
|
44
51
|
const output = result.output || '(无输出)';
|
|
45
52
|
obs.output = output;
|
|
46
|
-
|
|
53
|
+
// 2026-10-01 (用户: 「完整看一下这个反思模式,为啥一直出错?」):
|
|
54
|
+
// 原来只报 **字节数** ✗(`✅ terminal 成功 (1234B)`) ⇒ 模型不知道**发生了什么**, 只能猜 ⇒ 猜错 ✓
|
|
55
|
+
// 现在附一段**内容预览**(压平换行 ✓ 头部 160 字符 ✓) ⇒ 观察里就有事实 ✓。
|
|
56
|
+
const preview = output.replace(/\s+/g, ' ').trim().slice(0, 160);
|
|
57
|
+
obs.summary = `✅ ${tool} 成功 (${output.length}B)${preview ? `: ${preview}` : ''}`;
|
|
47
58
|
}
|
|
48
59
|
else {
|
|
49
60
|
const errMsg = result.error || '未知失败';
|
|
50
61
|
const cls = classifyError(errMsg);
|
|
51
62
|
obs.errorClass = cls.cls;
|
|
52
63
|
obs.errorLabel = cls.label;
|
|
53
|
-
|
|
64
|
+
// ② 2026-10-01: 退出码非零时**先不写"失败"** ✓(钩子/子命令常以非零退出而动作已生效);
|
|
65
|
+
// ③ 并附**输出尾部** —— 原来只截错误文本前 120 字符 ✗, 而 lefthook 那类"到底干了什么"恰在**尾部** ⇒ 必然误判 ✗
|
|
66
|
+
const __exit0 = Number((/exit\s+(\d+)/i.exec(result.error || '') || [])[1]);
|
|
67
|
+
const __tail = String(result.output || '').replace(/\s+/g, ' ').trim().slice(-200);
|
|
68
|
+
const __head = __exit0 && Number.isFinite(__exit0) && __exit0 !== 0 && tool === 'terminal'
|
|
69
|
+
? `⚠️ ${tool} 退出码 ${__exit0}(结果待核, 不等于失败)`
|
|
70
|
+
: `❌ ${tool} 失败: ${cls.label}`;
|
|
71
|
+
obs.summary = `${__head} — ${errMsg.slice(0, 160)}${__tail ? ` · 输出尾部: ${__tail}` : ''}`;
|
|
72
|
+
// 2026-10-01 (用户: 「反思还是有错误」—— 现场实测: git 提交**已经成功**, 但那一行却写着
|
|
73
|
+
// `Reflection: ❌ terminal 失败: — exit 1` ✗, 把 lefthook 的钩子退出码当成了任务失败 ✗):
|
|
74
|
+
// **退出码非零 ≠ 任务失败** ✓ —— 用命令核一次事实再下结论, 别直接写"失败"。
|
|
75
|
+
const __exit = Number((/exit\s+(\d+)/i.exec(errMsg) || [])[1]);
|
|
76
|
+
if (tool === 'terminal' && Number.isFinite(__exit) && __exit !== 0) {
|
|
77
|
+
obs.summary = `${obs.summary} · ⚠️ 退出码 ${__exit} **不等于任务失败**(钩子/子命令常以非零退出而动作已生效)`
|
|
78
|
+
+ ` —— 先核事实(git status / git log / read_file 看结果)再下结论, 别写成"失败"`;
|
|
79
|
+
}
|
|
80
|
+
// 2026-10-01 (优化 #6): 顺手给出"下一步该干什么" —— 光有标签模型会原地重试同一条命令
|
|
81
|
+
const __advice = suggestNextAction(`${cls.label} ${errMsg}`);
|
|
82
|
+
if (__advice)
|
|
83
|
+
obs.summary = `${obs.summary} · ${__advice}`;
|
|
54
84
|
}
|
|
55
85
|
return obs;
|
|
56
86
|
}
|
|
@@ -87,10 +117,24 @@ const ERROR_TO_STRATEGIES = {
|
|
|
87
117
|
{ action: 'retry', reason: '错误类型不确定', detail: '换个方式或参数再试一次' },
|
|
88
118
|
{ action: 'change_tool', reason: '原方法不可行', detail: '尝试用其他工具组合达成目标' },
|
|
89
119
|
],
|
|
120
|
+
// 2026-10-01: 护栏拒绝 ⇒ **别无脑重试同一条路径** ✓: 先改到允许的根下, 或问用户要目标位置 ✓
|
|
121
|
+
policy: [
|
|
122
|
+
{ action: 'change_params', reason: '策略拒绝(护栏)', detail: '别原样重试: ① 换到白名单内的根(docs/** · src/web/** · *.md · ~/.bolloon/**); ② 或先问用户"这个文件该放哪"; ③ 若是工具/命令不在白名单, 换等价的白名单内工具' },
|
|
123
|
+
{ action: 'change_tool', reason: '换等价工具', detail: '写文件用 write_file 到允许路径; 命令换白名单内的等价命令' },
|
|
124
|
+
],
|
|
90
125
|
};
|
|
91
126
|
function buildReflection(toolName, errorMsg, errorCount, sameToolFailCount) {
|
|
92
127
|
if (!errorMsg)
|
|
93
128
|
return ERROR_TO_STRATEGIES.unknown.slice(0, 1);
|
|
129
|
+
// ④ 2026-10-01: 退出码非零 ⇒ **先核事实**, 而不是按规则表给重试 ✗
|
|
130
|
+
// (实测: git 提交已生效却因钩子退出码非零被判失败 ⇒ 模型又去重试/绕道, 越走越偏 ✗)
|
|
131
|
+
const __exitCode = Number((/exit\s+(\d+)/i.exec(errorMsg) || [])[1]);
|
|
132
|
+
if (Number.isFinite(__exitCode) && __exitCode !== 0) {
|
|
133
|
+
return [
|
|
134
|
+
{ action: 'change_params', reason: `退出码 ${__exitCode} —— 结果待核(不等于失败)`, detail: '先用 git status / git log / read_file 核事实, 确认到底成没成, 再决定下一步; 别直接当失败重试' },
|
|
135
|
+
{ action: 'change_tool', reason: '换法再试', detail: '若确未成功: 换参数/换工具/拆小步再执行' },
|
|
136
|
+
];
|
|
137
|
+
}
|
|
94
138
|
const cls = classifyError(errorMsg);
|
|
95
139
|
// 阶梯升档: 根据连续失败次数选择更激进的策略
|
|
96
140
|
if (sameToolFailCount >= 3) {
|
|
@@ -122,4 +166,23 @@ function formatObservationWithReflection(obs, reflection) {
|
|
|
122
166
|
}
|
|
123
167
|
return lines.join('\n');
|
|
124
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* 错误 ⇒ **下一步动作建议** (2026-10-01 优化 #6)。
|
|
171
|
+
* 光有标签(路径不存在/参数错误)还不够 —— 模型得知道**接着该干什么**, 否则就原地重试同一条命令。
|
|
172
|
+
*/
|
|
173
|
+
function suggestNextAction(labelOrMsg) {
|
|
174
|
+
const s = String(labelOrMsg || '');
|
|
175
|
+
const rules = [
|
|
176
|
+
[/路径|不存在|ENOENT/i, '用 list_files / glob_files 看**真实**路径再重试(注意 ~ 展开与工作目录)'],
|
|
177
|
+
[/超时|timeout/i, '拆小(分成多次/加 limit)或改用后台执行, 别原样重发'],
|
|
178
|
+
[/权限|EACCES|EPERM/i, '换可写目录(如工作区或 /tmp), 不要动系统目录'],
|
|
179
|
+
[/参数|invalid|missing|必填/i, '对照工具描述里的必填项补齐参数; 不确定就先 list_tools 看schema'],
|
|
180
|
+
[/网络|ECONN|fetch failed|不可达/i, '确认本机服务/代理可达; 自家域走 --resolve + --noproxy'],
|
|
181
|
+
[/缓冲区|maxBuffer|输出过大/i, '加过滤(只取需要的行)或改写到文件再按需读取'],
|
|
182
|
+
];
|
|
183
|
+
for (const [re, advice] of rules)
|
|
184
|
+
if (re.test(s))
|
|
185
|
+
return `下一步: ${advice}`;
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
125
188
|
//# sourceMappingURL=error-classifier.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-classifier.js","sourceRoot":"","sources":["../../../src/agents/error-classifier.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG
|
|
1
|
+
{"version":3,"file":"error-classifier.js","sourceRoot":"","sources":["../../../src/agents/error-classifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DEFAULT_MIN_INTERVAL_MS = exports.EXPERIENCE_DIR_NAME = void 0;
|
|
7
|
+
exports.experienceDir = experienceDir;
|
|
8
|
+
exports.sanitizeClass = sanitizeClass;
|
|
9
|
+
exports.sanitizeTitle = sanitizeTitle;
|
|
10
|
+
exports.shouldReview = shouldReview;
|
|
11
|
+
exports.shouldReviewTask = shouldReviewTask;
|
|
12
|
+
exports.listExperienceIndex = listExperienceIndex;
|
|
13
|
+
exports.buildReviewPrompt = buildReviewPrompt;
|
|
14
|
+
exports.parseReviewDecision = parseReviewDecision;
|
|
15
|
+
exports.applyExperience = applyExperience;
|
|
16
|
+
exports.runExperienceReview = runExperienceReview;
|
|
17
|
+
/**
|
|
18
|
+
* 回合后自审 → 沉淀可复用经验 (2026-10-01, 用户「落实」)。
|
|
19
|
+
*
|
|
20
|
+
* 为什么: 智能体目前只在**回合内**反应 —— 做完就完了, 经验不落库。飞轮目标里"结束后沉淀可复用经验"
|
|
21
|
+
* 是唯一还没动工的一段。做法学的是运行时那套**回合后自审**的机制, 约束照搬:
|
|
22
|
+
* · 只写**沉淀库**, **绝不碰主对话 / prompt 缓存**(所以纯旁路, 不影响用户看到的内容);
|
|
23
|
+
* · **不阻塞主回合**: 调用方 fire-and-forget, 本模块自己吞掉所有异常;
|
|
24
|
+
* · **工具白名单**: v1 连工具都不用 —— 只把"本回合摘要 + 现有库目录"喂给模型, 要它回一段 JSON;
|
|
25
|
+
* · **fail-open**: 节流/配置读不出来时**放行**(宁可偶尔多审一次, 不要因配置坏掉就永久不审);
|
|
26
|
+
* · **绝不编造**: 没有值得记的就回 none, 那就什么都不写(写空是正常结果, 不是失败)。
|
|
27
|
+
* 库的形态(反囤积): `~/.bolloon/experience/<类>.md`, **类级**文件 + 条目标题去重 ——
|
|
28
|
+
* 「同一教训学两次 = 改那一条, 不是加一条」。
|
|
29
|
+
*/
|
|
30
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
31
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
32
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
33
|
+
exports.EXPERIENCE_DIR_NAME = 'experience';
|
|
34
|
+
/** 同一个 agent 两次自审的最小间隔 (默认 10 分钟; 可用 BOLLOON_EXPERIENCE_REVIEW_MIN_MS 覆盖) */
|
|
35
|
+
exports.DEFAULT_MIN_INTERVAL_MS = 10 * 60 * 1000;
|
|
36
|
+
function experienceDir(home = node_os_1.default.homedir()) {
|
|
37
|
+
return node_path_1.default.join(home, '.bolloon', exports.EXPERIENCE_DIR_NAME);
|
|
38
|
+
}
|
|
39
|
+
/** 类名净化 (只允许安全字符; 空 ⇒ 'general') */
|
|
40
|
+
function sanitizeClass(k) {
|
|
41
|
+
const s = String(k ?? '').trim().toLowerCase().replace(/[^a-z0-9_.-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
42
|
+
return s || 'general';
|
|
43
|
+
}
|
|
44
|
+
/** 条目标题净化 (空 ⇒ 拒绝写: 没有标题的沉淀无法去重, 宁可不要) */
|
|
45
|
+
function sanitizeTitle(t) {
|
|
46
|
+
return String(t ?? '').trim().replace(/\s+/g, ' ').slice(0, 120);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 节流判据 (纯函数)。**fail-open**: 时间戳坏了/缺失 ⇒ 放行。
|
|
50
|
+
* @param lastAtMs 上一次自审时间 (0/undefined = 从未)
|
|
51
|
+
*/
|
|
52
|
+
function shouldReview(nowMs, lastAtMs, minIntervalMs = exports.DEFAULT_MIN_INTERVAL_MS) {
|
|
53
|
+
if (!Number.isFinite(nowMs) || nowMs <= 0)
|
|
54
|
+
return true; // 时钟坏了 ⇒ 放行 (fail-open)
|
|
55
|
+
if (!lastAtMs || !Number.isFinite(lastAtMs) || lastAtMs <= 0)
|
|
56
|
+
return true; // 从未审过 ⇒ 放行
|
|
57
|
+
if (!Number.isFinite(minIntervalMs) || minIntervalMs <= 0)
|
|
58
|
+
return true; // 配置坏了 ⇒ 放行 (fail-open)
|
|
59
|
+
return nowMs - lastAtMs >= minIntervalMs;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* **每次任务都复盘** (2026-10-01, 用户: 「自动每次做完任务都要总结经验」)。
|
|
63
|
+
* 与 `shouldReview` 的区别: 那个是"每 10 分钟最多一次" ✗ —— 会漏掉"做完一个任务"这种**事件** ✓;
|
|
64
|
+
* 这条按**任务签名**判: 签名变了(换任务了) ⇒ **立刻复盘** ✓; 同一任务重复 ⇒ 才回到节流 ✓。
|
|
65
|
+
* (成本控制: 只在"这一回合真的干了活"时才算签名 —— 由调用方保证 ✓; 且复盘是 fire-and-forget ✓。)
|
|
66
|
+
*/
|
|
67
|
+
function shouldReviewTask(nowMs, lastAtMs, lastSig, sig, minIntervalMs = exports.DEFAULT_MIN_INTERVAL_MS) {
|
|
68
|
+
if (sig && sig !== lastSig)
|
|
69
|
+
return true; // ★ 换了任务 ⇒ 立刻复盘
|
|
70
|
+
return shouldReview(nowMs, lastAtMs, minIntervalMs); // 同一任务 ⇒ 仍按节流, 免得反复审同一段
|
|
71
|
+
}
|
|
72
|
+
/** 现有库的目录 (喂给模型, 让它知道"已经有哪些类/条目", 从而倾向于改而不是加) */
|
|
73
|
+
function listExperienceIndex(home = node_os_1.default.homedir()) {
|
|
74
|
+
const dir = experienceDir(home);
|
|
75
|
+
try {
|
|
76
|
+
const files = node_fs_1.default.readdirSync(dir).filter((f) => f.endsWith('.md'));
|
|
77
|
+
if (!files.length)
|
|
78
|
+
return '(沉淀库还是空的)';
|
|
79
|
+
return files.slice(0, 30).map((f) => {
|
|
80
|
+
const body = node_fs_1.default.readFileSync(node_path_1.default.join(dir, f), 'utf-8');
|
|
81
|
+
const titles = [...body.matchAll(/^##\s+(.+)$/gm)].map((m) => m[1].trim()).slice(0, 20);
|
|
82
|
+
return `- ${f} (${titles.length} 条)\n${titles.map((t) => ` · ${t}`).join('\n')}`;
|
|
83
|
+
}).join('\n');
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return '(沉淀库还是空的)';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* 自审提示词 (纯函数)。**反囤积**规矩逐条写进去 —— 这是"库会变干净还是变垃圾堆"的分水岭。
|
|
91
|
+
*/
|
|
92
|
+
function buildReviewPrompt(turnSummary, existingIndex) {
|
|
93
|
+
return [
|
|
94
|
+
'你在做**回合后自审**: 判断上面这一回合里有没有值得沉淀成"可复用经验"的东西。',
|
|
95
|
+
'',
|
|
96
|
+
'已经沉淀过的(先看这里, 别重复):',
|
|
97
|
+
existingIndex,
|
|
98
|
+
'',
|
|
99
|
+
'这一回合发生了什么:',
|
|
100
|
+
'"""',
|
|
101
|
+
String(turnSummary || '').slice(0, 6000),
|
|
102
|
+
'"""',
|
|
103
|
+
'',
|
|
104
|
+
'规矩(按这个判):',
|
|
105
|
+
'1. **同一教训学两次 = 一条规矩**: 若上面已有同类条目 ⇒ 用同一个 title, 表示"更新那一条"; 否则才算新条目。',
|
|
106
|
+
'2. 只沉淀**可复用**的: 类级规律 · 踩过的坑与判据 · 用户对做法/风格的纠正 · 稳定的环境事实。',
|
|
107
|
+
'3. **不要**沉淀: 本次事件叙述 · 版本号/日期流水 · 一次性任务进度 · 环境依赖型失败(缺二进制、首次安装报错)。',
|
|
108
|
+
'4. 没有值得记的就回 none —— **写空是正常结果**, 不是失败; 编一条凑数比不写更糟。',
|
|
109
|
+
'5. 内容要短: title ≤ 60 字, body ≤ 400 字, body 写"规矩 + 为什么", 不写过程。',
|
|
110
|
+
'',
|
|
111
|
+
'只回一个 JSON(不要别的文字):',
|
|
112
|
+
'{"action":"none"} 或 {"action":"write","class":"类名(小写-连字符)","title":"一句话标题","body":"规矩与理由"}',
|
|
113
|
+
].join('\n');
|
|
114
|
+
}
|
|
115
|
+
/** 容错解析模型回的那段 JSON (纯函数)。任何异常/不完整 ⇒ none (绝不半写) */
|
|
116
|
+
function parseReviewDecision(text) {
|
|
117
|
+
const raw = String(text || '');
|
|
118
|
+
const m = raw.match(/\{[\s\S]*\}/);
|
|
119
|
+
// 2026-10-01 (用户: 「卡在这就不动了」现场: 日志里连着出现 "审了但没写: 没找到 JSON" ✗ ⇒ 复盘白跑):
|
|
120
|
+
// 模型 Often 不给严格 JSON ⇒ 原来直接当"没写" ✗。现在: ① 没花括号时**宽解析**(从文本里抠字段 ✓);
|
|
121
|
+
// ② 仍解析不出 ⇒ 把**原文头部**记进日志(才能知道它到底回了什么 ✓, 不再瞎 ✗)。
|
|
122
|
+
if (!m) {
|
|
123
|
+
const act = /(?:^|["'\s])action["'\s::]*["']?([a-z_]+)/i.exec(raw)?.[1]
|
|
124
|
+
|| (/\bwrite\b/i.test(raw) ? 'write' : (/\bnone\b|不用|无需/i.test(raw) ? 'none' : ''));
|
|
125
|
+
if (act === 'write') {
|
|
126
|
+
const title = /(?:title|标题)["'\s::]*["']?([^"'\n]{2,80})/i.exec(raw)?.[1]?.trim() || '未命名教训';
|
|
127
|
+
const body = /(?:body|内容|教训)["'\s::]*["']?([\s\S]{20,1200}?)(?:["'\n]|$)/i.exec(raw)?.[1]?.trim() || raw.slice(0, 400);
|
|
128
|
+
if (body.length >= 20)
|
|
129
|
+
return { action: 'write', file: 'lessons.md', title, body, klass: 'general', reason: '宽解析' };
|
|
130
|
+
}
|
|
131
|
+
if (act === 'none')
|
|
132
|
+
return { action: 'none', reason: '模型判定无需记录(宽解析)' };
|
|
133
|
+
return { action: 'none', reason: `没找到 JSON(模型原文头部: ${raw.replace(/\s+/g, ' ').slice(0, 120)})` };
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
const j = JSON.parse(m[0]);
|
|
137
|
+
if (String(j?.action || '').toLowerCase() !== 'write')
|
|
138
|
+
return { action: 'none' };
|
|
139
|
+
const title = sanitizeTitle(j.title);
|
|
140
|
+
const body = String(j?.body ?? '').trim().slice(0, 800);
|
|
141
|
+
if (!title || !body)
|
|
142
|
+
return { action: 'none', reason: 'title/body 不全' }; // 宁可丢, 不写半条
|
|
143
|
+
return { action: 'write', klass: sanitizeClass(j.class), title, body };
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return { action: 'none', reason: 'JSON 解析失败' };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* 落库: **同 title 就更新那一条**(hit+1, 替换正文), 否则追加。
|
|
151
|
+
* @returns 实际动作, 便于测试与日志
|
|
152
|
+
*/
|
|
153
|
+
function applyExperience(d, home = node_os_1.default.homedir(), nowIso = new Date().toISOString()) {
|
|
154
|
+
if (d.action !== 'write' || !d.title || !d.body)
|
|
155
|
+
return { wrote: false };
|
|
156
|
+
const dir = experienceDir(home);
|
|
157
|
+
const file = node_path_1.default.join(dir, `${sanitizeClass(d.klass)}.md`);
|
|
158
|
+
try {
|
|
159
|
+
node_fs_1.default.mkdirSync(dir, { recursive: true });
|
|
160
|
+
let cur = '';
|
|
161
|
+
try {
|
|
162
|
+
cur = node_fs_1.default.readFileSync(file, 'utf-8');
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
cur = '';
|
|
166
|
+
}
|
|
167
|
+
const header = cur.trim() ? cur : `# 可复用经验 · ${sanitizeClass(d.klass)}\n\n> 回合后自审沉淀; 同一教训只留一条(重复出现则更新) · 每条带出现次数与末次时间\n`;
|
|
168
|
+
// 2026-10-01 修: 上一版用正则 `...(?=^## |\Z)` 找条目边界 —— **JS 不支持 \Z**, 它被当成字面量 Z,
|
|
169
|
+
// 而条目的时间戳正好以 Z 结尾 ⇒ 从那儿截断, 更新后留下旧正文残渣(实跑看到: "Z -->" + 旧正文)✗。
|
|
170
|
+
// 改成**按 `## ` 切块再拼** —— 不依赖任何正则边界语义。
|
|
171
|
+
const blockOf = (t, body, hits) => `## ${t}\n<!-- hit:${hits} last:${nowIso} -->\n${body}\n`;
|
|
172
|
+
const parts = header.split(/\n(?=## )/);
|
|
173
|
+
const head = parts[0].trimEnd();
|
|
174
|
+
const entries = parts.slice(1).filter((x) => x.trim().startsWith('## '));
|
|
175
|
+
const titleOf = (blk) => (blk.split('\n')[0] || '').replace(/^##\s*/, '').trim();
|
|
176
|
+
const hitsOf = (blk) => Number((blk.match(/<!--\s*hit:(\d+)/) || [])[1] || 0);
|
|
177
|
+
const idx = entries.findIndex((blk) => titleOf(blk) === d.title);
|
|
178
|
+
if (idx >= 0) {
|
|
179
|
+
const n = hitsOf(entries[idx]) + 1; // 上一次次数 + 本次 (更新 ⇒ 至少 2)
|
|
180
|
+
entries[idx] = blockOf(d.title, d.body, n);
|
|
181
|
+
node_fs_1.default.writeFileSync(file, head + '\n\n' + entries.map((x) => x.trimEnd()).join('\n\n') + '\n', 'utf-8');
|
|
182
|
+
return { wrote: true, file, updated: true };
|
|
183
|
+
}
|
|
184
|
+
entries.push(blockOf(d.title, d.body, 1));
|
|
185
|
+
node_fs_1.default.writeFileSync(file, head + '\n\n' + entries.map((x) => x.trimEnd()).join('\n\n') + '\n', 'utf-8');
|
|
186
|
+
return { wrote: true, file, updated: false };
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return { wrote: false };
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function escapeRe(s) {
|
|
193
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* 自审编排 (纯旁路, **绝不抛错**)。
|
|
197
|
+
*
|
|
198
|
+
* `chat` 由调用方注入 (便于门测; 生产用现成的单次补全)。整段逻辑:
|
|
199
|
+
* 节流 ⇒ 取库目录 ⇒ 拼提示 ⇒ 一次补全 ⇒ 解析 ⇒ 落库。
|
|
200
|
+
* 任何一步失败都只是"这次没沉淀", 不影响主回合 —— 调用方 fire-and-forget 即可。
|
|
201
|
+
*/
|
|
202
|
+
async function runExperienceReview(opts) {
|
|
203
|
+
const home = opts.home || node_os_1.default.homedir();
|
|
204
|
+
const now = opts.nowMs ?? Date.now();
|
|
205
|
+
const log = opts.log || (() => { });
|
|
206
|
+
try {
|
|
207
|
+
if (!opts.force && !shouldReview(now, opts.lastAtMs, opts.minIntervalMs))
|
|
208
|
+
return { reviewed: false, applied: false, reason: 'throttled' };
|
|
209
|
+
const summary = String(opts.turnSummary || '').trim();
|
|
210
|
+
if (summary.length < 40)
|
|
211
|
+
return { reviewed: false, applied: false, reason: '回合太短, 不值得审' };
|
|
212
|
+
const prompt = buildReviewPrompt(summary, listExperienceIndex(home));
|
|
213
|
+
// 2026-10-01 (用户: 「卡在这就不动了」+ 复盘日志里 "开始" 之后没有下文 ✗):
|
|
214
|
+
// review 的 chat 调用**原来没有超时** ⇒ 一旦 provider 慢/挂, 这条就永远不返回 ✓。
|
|
215
|
+
// 现在给**硬超时**(默认 25s, 可配 ✓), 超时如实记一条并放行 ✓ —— 复盘是锦上添花, 绝不许拖住主流程 ✓。
|
|
216
|
+
const chatTimeoutMs = Number(process.env.BOLLOON_REVIEW_TIMEOUT_MS) || 25_000;
|
|
217
|
+
const raw = await Promise.race([
|
|
218
|
+
opts.chat(prompt),
|
|
219
|
+
new Promise((resolve) => setTimeout(() => resolve('__TIMEOUT__'), chatTimeoutMs)),
|
|
220
|
+
]);
|
|
221
|
+
if (raw === '__TIMEOUT__') {
|
|
222
|
+
log(`审查超时(${chatTimeoutMs}ms) —— 本轮不复盘, 已放行(绝不拖住主流程)`);
|
|
223
|
+
return { reviewed: false, applied: false, reason: 'chat-timeout' };
|
|
224
|
+
}
|
|
225
|
+
const d = parseReviewDecision(raw);
|
|
226
|
+
if (d.action !== 'write')
|
|
227
|
+
return { reviewed: true, applied: false, reason: d.reason || 'none' };
|
|
228
|
+
const r = applyExperience(d, home, new Date(now).toISOString());
|
|
229
|
+
if (r.wrote) {
|
|
230
|
+
log(`[experience] ${r.updated ? '更新' : '新增'} ${node_path_1.default.basename(r.file)} · ${d.title}`);
|
|
231
|
+
try {
|
|
232
|
+
opts.onLesson?.({ title: d.title, body: d.body, klass: d.klass });
|
|
233
|
+
}
|
|
234
|
+
catch { /* 回调绝不影响落库 */ }
|
|
235
|
+
}
|
|
236
|
+
return { reviewed: true, applied: r.wrote, file: r.wrote ? r.file : undefined, updated: r.wrote ? r.updated : undefined };
|
|
237
|
+
}
|
|
238
|
+
catch (e) {
|
|
239
|
+
log(`[experience] 自审跳过(非致命): ${String(e?.message || e).slice(0, 120)}`);
|
|
240
|
+
return { reviewed: false, applied: false, reason: 'error' };
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=experience-review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"experience-review.js","sourceRoot":"","sources":["../../../src/agents/experience-review.ts"],"names":[],"mappings":"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"}
|