principles-disciple 1.143.0 → 1.145.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/dist/commands/evolution-status.js +0 -2
- package/dist/core/event-log.d.ts +1 -3
- package/dist/core/event-log.js +10 -21
- package/dist/core/evolution-reducer.d.ts +0 -8
- package/dist/core/evolution-reducer.js +9 -19
- package/dist/core/pain.d.ts +0 -62
- package/dist/core/pain.js +0 -143
- package/dist/core/paths.d.ts +0 -1
- package/dist/core/paths.js +2 -1
- package/dist/core/schema/migrations/001-init-trajectory.js +3 -6
- package/dist/core/schema/schema-definitions.js +4 -9
- package/dist/core/trajectory.d.ts +0 -16
- package/dist/core/trajectory.js +9 -60
- package/dist/hooks/lifecycle.js +3 -6
- package/dist/hooks/llm.js +2 -8
- package/dist/service/evolution-worker.js +6 -64
- package/dist/service/runtime-summary-service.d.ts +0 -2
- package/dist/service/runtime-summary-service.js +0 -4
- package/dist/types/event-types.d.ts +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/scripts/validate-live-path.ts +0 -18
|
@@ -38,7 +38,6 @@ function buildEnglishOutput(workspaceDir, sessionId, warnings, stats, summary, r
|
|
|
38
38
|
'Control Plane',
|
|
39
39
|
`- Session GFI: current ${formatNumber(summary.gfi.current)}, peak ${formatNumber(summary.gfi.peak)} (${summary.gfi.dataQuality})`,
|
|
40
40
|
`- GFI Sources: ${formatSources(summary.gfi.sources)}`,
|
|
41
|
-
`- Pain Flag: ${summary.pain.activeFlag ? 'active' : 'inactive'}${summary.pain.activeFlagSource ? ` (${summary.pain.activeFlagSource})` : ''}`,
|
|
42
41
|
`- Last Pain Signal: ${summary.pain.lastSignal ? `${summary.pain.lastSignal.source}${summary.pain.lastSignal.reason ? ` - ${summary.pain.lastSignal.reason}` : ''}` : '--'}`,
|
|
43
42
|
`- Gate Events: blocks ${formatNumber(summary.gate.recentBlocks)}, bypasses ${formatNumber(summary.gate.recentBypasses)} (${summary.gate.dataQuality})`,
|
|
44
43
|
'',
|
|
@@ -97,7 +96,6 @@ function buildChineseOutput(workspaceDir, sessionId, warnings, stats, summary, r
|
|
|
97
96
|
'控制面',
|
|
98
97
|
`- 会话 GFI: 当前 ${formatNumber(summary.gfi.current)},峰值 ${formatNumber(summary.gfi.peak)} (${summary.gfi.dataQuality})`,
|
|
99
98
|
`- GFI 来源: ${formatSources(summary.gfi.sources)}`,
|
|
100
|
-
`- Pain Flag: ${summary.pain.activeFlag ? 'active' : 'inactive'}${summary.pain.activeFlagSource ? ` (${summary.pain.activeFlagSource})` : ''}`,
|
|
101
99
|
`- 最近 Pain 信号: ${summary.pain.lastSignal ? `${summary.pain.lastSignal.source}${summary.pain.lastSignal.reason ? ` - ${summary.pain.lastSignal.reason}` : ''}` : '--'}`,
|
|
102
100
|
`- Gate 事件: block ${formatNumber(summary.gate.recentBlocks)},bypass ${formatNumber(summary.gate.recentBypasses)} (${summary.gate.dataQuality})`,
|
|
103
101
|
'',
|
package/dist/core/event-log.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EventLogEntry, DailyStats, EmpathyEventStats, ToolCallEventData, PainSignalEventData,
|
|
1
|
+
import type { EventLogEntry, DailyStats, EmpathyEventStats, ToolCallEventData, PainSignalEventData, RulePromotionEventData, HookExecutionEventData, GateBlockEventData, GateBypassEventData, PlanApprovalEventData, EvolutionTaskEventData, EmpathyRollbackEventData, DiagnosisTaskEventData, HeartbeatDiagnosisEventData, DiagnosticianReportEventData, PrincipleCandidateEventData, RuleEnforcedEventData, RuleHostEvaluatedEventData, RuleHostBlockedEventData, RuleHostRequireApprovalEventData, RuleHostAutoCorrectProposedEventData, RuleHostAutoCorrectAppliedEventData, RuntimeV2PromptActivationsInjectedEventData, RuleHostUnhealthyEventData } from '../types/event-types.js';
|
|
2
2
|
import type { PluginLogger } from '../openclaw-sdk.js';
|
|
3
3
|
export declare class EventLog {
|
|
4
4
|
private readonly logsDir;
|
|
@@ -11,7 +11,6 @@ export declare class EventLog {
|
|
|
11
11
|
private flushTimer?;
|
|
12
12
|
private currentEventsFile;
|
|
13
13
|
private currentDate;
|
|
14
|
-
private readonly painScoreSums;
|
|
15
14
|
constructor(stateDir: string, logger?: PluginLogger);
|
|
16
15
|
private getEventsFile;
|
|
17
16
|
private getTodayStr;
|
|
@@ -19,7 +18,6 @@ export declare class EventLog {
|
|
|
19
18
|
private cleanupOldEventFiles;
|
|
20
19
|
recordToolCall(sessionId: string | undefined, data: ToolCallEventData): void;
|
|
21
20
|
recordPainSignal(sessionId: string | undefined, data: PainSignalEventData): void;
|
|
22
|
-
recordRuleMatch(sessionId: string | undefined, data: RuleMatchEventData): void;
|
|
23
21
|
recordRulePromotion(data: RulePromotionEventData): void;
|
|
24
22
|
recordHookExecution(data: HookExecutionEventData, opts?: {
|
|
25
23
|
flushImmediately?: boolean;
|
package/dist/core/event-log.js
CHANGED
|
@@ -15,7 +15,8 @@ export class EventLog {
|
|
|
15
15
|
flushTimer;
|
|
16
16
|
currentEventsFile;
|
|
17
17
|
currentDate;
|
|
18
|
-
painScoreSums
|
|
18
|
+
// painScoreSums map removed (PRI-451 Wave 1.5): it only fed the dead
|
|
19
|
+
// stats.pain.avgScore counter, which is also removed.
|
|
19
20
|
constructor(stateDir, logger) {
|
|
20
21
|
this.logsDir = path.join(stateDir, 'logs');
|
|
21
22
|
if (!fs.existsSync(this.logsDir)) {
|
|
@@ -68,9 +69,8 @@ export class EventLog {
|
|
|
68
69
|
recordPainSignal(sessionId, data) {
|
|
69
70
|
this.record('pain_signal', 'detected', sessionId, data);
|
|
70
71
|
}
|
|
71
|
-
recordRuleMatch(
|
|
72
|
-
|
|
73
|
-
}
|
|
72
|
+
// recordRuleMatch removed (PRI-451 Wave 1): dead code. Its only effect was
|
|
73
|
+
// incrementing stats.pain.rulesMatched (also dead, removed in Wave 1.5).
|
|
74
74
|
recordRulePromotion(data) {
|
|
75
75
|
this.record('rule_promotion', 'promoted', undefined, data);
|
|
76
76
|
}
|
|
@@ -293,16 +293,8 @@ export class EventLog {
|
|
|
293
293
|
}
|
|
294
294
|
else if (entry.type === 'pain_signal') {
|
|
295
295
|
const data = entry.data;
|
|
296
|
-
stats.pain.
|
|
297
|
-
|
|
298
|
-
if (data.source) {
|
|
299
|
-
stats.pain.signalsBySource[data.source] = (stats.pain.signalsBySource[data.source] || 0) + 1;
|
|
300
|
-
}
|
|
301
|
-
const currentSum = this.painScoreSums.get(entry.date) ?? 0;
|
|
302
|
-
this.painScoreSums.set(entry.date, currentSum + (data.score || 0));
|
|
303
|
-
stats.pain.avgScore = stats.pain.signalsDetected > 0
|
|
304
|
-
? Math.round((currentSum + (data.score || 0)) / stats.pain.signalsDetected)
|
|
305
|
-
: 0;
|
|
296
|
+
// stats.pain.* counters removed (PRI-451 Wave 1.5): no live reader.
|
|
297
|
+
// The user_empathy aggregation below (stats.empathy.*) is LIVE and stays.
|
|
306
298
|
if (data.source === 'user_empathy') {
|
|
307
299
|
if (data.deduped) {
|
|
308
300
|
stats.empathy.dedupedCount++;
|
|
@@ -358,14 +350,11 @@ export class EventLog {
|
|
|
358
350
|
stats.empathy.rollbackCount++;
|
|
359
351
|
stats.empathy.rolledBackScore += data.originalScore || 0;
|
|
360
352
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
stats.pain.rulesMatched[data.ruleId] = (stats.pain.rulesMatched[data.ruleId] || 0) + 1;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
353
|
+
// rule_match handler removed (PRI-451 Wave 1.5): recordRuleMatch is gone
|
|
354
|
+
// (Wave 1.1), so no rule_match events are emitted; stats.pain.rulesMatched
|
|
355
|
+
// was its only consumer and is also removed.
|
|
367
356
|
else if (entry.type === 'rule_promotion') {
|
|
368
|
-
stats.pain.candidatesPromoted
|
|
357
|
+
// stats.pain.candidatesPromoted removed (PRI-451 Wave 1.5): dead counter.
|
|
369
358
|
stats.evolution.rulesPromoted++;
|
|
370
359
|
}
|
|
371
360
|
else if (entry.type === 'evolution_task') {
|
|
@@ -52,20 +52,12 @@ export declare class EvolutionReducerImpl implements EvolutionReducer {
|
|
|
52
52
|
private readonly failureStreak;
|
|
53
53
|
private lastPromotedAt;
|
|
54
54
|
private isReplaying;
|
|
55
|
-
/** Registered pain_detected callbacks (e.g., PainSignalBridge). */
|
|
56
|
-
private readonly _painCallbacks;
|
|
57
55
|
constructor(opts: {
|
|
58
56
|
workspaceDir: string;
|
|
59
57
|
stateDir?: string;
|
|
60
58
|
});
|
|
61
59
|
emit(event: EvolutionLoopEvent): void;
|
|
62
60
|
emitSync(event: EvolutionLoopEvent): void;
|
|
63
|
-
/**
|
|
64
|
-
* Register a callback for 'pain_detected' events.
|
|
65
|
-
* The callback is invoked synchronously within emitSync() after applyEvent completes.
|
|
66
|
-
* HG-4: Callbacks are fire-and-forget from the emitSync perspective.
|
|
67
|
-
*/
|
|
68
|
-
on(callback: (event: EvolutionLoopEvent) => void): void;
|
|
69
61
|
getEventLog(): EvolutionLoopEvent[];
|
|
70
62
|
getCandidatePrinciples(): Principle[];
|
|
71
63
|
getProbationPrinciples(): Principle[];
|
|
@@ -38,8 +38,9 @@ export class EvolutionReducerImpl {
|
|
|
38
38
|
failureStreak = new Map();
|
|
39
39
|
lastPromotedAt = null;
|
|
40
40
|
isReplaying = false;
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
// _painCallbacks pub/sub removed (PRI-451 Wave 1): no code ever registered a
|
|
42
|
+
// callback via .on(). The pain_detected → PainSignalBridge bridge happens via
|
|
43
|
+
// emitPainDetectedEvent (hooks/pain.ts), not this pub/sub layer.
|
|
43
44
|
constructor(opts) {
|
|
44
45
|
this.workspaceDir = opts.workspaceDir;
|
|
45
46
|
this.stateDir = opts.stateDir;
|
|
@@ -77,14 +78,9 @@ export class EvolutionReducerImpl {
|
|
|
77
78
|
}
|
|
78
79
|
// Performance: sweepExpiredProbation() moved to getProbationPrinciples() for lazy cleanup
|
|
79
80
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
* HG-4: Callbacks are fire-and-forget from the emitSync perspective.
|
|
84
|
-
*/
|
|
85
|
-
on(callback) {
|
|
86
|
-
this._painCallbacks.push(callback);
|
|
87
|
-
}
|
|
81
|
+
// on(callback) pub/sub registration removed (PRI-451 Wave 1): dead code —
|
|
82
|
+
// no caller ever registered a callback. pain_detected events still flow via
|
|
83
|
+
// emitPainDetectedEvent → PainSignalBridge, unaffected.
|
|
88
84
|
getEventLog() {
|
|
89
85
|
return [...this.memoryEvents];
|
|
90
86
|
}
|
|
@@ -522,15 +518,9 @@ export class EvolutionReducerImpl {
|
|
|
522
518
|
if (!this.isReplaying) {
|
|
523
519
|
this.onPainDetected(event.data, event.ts);
|
|
524
520
|
}
|
|
525
|
-
//
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
cb(event);
|
|
529
|
-
}
|
|
530
|
-
catch {
|
|
531
|
-
// Keep the evolution loop resilient — callback errors must not propagate
|
|
532
|
-
}
|
|
533
|
-
}
|
|
521
|
+
// _painCallbacks dispatch removed (PRI-451 Wave 1): no callback was ever
|
|
522
|
+
// registered via .on() (also removed). The pain_detected → PainSignalBridge
|
|
523
|
+
// bridge flows through emitPainDetectedEvent (hooks/pain.ts), not here.
|
|
534
524
|
return;
|
|
535
525
|
case 'candidate_created':
|
|
536
526
|
this.onCandidateCreated(event.data, event.ts);
|
package/dist/core/pain.d.ts
CHANGED
|
@@ -1,67 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Required fields — every pain flag MUST have these.
|
|
3
|
-
*/
|
|
4
|
-
export interface PainFlagData {
|
|
5
|
-
/** What triggered this pain signal (e.g., tool_failure, human_intervention, intercept_extraction) */
|
|
6
|
-
source: string;
|
|
7
|
-
/** Pain score 0-100 */
|
|
8
|
-
score: string;
|
|
9
|
-
/** ISO 8601 timestamp */
|
|
10
|
-
time: string;
|
|
11
|
-
/** Human-readable reason / error description */
|
|
12
|
-
reason: string;
|
|
13
|
-
/** Session ID — identifies which conversation this happened in */
|
|
14
|
-
session_id: string;
|
|
15
|
-
/** Agent ID — identifies which agent (main, builder, diagnostician, etc.) */
|
|
16
|
-
agent_id: string;
|
|
17
|
-
/** Whether this involves risky operation ('true' / 'false') */
|
|
18
|
-
is_risky: string;
|
|
19
|
-
/** Correlation trace ID (for linking events across the pipeline) */
|
|
20
|
-
trace_id: string;
|
|
21
|
-
/** Preview of the text that triggered this pain */
|
|
22
|
-
trigger_text_preview: string;
|
|
23
|
-
/** Trajectory pain_events row ID (set by recordPainEvent) */
|
|
24
|
-
pain_event_id?: string;
|
|
25
|
-
}
|
|
26
|
-
export interface PainFlagContractResult {
|
|
27
|
-
status: 'missing' | 'valid' | 'invalid';
|
|
28
|
-
format: 'missing' | 'empty' | 'kv' | 'json' | 'invalid_json';
|
|
29
|
-
data: Record<string, string>;
|
|
30
|
-
missingFields: string[];
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Builds legacy pain flag data for compatibility tests and historical readers.
|
|
34
|
-
* Do not use this to create new Runtime V2 diagnosis requests.
|
|
35
|
-
*/
|
|
36
|
-
export declare function buildPainFlag(input: {
|
|
37
|
-
source: string;
|
|
38
|
-
score: string;
|
|
39
|
-
time?: string;
|
|
40
|
-
reason: string;
|
|
41
|
-
session_id?: string;
|
|
42
|
-
agent_id?: string;
|
|
43
|
-
is_risky?: boolean;
|
|
44
|
-
trace_id?: string;
|
|
45
|
-
trigger_text_preview?: string;
|
|
46
|
-
pain_event_id?: string;
|
|
47
|
-
}): PainFlagData;
|
|
48
|
-
/**
|
|
49
|
-
* Validates a pain flag read from disk.
|
|
50
|
-
* Returns list of missing required fields — empty string means all present.
|
|
51
|
-
*/
|
|
52
|
-
export declare function validatePainFlag(data: Record<string, string>): string[];
|
|
53
1
|
export declare function computePainScore(rc: number, isSpiral: boolean, missingTestCommand: boolean, softScore: number, projectDir?: string): number;
|
|
54
2
|
export declare function painSeverityLabel(painScore: number, isSpiral?: boolean, projectDir?: string): string;
|
|
55
|
-
/**
|
|
56
|
-
* Reads and validates the legacy pain flag file.
|
|
57
|
-
*
|
|
58
|
-
* - If file doesn't exist → returns {}
|
|
59
|
-
* - If file is JSON format → converts to KV in memory only
|
|
60
|
-
* - If file is KV format → validates required fields, logs warning if missing
|
|
61
|
-
* - If file has unknown fields → silently ignores them (forward-compatible)
|
|
62
|
-
*/
|
|
63
|
-
export declare function readPainFlagData(projectDir: string): Record<string, string>;
|
|
64
|
-
export declare function readPainFlagContract(projectDir: string): PainFlagContractResult;
|
|
65
3
|
/**
|
|
66
4
|
* Track principle value metrics when a pain signal is written.
|
|
67
5
|
* This is observation-only — it does NOT affect the pain flag write flow.
|
package/dist/core/pain.js
CHANGED
|
@@ -1,44 +1,5 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import { parseKvLines } from '../utils/io.js';
|
|
3
1
|
import { resolvePdPath } from './paths.js';
|
|
4
2
|
import { ConfigService } from './config-service.js';
|
|
5
|
-
import { SystemLogger } from './system-logger.js';
|
|
6
|
-
/**
|
|
7
|
-
* Builds legacy pain flag data for compatibility tests and historical readers.
|
|
8
|
-
* Do not use this to create new Runtime V2 diagnosis requests.
|
|
9
|
-
*/
|
|
10
|
-
export function buildPainFlag(input) {
|
|
11
|
-
// Omit optional fields when not provided — prevents writing empty lines to disk
|
|
12
|
-
// which causes agent confusion (SKILL.md vs reality drift)
|
|
13
|
-
return {
|
|
14
|
-
source: input.source,
|
|
15
|
-
score: input.score,
|
|
16
|
-
time: input.time || new Date().toISOString(),
|
|
17
|
-
reason: input.reason,
|
|
18
|
-
session_id: input.session_id ?? '',
|
|
19
|
-
agent_id: input.agent_id ?? '',
|
|
20
|
-
is_risky: input.is_risky ? 'true' : 'false',
|
|
21
|
-
trace_id: input.trace_id ?? '',
|
|
22
|
-
trigger_text_preview: input.trigger_text_preview ?? '',
|
|
23
|
-
pain_event_id: input.pain_event_id,
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Validates a pain flag read from disk.
|
|
28
|
-
* Returns list of missing required fields — empty string means all present.
|
|
29
|
-
*/
|
|
30
|
-
export function validatePainFlag(data) {
|
|
31
|
-
const missing = [];
|
|
32
|
-
// Only source/score/time/reason are truly required — session_id/agent_id
|
|
33
|
-
// may be empty in automated contexts (heartbeat, background workers)
|
|
34
|
-
const required = ['source', 'score', 'time', 'reason'];
|
|
35
|
-
for (const field of required) {
|
|
36
|
-
if (!data[field] || data[field].trim() === '') {
|
|
37
|
-
missing.push(field);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return missing;
|
|
41
|
-
}
|
|
42
3
|
export function computePainScore(rc, isSpiral, missingTestCommand, softScore, projectDir) {
|
|
43
4
|
let score = Math.max(0, softScore || 0);
|
|
44
5
|
const stateDir = projectDir ? resolvePdPath(projectDir, 'STATE_DIR') : undefined;
|
|
@@ -83,110 +44,6 @@ export function painSeverityLabel(painScore, isSpiral = false, projectDir) {
|
|
|
83
44
|
return "info";
|
|
84
45
|
}
|
|
85
46
|
}
|
|
86
|
-
/**
|
|
87
|
-
* Converts a JSON pain flag object to KV format.
|
|
88
|
-
*/
|
|
89
|
-
function convertJsonToKv(json) {
|
|
90
|
-
const kvData = {};
|
|
91
|
-
const fieldMap = {
|
|
92
|
-
source: 'source',
|
|
93
|
-
score: 'score',
|
|
94
|
-
time: 'time',
|
|
95
|
-
timestamp: 'time',
|
|
96
|
-
reason: 'reason',
|
|
97
|
-
session_id: 'session_id',
|
|
98
|
-
sessionId: 'session_id',
|
|
99
|
-
agent_id: 'agent_id',
|
|
100
|
-
agentId: 'agent_id',
|
|
101
|
-
is_risky: 'is_risky',
|
|
102
|
-
isRisky: 'is_risky',
|
|
103
|
-
severity: 'severity',
|
|
104
|
-
painId: 'pain_id',
|
|
105
|
-
};
|
|
106
|
-
for (const [jsonKey, kvKey] of Object.entries(fieldMap)) {
|
|
107
|
-
if (json[jsonKey] !== undefined) {
|
|
108
|
-
kvData[kvKey] = String(json[jsonKey]);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
for (const [key, value] of Object.entries(json)) {
|
|
112
|
-
if (fieldMap[key] === undefined && value !== undefined && value !== null) {
|
|
113
|
-
kvData[key] = String(value);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
return kvData;
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Reads and validates the legacy pain flag file.
|
|
120
|
-
*
|
|
121
|
-
* - If file doesn't exist → returns {}
|
|
122
|
-
* - If file is JSON format → converts to KV in memory only
|
|
123
|
-
* - If file is KV format → validates required fields, logs warning if missing
|
|
124
|
-
* - If file has unknown fields → silently ignores them (forward-compatible)
|
|
125
|
-
*/
|
|
126
|
-
export function readPainFlagData(projectDir) {
|
|
127
|
-
const painFlagPath = resolvePdPath(projectDir, 'PAIN_FLAG');
|
|
128
|
-
try {
|
|
129
|
-
if (!fs.existsSync(painFlagPath)) {
|
|
130
|
-
return {};
|
|
131
|
-
}
|
|
132
|
-
const content = fs.readFileSync(painFlagPath, "utf-8").trim();
|
|
133
|
-
if (!content) {
|
|
134
|
-
return {};
|
|
135
|
-
}
|
|
136
|
-
// Detect JSON format. Legacy compatibility only: parse in memory and do not
|
|
137
|
-
// rewrite .pain_flag, because Runtime V2 must not create or repair this file.
|
|
138
|
-
if (content.startsWith('{')) {
|
|
139
|
-
let json;
|
|
140
|
-
try {
|
|
141
|
-
json = JSON.parse(content);
|
|
142
|
-
}
|
|
143
|
-
catch {
|
|
144
|
-
SystemLogger.log(projectDir, 'PAIN_FLAG_CORRUPT', 'Pain flag file contains invalid JSON');
|
|
145
|
-
return {};
|
|
146
|
-
}
|
|
147
|
-
const kvData = convertJsonToKv(json);
|
|
148
|
-
SystemLogger.log(projectDir, 'PAIN_FLAG_LEGACY_JSON_READ', `Read legacy JSON pain flag in memory (${Object.keys(json).length} fields)`);
|
|
149
|
-
return kvData;
|
|
150
|
-
}
|
|
151
|
-
// KV format — parse and validate
|
|
152
|
-
const data = parseKvLines(content);
|
|
153
|
-
const missing = validatePainFlag(data);
|
|
154
|
-
if (missing.length > 0) {
|
|
155
|
-
SystemLogger.log(projectDir, 'PAIN_FLAG_INCOMPLETE', `Pain flag missing required fields: ${missing.join(', ')}`);
|
|
156
|
-
}
|
|
157
|
-
return data;
|
|
158
|
-
}
|
|
159
|
-
catch (e) {
|
|
160
|
-
SystemLogger.log(projectDir, 'PAIN_FLAG_READ_ERROR', `Failed to read pain flag: ${String(e)}`);
|
|
161
|
-
return {};
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
export function readPainFlagContract(projectDir) {
|
|
165
|
-
const data = readPainFlagData(projectDir);
|
|
166
|
-
if (Object.keys(data).length === 0) {
|
|
167
|
-
const painFlagPath = resolvePdPath(projectDir, 'PAIN_FLAG');
|
|
168
|
-
if (!fs.existsSync(painFlagPath)) {
|
|
169
|
-
return { status: 'missing', format: 'missing', data: {}, missingFields: [] };
|
|
170
|
-
}
|
|
171
|
-
const raw = fs.readFileSync(painFlagPath, 'utf-8').trim();
|
|
172
|
-
if (!raw) {
|
|
173
|
-
return { status: 'missing', format: 'empty', data: {}, missingFields: [] };
|
|
174
|
-
}
|
|
175
|
-
return {
|
|
176
|
-
status: 'invalid',
|
|
177
|
-
format: raw.startsWith('{') ? 'invalid_json' : 'kv',
|
|
178
|
-
data: {},
|
|
179
|
-
missingFields: ['unparseable'],
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
const missing = validatePainFlag(data);
|
|
183
|
-
return {
|
|
184
|
-
status: missing.length > 0 ? 'invalid' : 'valid',
|
|
185
|
-
format: 'kv',
|
|
186
|
-
data,
|
|
187
|
-
missingFields: missing,
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
47
|
/**
|
|
191
48
|
* Track principle value metrics when a pain signal is written.
|
|
192
49
|
* This is observation-only — it does NOT affect the pain flag write flow.
|
package/dist/core/paths.d.ts
CHANGED
package/dist/core/paths.js
CHANGED
|
@@ -64,7 +64,8 @@ export const PD_FILES = {
|
|
|
64
64
|
CURRENT_FOCUS: posixJoin(PD_DIRS.OKR, 'CURRENT_FOCUS.md'),
|
|
65
65
|
WEEK_STATE: posixJoin(PD_DIRS.OKR, 'WEEK_STATE.json'),
|
|
66
66
|
THINKING_OS_CANDIDATES: posixJoin(PD_DIRS.MEMORY, 'THINKING_OS_CANDIDATES.md'),
|
|
67
|
-
SEMANTIC_PAIN
|
|
67
|
+
// SEMANTIC_PAIN (confusion_samples.md) removed (PRI-451 Wave 1): the file
|
|
68
|
+
// had zero readers. The lifecycle write that populated it was also removed.
|
|
68
69
|
EVOLUTION_STREAM: posixJoin(PD_DIRS.MEMORY, 'evolution.jsonl'),
|
|
69
70
|
EVOLUTION_LOCK: posixJoin(PD_DIRS.LOCKS, 'evolution'),
|
|
70
71
|
};
|
|
@@ -72,12 +72,9 @@ export const migration = {
|
|
|
72
72
|
)`);
|
|
73
73
|
db.exec(`CREATE INDEX IF NOT EXISTS idx_pain_events_session_id ON pain_events(session_id)`);
|
|
74
74
|
db.exec(`CREATE INDEX IF NOT EXISTS idx_pain_events_created_at ON pain_events(created_at)`);
|
|
75
|
-
// FTS5
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
pain_event_id UNINDEXED,
|
|
79
|
-
tokenize='porter unicode61'
|
|
80
|
-
)`);
|
|
75
|
+
// pain_events_fts FTS5 virtual table removed (PRI-451 Wave 1): the only
|
|
76
|
+
// reader (searchPainEvents) was dead code. Existing DBs keep the orphan
|
|
77
|
+
// table harmlessly (CREATE was IF NOT EXISTS); new DBs simply don't create it.
|
|
81
78
|
db.exec(`CREATE TABLE IF NOT EXISTS gate_blocks (
|
|
82
79
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
83
80
|
session_id TEXT,
|
|
@@ -370,15 +370,10 @@ export const SCHEMAS = {
|
|
|
370
370
|
FROM thinking_model_events GROUP BY date(created_at), model_id ORDER BY day ASC`,
|
|
371
371
|
},
|
|
372
372
|
},
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
pain_event_id UNINDEXED,
|
|
378
|
-
tokenize='porter unicode61'
|
|
379
|
-
)`,
|
|
380
|
-
},
|
|
381
|
-
},
|
|
373
|
+
// pain_events_fts FTS5 catalog entry removed (PRI-451 Wave 1): the only
|
|
374
|
+
// reader (searchPainEvents) was dead code. fts is now empty, matching the
|
|
375
|
+
// other DbType catalogs. Existing DBs keep the orphan table harmlessly.
|
|
376
|
+
fts: {},
|
|
382
377
|
},
|
|
383
378
|
// ========================================================================
|
|
384
379
|
// central.db — Aggregated multi-workspace data
|
|
@@ -16,22 +16,6 @@ export declare class TrajectoryDatabase {
|
|
|
16
16
|
recordUserTurn(input: TrajectoryUserTurnInput): number;
|
|
17
17
|
recordToolCall(input: TrajectoryToolCallInput): number;
|
|
18
18
|
recordPainEvent(input: TrajectoryPainEventInput): number;
|
|
19
|
-
/**
|
|
20
|
-
* Search pain_events using FTS5 full-text search (MEM-04).
|
|
21
|
-
* Returns pain events matching the query, ordered by relevance.
|
|
22
|
-
*/
|
|
23
|
-
searchPainEvents(query: string, limit?: number): {
|
|
24
|
-
id: number;
|
|
25
|
-
sessionId: string;
|
|
26
|
-
source: string;
|
|
27
|
-
score: number;
|
|
28
|
-
reason: string | null;
|
|
29
|
-
severity: string | null;
|
|
30
|
-
origin: string | null;
|
|
31
|
-
confidence: number | null;
|
|
32
|
-
text: string | null;
|
|
33
|
-
createdAt: string;
|
|
34
|
-
}[];
|
|
35
19
|
recordGateBlock(input: TrajectoryGateBlockInput): void;
|
|
36
20
|
recordTrustChange(input: TrajectoryTrustChangeInput): void;
|
|
37
21
|
recordPrincipleEvent(input: TrajectoryPrincipleEventInput): void;
|
package/dist/core/trajectory.js
CHANGED
|
@@ -170,60 +170,14 @@ export class TrajectoryDatabase {
|
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
172
|
});
|
|
173
|
-
//
|
|
174
|
-
//
|
|
175
|
-
|
|
176
|
-
try {
|
|
177
|
-
this.db.prepare(`
|
|
178
|
-
INSERT INTO pain_events_fts (text, pain_event_id) VALUES (?, ?)
|
|
179
|
-
`).run(input.text, insertedId);
|
|
180
|
-
}
|
|
181
|
-
catch (err) {
|
|
182
|
-
// Non-fatal: FTS index is for search convenience, not correctness.
|
|
183
|
-
// Log but do not re-throw — the pain event itself is already committed.
|
|
184
|
-
console.warn(`[trajectory] FTS index insert failed for pain_event ${insertedId}: ${String(err)}`);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
173
|
+
// FTS5 index write removed (PRI-451 Wave 1): the only reader (searchPainEvents)
|
|
174
|
+
// was dead code removed in Wave 1.1. The pain event row itself is committed
|
|
175
|
+
// above; nothing reads the FTS index anymore.
|
|
187
176
|
return insertedId;
|
|
188
177
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
*/
|
|
193
|
-
searchPainEvents(query, limit = 10) {
|
|
194
|
-
if (!query || query.trim().length === 0) {
|
|
195
|
-
return [];
|
|
196
|
-
}
|
|
197
|
-
// Escape FTS5 special characters and format query for porter tokenizer
|
|
198
|
-
const ftsQuery = query.trim().split(/\s+/).map(term => `"${term.replace(/"/g, '""')}"`).join(' ');
|
|
199
|
-
try {
|
|
200
|
-
const results = this.db.prepare(`
|
|
201
|
-
SELECT pe.*
|
|
202
|
-
FROM pain_events_fts pf
|
|
203
|
-
JOIN pain_events pe ON pe.id = pf.pain_event_id
|
|
204
|
-
WHERE pain_events_fts MATCH ?
|
|
205
|
-
ORDER BY bm25(pain_events_fts) DESC
|
|
206
|
-
LIMIT ?
|
|
207
|
-
`).all(ftsQuery, limit);
|
|
208
|
-
return results.map(row => ({
|
|
209
|
-
id: row.id,
|
|
210
|
-
sessionId: row.session_id,
|
|
211
|
-
source: row.source,
|
|
212
|
-
score: row.score,
|
|
213
|
-
reason: row.reason,
|
|
214
|
-
severity: row.severity,
|
|
215
|
-
origin: row.origin,
|
|
216
|
-
confidence: row.confidence,
|
|
217
|
-
text: row.text,
|
|
218
|
-
createdAt: row.created_at,
|
|
219
|
-
}));
|
|
220
|
-
}
|
|
221
|
-
catch (err) {
|
|
222
|
-
// If FTS5 query fails (e.g., syntax error), return empty results
|
|
223
|
-
console.warn(`[PD:TrajectoryDatabase] FTS5 search failed: ${String(err)}`);
|
|
224
|
-
return [];
|
|
225
|
-
}
|
|
226
|
-
}
|
|
178
|
+
// searchPainEvents removed (PRI-451 Wave 1): dead code. Its only caller was
|
|
179
|
+
// processDetectionQueue (also removed). The FTS5 index write it read is
|
|
180
|
+
// removed in Wave 1.2.
|
|
227
181
|
recordGateBlock(input) {
|
|
228
182
|
this.withWrite(() => {
|
|
229
183
|
this.db.prepare(`
|
|
@@ -1112,14 +1066,9 @@ export class TrajectoryDatabase {
|
|
|
1112
1066
|
ON pain_events(canonical_pain_id)
|
|
1113
1067
|
WHERE canonical_pain_id IS NOT NULL
|
|
1114
1068
|
`);
|
|
1115
|
-
//
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
text,
|
|
1119
|
-
pain_event_id UNINDEXED,
|
|
1120
|
-
tokenize='porter unicode61'
|
|
1121
|
-
)
|
|
1122
|
-
`);
|
|
1069
|
+
// pain_events_fts FTS5 virtual table creation removed (PRI-451 Wave 1):
|
|
1070
|
+
// the only reader (searchPainEvents) was dead code. Existing DBs keep the
|
|
1071
|
+
// orphan table harmlessly (CREATE was IF NOT EXISTS); new DBs skip it.
|
|
1123
1072
|
// V2 migration: Add V2 columns to evolution_tasks if they don't exist
|
|
1124
1073
|
// SQLite does not support IF NOT EXISTS for ADD COLUMN, so we must check manually
|
|
1125
1074
|
// before each ALTER to avoid "duplicate column name" errors on existing DBs
|
package/dist/hooks/lifecycle.js
CHANGED
|
@@ -120,12 +120,9 @@ export async function extractPainFromSessionFile(sessionFile, ctx) {
|
|
|
120
120
|
if (!fs.existsSync(dir))
|
|
121
121
|
fs.mkdirSync(dir, { recursive: true });
|
|
122
122
|
fs.appendFileSync(dailyLogPath, entry, 'utf8');
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
fs.mkdirSync(semanticDir, { recursive: true });
|
|
127
|
-
let semanticEntry = `\n### Sample ${timestamp}\n- Source: compaction\n\n\`\`\`\n${painPoints.join('\n---\n')}\n\`\`\`\n`;
|
|
128
|
-
fs.appendFileSync(semanticPath, semanticEntry, 'utf8');
|
|
123
|
+
// SEMANTIC_PAIN (confusion_samples.md) write removed (PRI-451 Wave 1):
|
|
124
|
+
// the file had zero readers. The MEMORY.md write above and the fatal-intercept
|
|
125
|
+
// emitPainDetectedEvent below are both LIVE and remain.
|
|
129
126
|
const hasFatal = painPoints.some(p => p.includes('[FATAL INTERCEPT]'));
|
|
130
127
|
if (hasFatal) {
|
|
131
128
|
// Emit via the Runtime v2 pain chain — no .pain_flag file written
|
package/dist/hooks/llm.js
CHANGED
|
@@ -194,14 +194,8 @@ export function handleLlmOutput(event, ctx) {
|
|
|
194
194
|
const detectionText = isEmpathyAuditPayload(text) ? '' : text;
|
|
195
195
|
const detectionService = DetectionService.get(wctx.stateDir);
|
|
196
196
|
const detection = detectionService.detect(detectionText);
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
ruleId: detection.ruleId || detection.source,
|
|
200
|
-
layer: detection.source === 'l1_exact' ? 'L1' : (detection.source === 'l2_cache' ? 'L2' : 'L3'),
|
|
201
|
-
severity: detection.severity || 0,
|
|
202
|
-
textPreview: detectionText.substring(0, 100)
|
|
203
|
-
});
|
|
204
|
-
}
|
|
197
|
+
// recordRuleMatch call removed (PRI-451 Wave 1): dead code — its only
|
|
198
|
+
// consumer was stats.pain.rulesMatched (dead counter, removed in Wave 1.5).
|
|
205
199
|
let painScore = detection.detected ? (detection.severity || 0) : 0;
|
|
206
200
|
let source = detection.detected
|
|
207
201
|
? (detection.ruleId ? `llm_${detection.ruleId.toLowerCase()}` : `llm_${detection.source}`)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/* global NodeJS */
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
// DetectionService + DictionaryService imports removed — their only consumer
|
|
5
|
+
// (processDetectionQueue) was dead code retired in PRI-451 Wave 1.
|
|
6
6
|
import { ensureStateTemplates, ensureCorePrinciples } from '../core/init.js';
|
|
7
7
|
import { SystemLogger } from '../core/system-logger.js';
|
|
8
8
|
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
@@ -351,62 +351,8 @@ async function processEvolutionQueue(wctx, logger, _eventLog, _api) {
|
|
|
351
351
|
}
|
|
352
352
|
}
|
|
353
353
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
try {
|
|
357
|
-
const funnel = DetectionService.get(wctx.stateDir);
|
|
358
|
-
const queue = funnel.flushQueue();
|
|
359
|
-
if (queue.length === 0)
|
|
360
|
-
return;
|
|
361
|
-
if (logger)
|
|
362
|
-
logger.info(`[PD:EvolutionWorker] Processing ${queue.length} items from detection funnel.`);
|
|
363
|
-
const dictionary = DictionaryService.get(wctx.stateDir);
|
|
364
|
-
for (const text of queue) {
|
|
365
|
-
const match = dictionary.match(text);
|
|
366
|
-
if (match) {
|
|
367
|
-
if (eventLog) {
|
|
368
|
-
eventLog.recordRuleMatch(undefined, {
|
|
369
|
-
ruleId: match.ruleId,
|
|
370
|
-
layer: 'L2',
|
|
371
|
-
severity: match.severity,
|
|
372
|
-
textPreview: text.substring(0, 100)
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
else {
|
|
377
|
-
// L3 semantic search via trajectory database FTS5 (MEM-04)
|
|
378
|
-
if (wctx.trajectory) {
|
|
379
|
-
const searchResults = wctx.trajectory.searchPainEvents(text, 5);
|
|
380
|
-
if (searchResults.length > 0) {
|
|
381
|
-
const topResult = searchResults[0];
|
|
382
|
-
if (!topResult)
|
|
383
|
-
continue;
|
|
384
|
-
// Found similar pain events - record as L3 semantic hit
|
|
385
|
-
if (eventLog) {
|
|
386
|
-
eventLog.recordRuleMatch(undefined, {
|
|
387
|
-
ruleId: 'l3_semantic',
|
|
388
|
-
layer: 'L3',
|
|
389
|
-
severity: topResult.score,
|
|
390
|
-
textPreview: text.substring(0, 100)
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
// Update detection funnel cache with L3 hit result
|
|
394
|
-
funnel.updateCache(text, { detected: true, severity: topResult.score });
|
|
395
|
-
// Don't track as candidate - this is a confirmed L3 hit
|
|
396
|
-
if (logger)
|
|
397
|
-
logger.info(`[PD:EvolutionWorker] L3 semantic hit: found ${searchResults.length} similar pain events for "${text.substring(0, 50)}..."`);
|
|
398
|
-
continue;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
// No L3 hit — pain candidate tracking removed (D-05)
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
catch (err) {
|
|
406
|
-
if (logger)
|
|
407
|
-
logger.warn(`[PD:EvolutionWorker] Detection queue failed: ${String(err)}`);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
354
|
+
// processDetectionQueue removed (PRI-451 Wave 1): dead code. Its only effects
|
|
355
|
+
// were recordRuleMatch (dead) and searchPainEvents (dead) — see PRI-451.
|
|
410
356
|
// PAIN_CANDIDATES system removed (D-05, D-06): trackPainCandidate and processPromotion deleted
|
|
411
357
|
// Evolution queue is now the single active pain→principle path
|
|
412
358
|
export async function registerEvolutionTaskSession(workspaceResolve, taskId, sessionKey, logger) {
|
|
@@ -544,9 +490,7 @@ export const EvolutionWorkerService = {
|
|
|
544
490
|
cycleResult.queue = queueResult.queue;
|
|
545
491
|
if (queueResult.errors)
|
|
546
492
|
cycleResult.errors.push(...queueResult.errors);
|
|
547
|
-
|
|
548
|
-
await processDetectionQueue(wctx, api, eventLog);
|
|
549
|
-
}
|
|
493
|
+
// processDetectionQueue removed (PRI-451 Wave 1) — was dead code.
|
|
550
494
|
// processPromotion removed (D-06) — promotion via PAIN_CANDIDATES no longer needed
|
|
551
495
|
// Correction Observer extracted to independent service (PRI-293) — no longer runs on EvolutionWorker heartbeat
|
|
552
496
|
try {
|
|
@@ -630,9 +574,7 @@ export const EvolutionWorkerService = {
|
|
|
630
574
|
if (queueResult.errors.length > 0) {
|
|
631
575
|
queueResult.errors.forEach((e) => logger?.error?.(`[PD:EvolutionWorker] Startup cycle error: ${e}`));
|
|
632
576
|
}
|
|
633
|
-
|
|
634
|
-
await processDetectionQueue(wctx, api, eventLog);
|
|
635
|
-
}
|
|
577
|
+
// processDetectionQueue removed (PRI-451 Wave 1) — was dead code.
|
|
636
578
|
// processPromotion removed (D-06)
|
|
637
579
|
timeoutId = setTimeout(runCycle, interval);
|
|
638
580
|
timeoutId.unref();
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import Database from 'better-sqlite3';
|
|
3
3
|
import * as path from 'path';
|
|
4
|
-
import { readPainFlagData } from '../core/pain.js';
|
|
5
4
|
import { listSessions } from '../core/session-tracker.js';
|
|
6
5
|
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
7
6
|
import { evaluatePhase3Inputs } from './phase3-input-filter.js';
|
|
@@ -130,7 +129,6 @@ export class RuntimeSummaryService {
|
|
|
130
129
|
const directive = this.readJsonFile(wctx.resolve('EVOLUTION_DIRECTIVE'), warnings, false);
|
|
131
130
|
const queueStats = this.buildQueueStats(queue);
|
|
132
131
|
const directiveSummary = this.buildDirectiveSummary(queue, directive, generatedAt, warnings);
|
|
133
|
-
const painFlag = readPainFlagData(workspaceDir);
|
|
134
132
|
const painCandidates = this.readJsonFile(wctx.resolve('PAIN_CANDIDATES'), warnings, false);
|
|
135
133
|
const phase3Inputs = evaluatePhase3Inputs(queue ?? []);
|
|
136
134
|
const lastPainSignal = this.findLastPainSignal(events, selectedSessionId);
|
|
@@ -239,8 +237,6 @@ export class RuntimeSummaryService {
|
|
|
239
237
|
eligibilitySource: 'runtime_truth',
|
|
240
238
|
},
|
|
241
239
|
pain: {
|
|
242
|
-
activeFlag: Object.keys(painFlag).length > 0,
|
|
243
|
-
activeFlagSource: painFlag.source || null,
|
|
244
240
|
candidates: painCandidates?.candidates && typeof painCandidates.candidates === 'object'
|
|
245
241
|
? Object.keys(painCandidates.candidates).length
|
|
246
242
|
: null,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type { EventType, EventCategory, EventLogEntry, ToolCallEventData, PainSignalEventData, RuleMatchEventData, RulePromotionEventData, HookExecutionEventData, GateBlockEventData, GateBypassEventData, PlanApprovalEventData, EvolutionTaskEventData, EmpathyRollbackEventData, HeartbeatDiagnosisEventData, DiagnosisTaskEventData, DiagnosticianReportEventData, PrincipleCandidateEventData, RuleEnforcedEventData, RuleHostEvaluatedEventData, RuleHostBlockedEventData, RuleHostRequireApprovalEventData, RuleHostAutoCorrectProposedEventData, RuleHostAutoCorrectAppliedEventData, RuntimeV2PromptActivationsInjectedEventData, RuleHostUnhealthyEventData, ToolCallStats, ErrorStats,
|
|
1
|
+
export type { EventType, EventCategory, EventLogEntry, ToolCallEventData, PainSignalEventData, RuleMatchEventData, RulePromotionEventData, HookExecutionEventData, GateBlockEventData, GateBypassEventData, PlanApprovalEventData, EvolutionTaskEventData, EmpathyRollbackEventData, HeartbeatDiagnosisEventData, DiagnosisTaskEventData, DiagnosticianReportEventData, PrincipleCandidateEventData, RuleEnforcedEventData, RuleHostEvaluatedEventData, RuleHostBlockedEventData, RuleHostRequireApprovalEventData, RuleHostAutoCorrectProposedEventData, RuleHostAutoCorrectAppliedEventData, RuntimeV2PromptActivationsInjectedEventData, RuleHostUnhealthyEventData, ToolCallStats, ErrorStats, EmpathyEventStats, GfiStats, EvolutionStats as EventEvolutionStats, HookStats, DailyStats, } from '@principles/core/runtime-v2';
|
|
2
2
|
export { createEmptyDailyStats, } from '@principles/core/runtime-v2';
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "principles-disciple",
|
|
3
3
|
"name": "Principles Disciple",
|
|
4
4
|
"description": "Evolutionary programming agent framework with strategic guardrails and reflection loops.",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.145.0",
|
|
6
6
|
"activation": {
|
|
7
7
|
"onCapabilities": [
|
|
8
8
|
"hook"
|
package/package.json
CHANGED
|
@@ -37,7 +37,6 @@ const STATE_DIR = path.join(WORKSPACE_DIR, '.state');
|
|
|
37
37
|
const QUEUE_PATH = path.join(STATE_DIR, 'EVOLUTION_QUEUE');
|
|
38
38
|
const LEDGER_PATH = path.join(STATE_DIR, 'principle_training_state.json');
|
|
39
39
|
const DB_PATH = path.join(STATE_DIR, 'subagent_workflows.db');
|
|
40
|
-
const PAIN_FLAG_PATH = path.join(STATE_DIR, '.pain_flag');
|
|
41
40
|
const SAMPLES_DIR = path.join(STATE_DIR, 'nocturnal', 'samples');
|
|
42
41
|
|
|
43
42
|
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
@@ -310,11 +309,6 @@ async function main() {
|
|
|
310
309
|
logStep('BASELINE', 'Capturing current state before validation');
|
|
311
310
|
const queueBefore = safeReadJson(QUEUE_PATH) as QueueItem[] | null;
|
|
312
311
|
logData('EVOLUTION_QUEUE (before)', queueBefore?.length ?? 0);
|
|
313
|
-
if (fs.existsSync(PAIN_FLAG_PATH)) {
|
|
314
|
-
logData('.pain_flag', 'EXISTS — ' + fs.readFileSync(PAIN_FLAG_PATH, 'utf8').slice(0, 100));
|
|
315
|
-
} else {
|
|
316
|
-
logData('.pain_flag', 'not present');
|
|
317
|
-
}
|
|
318
312
|
if (fs.existsSync(SAMPLES_DIR)) {
|
|
319
313
|
const samplesBefore = fs.readdirSync(SAMPLES_DIR).length;
|
|
320
314
|
logData('nocturnal/samples/', `${samplesBefore} files`);
|
|
@@ -432,18 +426,6 @@ async function main() {
|
|
|
432
426
|
}
|
|
433
427
|
}
|
|
434
428
|
|
|
435
|
-
// Check pain_flag cleanup
|
|
436
|
-
if (fs.existsSync(PAIN_FLAG_PATH)) {
|
|
437
|
-
const flagContent = fs.readFileSync(PAIN_FLAG_PATH, 'utf8');
|
|
438
|
-
if (flagContent.includes('[object Object]')) {
|
|
439
|
-
logStep('⚠️ WARNING', 'pain_flag is corrupted ([object Object])');
|
|
440
|
-
} else {
|
|
441
|
-
logData('.pain_flag (after)', `still exists, ${flagContent.length} bytes`);
|
|
442
|
-
}
|
|
443
|
-
} else {
|
|
444
|
-
logData('.pain_flag (after)', 'cleaned up (file removed)');
|
|
445
|
-
}
|
|
446
|
-
|
|
447
429
|
if (result.resolution === 'MISSING' || result.resolution === 'expired') {
|
|
448
430
|
console.error('FAIL: resolution not explicit');
|
|
449
431
|
process.exit(1);
|