@bolloon/bolloon-agent 0.4.5 → 0.4.7
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/README.md +135 -135
- package/bin/bolloon-cli.cjs +183 -183
- package/bin/bolloon.cjs +0 -0
- package/bin/bolloon.js +157 -0
- package/dist/agents/constraint-layer.js +19 -19
- package/dist/agents/judgment-protocol.js +14 -14
- package/dist/agents/pi-sdk-tools.js +128 -46
- package/dist/agents/pi-sdk-types.js +12 -12
- package/dist/agents/pi-sdk.js +103 -90
- package/dist/agents/process-runner.js +105 -0
- package/dist/agents/skill-organizer.js +19 -19
- package/dist/agents/skill-writer.js +15 -3
- package/dist/agents/workflow-pivot-loop.js +13 -3
- package/dist/agents/write-staging.js +85 -0
- package/dist/bollharness/src/scripts/deploy-guard.js +8 -8
- package/dist/bollharness-integration/context-router-judgment.js +4 -4
- package/dist/bollharness-integration/context-router.js +292 -292
- package/dist/bollharness-integration/gate-state-machine.js +13 -13
- package/dist/bollharness-integration/llm-judgment-engine.js +134 -134
- package/dist/bollharness-integration/skill-adapter.js +21 -21
- package/dist/bootstrap/chat-archiver.js +14 -14
- package/dist/bootstrap/context-os.js +24 -24
- package/dist/bootstrap/memory-compressor.js +14 -14
- package/dist/cli/ink-app.js +18 -0
- package/dist/cli-entry.js +36 -36
- package/dist/context-compaction/auto-compact.js +7 -7
- package/dist/electron/first-run.js +54 -54
- package/dist/electron-build/electron/first-run.js +54 -54
- package/dist/electron-preload.js.map +1 -1
- package/dist/electron.js.map +1 -1
- package/dist/index.js +230 -137
- package/dist/llm/llm-judgment-client.js +102 -102
- package/dist/llm/pi-ai.js +48 -48
- package/dist/llm/system-prompt/layers/channel/human-async.md +41 -41
- package/dist/llm/system-prompt/layers/channel/local.md +14 -14
- package/dist/llm/system-prompt/layers/channel/p2p-agent.md +18 -18
- package/dist/llm/system-prompt/layers/channel/p2p-peer-sync.md +51 -51
- package/dist/llm/system-prompt/layers/channel/p2p-proactive.md +43 -43
- package/dist/llm/system-prompt/layers/channel/p2p-visitor.md +19 -19
- package/dist/llm/system-prompt/layers/channel/session-handoff.md +61 -61
- package/dist/llm/system-prompt/layers/core/artifacts_storage.md +89 -89
- package/dist/llm/system-prompt/layers/core/evenhandedness.md +21 -21
- package/dist/llm/system-prompt/layers/core/external-engagement.md +73 -73
- package/dist/llm/system-prompt/layers/core/hibs_reminders.md +15 -15
- package/dist/llm/system-prompt/layers/core/identity.md +48 -48
- package/dist/llm/system-prompt/layers/core/knowledge.md +17 -17
- package/dist/llm/system-prompt/layers/core/memory_system.md +12 -12
- package/dist/llm/system-prompt/layers/core/network_filesystem.md +28 -28
- package/dist/llm/system-prompt/layers/core/refusal.md +37 -37
- package/dist/llm/system-prompt/layers/core/tone.md +31 -31
- package/dist/llm/system-prompt/layers/core/tools.thin.md +13 -13
- package/dist/llm/system-prompt/layers/core/wellbeing.md +41 -41
- package/dist/llm/system-prompt/layers/role/architect.md +20 -20
- package/dist/llm/system-prompt/layers/role/expert.md +19 -19
- package/dist/llm/system-prompt/layers/role/implementer.md +15 -15
- package/dist/llm/system-prompt/layers/role/security.md +15 -15
- package/dist/llm/system-prompt/layers/tool/artifacts.md +72 -72
- package/dist/llm/system-prompt/layers/tool/bash.md +25 -25
- package/dist/llm/system-prompt/layers/tool/goal_handoff.md +77 -77
- package/dist/llm/system-prompt/layers/tool/hibs_api.md +171 -171
- package/dist/llm/system-prompt/layers/tool/image_search.md +70 -70
- package/dist/llm/system-prompt/layers/tool/manifest.md +89 -89
- package/dist/llm/system-prompt/layers/tool/mcp_apps.md +53 -53
- package/dist/llm/system-prompt/layers/tool/p2p_request.md +61 -61
- package/dist/llm/system-prompt/layers/tool/web_search.md +83 -83
- package/dist/llm/tool-manifest/ask_user_input.js +1 -1
- package/dist/llm/tool-manifest/bash.js +3 -3
- package/dist/llm/tool-manifest/create_file.js +4 -4
- package/dist/llm/tool-manifest/fetch_sports_data.js +3 -3
- package/dist/llm/tool-manifest/image_search.js +2 -2
- package/dist/llm/tool-manifest/index.js +0 -59
- package/dist/llm/tool-manifest/mcp.js +2 -2
- package/dist/llm/tool-manifest/message_compose.js +3 -3
- package/dist/llm/tool-manifest/places.js +2 -2
- package/dist/llm/tool-manifest/present_files.js +1 -1
- package/dist/llm/tool-manifest/recipe.js +2 -2
- package/dist/llm/tool-manifest/str_replace.js +5 -5
- package/dist/llm/tool-manifest/view.js +3 -3
- package/dist/llm/tool-manifest/weather.js +4 -4
- package/dist/llm/tool-manifest/web.js +4 -4
- package/dist/orbitdb/agent-tools.js +139 -0
- package/dist/orbitdb/kanban-store.js +259 -0
- package/dist/orbitdb/task-store.js +112 -0
- package/dist/pi-ecosystem-colony/index.js +365 -0
- package/dist/pi-ecosystem-judgment/causal-judge.js +51 -51
- package/dist/pi-ecosystem-judgment/distill-prompt.js +42 -42
- package/dist/pi-ecosystem-judgment/distillation.js +14 -14
- package/dist/pi-ecosystem-judgment/monitor-gate.js +28 -28
- package/dist/pi-ecosystem-judgment/value-injection.js +102 -102
- package/dist/security/context-router-tool.js +15 -15
- package/dist/security/tool-gate.js +2 -0
- package/dist/social/ant-colony/AdaptiveHeartbeat.js +101 -0
- package/dist/social/ant-colony/PheromoneEngine.js +227 -0
- package/dist/social/ant-colony/index.js +6 -0
- package/dist/social/ant-colony/types.js +24 -0
- package/dist/storage/trajectory.js +101 -0
- package/dist/test/ai-judgment-test.js +80 -0
- package/dist/test/bollharness-integration.test.js +318 -0
- package/dist/test/channel-agent-multi-dialogue.js +205 -0
- package/dist/test/channel-heartbeat-agent-test.js +201 -0
- package/dist/test/constraint-layer.test.js +164 -0
- package/dist/test/diap-identity-test.js +172 -0
- package/dist/test/diap-quick-test.js +62 -0
- package/dist/test/global-shared-context.test.js +315 -0
- package/dist/test/harness-judgment-injection.test.js +246 -0
- package/dist/test/harness-workflow-integrator-test.js +228 -0
- package/dist/test/human-value-store.test.js +243 -0
- package/dist/test/hybrid-integration-test.js +118 -0
- package/dist/test/hybrid-messenger-verify.js +55 -0
- package/dist/test/iroh-bistream-debug.js +38 -0
- package/dist/test/iroh-communication.test.js +66 -0
- package/dist/test/iroh-debug-test.js +57 -0
- package/dist/test/iroh-diap-test.js +71 -0
- package/dist/test/iroh-direct-connect.js +55 -0
- package/dist/test/iroh-e2e-fixed.js +89 -0
- package/dist/test/iroh-e2e-same-process.js +63 -0
- package/dist/test/iroh-e2e.js +66 -0
- package/dist/test/iroh-final-e2e.js +72 -0
- package/dist/test/iroh-relay-test.js +37 -0
- package/dist/test/iroh-simple-test.js +41 -0
- package/dist/test/iroh-transport-verify.js +54 -0
- package/dist/test/iroh-transport.test.js +37 -0
- package/dist/test/iroh-two-nodes.js +70 -0
- package/dist/test/iroh-verify.js +44 -0
- package/dist/test/judgment-decision.test.js +219 -0
- package/dist/test/llm-judgment-integration.test.js +220 -0
- package/dist/test/p2p-agent-complex-dialogue.js +385 -0
- package/dist/test/p2p-agent-dialogue.js +341 -0
- package/dist/test/p2p-agent-full-bidirectional.js +510 -0
- package/dist/test/p2p-agent-harness-flow.js +437 -0
- package/dist/test/p2p-agent-harness-single.js +143 -0
- package/dist/test/p2p-ai-dialogue-test.js +318 -0
- package/dist/test/p2p-cid-connect-test.js +195 -0
- package/dist/test/p2p-connect-receiver.js +69 -0
- package/dist/test/p2p-doc-transfer.js +110 -0
- package/dist/test/p2p-identity-page-test.js +77 -0
- package/dist/test/p2p-iroh-test.js +171 -0
- package/dist/test/p2p-minimal-test.js +241 -0
- package/dist/test/p2p-node-1.js +148 -0
- package/dist/test/p2p-node-2.js +148 -0
- package/dist/test/p2p-server.js +281 -0
- package/dist/test/p2p-two-nodes-test.js +438 -0
- package/dist/test/pi-sdk.test.js +44 -0
- package/dist/test/set-persona.js +40 -0
- package/dist/test/simple.test.js +9 -0
- package/dist/test/storage-integration.test.js +150 -0
- package/dist/test/subagent-manager.test.js +276 -0
- package/dist/test/test-gate-flow.test.js +81 -0
- package/dist/test/workflow-engine.test.js +87 -0
- package/dist/test/workflow-pivot-loop.test.js +246 -0
- package/dist/web/api-config.html +779 -779
- package/dist/web/client-hearth.js +10 -10
- package/dist/web/components/wallet-viem.mjs +118 -118
- package/dist/web/index.html +472 -472
- package/dist/web/manifest.json +20 -20
- package/dist/web/server-storage.js +29 -0
- package/dist/web/server.js +30 -14
- package/dist/web/style.css +4946 -4946
- package/package.json +195 -195
- package/scripts/build-cli.js +215 -215
- package/scripts/build-web.ts +130 -130
- package/scripts/postinstall.js +152 -152
- package/bin/bolloon-daemon.sh +0 -207
- package/dist/constraint-runtime/reference_data/archive_surface_snapshot.json +0 -63
- package/dist/constraint-runtime/reference_data/commands_snapshot.json +0 -1037
- package/dist/constraint-runtime/reference_data/subsystems/assistant.json +0 -8
- package/dist/constraint-runtime/reference_data/subsystems/bootstrap.json +0 -8
- package/dist/constraint-runtime/reference_data/subsystems/bridge.json +0 -32
- package/dist/constraint-runtime/reference_data/subsystems/buddy.json +0 -13
- package/dist/constraint-runtime/reference_data/subsystems/cli.json +0 -26
- package/dist/constraint-runtime/reference_data/subsystems/components.json +0 -32
- package/dist/constraint-runtime/reference_data/subsystems/constants.json +0 -28
- package/dist/constraint-runtime/reference_data/subsystems/coordinator.json +0 -8
- package/dist/constraint-runtime/reference_data/subsystems/entrypoints.json +0 -15
- package/dist/constraint-runtime/reference_data/subsystems/hooks.json +0 -32
- package/dist/constraint-runtime/reference_data/subsystems/keybindings.json +0 -21
- package/dist/constraint-runtime/reference_data/subsystems/memdir.json +0 -15
- package/dist/constraint-runtime/reference_data/subsystems/migrations.json +0 -18
- package/dist/constraint-runtime/reference_data/subsystems/moreright.json +0 -8
- package/dist/constraint-runtime/reference_data/subsystems/native_ts.json +0 -11
- package/dist/constraint-runtime/reference_data/subsystems/outputStyles.json +0 -8
- package/dist/constraint-runtime/reference_data/subsystems/plugins.json +0 -9
- package/dist/constraint-runtime/reference_data/subsystems/remote.json +0 -11
- package/dist/constraint-runtime/reference_data/subsystems/schemas.json +0 -8
- package/dist/constraint-runtime/reference_data/subsystems/screens.json +0 -10
- package/dist/constraint-runtime/reference_data/subsystems/server.json +0 -10
- package/dist/constraint-runtime/reference_data/subsystems/services.json +0 -32
- package/dist/constraint-runtime/reference_data/subsystems/skills.json +0 -27
- package/dist/constraint-runtime/reference_data/subsystems/state.json +0 -13
- package/dist/constraint-runtime/reference_data/subsystems/types.json +0 -18
- package/dist/constraint-runtime/reference_data/subsystems/upstreamproxy.json +0 -9
- package/dist/constraint-runtime/reference_data/subsystems/utils.json +0 -32
- package/dist/constraint-runtime/reference_data/subsystems/vim.json +0 -12
- package/dist/constraint-runtime/reference_data/subsystems/voice.json +0 -8
- package/dist/constraint-runtime/reference_data/tools_snapshot.json +0 -922
- package/dist/electron/config.js.map +0 -1
- package/dist/electron/dialogs.js.map +0 -1
- package/dist/electron/first-run.js.map +0 -1
- package/dist/electron/ipc.js.map +0 -1
- package/dist/electron/logger.js.map +0 -1
- package/dist/electron/main.js.map +0 -1
- package/dist/electron/menu.js.map +0 -1
- package/dist/electron/paths.js.map +0 -1
- package/dist/electron/server.js.map +0 -1
- package/dist/electron/tray.js.map +0 -1
- package/dist/electron/window.js.map +0 -1
- package/dist/utils/auto-update.js.map +0 -1
- package/dist/web/client.js.map +0 -7
package/dist/agents/pi-sdk.js
CHANGED
|
@@ -201,6 +201,15 @@ export class PiAgentSession {
|
|
|
201
201
|
currentSignal = null;
|
|
202
202
|
/** Bootstrap SessionStart 拼的 system prompt 片段 (用完即清) */
|
|
203
203
|
bootstrapAddition = '';
|
|
204
|
+
/** 2026-08-12 (Task3 认知卸载): 工具选择与认知卸载指南 — 注入 system prompt, 降低"模型不触发 write/edit/read"率. */
|
|
205
|
+
static TOOL_SELECTION_GUIDE = `【工具选择与认知卸载指南 (严格遵循)】
|
|
206
|
+
- 写/改文件: 必须用 write_file / edit_file (不要用 terminal 拼字符串写文件).
|
|
207
|
+
- 读文件: 用 read_file / read_directory / list_files.
|
|
208
|
+
- 跑命令/查状态/装依赖/跑脚本: 用 terminal.
|
|
209
|
+
- 执行 git 操作: 用 git_* 专用工具 (git_diff/git_commit/git_push/git_branch/git_log).
|
|
210
|
+
- 查看/调用技能: 用 list_skills / use_skill.
|
|
211
|
+
- 认知卸载: 当任务对你过大或反复失败时, 不要死磕 — 用 delegate_to_engine 把编码/复杂任务委派给外部引擎 (codex/claude-code/opencode), 或把目标拆小分步完成.
|
|
212
|
+
- 每步只选一个最合适的工具, 先读后写, 写完验证 (跑 tsc/vitest 或读回文件).`;
|
|
204
213
|
/** 当前 prompt 开始时间 (供 Stop hook 计算 durationMs) */
|
|
205
214
|
promptStartTime = 0;
|
|
206
215
|
/** 当前 channel id (由 getAgentForChannel / prompt 4 参注入, 供 hook / log 使用) */
|
|
@@ -1042,38 +1051,40 @@ export class PiAgentSession {
|
|
|
1042
1051
|
}
|
|
1043
1052
|
// M2.4: persona 缓存
|
|
1044
1053
|
if (!this.cachedPersonaSection && this.persona) {
|
|
1045
|
-
this.cachedPersonaSection = `
|
|
1046
|
-
角色描述: ${this.persona.description || '无'}
|
|
1047
|
-
性格特点: ${this.persona.personality || '无'}
|
|
1048
|
-
问候语: ${this.persona.greeting || '无'}
|
|
1054
|
+
this.cachedPersonaSection = `
|
|
1055
|
+
角色描述: ${this.persona.description || '无'}
|
|
1056
|
+
性格特点: ${this.persona.personality || '无'}
|
|
1057
|
+
问候语: ${this.persona.greeting || '无'}
|
|
1049
1058
|
`;
|
|
1050
1059
|
}
|
|
1051
|
-
const systemPrompt = `${this.bootstrapAddition}你是 ${this.identity.name},基于ReAct (Reasoning + Acting)模式工作。${this.cachedPersonaSection}
|
|
1052
|
-
当前工作目录: ${this.cwd}
|
|
1053
|
-
当前身份: ${this.identity.name} (${this.identity.did})
|
|
1054
|
-
${this.currentIntentHint}
|
|
1055
|
-
|
|
1056
|
-
${this.getToolDefinitions()}
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
-
|
|
1069
|
-
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
-
|
|
1075
|
-
-
|
|
1076
|
-
-
|
|
1060
|
+
const systemPrompt = `${this.bootstrapAddition}你是 ${this.identity.name},基于ReAct (Reasoning + Acting)模式工作。${this.cachedPersonaSection}
|
|
1061
|
+
当前工作目录: ${this.cwd}
|
|
1062
|
+
当前身份: ${this.identity.name} (${this.identity.did})
|
|
1063
|
+
${this.currentIntentHint}
|
|
1064
|
+
|
|
1065
|
+
${this.getToolDefinitions()}
|
|
1066
|
+
|
|
1067
|
+
${PiAgentSession.TOOL_SELECTION_GUIDE}
|
|
1068
|
+
|
|
1069
|
+
工作模式:
|
|
1070
|
+
1. 理解用户自然语言请求
|
|
1071
|
+
2. 分析需要哪些工具来完成
|
|
1072
|
+
3. 按顺序调用工具并观察结果
|
|
1073
|
+
4. 根据观察结果决定下一步
|
|
1074
|
+
5. 最终给出完整回答
|
|
1075
|
+
|
|
1076
|
+
重要 (一次命中要求):
|
|
1077
|
+
- 每次只调用一个工具
|
|
1078
|
+
- 仔细分析工具返回结果
|
|
1079
|
+
- 当任务完成时,必须在回答末尾添加 <final gen> 标记表示结束
|
|
1080
|
+
- 如果需要更多信息,继续调用工具
|
|
1081
|
+
|
|
1082
|
+
【工具调用格式 (严格遵守, 否则系统无法解析)】
|
|
1083
|
+
- 你只能输出**一个**工具调用, 不要堆叠多个 invoke
|
|
1084
|
+
- 工具调用格式: {"name":"<tool_name>","input":{"arg1":"value1"}}
|
|
1085
|
+
- 用 markdown json code block 包裹: \`\`\`json\n{"name":"X","input":{...}}\n\`\`\`
|
|
1086
|
+
- 工具调用前可以简短思考 (1-2 句话), 但**不要写长篇 thinking** (会撞 max_tokens)
|
|
1087
|
+
- 工具调用后必须等结果, 不要在同一个回复里继续输出
|
|
1077
1088
|
- <final gen> 只在**真完成所有任务**时输出, 不要在工具调用前/中输出${this.judgmentGateAddition}${this.contextHintAddition}`;
|
|
1078
1089
|
// 2026-06-15: 把 currentOnStream 传给 loop, 让 step-timeline 在 pivot 循环里也能 emit step_start/done
|
|
1079
1090
|
// 之前 loop.execute() 不接 streamCallback, 导致 step-timeline 只能看到老 runReActLoop 路径
|
|
@@ -1265,10 +1276,10 @@ ${this.getToolDefinitions()}
|
|
|
1265
1276
|
}
|
|
1266
1277
|
// M2.4: persona section 缓存 — persona 在 loadPersona() 时一次设定, 此后不变
|
|
1267
1278
|
if (!this.cachedPersonaSection && this.persona) {
|
|
1268
|
-
this.cachedPersonaSection = `
|
|
1269
|
-
角色描述: ${this.persona.description || '无'}
|
|
1270
|
-
性格特点: ${this.persona.personality || '无'}
|
|
1271
|
-
问候语: ${this.persona.greeting || '无'}
|
|
1279
|
+
this.cachedPersonaSection = `
|
|
1280
|
+
角色描述: ${this.persona.description || '无'}
|
|
1281
|
+
性格特点: ${this.persona.personality || '无'}
|
|
1282
|
+
问候语: ${this.persona.greeting || '无'}
|
|
1272
1283
|
`;
|
|
1273
1284
|
}
|
|
1274
1285
|
const personaSection = this.cachedPersonaSection;
|
|
@@ -1286,26 +1297,28 @@ ${this.getToolDefinitions()}
|
|
|
1286
1297
|
.join('\n');
|
|
1287
1298
|
loopProgressSection = `\n【本轮循环进度】你已完成以下 ${loopActionLog.length} 个动作, 这是连续执行的同一轮任务:\n${actionLines}\n请基于已有结果继续推进, 不要重复执行上面已成功的动作. 全部完成后用 <final gen> 结束.\n`;
|
|
1288
1299
|
}
|
|
1289
|
-
const systemPrompt = `${this.bootstrapAddition}你是 ${this.identity.name},基于ReAct (Reasoning + Acting)模式工作。${personaSection}
|
|
1290
|
-
当前工作目录: ${this.cwd}
|
|
1291
|
-
当前身份: ${this.identity.name} (${this.identity.did})
|
|
1292
|
-
${refineContext}
|
|
1293
|
-
${this.currentIntentHint}
|
|
1294
|
-
${loopProgressSection}
|
|
1295
|
-
|
|
1296
|
-
${toolDefs}
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
-
|
|
1300
|
+
const systemPrompt = `${this.bootstrapAddition}你是 ${this.identity.name},基于ReAct (Reasoning + Acting)模式工作。${personaSection}
|
|
1301
|
+
当前工作目录: ${this.cwd}
|
|
1302
|
+
当前身份: ${this.identity.name} (${this.identity.did})
|
|
1303
|
+
${refineContext}
|
|
1304
|
+
${this.currentIntentHint}
|
|
1305
|
+
${loopProgressSection}
|
|
1306
|
+
|
|
1307
|
+
${toolDefs}
|
|
1308
|
+
|
|
1309
|
+
${PiAgentSession.TOOL_SELECTION_GUIDE}
|
|
1310
|
+
|
|
1311
|
+
工作模式:
|
|
1312
|
+
1. 理解用户自然语言请求
|
|
1313
|
+
2. 分析需要哪些工具来完成
|
|
1314
|
+
3. 按顺序调用工具并观察结果
|
|
1315
|
+
4. 根据观察结果决定下一步
|
|
1316
|
+
5. 最终给出完整回答
|
|
1317
|
+
|
|
1318
|
+
重要:
|
|
1319
|
+
- 每次只调用一个工具
|
|
1320
|
+
- 仔细分析工具返回结果
|
|
1321
|
+
- 当任务完成时,必须在回答末尾添加 <final gen> 标记表示结束
|
|
1309
1322
|
- 如果需要更多信息,继续调用工具${this.judgmentGateAddition}${this.contextHintAddition}`;
|
|
1310
1323
|
// 3 个恢复机制 (Claude Code 论文 9-step pipeline 内部):
|
|
1311
1324
|
// 1. max output token 升级 (最多 3 次, 每次 maxOutputTokens 翻倍)
|
|
@@ -2376,37 +2389,37 @@ ${toolDefs}
|
|
|
2376
2389
|
getDefaultResponse(input) {
|
|
2377
2390
|
const operationsRef = PiAgentSession.getOperationsReference();
|
|
2378
2391
|
if (operationsRef) {
|
|
2379
|
-
return `收到了: "${input}"
|
|
2380
|
-
|
|
2381
|
-
我是一个判断力处理智能体,支持自然语言交互。
|
|
2382
|
-
|
|
2383
|
-
可用操作(直接说出即可):
|
|
2384
|
-
${this.extractOperationsFromRef(operationsRef)}
|
|
2385
|
-
|
|
2386
|
-
示例请求:
|
|
2387
|
-
- "读取 src/index.ts 文件"
|
|
2388
|
-
- "总结一下 README.md"
|
|
2389
|
-
- "查看当前连接了哪些节点"
|
|
2392
|
+
return `收到了: "${input}"
|
|
2393
|
+
|
|
2394
|
+
我是一个判断力处理智能体,支持自然语言交互。
|
|
2395
|
+
|
|
2396
|
+
可用操作(直接说出即可):
|
|
2397
|
+
${this.extractOperationsFromRef(operationsRef)}
|
|
2398
|
+
|
|
2399
|
+
示例请求:
|
|
2400
|
+
- "读取 src/index.ts 文件"
|
|
2401
|
+
- "总结一下 README.md"
|
|
2402
|
+
- "查看当前连接了哪些节点"
|
|
2390
2403
|
- "向 QmABC... 发送测试消息"`;
|
|
2391
2404
|
}
|
|
2392
|
-
return `收到了: "${input}"
|
|
2393
|
-
|
|
2394
|
-
我是一个判断力处理智能体,支持自然语言交互。
|
|
2395
|
-
|
|
2396
|
-
可用操作(直接说出即可):
|
|
2397
|
-
- "读取 README.md" - 读取并分析文档
|
|
2398
|
-
- "总结文档" - 总结文档内容
|
|
2399
|
-
- "改进文档,按照X要求" - 改进文档
|
|
2400
|
-
- "查看节点" - 查看已连接的对等节点
|
|
2401
|
-
- "向X发送消息Y" - 向对等节点发送消息
|
|
2402
|
-
- "广播消息X" - 广播消息到所有节点
|
|
2403
|
-
- "查看身份" - 查看当前智能体身份
|
|
2404
|
-
- "查看日志" - 查看最近操作日志
|
|
2405
|
-
|
|
2406
|
-
示例请求:
|
|
2407
|
-
- "读取 src/index.ts 文件"
|
|
2408
|
-
- "总结一下 README.md"
|
|
2409
|
-
- "查看当前连接了哪些节点"
|
|
2405
|
+
return `收到了: "${input}"
|
|
2406
|
+
|
|
2407
|
+
我是一个判断力处理智能体,支持自然语言交互。
|
|
2408
|
+
|
|
2409
|
+
可用操作(直接说出即可):
|
|
2410
|
+
- "读取 README.md" - 读取并分析文档
|
|
2411
|
+
- "总结文档" - 总结文档内容
|
|
2412
|
+
- "改进文档,按照X要求" - 改进文档
|
|
2413
|
+
- "查看节点" - 查看已连接的对等节点
|
|
2414
|
+
- "向X发送消息Y" - 向对等节点发送消息
|
|
2415
|
+
- "广播消息X" - 广播消息到所有节点
|
|
2416
|
+
- "查看身份" - 查看当前智能体身份
|
|
2417
|
+
- "查看日志" - 查看最近操作日志
|
|
2418
|
+
|
|
2419
|
+
示例请求:
|
|
2420
|
+
- "读取 src/index.ts 文件"
|
|
2421
|
+
- "总结一下 README.md"
|
|
2422
|
+
- "查看当前连接了哪些节点"
|
|
2410
2423
|
- "向 QmABC... 发送测试消息"`;
|
|
2411
2424
|
}
|
|
2412
2425
|
extractOperationsFromRef(ref) {
|
|
@@ -2431,13 +2444,13 @@ ${this.extractOperationsFromRef(operationsRef)}
|
|
|
2431
2444
|
}
|
|
2432
2445
|
}
|
|
2433
2446
|
return operationLines.length > 0 ? operationLines.join('\n') :
|
|
2434
|
-
` - "读取 README.md" - 读取并分析文档
|
|
2435
|
-
- "总结文档" - 总结文档内容
|
|
2436
|
-
- "改进文档,按照X要求" - 改进文档
|
|
2437
|
-
- "查看节点" - 查看已连接的对等节点
|
|
2438
|
-
- "向X发送消息Y" - 向对等节点发送消息
|
|
2439
|
-
- "广播消息X" - 广播消息到所有节点
|
|
2440
|
-
- "查看身份" - 查看当前智能体身份
|
|
2447
|
+
` - "读取 README.md" - 读取并分析文档
|
|
2448
|
+
- "总结文档" - 总结文档内容
|
|
2449
|
+
- "改进文档,按照X要求" - 改进文档
|
|
2450
|
+
- "查看节点" - 查看已连接的对等节点
|
|
2451
|
+
- "向X发送消息Y" - 向对等节点发送消息
|
|
2452
|
+
- "广播消息X" - 广播消息到所有节点
|
|
2453
|
+
- "查看身份" - 查看当前智能体身份
|
|
2441
2454
|
- "查看日志" - 查看最近操作日志`;
|
|
2442
2455
|
}
|
|
2443
2456
|
async suggestRename(messages) {
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* process-runner.ts — 后台进程管理 (2026-08-12, TaskD)
|
|
3
|
+
*
|
|
4
|
+
* 借鉴 hermes terminal_tool: 长期运行命令不应阻塞对话.
|
|
5
|
+
* - foreground: 命令结束立即返回 (即使超时设长, 不 sleep 阻塞)
|
|
6
|
+
* - background: spawn 后台执行, 返回 session_id, 用 process(action=poll/wait/kill) 管理
|
|
7
|
+
* - 禁止 nohup/setsid/trailing '&' — 用 background 让系统跟踪进程
|
|
8
|
+
*
|
|
9
|
+
* 进程注册表: 模块级 Map<sessionId, {proc, cmd, startedAt, output[]}>, 供 process 工具查询.
|
|
10
|
+
*/
|
|
11
|
+
import { spawn } from 'child_process';
|
|
12
|
+
import * as path from 'path';
|
|
13
|
+
const sessions = new Map();
|
|
14
|
+
let seq = 0;
|
|
15
|
+
function genId() {
|
|
16
|
+
seq++;
|
|
17
|
+
return `proc-${Date.now().toString(36)}-${seq}`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 后台启动一个 shell 命令. 立即返回 session 记录 (不阻塞).
|
|
21
|
+
* 命令字符串经调用方 (runTerminalCommand) 护栏检查.
|
|
22
|
+
*/
|
|
23
|
+
export function spawnBackground(raw, cwd = process.cwd()) {
|
|
24
|
+
const id = genId();
|
|
25
|
+
const session = { id, cmd: raw.slice(0, 300), cwd, startedAt: Date.now(), exitCode: null, status: 'running', output: '', proc: null };
|
|
26
|
+
sessions.set(id, session);
|
|
27
|
+
// Windows 用 cmd /c; POSIX 用 /bin/sh -c
|
|
28
|
+
const shell = process.platform === 'win32' ? 'cmd' : '/bin/sh';
|
|
29
|
+
const shellArgs = process.platform === 'win32' ? ['/c', raw] : ['-c', raw];
|
|
30
|
+
try {
|
|
31
|
+
const proc = spawn(shell, shellArgs, { cwd, env: { ...process.env, GIT_TERMINAL_PROMPT: '0' }, windowsHide: true });
|
|
32
|
+
session.proc = proc;
|
|
33
|
+
proc.stdout.on('data', (d) => { session.output = (session.output + d.toString()).slice(-16000); });
|
|
34
|
+
proc.stderr.on('data', (d) => { session.output = (session.output + d.toString()).slice(-16000); });
|
|
35
|
+
proc.on('close', (code) => { session.exitCode = code; session.status = 'exited'; });
|
|
36
|
+
proc.on('error', (e) => { session.status = 'error'; session.error = e.message; });
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
session.status = 'error';
|
|
40
|
+
session.error = e?.message;
|
|
41
|
+
}
|
|
42
|
+
return session;
|
|
43
|
+
}
|
|
44
|
+
/** 查询后台进程状态 (不阻塞). 返回脱敏视图. */
|
|
45
|
+
export function pollSession(id) {
|
|
46
|
+
const s = sessions.get(id);
|
|
47
|
+
if (!s)
|
|
48
|
+
return { ok: false, found: false };
|
|
49
|
+
return {
|
|
50
|
+
ok: true,
|
|
51
|
+
found: true,
|
|
52
|
+
session: {
|
|
53
|
+
id: s.id, cmd: s.cmd, startedAt: s.startedAt, exitCode: s.exitCode,
|
|
54
|
+
status: s.status, output: s.output.slice(-4000), error: s.error,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** 等待后台进程结束 (最多 timeoutMs). 返回最终状态. */
|
|
59
|
+
export async function waitSession(id, timeoutMs = 30000) {
|
|
60
|
+
const s = sessions.get(id);
|
|
61
|
+
if (!s)
|
|
62
|
+
return { ok: false, error: `未知 session ${id}` };
|
|
63
|
+
if (s.status !== 'running') {
|
|
64
|
+
return { ok: true, session: { id: s.id, status: s.status, exitCode: s.exitCode, output: s.output.slice(-4000) } };
|
|
65
|
+
}
|
|
66
|
+
const deadline = Date.now() + timeoutMs;
|
|
67
|
+
while (Date.now() < deadline && s.status === 'running') {
|
|
68
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
ok: s.status !== 'running',
|
|
72
|
+
session: { id: s.id, status: s.status, exitCode: s.exitCode, output: s.output.slice(-4000), timedOut: s.status === 'running' },
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/** 杀死后台进程. 返回是否已终止. */
|
|
76
|
+
export function killSession(id) {
|
|
77
|
+
const s = sessions.get(id);
|
|
78
|
+
if (!s)
|
|
79
|
+
return { ok: false, reason: `未知 session ${id}` };
|
|
80
|
+
if (s.status !== 'running' || !s.proc) {
|
|
81
|
+
return { ok: true, reason: `进程已 ${s.status}` };
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
if (process.platform === 'win32')
|
|
85
|
+
s.proc.kill('SIGTERM');
|
|
86
|
+
else
|
|
87
|
+
s.proc.kill('SIGTERM');
|
|
88
|
+
s.status = 'killed';
|
|
89
|
+
return { ok: true };
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
return { ok: false, reason: `kill 失败: ${e?.message}` };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** 列出所有后台进程 */
|
|
96
|
+
export function listSessions() {
|
|
97
|
+
return Array.from(sessions.values()).map((s) => ({ id: s.id, cmd: s.cmd, status: s.status, exitCode: s.exitCode }));
|
|
98
|
+
}
|
|
99
|
+
/** session id 合法性 (防注入) */
|
|
100
|
+
export function isValidSessionId(id) {
|
|
101
|
+
return /^proc-[a-z0-9]+-\d+$/.test(id || '');
|
|
102
|
+
}
|
|
103
|
+
export function sessionIdFromPath(p) {
|
|
104
|
+
return path.basename(String(p || '').trim());
|
|
105
|
+
}
|
|
@@ -143,25 +143,25 @@ export async function scanLeftoverSkills(opts = {}) {
|
|
|
143
143
|
// ───────────────────────── B. 经验进化 (LLM 完整总结) ─────────────────────────
|
|
144
144
|
/** 把候选的工具调用记录扩写成完整经验 — LLM prompt (结构化 JSON 输出) */
|
|
145
145
|
export function buildEvolvePrompt(c) {
|
|
146
|
-
return `你是经验总结专家。下面是 Bolloon 智能体在一轮运行中连续成功调用的工具记录 (已运行 ${c.runs || 1} 次)。
|
|
147
|
-
|
|
148
|
-
工具记录:
|
|
149
|
-
名称: ${c.name}
|
|
150
|
-
描述: ${c.description}
|
|
151
|
-
原始记录:
|
|
152
|
-
${String(c.body || '').slice(0, 4000)}
|
|
153
|
-
|
|
154
|
-
请把这份"工具调用记录"扩写成一份完整的、可复用的经验文档, 要求:
|
|
155
|
-
1. name: 一个简短的小写英文名 (字母数字连字符, 概括这套操作)
|
|
156
|
-
2. description: 一句话描述 (什么场景用)
|
|
157
|
-
3. body: 完整经验正文, 按以下结构:
|
|
158
|
-
## 背景 — 这套操作在什么场景下用, 解决什么问题
|
|
159
|
-
## 触发条件 — 什么信号/需求出现时应该使用
|
|
160
|
-
## 流程 — 具体步骤 (保留原始工具名), 每步说明目的
|
|
161
|
-
## 注意事项 — 易错点 / 边界情况 / 已知坑
|
|
162
|
-
## 验证 — 怎么确认结果正确
|
|
163
|
-
|
|
164
|
-
只输出一个 JSON 对象, 不要任何其他文字:
|
|
146
|
+
return `你是经验总结专家。下面是 Bolloon 智能体在一轮运行中连续成功调用的工具记录 (已运行 ${c.runs || 1} 次)。
|
|
147
|
+
|
|
148
|
+
工具记录:
|
|
149
|
+
名称: ${c.name}
|
|
150
|
+
描述: ${c.description}
|
|
151
|
+
原始记录:
|
|
152
|
+
${String(c.body || '').slice(0, 4000)}
|
|
153
|
+
|
|
154
|
+
请把这份"工具调用记录"扩写成一份完整的、可复用的经验文档, 要求:
|
|
155
|
+
1. name: 一个简短的小写英文名 (字母数字连字符, 概括这套操作)
|
|
156
|
+
2. description: 一句话描述 (什么场景用)
|
|
157
|
+
3. body: 完整经验正文, 按以下结构:
|
|
158
|
+
## 背景 — 这套操作在什么场景下用, 解决什么问题
|
|
159
|
+
## 触发条件 — 什么信号/需求出现时应该使用
|
|
160
|
+
## 流程 — 具体步骤 (保留原始工具名), 每步说明目的
|
|
161
|
+
## 注意事项 — 易错点 / 边界情况 / 已知坑
|
|
162
|
+
## 验证 — 怎么确认结果正确
|
|
163
|
+
|
|
164
|
+
只输出一个 JSON 对象, 不要任何其他文字:
|
|
165
165
|
{"name": "...", "description": "...", "body": "..."}`;
|
|
166
166
|
}
|
|
167
167
|
/** 解析 LLM 返回的 JSON (容错: 剥 markdown 代码块, 取第一个 {...}) */
|
|
@@ -266,9 +266,21 @@ export async function writeRunEndSkillCandidates(steps, source, minOk = 2) {
|
|
|
266
266
|
return { wrote: false, reason: `成功工具不足 (${okSteps.length} < ${minOk})` };
|
|
267
267
|
}
|
|
268
268
|
const toolNames = okSteps.map((s) => s.name).slice(0, 5).join(', ');
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
269
|
+
// 2026-08-12 (Task5): 让 run-end 候选 body 更可复用 — 结构化呈现调用链 + 每步作用 + 适用场景.
|
|
270
|
+
const stepLines = okSteps.map((s, i) => {
|
|
271
|
+
const out = s.output ? String(s.output).replace(/\s+/g, ' ').slice(0, 140) : '';
|
|
272
|
+
return `${i + 1}. **${s.name}**${out ? ` — ${out}` : ''}`;
|
|
273
|
+
}).join('\n');
|
|
274
|
+
const body = `## 适用场景\n用户请求需要连续执行工具序列: ${toolNames}.\n\n` +
|
|
275
|
+
`## 调用链\n${stepLines}\n\n` +
|
|
276
|
+
`## 流程要点\n${okSteps.map((s, i) => {
|
|
277
|
+
// 2026-08-12 (Task5 优化): 用真实输出提炼每步作用, 避免千篇一律的"调用 X 获取下一步输入".
|
|
278
|
+
const out = s.output ? String(s.output).replace(/\s+/g, ' ').trim() : '';
|
|
279
|
+
const clue = out && out.length > 30 ? out.slice(0, 60) + '…' : (out || '完成');
|
|
280
|
+
return `- 第 ${i + 1} 步 ${s.name}: ${clue}`;
|
|
281
|
+
}).join('\n')}\n\n` +
|
|
282
|
+
`## 如何验证\n- 涉及改代码: 跑 tsc + vitest 确认无错; 涉及写文件: read_file 读回校验.\n` +
|
|
283
|
+
`- 沉淀为正式 skill 前请人工确认流程可复用, 再 list_skill_candidates / promote_skill 转正\n`;
|
|
272
284
|
// 2026-08-08: 稳定签名 + 固定文件名 → 同一套工具反复跑时合并更新到同一个候选 (runs++)
|
|
273
285
|
const signature = toolSignature(okSteps);
|
|
274
286
|
const candName = `auto-${signature}`;
|
|
@@ -193,6 +193,16 @@ export class WorkflowPivotLoop {
|
|
|
193
193
|
* 传原生 tools + tool_choice auto → LLM 返回结构化 tool_calls.
|
|
194
194
|
*/
|
|
195
195
|
buildOpenAITools() {
|
|
196
|
+
// 2026-08-12 (Task3 认知卸载): 核心编码工具加强 description, 提升模型触发率.
|
|
197
|
+
const USAGE_HINT = {
|
|
198
|
+
write_file: '写/创建文件用此工具 (改代码首选). ',
|
|
199
|
+
edit_file: '修改文件指定内容用此工具 (改代码首选). ',
|
|
200
|
+
read_file: '读取文件内容用此工具. ',
|
|
201
|
+
read_directory: '列目录用此工具. ',
|
|
202
|
+
list_files: '列出文件用此工具. ',
|
|
203
|
+
terminal: '跑 shell 命令/脚本/装依赖/查状态用此工具 (不要用它写文件). ',
|
|
204
|
+
delegate_to_engine: '任务过大或复杂时委派给外部编码引擎 (认知卸载). ',
|
|
205
|
+
};
|
|
196
206
|
const out = [];
|
|
197
207
|
for (const [name, tool] of this.tools) {
|
|
198
208
|
const params = tool.parameters || {};
|
|
@@ -207,7 +217,7 @@ export class WorkflowPivotLoop {
|
|
|
207
217
|
type: 'function',
|
|
208
218
|
function: {
|
|
209
219
|
name,
|
|
210
|
-
description: tool.description || name,
|
|
220
|
+
description: (USAGE_HINT[name] || '') + (tool.description || name),
|
|
211
221
|
parameters: { type: 'object', properties, required },
|
|
212
222
|
},
|
|
213
223
|
});
|
|
@@ -866,8 +876,8 @@ export class WorkflowPivotLoop {
|
|
|
866
876
|
// 完整重装只在 iter=1 和 compact 之后两次
|
|
867
877
|
const lines = fullSystemPrompt.split('\n');
|
|
868
878
|
const identityLine = lines.find((l) => l.includes('你是') && !l.includes('(续)')) ?? '';
|
|
869
|
-
return `[continuation] 你的 persona + tools + judgments 已在首轮 messageHistory 中.
|
|
870
|
-
${identityLine ? identityLine + '\n' : ''}当前 step 别再读 persona 全文, 继续推进任务即可.
|
|
879
|
+
return `[continuation] 你的 persona + tools + judgments 已在首轮 messageHistory 中.
|
|
880
|
+
${identityLine ? identityLine + '\n' : ''}当前 step 别再读 persona 全文, 继续推进任务即可.
|
|
871
881
|
> <final gen> 只在真完成时输出.`;
|
|
872
882
|
}
|
|
873
883
|
/**
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* write-staging.ts — 写操作准备阶段适配 (2026-08-12, TaskC)
|
|
3
|
+
*
|
|
4
|
+
* 借鉴 hermes write_approval.py 的 staging gate: 写操作先 stage (暂存) 再 commit,
|
|
5
|
+
* 保留完整 payload (可重放/回滚) 用于审计与撤销 — 但不强制人工审批 (bolloon 是自主 agent,
|
|
6
|
+
* 写操作直接提交, 只是保留 stage 记录做"准备阶段"审计, 不打断自主循环).
|
|
7
|
+
*
|
|
8
|
+
* 设计:
|
|
9
|
+
* - 每次 write_file / edit_file 写盘前, 记录一条 stage 到 ~/.bolloon/write-log/<ts>-<rand>.json
|
|
10
|
+
* - stage 记录含: 相对路径 / 绝对路径 / action (create|overwrite|edit) / 变更前内容快照 / 变更意图 / 时间
|
|
11
|
+
* - 提供 listStagedWrites / undoLastWrite (撤销最近一次写) / getStagedWrite
|
|
12
|
+
* - 失败静默 (写日志不影响工具主路径)
|
|
13
|
+
*/
|
|
14
|
+
import * as fs from 'fs/promises';
|
|
15
|
+
import * as os from 'os';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
const home = () => process.env.HOME || os.homedir() || '/tmp';
|
|
18
|
+
export function writeLogDir(homeDir = home()) {
|
|
19
|
+
return path.join(homeDir, '.bolloon', 'write-log');
|
|
20
|
+
}
|
|
21
|
+
/** 生成唯一 stage id */
|
|
22
|
+
function genId() {
|
|
23
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 写前暂存: 记录一次写操作 (准备阶段). 返回记录; 失败静默返回 null.
|
|
27
|
+
* @param relPath 相对路径
|
|
28
|
+
* @param beforeContent 变更前内容 (读盘或空)
|
|
29
|
+
* @param afterContent 变更后内容
|
|
30
|
+
* @param action 动作类型
|
|
31
|
+
*/
|
|
32
|
+
export async function stageWrite(relPath, beforeContent, afterContent, action, cwd = process.cwd(), homeDir = home()) {
|
|
33
|
+
try {
|
|
34
|
+
const id = genId();
|
|
35
|
+
const absPath = path.resolve(cwd, relPath);
|
|
36
|
+
const rec = { id, absPath, relPath, action, beforeContent, afterContent, createdAt: Date.now() };
|
|
37
|
+
const dir = writeLogDir(homeDir);
|
|
38
|
+
await fs.mkdir(dir, { recursive: true });
|
|
39
|
+
await fs.writeFile(path.join(dir, `${id}.json`), JSON.stringify(rec, null, 2), 'utf-8');
|
|
40
|
+
return rec;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** 列出最近的暂存写记录 (新→旧) */
|
|
47
|
+
export async function listStagedWrites(homeDir = home()) {
|
|
48
|
+
const dir = writeLogDir(homeDir);
|
|
49
|
+
let files;
|
|
50
|
+
try {
|
|
51
|
+
files = await fs.readdir(dir);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
const out = [];
|
|
57
|
+
for (const f of files.filter(f => f.endsWith('.json')).sort().reverse()) {
|
|
58
|
+
try {
|
|
59
|
+
out.push(JSON.parse(await fs.readFile(path.join(dir, f), 'utf-8')));
|
|
60
|
+
}
|
|
61
|
+
catch { /* 坏文件跳过 */ }
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
/** 撤销最近一次写 (若文件内容仍等于 afterContent → 恢复 beforeContent). 返回是否撤销. */
|
|
66
|
+
export async function undoLastWrite(homeDir = home()) {
|
|
67
|
+
const staged = await listStagedWrites(homeDir);
|
|
68
|
+
if (staged.length === 0)
|
|
69
|
+
return { ok: false, reason: '无暂存写记录' };
|
|
70
|
+
const rec = staged[0];
|
|
71
|
+
try {
|
|
72
|
+
// 仅当文件未被后续修改 (内容仍 = afterContent) 时才安全撤销
|
|
73
|
+
const current = await fs.readFile(rec.absPath, 'utf-8').catch(() => null);
|
|
74
|
+
if (current === rec.afterContent) {
|
|
75
|
+
await fs.writeFile(rec.absPath, rec.beforeContent, 'utf-8');
|
|
76
|
+
// 清理该 stage 记录
|
|
77
|
+
await fs.rm(path.join(writeLogDir(homeDir), `${rec.id}.json`), { force: true }).catch(() => { });
|
|
78
|
+
return { ok: true };
|
|
79
|
+
}
|
|
80
|
+
return { ok: false, reason: '文件已被后续修改, 跳过撤销' };
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
return { ok: false, reason: `撤销失败: ${e?.message}` };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -104,17 +104,17 @@ function checkRsync(cmd) {
|
|
|
104
104
|
function block(reason, host = null) {
|
|
105
105
|
let guidance;
|
|
106
106
|
if (host === PROD_IP) {
|
|
107
|
-
guidance = `请使用标准部署流程:
|
|
108
|
-
后端: bash scripts/deploy.sh --yes
|
|
109
|
-
Demo 正式: bash scripts/deploy-demo.sh <name> --channel prod --yes
|
|
110
|
-
Demo 内测: bash scripts/deploy-demo.sh <name> --channel preview --yes
|
|
111
|
-
Edge/Nginx: bash scripts/deploy-edge.sh --yes
|
|
107
|
+
guidance = `请使用标准部署流程:
|
|
108
|
+
后端: bash scripts/deploy.sh --yes
|
|
109
|
+
Demo 正式: bash scripts/deploy-demo.sh <name> --channel prod --yes
|
|
110
|
+
Demo 内测: bash scripts/deploy-demo.sh <name> --channel preview --yes
|
|
111
|
+
Edge/Nginx: bash scripts/deploy-edge.sh --yes
|
|
112
112
|
详见 Bolloon.md Development Commands。`;
|
|
113
113
|
}
|
|
114
114
|
else if (BRIDGE_VPS_HOSTS.includes(host ?? "")) {
|
|
115
|
-
guidance = `Bridge VPS 必须走 git pull 更新路径:
|
|
116
|
-
ssh root@${host} 'sudo -u boll git -C /opt/boll pull --ff-only'
|
|
117
|
-
scp/rsync 直传会重新制造 orphan worktree。
|
|
115
|
+
guidance = `Bridge VPS 必须走 git pull 更新路径:
|
|
116
|
+
ssh root@${host} 'sudo -u boll git -C /opt/boll pull --ff-only'
|
|
117
|
+
scp/rsync 直传会重新制造 orphan worktree。
|
|
118
118
|
详见 docs/issues/guard-20260408-0445-bridge-vps-orphan-worktree.md。`;
|
|
119
119
|
}
|
|
120
120
|
else {
|
|
@@ -253,9 +253,9 @@ export async function generateJudgmentInjection(filePath, gate, options = {}) {
|
|
|
253
253
|
const header = gate === 3
|
|
254
254
|
? '# Plan Freeze - Decision Principles'
|
|
255
255
|
: `# Gate ${gate} - Active Judgments`;
|
|
256
|
-
return `${header}
|
|
257
|
-
# Path: ${filePath}
|
|
258
|
-
# Confidence: ${(result.confidence * 100).toFixed(0)}%
|
|
259
|
-
|
|
256
|
+
return `${header}
|
|
257
|
+
# Path: ${filePath}
|
|
258
|
+
# Confidence: ${(result.confidence * 100).toFixed(0)}%
|
|
259
|
+
|
|
260
260
|
${result.contextYaml}`;
|
|
261
261
|
}
|