principles-disciple 1.28.0 → 1.28.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/openclaw.plugin.json +4 -4
- package/package.json +4 -4
- package/scripts/validate-live-path.ts +18 -18
- package/src/commands/context.ts +1 -0
- package/src/commands/disable-impl.ts +2 -0
- package/src/commands/evolution-status.ts +2 -0
- package/src/commands/focus.ts +2 -0
- package/src/commands/nocturnal-train.ts +4 -6
- package/src/commands/pain.ts +9 -11
- package/src/commands/pd-reflect.ts +1 -1
- package/src/commands/principle-rollback.ts +1 -0
- package/src/commands/rollback-impl.ts +1 -0
- package/src/core/adaptive-thresholds.ts +1 -0
- package/src/core/bootstrap-rules.ts +3 -3
- package/src/core/dictionary.ts +1 -0
- package/src/core/empathy-keyword-matcher.ts +1 -0
- package/src/core/event-log.ts +2 -0
- package/src/core/evolution-engine.ts +1 -0
- package/src/core/external-training-contract.ts +1 -0
- package/src/core/focus-history.ts +3 -0
- package/src/core/init.ts +1 -0
- package/src/core/merge-gate-audit.ts +1 -1
- package/src/core/nocturnal-arbiter.ts +3 -0
- package/src/core/nocturnal-candidate-scoring.ts +131 -0
- package/src/core/nocturnal-compliance.ts +1 -0
- package/src/core/nocturnal-dataset.ts +1 -0
- package/src/core/nocturnal-executability.ts +1 -0
- package/src/core/nocturnal-reasoning-deriver.ts +338 -0
- package/src/core/nocturnal-rule-implementation-validator.ts +1 -0
- package/src/core/nocturnal-trinity.ts +457 -18
- package/src/core/pain-context-extractor.ts +2 -3
- package/src/core/pain.ts +1 -0
- package/src/core/pd-task-reconciler.ts +1 -0
- package/src/core/pd-task-service.ts +1 -0
- package/src/core/principle-internalization/deprecated-readiness.ts +1 -0
- package/src/core/principle-internalization/principle-lifecycle-service.ts +1 -0
- package/src/core/principle-tree-migration.ts +3 -4
- package/src/core/replay-engine.ts +4 -0
- package/src/core/risk-calculator.ts +1 -0
- package/src/core/rule-host.ts +2 -0
- package/src/core/session-tracker.ts +2 -0
- package/src/core/thinking-models.ts +1 -0
- package/src/core/thinking-os-parser.ts +3 -3
- package/src/core/trajectory.ts +4 -0
- package/src/hooks/bash-risk.ts +1 -1
- package/src/hooks/gfi-gate.ts +1 -1
- package/src/hooks/lifecycle-routing.ts +1 -0
- package/src/hooks/pain.ts +2 -1
- package/src/hooks/prompt.ts +37 -2
- package/src/hooks/subagent.ts +1 -1
- package/src/hooks/trajectory-collector.ts +1 -0
- package/src/http/principles-console-route.ts +2 -0
- package/src/index.ts +1 -1
- package/src/service/central-database.ts +2 -0
- package/src/service/central-sync-service.ts +1 -0
- package/src/service/control-ui-query-service.ts +2 -0
- package/src/service/event-log-auditor.ts +2 -0
- package/src/service/evolution-worker.ts +2 -1
- package/src/service/health-query-service.ts +20 -6
- package/src/service/nocturnal-runtime.ts +4 -0
- package/src/service/runtime-summary-service.ts +5 -0
- package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +1 -0
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +2 -1
- package/src/service/subagent-workflow/subagent-error-utils.ts +1 -0
- package/src/service/subagent-workflow/workflow-manager-base.ts +1 -0
- package/src/tools/critique-prompt.ts +1 -0
- package/src/utils/io.ts +1 -0
- package/tests/core/nocturnal-candidate-scoring.test.ts +132 -0
- package/tests/core/nocturnal-reasoning-deriver.test.ts +372 -0
- package/tests/core/nocturnal-trinity.test.ts +791 -0
|
@@ -203,6 +203,7 @@ function extractTurn(msg: ParsedMessage): string | null {
|
|
|
203
203
|
* SAFETY: Tail-only read, skip oversized lines, cap output.
|
|
204
204
|
* Returns empty string on any failure — caller should use pain reason as fallback.
|
|
205
205
|
*/
|
|
206
|
+
// eslint-disable-next-line complexity -- complexity 14, refactor candidate
|
|
206
207
|
export async function extractRecentConversation(
|
|
207
208
|
sessionId: string,
|
|
208
209
|
agentId = 'main',
|
|
@@ -235,11 +236,9 @@ export async function extractRecentConversation(
|
|
|
235
236
|
/**
|
|
236
237
|
* Extracts failed tool call context with argument correlation.
|
|
237
238
|
*/
|
|
238
|
-
// Reason: breaking API change - default param must precede required params for type inference compatibility
|
|
239
|
-
|
|
240
239
|
export async function extractFailedToolContext(
|
|
241
240
|
sessionId: string,
|
|
242
|
-
agentId
|
|
241
|
+
agentId: string,
|
|
243
242
|
toolName: string,
|
|
244
243
|
filePath?: string,
|
|
245
244
|
): Promise<string> {
|
package/src/core/pain.ts
CHANGED
|
@@ -279,6 +279,7 @@ export function readPainFlagContract(projectDir: string): PainFlagContractResult
|
|
|
279
279
|
* Errors are silently ignored to avoid disrupting the pain pipeline.
|
|
280
280
|
*/
|
|
281
281
|
|
|
282
|
+
// eslint-disable-next-line complexity -- complexity 12, refactor candidate
|
|
282
283
|
export function trackPrincipleValue(
|
|
283
284
|
workspaceDir: string,
|
|
284
285
|
painData: { reason?: string; source?: string; score?: string },
|
|
@@ -103,6 +103,7 @@ async function writeCronStore(store: CronStoreFile): Promise<void> {
|
|
|
103
103
|
});
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
// eslint-disable-next-line complexity -- complexity 12, refactor candidate
|
|
106
107
|
function diff(declared: PDTaskSpec[], actual: CronJob[]): DiffAction[] {
|
|
107
108
|
const actions: DiffAction[] = [];
|
|
108
109
|
const actualByName = new Map<string, CronJob>();
|
|
@@ -4,6 +4,7 @@ import { reconcilePDTasks } from './pd-task-reconciler.js';
|
|
|
4
4
|
export const PDTaskService: OpenClawPluginService = {
|
|
5
5
|
id: 'principles-disciple-task-manager',
|
|
6
6
|
|
|
7
|
+
// eslint-disable-next-line complexity -- complexity 14, refactor candidate
|
|
7
8
|
async start(ctx: OpenClawPluginServiceContext): Promise<void> {
|
|
8
9
|
const {workspaceDir} = ctx;
|
|
9
10
|
if (!workspaceDir) {
|
|
@@ -27,6 +27,7 @@ function clampScore(value: number): number {
|
|
|
27
27
|
return Math.max(0, Math.min(100, Number(value.toFixed(2))));
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
// eslint-disable-next-line complexity -- complexity 15, refactor candidate
|
|
30
31
|
export function assessDeprecatedReadiness(
|
|
31
32
|
principle: PrincipleLifecycleEvidence,
|
|
32
33
|
precomputedRuleMetrics?: Record<string, RuleMetricResult>,
|
|
@@ -38,6 +38,7 @@ export interface PrincipleLifecycleAssessment {
|
|
|
38
38
|
routeRecommendation: InternalizationRouteRecommendation;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// eslint-disable-next-line complexity -- complexity 15, refactor candidate
|
|
41
42
|
function createValueMetrics(
|
|
42
43
|
principle: PrincipleLifecycleEvidence,
|
|
43
44
|
adherence: PrincipleAdherenceResult,
|
|
@@ -9,8 +9,6 @@
|
|
|
9
9
|
* - Or run manually: node scripts/migrate-principle-tree.mjs <workspace-dir>
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import * as fs from 'fs';
|
|
13
|
-
import * as path from 'path';
|
|
14
12
|
import {
|
|
15
13
|
loadLedger,
|
|
16
14
|
saveLedger,
|
|
@@ -23,11 +21,11 @@ export interface PrincipleTreeMigrationResult {
|
|
|
23
21
|
migratedCount: number;
|
|
24
22
|
skippedCount: number;
|
|
25
23
|
errorCount: number;
|
|
26
|
-
details:
|
|
24
|
+
details: {
|
|
27
25
|
principleId: string;
|
|
28
26
|
status: 'migrated' | 'skipped' | 'error';
|
|
29
27
|
reason?: string;
|
|
30
|
-
}
|
|
28
|
+
}[];
|
|
31
29
|
}
|
|
32
30
|
|
|
33
31
|
/**
|
|
@@ -94,6 +92,7 @@ function mapInternalizationStatusToPrincipleStatus(
|
|
|
94
92
|
* This function is idempotent: it only migrates principles that don't exist
|
|
95
93
|
* in tree.principles yet.
|
|
96
94
|
*/
|
|
95
|
+
// eslint-disable-next-line complexity -- complexity 11, slightly over threshold
|
|
97
96
|
export function migratePrincipleTree(
|
|
98
97
|
stateDir: string,
|
|
99
98
|
workspaceDir?: string
|
|
@@ -236,6 +236,7 @@ export class ReplayEngine {
|
|
|
236
236
|
};
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
// eslint-disable-next-line complexity -- complexity 13, refactor candidate
|
|
239
240
|
private _buildRuleHostInput(sample: ReplaySample): RuleHostInput | null {
|
|
240
241
|
const snapshot = getNocturnalSessionSnapshot(
|
|
241
242
|
TrajectoryRegistry.get(this.workspaceDir),
|
|
@@ -294,6 +295,7 @@ export class ReplayEngine {
|
|
|
294
295
|
}
|
|
295
296
|
|
|
296
297
|
|
|
298
|
+
// eslint-disable-next-line complexity -- complexity 11, slightly over threshold
|
|
297
299
|
private _selectToolCall(
|
|
298
300
|
snapshot: NocturnalSessionSnapshot,
|
|
299
301
|
classification: SampleClassification,
|
|
@@ -380,7 +382,9 @@ export class ReplayEngine {
|
|
|
380
382
|
return toolCall.outcome === 'success' ? 'safe' : 'normal';
|
|
381
383
|
}
|
|
382
384
|
|
|
385
|
+
// eslint-disable-next-line complexity -- complexity 11, slightly over threshold
|
|
383
386
|
|
|
387
|
+
// eslint-disable-next-line complexity -- complexity 11
|
|
384
388
|
private _scoreEvaluation(
|
|
385
389
|
sample: ReplaySample,
|
|
386
390
|
result: RuleHostResult,
|
|
@@ -9,6 +9,7 @@ export interface FileModification {
|
|
|
9
9
|
params: Record<string, unknown>;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
// eslint-disable-next-line complexity -- complexity 12, refactor candidate
|
|
12
13
|
export function estimateLineChanges(modification: FileModification): number {
|
|
13
14
|
const { toolName, params } = modification;
|
|
14
15
|
|
package/src/core/rule-host.ts
CHANGED
|
@@ -59,6 +59,7 @@ export class RuleHost {
|
|
|
59
59
|
* - { decision: 'block', ... } when any implementation returns block (short-circuits)
|
|
60
60
|
* - { decision: 'requireApproval', ... } when any implementation returns requireApproval
|
|
61
61
|
*/
|
|
62
|
+
// eslint-disable-next-line complexity -- complexity 12, refactor candidate
|
|
62
63
|
evaluate(input: RuleHostInput): RuleHostResult | undefined {
|
|
63
64
|
try {
|
|
64
65
|
// Load active code implementations from the ledger
|
|
@@ -181,6 +182,7 @@ export class RuleHost {
|
|
|
181
182
|
* Uses the shared isolated runtime loader so candidate code does not execute
|
|
182
183
|
* in the host global realm.
|
|
183
184
|
*/
|
|
185
|
+
// eslint-disable-next-line complexity -- complexity 11, slightly over threshold
|
|
184
186
|
private _loadSingleImplementation(
|
|
185
187
|
impl: Implementation
|
|
186
188
|
): LoadedImplementation | null {
|
|
@@ -243,6 +243,7 @@ export function trackToolRead(sessionId: string, filePath: string, workspaceDir?
|
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
|
|
246
|
+
// eslint-disable-next-line complexity -- complexity 12, refactor candidate
|
|
246
247
|
export function trackLlmOutput(sessionId: string, usage: TokenUsage | undefined, config?: PainConfig, workspaceDir?: string, sessionKey?: string, trigger?: string): SessionState {
|
|
247
248
|
const state = getOrCreateSession(sessionId, workspaceDir, sessionKey, trigger);
|
|
248
249
|
state.llmTurns += 1;
|
|
@@ -282,6 +283,7 @@ export function trackLlmOutput(sessionId: string, usage: TokenUsage | undefined,
|
|
|
282
283
|
* Tracks physical friction based on tool execution failures.
|
|
283
284
|
*/
|
|
284
285
|
|
|
286
|
+
// eslint-disable-next-line complexity -- complexity 11, slightly over threshold
|
|
285
287
|
export function trackFriction(
|
|
286
288
|
sessionId: string,
|
|
287
289
|
deltaF: number,
|
|
@@ -202,6 +202,7 @@ let _cachedWorkspace: string | null = null;
|
|
|
202
202
|
*
|
|
203
203
|
* @param workspaceDir Optional. If provided, loads from that workspace's THINKING_OS.md.
|
|
204
204
|
*/
|
|
205
|
+
// eslint-disable-next-line complexity -- complexity 14, refactor candidate
|
|
205
206
|
export function listThinkingModels(workspaceDir?: string): ThinkingModelDefinition[] {
|
|
206
207
|
const cacheKey = workspaceDir ?? '__global__';
|
|
207
208
|
if (_cachedDefinitions && _cachedWorkspace === cacheKey) {
|
|
@@ -45,10 +45,10 @@ export function parseThinkingOsMd(content: string): ThinkingOsDirective[] {
|
|
|
45
45
|
// Match all <directive ...> ... </directive> blocks
|
|
46
46
|
const directiveRegex = /<directive\s+([^>]*)>([\s\S]*?)<\/directive>/gi;
|
|
47
47
|
|
|
48
|
-
let
|
|
48
|
+
let _match: RegExpExecArray | null = null;
|
|
49
49
|
|
|
50
|
-
while ((
|
|
51
|
-
const [, attrs, body] =
|
|
50
|
+
while ((_match = directiveRegex.exec(content)) !== null) {
|
|
51
|
+
const [, attrs, body] = _match;
|
|
52
52
|
|
|
53
53
|
const idMatch = /id="([^"]+)"/i.exec(attrs);
|
|
54
54
|
const nameMatch = /name="([^"]+)"/i.exec(attrs);
|
package/src/core/trajectory.ts
CHANGED
|
@@ -442,6 +442,7 @@ export class TrajectoryDatabase {
|
|
|
442
442
|
const now = nowIso();
|
|
443
443
|
// Cast to V2 to access new fields
|
|
444
444
|
const v2Updates = updates;
|
|
445
|
+
// eslint-disable-next-line complexity -- complexity 12, refactor candidate
|
|
445
446
|
this.withWrite(() => {
|
|
446
447
|
const setClauses: string[] = ['updated_at = ?'];
|
|
447
448
|
const values: unknown[] = [now];
|
|
@@ -554,6 +555,7 @@ export class TrajectoryDatabase {
|
|
|
554
555
|
LIMIT ? OFFSET ?
|
|
555
556
|
`).all(...values, limit, offset) as Record<string, unknown>[];
|
|
556
557
|
|
|
558
|
+
// eslint-disable-next-line complexity -- complexity 13, refactor candidate
|
|
557
559
|
return rows.map((row) => ({
|
|
558
560
|
id: Number(row.id),
|
|
559
561
|
taskId: String(row.task_id),
|
|
@@ -625,6 +627,7 @@ export class TrajectoryDatabase {
|
|
|
625
627
|
* Returns: Analytics data aggregated from trajectory database.
|
|
626
628
|
* Not: Runtime truth or real-time queue state.
|
|
627
629
|
*/
|
|
630
|
+
// eslint-disable-next-line complexity -- complexity 14, refactor candidate
|
|
628
631
|
getEvolutionTaskByTraceId(traceId: string): EvolutionTaskRecord | null {
|
|
629
632
|
const row = this.db.prepare(`
|
|
630
633
|
SELECT id, task_id, trace_id, source, reason, score, status,
|
|
@@ -775,6 +778,7 @@ export class TrajectoryDatabase {
|
|
|
775
778
|
WHERE session_id = ?
|
|
776
779
|
ORDER BY id ASC
|
|
777
780
|
`).all(sessionId) as Record<string, unknown>[];
|
|
781
|
+
// eslint-disable-next-line complexity -- complexity 12, refactor candidate
|
|
778
782
|
|
|
779
783
|
return rows.map((row) => {
|
|
780
784
|
// Extract filePath from params_json if present
|
package/src/hooks/bash-risk.ts
CHANGED
|
@@ -66,7 +66,7 @@ export function analyzeBashCommand(
|
|
|
66
66
|
// - Word joiner (U+2060)
|
|
67
67
|
// - Zero-width invisible separator (U+FEFF)
|
|
68
68
|
|
|
69
|
-
const ZERO_WIDTH_CHARS =
|
|
69
|
+
const ZERO_WIDTH_CHARS = /\u200B|\u200C|\u200D|\u2060|\uFEFF/g;
|
|
70
70
|
if (ZERO_WIDTH_CHARS.test(command)) {
|
|
71
71
|
logger?.warn?.(`[PD_GATE] Bash command contains zero-width characters — blocking as dangerous`);
|
|
72
72
|
return 'dangerous'; // Fail-closed: zero-width chars are suspicious
|
package/src/hooks/gfi-gate.ts
CHANGED
|
@@ -66,6 +66,7 @@ export type LifecycleIntent = 'promote' | 'disable' | 'rollback' | null;
|
|
|
66
66
|
* Detect implementation lifecycle intent from user message.
|
|
67
67
|
* Returns the detected intent type or null.
|
|
68
68
|
*/
|
|
69
|
+
// eslint-disable-next-line complexity -- complexity 13, refactor candidate
|
|
69
70
|
export function detectLifecycleIntent(message: string): LifecycleIntent {
|
|
70
71
|
// Check promote patterns
|
|
71
72
|
for (const p of PROMOTE_PATTERNS_EN) {
|
package/src/hooks/pain.ts
CHANGED
|
@@ -191,7 +191,7 @@ export function handleAfterToolCall(
|
|
|
191
191
|
// Only reduce tool_failure source GFI by 50%, preserve user_empathy and other sources
|
|
192
192
|
// This prevents "read file success" from wiping user frustration signals
|
|
193
193
|
const session = getSession(sessionId);
|
|
194
|
-
const toolFailureGfi = session?.gfiBySource?.
|
|
194
|
+
const toolFailureGfi = session?.gfiBySource?.tool_failure || 0;
|
|
195
195
|
|
|
196
196
|
let resetState: SessionState;
|
|
197
197
|
if (toolFailureGfi > 0) {
|
|
@@ -391,6 +391,7 @@ export function handleAfterToolCall(
|
|
|
391
391
|
});
|
|
392
392
|
}
|
|
393
393
|
|
|
394
|
+
// eslint-disable-next-line complexity -- complexity 15, refactor candidate
|
|
394
395
|
function extractErrorType(error: unknown): string {
|
|
395
396
|
if (!error) return 'Unknown';
|
|
396
397
|
const msg = String(error);
|
package/src/hooks/prompt.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { extractSummary, getHistoryVersions, parseWorkingMemorySection, workingM
|
|
|
10
10
|
import { EmpathyObserverWorkflowManager, empathyObserverWorkflowSpec, isExpectedSubagentError } from '../service/subagent-workflow/index.js';
|
|
11
11
|
import { PathResolver } from '../core/path-resolver.js';
|
|
12
12
|
import { isSubagentRuntimeAvailable } from '../utils/subagent-probe.js';
|
|
13
|
+
import { getPendingDiagnosticianTasks } from '../core/diagnostician-task-store.js';
|
|
13
14
|
import {
|
|
14
15
|
matchEmpathyKeywords,
|
|
15
16
|
loadKeywordStore,
|
|
@@ -201,6 +202,7 @@ export function loadContextInjectionConfig(workspaceDir: string): ContextInjecti
|
|
|
201
202
|
* Falls back to main model if no diagnostician model is configured
|
|
202
203
|
* @internal Helper for model configuration resolution
|
|
203
204
|
*/
|
|
205
|
+
// eslint-disable-next-line complexity -- complexity 12, refactor candidate
|
|
204
206
|
export function getDiagnosticianModel(api: PromptHookApi | null, logger?: PluginLogger): string {
|
|
205
207
|
// Determines logger: prefer api.logger, fallback to provided logger
|
|
206
208
|
// 1. getDiagnosticianModel(api) - uses api.logger
|
|
@@ -366,7 +368,7 @@ export async function handleBeforePromptBuild(
|
|
|
366
368
|
// prependContext: Only short dynamic directives: evolutionDirective + heartbeat
|
|
367
369
|
|
|
368
370
|
|
|
369
|
-
let prependSystemContext
|
|
371
|
+
let prependSystemContext: string;
|
|
370
372
|
let prependContext = '';
|
|
371
373
|
let appendSystemContext = '';
|
|
372
374
|
|
|
@@ -644,11 +646,44 @@ ACTION: Run self-audit. If stable, reply ONLY with "HEARTBEAT_OK".
|
|
|
644
646
|
logger?.error(`[PD:Prompt] Failed to read HEARTBEAT: ${String(e)}`);
|
|
645
647
|
}
|
|
646
648
|
}
|
|
649
|
+
|
|
650
|
+
// ──── 4b. Inject pending diagnostician tasks ────
|
|
651
|
+
// FIX (#283): The evolution worker writes pain diagnosis tasks to
|
|
652
|
+
// diagnostician_tasks.json. The heartbeat prompt hook must read and inject
|
|
653
|
+
// them so the LLM (acting as diagnostician) can process them.
|
|
654
|
+
try {
|
|
655
|
+
const pendingTasks = getPendingDiagnosticianTasks(wctx.stateDir);
|
|
656
|
+
if (pendingTasks.length > 0) {
|
|
657
|
+
const taskBlocks = pendingTasks
|
|
658
|
+
.slice(0, 3)
|
|
659
|
+
.map(({ id, task }) => `<diagnostician_task id="${id}">\n${task.prompt}\n</diagnostician_task>`)
|
|
660
|
+
.join('\n\n');
|
|
661
|
+
|
|
662
|
+
const pendingCount = pendingTasks.length;
|
|
663
|
+
const processingNote = pendingCount > 3
|
|
664
|
+
? `\n\nNOTE: ${pendingCount - 3} more tasks are queued. Process these 3 first; remaining tasks will be handled on subsequent heartbeats.`
|
|
665
|
+
: '';
|
|
666
|
+
|
|
667
|
+
prependContext += `<diagnostician_tasks pending="${pendingCount}">
|
|
668
|
+
You are acting as a **Pain Diagnostician**. Process the following task(s) by:
|
|
669
|
+
1. Analyzing the pain signal and its context
|
|
670
|
+
2. Identifying the root cause and violated principles
|
|
671
|
+
3. Writing a completion marker file: .evolution_complete_<TASK_ID>
|
|
672
|
+
4. Writing a diagnostic report: .diagnostician_report_<TASK_ID>.json
|
|
673
|
+
|
|
674
|
+
${taskBlocks}${processingNote}
|
|
675
|
+
</diagnostician_tasks>\n`;
|
|
676
|
+
|
|
677
|
+
logger?.info?.(`[PD:Prompt] Injected ${Math.min(pendingCount, 3)}/${pendingCount} pending diagnostician task(s) into heartbeat prompt`);
|
|
678
|
+
}
|
|
679
|
+
} catch (e) {
|
|
680
|
+
logger?.warn?.(`[PD:Prompt] Failed to read diagnostician tasks: ${String(e)}`);
|
|
681
|
+
}
|
|
647
682
|
}
|
|
648
683
|
|
|
649
684
|
// ──── 6. Dynamic Attitude Matrix (based on GFI) ────
|
|
650
685
|
|
|
651
|
-
let attitudeDirective
|
|
686
|
+
let attitudeDirective: string;
|
|
652
687
|
const currentGfi = session?.currentGfi || 0;
|
|
653
688
|
|
|
654
689
|
if (currentGfi >= 70) {
|
package/src/hooks/subagent.ts
CHANGED
|
@@ -212,6 +212,7 @@ export function handleLlmOutput(
|
|
|
212
212
|
* 消息写入前的处理
|
|
213
213
|
* 记录:用户/助手消息内容
|
|
214
214
|
*/
|
|
215
|
+
// eslint-disable-next-line complexity -- complexity 11, slightly over threshold
|
|
215
216
|
export function handleBeforeMessageWrite(
|
|
216
217
|
event: PluginHookBeforeMessageWriteEvent,
|
|
217
218
|
ctx: PluginHookAgentContext & { workspaceDir?: string }
|
|
@@ -578,6 +578,7 @@ export function createPrinciplesConsoleRoutes(api: OpenClawPluginApi): OpenClawP
|
|
|
578
578
|
path: ROUTE_PREFIX,
|
|
579
579
|
auth: 'plugin',
|
|
580
580
|
match: 'prefix',
|
|
581
|
+
// eslint-disable-next-line complexity -- complexity 13, refactor candidate
|
|
581
582
|
async handler(req, res) {
|
|
582
583
|
if (!api.rootDir) { text(res, 500, 'Plugin rootDir not available'); return true; }
|
|
583
584
|
const url = new URL(req.url || ROUTE_PREFIX, 'http://127.0.0.1');
|
|
@@ -640,6 +641,7 @@ export function createPrinciplesConsoleRoute(api: OpenClawPluginApi): OpenClawPl
|
|
|
640
641
|
path: ROUTE_PREFIX,
|
|
641
642
|
auth: 'plugin',
|
|
642
643
|
match: 'prefix',
|
|
644
|
+
// eslint-disable-next-line complexity -- complexity 15, refactor candidate
|
|
643
645
|
async handler(req, res) {
|
|
644
646
|
if (!api.rootDir) { text(res, 500, 'Plugin rootDir not available'); return true; }
|
|
645
647
|
const url = new URL(req.url || ROUTE_PREFIX, 'http://127.0.0.1');
|
package/src/index.ts
CHANGED
|
@@ -118,7 +118,7 @@ function computeRuntimeShadowTaskFingerprint(event: PluginHookSubagentSpawningEv
|
|
|
118
118
|
return crypto.createHash('sha256').update(JSON.stringify(payload)).digest('hex').slice(0, 16);
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
function
|
|
121
|
+
function _resolveCommandWorkspaceDirStrict(
|
|
122
122
|
api: OpenClawPluginApi,
|
|
123
123
|
ctx: WorkspaceResolutionContext,
|
|
124
124
|
): string {
|
|
@@ -200,6 +200,7 @@ export class CentralDatabase {
|
|
|
200
200
|
/**
|
|
201
201
|
* Sync data from a single workspace into the central database
|
|
202
202
|
*/
|
|
203
|
+
// eslint-disable-next-line complexity -- complexity 12, refactor candidate
|
|
203
204
|
syncWorkspace(workspaceName: string): number {
|
|
204
205
|
const workspace = this.workspaces.find(w => w.name === workspaceName);
|
|
205
206
|
if (!workspace) {
|
|
@@ -713,6 +714,7 @@ export class CentralDatabase {
|
|
|
713
714
|
syncEnabled: c.sync_enabled === 1,
|
|
714
715
|
}));
|
|
715
716
|
}
|
|
717
|
+
// eslint-disable-next-line complexity -- complexity 12, refactor candidate
|
|
716
718
|
|
|
717
719
|
updateWorkspaceConfig(
|
|
718
720
|
workspaceName: string,
|
|
@@ -18,6 +18,7 @@ let centralDb: CentralDatabase | null = null;
|
|
|
18
18
|
*/
|
|
19
19
|
const DEFAULT_SYNC_INTERVAL_MS = 5 * 60 * 1000;
|
|
20
20
|
|
|
21
|
+
// eslint-disable-next-line complexity -- complexity 12, refactor candidate
|
|
21
22
|
async function runSyncCycle(): Promise<void> {
|
|
22
23
|
if (!centralDb) {
|
|
23
24
|
logger?.warn?.('[PD:CentralSync] CentralDatabase not initialized, skipping sync');
|
|
@@ -255,6 +255,7 @@ export class ControlUiQueryService {
|
|
|
255
255
|
this.uiDb.dispose();
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
+
// eslint-disable-next-line complexity -- complexity 14, refactor candidate
|
|
258
259
|
getOverview(days = 30): OverviewResponse {
|
|
259
260
|
const stats = this.trajectory.getDataStats();
|
|
260
261
|
const regressionRows = this.uiDb.all<{
|
|
@@ -399,6 +400,7 @@ export class ControlUiQueryService {
|
|
|
399
400
|
};
|
|
400
401
|
}
|
|
401
402
|
|
|
403
|
+
// eslint-disable-next-line complexity -- complexity 13, refactor candidate
|
|
402
404
|
listSamples(filters: SampleListFilters = {}): SamplesResponse {
|
|
403
405
|
const page = Math.max(1, Number(filters.page ?? 1));
|
|
404
406
|
const pageSize = clampPageSize(filters.pageSize);
|
|
@@ -137,6 +137,7 @@ function countAllHooks(filePath: string): Record<string, number> {
|
|
|
137
137
|
* @param openclawDir - Base OpenClaw directory (e.g., ~/.openclaw)
|
|
138
138
|
* @param expectedToolHooks - Hook names that should appear in the primary workspace
|
|
139
139
|
*/
|
|
140
|
+
// eslint-disable-next-line complexity -- complexity 11, slightly over threshold
|
|
140
141
|
export async function auditEventLogs(
|
|
141
142
|
openclawDir: string,
|
|
142
143
|
expectedToolHooks: string[] = ['before_tool_call', 'after_tool_call'],
|
|
@@ -209,6 +210,7 @@ export async function auditEventLogs(
|
|
|
209
210
|
/**
|
|
210
211
|
* Format audit report for display.
|
|
211
212
|
*/
|
|
213
|
+
// eslint-disable-next-line complexity -- complexity 13, refactor candidate
|
|
212
214
|
export function formatAuditReport(report: AuditReport): string {
|
|
213
215
|
const lines: string[] = [];
|
|
214
216
|
|
|
@@ -382,7 +382,7 @@ function buildFallbackNocturnalSnapshot(
|
|
|
382
382
|
// #246-fix: Use minToolCalls=0 to avoid filtering out sessions with 0 tool calls.
|
|
383
383
|
// The pain-triggering session may have no tool calls but still be worth tracking.
|
|
384
384
|
const summaries = extractor.listRecentNocturnalCandidateSessions({ limit: 300, minToolCalls: 0 });
|
|
385
|
-
const match = summaries.find(s => s.sessionId === painContext.mostRecent
|
|
385
|
+
const match = summaries.find(s => s.sessionId === painContext.mostRecent?.sessionId);
|
|
386
386
|
if (match) {
|
|
387
387
|
realStats = {
|
|
388
388
|
totalAssistantTurns: match.assistantTurnCount,
|
|
@@ -1873,6 +1873,7 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
|
|
|
1873
1873
|
}
|
|
1874
1874
|
}
|
|
1875
1875
|
|
|
1876
|
+
// eslint-disable-next-line complexity -- complexity 12, refactor candidate
|
|
1876
1877
|
async function processDetectionQueue(wctx: WorkspaceContext, api: OpenClawPluginApi, eventLog: EventLog) {
|
|
1877
1878
|
const {logger} = api;
|
|
1878
1879
|
try {
|
|
@@ -337,6 +337,7 @@ export class HealthQueryService {
|
|
|
337
337
|
}));
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
+
// eslint-disable-next-line complexity -- complexity 13, refactor candidate
|
|
340
341
|
getGateStats(): {
|
|
341
342
|
today: {
|
|
342
343
|
gfiBlocks: number;
|
|
@@ -553,8 +554,8 @@ export class HealthQueryService {
|
|
|
553
554
|
const streamPath = resolvePdPath(this.workspaceDir, 'EVOLUTION_STREAM');
|
|
554
555
|
if (!fs.existsSync(streamPath)) return [];
|
|
555
556
|
|
|
556
|
-
|
|
557
|
-
let lines: string[]
|
|
557
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
558
|
+
let lines: string[];
|
|
558
559
|
try {
|
|
559
560
|
const raw = fs.readFileSync(streamPath, 'utf8').trim();
|
|
560
561
|
if (!raw) return [];
|
|
@@ -565,8 +566,9 @@ export class HealthQueryService {
|
|
|
565
566
|
|
|
566
567
|
const records: RecentPrincipleChange[] = [];
|
|
567
568
|
for (const line of lines) {
|
|
568
|
-
|
|
569
|
-
|
|
569
|
+
|
|
570
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations
|
|
571
|
+
let event: EvolutionStreamRecord | null;
|
|
570
572
|
try {
|
|
571
573
|
event = JSON.parse(line) as EvolutionStreamRecord;
|
|
572
574
|
} catch {
|
|
@@ -657,6 +659,7 @@ export class HealthQueryService {
|
|
|
657
659
|
return null;
|
|
658
660
|
}
|
|
659
661
|
|
|
662
|
+
// eslint-disable-next-line complexity -- complexity 14, refactor candidate
|
|
660
663
|
private readNocturnalTraining(): {
|
|
661
664
|
queue: { pending: number; inProgress: number; completed: number };
|
|
662
665
|
trinityRecords: { artifactId: string; status: string; createdAt: string }[];
|
|
@@ -784,6 +787,8 @@ export class HealthQueryService {
|
|
|
784
787
|
}
|
|
785
788
|
|
|
786
789
|
|
|
790
|
+
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
791
|
+
// eslint-disable-next-line complexity -- complexity 15, refactor candidate
|
|
787
792
|
private getEventDedupKey(entry: EventLogEntry): string {
|
|
788
793
|
const eventId = typeof entry.data?.eventId === 'string' ? entry.data.eventId : null;
|
|
789
794
|
if (eventId) {
|
|
@@ -854,6 +859,8 @@ export class HealthQueryService {
|
|
|
854
859
|
}
|
|
855
860
|
|
|
856
861
|
|
|
862
|
+
// eslint-disable-next-line complexity -- complexity 13, refactor candidate
|
|
863
|
+
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
857
864
|
private resolveGateType(row: GateBlockRow): string {
|
|
858
865
|
if (typeof row.gate_type === 'string' && row.gate_type.trim().length > 0) {
|
|
859
866
|
return row.gate_type;
|
|
@@ -878,6 +885,7 @@ export class HealthQueryService {
|
|
|
878
885
|
}
|
|
879
886
|
|
|
880
887
|
|
|
888
|
+
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
881
889
|
private scoreToStatus(score: number): string {
|
|
882
890
|
if (score >= 70) return 'healthy';
|
|
883
891
|
if (score >= 40) return 'warning';
|
|
@@ -885,6 +893,7 @@ export class HealthQueryService {
|
|
|
885
893
|
}
|
|
886
894
|
|
|
887
895
|
|
|
896
|
+
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
888
897
|
private evolutionToStatus(tier: string, points: number): string {
|
|
889
898
|
const lower = tier.toLowerCase();
|
|
890
899
|
if (lower === 'forest' || lower === 'tree') return 'healthy';
|
|
@@ -893,6 +902,7 @@ export class HealthQueryService {
|
|
|
893
902
|
}
|
|
894
903
|
|
|
895
904
|
|
|
905
|
+
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
896
906
|
private safeListFiles(dirPath: string, predicate: (_name: string) => boolean): string[] {
|
|
897
907
|
if (!fs.existsSync(dirPath)) return [];
|
|
898
908
|
try {
|
|
@@ -905,6 +915,7 @@ export class HealthQueryService {
|
|
|
905
915
|
}
|
|
906
916
|
|
|
907
917
|
|
|
918
|
+
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
908
919
|
private readJsonFile<T>(filePath: string, fallback: T): T {
|
|
909
920
|
if (!fs.existsSync(filePath)) return fallback;
|
|
910
921
|
try {
|
|
@@ -915,11 +926,13 @@ export class HealthQueryService {
|
|
|
915
926
|
}
|
|
916
927
|
|
|
917
928
|
|
|
929
|
+
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
918
930
|
private asNumber(value: unknown, fallback: number): number {
|
|
919
931
|
return Number.isFinite(value) ? Number(value) : fallback;
|
|
920
932
|
}
|
|
921
933
|
|
|
922
934
|
|
|
935
|
+
// eslint-disable-next-line @typescript-eslint/class-methods-use-this
|
|
923
936
|
private asNullableNumber(value: unknown): number | null {
|
|
924
937
|
if (Number.isFinite(value)) return Number(value);
|
|
925
938
|
if (typeof value === 'string' && value.trim().length > 0) {
|
|
@@ -954,7 +967,7 @@ export class HealthQueryService {
|
|
|
954
967
|
dailyGfiPeak,
|
|
955
968
|
today,
|
|
956
969
|
);
|
|
957
|
-
} catch
|
|
970
|
+
} catch {
|
|
958
971
|
// Non-critical: GFI sync failure should not block queries
|
|
959
972
|
}
|
|
960
973
|
}
|
|
@@ -981,6 +994,7 @@ export class HealthQueryService {
|
|
|
981
994
|
* Read the most recent session JSON file from disk.
|
|
982
995
|
* Used to sync GFI from session-tracker's persistence into SQLite.
|
|
983
996
|
*/
|
|
997
|
+
// eslint-disable-next-line complexity -- complexity 11, slightly over threshold
|
|
984
998
|
private readLatestSessionFromFile(): SessionState | null {
|
|
985
999
|
const sessionsDir = path.join(this.stateDir, 'sessions');
|
|
986
1000
|
if (!fs.existsSync(sessionsDir)) {
|
|
@@ -1015,7 +1029,7 @@ export class HealthQueryService {
|
|
|
1015
1029
|
}
|
|
1016
1030
|
|
|
1017
1031
|
return latest;
|
|
1018
|
-
} catch
|
|
1032
|
+
} catch {
|
|
1019
1033
|
// Non-critical: failure to read session files should not crash the service
|
|
1020
1034
|
return null;
|
|
1021
1035
|
}
|
|
@@ -40,6 +40,7 @@ import { withLockAsync } from '../utils/file-lock.js';
|
|
|
40
40
|
* Excluded (NOT system sessions):
|
|
41
41
|
* - User sessions like agent:main:feishu:user:xxx — third component is channel type
|
|
42
42
|
*/
|
|
43
|
+
// eslint-disable-next-line complexity -- complexity 15, refactor candidate
|
|
43
44
|
function isSystemSession(state: SessionState): boolean {
|
|
44
45
|
const { sessionId, sessionKey, trigger } = state;
|
|
45
46
|
|
|
@@ -269,6 +270,7 @@ async function writeState(stateDir: string, state: NocturnalRuntimeState): Promi
|
|
|
269
270
|
* @param trajectoryLastActivityAt - Optional trajectory timestamp as secondary guardrail
|
|
270
271
|
* @returns IdleCheckResult with full diagnostic information
|
|
271
272
|
*/
|
|
273
|
+
// eslint-disable-next-line complexity -- complexity 14, refactor candidate
|
|
272
274
|
export function checkWorkspaceIdle(
|
|
273
275
|
workspaceDir: string,
|
|
274
276
|
options: {
|
|
@@ -355,6 +357,7 @@ export function checkWorkspaceIdle(
|
|
|
355
357
|
* @param options - Cooldown configuration options
|
|
356
358
|
* @returns CooldownCheckResult
|
|
357
359
|
*/
|
|
360
|
+
// eslint-disable-next-line complexity -- complexity 11, slightly over threshold
|
|
358
361
|
export function checkCooldown(
|
|
359
362
|
stateDir: string,
|
|
360
363
|
principleId?: string,
|
|
@@ -555,6 +558,7 @@ export interface PreflightCheckResult {
|
|
|
555
558
|
* @param idleCheckOverride - Optional override for idle check result (for testing)
|
|
556
559
|
*/
|
|
557
560
|
|
|
561
|
+
// eslint-disable-next-line complexity -- complexity 12, refactor candidate
|
|
558
562
|
export function checkPreflight(
|
|
559
563
|
workspaceDir: string,
|
|
560
564
|
stateDir: string,
|
|
@@ -362,6 +362,7 @@ export class RuntimeSummaryService {
|
|
|
362
362
|
return { session: sessions[0], reason: 'latest_active' };
|
|
363
363
|
}
|
|
364
364
|
|
|
365
|
+
// eslint-disable-next-line complexity -- complexity 11, slightly over threshold
|
|
365
366
|
private static mergeSessionSnapshots(
|
|
366
367
|
persistedSessions: PersistedSessionState[],
|
|
367
368
|
workspaceDir: string
|
|
@@ -510,7 +511,9 @@ export class RuntimeSummaryService {
|
|
|
510
511
|
})
|
|
511
512
|
.slice(-MAX_SOURCE_EVENTS)
|
|
512
513
|
.reverse();
|
|
514
|
+
// eslint-disable-next-line complexity -- complexity 11, slightly over threshold
|
|
513
515
|
|
|
516
|
+
// eslint-disable-next-line complexity -- complexity 11
|
|
514
517
|
return filtered.map((entry) => {
|
|
515
518
|
if (entry.type === 'pain_signal') {
|
|
516
519
|
return {
|
|
@@ -581,6 +584,7 @@ export class RuntimeSummaryService {
|
|
|
581
584
|
return [...merged.values()].sort((a, b) => (a.ts || '').localeCompare(b.ts || ''));
|
|
582
585
|
}
|
|
583
586
|
|
|
587
|
+
// eslint-disable-next-line complexity -- complexity 15, refactor candidate
|
|
584
588
|
private static getEventDedupKey(entry: EventLogEntry): string {
|
|
585
589
|
const eventId = typeof entry.data?.eventId === 'string' ? entry.data.eventId : null;
|
|
586
590
|
if (eventId) {
|
|
@@ -637,6 +641,7 @@ export class RuntimeSummaryService {
|
|
|
637
641
|
return candidate ? new Date(candidate).getTime() : NaN;
|
|
638
642
|
}
|
|
639
643
|
|
|
644
|
+
// eslint-disable-next-line complexity -- complexity 14, refactor candidate
|
|
640
645
|
private static buildDirectiveTaskPreview(item: QueueItem): string {
|
|
641
646
|
const task = typeof item.task === 'string' ? item.task.trim() : '';
|
|
642
647
|
if (task && task.toLowerCase() !== 'undefined') {
|