principles-disciple 1.35.0 → 1.37.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 +1 -1
- package/package.json +1 -1
- package/src/commands/nocturnal-train.ts +1 -0
- package/src/core/correction-cue-learner.ts +23 -8
- package/src/core/event-log.ts +3 -0
- package/src/core/evolution-engine.ts +1 -0
- package/src/core/init.ts +2 -2
- package/src/core/nocturnal-trinity-types.ts +124 -0
- package/src/core/session-tracker.ts +1 -0
- package/src/core/training-program.ts +1 -0
- package/src/hooks/gate-block-helper.ts +1 -1
- package/src/hooks/prompt.ts +3 -3
- package/src/index.ts +2 -1
- package/src/service/central-sync-service.ts +2 -0
- package/src/service/evolution-dedup.ts +74 -0
- package/src/service/evolution-pain-context.ts +79 -0
- package/src/service/evolution-queue-lock.ts +47 -0
- package/src/service/evolution-queue-migration.ts +173 -0
- package/src/service/evolution-worker.ts +43 -34
- package/src/service/keyword-optimization-service.ts +2 -2
- package/src/service/subagent-workflow/correction-observer-types.ts +69 -0
- package/src/service/subagent-workflow/correction-observer-workflow-manager.ts +246 -0
- package/src/service/subagent-workflow/index.ts +13 -0
- package/src/service/subagent-workflow/workflow-manager-base.ts +1 -0
- package/tests/core/correction-cue-learner.test.ts +345 -0
- package/tests/core/pain-score.property.test.ts +205 -0
- package/tests/integration/chaos-resilience.test.ts +348 -0
- package/tests/integration/gate-real-io.e2e.test.ts +251 -0
- package/tests/integration/pain-diagnostician-loop.e2e.test.ts +380 -0
- package/tests/integration/tool-hooks-workspace-dir.e2e.test.ts +8 -2
- package/tests/integration/trajectory-lifecycle.e2e.test.ts +523 -0
- package/vitest.config.ts +23 -4
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Evolution Queue Migration — V1 to V2 Schema
|
|
3
|
+
*
|
|
4
|
+
* Pure transformation functions and shared queue types.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { TaskKind, TaskPriority } from '../core/trajectory-types.js';
|
|
8
|
+
|
|
9
|
+
// Re-export TaskKind and TaskPriority for convenience
|
|
10
|
+
export type { TaskKind, TaskPriority } from '../core/trajectory-types.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Queue item status values.
|
|
14
|
+
*/
|
|
15
|
+
export type QueueStatus = 'pending' | 'in_progress' | 'completed' | 'failed' | 'canceled';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Task resolution strings.
|
|
19
|
+
*/
|
|
20
|
+
export type TaskResolution =
|
|
21
|
+
| 'marker_detected'
|
|
22
|
+
| 'auto_completed_timeout'
|
|
23
|
+
| 'failed_max_retries'
|
|
24
|
+
| 'runtime_unavailable'
|
|
25
|
+
| 'canceled'
|
|
26
|
+
| 'late_marker_principle_created'
|
|
27
|
+
| 'late_marker_no_principle'
|
|
28
|
+
| 'stub_fallback'
|
|
29
|
+
| 'skipped_thin_violation';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Recent pain context for sleep_reflection tasks.
|
|
33
|
+
* Attached to queue items to provide pain signal context.
|
|
34
|
+
*/
|
|
35
|
+
export interface RecentPainContext {
|
|
36
|
+
mostRecent: { score: number; source: string; reason: string; timestamp: string; sessionId: string } | null;
|
|
37
|
+
recentPainCount: number;
|
|
38
|
+
recentMaxPainScore: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Default values for new V2 fields when migrating legacy items.
|
|
43
|
+
*/
|
|
44
|
+
export const DEFAULT_TASK_KIND: TaskKind = 'pain_diagnosis';
|
|
45
|
+
export const DEFAULT_PRIORITY: TaskPriority = 'medium';
|
|
46
|
+
export const DEFAULT_MAX_RETRIES = 3;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Legacy (pre-V2) queue item schema.
|
|
50
|
+
*/
|
|
51
|
+
export interface LegacyEvolutionQueueItem {
|
|
52
|
+
id: string;
|
|
53
|
+
source: string;
|
|
54
|
+
traceId?: string;
|
|
55
|
+
task?: string;
|
|
56
|
+
score: number;
|
|
57
|
+
reason: string;
|
|
58
|
+
timestamp: string;
|
|
59
|
+
enqueued_at?: string;
|
|
60
|
+
started_at?: string;
|
|
61
|
+
completed_at?: string;
|
|
62
|
+
assigned_session_key?: string;
|
|
63
|
+
trigger_text_preview?: string;
|
|
64
|
+
status?: string;
|
|
65
|
+
resolution?: string;
|
|
66
|
+
session_id?: string;
|
|
67
|
+
agent_id?: string;
|
|
68
|
+
taskKind?: string;
|
|
69
|
+
priority?: string;
|
|
70
|
+
retryCount?: number;
|
|
71
|
+
maxRetries?: number;
|
|
72
|
+
lastError?: string;
|
|
73
|
+
resultRef?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* V2 queue item schema.
|
|
78
|
+
*/
|
|
79
|
+
export interface EvolutionQueueItem {
|
|
80
|
+
id: string;
|
|
81
|
+
taskKind: TaskKind;
|
|
82
|
+
priority: TaskPriority;
|
|
83
|
+
source: string;
|
|
84
|
+
traceId?: string;
|
|
85
|
+
task?: string;
|
|
86
|
+
score: number;
|
|
87
|
+
reason: string;
|
|
88
|
+
timestamp: string;
|
|
89
|
+
enqueued_at?: string;
|
|
90
|
+
started_at?: string;
|
|
91
|
+
completed_at?: string;
|
|
92
|
+
assigned_session_key?: string;
|
|
93
|
+
trigger_text_preview?: string;
|
|
94
|
+
status: QueueStatus;
|
|
95
|
+
resolution?: TaskResolution;
|
|
96
|
+
session_id?: string;
|
|
97
|
+
agent_id?: string;
|
|
98
|
+
retryCount: number;
|
|
99
|
+
maxRetries: number;
|
|
100
|
+
lastError?: string;
|
|
101
|
+
resultRef?: string;
|
|
102
|
+
recentPainContext?: RecentPainContext;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type RawQueueItem = Record<string, unknown>;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Migrate a legacy queue item to V2 schema.
|
|
109
|
+
* Old items without taskKind are assumed to be pain_diagnosis for backward compatibility.
|
|
110
|
+
*/
|
|
111
|
+
export function migrateToV2(item: LegacyEvolutionQueueItem): {
|
|
112
|
+
id: string;
|
|
113
|
+
taskKind: TaskKind;
|
|
114
|
+
priority: TaskPriority;
|
|
115
|
+
source: string;
|
|
116
|
+
traceId?: string;
|
|
117
|
+
task?: string;
|
|
118
|
+
score: number;
|
|
119
|
+
reason: string;
|
|
120
|
+
timestamp: string;
|
|
121
|
+
enqueued_at?: string;
|
|
122
|
+
started_at?: string;
|
|
123
|
+
completed_at?: string;
|
|
124
|
+
assigned_session_key?: string;
|
|
125
|
+
trigger_text_preview?: string;
|
|
126
|
+
status: QueueStatus;
|
|
127
|
+
resolution?: TaskResolution;
|
|
128
|
+
session_id?: string;
|
|
129
|
+
agent_id?: string;
|
|
130
|
+
retryCount: number;
|
|
131
|
+
maxRetries: number;
|
|
132
|
+
lastError?: string;
|
|
133
|
+
resultRef?: string;
|
|
134
|
+
} {
|
|
135
|
+
return {
|
|
136
|
+
id: item.id,
|
|
137
|
+
taskKind: (item.taskKind as TaskKind) || DEFAULT_TASK_KIND,
|
|
138
|
+
priority: (item.priority as TaskPriority) || DEFAULT_PRIORITY,
|
|
139
|
+
source: item.source,
|
|
140
|
+
traceId: item.traceId,
|
|
141
|
+
task: item.task,
|
|
142
|
+
score: item.score,
|
|
143
|
+
reason: item.reason,
|
|
144
|
+
timestamp: item.timestamp,
|
|
145
|
+
enqueued_at: item.enqueued_at,
|
|
146
|
+
started_at: item.started_at,
|
|
147
|
+
completed_at: item.completed_at,
|
|
148
|
+
assigned_session_key: item.assigned_session_key,
|
|
149
|
+
trigger_text_preview: item.trigger_text_preview,
|
|
150
|
+
status: (item.status as QueueStatus) || 'pending',
|
|
151
|
+
resolution: item.resolution as TaskResolution | undefined,
|
|
152
|
+
session_id: item.session_id,
|
|
153
|
+
agent_id: item.agent_id,
|
|
154
|
+
retryCount: item.retryCount || 0,
|
|
155
|
+
maxRetries: item.maxRetries || DEFAULT_MAX_RETRIES,
|
|
156
|
+
lastError: item.lastError,
|
|
157
|
+
resultRef: item.resultRef,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Check if an item is a legacy (pre-V2) queue item.
|
|
163
|
+
*/
|
|
164
|
+
export function isLegacyQueueItem(item: RawQueueItem): boolean {
|
|
165
|
+
return item && typeof item === 'object' && !('taskKind' in item);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Migrate entire queue to V2 schema if needed.
|
|
170
|
+
*/
|
|
171
|
+
export function migrateQueueToV2(queue: RawQueueItem[]): ReturnType<typeof migrateToV2>[] {
|
|
172
|
+
return queue.map(item => isLegacyQueueItem(item) ? migrateToV2(item as unknown as LegacyEvolutionQueueItem) : item as unknown as ReturnType<typeof migrateToV2>);
|
|
173
|
+
}
|
|
@@ -16,11 +16,10 @@ import { getEvolutionLogger } from '../core/evolution-logger.js';
|
|
|
16
16
|
import type { TaskKind, TaskPriority } from '../core/trajectory-types.js';
|
|
17
17
|
export type { TaskKind, TaskPriority } from '../core/trajectory-types.js';
|
|
18
18
|
import { LockUnavailableError } from '../config/index.js';
|
|
19
|
-
import { PAIN_QUEUE_DEDUP_WINDOW_MS } from '../config/defaults/runtime.js';
|
|
20
19
|
import { checkWorkspaceIdle, checkCooldown } from './nocturnal-runtime.js';
|
|
21
20
|
import { loadNocturnalConfig } from './nocturnal-config.js';
|
|
22
21
|
import { WorkflowStore } from './subagent-workflow/workflow-store.js';
|
|
23
|
-
import type { WorkflowRow
|
|
22
|
+
import type { WorkflowRow } from './subagent-workflow/types.js';
|
|
24
23
|
import { EmpathyObserverWorkflowManager } from './subagent-workflow/empathy-observer-workflow-manager.js';
|
|
25
24
|
import { DeepReflectWorkflowManager } from './subagent-workflow/deep-reflect-workflow-manager.js';
|
|
26
25
|
import { NocturnalWorkflowManager, nocturnalWorkflowSpec } from './subagent-workflow/nocturnal-workflow-manager.js';
|
|
@@ -32,22 +31,14 @@ import {
|
|
|
32
31
|
import { validateNocturnalSnapshotIngress } from '../core/nocturnal-snapshot-contract.js';
|
|
33
32
|
import { isExpectedSubagentError } from './subagent-workflow/subagent-error-utils.js';
|
|
34
33
|
import { readPainFlagContract } from '../core/pain.js';
|
|
35
|
-
import { CorrectionObserverWorkflowManager, correctionObserverWorkflowSpec } from './correction-observer-workflow-manager.js';
|
|
36
|
-
import type { CorrectionObserverPayload } from './correction-observer-types.js';
|
|
34
|
+
import { CorrectionObserverWorkflowManager, correctionObserverWorkflowSpec } from './subagent-workflow/correction-observer-workflow-manager.js';
|
|
35
|
+
import type { CorrectionObserverPayload } from './subagent-workflow/correction-observer-types.js';
|
|
37
36
|
import { KeywordOptimizationService } from './keyword-optimization-service.js';
|
|
38
37
|
import { TrajectoryRegistry } from '../core/trajectory.js';
|
|
39
38
|
import { CorrectionCueLearner } from '../core/correction-cue-learner.js';
|
|
40
|
-
import { WORKFLOW_TTL_MS } from '../config/defaults/runtime.js';
|
|
41
|
-
import { OpenClawTrinityRuntimeAdapter } from '../core/nocturnal-trinity.js';
|
|
42
39
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
*/
|
|
46
|
-
function atomicWriteFileSync(filePath: string, data: string): void {
|
|
47
|
-
const tmpPath = filePath + '.tmp';
|
|
48
|
-
fs.writeFileSync(tmpPath, data, 'utf8');
|
|
49
|
-
fs.renameSync(tmpPath, filePath);
|
|
50
|
-
}
|
|
40
|
+
const WORKFLOW_TTL_MS = 5 * 60 * 1000; // 5 minutes default TTL for helper workflows
|
|
41
|
+
import { OpenClawTrinityRuntimeAdapter } from '../core/nocturnal-trinity.js';
|
|
51
42
|
|
|
52
43
|
// ── Workflow Watchdog ────────────────────────────────────────────────────────
|
|
53
44
|
// Detects stale/orphaned workflows, invalid results, and cleanup failures.
|
|
@@ -209,6 +200,27 @@ let timeoutId: NodeJS.Timeout | null = null;
|
|
|
209
200
|
export type QueueStatus = 'pending' | 'in_progress' | 'completed' | 'failed' | 'canceled';
|
|
210
201
|
export type TaskResolution = 'marker_detected' | 'auto_completed_timeout' | 'failed_max_retries' | 'runtime_unavailable' | 'canceled' | 'late_marker_principle_created' | 'late_marker_no_principle' | 'stub_fallback' | 'skipped_thin_violation';
|
|
211
202
|
|
|
203
|
+
/**
|
|
204
|
+
* Recent pain context attached to sleep_reflection tasks.
|
|
205
|
+
* Carries explicit recent pain signal metadata without being a separate task kind.
|
|
206
|
+
* Used by NocturnalTargetSelector for ranking bias and context enrichment.
|
|
207
|
+
*/
|
|
208
|
+
export interface RecentPainContext {
|
|
209
|
+
/** Most recent unresolved pain event */
|
|
210
|
+
mostRecent: {
|
|
211
|
+
score: number;
|
|
212
|
+
source: string;
|
|
213
|
+
reason: string;
|
|
214
|
+
timestamp: string;
|
|
215
|
+
/** Session ID where the pain occurred */
|
|
216
|
+
sessionId: string;
|
|
217
|
+
} | null;
|
|
218
|
+
/** Count of pain events in the recent window (for signal strength) */
|
|
219
|
+
recentPainCount: number;
|
|
220
|
+
/** Highest pain score in the recent window */
|
|
221
|
+
recentMaxPainScore: number;
|
|
222
|
+
}
|
|
223
|
+
|
|
212
224
|
export interface EvolutionQueueItem {
|
|
213
225
|
// Core identity
|
|
214
226
|
id: string;
|
|
@@ -414,6 +426,7 @@ function buildFallbackNocturnalSnapshot(
|
|
|
414
426
|
};
|
|
415
427
|
}
|
|
416
428
|
|
|
429
|
+
const PAIN_QUEUE_DEDUP_WINDOW_MS = 30 * 60 * 1000;
|
|
417
430
|
|
|
418
431
|
// P0 fix: File lock constants and helper for queue operations (prevents TOCTOU race)
|
|
419
432
|
export const EVOLUTION_QUEUE_LOCK_SUFFIX = '.lock';
|
|
@@ -711,7 +724,7 @@ function enqueueNewSleepReflectionTask(
|
|
|
711
724
|
recentPainContext,
|
|
712
725
|
});
|
|
713
726
|
|
|
714
|
-
|
|
727
|
+
fs.writeFileSync(queuePath, JSON.stringify(queue, null, 2), 'utf8');
|
|
715
728
|
logger?.info?.(`[PD:EvolutionWorker] Enqueued sleep_reflection task ${taskId}`);
|
|
716
729
|
}
|
|
717
730
|
|
|
@@ -856,7 +869,7 @@ async function doEnqueuePainTask(
|
|
|
856
869
|
retryCount: 0, maxRetries: 3,
|
|
857
870
|
});
|
|
858
871
|
|
|
859
|
-
|
|
872
|
+
fs.writeFileSync(queuePath, JSON.stringify(queue, null, 2), 'utf8');
|
|
860
873
|
fs.appendFileSync(painFlagPath, `\nstatus: queued\ntask_id: ${taskId}\n`, 'utf8');
|
|
861
874
|
result.enqueued = true;
|
|
862
875
|
|
|
@@ -1645,7 +1658,7 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
|
|
|
1645
1658
|
|
|
1646
1659
|
// Write claimed state (includes any pain changes from above) and release lock
|
|
1647
1660
|
if (queueChanged) {
|
|
1648
|
-
|
|
1661
|
+
fs.writeFileSync(queuePath, JSON.stringify(queue, null, 2), 'utf8');
|
|
1649
1662
|
}
|
|
1650
1663
|
releaseLock();
|
|
1651
1664
|
for (const sleepTask of sleepReflectionTasks) {
|
|
@@ -1899,7 +1912,7 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
|
|
|
1899
1912
|
freshQueue[idx] = sleepTask;
|
|
1900
1913
|
}
|
|
1901
1914
|
}
|
|
1902
|
-
|
|
1915
|
+
fs.writeFileSync(queuePath, JSON.stringify(freshQueue, null, 2), 'utf8');
|
|
1903
1916
|
|
|
1904
1917
|
// Log completions to EvolutionLogger
|
|
1905
1918
|
for (const sleepTask of sleepReflectionTasks) {
|
|
@@ -1992,14 +2005,10 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
|
|
|
1992
2005
|
};
|
|
1993
2006
|
|
|
1994
2007
|
// Dispatch LLM subagent via CorrectionObserverWorkflowManager
|
|
1995
|
-
const subagent = api?.runtime?.subagent;
|
|
1996
|
-
if (!subagent) {
|
|
1997
|
-
throw new Error('[PD:EvolutionWorker] subagent runtime not available for keyword_optimization');
|
|
1998
|
-
}
|
|
1999
2008
|
const manager = new CorrectionObserverWorkflowManager({
|
|
2000
2009
|
workspaceDir: wctx.workspaceDir,
|
|
2001
2010
|
logger,
|
|
2002
|
-
subagent
|
|
2011
|
+
subagent: api?.runtime?.subagent!,
|
|
2003
2012
|
agentSession: api?.runtime?.agent?.session,
|
|
2004
2013
|
});
|
|
2005
2014
|
|
|
@@ -2013,11 +2022,7 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
|
|
|
2013
2022
|
workflowId = handle.workflowId;
|
|
2014
2023
|
koTask.resultRef = workflowId;
|
|
2015
2024
|
} else {
|
|
2016
|
-
|
|
2017
|
-
workflowId = koTask.resultRef;
|
|
2018
|
-
if (!workflowId) {
|
|
2019
|
-
throw new Error(`[PD:EvolutionWorker] keyword_optimization task ${koTask.id} has no resultRef in polling mode`);
|
|
2020
|
-
}
|
|
2025
|
+
workflowId = koTask.resultRef!;
|
|
2021
2026
|
}
|
|
2022
2027
|
|
|
2023
2028
|
// Poll workflow state
|
|
@@ -2029,7 +2034,7 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
|
|
|
2029
2034
|
|
|
2030
2035
|
if (parsedResult?.updated) {
|
|
2031
2036
|
koService.applyResult(parsedResult);
|
|
2032
|
-
|
|
2037
|
+
learner.recordOptimizationPerformed();
|
|
2033
2038
|
logger?.info?.(`[PD:EvolutionWorker] keyword_optimization applied mutations: ${parsedResult.summary}`);
|
|
2034
2039
|
} else {
|
|
2035
2040
|
logger?.info?.(`[PD:EvolutionWorker] keyword_optimization completed with no updates`);
|
|
@@ -2082,7 +2087,7 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
|
|
|
2082
2087
|
freshQueue.push(koTask);
|
|
2083
2088
|
}
|
|
2084
2089
|
}
|
|
2085
|
-
fs.writeFileSync(queuePath, JSON.stringify(freshQueue, null, 2));
|
|
2090
|
+
fs.writeFileSync(queuePath, JSON.stringify(freshQueue, null, 2), 'utf8');
|
|
2086
2091
|
} catch (koResultErr) {
|
|
2087
2092
|
logger?.warn?.(`[PD:EvolutionWorker] Failed to write keyword_optimization results: ${String(koResultErr)}`);
|
|
2088
2093
|
} finally {
|
|
@@ -2092,7 +2097,7 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
|
|
|
2092
2097
|
}
|
|
2093
2098
|
|
|
2094
2099
|
if (queueChanged) {
|
|
2095
|
-
|
|
2100
|
+
fs.writeFileSync(queuePath, JSON.stringify(queue, null, 2), 'utf8');
|
|
2096
2101
|
}
|
|
2097
2102
|
|
|
2098
2103
|
// Pipeline observability: log stage-level summary at end of cycle
|
|
@@ -2210,7 +2215,7 @@ export async function registerEvolutionTaskSession(
|
|
|
2210
2215
|
if (!task.started_at) {
|
|
2211
2216
|
task.started_at = new Date().toISOString();
|
|
2212
2217
|
}
|
|
2213
|
-
|
|
2218
|
+
fs.writeFileSync(queuePath, JSON.stringify(queue, null, 2), 'utf8');
|
|
2214
2219
|
return true;
|
|
2215
2220
|
} finally {
|
|
2216
2221
|
releaseLock();
|
|
@@ -2250,7 +2255,7 @@ interface WorkerStatusReport {
|
|
|
2250
2255
|
function writeWorkerStatus(stateDir: string, report: WorkerStatusReport): void {
|
|
2251
2256
|
try {
|
|
2252
2257
|
const statusPath = path.join(stateDir, 'worker-status.json');
|
|
2253
|
-
|
|
2258
|
+
fs.writeFileSync(statusPath, JSON.stringify(report, null, 2), 'utf8');
|
|
2254
2259
|
} catch (statusErr) {
|
|
2255
2260
|
// Non-critical: worker-status.json is for monitoring, failure is acceptable
|
|
2256
2261
|
// (no logger available in this standalone helper)
|
|
@@ -2281,7 +2286,7 @@ async function processEvolutionQueueWithResult(
|
|
|
2281
2286
|
const purgeResult = purgeStaleFailedTasks(queue, logger);
|
|
2282
2287
|
if (purgeResult.purged > 0) {
|
|
2283
2288
|
// Write back the cleaned queue
|
|
2284
|
-
|
|
2289
|
+
fs.writeFileSync(queuePath, JSON.stringify(queue, null, 2), 'utf8');
|
|
2285
2290
|
}
|
|
2286
2291
|
|
|
2287
2292
|
queueResult.total = queue.length;
|
|
@@ -2558,6 +2563,7 @@ export const EvolutionWorkerService: ExtendedEvolutionWorkerService = {
|
|
|
2558
2563
|
}
|
|
2559
2564
|
|
|
2560
2565
|
timeoutId = setTimeout(runCycle, interval);
|
|
2566
|
+
timeoutId.unref();
|
|
2561
2567
|
}
|
|
2562
2568
|
|
|
2563
2569
|
timeoutId = setTimeout(() => {
|
|
@@ -2573,11 +2579,14 @@ export const EvolutionWorkerService: ExtendedEvolutionWorkerService = {
|
|
|
2573
2579
|
}
|
|
2574
2580
|
// processPromotion removed (D-06)
|
|
2575
2581
|
timeoutId = setTimeout(runCycle, interval);
|
|
2582
|
+
timeoutId.unref();
|
|
2576
2583
|
})().catch((err) => {
|
|
2577
2584
|
if (logger) logger.error(`[PD:EvolutionWorker] Startup worker cycle failed: ${String(err)}`);
|
|
2578
2585
|
timeoutId = setTimeout(runCycle, interval);
|
|
2586
|
+
timeoutId.unref();
|
|
2579
2587
|
});
|
|
2580
2588
|
}, initialDelay);
|
|
2589
|
+
timeoutId.unref();
|
|
2581
2590
|
},
|
|
2582
2591
|
|
|
2583
2592
|
stop(ctx: OpenClawPluginServiceContext): void {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { CorrectionCueLearner } from '../core/correction-cue-learner.js';
|
|
10
|
-
import type { CorrectionObserverResult } from './correction-observer-types.js';
|
|
10
|
+
import type { CorrectionObserverResult } from './subagent-workflow/correction-observer-types.js';
|
|
11
11
|
import type { PluginLogger } from '../openclaw-sdk.js';
|
|
12
12
|
import { TrajectoryRegistry } from '../core/trajectory.js';
|
|
13
13
|
|
|
@@ -137,4 +137,4 @@ export type TrajectoryHistoryEntry = {
|
|
|
137
137
|
};
|
|
138
138
|
|
|
139
139
|
/** Re-export CorrectionObserverPayload for convenience */
|
|
140
|
-
export type { CorrectionObserverPayload } from './correction-observer-types.js';
|
|
140
|
+
export type { CorrectionObserverPayload } from './subagent-workflow/correction-observer-types.js';
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Correction Observer Workflow - Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Types for the correction observer LLM optimization workflow.
|
|
5
|
+
* This workflow dispatches an LLM subagent to analyze keyword performance
|
|
6
|
+
* and recommend ADD/UPDATE/REMOVE actions for the correction keyword store.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { SubagentWorkflowSpec } from './types.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Input passed to the correction observer subagent.
|
|
13
|
+
*/
|
|
14
|
+
export interface CorrectionObserverPayload {
|
|
15
|
+
/** Parent session that triggered the optimization */
|
|
16
|
+
parentSessionId: string;
|
|
17
|
+
/** Workspace directory */
|
|
18
|
+
workspaceDir: string;
|
|
19
|
+
/** Current keyword store summary for context */
|
|
20
|
+
keywordStoreSummary: {
|
|
21
|
+
totalKeywords: number;
|
|
22
|
+
terms: Array<{
|
|
23
|
+
term: string;
|
|
24
|
+
weight: number;
|
|
25
|
+
hitCount: number;
|
|
26
|
+
truePositiveCount: number;
|
|
27
|
+
falsePositiveCount: number;
|
|
28
|
+
}>;
|
|
29
|
+
};
|
|
30
|
+
/** Recent user messages for pattern analysis */
|
|
31
|
+
recentMessages: string[];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Trajectory history: user turns where correctionDetected=true (D-40-08).
|
|
35
|
+
* Includes term matched, timestamp, sessionId for FPR trend analysis.
|
|
36
|
+
*/
|
|
37
|
+
trajectoryHistory: Array<{
|
|
38
|
+
sessionId: string;
|
|
39
|
+
timestamp: string;
|
|
40
|
+
term: string;
|
|
41
|
+
userMessage: string;
|
|
42
|
+
}>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Result from the correction observer subagent.
|
|
47
|
+
*/
|
|
48
|
+
export interface CorrectionObserverResult {
|
|
49
|
+
/** Whether any changes were made */
|
|
50
|
+
updated: boolean;
|
|
51
|
+
/** The optimization decisions returned by the LLM */
|
|
52
|
+
updates: Record<string, {
|
|
53
|
+
action: 'add' | 'update' | 'remove';
|
|
54
|
+
weight?: number;
|
|
55
|
+
falsePositiveRate?: number;
|
|
56
|
+
reasoning: string;
|
|
57
|
+
}>;
|
|
58
|
+
/** Human-readable summary */
|
|
59
|
+
summary: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Workflow spec for the correction observer optimization workflow.
|
|
64
|
+
*/
|
|
65
|
+
export interface CorrectionObserverWorkflowSpec extends SubagentWorkflowSpec<CorrectionObserverResult> {
|
|
66
|
+
workflowType: 'correction_observer';
|
|
67
|
+
payload: CorrectionObserverPayload;
|
|
68
|
+
result?: CorrectionObserverResult;
|
|
69
|
+
}
|