@bolloon/bolloon-agent 0.5.4 → 0.6.0
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 +43 -32
- package/dist/agents/pi-sdk-session-factory.js +36 -3
- package/dist/agents/pi-sdk-session-manager.js +72 -3
- package/dist/agents/pi-sdk-tools.js +201 -32
- package/dist/agents/pi-sdk.js +798 -206
- package/dist/agents/plan-store.js +36 -0
- package/dist/agents/process-runner.js +64 -2
- package/dist/agents/run-context.js +20 -0
- package/dist/agents/runner-resolver.js +2 -1
- 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/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 +153 -6
- package/dist/cli/input-paste.js +218 -0
- package/dist/cli/loading-tui.js +3 -1
- package/dist/cli/log-gate.js +7 -0
- package/dist/cli/mention-data.js +30 -0
- package/dist/cli/reasoning-view.js +103 -0
- package/dist/cli/startup-notice.js +25 -0
- package/dist/cli/status-segments.js +87 -2
- package/dist/cli/trace-line.js +54 -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 +43 -32
- package/dist/electron-build/agents/parse-tool-call.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk-session-factory.js +36 -3
- package/dist/electron-build/agents/pi-sdk-session-factory.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 +200 -31
- 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 +799 -206
- 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/run-context.js +24 -0
- package/dist/electron-build/agents/run-context.js.map +1 -0
- package/dist/electron-build/agents/runner-resolver.js +2 -1
- package/dist/electron-build/agents/runner-resolver.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/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/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/kernel/channel-actor.js +314 -0
- package/dist/electron-build/kernel/channel-actor.js.map +1 -0
- 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 +526 -95
- package/dist/kernel/channel-actor.js +297 -0
- package/dist/kernel/control.js +95 -0
- package/dist/kernel/gate-scan.js +1036 -0
- package/dist/kernel/model-runtime.js +472 -0
- package/dist/kernel/plan-channel-actor.js +298 -0
- package/dist/kernel/plan-constraint.js +165 -0
- package/dist/kernel/plan-deletion.js +32 -0
- package/dist/kernel/plan-modelruntime.js +60 -0
- package/dist/kernel/plan-runcontext.js +163 -0
- package/dist/kernel/plan.js +153 -0
- package/dist/kernel/roster.js +242 -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 +4 -2
- package/dist/web/mobile-core.js +7762 -6289
- package/dist/web/routes-mobile-tasks.js +2 -1
- package/dist/web/routes-tasks.js +5 -1
- package/dist/web/server-storage.js +26 -5
- package/dist/web/server.js +60 -18
- package/dist/web/sw.js +1 -1
- package/dist/web/ui/message-renderer.js +473 -29
- package/dist/web/ui/step-timeline.js +13 -12
- package/package.json +2 -1
- package/scripts/build-web.ts +74 -1
- package/dist/constraint-runtime/_archive_helper.d.ts +0 -1
- package/dist/constraint-runtime/_archive_helper.js +0 -9
- package/dist/constraint-runtime/assistant/index.d.ts +0 -11
- package/dist/constraint-runtime/assistant/index.js +0 -12
- package/dist/constraint-runtime/bootstrap/index.d.ts +0 -11
- package/dist/constraint-runtime/bootstrap/index.js +0 -12
- package/dist/constraint-runtime/bridge/index.d.ts +0 -11
- package/dist/constraint-runtime/bridge/index.js +0 -12
- package/dist/constraint-runtime/buddy/index.d.ts +0 -11
- package/dist/constraint-runtime/buddy/index.js +0 -12
- package/dist/constraint-runtime/cli/index.d.ts +0 -11
- package/dist/constraint-runtime/cli/index.js +0 -12
- package/dist/constraint-runtime/components/index.d.ts +0 -11
- package/dist/constraint-runtime/components/index.js +0 -12
- package/dist/constraint-runtime/constants/index.d.ts +0 -11
- package/dist/constraint-runtime/constants/index.js +0 -12
- package/dist/constraint-runtime/coordinator/index.d.ts +0 -11
- package/dist/constraint-runtime/coordinator/index.js +0 -12
- package/dist/constraint-runtime/cost_hook.d.ts +0 -2
- package/dist/constraint-runtime/cost_hook.js +0 -4
- package/dist/constraint-runtime/entrypoints/index.d.ts +0 -11
- package/dist/constraint-runtime/entrypoints/index.js +0 -12
- package/dist/constraint-runtime/execution_registry.d.ts +0 -20
- package/dist/constraint-runtime/execution_registry.js +0 -44
- package/dist/constraint-runtime/hooks/index.d.ts +0 -11
- package/dist/constraint-runtime/hooks/index.js +0 -12
- package/dist/constraint-runtime/ink.d.ts +0 -1
- package/dist/constraint-runtime/ink.js +0 -4
- package/dist/constraint-runtime/keybindings/index.d.ts +0 -11
- package/dist/constraint-runtime/keybindings/index.js +0 -12
- package/dist/constraint-runtime/memdir/index.d.ts +0 -11
- package/dist/constraint-runtime/memdir/index.js +0 -12
- package/dist/constraint-runtime/migrations/index.d.ts +0 -11
- package/dist/constraint-runtime/migrations/index.js +0 -12
- package/dist/constraint-runtime/moreright/index.d.ts +0 -11
- package/dist/constraint-runtime/moreright/index.js +0 -12
- package/dist/constraint-runtime/native_ts/index.d.ts +0 -11
- package/dist/constraint-runtime/native_ts/index.js +0 -12
- package/dist/constraint-runtime/output_styles/index.d.ts +0 -11
- package/dist/constraint-runtime/output_styles/index.js +0 -12
- package/dist/constraint-runtime/plugins/index.d.ts +0 -11
- package/dist/constraint-runtime/plugins/index.js +0 -12
- package/dist/constraint-runtime/port_manifest.d.ts +0 -7
- package/dist/constraint-runtime/port_manifest.js +0 -11
- package/dist/constraint-runtime/query.d.ts +0 -6
- package/dist/constraint-runtime/query.js +0 -1
- package/dist/constraint-runtime/remote/index.d.ts +0 -11
- package/dist/constraint-runtime/remote/index.js +0 -12
- package/dist/constraint-runtime/schemas/index.d.ts +0 -11
- package/dist/constraint-runtime/schemas/index.js +0 -12
- package/dist/constraint-runtime/screens/index.d.ts +0 -11
- package/dist/constraint-runtime/screens/index.js +0 -12
- package/dist/constraint-runtime/server/index.d.ts +0 -11
- package/dist/constraint-runtime/server/index.js +0 -12
- package/dist/constraint-runtime/services/index.d.ts +0 -11
- package/dist/constraint-runtime/services/index.js +0 -12
- package/dist/constraint-runtime/state/index.d.ts +0 -11
- package/dist/constraint-runtime/state/index.js +0 -12
- package/dist/constraint-runtime/system_init.d.ts +0 -1
- package/dist/constraint-runtime/system_init.js +0 -20
- package/dist/constraint-runtime/types/index.d.ts +0 -11
- package/dist/constraint-runtime/types/index.js +0 -12
- package/dist/constraint-runtime/upstream_proxy/index.d.ts +0 -11
- package/dist/constraint-runtime/upstream_proxy/index.js +0 -12
- package/dist/constraint-runtime/utils/index.d.ts +0 -11
- package/dist/constraint-runtime/utils/index.js +0 -12
- package/dist/constraint-runtime/vim/index.d.ts +0 -11
- package/dist/constraint-runtime/vim/index.js +0 -12
- package/dist/constraint-runtime/voice/index.d.ts +0 -11
- package/dist/constraint-runtime/voice/index.js +0 -12
|
@@ -0,0 +1,103 @@
|
|
|
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.STALE_AFTER_MS = void 0;
|
|
7
|
+
exports.usagePath = usagePath;
|
|
8
|
+
exports.bumpUsage = bumpUsage;
|
|
9
|
+
exports.markCreatedBy = markCreatedBy;
|
|
10
|
+
exports.setPinned = setPinned;
|
|
11
|
+
exports.lifecycleOf = lifecycleOf;
|
|
12
|
+
exports.usageSummary = usageSummary;
|
|
13
|
+
/**
|
|
14
|
+
* 技能用量遥测 + 生命周期 (2026-10-01)。
|
|
15
|
+
* 形态要点: 遥测放**旁挂文件** ✓ —— 绝不写进用户自己写的 SKILL.md ✗(那是用户的文件 ✓)。
|
|
16
|
+
* 生命周期: active → stale(久未用) → archived; `pinned` 可退出自动流转 ✓。
|
|
17
|
+
* 边界(如实): 本模块只**计算状态并给建议** ✓, **不自动移动/删除任何技能文件** ✗(那是破坏性动作, 交人决定 ✓)。
|
|
18
|
+
*/
|
|
19
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
20
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
21
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
22
|
+
function usagePath(home = node_os_1.default.homedir()) {
|
|
23
|
+
return node_path_1.default.join(home, '.bolloon', 'skills', '.usage.json');
|
|
24
|
+
}
|
|
25
|
+
function load(home) {
|
|
26
|
+
try {
|
|
27
|
+
const o = JSON.parse(node_fs_1.default.readFileSync(usagePath(home), 'utf-8'));
|
|
28
|
+
return o && typeof o === 'object' ? o : {};
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return {};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function save(m, home) {
|
|
35
|
+
try {
|
|
36
|
+
const p = usagePath(home);
|
|
37
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(p), { recursive: true });
|
|
38
|
+
node_fs_1.default.writeFileSync(p, JSON.stringify(m), { mode: 0o600 });
|
|
39
|
+
}
|
|
40
|
+
catch { /* 遥测失败不影响工具调用 */ }
|
|
41
|
+
}
|
|
42
|
+
/** 用了一次(读技能时调 ✓); 失败只吞 ✓ */
|
|
43
|
+
function bumpUsage(skill, home, now = Date.now()) {
|
|
44
|
+
try {
|
|
45
|
+
const m = load(home);
|
|
46
|
+
const cur = m[skill] || { count: 0, lastUsedAt: 0 };
|
|
47
|
+
m[skill] = { ...cur, count: cur.count + 1, lastUsedAt: now };
|
|
48
|
+
save(m, home);
|
|
49
|
+
}
|
|
50
|
+
catch { /* best-effort */ }
|
|
51
|
+
}
|
|
52
|
+
/** 记下创建者(自审 / 用户点名) */
|
|
53
|
+
function markCreatedBy(skill, by, home, now = Date.now()) {
|
|
54
|
+
try {
|
|
55
|
+
const m = load(home);
|
|
56
|
+
const cur = m[skill] || { count: 0, lastUsedAt: 0 };
|
|
57
|
+
m[skill] = { ...cur, createdBy: by, lastUsedAt: cur.lastUsedAt || now };
|
|
58
|
+
save(m, home);
|
|
59
|
+
}
|
|
60
|
+
catch { /* best-effort */ }
|
|
61
|
+
}
|
|
62
|
+
/** 钉住(退出自动流转) */
|
|
63
|
+
function setPinned(skill, pinned, home) {
|
|
64
|
+
try {
|
|
65
|
+
const m = load(home);
|
|
66
|
+
const cur = m[skill] || { count: 0, lastUsedAt: 0 };
|
|
67
|
+
m[skill] = { ...cur, pinned };
|
|
68
|
+
save(m, home);
|
|
69
|
+
}
|
|
70
|
+
catch { /* best-effort */ }
|
|
71
|
+
}
|
|
72
|
+
exports.STALE_AFTER_MS = 45 * 24 * 3600 * 1000; // 45 天没用 ⇒ stale
|
|
73
|
+
/** 生命周期(只算不给动作 ✓): active / stale / archived-eligible */
|
|
74
|
+
function lifecycleOf(skill, home, now = Date.now()) {
|
|
75
|
+
const u = load(home)[skill];
|
|
76
|
+
if (!u)
|
|
77
|
+
return { state: 'unknown', reason: '旁挂遥测里没有它(可能是导入/内置技能)', count: 0, pinned: false };
|
|
78
|
+
if (u.pinned)
|
|
79
|
+
return { state: 'active', reason: '已 pinned(退出自动流转)', count: u.count, pinned: true };
|
|
80
|
+
const idle = now - (u.lastUsedAt || 0);
|
|
81
|
+
return {
|
|
82
|
+
state: idle > exports.STALE_AFTER_MS ? 'stale' : 'active',
|
|
83
|
+
reason: idle > exports.STALE_AFTER_MS ? `已 ${Math.round(idle / 86400000)} 天未用(仅状态, 不会自动删 ✓)` : `近用过(${u.count} 次)`,
|
|
84
|
+
count: u.count,
|
|
85
|
+
pinned: false,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** 汇总(给面板/工具用 ✓) */
|
|
89
|
+
function usageSummary(home, now = Date.now()) {
|
|
90
|
+
const m = load(home);
|
|
91
|
+
let active = 0, stale = 0, pinned = 0;
|
|
92
|
+
for (const k of Object.keys(m)) {
|
|
93
|
+
const l = lifecycleOf(k, home, now);
|
|
94
|
+
if (l.pinned)
|
|
95
|
+
pinned++;
|
|
96
|
+
if (l.state === 'active')
|
|
97
|
+
active++;
|
|
98
|
+
else if (l.state === 'stale')
|
|
99
|
+
stale++;
|
|
100
|
+
}
|
|
101
|
+
return { total: Object.keys(m).length, active, stale, pinned };
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=skill-health.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-health.js","sourceRoot":"","sources":["../../../src/agents/skill-health.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,146 @@
|
|
|
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.runWithWriteOrigin = runWithWriteOrigin;
|
|
7
|
+
exports.currentWriteOrigin = currentWriteOrigin;
|
|
8
|
+
exports.isReviewWrite = isReviewWrite;
|
|
9
|
+
exports.ledgerPath = ledgerPath;
|
|
10
|
+
exports.blobsDir = blobsDir;
|
|
11
|
+
exports.captureSkillBefore = captureSkillBefore;
|
|
12
|
+
exports.appendLedger = appendLedger;
|
|
13
|
+
exports.readLedger = readLedger;
|
|
14
|
+
exports.isAgentCreated = isAgentCreated;
|
|
15
|
+
exports.reviewMayTouch = reviewMayTouch;
|
|
16
|
+
exports.rollbackEntry = rollbackEntry;
|
|
17
|
+
/**
|
|
18
|
+
* 技能账本 + 单条回滚 + 写来源隔离 (2026-10-01, 按"账本而非闸门"的形态做)。
|
|
19
|
+
*
|
|
20
|
+
* 三件事:
|
|
21
|
+
* ① **账本**: 每次技能变更追加一条 JSONL(前后清单 + 内容寻址备份)⇒ 可单条回滚 ✓。
|
|
22
|
+
* 放 JSONL 不放数据库: 耐久 · 可 grep · 数据库重置也不丢 ✓。
|
|
23
|
+
* ② **单条回滚**: 按 id 还原那一次变更; **唯独回滚是 fail-closed** ✓(安全快照拿不到就拒绝回滚,
|
|
24
|
+
* 绝不"半还原"✗); 其余全部吞错只记日志(账本坏掉不该挡住干活 ✓)。
|
|
25
|
+
* ③ **写来源隔离**: 用 AsyncLocalStorage 区分"用户点名要的写"(foreground ✓)与
|
|
26
|
+
* "自审/复盘产生的写"(review ✓)⇒ 自审**只许治理自己造出来的技能** ✓, 用户要的归用户 ✓。
|
|
27
|
+
*/
|
|
28
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
29
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
30
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
31
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
32
|
+
const node_async_hooks_1 = require("node:async_hooks");
|
|
33
|
+
const originStore = new node_async_hooks_1.AsyncLocalStorage();
|
|
34
|
+
/** 在"自审"上下文里跑一段(其余一律按 foreground ✓) */
|
|
35
|
+
function runWithWriteOrigin(origin, fn) {
|
|
36
|
+
return originStore.run({ origin }, fn);
|
|
37
|
+
}
|
|
38
|
+
function currentWriteOrigin() {
|
|
39
|
+
return originStore.getStore()?.origin ?? 'foreground';
|
|
40
|
+
}
|
|
41
|
+
function isReviewWrite() {
|
|
42
|
+
return currentWriteOrigin() === 'review';
|
|
43
|
+
}
|
|
44
|
+
function ledgerPath(home = node_os_1.default.homedir()) {
|
|
45
|
+
return node_path_1.default.join(home, '.bolloon', 'skills', '.skill-ledger.jsonl');
|
|
46
|
+
}
|
|
47
|
+
function blobsDir(home = node_os_1.default.homedir()) {
|
|
48
|
+
return node_path_1.default.join(home, '.bolloon', 'skills', '.ledger-blobs');
|
|
49
|
+
}
|
|
50
|
+
function sha256(text) {
|
|
51
|
+
return node_crypto_1.default.createHash('sha256').update(text, 'utf-8').digest('hex');
|
|
52
|
+
}
|
|
53
|
+
/** 把内容按哈希存进 blobs(已存在则复用 ✓ 天然去重) */
|
|
54
|
+
function putBlob(text, home) {
|
|
55
|
+
const h = sha256(text);
|
|
56
|
+
const p = node_path_1.default.join(blobsDir(home), h);
|
|
57
|
+
try {
|
|
58
|
+
if (!node_fs_1.default.existsSync(p)) {
|
|
59
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(p), { recursive: true });
|
|
60
|
+
node_fs_1.default.writeFileSync(p, text, { mode: 0o600 });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch { /* 备份失败 ⇒ 由调用方决定是否允许这次变更(见 captureBefore) */ }
|
|
64
|
+
return h;
|
|
65
|
+
}
|
|
66
|
+
/** 变更前快照: 返回 beforeSha(拿不到安全快照时返回 undefined ⇒ 回滚能力缺失, 调用方须知道 ✓) */
|
|
67
|
+
function captureSkillBefore(file, home) {
|
|
68
|
+
try {
|
|
69
|
+
const text = node_fs_1.default.readFileSync(file, 'utf-8');
|
|
70
|
+
return { beforeSha: putBlob(text, home), bytesBefore: text.length };
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return {}; // 文件不存在(新建)= 正常; 读不到 = 快照缺失
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** 追加一条账本(吞错只记日志 ✓ —— 账本坏掉不该挡住干活) */
|
|
77
|
+
function appendLedger(entry, home) {
|
|
78
|
+
const full = { id: entry.id || node_crypto_1.default.randomBytes(8).toString('hex'), at: entry.at ?? Date.now(), ...entry };
|
|
79
|
+
try {
|
|
80
|
+
const p = ledgerPath(home);
|
|
81
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(p), { recursive: true });
|
|
82
|
+
node_fs_1.default.appendFileSync(p, JSON.stringify(full) + '\n', { mode: 0o600 });
|
|
83
|
+
}
|
|
84
|
+
catch { /* telemetry, not a gate */ }
|
|
85
|
+
return full;
|
|
86
|
+
}
|
|
87
|
+
function readLedger(home) {
|
|
88
|
+
try {
|
|
89
|
+
const raw = node_fs_1.default.readFileSync(ledgerPath(home), 'utf-8');
|
|
90
|
+
const out = [];
|
|
91
|
+
for (const line of raw.split('\n')) {
|
|
92
|
+
if (!line.trim())
|
|
93
|
+
continue;
|
|
94
|
+
try {
|
|
95
|
+
out.push(JSON.parse(line));
|
|
96
|
+
}
|
|
97
|
+
catch { /* 坏行跳过 */ }
|
|
98
|
+
}
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** 这个技能是不是"自审造出来的"?(决定自审有没有资格改它 ✓) */
|
|
106
|
+
function isAgentCreated(skill, home) {
|
|
107
|
+
return readLedger(home).some((e) => e.skill === skill && e.tool === 'create_skill' && e.origin === 'review');
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* 自审写前的资格检查(只读判断, 不改任何东西 ✓):
|
|
111
|
+
* 自审只许动**自己造出来**的技能 ✓; 用户点名要的归用户 ✓。
|
|
112
|
+
*/
|
|
113
|
+
function reviewMayTouch(skill, home) {
|
|
114
|
+
if (!isReviewWrite())
|
|
115
|
+
return { ok: true, reason: 'foreground(用户点名要的写)' };
|
|
116
|
+
return isAgentCreated(skill, home)
|
|
117
|
+
? { ok: true, reason: 'review(该技能由自审创建, 可治理)' }
|
|
118
|
+
: { ok: false, reason: '该技能不是自审创建的(用户点名要的归用户)⇒ 自审只记候选, 不动它' };
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* 单条回滚: 把某次变更前的快照还原回去 ✓。
|
|
122
|
+
* **fail-closed**: 快照缺失/内容哈希对不上 ⇒ 拒绝(绝不半还原 ✗)。
|
|
123
|
+
*/
|
|
124
|
+
function rollbackEntry(id, home) {
|
|
125
|
+
const entry = readLedger(home).find((e) => e.id === id);
|
|
126
|
+
if (!entry)
|
|
127
|
+
return { ok: false, reason: `账本里没有 id=${id}` };
|
|
128
|
+
if (entry.tool === 'create_skill') {
|
|
129
|
+
// 创建类变更: 回滚 = 删掉那个文件(要用户明确同意 ⇒ 这里只报告, 不擅自删 ✗)
|
|
130
|
+
return { ok: false, reason: '该条是"新建技能"; 回滚等于删除文件 —— 需要你明确同意, 我不擅自删' };
|
|
131
|
+
}
|
|
132
|
+
if (!entry.beforeSha)
|
|
133
|
+
return { ok: false, reason: '该条没有变更前快照 ⇒ 拒绝回滚(fail-closed, 不做半还原)' };
|
|
134
|
+
try {
|
|
135
|
+
const blob = node_fs_1.default.readFileSync(node_path_1.default.join(blobsDir(home), entry.beforeSha), 'utf-8');
|
|
136
|
+
if (sha256(blob) !== entry.beforeSha)
|
|
137
|
+
return { ok: false, reason: '快照内容与哈希不符 ⇒ 拒绝回滚' };
|
|
138
|
+
node_fs_1.default.writeFileSync(entry.file, blob, { mode: 0o600 });
|
|
139
|
+
appendLedger({ origin: 'foreground', tool: 'rollback', skill: entry.skill, file: entry.file, beforeSha: entry.afterSha, afterSha: entry.beforeSha, note: `回到 ${entry.id}` }, home);
|
|
140
|
+
return { ok: true, reason: `已回滚到变更前(${entry.id})`, restored: entry.file };
|
|
141
|
+
}
|
|
142
|
+
catch (e) {
|
|
143
|
+
return { ok: false, reason: `回滚失败: ${String(e?.message || e).slice(0, 120)}` };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=skill-ledger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-ledger.js","sourceRoot":"","sources":["../../../src/agents/skill-ledger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_THRESHOLDS = exports.LoopStallState = exports.STUB_ARGS_PREVIEW_CHARS = exports.REPEAT_STUB_MIN_CHARS = exports.STUB_MIN_CHARS = exports.CYCLE_HISTORY = exports.MAX_CYCLE_PERIOD = exports.IDENTICAL_CALL_THRESHOLD = exports.FAILURE_TOLERANT_TOOLS = exports.REPEATABLE_SUFFIXES = exports.REPEATABLE_TOOLS = exports.MUTATING_TOOLS = exports.IDEMPOTENT_TOOLS = exports.CONSTANT_RESULT_TOOLS = void 0;
|
|
4
|
+
exports.canonicalToolArgs = canonicalToolArgs;
|
|
5
|
+
exports.toolCallSignature = toolCallSignature;
|
|
6
|
+
exports.resultFingerprint = resultFingerprint;
|
|
7
|
+
exports.isRepeatableTool = isRepeatableTool;
|
|
8
|
+
exports.observeToolCall = observeToolCall;
|
|
9
|
+
exports.detectCycle = detectCycle;
|
|
10
|
+
exports.toolMayHaveSideEffect = toolMayHaveSideEffect;
|
|
11
|
+
exports.defaultHardStopFor = defaultHardStopFor;
|
|
12
|
+
exports.newProgressBookkeeping = newProgressBookkeeping;
|
|
13
|
+
exports.trackProgress = trackProgress;
|
|
14
|
+
exports.batchHint = batchHint;
|
|
15
|
+
/**
|
|
16
|
+
* tool-loop-guard.ts — 工具调用的**停滞观测与温和引导** (2026-10-01)
|
|
17
|
+
*
|
|
18
|
+
* 为什么需要它 (用户原话: 「一个简单的任务居然花这么多次工具调用」):
|
|
19
|
+
* 原先的防重只有一条「同工具连续 5 次 ⇒ 注入一条提示, 然后重置计数」。实测模型无视提示后
|
|
20
|
+
* 完全没刹车 —— 同一个 get_identity 连调 10 次 ✗; 而且**只看工具名**, 连"同工具换参数的正常
|
|
21
|
+
* 迭代"(逐文件读)也会被误判。
|
|
22
|
+
* 反过来"硬停"也不行 (用户明确否决): 那是在症状上下手, 而且会堵掉正当的重复轮询。
|
|
23
|
+
*
|
|
24
|
+
* 所以这里采用**分类 + 温和合成**的口径:
|
|
25
|
+
* ① 工具先分类: 幂等读 / 有副作用 / 可重复轮询 (含命名约定 `_poll` / `_get_result`);
|
|
26
|
+
* ② 判据看**签名** (工具名 + 参数的规范 JSON 的哈希), 不只看工具名 ⇒ 换参数不误伤;
|
|
27
|
+
* ③ 命中停滞**不拒绝执行**, 而是产出一条**引导文本**追加在真结果后面 (保留模型的选择权);
|
|
28
|
+
* ④ 完全相同的**结果**从第 2 次起用**引用 stub** 替代 (省上下文; 且明确告诉模型"与上次相同");
|
|
29
|
+
* ⑤ 抓**循环** (A→B→A→B…, 周期 ≤4): 相邻重复计数会被交替调用清零, 只抓相邻的会漏;
|
|
30
|
+
* ⑥ 失败宽容类工具 (跑测试红了 / grep 空 / 页面超时) 的"失败"是正常工作输出, 不参与停滞判定。
|
|
31
|
+
*
|
|
32
|
+
* 纯逻辑, 不碰 IO / 不发请求 —— 便于单测与变异验证。
|
|
33
|
+
*/
|
|
34
|
+
const node_crypto_1 = require("node:crypto");
|
|
35
|
+
/** 幂等只读类: 同参数重复调用不会产生新信息 (但允许换参数继续用) */
|
|
36
|
+
/**
|
|
37
|
+
* **结果恒定**的工具 (2026-10-01): 同一会话内反复调, 返回**永远一样**, 且答案本身很短。
|
|
38
|
+
* 用户实测: `get_identity` 连调 3–4 次 ✗ —— 原有"引用 stub"有字符数门槛, 而它的输出才 ~90 字符
|
|
39
|
+
* ⇒ 门槛拦不住 ⇒ 模型看不出"再问也一样"。
|
|
40
|
+
* 规矩 (工具设置层, 不做硬刹车): 从**第 2 次**完全相同的调用起, 直接把结果换成一句标准答复,
|
|
41
|
+
* 明确告诉模型"答案就在上面那次返回里, 再调不会变" ⇒ 去掉它重复调的理由。
|
|
42
|
+
*/
|
|
43
|
+
exports.CONSTANT_RESULT_TOOLS = new Set([
|
|
44
|
+
'get_identity', 'bolloon_config_get', 'get_agent_info', 'whoami',
|
|
45
|
+
]);
|
|
46
|
+
exports.IDEMPOTENT_TOOLS = new Set([
|
|
47
|
+
'read_file', 'list_files', 'search_files', 'get_identity', 'get_operation_logs',
|
|
48
|
+
'list_remote_channels', 'list_context_layers', 'read_context_assets', 'get_balance', 'wallet_list',
|
|
49
|
+
'web_search', 'web_extract', 'check_inbox',
|
|
50
|
+
]);
|
|
51
|
+
/** 有副作用类: 重复调用可能是正当的 (例如再跑一条命令), 由"同签名同结果"判定停滞 */
|
|
52
|
+
exports.MUTATING_TOOLS = new Set([
|
|
53
|
+
'terminal', 'shell_exec', 'execute_code', 'write_file', 'patch', 'set_persona', 'memory',
|
|
54
|
+
'todo_list', 'send_message', 'send_to_remote_channel', 'delegate_task', 'create_group', 'set_wallet',
|
|
55
|
+
]);
|
|
56
|
+
/** 轮询类: 同参数重复**本来就正当** (等异步任务), 永不触发相同调用提示 */
|
|
57
|
+
exports.REPEATABLE_TOOLS = new Set(['process_manage', 'check_task_status']);
|
|
58
|
+
/** 生成/MCP 轮询工具的命名约定 (与上面同理) */
|
|
59
|
+
exports.REPEATABLE_SUFFIXES = ['_poll', '_get_result'];
|
|
60
|
+
/** 失败的"正常输出"类: 红了/空结果/超时属于工作产出, 不参与停滞判定 */
|
|
61
|
+
exports.FAILURE_TOLERANT_TOOLS = new Set(['terminal', 'shell_exec', 'execute_code', 'web_search', 'web_extract']);
|
|
62
|
+
/** 第几次**连续相同 (工具+参数+结果)** 触发引导 (3 = 容忍一次复核) */
|
|
63
|
+
exports.IDENTICAL_CALL_THRESHOLD = 3;
|
|
64
|
+
/** 抓循环时最长的周期 (A→B→A→B 的周期是 2) */
|
|
65
|
+
exports.MAX_CYCLE_PERIOD = 4;
|
|
66
|
+
/** 历史窗口: 够放最长的周期若干圈 + 余量 */
|
|
67
|
+
exports.CYCLE_HISTORY = 64;
|
|
68
|
+
/** 结果达到这个字符数才值得用 stub 替代 (太短本来就省不了多少) */
|
|
69
|
+
exports.STUB_MIN_CHARS = 512;
|
|
70
|
+
/**
|
|
71
|
+
* **同签名 + 同结果**的重复调用: 门槛降到这个值 (2026-10-01, 优化 #1 的收尾)。
|
|
72
|
+
* 为什么单独一条: 512 是"值不值得压缩"的门槛 ✗, 而"同一个调用又跑了一遍、结果一字不差"**本身就是浪费** ✓
|
|
73
|
+
* —— 实测 `get_identity` 输出才 ~90 字符 ⇒ 卡在 512 门外, 于是被连调 3–4 次 ✓。
|
|
74
|
+
* 现在: 同签名重复 且 结果一致 且 ≥64 字符 ⇒ 从第 2 次起换引用 stub ✓ (少于 64 字符的本来也占不了多少上下文, 放行 ✓)。
|
|
75
|
+
*/
|
|
76
|
+
exports.REPEAT_STUB_MIN_CHARS = 64;
|
|
77
|
+
/** stub 里保留的参数预览长度 (万一压缩把原结果挤掉, 还能看出当初调了什么) */
|
|
78
|
+
exports.STUB_ARGS_PREVIEW_CHARS = 120;
|
|
79
|
+
/** 参数的规范 JSON (键排序 + 紧凑分隔符) —— 让 {a,b} 与 {b,a} 同签名 */
|
|
80
|
+
function canonicalToolArgs(args) {
|
|
81
|
+
if (args === null || typeof args !== 'object' || Array.isArray(args))
|
|
82
|
+
return '';
|
|
83
|
+
const obj = args;
|
|
84
|
+
const keys = Object.keys(obj).sort();
|
|
85
|
+
const parts = keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`);
|
|
86
|
+
return `{${parts.join(',')}}`;
|
|
87
|
+
}
|
|
88
|
+
function stableStringify(v) {
|
|
89
|
+
if (v === null || typeof v !== 'object')
|
|
90
|
+
return JSON.stringify(v) ?? 'null';
|
|
91
|
+
if (Array.isArray(v))
|
|
92
|
+
return `[${v.map(stableStringify).join(',')}]`;
|
|
93
|
+
const o = v;
|
|
94
|
+
return `{${Object.keys(o).sort().map((k) => `${JSON.stringify(k)}:${stableStringify(o[k])}`).join(',')}}`;
|
|
95
|
+
}
|
|
96
|
+
/** 调用签名: 不可逆 (只留哈希), 不泄漏参数原文 */
|
|
97
|
+
function toolCallSignature(toolName, args) {
|
|
98
|
+
return (0, node_crypto_1.createHash)('sha256').update(`${toolName}\u0000${canonicalToolArgs(args)}`).digest('hex').slice(0, 16);
|
|
99
|
+
}
|
|
100
|
+
/** 结果指纹 (判"完全相同的返回") */
|
|
101
|
+
function resultFingerprint(resultText) {
|
|
102
|
+
return (0, node_crypto_1.createHash)('sha256').update(String(resultText ?? '')).digest('hex').slice(0, 16);
|
|
103
|
+
}
|
|
104
|
+
function isRepeatableTool(toolName) {
|
|
105
|
+
if (exports.REPEATABLE_TOOLS.has(toolName))
|
|
106
|
+
return true;
|
|
107
|
+
return exports.REPEATABLE_SUFFIXES.some((s) => toolName.endsWith(s));
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* 记录一次调用并给出观测。**纯函数式**: 调用方每次都要把上一次的 streak 传进来
|
|
111
|
+
* (见 `LoopStallState`), 这样状态归属清晰、也便于单测。
|
|
112
|
+
*/
|
|
113
|
+
function observeToolCall(state, input) {
|
|
114
|
+
const ok = input.ok !== false;
|
|
115
|
+
const tolerant = exports.FAILURE_TOLERANT_TOOLS.has(input.toolName);
|
|
116
|
+
const repeatable = isRepeatableTool(input.toolName);
|
|
117
|
+
const text = String(input.resultText ?? '');
|
|
118
|
+
const chars = input.resultChars ?? text.length;
|
|
119
|
+
// ① 轮询类: 同参数重复本就正当 ⇒ 只记录, 不出声
|
|
120
|
+
if (repeatable) {
|
|
121
|
+
state.push(input.toolName, input.args, text);
|
|
122
|
+
return { action: 'allow', code: 'allow', count: 0 };
|
|
123
|
+
}
|
|
124
|
+
const sig = toolCallSignature(input.toolName, input.args);
|
|
125
|
+
const fp = resultFingerprint(text);
|
|
126
|
+
const prev = state.last;
|
|
127
|
+
const sameAsPrev = !!prev && prev.sig === sig && prev.fp === fp;
|
|
128
|
+
// ② 连续相同 (工具 + 参数 + 结果): 每次 +1, 否则归 1
|
|
129
|
+
state.streak = sameAsPrev ? state.streak + 1 : 1;
|
|
130
|
+
state.push(input.toolName, input.args, text);
|
|
131
|
+
// ③ 失败宽容类工具的失败不参与停滞判定 (红测试/空 grep 是产出, 不是卡住)
|
|
132
|
+
if (!ok && tolerant) {
|
|
133
|
+
return { action: 'allow', code: 'allow', count: state.streak };
|
|
134
|
+
}
|
|
135
|
+
// ③.5 恒定结果工具 (get_identity 这类): 从第 2 次起换标准答复 —— 不管结果长短
|
|
136
|
+
if (sameAsPrev && exports.CONSTANT_RESULT_TOOLS.has(input.toolName) && ok) {
|
|
137
|
+
return {
|
|
138
|
+
action: 'allow',
|
|
139
|
+
code: 'allow',
|
|
140
|
+
count: state.streak,
|
|
141
|
+
stub: `[已答] ${input.toolName} 的结果在本次会话内**恒定不变**(它读的是固定身份/配置, 不是活数据)。` +
|
|
142
|
+
`上面那一次返回就是答案, 直接用它作答即可; 再调一次只会得到同样的内容。` +
|
|
143
|
+
`若确实需要变化, 请改用写类工具(如 set_persona / 相应 update 工具)。`,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
// ④ 结果引用 stub: 从**第 2 次**完全相同的返回起 (够长才替代; 失败永不替代)
|
|
147
|
+
let stub;
|
|
148
|
+
// 2026-10-01 (优化 #1 收尾): 同签名重复 + 结果一字不差 ⇒ 用更低门槛 (重复本身就是浪费, 不管长短)
|
|
149
|
+
const repeatFloor = input.sameSignatureBefore ? exports.REPEAT_STUB_MIN_CHARS : exports.STUB_MIN_CHARS;
|
|
150
|
+
if (ok && input.seenResultBefore && chars >= repeatFloor) {
|
|
151
|
+
stub = `[结果引用] 与本次会话中此前一次 ${input.toolName} 的返回**逐字相同** (${chars} 字符, 已折叠)。`
|
|
152
|
+
+ `参数预览: ${canonicalToolArgs(input.args).slice(0, exports.STUB_ARGS_PREVIEW_CHARS)}`;
|
|
153
|
+
}
|
|
154
|
+
if (state.streak >= exports.IDENTICAL_CALL_THRESHOLD) {
|
|
155
|
+
// ⑤ 连续相同达阈值 ⇒ 温和引导 (不拒绝执行)
|
|
156
|
+
return {
|
|
157
|
+
action: 'warn',
|
|
158
|
+
code: 'identical_call_streak',
|
|
159
|
+
count: state.streak,
|
|
160
|
+
stub,
|
|
161
|
+
notice: `[系统提示] 这是第 ${state.streak} 次**用完全相同的参数**调用 ${input.toolName}, 且返回与上次完全相同。`
|
|
162
|
+
+ `重复调用不会得到新信息 —— 请换参数、换工具, 或基于已有结果直接回答。`,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
// ⑥ 循环检测: 最近 N 次里出现了周期 ≤4 的重复串 (相邻计数会被交替调用清零, 只抓相邻必漏)
|
|
166
|
+
const cycle = detectCycle(state.history);
|
|
167
|
+
if (cycle) {
|
|
168
|
+
return {
|
|
169
|
+
action: 'warn',
|
|
170
|
+
code: 'identical_cycle',
|
|
171
|
+
count: cycle.laps,
|
|
172
|
+
stub,
|
|
173
|
+
notice: `[系统提示] 检测到最近 ${cycle.laps} 圈重复同一组 ${cycle.period} 次调用`
|
|
174
|
+
+ `(以 ${input.toolName} 结尾), 参数与返回都逐字相同。请不要重复这一组 —— 换参数、换工具, 或基于已有结果直接回答。`,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
return { action: 'allow', code: 'allow', count: state.streak, stub };
|
|
178
|
+
}
|
|
179
|
+
/** 每轮 ReAct 循环一份的停滞状态 */
|
|
180
|
+
class LoopStallState {
|
|
181
|
+
streak = 0;
|
|
182
|
+
last = null;
|
|
183
|
+
history = [];
|
|
184
|
+
/** 已见过的结果指纹 (用于"完全相同返回"的 stub 判定) */
|
|
185
|
+
seenResults = new Set();
|
|
186
|
+
push(toolName, args, resultText) {
|
|
187
|
+
const step = { sig: toolCallSignature(toolName, args), fp: resultFingerprint(resultText) };
|
|
188
|
+
this.last = step;
|
|
189
|
+
this.history.push(step);
|
|
190
|
+
if (this.history.length > exports.CYCLE_HISTORY)
|
|
191
|
+
this.history.shift();
|
|
192
|
+
this.seenResults.add(step.fp);
|
|
193
|
+
}
|
|
194
|
+
/** 这个结果指纹此前是否出现过 (供 observeToolCall 的 seenResultBefore) */
|
|
195
|
+
hasSeenResult(text) {
|
|
196
|
+
return this.seenResults.has(resultFingerprint(text));
|
|
197
|
+
}
|
|
198
|
+
reset() {
|
|
199
|
+
this.streak = 0;
|
|
200
|
+
this.last = null;
|
|
201
|
+
this.history.length = 0;
|
|
202
|
+
this.seenResults.clear();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
exports.LoopStallState = LoopStallState;
|
|
206
|
+
/**
|
|
207
|
+
* 找"最近若干圈完全重复"的串: 对每个候选周期 p ∈ [1, MAX_CYCLE_PERIOD],
|
|
208
|
+
* 看末尾是否由同一个 p 长度的块**重复 ≥ THRESHOLD 次**组成 (块内每个 (签名,结果指纹) 都相同)。
|
|
209
|
+
* 返回圈数与周期, 没有则 null。
|
|
210
|
+
*/
|
|
211
|
+
function detectCycle(history) {
|
|
212
|
+
const h = history;
|
|
213
|
+
for (let p = 1; p <= exports.MAX_CYCLE_PERIOD; p++) {
|
|
214
|
+
const need = p * exports.IDENTICAL_CALL_THRESHOLD;
|
|
215
|
+
if (h.length < need)
|
|
216
|
+
continue;
|
|
217
|
+
const tail = h.slice(-need);
|
|
218
|
+
let same = true;
|
|
219
|
+
for (let i = p; i < need && same; i++) {
|
|
220
|
+
if (tail[i].sig !== tail[i % p].sig || tail[i].fp !== tail[i % p].fp)
|
|
221
|
+
same = false;
|
|
222
|
+
}
|
|
223
|
+
if (same)
|
|
224
|
+
return { period: p, laps: exports.IDENTICAL_CALL_THRESHOLD };
|
|
225
|
+
}
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
229
|
+
// 2026-10-01 第二轮 (继续学): 三处补上
|
|
230
|
+
// ① **无进展**是独立一轴: 幂等工具即使**换了参数**, 只要结果与上次相同 ⇒ 也是无进展
|
|
231
|
+
// (原先只抓"同参数" ⇒ 换个措辞再问一遍就绕过去了)
|
|
232
|
+
// ② **进展的定义 = 有副作用的调用成功过** (纯读再多都不算) ⇒ 用它清零失败/无进展计数
|
|
233
|
+
// ③ 升级策略**按在场与否分流**: 有人在 ⇒ 只提醒(从不阻止执行); 无人值守 ⇒ 才允许拦/停
|
|
234
|
+
// (人在场时不该替用户做决定; 但 cron/supervisor 这种无人值守的场景, 该果断收)
|
|
235
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
236
|
+
/** 有副作用 = 会改变世界 (文件/进程/网络/资金/记忆). 唯一判据, 供"进展"与"无进展"用 */
|
|
237
|
+
function toolMayHaveSideEffect(toolName) {
|
|
238
|
+
if (exports.MUTATING_TOOLS.has(toolName))
|
|
239
|
+
return true;
|
|
240
|
+
if (exports.IDEMPOTENT_TOOLS.has(toolName))
|
|
241
|
+
return false;
|
|
242
|
+
if (isRepeatableTool(toolName))
|
|
243
|
+
return false;
|
|
244
|
+
// 未登记的工具按"可能有副作用"处理 (保守: 不轻易判定"它没改变世界")
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
exports.DEFAULT_THRESHOLDS = {
|
|
248
|
+
exactFailureWarnAfter: 2,
|
|
249
|
+
exactFailureBlockAfter: 5,
|
|
250
|
+
sameToolFailureWarnAfter: 3,
|
|
251
|
+
sameToolFailureHaltAfter: 8,
|
|
252
|
+
noProgressWarnAfter: 2,
|
|
253
|
+
noProgressBlockAfter: 5,
|
|
254
|
+
hardStopEnabled: false,
|
|
255
|
+
};
|
|
256
|
+
/** 无人值守场景 (cron / supervisor / 非交互) —— 那里才默认允许拦与停 */
|
|
257
|
+
function defaultHardStopFor(context = {}) {
|
|
258
|
+
if (typeof context.unattended === 'boolean')
|
|
259
|
+
return context.unattended;
|
|
260
|
+
const env = context.env || process.env;
|
|
261
|
+
return env.BOLLOON_UNATTENDED === '1' || env.BOLLOON_CRON === '1' || env.BOLLOON_SUPERVISOR === '1';
|
|
262
|
+
}
|
|
263
|
+
function newProgressBookkeeping() {
|
|
264
|
+
return { progressClearedSignatures: new Set(), noProgress: new Map() };
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* 记一次调用结果, 维护"进展 / 无进展"两本账。
|
|
268
|
+
* - 有副作用的调用**成功** ⇒ 视为进展: 清掉该签名的失败标记 + 清掉**所有**无进展记录
|
|
269
|
+
* (世界变了 ⇒ 之前"读了没变化"的判定作废)
|
|
270
|
+
* - 幂等工具: 结果指纹与上次相同 ⇒ 无进展 +1 (**参数变没变不影响**), 不同 ⇒ 归 1
|
|
271
|
+
*/
|
|
272
|
+
function trackProgress(book, input, thresholds = exports.DEFAULT_THRESHOLDS) {
|
|
273
|
+
const ok = input.ok !== false;
|
|
274
|
+
const sig = toolCallSignature(input.toolName, input.args);
|
|
275
|
+
const fp = resultFingerprint(String(input.resultText ?? ''));
|
|
276
|
+
if (ok && toolMayHaveSideEffect(input.toolName)) {
|
|
277
|
+
book.progressClearedSignatures.add(sig);
|
|
278
|
+
if (book.noProgress.size > 0)
|
|
279
|
+
book.noProgress.clear();
|
|
280
|
+
return { action: 'allow', code: 'allow', count: 0 };
|
|
281
|
+
}
|
|
282
|
+
if (!toolMayHaveSideEffect(input.toolName)) {
|
|
283
|
+
const prev = book.noProgress.get(input.toolName);
|
|
284
|
+
const count = prev && prev.lastResultFp === fp ? prev.count + 1 : 1;
|
|
285
|
+
book.noProgress.set(input.toolName, { lastResultFp: fp, count });
|
|
286
|
+
if (count >= thresholds.noProgressWarnAfter) {
|
|
287
|
+
return {
|
|
288
|
+
action: 'warn',
|
|
289
|
+
code: 'idempotent_no_progress',
|
|
290
|
+
count,
|
|
291
|
+
notice: `[系统提示] 连续 ${count} 次读取类调用 (${input.toolName}) 返回的都是**同一份内容**`
|
|
292
|
+
+ `(参数不同也一样) —— 世界没有变化, 再读还是它。请基于已有内容作答, 或改去查别的来源。`,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
return { action: 'allow', code: 'allow', count };
|
|
296
|
+
}
|
|
297
|
+
return { action: 'allow', code: 'allow', count: 0 };
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* 2026-10-01 (用户: 「为什么这么慢」): 量到的事实 —— 每次 LLM 往返平均 **3338ms**, 而
|
|
301
|
+
* `toolCalls>1`(一轮发多个工具)**只占 12/63** ✗ ⇒ 回合时长 ≈ 往返次数 × 3.3s ✓;
|
|
302
|
+
* 也就是说"慢"主要不是工具慢(工具合计仅占 4% ✗), 而是**同一轮只干一件事**、来回跑 ✓。
|
|
303
|
+
* 做法: 一轮只调了一个**只读**工具时, 在结果后附一句**带代价**的提醒(不是口号 ✓)。
|
|
304
|
+
* 只读工具才提醒(写类工具串行更安全 ✓ 不催)。
|
|
305
|
+
*/
|
|
306
|
+
const BATCHABLE_HINT_TOOLS = new Set([
|
|
307
|
+
'read_file', 'grep_files', 'glob_files', 'list_files', 'read_document',
|
|
308
|
+
'git_status', 'git_log', 'git_diff', 'get_identity', 'list_tools', 'list_skills',
|
|
309
|
+
'process', 'list_local_channels', 'get_balance',
|
|
310
|
+
]);
|
|
311
|
+
function batchHint(toolName, toolCallsThisRound) {
|
|
312
|
+
if (toolCallsThisRound !== 1)
|
|
313
|
+
return '';
|
|
314
|
+
if (!BATCHABLE_HINT_TOOLS.has(String(toolName || '')))
|
|
315
|
+
return '';
|
|
316
|
+
return '\n⚡ 若你接下来还要查别的(或还要读别的文件), **同一轮可以一次发多个工具** —— '
|
|
317
|
+
+ '每次来回约 3.3 秒, 分成多轮就是纯等待; 独立的读操作请并成一轮发。';
|
|
318
|
+
}
|
|
319
|
+
//# sourceMappingURL=tool-loop-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-loop-guard.js","sourceRoot":"","sources":["../../../src/agents/tool-loop-guard.ts"],"names":[],"mappings":"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"}
|