principles-disciple 1.7.3 → 1.7.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/commands/evolution-status.js +4 -2
- package/dist/commands/focus.js +30 -155
- package/dist/constants/diagnostician.d.ts +16 -0
- package/dist/constants/diagnostician.js +60 -0
- package/dist/constants/tools.d.ts +2 -2
- package/dist/constants/tools.js +1 -1
- package/dist/core/config.d.ts +23 -0
- package/dist/core/config.js +26 -1
- package/dist/core/evolution-engine.js +1 -1
- package/dist/core/evolution-logger.d.ts +137 -0
- package/dist/core/evolution-logger.js +256 -0
- package/dist/core/evolution-reducer.d.ts +23 -0
- package/dist/core/evolution-reducer.js +73 -29
- package/dist/core/evolution-types.d.ts +6 -0
- package/dist/core/focus-history.d.ts +145 -0
- package/dist/core/focus-history.js +919 -0
- package/dist/core/init.js +24 -0
- package/dist/core/profile.js +1 -1
- package/dist/core/risk-calculator.d.ts +15 -0
- package/dist/core/risk-calculator.js +48 -0
- package/dist/core/trajectory.d.ts +73 -0
- package/dist/core/trajectory.js +206 -0
- package/dist/hooks/gate.js +130 -20
- package/dist/hooks/lifecycle.js +104 -0
- package/dist/hooks/pain.js +31 -0
- package/dist/hooks/prompt.js +136 -38
- package/dist/hooks/subagent.d.ts +1 -0
- package/dist/hooks/subagent.js +200 -18
- package/dist/http/principles-console-route.d.ts +7 -0
- package/dist/http/principles-console-route.js +301 -1
- package/dist/index.js +0 -2
- package/dist/service/central-database.d.ts +104 -0
- package/dist/service/central-database.js +648 -0
- package/dist/service/control-ui-query-service.d.ts +2 -0
- package/dist/service/control-ui-query-service.js +4 -0
- package/dist/service/empathy-observer-manager.d.ts +8 -0
- package/dist/service/empathy-observer-manager.js +40 -0
- package/dist/service/evolution-query-service.d.ts +155 -0
- package/dist/service/evolution-query-service.js +258 -0
- package/dist/service/evolution-worker.d.ts +4 -0
- package/dist/service/evolution-worker.js +185 -63
- package/dist/service/phase3-input-filter.d.ts +37 -0
- package/dist/service/phase3-input-filter.js +106 -0
- package/dist/service/runtime-summary-service.d.ts +15 -0
- package/dist/service/runtime-summary-service.js +111 -23
- package/dist/tools/deep-reflect.js +8 -2
- package/dist/utils/subagent-probe.d.ts +34 -0
- package/dist/utils/subagent-probe.js +81 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +6 -4
- package/templates/langs/en/core/AGENTS.md +15 -3
- package/templates/langs/en/core/BOOTSTRAP.md +24 -1
- package/templates/langs/en/core/TOOLS.md +9 -0
- package/templates/langs/zh/core/AGENTS.md +15 -3
- package/templates/langs/zh/core/BOOTSTRAP.md +24 -1
- package/templates/langs/zh/core/TOOLS.md +9 -0
- package/templates/langs/zh/skills/pd-auditor/SKILL.md +61 -0
- package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +287 -0
- package/templates/langs/zh/skills/pd-explorer/SKILL.md +65 -0
- package/templates/langs/zh/skills/pd-implementer/SKILL.md +68 -0
- package/templates/langs/zh/skills/pd-planner/SKILL.md +65 -0
- package/templates/langs/zh/skills/pd-reporter/SKILL.md +78 -0
- package/templates/langs/zh/skills/pd-reviewer/SKILL.md +66 -0
- package/dist/core/agent-loader.d.ts +0 -44
- package/dist/core/agent-loader.js +0 -147
- package/dist/tools/agent-spawn.d.ts +0 -54
- package/dist/tools/agent-spawn.js +0 -445
package/dist/hooks/lifecycle.js
CHANGED
|
@@ -4,6 +4,7 @@ import * as readline from 'readline';
|
|
|
4
4
|
import { writePainFlag } from '../core/pain.js';
|
|
5
5
|
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
6
6
|
import { PD_DIRS } from '../core/paths.js';
|
|
7
|
+
import { extractWorkingMemory, mergeWorkingMemory } from '../core/focus-history.js';
|
|
7
8
|
export async function handleBeforeReset(event, ctx) {
|
|
8
9
|
if (!ctx.workspaceDir || !event.messages || event.messages.length === 0) {
|
|
9
10
|
return;
|
|
@@ -147,6 +148,7 @@ export async function extractPainFromSessionFile(sessionFile, ctx) {
|
|
|
147
148
|
export async function handleBeforeCompaction(event, ctx) {
|
|
148
149
|
if (!ctx.workspaceDir)
|
|
149
150
|
return;
|
|
151
|
+
const wctx = WorkspaceContext.fromHookContext(ctx);
|
|
150
152
|
const dateStr = new Date().toISOString().split('T')[0];
|
|
151
153
|
const checkpointPath = path.join(ctx.workspaceDir, PD_DIRS.MEMORY, `${dateStr}.md`);
|
|
152
154
|
const log = `\n## [${new Date().toISOString()}] Pre-Compaction Checkpoint\n` +
|
|
@@ -161,8 +163,110 @@ export async function handleBeforeCompaction(event, ctx) {
|
|
|
161
163
|
catch (_e) {
|
|
162
164
|
console.error(`[PD:Lifecycle] Failed to write pre-compaction checkpoint: ${String(_e)}`);
|
|
163
165
|
}
|
|
166
|
+
// 提取工作记忆(从 sessionFile)
|
|
164
167
|
if (event.sessionFile) {
|
|
165
168
|
await extractPainFromSessionFile(event.sessionFile, ctx);
|
|
169
|
+
// 新增:提取并保存工作记忆
|
|
170
|
+
await extractAndSaveWorkingMemory(event.sessionFile, ctx, wctx);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* 从会话文件提取工作记忆并保存到 CURRENT_FOCUS.md
|
|
175
|
+
*/
|
|
176
|
+
async function extractAndSaveWorkingMemory(sessionFile, ctx, wctx) {
|
|
177
|
+
if (!fs.existsSync(sessionFile)) {
|
|
178
|
+
if (ctx.logger?.debug)
|
|
179
|
+
ctx.logger.debug(`[WorkingMemory] Session file not found: ${sessionFile}`);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const messages = [];
|
|
183
|
+
let lineCount = 0;
|
|
184
|
+
const MAX_LINES = 1000; // 限制处理的行数,避免内存问题
|
|
185
|
+
// 读取会话文件
|
|
186
|
+
const fileStream = fs.createReadStream(sessionFile);
|
|
187
|
+
const rl = readline.createInterface({
|
|
188
|
+
input: fileStream,
|
|
189
|
+
crlfDelay: Infinity
|
|
190
|
+
});
|
|
191
|
+
try {
|
|
192
|
+
for await (const line of rl) {
|
|
193
|
+
if (lineCount >= MAX_LINES)
|
|
194
|
+
break;
|
|
195
|
+
if (!line.trim())
|
|
196
|
+
continue;
|
|
197
|
+
lineCount++;
|
|
198
|
+
try {
|
|
199
|
+
const msg = JSON.parse(line);
|
|
200
|
+
messages.push(msg);
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
// 忽略解析错误
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
finally {
|
|
208
|
+
try {
|
|
209
|
+
rl.close();
|
|
210
|
+
fileStream.destroy();
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
// 忽略清理错误
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (messages.length === 0) {
|
|
217
|
+
if (ctx.logger?.debug)
|
|
218
|
+
ctx.logger.debug(`[WorkingMemory] No messages found in session file`);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
// 提取工作记忆
|
|
222
|
+
const snapshot = extractWorkingMemory(messages, ctx.workspaceDir);
|
|
223
|
+
// 检查是否有有效内容
|
|
224
|
+
if (snapshot.artifacts.length === 0 &&
|
|
225
|
+
snapshot.activeProblems.length === 0 &&
|
|
226
|
+
snapshot.nextActions.length === 0) {
|
|
227
|
+
if (ctx.logger?.debug)
|
|
228
|
+
ctx.logger.debug(`[WorkingMemory] No working memory to preserve`);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
// 读取并更新 CURRENT_FOCUS.md
|
|
232
|
+
const focusPath = wctx.resolve('CURRENT_FOCUS');
|
|
233
|
+
try {
|
|
234
|
+
let content = '';
|
|
235
|
+
if (fs.existsSync(focusPath)) {
|
|
236
|
+
content = fs.readFileSync(focusPath, 'utf-8');
|
|
237
|
+
// 备份原文件(防止损坏)
|
|
238
|
+
const backupPath = `${focusPath}.wm-backup`;
|
|
239
|
+
fs.copyFileSync(focusPath, backupPath);
|
|
240
|
+
}
|
|
241
|
+
// 合并工作记忆
|
|
242
|
+
const updatedContent = mergeWorkingMemory(content, snapshot);
|
|
243
|
+
// 确保目录存在
|
|
244
|
+
const focusDir = path.dirname(focusPath);
|
|
245
|
+
if (!fs.existsSync(focusDir)) {
|
|
246
|
+
fs.mkdirSync(focusDir, { recursive: true });
|
|
247
|
+
}
|
|
248
|
+
// 写入文件
|
|
249
|
+
fs.writeFileSync(focusPath, updatedContent, 'utf-8');
|
|
250
|
+
if (ctx.logger) {
|
|
251
|
+
ctx.logger.info(`[WorkingMemory] Preserved ${snapshot.artifacts.length} artifacts, ` +
|
|
252
|
+
`${snapshot.activeProblems.length} problems, ` +
|
|
253
|
+
`${snapshot.nextActions.length} next actions to CURRENT_FOCUS.md`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
catch (err) {
|
|
257
|
+
console.error(`[PD:Lifecycle] Failed to save working memory: ${String(err)}`);
|
|
258
|
+
// 尝试恢复备份
|
|
259
|
+
const backupPath = `${focusPath}.wm-backup`;
|
|
260
|
+
if (fs.existsSync(backupPath)) {
|
|
261
|
+
try {
|
|
262
|
+
fs.copyFileSync(backupPath, focusPath);
|
|
263
|
+
if (ctx.logger)
|
|
264
|
+
ctx.logger.warn(`[WorkingMemory] Restored from backup after failure`);
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
// 忽略恢复错误
|
|
268
|
+
}
|
|
269
|
+
}
|
|
166
270
|
}
|
|
167
271
|
}
|
|
168
272
|
export async function handleAfterCompaction(event, ctx) {
|
package/dist/hooks/pain.js
CHANGED
|
@@ -6,6 +6,7 @@ import { getSession, trackFriction, resetFriction, getInjectedProbationIds, clea
|
|
|
6
6
|
import { denoiseError, computeHash } from '../utils/hashing.js';
|
|
7
7
|
import { SystemLogger } from '../core/system-logger.js';
|
|
8
8
|
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
9
|
+
import { getEvolutionLogger, createTraceId } from '../core/evolution-logger.js';
|
|
9
10
|
const WRITE_TOOLS = ['write', 'edit', 'apply_patch', 'write_file', 'edit_file', 'replace'];
|
|
10
11
|
function shouldAttributePrincipleToTool(principle, toolName) {
|
|
11
12
|
return principle.contextTags.includes(toolName) || principle.trigger.includes(toolName);
|
|
@@ -38,6 +39,7 @@ export function handleAfterToolCall(event, ctx, api) {
|
|
|
38
39
|
// 0. Special Case: Manual Pain Intervention
|
|
39
40
|
if (event.toolName === 'pain' || event.toolName === 'skill:pain') {
|
|
40
41
|
const reason = params.input || params.arguments || 'Manual intervention';
|
|
42
|
+
const traceId = createTraceId();
|
|
41
43
|
trackFriction(sessionId, 100, 'manual_pain', effectiveWorkspaceDir);
|
|
42
44
|
SystemLogger.log(effectiveWorkspaceDir, 'MANUAL_PAIN', `User manually triggered pain: ${reason}`);
|
|
43
45
|
eventLog.recordPainSignal(sessionId, {
|
|
@@ -53,6 +55,16 @@ export function handleAfterToolCall(event, ctx, api) {
|
|
|
53
55
|
reason: `User intervention: ${reason}`,
|
|
54
56
|
origin: 'user_manual',
|
|
55
57
|
});
|
|
58
|
+
// Log to EvolutionLogger
|
|
59
|
+
const evoLogger = getEvolutionLogger(effectiveWorkspaceDir, wctx.trajectory);
|
|
60
|
+
evoLogger.logPainDetected({
|
|
61
|
+
traceId,
|
|
62
|
+
source: 'manual',
|
|
63
|
+
reason: `User intervention: ${reason}`,
|
|
64
|
+
score: 100,
|
|
65
|
+
toolName: event.toolName,
|
|
66
|
+
sessionId,
|
|
67
|
+
});
|
|
56
68
|
emitPainDetectedEvent(wctx, {
|
|
57
69
|
ts: new Date().toISOString(),
|
|
58
70
|
type: 'pain_detected',
|
|
@@ -63,6 +75,8 @@ export function handleAfterToolCall(event, ctx, api) {
|
|
|
63
75
|
reason: `User intervention: ${reason}`,
|
|
64
76
|
score: 100,
|
|
65
77
|
sessionId,
|
|
78
|
+
traceId,
|
|
79
|
+
agentId: ctx.agentId,
|
|
66
80
|
},
|
|
67
81
|
});
|
|
68
82
|
return;
|
|
@@ -212,12 +226,16 @@ export function handleAfterToolCall(event, ctx, api) {
|
|
|
212
226
|
}
|
|
213
227
|
const isRisk = isRisky(relPath, profile.risk_paths);
|
|
214
228
|
const painScore = computePainScore(1, false, false, isRisk ? 20 : 0, effectiveWorkspaceDir);
|
|
229
|
+
const traceId = createTraceId();
|
|
215
230
|
const painData = {
|
|
216
231
|
score: String(painScore),
|
|
217
232
|
source: 'tool_failure',
|
|
218
233
|
time: new Date().toISOString(),
|
|
219
234
|
reason: `Tool ${event.toolName} failed on ${relPath}. Error: ${event.error ?? 'Non-zero exit code'}`,
|
|
220
235
|
is_risky: String(isRisk),
|
|
236
|
+
trace_id: traceId,
|
|
237
|
+
session_id: sessionId,
|
|
238
|
+
agent_id: ctx.agentId || '',
|
|
221
239
|
};
|
|
222
240
|
writePainFlag(effectiveWorkspaceDir, painData);
|
|
223
241
|
eventLog.recordPainSignal(sessionId, {
|
|
@@ -234,6 +252,17 @@ export function handleAfterToolCall(event, ctx, api) {
|
|
|
234
252
|
severity: painScore >= 70 ? 'severe' : painScore >= 40 ? 'moderate' : 'mild',
|
|
235
253
|
origin: 'system_infer',
|
|
236
254
|
});
|
|
255
|
+
// Log to EvolutionLogger
|
|
256
|
+
const evoLogger = getEvolutionLogger(effectiveWorkspaceDir, wctx.trajectory);
|
|
257
|
+
evoLogger.logPainDetected({
|
|
258
|
+
traceId,
|
|
259
|
+
source: 'tool_failure',
|
|
260
|
+
reason: `Tool ${event.toolName} failed on ${relPath}`,
|
|
261
|
+
score: painScore,
|
|
262
|
+
toolName: event.toolName,
|
|
263
|
+
filePath: relPath,
|
|
264
|
+
sessionId,
|
|
265
|
+
});
|
|
237
266
|
emitPainDetectedEvent(wctx, {
|
|
238
267
|
ts: new Date().toISOString(),
|
|
239
268
|
type: 'pain_detected',
|
|
@@ -244,6 +273,8 @@ export function handleAfterToolCall(event, ctx, api) {
|
|
|
244
273
|
reason: `Tool ${event.toolName} failed on ${relPath}`,
|
|
245
274
|
score: painScore,
|
|
246
275
|
sessionId,
|
|
276
|
+
traceId,
|
|
277
|
+
agentId: ctx.agentId,
|
|
247
278
|
},
|
|
248
279
|
});
|
|
249
280
|
}
|
package/dist/hooks/prompt.js
CHANGED
|
@@ -3,7 +3,7 @@ import * as path from 'path';
|
|
|
3
3
|
import { clearInjectedProbationIds, getSession, resetFriction, setInjectedProbationIds } from '../core/session-tracker.js';
|
|
4
4
|
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
5
5
|
import { defaultContextConfig } from '../types.js';
|
|
6
|
-
import { extractSummary, getHistoryVersions } from '../core/focus-history.js';
|
|
6
|
+
import { extractSummary, getHistoryVersions, parseWorkingMemorySection, workingMemoryToInjection, autoCompressFocus, safeReadCurrentFocus } from '../core/focus-history.js';
|
|
7
7
|
import { empathyObserverManager } from '../service/empathy-observer-manager.js';
|
|
8
8
|
import { PathResolver } from '../core/path-resolver.js';
|
|
9
9
|
/**
|
|
@@ -123,13 +123,15 @@ function resolveEvolutionTask(inProgressTask, messages, maxContextMessages = 4,
|
|
|
123
123
|
const rawTask = typeof inProgressTask.task === 'string' ? inProgressTask.task.trim() : '';
|
|
124
124
|
if (rawTask && rawTask.toLowerCase() !== 'undefined')
|
|
125
125
|
return rawTask;
|
|
126
|
+
if (typeof inProgressTask.id !== 'string' || !inProgressTask.id.trim())
|
|
127
|
+
return null;
|
|
126
128
|
const source = typeof inProgressTask.source === 'string' ? inProgressTask.source.trim() : 'unknown';
|
|
127
129
|
const reason = typeof inProgressTask.reason === 'string' ? inProgressTask.reason.trim() : 'Systemic pain detected';
|
|
128
130
|
const preview = typeof inProgressTask.trigger_text_preview === 'string' && inProgressTask.trigger_text_preview.trim()
|
|
129
131
|
? inProgressTask.trigger_text_preview.trim()
|
|
130
132
|
: 'N/A';
|
|
131
|
-
|
|
132
|
-
|
|
133
|
+
const sessionId = typeof inProgressTask.session_id === 'string' ? inProgressTask.session_id.trim() : '';
|
|
134
|
+
const agentId = typeof inProgressTask.agent_id === 'string' ? inProgressTask.agent_id.trim() : '';
|
|
133
135
|
const conversationContext = includeConversationContext
|
|
134
136
|
? extractRecentConversationContext(messages, maxContextMessages, maxCharsPerMsg)
|
|
135
137
|
: '';
|
|
@@ -142,16 +144,60 @@ function resolveEvolutionTask(inProgressTask, messages, maxContextMessages = 4,
|
|
|
142
144
|
`;
|
|
143
145
|
taskDescription += `**Trigger Text**: "${preview}"
|
|
144
146
|
`;
|
|
147
|
+
if (sessionId) {
|
|
148
|
+
taskDescription += `**Session ID**: ${sessionId}
|
|
149
|
+
`;
|
|
150
|
+
}
|
|
151
|
+
if (agentId) {
|
|
152
|
+
taskDescription += `**Agent ID**: ${agentId}
|
|
153
|
+
`;
|
|
154
|
+
}
|
|
145
155
|
if (conversationContext) {
|
|
146
156
|
taskDescription += `
|
|
147
157
|
---
|
|
148
158
|
**Recent Conversation Context**:
|
|
149
159
|
${conversationContext}`;
|
|
160
|
+
}
|
|
161
|
+
else if (!sessionId) {
|
|
162
|
+
taskDescription += `
|
|
163
|
+
---
|
|
164
|
+
**Note**: 对话上下文不可用。请主动收集证据:
|
|
165
|
+
1. 从 Reason 字段提取关键词,搜索相关代码
|
|
166
|
+
2. 读取 .state/logs/events.jsonl 最近日志
|
|
167
|
+
3. 基于 Reason 中的文件路径定位问题`;
|
|
150
168
|
}
|
|
151
169
|
taskDescription += `
|
|
152
170
|
|
|
153
171
|
---
|
|
154
|
-
|
|
172
|
+
## 执行指令
|
|
173
|
+
|
|
174
|
+
使用 5 Whys 方法进行根因分析,输出 JSON 格式结果。
|
|
175
|
+
|
|
176
|
+
### 必执行步骤:
|
|
177
|
+
1. **Phase 1 - 证据收集**: 读取日志、搜索代码,记录证据来源
|
|
178
|
+
2. **Phase 2 - 因果链构建**: 每个 Why 必须有证据支撑,最多 5 层
|
|
179
|
+
3. **Phase 3 - 根因分类**: 归类为 People/Design/Assumption/Tooling
|
|
180
|
+
4. **Phase 4 - 原则提炼**: 提炼可复用的防护原则
|
|
181
|
+
|
|
182
|
+
### 终止条件(满足任一即停止):
|
|
183
|
+
- 找到可修改代码直接解决的问题
|
|
184
|
+
- 找到缺失的门禁规则或检查机制
|
|
185
|
+
- 连续 2 个 Why 无法提出更深假设
|
|
186
|
+
|
|
187
|
+
### 输出格式:
|
|
188
|
+
\`\`\`json
|
|
189
|
+
{
|
|
190
|
+
"diagnosis_report": {
|
|
191
|
+
"task_id": "...",
|
|
192
|
+
"summary": "一句话总结根因",
|
|
193
|
+
"causal_chain": [...],
|
|
194
|
+
"root_cause": { "category": "Design", "description": "..." },
|
|
195
|
+
"principle": { "trigger_pattern": "...", "action": "..." }
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
\`\`\`
|
|
199
|
+
|
|
200
|
+
详细执行协议请参考你的系统提示词。`;
|
|
155
201
|
return taskDescription;
|
|
156
202
|
}
|
|
157
203
|
/**
|
|
@@ -199,7 +245,6 @@ export function resolveModelFromConfig(modelConfig, logger) {
|
|
|
199
245
|
}
|
|
200
246
|
// 闁哄秶鍘х槐?3: 闁轰焦澹嗙划宥夊冀閻撳海纭€闁挎稑鐗呯粭澶愬绩椤栨稑鐦柨娑樿嫰瑜板倿宕欐ウ娆惧妳闁告稑顭槐?
|
|
201
247
|
if (Array.isArray(modelConfig)) {
|
|
202
|
-
console.warn(`[PD:Prompt] Array model config not supported. Expected "provider/model" string or { primary: "..." } object.`);
|
|
203
248
|
logger?.warn(`[PD:Prompt] Array model config not supported. Expected "provider/model" string or { primary: "..." } object.`);
|
|
204
249
|
return null;
|
|
205
250
|
}
|
|
@@ -359,8 +404,8 @@ You are a **self-evolving AI agent** powered by Principles Disciple.
|
|
|
359
404
|
**Tool Routing Rules**:
|
|
360
405
|
- Use the current session for the normal user reply.
|
|
361
406
|
- Use sessions_send for cross-session messaging.
|
|
362
|
-
- Use agents_list / sessions_list / sessions_spawn for peer-agent or session orchestration.
|
|
363
|
-
- Use
|
|
407
|
+
- Use agents_list / sessions_list / sessions_spawn for peer-agent or peer-session orchestration.
|
|
408
|
+
- Use sessions_spawn with pd-diagnostician/pd-explorer/etc skills for internal worker tasks.
|
|
364
409
|
|
|
365
410
|
## 妫e啯鎯?INTERNAL SYSTEM LAYOUT
|
|
366
411
|
- Your core plugin logic is rooted at: ${PathResolver.getExtensionRoot() || 'EXTENSION_ROOT (unresolved)'}
|
|
@@ -378,7 +423,7 @@ You are a **self-evolving AI agent** powered by Principles Disciple.
|
|
|
378
423
|
trustContext += `Hygiene: ${hygiene.persistenceCount} persists today\n`;
|
|
379
424
|
// Stage-based restrictions
|
|
380
425
|
if (safeStage === 1) {
|
|
381
|
-
trustContext += `ACTION CONSTRAINT: You are in READ-ONLY MODE. You MUST use
|
|
426
|
+
trustContext += `ACTION CONSTRAINT: You are in READ-ONLY MODE. You MUST use sessions_spawn with the pd-diagnostician skill to recover trust before writing files.\n`;
|
|
382
427
|
}
|
|
383
428
|
else if (safeStage === 2) {
|
|
384
429
|
trustContext += `ACTION CONSTRAINT: LIMITED MODE. You are restricted to a maximum of 50 lines per edit.\n`;
|
|
@@ -397,47 +442,71 @@ You are a **self-evolving AI agent** powered by Principles Disciple.
|
|
|
397
442
|
if (fs.existsSync(queuePath)) {
|
|
398
443
|
try {
|
|
399
444
|
const queue = JSON.parse(fs.readFileSync(queuePath, 'utf8'));
|
|
400
|
-
const
|
|
401
|
-
|
|
445
|
+
const inProgressTasks = [...queue]
|
|
446
|
+
.filter((t) => t.status === 'in_progress')
|
|
447
|
+
.sort((a, b) => {
|
|
448
|
+
const scoreA = Number.isFinite(a?.score) ? Number(a.score) : 0;
|
|
449
|
+
const scoreB = Number.isFinite(b?.score) ? Number(b.score) : 0;
|
|
450
|
+
return scoreB - scoreA;
|
|
451
|
+
});
|
|
452
|
+
for (const inProgressTask of inProgressTasks) {
|
|
402
453
|
const resolvedTask = resolveEvolutionTask(inProgressTask, event.messages, contextConfig.evolutionContext.maxMessages, contextConfig.evolutionContext.maxCharsPerMessage, contextConfig.evolutionContext.enabled);
|
|
403
454
|
if (!resolvedTask) {
|
|
404
|
-
|
|
455
|
+
continue;
|
|
405
456
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
evolutionDirective = `<evolution_task priority="high">
|
|
457
|
+
const escapedTask = JSON.stringify(resolvedTask);
|
|
458
|
+
logger?.info(`[PD:Prompt] Injecting EVOLUTION TASK for: ${inProgressTask.id}`);
|
|
459
|
+
if (trigger === 'user') {
|
|
460
|
+
evolutionDirective = `<evolution_task priority="high">
|
|
411
461
|
TASK: ${escapedTask}
|
|
412
462
|
|
|
413
|
-
REQUIRED ACTION:
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
463
|
+
REQUIRED ACTION (两阶段回复):
|
|
464
|
+
|
|
465
|
+
**阶段 1 - 立即回复用户:**
|
|
466
|
+
简要告知用户你检测到的问题,例如:
|
|
467
|
+
"我注意到刚才的操作遇到了一些问题(简述问题),我已安排诊断代理在后台分析根因。分析完成后会继续告诉你结果。"
|
|
468
|
+
|
|
469
|
+
然后调用:
|
|
470
|
+
sessions_spawn(task="使用 pd-diagnostician skill 执行诊断任务。\n\n任务: ${escapedTask}")
|
|
471
|
+
|
|
472
|
+
**阶段 2 - 收到子代理结果后:**
|
|
473
|
+
当收到诊断代理的完成消息后,用以下格式回复用户:
|
|
474
|
+
|
|
475
|
+
"**诊断完成** 🔍
|
|
476
|
+
|
|
477
|
+
基于后台分析结果:
|
|
478
|
+
- **根因**: [一句话总结]
|
|
479
|
+
- **分类**: [People/Design/Assumption/Tooling]
|
|
480
|
+
- **建议**: [可执行的改进建议]
|
|
481
|
+
|
|
482
|
+
相关原则已记录到 PRINCIPLES.md。"
|
|
483
|
+
|
|
484
|
+
IMPORTANT:
|
|
418
485
|
- Do NOT reply with "[EVOLUTION_ACK]".
|
|
419
|
-
-
|
|
486
|
+
- 子代理结果会作为新消息到达,届时再进行阶段2的回复。
|
|
420
487
|
</evolution_task>\n`;
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
evolutionDirective = `<evolution_task priority="critical">
|
|
424
491
|
TASK: ${escapedTask}
|
|
425
492
|
|
|
426
493
|
REQUIRED ACTION:
|
|
427
494
|
- Start diagnostics immediately by calling:
|
|
428
|
-
|
|
429
|
-
- Treat pd_run_worker as an internal Principles Disciple worker, not a peer-session messaging tool.
|
|
495
|
+
sessions_spawn(task="使用 pd-diagnostician skill 执行诊断任务。\n\n任务: ${escapedTask}")
|
|
430
496
|
- Do NOT reply with "[EVOLUTION_ACK]".
|
|
431
497
|
</evolution_task>\n`;
|
|
432
|
-
}
|
|
433
498
|
}
|
|
499
|
+
break;
|
|
500
|
+
}
|
|
501
|
+
if (!evolutionDirective && inProgressTasks.length > 0) {
|
|
502
|
+
logger?.warn('[PD:Prompt] Skipping evolution task injection because task payload is invalid.');
|
|
434
503
|
}
|
|
435
504
|
}
|
|
436
505
|
catch (e) {
|
|
437
506
|
logger?.error(`[PD:Prompt] Failed to parse EVOLUTION_QUEUE: ${String(e)}`);
|
|
438
507
|
}
|
|
439
508
|
}
|
|
440
|
-
// Inject evolution
|
|
509
|
+
// Inject queue-derived evolution task at the front of prependContext
|
|
441
510
|
if (evolutionDirective) {
|
|
442
511
|
prependContext = evolutionDirective + prependContext;
|
|
443
512
|
}
|
|
@@ -541,31 +610,56 @@ ACTION: Run self-audit. If stable, reply ONLY with "HEARTBEAT_OK".
|
|
|
541
610
|
}
|
|
542
611
|
// Project Context (configurable: full/summary/off) - moved to appendSystemContext for WebUI UX
|
|
543
612
|
let projectContextContent = '';
|
|
613
|
+
let workingMemoryContent = '';
|
|
544
614
|
if (!isMinimalMode && contextConfig.projectFocus !== 'off') {
|
|
545
615
|
const focusPath = wctx.resolve('CURRENT_FOCUS');
|
|
546
|
-
|
|
616
|
+
const extensionRoot = PathResolver.getExtensionRoot();
|
|
617
|
+
// 🔒 安全读取:自动验证格式,损坏时从模板恢复
|
|
618
|
+
const { content: currentFocus, recovered, validationErrors } = safeReadCurrentFocus(focusPath, extensionRoot || '', logger);
|
|
619
|
+
if (recovered) {
|
|
620
|
+
logger?.info?.(`[PD:Prompt] CURRENT_FOCUS.md was recovered from template`);
|
|
621
|
+
}
|
|
622
|
+
if (validationErrors.length > 0) {
|
|
623
|
+
logger?.warn?.(`[PD:Prompt] CURRENT_FOCUS validation errors: ${validationErrors.join(', ')}`);
|
|
624
|
+
}
|
|
625
|
+
if (currentFocus.trim()) {
|
|
547
626
|
try {
|
|
548
|
-
|
|
549
|
-
|
|
627
|
+
// 🚀 自动压缩门禁:检查文件大小,超过阈值自动压缩
|
|
628
|
+
const stateDir = wctx.stateDir;
|
|
629
|
+
const compressResult = autoCompressFocus(focusPath, workspaceDir, stateDir);
|
|
630
|
+
if (compressResult.compressed) {
|
|
631
|
+
logger?.info?.(`[PD:Prompt] Auto-compressed CURRENT_FOCUS: ${compressResult.oldLines} → ${compressResult.newLines} lines. Milestones archived: ${compressResult.milestonesArchived}`);
|
|
632
|
+
}
|
|
633
|
+
else if (compressResult.reason === 'Rate limited (24h interval)') {
|
|
634
|
+
logger?.debug?.(`[PD:Prompt] Auto-compress skipped: ${compressResult.reason}`);
|
|
635
|
+
}
|
|
636
|
+
// 重新读取(可能被压缩更新了)
|
|
637
|
+
const finalContent = fs.readFileSync(focusPath, 'utf8').trim();
|
|
638
|
+
if (finalContent) {
|
|
639
|
+
// 解析工作记忆部分(用于独立注入)
|
|
640
|
+
const workingMemorySnapshot = parseWorkingMemorySection(finalContent);
|
|
641
|
+
if (workingMemorySnapshot) {
|
|
642
|
+
workingMemoryContent = workingMemoryToInjection(workingMemorySnapshot);
|
|
643
|
+
}
|
|
550
644
|
if (contextConfig.projectFocus === 'summary') {
|
|
551
645
|
// Summary mode: intelligent extraction prioritizing key sections
|
|
552
|
-
projectContextContent = extractSummary(
|
|
646
|
+
projectContextContent = extractSummary(finalContent, 30);
|
|
553
647
|
}
|
|
554
648
|
else {
|
|
555
649
|
// Full mode: current version + recent history (3 versions)
|
|
556
650
|
const historyVersions = getHistoryVersions(focusPath, 3);
|
|
557
651
|
if (historyVersions.length > 0) {
|
|
558
|
-
const historySections = historyVersions.map((v, i) => `\n---\n\n
|
|
559
|
-
projectContextContent = `${
|
|
652
|
+
const historySections = historyVersions.map((v, i) => `\n---\n\n**历史版本 v${historyVersions.length - i}**\n\n${v}`).join('');
|
|
653
|
+
projectContextContent = `${finalContent}${historySections}`;
|
|
560
654
|
}
|
|
561
655
|
else {
|
|
562
|
-
projectContextContent =
|
|
656
|
+
projectContextContent = finalContent;
|
|
563
657
|
}
|
|
564
658
|
}
|
|
565
659
|
}
|
|
566
660
|
}
|
|
567
661
|
catch (e) {
|
|
568
|
-
logger?.error(`[PD:Prompt] Failed to
|
|
662
|
+
logger?.error(`[PD:Prompt] Failed to process CURRENT_FOCUS: ${String(e)}`);
|
|
569
663
|
}
|
|
570
664
|
}
|
|
571
665
|
}
|
|
@@ -607,12 +701,16 @@ ACTION: Run self-audit. If stable, reply ONLY with "HEARTBEAT_OK".
|
|
|
607
701
|
logger?.warn?.(`[PD:Prompt] Failed to load evolution principles: ${String(e)}`);
|
|
608
702
|
}
|
|
609
703
|
// Build appendSystemContext with recency effect
|
|
610
|
-
// Content order (most important last): project_context -> reflection_log -> thinking_os -> principles
|
|
704
|
+
// Content order (most important last): project_context -> working_memory -> reflection_log -> thinking_os -> principles
|
|
611
705
|
const appendParts = [];
|
|
612
706
|
// 1. Project Context (lowest priority, goes first)
|
|
613
707
|
if (projectContextContent) {
|
|
614
708
|
appendParts.push(`<project_context>\n${projectContextContent}\n</project_context>`);
|
|
615
709
|
}
|
|
710
|
+
// 1.5. Working Memory (preserved from last compaction)
|
|
711
|
+
if (workingMemoryContent) {
|
|
712
|
+
appendParts.push(workingMemoryContent);
|
|
713
|
+
}
|
|
616
714
|
// 2. Reflection Log
|
|
617
715
|
if (reflectionLogContent) {
|
|
618
716
|
appendParts.push(`<reflection_log>\n${reflectionLogContent}\n</reflection_log>`);
|
package/dist/hooks/subagent.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ type SubagentEndedHookContext = PluginHookSubagentContext & {
|
|
|
4
4
|
api?: EmpathyObserverApi;
|
|
5
5
|
workspaceDir?: string;
|
|
6
6
|
sessionId?: string;
|
|
7
|
+
agentId?: string;
|
|
7
8
|
};
|
|
8
9
|
export declare function handleSubagentEnded(event: PluginHookSubagentEndedEvent, ctx: SubagentEndedHookContext): Promise<void>;
|
|
9
10
|
export {};
|