principles-disciple 1.32.0 → 1.34.0
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/openclaw.plugin.json +4 -4
- package/package.json +1 -1
- package/src/core/correction-cue-learner.ts +203 -0
- package/src/core/correction-types.ts +88 -0
- package/src/core/evolution-logger.ts +3 -3
- package/src/core/init.ts +67 -0
- package/src/service/correction-observer-types.ts +58 -0
- package/src/service/correction-observer-workflow-manager.ts +218 -0
- package/src/service/evolution-worker.ts +172 -146
- package/src/service/nocturnal-service.ts +4 -1
- package/src/service/subagent-workflow/index.ts +14 -0
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +3 -1
- package/tests/service/evolution-worker.nocturnal.test.ts +14 -1
- package/tests/service/evolution-worker.timeout.test.ts +350 -0
- package/tests/commands/implementation-lifecycle.test.ts +0 -362
- package/tests/core/detection-funnel.test.ts +0 -63
- package/tests/core/evolution-e2e.test.ts +0 -58
- package/tests/core/evolution-engine-gate-integration.test.ts +0 -543
- package/tests/core/evolution-engine.test.ts +0 -562
- package/tests/core/evolution-reducer.test.ts +0 -180
- package/tests/core/evolution-user-stories.e2e.test.ts +0 -249
- package/tests/core/local-worker-routing.test.ts +0 -757
- package/tests/core/rule-host.test.ts +0 -389
- package/tests/core/trajectory-correction-pain.test.ts +0 -180
- package/tests/hooks/gate-edit-verification.test.ts +0 -435
- package/tests/hooks/llm.test.ts +0 -308
- package/tests/hooks/progressive-trust-gate.test.ts +0 -277
- package/tests/hooks/prompt.test.ts +0 -1473
- package/tests/index.integration.test.ts +0 -179
- package/tests/index.shadow-routing.integration.test.ts +0 -140
- package/tests/service/evolution-worker.test.ts +0 -462
- package/tests/service/nocturnal-service.test.ts +0 -577
- package/tests/service/nocturnal-workflow-manager.test.ts +0 -441
- package/tests/tools/critique-prompt.test.ts +0 -260
- package/tests/tools/deep-reflect.test.ts +0 -232
- package/tests/tools/model-index.test.ts +0 -246
- package/tests/ui/app.test.tsx +0 -114
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CorrectionObserverWorkflowManager
|
|
3
|
+
*
|
|
4
|
+
* Workflow manager that dispatches an LLM subagent to optimize correction
|
|
5
|
+
* keywords based on recent match performance data and user feedback.
|
|
6
|
+
*
|
|
7
|
+
* Follows the established WorkflowManagerBase pattern from EmpathyObserverWorkflowManager.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { PluginLogger } from '../openclaw-sdk.js';
|
|
11
|
+
import type {
|
|
12
|
+
SubagentWorkflowSpec,
|
|
13
|
+
WorkflowMetadata,
|
|
14
|
+
WorkflowResultContext,
|
|
15
|
+
WorkflowPersistContext,
|
|
16
|
+
WorkflowHandle,
|
|
17
|
+
} from './subagent-workflow/types.js';
|
|
18
|
+
import type { RuntimeDirectDriver } from './subagent-workflow/runtime-direct-driver.js';
|
|
19
|
+
import { WorkflowManagerBase } from './subagent-workflow/workflow-manager-base.js';
|
|
20
|
+
import { isSubagentRuntimeAvailable } from '../utils/subagent-probe.js';
|
|
21
|
+
import type {
|
|
22
|
+
CorrectionObserverPayload,
|
|
23
|
+
CorrectionObserverResult,
|
|
24
|
+
CorrectionObserverWorkflowSpec,
|
|
25
|
+
} from './correction-observer-types.js';
|
|
26
|
+
|
|
27
|
+
const WORKFLOW_SESSION_PREFIX = 'agent:main:subagent:workflow-correction-';
|
|
28
|
+
|
|
29
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
30
|
+
const DEFAULT_TTL_MS = 5 * 60 * 1000;
|
|
31
|
+
|
|
32
|
+
// ── Options ─────────────────────────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
export interface CorrectionObserverWorkflowOptions {
|
|
35
|
+
workspaceDir: string;
|
|
36
|
+
logger: PluginLogger;
|
|
37
|
+
subagent: RuntimeDirectDriver['subagent'];
|
|
38
|
+
/** Pass api.runtime.agent.session to enable heartbeat-safe cleanup (#188) */
|
|
39
|
+
agentSession?: RuntimeDirectDriver['agentSession'];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ── Helper Functions ─────────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Extract raw assistant text from messages or assistantTexts array.
|
|
46
|
+
*/
|
|
47
|
+
function extractAssistantTextForSpec(messages: unknown[], assistantTexts?: string[]): string {
|
|
48
|
+
if (assistantTexts && assistantTexts.length > 0) {
|
|
49
|
+
return assistantTexts[assistantTexts.length - 1] || '';
|
|
50
|
+
}
|
|
51
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
52
|
+
const msg = messages[i] as { role?: string; content?: unknown };
|
|
53
|
+
if (msg?.role !== 'assistant') continue;
|
|
54
|
+
if (typeof msg.content === 'string') return msg.content;
|
|
55
|
+
if (Array.isArray(msg.content)) {
|
|
56
|
+
const txt = msg.content
|
|
57
|
+
.filter((part: unknown) => part && typeof part === 'object' && (part as { type?: string }).type === 'text' && typeof (part as { text?: unknown }).text === 'string')
|
|
58
|
+
.map((part: unknown) => (part as { text: string }).text)
|
|
59
|
+
.join('\n');
|
|
60
|
+
if (txt) return txt;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return '';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Parse correction observer JSON payload from raw text.
|
|
68
|
+
*/
|
|
69
|
+
function parseCorrectionObserverPayload(rawText: string): CorrectionObserverResult | null {
|
|
70
|
+
if (!rawText?.trim()) return null;
|
|
71
|
+
try {
|
|
72
|
+
return JSON.parse(rawText.trim()) as CorrectionObserverResult;
|
|
73
|
+
} catch {
|
|
74
|
+
const match = /\{[\s\S]*\}/.exec(rawText);
|
|
75
|
+
if (!match) return null;
|
|
76
|
+
try {
|
|
77
|
+
return JSON.parse(match[0]) as CorrectionObserverResult;
|
|
78
|
+
} catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ── Workflow Spec ─────────────────────────────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
export const correctionObserverWorkflowSpec: SubagentWorkflowSpec<CorrectionObserverResult> = {
|
|
87
|
+
workflowType: 'correction_observer',
|
|
88
|
+
transport: 'runtime_direct',
|
|
89
|
+
timeoutMs: 30_000,
|
|
90
|
+
ttlMs: 300_000,
|
|
91
|
+
shouldDeleteSessionAfterFinalize: true,
|
|
92
|
+
|
|
93
|
+
buildPrompt(taskInput: unknown, _metadata: WorkflowMetadata): string {
|
|
94
|
+
const payload = taskInput as CorrectionObserverPayload;
|
|
95
|
+
const { keywordStoreSummary, recentMessages } = payload;
|
|
96
|
+
|
|
97
|
+
const termsList = keywordStoreSummary.terms
|
|
98
|
+
.map(t => ` - term="${t.term}", weight=${t.weight}, hits=${t.hitCount}, TP=${t.truePositiveCount}, FP=${t.falsePositiveCount}`)
|
|
99
|
+
.join('\n');
|
|
100
|
+
|
|
101
|
+
const messages = recentMessages.length > 0
|
|
102
|
+
? recentMessages.map(m => ` - ${JSON.stringify(m)}`).join('\n')
|
|
103
|
+
: ' (none)';
|
|
104
|
+
|
|
105
|
+
return [
|
|
106
|
+
'You are a correction keyword optimizer.',
|
|
107
|
+
'',
|
|
108
|
+
'## TASK',
|
|
109
|
+
'Analyze the current correction keyword store and recent user messages.',
|
|
110
|
+
'Recommend ADD/UPDATE/REMOVE actions to improve correction cue accuracy.',
|
|
111
|
+
'',
|
|
112
|
+
'## Current Keyword Store (' + keywordStoreSummary.totalKeywords + ' terms):',
|
|
113
|
+
termsList,
|
|
114
|
+
'',
|
|
115
|
+
'## Recent User Messages (' + recentMessages.length + ' messages):',
|
|
116
|
+
messages,
|
|
117
|
+
'',
|
|
118
|
+
'## Rules:',
|
|
119
|
+
'- ADD: If a correction pattern is detected in messages but not in store',
|
|
120
|
+
'- UPDATE: If a term\'s weight should change based on TP/FP ratio',
|
|
121
|
+
'- REMOVE: If a term has 0 hits after many uses AND high false positive rate (>0.3)',
|
|
122
|
+
'- Keep reasoning concise (max 100 chars)',
|
|
123
|
+
'- Weight range: 0.1-0.9',
|
|
124
|
+
'',
|
|
125
|
+
'Return strict JSON (no markdown):',
|
|
126
|
+
'{"updated": boolean, "updates": {...}, "summary": string}',
|
|
127
|
+
].join('\n');
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
async parseResult(ctx: WorkflowResultContext): Promise<CorrectionObserverResult | null> {
|
|
131
|
+
const rawText = extractAssistantTextForSpec(ctx.messages, ctx.assistantTexts);
|
|
132
|
+
return parseCorrectionObserverPayload(rawText);
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
async persistResult(_ctx: WorkflowPersistContext<CorrectionObserverResult>): Promise<void> {
|
|
136
|
+
// Result persistence is handled by the caller (evolution-worker.ts)
|
|
137
|
+
// which reads the result and applies keyword store updates.
|
|
138
|
+
// This spec handles only the LLM dispatch and result parsing.
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
shouldFinalizeOnWaitStatus(status: 'ok' | 'error' | 'timeout'): boolean {
|
|
142
|
+
return status === 'ok';
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// ── Manager Class ─────────────────────────────────────────────────────────────
|
|
147
|
+
|
|
148
|
+
export class CorrectionObserverWorkflowManager extends WorkflowManagerBase {
|
|
149
|
+
constructor(opts: CorrectionObserverWorkflowOptions) {
|
|
150
|
+
super({
|
|
151
|
+
workspaceDir: opts.workspaceDir,
|
|
152
|
+
logger: opts.logger,
|
|
153
|
+
subagent: opts.subagent,
|
|
154
|
+
agentSession: opts.agentSession,
|
|
155
|
+
workflowType: 'correction_observer',
|
|
156
|
+
sessionPrefix: WORKFLOW_SESSION_PREFIX,
|
|
157
|
+
defaultTimeoutMs: DEFAULT_TIMEOUT_MS,
|
|
158
|
+
defaultTtlMs: DEFAULT_TTL_MS,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async startWorkflow<TResult>(
|
|
163
|
+
spec: SubagentWorkflowSpec<TResult>,
|
|
164
|
+
options: {
|
|
165
|
+
parentSessionId: string;
|
|
166
|
+
workspaceDir?: string;
|
|
167
|
+
taskInput: unknown;
|
|
168
|
+
metadata?: Record<string, unknown>;
|
|
169
|
+
}
|
|
170
|
+
): Promise<WorkflowHandle> {
|
|
171
|
+
// Surface degrade: skip boot sessions
|
|
172
|
+
if (options.parentSessionId.startsWith('boot-')) {
|
|
173
|
+
this.logger.info(`[PD:CorrectionObserver] Skipping workflow: boot session`);
|
|
174
|
+
throw new Error(`CorrectionObserverWorkflowManager: cannot start workflow for boot session`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Surface degrade: check subagent runtime availability
|
|
178
|
+
if (!isSubagentRuntimeAvailable(this.driver.getSubagent())) {
|
|
179
|
+
this.logger.info(`[PD:CorrectionObserver] Skipping workflow: subagent runtime unavailable`);
|
|
180
|
+
throw new Error(`CorrectionObserverWorkflowManager: subagent runtime unavailable`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (spec.transport !== 'runtime_direct') {
|
|
184
|
+
throw new Error(`CorrectionObserverWorkflowManager only supports runtime_direct transport`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return super.startWorkflow(spec, options);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
191
|
+
protected override createWorkflowMetadata<TResult>(
|
|
192
|
+
spec: SubagentWorkflowSpec<TResult>,
|
|
193
|
+
options: {
|
|
194
|
+
parentSessionId: string;
|
|
195
|
+
workspaceDir?: string;
|
|
196
|
+
taskInput: unknown;
|
|
197
|
+
metadata?: Record<string, unknown>;
|
|
198
|
+
},
|
|
199
|
+
now: number
|
|
200
|
+
): WorkflowMetadata {
|
|
201
|
+
return {
|
|
202
|
+
parentSessionId: options.parentSessionId,
|
|
203
|
+
workspaceDir: options.workspaceDir,
|
|
204
|
+
taskInput: options.taskInput,
|
|
205
|
+
startedAt: now,
|
|
206
|
+
workflowType: spec.workflowType,
|
|
207
|
+
...options.metadata,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ── Factory ─────────────────────────────────────────────────────────────────
|
|
213
|
+
|
|
214
|
+
export function createCorrectionObserverWorkflowManager(
|
|
215
|
+
opts: CorrectionObserverWorkflowOptions
|
|
216
|
+
): CorrectionObserverWorkflowManager {
|
|
217
|
+
return new CorrectionObserverWorkflowManager(opts);
|
|
218
|
+
}
|