principles-disciple 1.107.0 → 1.108.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 +2 -2
- package/src/core/init.ts +3 -1
- package/src/core/workspace-dir-validation.ts +3 -3
- package/tests/core-anti-growth.test.ts +0 -13
- package/tests/hooks/prompt-characterization.test.ts +1 -11
- package/tests/hooks/prompt-diet.test.ts +3 -11
- package/tests/hooks/prompt-size-guard.test.ts +0 -10
- package/tests/hooks/runtime-v2-prompt-activation.test.ts +0 -10
- package/tests/index.test.ts +1 -1
- package/tests/runtime-v2-discovery-guard.test.ts +1 -2
- package/vitest.config.ts +2 -3
- package/vitest.unit.config.ts +12 -0
- package/src/core/evolution-hook.ts +0 -74
- package/src/core/file-storage-adapter.ts +0 -203
- package/src/core/merge-gate-audit.ts +0 -314
- package/src/core/pain-context-extractor.ts +0 -306
- package/src/core/pain-lifecycle.ts +0 -38
- package/src/core/pain-signal-adapter.ts +0 -42
- package/src/core/pain-signal.ts +0 -22
- package/src/core/principle-injector.ts +0 -84
- package/src/core/principle-tree-migration.ts +0 -196
- package/src/core/storage-adapter.ts +0 -65
- package/src/core/telemetry-event.ts +0 -109
- package/src/core/training-program.ts +0 -632
- package/src/core/workspace-dir-service.ts +0 -119
- package/src/hooks/lifecycle-routing.ts +0 -125
- package/src/service/event-log-auditor.ts +0 -284
- package/src/service/evolution-queue-lock.ts +0 -47
- package/src/service/failure-classifier.ts +0 -79
- package/src/service/internalization-trigger-adapter.ts +0 -302
- package/src/service/monitoring-query-service.ts +0 -67
- package/src/service/subagent-workflow/index.ts +0 -17
- package/src/tools/critique-prompt.ts +0 -1
- package/src/tools/model-index.ts +0 -1
- package/src/types/event-payload.ts +0 -16
- package/src/utils/glob-match.ts +0 -50
- package/src/utils/nlp.ts +0 -25
- package/src/utils/plugin-logger.ts +0 -97
- package/src/utils/subagent-probe.ts +0 -81
- package/tests/core/evolution-hook.test.ts +0 -123
- package/tests/core/file-storage-adapter.test.ts +0 -285
- package/tests/core/merge-gate-audit.test.ts +0 -117
- package/tests/core/pain-context-extractor.test.ts +0 -279
- package/tests/core/pain-lifecycle.test.ts +0 -38
- package/tests/core/pain-signal-adapter.test.ts +0 -116
- package/tests/core/pain-signal.test.ts +0 -190
- package/tests/core/principle-injector.test.ts +0 -90
- package/tests/core/principle-tree-migration.test.ts +0 -77
- package/tests/core/storage-conformance.test.ts +0 -429
- package/tests/core/telemetry-event.test.ts +0 -119
- package/tests/core/training-program.test.ts +0 -472
- package/tests/core/workspace-dir-service.test.ts +0 -68
- package/tests/core/workspace-dir-validation.test.ts +0 -143
- package/tests/integration/internalization-trigger-guard.test.ts +0 -69
- package/tests/integration/pain-lifecycle-e2e.test.ts +0 -75
- package/tests/integration/tool-hooks-workspace-dir.e2e.test.ts +0 -209
- package/tests/service/failure-classifier.test.ts +0 -171
- package/tests/service/internalization-trigger-adapter.test.ts +0 -251
- package/tests/service/monitoring-query-service.test.ts +0 -67
- package/tests/utils/nlp.test.ts +0 -35
- package/tests/utils/plugin-logger.test.ts +0 -156
- package/tests/utils/subagent-probe.test.ts +0 -79
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Internalization Dumb Trigger Adapter (PRI-63)
|
|
3
|
-
*
|
|
4
|
-
* Thin adapter that wakes periodically to probe SQLite for ready
|
|
5
|
-
* internalization tasks. Plugin layer only — does NOT execute LLM calls,
|
|
6
|
-
* does NOT directly chain PeerRunners, does NOT modify task status.
|
|
7
|
-
*
|
|
8
|
-
* Core decision functions (validateInternalizationTaskReady) determine
|
|
9
|
-
* if a task is ready to be processed. This adapter is only responsible
|
|
10
|
-
* for waking and logging — actual dispatch is handled by future
|
|
11
|
-
* Orchestrator PRI.
|
|
12
|
-
*
|
|
13
|
-
* @see docs/adr/0003-peer-agent-state-machine-orchestration.md
|
|
14
|
-
* @see packages/principles-core/src/runtime-v2/internalization/internalization-state-machine.ts (PRI-62)
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import type { TaskRecord } from '@principles/core/runtime-v2';
|
|
18
|
-
|
|
19
|
-
import {
|
|
20
|
-
validateInternalizationTaskReady,
|
|
21
|
-
isPeerRunnerKind,
|
|
22
|
-
hydratePITaskRecord,
|
|
23
|
-
type PITaskRecord,
|
|
24
|
-
type RunnerKind,
|
|
25
|
-
} from '@principles/core/runtime-v2';
|
|
26
|
-
|
|
27
|
-
// ── Structured log event types ───────────────────────────────────────────────
|
|
28
|
-
|
|
29
|
-
type TriggerWakeEvent = {
|
|
30
|
-
event: 'INTERNALIZATION_TRIGGER_WAKE';
|
|
31
|
-
workspaceDir: string;
|
|
32
|
-
taskId: string;
|
|
33
|
-
taskKind: RunnerKind;
|
|
34
|
-
correlationId?: string;
|
|
35
|
-
gateDecision: 'proceed';
|
|
36
|
-
timestamp: string;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
type TriggerNoopEvent = {
|
|
40
|
-
event: 'INTERNALIZATION_TRIGGER_NOOP';
|
|
41
|
-
workspaceDir: string;
|
|
42
|
-
timestamp: string;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
type TriggerBlockedEvent = {
|
|
46
|
-
event: 'INTERNALIZATION_TRIGGER_BLOCKED';
|
|
47
|
-
workspaceDir: string;
|
|
48
|
-
taskId: string;
|
|
49
|
-
taskKind: string;
|
|
50
|
-
gateDecision: 'blocked' | 'dependency_failed' | 'retry_wait_pending';
|
|
51
|
-
blockedBy?: string[];
|
|
52
|
-
failedDependencies?: string[];
|
|
53
|
-
retryAfter?: string;
|
|
54
|
-
timestamp: string;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
type TriggerFailedEvent = {
|
|
58
|
-
event: 'INTERNALIZATION_TRIGGER_FAILED';
|
|
59
|
-
workspaceDir: string;
|
|
60
|
-
failureCategory: 'invalid_context' | 'provider_error' | 'unexpected_error';
|
|
61
|
-
error?: string;
|
|
62
|
-
timestamp: string;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
type LogEvent = TriggerWakeEvent | TriggerNoopEvent | TriggerFailedEvent | TriggerBlockedEvent;
|
|
66
|
-
|
|
67
|
-
// ── Logger interface ─────────────────────────────────────────────────────────
|
|
68
|
-
|
|
69
|
-
export interface TriggerLogger {
|
|
70
|
-
debug?(msg: string, meta?: Record<string, unknown>): void;
|
|
71
|
-
info?(msg: string, meta?: Record<string, unknown>): void;
|
|
72
|
-
warn?(msg: string, meta?: Record<string, unknown>): void;
|
|
73
|
-
error?(msg: string, meta?: Record<string, unknown>): void;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// ── Task provider interface (dependency injection for testability) ───────────
|
|
77
|
-
|
|
78
|
-
export interface InternalizationTaskProvider {
|
|
79
|
-
listTasks(filter?: { status?: string; taskKind?: string }): Promise<TaskRecord[]>;
|
|
80
|
-
getTask(taskId: string): Promise<TaskRecord | null>;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// ── Wake context ──────────────────────────────────────────────────────────────
|
|
84
|
-
|
|
85
|
-
export interface TriggerContext {
|
|
86
|
-
workspaceDir: string;
|
|
87
|
-
stateDir: string;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// ── Adapter interface ─────────────────────────────────────────────────────────
|
|
91
|
-
|
|
92
|
-
export interface InternalizationTriggerAdapter {
|
|
93
|
-
wake(ctx: TriggerContext): Promise<void>;
|
|
94
|
-
start(ctx: TriggerContext, intervalMs?: number): () => void;
|
|
95
|
-
stop(): void;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// ── Internal state ─────────────────────────────────────────────────────────────
|
|
99
|
-
|
|
100
|
-
interface AdapterState {
|
|
101
|
-
intervalId: ReturnType<typeof setInterval> | null;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// ── Log helper ────────────────────────────────────────────────────────────────
|
|
105
|
-
|
|
106
|
-
function emitLog(
|
|
107
|
-
logger: TriggerLogger | undefined,
|
|
108
|
-
event: LogEvent,
|
|
109
|
-
): void {
|
|
110
|
-
const { event: eventType, ...meta } = event;
|
|
111
|
-
|
|
112
|
-
switch (eventType) {
|
|
113
|
-
case 'INTERNALIZATION_TRIGGER_WAKE':
|
|
114
|
-
logger?.info?.('[PD:InternalizationTrigger] INTERNALIZATION_TRIGGER_WAKE', meta);
|
|
115
|
-
break;
|
|
116
|
-
case 'INTERNALIZATION_TRIGGER_NOOP':
|
|
117
|
-
logger?.debug?.('[PD:InternalizationTrigger] INTERNALIZATION_TRIGGER_NOOP', meta);
|
|
118
|
-
break;
|
|
119
|
-
case 'INTERNALIZATION_TRIGGER_BLOCKED':
|
|
120
|
-
logger?.debug?.('[PD:InternalizationTrigger] INTERNALIZATION_TRIGGER_BLOCKED', meta);
|
|
121
|
-
break;
|
|
122
|
-
case 'INTERNALIZATION_TRIGGER_FAILED':
|
|
123
|
-
logger?.error?.('[PD:InternalizationTrigger] INTERNALIZATION_TRIGGER_FAILED', meta);
|
|
124
|
-
break;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// ── Core adapter factory ───────────────────────────────────────────────────────
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Creates a dumb trigger adapter that probes for ready internalization tasks.
|
|
132
|
-
*
|
|
133
|
-
* Design constraints (enforced by architecture regression guards):
|
|
134
|
-
* - PLUGIN_NO_INLINE_EXECUTION: wake() is read-only, does not await long tasks
|
|
135
|
-
* - PEER_NO_DIRECT_CHAINING: adapter does not call Dreamer/Philosopher/Scribe
|
|
136
|
-
* - TASK_MODEL_REUSE: reuses PITaskRecord / TaskRecord, no second task model
|
|
137
|
-
*
|
|
138
|
-
* @param provider - Task data access abstraction (SqliteTaskStore in production)
|
|
139
|
-
* @param logger - Optional structured logger
|
|
140
|
-
*/
|
|
141
|
-
export function createInternalizationTrigger(
|
|
142
|
-
provider: InternalizationTaskProvider,
|
|
143
|
-
logger?: TriggerLogger,
|
|
144
|
-
): InternalizationTriggerAdapter {
|
|
145
|
-
const state: AdapterState = {
|
|
146
|
-
intervalId: null,
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
// ── wake: single trigger cycle ─────────────────────────────────────────────
|
|
150
|
-
|
|
151
|
-
async function wake(ctx: TriggerContext): Promise<void> {
|
|
152
|
-
// Fail closed: missing workspaceDir or stateDir
|
|
153
|
-
if (!ctx.workspaceDir || !ctx.stateDir) {
|
|
154
|
-
emitLog(logger, {
|
|
155
|
-
event: 'INTERNALIZATION_TRIGGER_FAILED',
|
|
156
|
-
workspaceDir: ctx.workspaceDir ?? '(missing)',
|
|
157
|
-
failureCategory: 'invalid_context',
|
|
158
|
-
error: 'workspaceDir or stateDir is missing',
|
|
159
|
-
timestamp: new Date().toISOString(),
|
|
160
|
-
});
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
try {
|
|
165
|
-
// Query pending and retry_wait tasks (non-terminal states that can be leased)
|
|
166
|
-
const pendingTasks = await provider.listTasks({ status: 'pending' });
|
|
167
|
-
const retryWaitTasks = await provider.listTasks({ status: 'retry_wait' });
|
|
168
|
-
|
|
169
|
-
// Filter to only PeerRunner tasks, then hydrate PI metadata from diagnosticJson.
|
|
170
|
-
// Tasks without valid PI metadata in diagnosticJson return null and are filtered out.
|
|
171
|
-
const pendingPITasks = pendingTasks
|
|
172
|
-
.filter(t => isPeerRunnerKind(t.taskKind))
|
|
173
|
-
.map(t => hydratePITaskRecord(t))
|
|
174
|
-
.filter((t): t is PITaskRecord => t !== null);
|
|
175
|
-
|
|
176
|
-
const retryWaitPITasks = retryWaitTasks
|
|
177
|
-
.filter(t => isPeerRunnerKind(t.taskKind))
|
|
178
|
-
.map(t => hydratePITaskRecord(t))
|
|
179
|
-
.filter((t): t is PITaskRecord => t !== null);
|
|
180
|
-
|
|
181
|
-
const candidateTasks = [...pendingPITasks, ...retryWaitPITasks];
|
|
182
|
-
|
|
183
|
-
// No candidates → NOOP
|
|
184
|
-
if (candidateTasks.length === 0) {
|
|
185
|
-
emitLog(logger, {
|
|
186
|
-
event: 'INTERNALIZATION_TRIGGER_NOOP',
|
|
187
|
-
workspaceDir: ctx.workspaceDir,
|
|
188
|
-
timestamp: new Date().toISOString(),
|
|
189
|
-
});
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Evaluate each candidate
|
|
194
|
-
let hasReadyTask = false;
|
|
195
|
-
|
|
196
|
-
for (const task of candidateTasks) {
|
|
197
|
-
// Fetch dependency tasks for gate validation
|
|
198
|
-
const dependencies: TaskRecord[] = [];
|
|
199
|
-
if (task.dependencyTaskIds && task.dependencyTaskIds.length > 0) {
|
|
200
|
-
for (const depId of task.dependencyTaskIds) {
|
|
201
|
-
const dep = await provider.getTask(depId);
|
|
202
|
-
if (dep) dependencies.push(dep);
|
|
203
|
-
// Fail closed: if dep not found, dependencies array is incomplete
|
|
204
|
-
// validateInternalizationTaskReady will return 'blocked'
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const gateResult = validateInternalizationTaskReady(task, dependencies);
|
|
209
|
-
|
|
210
|
-
if (gateResult.decision === 'proceed') {
|
|
211
|
-
// Task is ready — log WAKE event (read-only, no mutation)
|
|
212
|
-
emitLog(logger, {
|
|
213
|
-
event: 'INTERNALIZATION_TRIGGER_WAKE',
|
|
214
|
-
workspaceDir: ctx.workspaceDir,
|
|
215
|
-
taskId: task.taskId,
|
|
216
|
-
taskKind: task.taskKind,
|
|
217
|
-
correlationId: task.correlationId,
|
|
218
|
-
gateDecision: gateResult.decision,
|
|
219
|
-
timestamp: new Date().toISOString(),
|
|
220
|
-
});
|
|
221
|
-
hasReadyTask = true;
|
|
222
|
-
} else {
|
|
223
|
-
// blocked, dependency_failed, or retry_wait_pending — debug visibility for operators
|
|
224
|
-
emitLog(logger, {
|
|
225
|
-
event: 'INTERNALIZATION_TRIGGER_BLOCKED',
|
|
226
|
-
workspaceDir: ctx.workspaceDir,
|
|
227
|
-
taskId: task.taskId,
|
|
228
|
-
taskKind: task.taskKind,
|
|
229
|
-
gateDecision: gateResult.decision,
|
|
230
|
-
blockedBy: gateResult.decision === 'blocked' ? gateResult.blockedBy : undefined,
|
|
231
|
-
failedDependencies: gateResult.decision === 'dependency_failed' ? gateResult.failedDependencies : undefined,
|
|
232
|
-
retryAfter: gateResult.decision === 'retry_wait_pending' ? gateResult.retryAfter : undefined,
|
|
233
|
-
timestamp: new Date().toISOString(),
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// All candidates were blocked
|
|
239
|
-
if (!hasReadyTask) {
|
|
240
|
-
emitLog(logger, {
|
|
241
|
-
event: 'INTERNALIZATION_TRIGGER_NOOP',
|
|
242
|
-
workspaceDir: ctx.workspaceDir,
|
|
243
|
-
timestamp: new Date().toISOString(),
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
} catch (err) {
|
|
247
|
-
emitLog(logger, {
|
|
248
|
-
event: 'INTERNALIZATION_TRIGGER_FAILED',
|
|
249
|
-
workspaceDir: ctx.workspaceDir,
|
|
250
|
-
failureCategory: 'provider_error',
|
|
251
|
-
error: err instanceof Error ? err.message : String(err),
|
|
252
|
-
timestamp: new Date().toISOString(),
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// ── start: begin periodic wake cycles ─────────────────────────────────────
|
|
258
|
-
|
|
259
|
-
function start(ctx: TriggerContext, intervalMs = 5 * 60 * 1000): () => void {
|
|
260
|
-
// Prevent re-entrancy: if already running, warn and return existing stop
|
|
261
|
-
if (state.intervalId !== null) {
|
|
262
|
-
logger?.warn?.('[PD:InternalizationTrigger] start() called while already running — returning existing stop', {
|
|
263
|
-
workspaceDir: ctx.workspaceDir,
|
|
264
|
-
stateDir: ctx.stateDir,
|
|
265
|
-
});
|
|
266
|
-
return stop;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// Immediate first wake
|
|
270
|
-
wake(ctx).catch(err => {
|
|
271
|
-
logger?.error?.('[PD:InternalizationTrigger] start() initial wake failed', {
|
|
272
|
-
error: err instanceof Error ? err.message : String(err),
|
|
273
|
-
});
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
// Schedule periodic wake
|
|
277
|
-
state.intervalId = setInterval(() => {
|
|
278
|
-
wake(ctx).catch(err => {
|
|
279
|
-
logger?.error?.('[PD:InternalizationTrigger] periodic wake failed', {
|
|
280
|
-
error: err instanceof Error ? err.message : String(err),
|
|
281
|
-
});
|
|
282
|
-
});
|
|
283
|
-
}, intervalMs);
|
|
284
|
-
|
|
285
|
-
return stop;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// ── stop: halt periodic wake ──────────────────────────────────────────────
|
|
289
|
-
|
|
290
|
-
function stop(): void {
|
|
291
|
-
if (state.intervalId !== null) {
|
|
292
|
-
clearInterval(state.intervalId);
|
|
293
|
-
state.intervalId = null;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
return { wake, start, stop };
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// ── Re-export types for consumers ────────────────────────────────────────────
|
|
301
|
-
|
|
302
|
-
export type { PITaskRecord, PeerRunnerKind } from '@principles/core/runtime-v2';
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { WorkflowStore } from './subagent-workflow/workflow-store.js';
|
|
2
|
-
|
|
3
|
-
export class MonitoringQueryService {
|
|
4
|
-
private readonly workspaceDir: string;
|
|
5
|
-
private readonly store: WorkflowStore;
|
|
6
|
-
|
|
7
|
-
constructor(workspaceDir: string) {
|
|
8
|
-
this.workspaceDir = workspaceDir;
|
|
9
|
-
this.store = new WorkflowStore({ workspaceDir });
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
dispose(): void {
|
|
13
|
-
this.store.dispose();
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
getWorkflows(filters: { state?: string; type?: string } = {}): WorkflowListResponse {
|
|
17
|
-
let workflows = filters.state
|
|
18
|
-
? this.store.listWorkflows(filters.state)
|
|
19
|
-
: this.store.listWorkflows();
|
|
20
|
-
|
|
21
|
-
if (filters.type) {
|
|
22
|
-
workflows = workflows.filter(wf => wf.workflow_type === filters.type);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const now = Date.now();
|
|
26
|
-
const workflowsWithStuckDetection = workflows.map(wf => {
|
|
27
|
-
const metadata = parseWorkflowMetadata(wf.metadata_json);
|
|
28
|
-
const timeoutMs = metadata.timeoutMs ?? 15 * 60 * 1000;
|
|
29
|
-
|
|
30
|
-
const isStuck = wf.state === 'active' && (now - wf.created_at) > timeoutMs;
|
|
31
|
-
const stuckDuration = isStuck ? now - wf.created_at : null;
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
workflowId: wf.workflow_id,
|
|
35
|
-
type: wf.workflow_type,
|
|
36
|
-
state: isStuck ? 'stuck' : wf.state,
|
|
37
|
-
duration: now - wf.created_at,
|
|
38
|
-
createdAt: new Date(wf.created_at).toISOString(),
|
|
39
|
-
stuckDuration,
|
|
40
|
-
};
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
return { workflows: workflowsWithStuckDetection };
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function parseWorkflowMetadata(metadataJson: string): { timeoutMs?: number } {
|
|
48
|
-
try {
|
|
49
|
-
const parsed = JSON.parse(metadataJson) as { timeoutMs?: number };
|
|
50
|
-
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
51
|
-
} catch {
|
|
52
|
-
return {};
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface WorkflowListResponse {
|
|
57
|
-
workflows: WorkflowInfo[];
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export interface WorkflowInfo {
|
|
61
|
-
workflowId: string;
|
|
62
|
-
type: string;
|
|
63
|
-
state: string;
|
|
64
|
-
duration: number;
|
|
65
|
-
createdAt: string;
|
|
66
|
-
stuckDuration: number | null;
|
|
67
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export { isExpectedSubagentError } from './subagent-error-utils.js';
|
|
2
|
-
|
|
3
|
-
export { WorkflowStore, type WorkflowStoreOptions } from './workflow-store.js';
|
|
4
|
-
|
|
5
|
-
export type {
|
|
6
|
-
WorkflowState,
|
|
7
|
-
WorkflowTransport,
|
|
8
|
-
WorkflowMetadata,
|
|
9
|
-
WorkflowResultContext,
|
|
10
|
-
WorkflowPersistContext,
|
|
11
|
-
WorkflowHandle,
|
|
12
|
-
SubagentWorkflowSpec,
|
|
13
|
-
WorkflowRow,
|
|
14
|
-
WorkflowEventRow,
|
|
15
|
-
WorkflowDebugSummary,
|
|
16
|
-
} from './types.js';
|
|
17
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/src/tools/model-index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export type {
|
|
2
|
-
EventLogEntry as DiscriminatedEventLogEntry,
|
|
3
|
-
} from '@principles/core/runtime-v2';
|
|
4
|
-
|
|
5
|
-
export {
|
|
6
|
-
isToolCallEventEntry,
|
|
7
|
-
isPainSignalEventEntry,
|
|
8
|
-
isRuleMatchEventEntry,
|
|
9
|
-
isRulePromotionEventEntry,
|
|
10
|
-
isHookExecutionEventEntry,
|
|
11
|
-
isGateBlockEventEntry,
|
|
12
|
-
isGateBypassEventEntry,
|
|
13
|
-
isPlanApprovalEventEntry,
|
|
14
|
-
isEvolutionTaskEventEntry,
|
|
15
|
-
isEmpathyRollbackEventEntry,
|
|
16
|
-
} from '@principles/core/runtime-v2';
|
package/src/utils/glob-match.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Glob pattern matching utilities using micromatch.
|
|
3
|
-
* Provides lightweight file path pattern matching for whitelist/blacklist operations.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import micromatch from 'micromatch';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Checks if a file path matches any of the provided glob patterns.
|
|
10
|
-
*
|
|
11
|
-
* @param filePath - The file path to check (relative or absolute)
|
|
12
|
-
* @param patterns - Array of glob patterns (supports *, **, ?, etc.)
|
|
13
|
-
* @returns true if the path matches any pattern, false otherwise
|
|
14
|
-
*/
|
|
15
|
-
export function matchesAnyPattern(filePath: string, patterns: string[]): boolean {
|
|
16
|
-
if (!patterns || patterns.length === 0) return false;
|
|
17
|
-
return micromatch.isMatch(filePath, patterns);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Filters an array of file paths to only those that match any pattern.
|
|
22
|
-
*
|
|
23
|
-
* @param filePaths - Array of file paths to filter
|
|
24
|
-
* @param patterns - Array of glob patterns (supports *, **, ?, !)
|
|
25
|
-
* @returns Array of matching file paths
|
|
26
|
-
*/
|
|
27
|
-
export function filterMatchingPaths(filePaths: string[], patterns: string[]): string[] {
|
|
28
|
-
if (!patterns || patterns.length === 0) return [];
|
|
29
|
-
return micromatch.match(filePaths, patterns);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Returns all patterns that match the given file path.
|
|
34
|
-
*
|
|
35
|
-
* @param filePath - The file path to check
|
|
36
|
-
* @param patterns - Array of glob patterns
|
|
37
|
-
* @returns Array of patterns that matched the path
|
|
38
|
-
*/
|
|
39
|
-
export function getMatchingPatterns(filePath: string, patterns: string[]): string[] {
|
|
40
|
-
if (!patterns || patterns.length === 0) return [];
|
|
41
|
-
return micromatch.match([filePath], patterns).map((match: string) => {
|
|
42
|
-
// Find the pattern that produced this match
|
|
43
|
-
for (const pattern of patterns) {
|
|
44
|
-
if (micromatch.isMatch(match, pattern)) {
|
|
45
|
-
return pattern;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return '';
|
|
49
|
-
}).filter(Boolean);
|
|
50
|
-
}
|
package/src/utils/nlp.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Extracts common phrases (N-grams) that appear in a majority of the given samples.
|
|
3
|
-
*/
|
|
4
|
-
export function extractCommonPhrases(samples: string[], minOccurrence = 3): string[] {
|
|
5
|
-
if (samples.length < minOccurrence) return [];
|
|
6
|
-
|
|
7
|
-
const phrases = new Map<string, number>();
|
|
8
|
-
const n = 3; // Look for 3-word n-grams as a baseline for "phrases"
|
|
9
|
-
|
|
10
|
-
for (const sample of samples) {
|
|
11
|
-
// Keep all words, lowercased, remove basic punctuation
|
|
12
|
-
const words = sample.toLowerCase().replace(/[.,!?;:()]/g, '').split(/\s+/).filter(w => w.length > 0);
|
|
13
|
-
|
|
14
|
-
for (let i = 0; i <= words.length - n; i++) {
|
|
15
|
-
const ngram = words.slice(i, i + n).join(' ');
|
|
16
|
-
phrases.set(ngram, (phrases.get(ngram) || 0) + 1);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Filter phrases that meet the threshold
|
|
21
|
-
return Array.from(phrases.entries())
|
|
22
|
-
|
|
23
|
-
.filter(([_elem, count]) => count >= minOccurrence)
|
|
24
|
-
.map(([phrase, _count]) => phrase);
|
|
25
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
|
|
4
|
-
export interface PluginLogger {
|
|
5
|
-
|
|
6
|
-
info(message: string, meta?: Record<string, unknown>): void;
|
|
7
|
-
warn(message: string, meta?: Record<string, unknown>): void;
|
|
8
|
-
error(message: string, meta?: Record<string, unknown>): void;
|
|
9
|
-
debug(message: string, meta?: Record<string, unknown>): void;
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface PluginLoggerConfig {
|
|
14
|
-
/** Directory to store log files (typically stateDir) */
|
|
15
|
-
logDir: string;
|
|
16
|
-
/** Plugin identifier for log file naming */
|
|
17
|
-
pluginId: string;
|
|
18
|
-
/** Console logger from OpenClaw API (for dual output) */
|
|
19
|
-
consoleLogger?: PluginLogger;
|
|
20
|
-
/** Whether to also output to console */
|
|
21
|
-
echoToConsole?: boolean;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Creates a plugin-specific logger that writes to its own log file.
|
|
26
|
-
*
|
|
27
|
-
* Log file location: {logDir}/logs/{pluginId}.log
|
|
28
|
-
*
|
|
29
|
-
* Example:
|
|
30
|
-
* ```
|
|
31
|
-
* const logger = createPluginLogger({
|
|
32
|
-
* logDir: stateDir, // e.g. ~/.openclaw/
|
|
33
|
-
* pluginId: 'principles-disciple',
|
|
34
|
-
* consoleLogger: api.logger,
|
|
35
|
-
* echoToConsole: true
|
|
36
|
-
* });
|
|
37
|
-
*
|
|
38
|
-
* logger.info('Evolution task completed', { taskId: 'abc123' });
|
|
39
|
-
* ```
|
|
40
|
-
*/
|
|
41
|
-
export function createPluginLogger(config: PluginLoggerConfig): PluginLogger {
|
|
42
|
-
const { logDir, pluginId, consoleLogger, echoToConsole = true } = config;
|
|
43
|
-
|
|
44
|
-
// Ensure logs directory exists
|
|
45
|
-
const logsDir = path.join(logDir, 'logs');
|
|
46
|
-
if (!fs.existsSync(logsDir)) {
|
|
47
|
-
fs.mkdirSync(logsDir, { recursive: true });
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const logFile = path.join(logsDir, `${pluginId}.log`);
|
|
51
|
-
|
|
52
|
-
const writeLog = (level: string, message: string, meta?: Record<string, unknown>) => {
|
|
53
|
-
const timestamp = new Date().toISOString();
|
|
54
|
-
const metaStr = meta ? ` ${JSON.stringify(meta)}` : '';
|
|
55
|
-
const logLine = `${timestamp} [${level.toUpperCase()}] ${message}${metaStr}\n`;
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
fs.appendFileSync(logFile, logLine, 'utf-8');
|
|
59
|
-
} catch {
|
|
60
|
-
// Silently fail if log write fails
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Also echo to console if enabled
|
|
64
|
-
if (echoToConsole && consoleLogger) {
|
|
65
|
-
const consoleMessage = `[${pluginId}] ${message}`;
|
|
66
|
-
switch (level) {
|
|
67
|
-
case 'info':
|
|
68
|
-
consoleLogger.info(consoleMessage, meta);
|
|
69
|
-
break;
|
|
70
|
-
case 'warn':
|
|
71
|
-
consoleLogger.warn(consoleMessage, meta);
|
|
72
|
-
break;
|
|
73
|
-
case 'error':
|
|
74
|
-
consoleLogger.error(consoleMessage, meta);
|
|
75
|
-
break;
|
|
76
|
-
case 'debug':
|
|
77
|
-
consoleLogger.debug(consoleMessage, meta);
|
|
78
|
-
break;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
info: (message, meta) => writeLog('info', message, meta),
|
|
85
|
-
warn: (message, meta) => writeLog('warn', message, meta),
|
|
86
|
-
error: (message, meta) => writeLog('error', message, meta),
|
|
87
|
-
debug: (message, meta) => writeLog('debug', message, meta),
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Log file path for the plugin.
|
|
93
|
-
* Returns the path where plugin logs are stored.
|
|
94
|
-
*/
|
|
95
|
-
export function getPluginLogPath(logDir: string, pluginId: string): string {
|
|
96
|
-
return path.join(logDir, 'logs', `${pluginId}.log`);
|
|
97
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Subagent Runtime Availability Probe
|
|
3
|
-
*
|
|
4
|
-
* This utility intentionally avoids inferring runtime availability from
|
|
5
|
-
* JavaScript implementation details like constructor names. The only contract
|
|
6
|
-
* we trust here is whether a callable `run` entrypoint exists.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { OpenClawPluginApi } from '../openclaw-sdk.js';
|
|
10
|
-
|
|
11
|
-
type SubagentRuntime = NonNullable<OpenClawPluginApi['runtime']>['subagent'];
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Try to access the global gateway subagent runtime.
|
|
15
|
-
* This is a fallback for cases where the plugin was loaded with
|
|
16
|
-
* allowGatewaySubagentBinding but the late-binding proxy isn't working.
|
|
17
|
-
*/
|
|
18
|
-
function getGlobalGatewaySubagent(): SubagentRuntime | null {
|
|
19
|
-
try {
|
|
20
|
-
// Access the global symbol that OpenClaw uses for gateway subagent
|
|
21
|
-
const symbol = Symbol.for('openclaw.plugin.gatewaySubagentRuntime');
|
|
22
|
-
const globalState = (globalThis as Record<string, unknown>)[symbol as unknown as string] as { subagent?: SubagentRuntime } | null;
|
|
23
|
-
return globalState?.subagent ?? null;
|
|
24
|
-
} catch {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Return a small, explicit availability assessment for the subagent runtime.
|
|
31
|
-
* This is a shape check only. Actual invocation failures are classified by the
|
|
32
|
-
* caller as runtime-unavailable vs downstream task failures.
|
|
33
|
-
*
|
|
34
|
-
* @param subagent - The subagent runtime object from api.runtime.subagent
|
|
35
|
-
* @returns availability status and reason
|
|
36
|
-
*/
|
|
37
|
-
export function getSubagentRuntimeAvailability(
|
|
38
|
-
subagent: { run?: unknown } | undefined
|
|
39
|
-
): { available: boolean; reason: 'missing_runtime' | 'missing_run' | 'callable' } {
|
|
40
|
-
if (!subagent) return { available: false, reason: 'missing_runtime' };
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
const runFn = subagent.run;
|
|
44
|
-
if (typeof runFn !== 'function') {
|
|
45
|
-
return { available: false, reason: 'missing_run' };
|
|
46
|
-
}
|
|
47
|
-
return { available: true, reason: 'callable' };
|
|
48
|
-
} catch {
|
|
49
|
-
return { available: false, reason: 'missing_run' };
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function isSubagentRuntimeAvailable(
|
|
54
|
-
subagent: { run?: unknown } | undefined
|
|
55
|
-
): boolean {
|
|
56
|
-
return getSubagentRuntimeAvailability(subagent).available;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Get the actual subagent runtime, preferring the global gateway subagent
|
|
61
|
-
* if the passed one is not available.
|
|
62
|
-
*
|
|
63
|
-
* This is useful for cases where the plugin was loaded with allowGatewaySubagentBinding
|
|
64
|
-
* but the late-binding proxy isn't resolving correctly.
|
|
65
|
-
*/
|
|
66
|
-
export function getAvailableSubagentRuntime(
|
|
67
|
-
subagent: SubagentRuntime | undefined
|
|
68
|
-
): SubagentRuntime | undefined {
|
|
69
|
-
// First check if the passed subagent is available
|
|
70
|
-
if (isSubagentRuntimeAvailable(subagent)) {
|
|
71
|
-
return subagent;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Fallback to global gateway subagent
|
|
75
|
-
const globalGateway = getGlobalGatewaySubagent();
|
|
76
|
-
if (globalGateway && isSubagentRuntimeAvailable(globalGateway)) {
|
|
77
|
-
return globalGateway;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return undefined;
|
|
81
|
-
}
|