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
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EvolutionLogger - 进化流程日志服务
|
|
3
|
+
*
|
|
4
|
+
* 提供两层日志:
|
|
5
|
+
* - 技术层:结构化 JSON,包含 trace_id、stage、metadata
|
|
6
|
+
* - 用户层:中文摘要,便于小白用户理解
|
|
7
|
+
*
|
|
8
|
+
* 日志写入两个地方:
|
|
9
|
+
* 1. SYSTEM_LOG (system.jsonl) - 技术细节
|
|
10
|
+
* 2. evolution_events 表 (SQLite) - 结构化查询
|
|
11
|
+
*/
|
|
12
|
+
import { randomBytes } from 'crypto';
|
|
13
|
+
import { SystemLogger } from './system-logger.js';
|
|
14
|
+
// 阶段对应的中文标签
|
|
15
|
+
export const STAGE_LABELS = {
|
|
16
|
+
pain_detected: '痛点检测',
|
|
17
|
+
queued: '加入队列',
|
|
18
|
+
started: '开始处理',
|
|
19
|
+
analyzing: '分析中',
|
|
20
|
+
principle_generated: '原则生成',
|
|
21
|
+
completed: '完成',
|
|
22
|
+
failed: '失败',
|
|
23
|
+
};
|
|
24
|
+
// 阶段对应的颜色(用于 UI)
|
|
25
|
+
export const STAGE_COLORS = {
|
|
26
|
+
pain_detected: '#ef4444', // 红色
|
|
27
|
+
queued: '#f59e0b', // 橙色
|
|
28
|
+
started: '#3b82f6', // 蓝色
|
|
29
|
+
analyzing: '#8b5cf6', // 紫色
|
|
30
|
+
principle_generated: '#10b981', // 绿色
|
|
31
|
+
completed: '#22c55e', // 绿色
|
|
32
|
+
failed: '#dc2626', // 深红色
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* 创建新的 trace_id
|
|
36
|
+
* 格式: ev_{timestamp}_{random}
|
|
37
|
+
* 使用 crypto.randomBytes 确保不可预测性
|
|
38
|
+
*/
|
|
39
|
+
export function createTraceId() {
|
|
40
|
+
const timestamp = Date.now().toString(36);
|
|
41
|
+
const random = randomBytes(3).toString('hex');
|
|
42
|
+
return `ev_${timestamp}_${random}`;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* EvolutionLogger 类
|
|
46
|
+
* 管理进化流程的日志记录
|
|
47
|
+
*/
|
|
48
|
+
export class EvolutionLogger {
|
|
49
|
+
workspaceDir;
|
|
50
|
+
trajectory;
|
|
51
|
+
constructor(workspaceDir, trajectory) {
|
|
52
|
+
this.workspaceDir = workspaceDir;
|
|
53
|
+
this.trajectory = trajectory || null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 记录进化事件
|
|
57
|
+
* 同时写入 SYSTEM_LOG 和 evolution_events 表
|
|
58
|
+
*/
|
|
59
|
+
log(input) {
|
|
60
|
+
const entry = {
|
|
61
|
+
...input,
|
|
62
|
+
level: input.level || 'info',
|
|
63
|
+
timestamp: new Date().toISOString(),
|
|
64
|
+
};
|
|
65
|
+
// 1. 写入 SYSTEM_LOG (技术细节)
|
|
66
|
+
SystemLogger.log(this.workspaceDir, 'EVOLUTION', JSON.stringify({
|
|
67
|
+
traceId: entry.traceId,
|
|
68
|
+
stage: entry.stage,
|
|
69
|
+
level: entry.level,
|
|
70
|
+
message: entry.message,
|
|
71
|
+
taskId: entry.taskId,
|
|
72
|
+
sessionId: entry.sessionId,
|
|
73
|
+
metadata: entry.metadata,
|
|
74
|
+
}));
|
|
75
|
+
// 2. 写入 evolution_events 表 (如果有 trajectory)
|
|
76
|
+
if (this.trajectory) {
|
|
77
|
+
try {
|
|
78
|
+
this.trajectory.recordEvolutionEvent({
|
|
79
|
+
traceId: entry.traceId,
|
|
80
|
+
taskId: entry.taskId,
|
|
81
|
+
stage: entry.stage,
|
|
82
|
+
level: entry.level,
|
|
83
|
+
message: entry.message,
|
|
84
|
+
summary: entry.summary,
|
|
85
|
+
metadata: entry.metadata,
|
|
86
|
+
createdAt: entry.timestamp,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
// 数据库写入失败不影响主流程
|
|
91
|
+
console.error(`[EvolutionLogger] Failed to write to trajectory: ${String(err)}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* 记录 pain_detected 事件
|
|
97
|
+
*/
|
|
98
|
+
logPainDetected(params) {
|
|
99
|
+
this.log({
|
|
100
|
+
traceId: params.traceId,
|
|
101
|
+
stage: 'pain_detected',
|
|
102
|
+
level: 'info',
|
|
103
|
+
message: `Pain detected: ${params.source} - ${params.reason}`,
|
|
104
|
+
summary: `检测到痛点:${params.source} - ${params.reason}`,
|
|
105
|
+
metadata: {
|
|
106
|
+
score: params.score,
|
|
107
|
+
toolName: params.toolName,
|
|
108
|
+
filePath: params.filePath,
|
|
109
|
+
},
|
|
110
|
+
sessionId: params.sessionId,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 记录 queued 事件
|
|
115
|
+
*/
|
|
116
|
+
logQueued(params) {
|
|
117
|
+
this.log({
|
|
118
|
+
traceId: params.traceId,
|
|
119
|
+
taskId: params.taskId,
|
|
120
|
+
stage: 'queued',
|
|
121
|
+
level: 'info',
|
|
122
|
+
message: `Task ${params.taskId} enqueued with score ${params.score}`,
|
|
123
|
+
summary: `任务 ${params.taskId} 已加入进化队列,优先级分数 ${params.score}`,
|
|
124
|
+
metadata: {
|
|
125
|
+
score: params.score,
|
|
126
|
+
source: params.source,
|
|
127
|
+
reason: params.reason,
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* 记录 started 事件
|
|
133
|
+
*/
|
|
134
|
+
logStarted(params) {
|
|
135
|
+
this.log({
|
|
136
|
+
traceId: params.traceId,
|
|
137
|
+
taskId: params.taskId,
|
|
138
|
+
stage: 'started',
|
|
139
|
+
level: 'info',
|
|
140
|
+
message: `Task ${params.taskId} started processing`,
|
|
141
|
+
summary: `任务 ${params.taskId} 开始处理,Diagnostician 正在分析...`,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* 记录 analyzing 事件
|
|
146
|
+
*/
|
|
147
|
+
logAnalyzing(params) {
|
|
148
|
+
this.log({
|
|
149
|
+
traceId: params.traceId,
|
|
150
|
+
taskId: params.taskId,
|
|
151
|
+
stage: 'analyzing',
|
|
152
|
+
level: 'info',
|
|
153
|
+
message: `Task ${params.taskId} analysis started`,
|
|
154
|
+
summary: `任务 ${params.taskId} 正在分析根因...`,
|
|
155
|
+
metadata: {
|
|
156
|
+
analysisType: params.analysisType,
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* 记录 principle_generated 事件
|
|
162
|
+
*/
|
|
163
|
+
logPrincipleGenerated(params) {
|
|
164
|
+
const truncatedText = params.principleText.length > 100
|
|
165
|
+
? params.principleText.substring(0, 100) + '...'
|
|
166
|
+
: params.principleText;
|
|
167
|
+
this.log({
|
|
168
|
+
traceId: params.traceId,
|
|
169
|
+
taskId: params.taskId,
|
|
170
|
+
stage: 'principle_generated',
|
|
171
|
+
level: 'info',
|
|
172
|
+
message: `Principle ${params.principleId} generated for task ${params.taskId}`,
|
|
173
|
+
summary: `生成新原则:${truncatedText}`,
|
|
174
|
+
metadata: {
|
|
175
|
+
principleId: params.principleId,
|
|
176
|
+
principleText: params.principleText,
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* 记录 completed 事件
|
|
182
|
+
*/
|
|
183
|
+
logCompleted(params) {
|
|
184
|
+
let summary;
|
|
185
|
+
if (params.resolution === 'marker_detected') {
|
|
186
|
+
summary = `任务 ${params.taskId} 完成,已生成 ${params.principlesGenerated || 0} 条原则`;
|
|
187
|
+
}
|
|
188
|
+
else if (params.resolution === 'auto_completed_timeout') {
|
|
189
|
+
summary = `任务 ${params.taskId} 超时自动完成`;
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
summary = `任务 ${params.taskId} 已完成`;
|
|
193
|
+
}
|
|
194
|
+
this.log({
|
|
195
|
+
traceId: params.traceId,
|
|
196
|
+
taskId: params.taskId,
|
|
197
|
+
stage: 'completed',
|
|
198
|
+
level: params.resolution === 'auto_completed_timeout' ? 'warn' : 'info',
|
|
199
|
+
message: `Task ${params.taskId} completed with resolution: ${params.resolution}`,
|
|
200
|
+
summary,
|
|
201
|
+
metadata: {
|
|
202
|
+
resolution: params.resolution,
|
|
203
|
+
durationMs: params.durationMs,
|
|
204
|
+
principlesGenerated: params.principlesGenerated,
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* 记录 failed 事件
|
|
210
|
+
*/
|
|
211
|
+
logFailed(params) {
|
|
212
|
+
this.log({
|
|
213
|
+
traceId: params.traceId,
|
|
214
|
+
taskId: params.taskId,
|
|
215
|
+
stage: 'failed',
|
|
216
|
+
level: 'error',
|
|
217
|
+
message: `Task ${params.taskId} failed: ${params.error}`,
|
|
218
|
+
summary: `任务 ${params.taskId} 失败:${params.error}`,
|
|
219
|
+
metadata: {
|
|
220
|
+
error: params.error,
|
|
221
|
+
stack: params.stack,
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// 单例缓存:键格式为 "workspaceDir" 或 "workspaceDir::with_trajectory"
|
|
227
|
+
const loggerCache = new Map();
|
|
228
|
+
/**
|
|
229
|
+
* 获取 EvolutionLogger 实例(单例)
|
|
230
|
+
*
|
|
231
|
+
* 注意:带 trajectory 和不带 trajectory 的请求会返回不同的实例,
|
|
232
|
+
* 因为 trajectory 影响事件持久化行为。
|
|
233
|
+
*/
|
|
234
|
+
export function getEvolutionLogger(workspaceDir, trajectory) {
|
|
235
|
+
// 缓存键区分是否带 trajectory,避免持久化行为不一致
|
|
236
|
+
const cacheKey = trajectory ? `${workspaceDir}::with_trajectory` : workspaceDir;
|
|
237
|
+
const cached = loggerCache.get(cacheKey);
|
|
238
|
+
if (cached) {
|
|
239
|
+
return cached;
|
|
240
|
+
}
|
|
241
|
+
const logger = new EvolutionLogger(workspaceDir, trajectory);
|
|
242
|
+
loggerCache.set(cacheKey, logger);
|
|
243
|
+
return logger;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* 清理指定 workspace 的 logger 缓存
|
|
247
|
+
*/
|
|
248
|
+
export function disposeEvolutionLogger(workspaceDir) {
|
|
249
|
+
return loggerCache.delete(workspaceDir);
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* 清理所有 logger 缓存(用于测试或进程退出)
|
|
253
|
+
*/
|
|
254
|
+
export function disposeAllEvolutionLoggers() {
|
|
255
|
+
loggerCache.clear();
|
|
256
|
+
}
|
|
@@ -11,6 +11,17 @@ export interface EvolutionReducer {
|
|
|
11
11
|
deprecate(principleId: string, reason: string): void;
|
|
12
12
|
rollbackPrinciple(principleId: string, reason: string): void;
|
|
13
13
|
recordProbationFeedback(principleId: string, success: boolean): void;
|
|
14
|
+
/**
|
|
15
|
+
* Creates a new principle with generalized trigger/action from diagnostician.
|
|
16
|
+
* Called after diagnostician analysis to create principle directly.
|
|
17
|
+
*/
|
|
18
|
+
createPrincipleFromDiagnosis(params: {
|
|
19
|
+
painId: string;
|
|
20
|
+
painType: 'tool_failure' | 'subagent_error' | 'user_frustration';
|
|
21
|
+
triggerPattern: string;
|
|
22
|
+
action: string;
|
|
23
|
+
source: string;
|
|
24
|
+
}): string | null;
|
|
14
25
|
getStats(): {
|
|
15
26
|
candidateCount: number;
|
|
16
27
|
probationCount: number;
|
|
@@ -43,6 +54,18 @@ export declare class EvolutionReducerImpl implements EvolutionReducer {
|
|
|
43
54
|
deprecate(principleId: string, reason: string): void;
|
|
44
55
|
rollbackPrinciple(principleId: string, reason: string): void;
|
|
45
56
|
recordProbationFeedback(principleId: string, success: boolean): void;
|
|
57
|
+
/**
|
|
58
|
+
* Creates a new principle with generalized trigger/action from diagnostician.
|
|
59
|
+
* Called after diagnostician analysis to create principle directly (no intermediate overfitted principle).
|
|
60
|
+
* @returns the new principle ID, or null if creation failed
|
|
61
|
+
*/
|
|
62
|
+
createPrincipleFromDiagnosis(params: {
|
|
63
|
+
painId: string;
|
|
64
|
+
painType: 'tool_failure' | 'subagent_error' | 'user_frustration';
|
|
65
|
+
triggerPattern: string;
|
|
66
|
+
action: string;
|
|
67
|
+
source: string;
|
|
68
|
+
}): string | null;
|
|
46
69
|
getStats(): {
|
|
47
70
|
candidateCount: number;
|
|
48
71
|
probationCount: number;
|
|
@@ -136,6 +136,66 @@ export class EvolutionReducerImpl {
|
|
|
136
136
|
this.deprecate(principleId, 'conflict_detected');
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Creates a new principle with generalized trigger/action from diagnostician.
|
|
141
|
+
* Called after diagnostician analysis to create principle directly (no intermediate overfitted principle).
|
|
142
|
+
* @returns the new principle ID, or null if creation failed
|
|
143
|
+
*/
|
|
144
|
+
createPrincipleFromDiagnosis(params) {
|
|
145
|
+
// Check blacklist first
|
|
146
|
+
if (this.isBlacklisted(params.painId, params.triggerPattern)) {
|
|
147
|
+
SystemLogger.log(this.workspaceDir, 'PRINCIPLE_BLACKLISTED', `Principle creation blocked by blacklist for trigger: "${params.triggerPattern.slice(0, 50)}..."`);
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
// Check if a principle already exists for this painId
|
|
151
|
+
const existingPrinciple = [...this.principles.values()].find(p => p.source.painId === params.painId);
|
|
152
|
+
if (existingPrinciple) {
|
|
153
|
+
// Update existing principle instead of creating new one
|
|
154
|
+
existingPrinciple.trigger = params.triggerPattern;
|
|
155
|
+
existingPrinciple.action = params.action;
|
|
156
|
+
existingPrinciple.text = `When ${params.triggerPattern}, then ${params.action}.`;
|
|
157
|
+
existingPrinciple.version += 1;
|
|
158
|
+
SystemLogger.log(this.workspaceDir, 'PRINCIPLE_UPDATED', `Principle ${existingPrinciple.id} updated from diagnostician: "${params.triggerPattern.slice(0, 50)}..."`);
|
|
159
|
+
return existingPrinciple.id;
|
|
160
|
+
}
|
|
161
|
+
// Create new principle with generalized content
|
|
162
|
+
const principleId = this.nextPrincipleId();
|
|
163
|
+
const now = new Date().toISOString();
|
|
164
|
+
const principle = {
|
|
165
|
+
id: principleId,
|
|
166
|
+
version: 1,
|
|
167
|
+
text: `When ${params.triggerPattern}, then ${params.action}.`,
|
|
168
|
+
source: {
|
|
169
|
+
painId: params.painId,
|
|
170
|
+
painType: params.painType,
|
|
171
|
+
timestamp: now,
|
|
172
|
+
},
|
|
173
|
+
trigger: params.triggerPattern,
|
|
174
|
+
action: params.action,
|
|
175
|
+
contextTags: [params.source],
|
|
176
|
+
validation: { successCount: 0, conflictCount: 0 },
|
|
177
|
+
status: 'candidate',
|
|
178
|
+
feedbackScore: 0,
|
|
179
|
+
usageCount: 0,
|
|
180
|
+
createdAt: now,
|
|
181
|
+
};
|
|
182
|
+
this.principles.set(principleId, principle);
|
|
183
|
+
this.emitSync({
|
|
184
|
+
ts: now,
|
|
185
|
+
type: 'candidate_created',
|
|
186
|
+
data: {
|
|
187
|
+
painId: principle.source.painId,
|
|
188
|
+
principleId,
|
|
189
|
+
trigger: params.triggerPattern,
|
|
190
|
+
action: params.action,
|
|
191
|
+
status: 'candidate',
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
// Auto-promote since it's already generalized
|
|
195
|
+
this.promote(principleId, 'diagnostician_generalized');
|
|
196
|
+
SystemLogger.log(this.workspaceDir, 'PRINCIPLE_CREATED', `Principle ${principleId} created from diagnostician: "${params.triggerPattern.slice(0, 50)}..."`);
|
|
197
|
+
return principleId;
|
|
198
|
+
}
|
|
139
199
|
getStats() {
|
|
140
200
|
return {
|
|
141
201
|
candidateCount: this.getCandidatePrinciples().length,
|
|
@@ -254,7 +314,6 @@ export class EvolutionReducerImpl {
|
|
|
254
314
|
}
|
|
255
315
|
onPainDetected(data, eventTs) {
|
|
256
316
|
const trigger = String(data.reason ?? data.source ?? 'unknown trigger');
|
|
257
|
-
const action = `Prevent recurrence for: ${String(data.source ?? 'unknown')}`;
|
|
258
317
|
// Defense in depth: protocol/system tokens must never become principles,
|
|
259
318
|
// even if a pain_detected event is emitted from a new callsite in the future.
|
|
260
319
|
if (shouldIgnorePainProtocolText(trigger)) {
|
|
@@ -263,38 +322,23 @@ export class EvolutionReducerImpl {
|
|
|
263
322
|
if (this.isBlacklisted(data.painId, trigger)) {
|
|
264
323
|
return;
|
|
265
324
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
text: `When ${trigger}, then ${action}.`,
|
|
271
|
-
source: {
|
|
272
|
-
painId: data.painId,
|
|
273
|
-
painType: data.painType,
|
|
274
|
-
timestamp: eventTs,
|
|
275
|
-
},
|
|
276
|
-
trigger,
|
|
277
|
-
action,
|
|
278
|
-
contextTags: [data.source],
|
|
279
|
-
validation: { successCount: 0, conflictCount: 0 },
|
|
280
|
-
status: 'candidate',
|
|
281
|
-
feedbackScore: 0,
|
|
282
|
-
usageCount: 0,
|
|
283
|
-
createdAt: eventTs,
|
|
284
|
-
};
|
|
285
|
-
this.principles.set(principleId, principle);
|
|
325
|
+
// NOTE: Principle creation is now deferred to diagnostician analysis.
|
|
326
|
+
// The diagnostician will read conversation context, generalize the trigger pattern,
|
|
327
|
+
// and create a principle via createPrincipleFromDiagnosis() after analysis.
|
|
328
|
+
// Record pain for tracking purposes (but don't create principle yet)
|
|
286
329
|
this.emitSync({
|
|
287
330
|
ts: new Date().toISOString(),
|
|
288
|
-
type: '
|
|
331
|
+
type: 'pain_recorded',
|
|
289
332
|
data: {
|
|
290
|
-
painId:
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
333
|
+
painId: data.painId,
|
|
334
|
+
painType: data.painType,
|
|
335
|
+
source: data.source,
|
|
336
|
+
reason: data.reason,
|
|
337
|
+
sessionId: data.sessionId,
|
|
338
|
+
agentId: data.agentId,
|
|
295
339
|
},
|
|
296
340
|
});
|
|
297
|
-
|
|
341
|
+
// Circuit breaker logic remains for subagent errors
|
|
298
342
|
if (data.painType === 'subagent_error') {
|
|
299
343
|
const key = String(data.taskId ?? data.source ?? 'subagent');
|
|
300
344
|
const next = this.failureStreak.get(key) ?? 0;
|
|
@@ -305,7 +349,7 @@ export class EvolutionReducerImpl {
|
|
|
305
349
|
type: 'circuit_breaker_opened',
|
|
306
350
|
data: {
|
|
307
351
|
taskId: key,
|
|
308
|
-
painId:
|
|
352
|
+
painId: data.painId,
|
|
309
353
|
failCount: next,
|
|
310
354
|
reason: 'Max retries exceeded',
|
|
311
355
|
requireHuman: true,
|
|
@@ -157,7 +157,9 @@ export interface PainDetectedData {
|
|
|
157
157
|
reason: string;
|
|
158
158
|
score?: number;
|
|
159
159
|
sessionId?: string;
|
|
160
|
+
agentId?: string;
|
|
160
161
|
taskId?: string;
|
|
162
|
+
traceId?: string;
|
|
161
163
|
}
|
|
162
164
|
export interface CandidateCreatedData {
|
|
163
165
|
painId: string;
|
|
@@ -202,6 +204,10 @@ export type EvolutionLoopEvent = {
|
|
|
202
204
|
ts: string;
|
|
203
205
|
type: 'pain_detected';
|
|
204
206
|
data: PainDetectedData;
|
|
207
|
+
} | {
|
|
208
|
+
ts: string;
|
|
209
|
+
type: 'pain_recorded';
|
|
210
|
+
data: PainDetectedData;
|
|
205
211
|
} | {
|
|
206
212
|
ts: string;
|
|
207
213
|
type: 'candidate_created';
|
|
@@ -5,7 +5,33 @@
|
|
|
5
5
|
* - 压缩时备份当前版本到历史目录
|
|
6
6
|
* - 清理过期历史版本
|
|
7
7
|
* - 读取历史版本(用于 full 模式)
|
|
8
|
+
* - 工作记忆提取与合并(压缩后恢复上下文)
|
|
8
9
|
*/
|
|
10
|
+
/**
|
|
11
|
+
* 文件输出记录
|
|
12
|
+
*/
|
|
13
|
+
export interface FileArtifact {
|
|
14
|
+
path: string;
|
|
15
|
+
action: 'created' | 'modified' | 'deleted';
|
|
16
|
+
description: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 工作记忆快照
|
|
20
|
+
*/
|
|
21
|
+
export interface WorkingMemorySnapshot {
|
|
22
|
+
lastUpdated: string;
|
|
23
|
+
artifacts: FileArtifact[];
|
|
24
|
+
currentTask?: {
|
|
25
|
+
description: string;
|
|
26
|
+
status: 'in_progress' | 'blocked' | 'reviewing' | 'completed';
|
|
27
|
+
progress: number;
|
|
28
|
+
};
|
|
29
|
+
activeProblems: Array<{
|
|
30
|
+
problem: string;
|
|
31
|
+
approach?: string;
|
|
32
|
+
}>;
|
|
33
|
+
nextActions: string[];
|
|
34
|
+
}
|
|
9
35
|
/**
|
|
10
36
|
* 获取历史目录路径
|
|
11
37
|
*/
|
|
@@ -63,3 +89,122 @@ export declare function compressFocus(focusPath: string, newContent: string): {
|
|
|
63
89
|
* @param maxLines 最大行数
|
|
64
90
|
*/
|
|
65
91
|
export declare function extractSummary(content: string, maxLines?: number): string;
|
|
92
|
+
/**
|
|
93
|
+
* 从会话消息中提取工作记忆
|
|
94
|
+
*
|
|
95
|
+
* @param messages 会话消息数组(OpenClaw 格式)
|
|
96
|
+
* @param workspaceDir 工作区目录(用于生成相对路径)
|
|
97
|
+
* @returns 提取的工作记忆快照
|
|
98
|
+
*/
|
|
99
|
+
export declare function extractWorkingMemory(messages: Array<{
|
|
100
|
+
role?: string;
|
|
101
|
+
content?: string | unknown[];
|
|
102
|
+
}>, workspaceDir?: string): WorkingMemorySnapshot;
|
|
103
|
+
/**
|
|
104
|
+
* 解析 CURRENT_FOCUS.md 中的 Working Memory 章节
|
|
105
|
+
*/
|
|
106
|
+
export declare function parseWorkingMemorySection(content: string): WorkingMemorySnapshot | null;
|
|
107
|
+
/**
|
|
108
|
+
* 将工作记忆合并到 CURRENT_FOCUS.md 内容中
|
|
109
|
+
*
|
|
110
|
+
* @param content 原始内容
|
|
111
|
+
* @param snapshot 工作记忆快照
|
|
112
|
+
* @returns 合并后的内容
|
|
113
|
+
*/
|
|
114
|
+
export declare function mergeWorkingMemory(content: string, snapshot: WorkingMemorySnapshot): string;
|
|
115
|
+
/**
|
|
116
|
+
* 生成工作记忆注入字符串(用于 prompt 注入)
|
|
117
|
+
*/
|
|
118
|
+
export declare function workingMemoryToInjection(snapshot: WorkingMemorySnapshot | null): string;
|
|
119
|
+
/**
|
|
120
|
+
* 压缩配置接口
|
|
121
|
+
*/
|
|
122
|
+
interface CompressionConfig {
|
|
123
|
+
lineThreshold: number;
|
|
124
|
+
sizeThreshold: number;
|
|
125
|
+
intervalMs: number;
|
|
126
|
+
keepCompletedTasks: number;
|
|
127
|
+
maxWorkingMemoryArtifacts: number;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* 提取已完成任务作为里程碑
|
|
131
|
+
*/
|
|
132
|
+
export declare function extractMilestones(content: string): {
|
|
133
|
+
completedTasks: string[];
|
|
134
|
+
fileArtifacts: string[];
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* 归档里程碑到 daily memory 文件
|
|
138
|
+
*/
|
|
139
|
+
export declare function archiveMilestonesToDaily(workspaceDir: string, milestones: {
|
|
140
|
+
completedTasks: string[];
|
|
141
|
+
fileArtifacts: string[];
|
|
142
|
+
}, version: string): string | null;
|
|
143
|
+
/**
|
|
144
|
+
* 清理过期信息和验证文件引用
|
|
145
|
+
*/
|
|
146
|
+
export declare function cleanupStaleInfo(content: string, workspaceDir?: string, config?: CompressionConfig): string;
|
|
147
|
+
/**
|
|
148
|
+
* 自动压缩 CURRENT_FOCUS.md
|
|
149
|
+
*
|
|
150
|
+
* @param focusPath CURRENT_FOCUS.md 的完整路径
|
|
151
|
+
* @param workspaceDir 工作区目录(可选,用于验证文件引用)
|
|
152
|
+
* @param stateDir state 目录路径(可选,用于频率限制)
|
|
153
|
+
* @returns 压缩结果信息,如果不需要压缩则返回 null
|
|
154
|
+
*/
|
|
155
|
+
export declare function autoCompressFocus(focusPath: string, workspaceDir?: string, stateDir?: string): {
|
|
156
|
+
compressed: boolean;
|
|
157
|
+
oldLines: number;
|
|
158
|
+
newLines: number;
|
|
159
|
+
milestonesArchived: boolean;
|
|
160
|
+
backupPath: string | null;
|
|
161
|
+
reason: string;
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* 检查是否需要自动压缩
|
|
165
|
+
*/
|
|
166
|
+
export declare function needsAutoCompression(focusPath: string, stateDir?: string): boolean;
|
|
167
|
+
/**
|
|
168
|
+
* 验证 CURRENT_FOCUS.md 格式
|
|
169
|
+
*
|
|
170
|
+
* 仅校验会导致程序崩溃的核心问题,不过度校验
|
|
171
|
+
*
|
|
172
|
+
* @param content 文件内容
|
|
173
|
+
* @returns 验证结果
|
|
174
|
+
*/
|
|
175
|
+
export declare function validateCurrentFocus(content: string): {
|
|
176
|
+
valid: boolean;
|
|
177
|
+
errors: string[];
|
|
178
|
+
warnings: string[];
|
|
179
|
+
};
|
|
180
|
+
/**
|
|
181
|
+
* 从模板恢复 CURRENT_FOCUS.md
|
|
182
|
+
*
|
|
183
|
+
* @param focusPath CURRENT_FOCUS.md 路径
|
|
184
|
+
* @param extensionRoot 插件根目录
|
|
185
|
+
* @returns 恢复是否成功
|
|
186
|
+
*/
|
|
187
|
+
export declare function recoverFromTemplate(focusPath: string, extensionRoot: string): {
|
|
188
|
+
success: boolean;
|
|
189
|
+
error?: string;
|
|
190
|
+
templatePath?: string;
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* 安全读取 CURRENT_FOCUS.md(自动验证 + 恢复)
|
|
194
|
+
*
|
|
195
|
+
* 仅在文件为空或损坏时才恢复,其他情况正常使用
|
|
196
|
+
*
|
|
197
|
+
* @param focusPath CURRENT_FOCUS.md 路径
|
|
198
|
+
* @param extensionRoot 插件根目录
|
|
199
|
+
* @param logger 日志记录器
|
|
200
|
+
* @returns 文件内容和恢复状态
|
|
201
|
+
*/
|
|
202
|
+
export declare function safeReadCurrentFocus(focusPath: string, extensionRoot: string, logger?: {
|
|
203
|
+
warn?: (msg: string) => void;
|
|
204
|
+
info?: (msg: string) => void;
|
|
205
|
+
}): {
|
|
206
|
+
content: string;
|
|
207
|
+
recovered: boolean;
|
|
208
|
+
validationErrors: string[];
|
|
209
|
+
};
|
|
210
|
+
export {};
|