@bolloon/bolloon-agent 0.3.49 → 0.3.50
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/deny-pipeline.js +5 -1
- package/dist/agents/loop-review.js +12 -15
- package/dist/agents/pi-sdk.js +8 -1
- package/dist/index.js +16 -0
- package/package.json +1 -1
|
@@ -81,11 +81,15 @@ export class DenyPipeline {
|
|
|
81
81
|
* 构建 permission-mode checker.
|
|
82
82
|
* default 模式: 禁用 shell_exec / git_commit / git_push 等危险工具
|
|
83
83
|
* bypassPermissions: 放行所有
|
|
84
|
+
*
|
|
85
|
+
* 2026-08-10: write_file/edit_file/delete_file 移出 default 禁列表 — 它们已有
|
|
86
|
+
* checkWritePath 写入白名单兜底 (self-improve-policy.json), 正常任务 (如写 HTML 发布
|
|
87
|
+
* IPFS 网站) 不该被 permission 层拦截; 实测日志: "write_file 被权限拦了" → LLM 只能
|
|
88
|
+
* 绕道, 任务无法推进.
|
|
84
89
|
*/
|
|
85
90
|
static permissionChecker() {
|
|
86
91
|
const DEFAULT_DENY_TOOLS = new Set([
|
|
87
92
|
'shell_exec', 'git_commit', 'git_push', 'git_branch',
|
|
88
|
-
'delete_file', 'write_file', 'edit_file',
|
|
89
93
|
]);
|
|
90
94
|
return (ctx) => {
|
|
91
95
|
if (ctx.permissionMode === 'bypassPermissions') {
|
|
@@ -21,24 +21,21 @@ export const DEFAULT_MAX_REVIEWS = 2;
|
|
|
21
21
|
/**
|
|
22
22
|
* 判定: LLM 想 <final gen> 时, 该续跑一次 review 还是真正结束.
|
|
23
23
|
*
|
|
24
|
-
*
|
|
25
|
-
* -
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
24
|
+
* 2026-08-10 重构 (用户纠正: 不要硬编码词表, 循环要智能, 自动触发后续):
|
|
25
|
+
* - 旧逻辑: 无 intent → 直接 finish. 但"发布一个 ipfs 网站..." 被 classifyIntent 误判
|
|
26
|
+
* chitchat → intentHint 空 → 1 次循环直接 <final gen> (任务没做就结束).
|
|
27
|
+
* - 新逻辑: **final 前总是让 LLM 自查** (是否完成用户需求/是否还有后续步骤) — 结束权
|
|
28
|
+
* 完全交给 LLM 判断, 规则只做上限兜底 (maxReviews). 达上限才放行, 保证不过度深挖.
|
|
29
|
+
* - 效果: 任务场景 LLM 自查发现"还没发布" → 自动继续调工具 (自动触发后续);
|
|
30
|
+
* 闲聊场景 LLM 快速确认完成, 2 次自查后放行.
|
|
30
31
|
*/
|
|
31
32
|
export function decideAfterReview(state, maxReviews = DEFAULT_MAX_REVIEWS) {
|
|
32
|
-
const intent = (state.userIntent || '').trim();
|
|
33
|
-
if (!intent) {
|
|
34
|
-
return { kind: 'finish', reason: 'no-user-intent' };
|
|
35
|
-
}
|
|
36
33
|
if (state.reviewsDone >= maxReviews) {
|
|
37
34
|
return { kind: 'finish', reason: `max-reviews-${maxReviews}` };
|
|
38
35
|
}
|
|
39
36
|
return {
|
|
40
37
|
kind: 'continue-review',
|
|
41
|
-
reason: `${state.reviewsDone + 1}/${maxReviews}
|
|
38
|
+
reason: `${state.reviewsDone + 1}/${maxReviews} 完成度自查`,
|
|
42
39
|
hint: buildReviewHint(state, maxReviews),
|
|
43
40
|
};
|
|
44
41
|
}
|
|
@@ -60,13 +57,13 @@ export function buildReviewHint(state, maxReviews = DEFAULT_MAX_REVIEWS) {
|
|
|
60
57
|
})
|
|
61
58
|
.join('\n');
|
|
62
59
|
}
|
|
63
|
-
return (`[
|
|
60
|
+
return (`[完成度自查 ${state.reviewsDone + 1}/${maxReviews}]` +
|
|
64
61
|
`\n用户需求: ${intent.slice(0, 300)}` +
|
|
65
62
|
actionLines +
|
|
66
63
|
`\n已完成工具: ${tools}` +
|
|
67
|
-
`\n请对照需求逐条自查:
|
|
68
|
-
|
|
69
|
-
`\n[重要]
|
|
64
|
+
`\n请对照需求逐条自查: 用户需求是否每一项都真正完成了? 如果还有未完成的子目标或自然衔接的后续步骤 → 继续调用工具推进 (已完成动作不要重复, 直接基于已有结果做下一步); ` +
|
|
65
|
+
`如果用户需求已全部满足 → 直接输出 <final gen> 结束.` +
|
|
66
|
+
`\n[重要] 不要因为做了一部分就提前结束 — 结束前逐条对照「用户需求」确认每一项都完成.`);
|
|
70
67
|
}
|
|
71
68
|
/** 布尔门: 是否该继续 review (测试/消融快速断言) */
|
|
72
69
|
export function shouldReviewAgain(state, maxReviews = DEFAULT_MAX_REVIEWS) {
|
package/dist/agents/pi-sdk.js
CHANGED
|
@@ -209,6 +209,8 @@ export class PiAgentSession {
|
|
|
209
209
|
currentAgentId = '';
|
|
210
210
|
/** M2.2 (2026-06-17): 当前轮的用户请求 intent, runReActLoop 拼 systemPrompt 时会读这个 */
|
|
211
211
|
currentIntent = 'chitchat';
|
|
212
|
+
/** 2026-08-10: 本轮用户原始输入 (loop-review 任务动词兜底检测用) */
|
|
213
|
+
currentUserInput = '';
|
|
212
214
|
currentIntentHint = '';
|
|
213
215
|
/**
|
|
214
216
|
* 算 judgment 注入门: 失败静默, 不阻塞主对话
|
|
@@ -673,6 +675,7 @@ export class PiAgentSession {
|
|
|
673
675
|
const { classifyIntent, intentHint } = await import('./intent-classifier.js');
|
|
674
676
|
this.currentIntent = classifyIntent(input);
|
|
675
677
|
this.currentIntentHint = intentHint(this.currentIntent);
|
|
678
|
+
this.currentUserInput = input;
|
|
676
679
|
}
|
|
677
680
|
catch (err) {
|
|
678
681
|
console.warn('[PiAgent] classifyIntent in prompt() failed:', err);
|
|
@@ -766,6 +769,7 @@ export class PiAgentSession {
|
|
|
766
769
|
const { classifyIntent, intentHint } = await import('./intent-classifier.js');
|
|
767
770
|
this.currentIntent = classifyIntent(userText);
|
|
768
771
|
this.currentIntentHint = intentHint(this.currentIntent);
|
|
772
|
+
this.currentUserInput = userText;
|
|
769
773
|
if (this.currentIntent !== 'chitchat') {
|
|
770
774
|
onStream({ type: 'phase', phase: 'intent_classified', detail: this.currentIntent, content: '' });
|
|
771
775
|
}
|
|
@@ -1031,6 +1035,7 @@ export class PiAgentSession {
|
|
|
1031
1035
|
const { classifyIntent, intentHint } = await import('./intent-classifier.js');
|
|
1032
1036
|
this.currentIntent = classifyIntent(input);
|
|
1033
1037
|
this.currentIntentHint = intentHint(this.currentIntent);
|
|
1038
|
+
this.currentUserInput = input;
|
|
1034
1039
|
}
|
|
1035
1040
|
catch (err) {
|
|
1036
1041
|
console.warn('[PiAgent] classifyIntent in pivot failed:', err);
|
|
@@ -1779,7 +1784,9 @@ ${toolDefs}
|
|
|
1779
1784
|
// 达成用户需求才放行真正结束. 达上限或无需深挖则以用户需求为准结束.
|
|
1780
1785
|
const reviewDecision = decideAfterReview({
|
|
1781
1786
|
reviewsDone: loopReviewCount,
|
|
1782
|
-
|
|
1787
|
+
// 2026-08-10: 传用户原始输入 (不是派生 intentHint) — LLM 对照原文自查完成度,
|
|
1788
|
+
// 未完成 → 自动继续调工具 (自动触发后续步骤)
|
|
1789
|
+
userIntent: this.currentUserInput,
|
|
1783
1790
|
completedTools: Array.from(loopReviewCompletedTools),
|
|
1784
1791
|
actionLog: loopActionLog,
|
|
1785
1792
|
}, DEFAULT_MAX_REVIEWS);
|
package/dist/index.js
CHANGED
|
@@ -538,6 +538,22 @@ async function startCLI(comm) {
|
|
|
538
538
|
catch { /* config-store 失败静默, 用 env 结果 */ }
|
|
539
539
|
cliAgentName = agentIdentity?.name || 'bolloon';
|
|
540
540
|
cliStartTime = Date.now();
|
|
541
|
+
// 2026-08-10: 启动后台拉起本地 Kubo (IPFS) — fire-and-forget, 失败静默 (ipfs 工具内会再尝试).
|
|
542
|
+
// 背景: 实测日志 ipfs_add 失败 "发送上传请求失败: http://127.0.0.1:5001/api/v0/add" —
|
|
543
|
+
// Kubo daemon 没起, 而 CLI 启动路径 (startCLI) 之前从不调 checkKuboSetup (只有 Web server 调).
|
|
544
|
+
// BOLLOON_SKIP_KUBO=1 可禁用 (pty 测试用临时 HOME 时避免拉起指向临时 repo 的 daemon 污染 5001)
|
|
545
|
+
if (process.env.BOLLOON_SKIP_KUBO !== '1') {
|
|
546
|
+
void (async () => {
|
|
547
|
+
try {
|
|
548
|
+
const sdk = await import('@diap/sdk');
|
|
549
|
+
const checkKuboSetup = sdk.checkKuboSetup;
|
|
550
|
+
if (typeof checkKuboSetup === 'function') {
|
|
551
|
+
await checkKuboSetup(true, true);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
catch { /* Kubo 拉起失败静默 — ipfs 工具内 ensureKuboReady 会再尝试 */ }
|
|
555
|
+
})();
|
|
556
|
+
}
|
|
541
557
|
// 恢复上次 active channel (session 恢复: CLI 与 Web 共用 active-channel.json)
|
|
542
558
|
try {
|
|
543
559
|
const { getIdentityStore } = await import('./agents/agent-identity-store.js');
|