principles-disciple 1.7.2 → 1.7.4
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/agents/auditor.md +61 -0
- package/agents/diagnostician.md +277 -0
- package/agents/explorer.md +65 -0
- package/agents/implementer.md +68 -0
- package/agents/planner.md +65 -0
- package/agents/reporter.md +78 -0
- package/agents/reviewer.md +66 -0
- package/dist/commands/evolution-status.js +4 -2
- package/dist/core/config.d.ts +11 -0
- package/dist/core/config.js +19 -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 +53 -0
- package/dist/core/focus-history.js +429 -0
- package/dist/core/init.js +24 -0
- 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 +127 -17
- package/dist/hooks/lifecycle.js +104 -0
- package/dist/hooks/pain.js +31 -0
- package/dist/hooks/prompt.js +66 -19
- package/dist/hooks/subagent.d.ts +1 -0
- package/dist/hooks/subagent.js +201 -18
- package/dist/http/principles-console-route.js +58 -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/agent-spawn.js +17 -6
- package/dist/tools/deep-reflect.js +9 -2
- package/dist/utils/subagent-probe.d.ts +23 -0
- package/dist/utils/subagent-probe.js +36 -0
- package/openclaw.plugin.json +12 -13
- package/package.json +5 -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/dist/core/config.d.ts
CHANGED
|
@@ -60,9 +60,19 @@ export interface TrustSettings {
|
|
|
60
60
|
limits: {
|
|
61
61
|
stage_2_max_lines: number;
|
|
62
62
|
stage_3_max_lines: number;
|
|
63
|
+
stage_2_max_percentage: number;
|
|
64
|
+
stage_3_max_percentage: number;
|
|
65
|
+
min_lines_fallback: number;
|
|
63
66
|
};
|
|
64
67
|
history_limit?: number;
|
|
65
68
|
}
|
|
69
|
+
export interface DiagnosticianSettings {
|
|
70
|
+
context: {
|
|
71
|
+
time_window_minutes: number;
|
|
72
|
+
max_message_length: number;
|
|
73
|
+
max_summary_length: number;
|
|
74
|
+
};
|
|
75
|
+
}
|
|
66
76
|
export interface PainSettings {
|
|
67
77
|
language: 'en' | 'zh';
|
|
68
78
|
trajectory?: {
|
|
@@ -70,6 +80,7 @@ export interface PainSettings {
|
|
|
70
80
|
busy_timeout_ms?: number;
|
|
71
81
|
orphan_blob_grace_days?: number;
|
|
72
82
|
};
|
|
83
|
+
diagnostician?: DiagnosticianSettings;
|
|
73
84
|
thresholds: {
|
|
74
85
|
pain_trigger: number;
|
|
75
86
|
cognitive_paralysis_input: number;
|
package/dist/core/config.js
CHANGED
|
@@ -10,6 +10,13 @@ import * as path from 'path';
|
|
|
10
10
|
// ─────────────────────────────────────────────────────────────
|
|
11
11
|
export const DEFAULT_SETTINGS = {
|
|
12
12
|
language: 'zh', // Optimized for the primary user base
|
|
13
|
+
diagnostician: {
|
|
14
|
+
context: {
|
|
15
|
+
time_window_minutes: 5, // pain_timestamp 前后各 5 分钟
|
|
16
|
+
max_message_length: 500, // 每条消息截断到 500 字符
|
|
17
|
+
max_summary_length: 3000, // 对话摘要最大 3000 字符
|
|
18
|
+
}
|
|
19
|
+
},
|
|
13
20
|
thresholds: {
|
|
14
21
|
pain_trigger: 40, // Increased tolerance before forcing a stop
|
|
15
22
|
cognitive_paralysis_input: 4000,
|
|
@@ -37,7 +44,7 @@ export const DEFAULT_SETTINGS = {
|
|
|
37
44
|
intervals: {
|
|
38
45
|
worker_poll_ms: 15 * 60 * 1000,
|
|
39
46
|
initial_delay_ms: 5000,
|
|
40
|
-
task_timeout_ms:
|
|
47
|
+
task_timeout_ms: 60 * 60 * 1000 // 1 hour, matching evolution-worker.ts default
|
|
41
48
|
},
|
|
42
49
|
trust: {
|
|
43
50
|
stages: {
|
|
@@ -73,6 +80,9 @@ export const DEFAULT_SETTINGS = {
|
|
|
73
80
|
limits: {
|
|
74
81
|
stage_2_max_lines: 50, // Was 10. 10 lines is barely enough to fix a function signature.
|
|
75
82
|
stage_3_max_lines: 300, // Was 100. Allow substantial feature implementation.
|
|
83
|
+
stage_2_max_percentage: 10, // Percentage-based threshold for Stage 2
|
|
84
|
+
stage_3_max_percentage: 15, // Percentage-based threshold for Stage 3
|
|
85
|
+
min_lines_fallback: 20, // Minimum threshold even for small files
|
|
76
86
|
},
|
|
77
87
|
history_limit: 50
|
|
78
88
|
},
|
|
@@ -210,6 +220,14 @@ export class PainConfig {
|
|
|
210
220
|
// Ensure intervals are positive
|
|
211
221
|
if (settings.intervals.worker_poll_ms < 1000)
|
|
212
222
|
settings.intervals.worker_poll_ms = 15 * 60 * 1000;
|
|
223
|
+
// Ensure percentage limits are in valid range [0, 100]
|
|
224
|
+
const l = settings.trust.limits;
|
|
225
|
+
if (l.stage_2_max_percentage < 0 || l.stage_2_max_percentage > 100)
|
|
226
|
+
l.stage_2_max_percentage = 10;
|
|
227
|
+
if (l.stage_3_max_percentage < 0 || l.stage_3_max_percentage > 100)
|
|
228
|
+
l.stage_3_max_percentage = 15;
|
|
229
|
+
if (l.min_lines_fallback < 1)
|
|
230
|
+
l.min_lines_fallback = 20;
|
|
213
231
|
}
|
|
214
232
|
/**
|
|
215
233
|
* Gets a value using dot notation (e.g. 'thresholds.pain_trigger')
|
|
@@ -0,0 +1,137 @@
|
|
|
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 type { TrajectoryDatabase } from './trajectory.js';
|
|
13
|
+
export type EvolutionStage = 'pain_detected' | 'queued' | 'started' | 'analyzing' | 'principle_generated' | 'completed' | 'failed';
|
|
14
|
+
export type EvolutionLogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
15
|
+
export interface EvolutionLogEntry {
|
|
16
|
+
traceId: string;
|
|
17
|
+
stage: EvolutionStage;
|
|
18
|
+
level: EvolutionLogLevel;
|
|
19
|
+
message: string;
|
|
20
|
+
summary: string;
|
|
21
|
+
timestamp: string;
|
|
22
|
+
metadata?: Record<string, unknown>;
|
|
23
|
+
taskId?: string;
|
|
24
|
+
sessionId?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface EvolutionLogInput {
|
|
27
|
+
traceId: string;
|
|
28
|
+
stage: EvolutionStage;
|
|
29
|
+
level?: EvolutionLogLevel;
|
|
30
|
+
message: string;
|
|
31
|
+
summary: string;
|
|
32
|
+
metadata?: Record<string, unknown>;
|
|
33
|
+
taskId?: string;
|
|
34
|
+
sessionId?: string;
|
|
35
|
+
}
|
|
36
|
+
export declare const STAGE_LABELS: Record<EvolutionStage, string>;
|
|
37
|
+
export declare const STAGE_COLORS: Record<EvolutionStage, string>;
|
|
38
|
+
/**
|
|
39
|
+
* 创建新的 trace_id
|
|
40
|
+
* 格式: ev_{timestamp}_{random}
|
|
41
|
+
* 使用 crypto.randomBytes 确保不可预测性
|
|
42
|
+
*/
|
|
43
|
+
export declare function createTraceId(): string;
|
|
44
|
+
/**
|
|
45
|
+
* EvolutionLogger 类
|
|
46
|
+
* 管理进化流程的日志记录
|
|
47
|
+
*/
|
|
48
|
+
export declare class EvolutionLogger {
|
|
49
|
+
private readonly workspaceDir;
|
|
50
|
+
private readonly trajectory;
|
|
51
|
+
constructor(workspaceDir: string, trajectory?: TrajectoryDatabase);
|
|
52
|
+
/**
|
|
53
|
+
* 记录进化事件
|
|
54
|
+
* 同时写入 SYSTEM_LOG 和 evolution_events 表
|
|
55
|
+
*/
|
|
56
|
+
log(input: EvolutionLogInput): void;
|
|
57
|
+
/**
|
|
58
|
+
* 记录 pain_detected 事件
|
|
59
|
+
*/
|
|
60
|
+
logPainDetected(params: {
|
|
61
|
+
traceId: string;
|
|
62
|
+
source: string;
|
|
63
|
+
reason: string;
|
|
64
|
+
score: number;
|
|
65
|
+
toolName?: string;
|
|
66
|
+
filePath?: string;
|
|
67
|
+
sessionId?: string;
|
|
68
|
+
}): void;
|
|
69
|
+
/**
|
|
70
|
+
* 记录 queued 事件
|
|
71
|
+
*/
|
|
72
|
+
logQueued(params: {
|
|
73
|
+
traceId: string;
|
|
74
|
+
taskId: string;
|
|
75
|
+
score: number;
|
|
76
|
+
source: string;
|
|
77
|
+
reason: string;
|
|
78
|
+
}): void;
|
|
79
|
+
/**
|
|
80
|
+
* 记录 started 事件
|
|
81
|
+
*/
|
|
82
|
+
logStarted(params: {
|
|
83
|
+
traceId: string;
|
|
84
|
+
taskId: string;
|
|
85
|
+
}): void;
|
|
86
|
+
/**
|
|
87
|
+
* 记录 analyzing 事件
|
|
88
|
+
*/
|
|
89
|
+
logAnalyzing(params: {
|
|
90
|
+
traceId: string;
|
|
91
|
+
taskId: string;
|
|
92
|
+
analysisType?: string;
|
|
93
|
+
}): void;
|
|
94
|
+
/**
|
|
95
|
+
* 记录 principle_generated 事件
|
|
96
|
+
*/
|
|
97
|
+
logPrincipleGenerated(params: {
|
|
98
|
+
traceId: string;
|
|
99
|
+
taskId: string;
|
|
100
|
+
principleId: string;
|
|
101
|
+
principleText: string;
|
|
102
|
+
}): void;
|
|
103
|
+
/**
|
|
104
|
+
* 记录 completed 事件
|
|
105
|
+
*/
|
|
106
|
+
logCompleted(params: {
|
|
107
|
+
traceId: string;
|
|
108
|
+
taskId: string;
|
|
109
|
+
resolution: 'marker_detected' | 'auto_completed_timeout' | 'manual';
|
|
110
|
+
durationMs?: number;
|
|
111
|
+
principlesGenerated?: number;
|
|
112
|
+
}): void;
|
|
113
|
+
/**
|
|
114
|
+
* 记录 failed 事件
|
|
115
|
+
*/
|
|
116
|
+
logFailed(params: {
|
|
117
|
+
traceId: string;
|
|
118
|
+
taskId: string;
|
|
119
|
+
error: string;
|
|
120
|
+
stack?: string;
|
|
121
|
+
}): void;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* 获取 EvolutionLogger 实例(单例)
|
|
125
|
+
*
|
|
126
|
+
* 注意:带 trajectory 和不带 trajectory 的请求会返回不同的实例,
|
|
127
|
+
* 因为 trajectory 影响事件持久化行为。
|
|
128
|
+
*/
|
|
129
|
+
export declare function getEvolutionLogger(workspaceDir: string, trajectory?: TrajectoryDatabase): EvolutionLogger;
|
|
130
|
+
/**
|
|
131
|
+
* 清理指定 workspace 的 logger 缓存
|
|
132
|
+
*/
|
|
133
|
+
export declare function disposeEvolutionLogger(workspaceDir: string): boolean;
|
|
134
|
+
/**
|
|
135
|
+
* 清理所有 logger 缓存(用于测试或进程退出)
|
|
136
|
+
*/
|
|
137
|
+
export declare function disposeAllEvolutionLoggers(): void;
|
|
@@ -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';
|