principles-disciple 1.30.0 → 1.31.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.
@@ -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.30.0",
5
+ "version": "1.31.0",
6
6
  "skills": [
7
7
  "./skills"
8
8
  ],
@@ -76,8 +76,8 @@
76
76
  }
77
77
  },
78
78
  "buildFingerprint": {
79
- "gitSha": "25bfc2bb209f",
80
- "bundleMd5": "b735aa483374dd2c7071295b11161676",
81
- "builtAt": "2026-04-13T14:25:24.799Z"
79
+ "gitSha": "15c19a4dc3f2",
80
+ "bundleMd5": "684e47fd5c521d722150a93813fddd02",
81
+ "builtAt": "2026-04-14T03:01:11.396Z"
82
82
  }
83
83
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "principles-disciple",
3
- "version": "1.30.0",
3
+ "version": "1.31.0",
4
4
  "description": "Native OpenClaw plugin for Principles Disciple",
5
5
  "type": "module",
6
6
  "main": "./dist/bundle.js",
@@ -2166,8 +2166,8 @@ export const EvolutionWorkerService: ExtendedEvolutionWorkerService = {
2166
2166
  shouldTrySleepReflection = true;
2167
2167
  }
2168
2168
 
2169
- // Path 2: Periodic trigger (bypasses idle requirement for debugging)
2170
- if (!idleResult.isIdle && sleepConfig.trigger_mode === 'periodic') {
2169
+ // Path 2: Periodic trigger (fires regardless of idle state)
2170
+ if (sleepConfig.trigger_mode === 'periodic') {
2171
2171
  if (heartbeatCounter >= sleepConfig.period_heartbeats) {
2172
2172
  logger?.info?.(`[PD:EvolutionWorker] Periodic trigger: heartbeatCounter=${heartbeatCounter} >= period_heartbeats=${sleepConfig.period_heartbeats}`);
2173
2173
  shouldTrySleepReflection = true;
@@ -2179,6 +2179,7 @@ export const EvolutionWorkerService: ExtendedEvolutionWorkerService = {
2179
2179
 
2180
2180
  if (shouldTrySleepReflection) {
2181
2181
  const cooldown = checkCooldown(wctx.stateDir, undefined, {
2182
+ globalCooldownMs: sleepConfig.cooldown_ms,
2182
2183
  maxRunsPerWindow: sleepConfig.max_runs_per_day,
2183
2184
  quotaWindowMs: 24 * 60 * 60 * 1000,
2184
2185
  });
@@ -436,10 +436,12 @@ export function checkCooldown(
436
436
  *
437
437
  * @param stateDir - State directory
438
438
  * @param principleId - Target principle ID for this run
439
+ * @param cooldownMs - Global cooldown duration in ms (default: 1 hour)
439
440
  */
440
441
  export async function recordRunStart(
441
442
  stateDir: string,
442
- principleId: string
443
+ principleId: string,
444
+ cooldownMs: number = DEFAULT_GLOBAL_COOLDOWN_MS
443
445
  ): Promise<void> {
444
446
  const state = await readState(stateDir);
445
447
  const now = new Date().toISOString();
@@ -450,8 +452,8 @@ export async function recordRunStart(
450
452
  status: 'skipped', // Will be updated on completion
451
453
  };
452
454
 
453
- // Set global cooldown
454
- const cooldownUntil = new Date(Date.now() + DEFAULT_GLOBAL_COOLDOWN_MS).toISOString();
455
+ // Set global cooldown (use configured value, not hardcoded default)
456
+ const cooldownUntil = new Date(Date.now() + cooldownMs).toISOString();
455
457
  state.globalCooldownUntil = cooldownUntil;
456
458
 
457
459
  // Add to recent runs for quota tracking
@@ -96,6 +96,7 @@ import {
96
96
  type IdleCheckResult,
97
97
  type PreflightCheckResult,
98
98
  } from './nocturnal-runtime.js';
99
+ import { loadNocturnalConfig } from './nocturnal-config.js';
99
100
  import { NocturnalPathResolver } from '../core/nocturnal-paths.js';
100
101
  import { registerSample } from '../core/nocturnal-dataset.js';
101
102
  import { getPrincipleState, setPrincipleState } from '../core/principle-training-state.js';
@@ -721,7 +722,8 @@ export function executeNocturnalReflection(
721
722
  if (!preflight.canRun) {
722
723
  return {
723
724
  success: false,
724
- noTargetSelected: false,
725
+ noTargetSelected: true,
726
+ skipReason: 'preflight_blocked',
725
727
  validationFailed: false,
726
728
  validationFailures: [],
727
729
  diagnostics,
@@ -784,7 +786,8 @@ export function executeNocturnalReflection(
784
786
  // -------------------------------------------------------------------------
785
787
  // Note: We use a sync approximation here since this is called from sync context
786
788
  // The async version would be used in real worker integration
787
- void recordRunStart(stateDir, selectedPrincipleId).catch((err) => {
789
+ const config = loadNocturnalConfig(stateDir);
790
+ void recordRunStart(stateDir, selectedPrincipleId, config.cooldown_ms).catch((err) => {
788
791
  console.warn(`[nocturnal-service] Failed to record run start: ${String(err)}`);
789
792
  });
790
793
 
@@ -1195,7 +1198,14 @@ async function executeNocturnalReflectionWithAdapter(
1195
1198
  diagnostics.preflight = preflight;
1196
1199
 
1197
1200
  if (!preflight.canRun) {
1198
- return { success: false, noTargetSelected: false, validationFailed: false, validationFailures: [], diagnostics };
1201
+ return {
1202
+ success: false,
1203
+ noTargetSelected: true,
1204
+ skipReason: 'preflight_blocked',
1205
+ validationFailed: false,
1206
+ validationFailures: [],
1207
+ diagnostics
1208
+ };
1199
1209
  }
1200
1210
 
1201
1211
  // Step 2: Target selection (or use override to skip)
@@ -1317,7 +1327,8 @@ async function executeNocturnalReflectionWithAdapter(
1317
1327
  }
1318
1328
 
1319
1329
  // Step 3: Record run start
1320
- void recordRunStart(stateDir, selectedPrincipleId).catch((err) => {
1330
+ const config = loadNocturnalConfig(stateDir);
1331
+ void recordRunStart(stateDir, selectedPrincipleId, config.cooldown_ms).catch((err) => {
1321
1332
  console.warn(`[nocturnal-service] Failed to record run start: ${String(err)}`);
1322
1333
  });
1323
1334
 
@@ -63,7 +63,8 @@ export type SkipReason =
63
63
  | 'workspace_not_idle' // Workspace is active, nocturnal not allowed
64
64
  | 'quota_exhausted' // Max runs per quota window reached
65
65
  | 'insufficient_snapshot_data' // Sessions exist but lack tool calls / events
66
- | 'global_cooldown_active'; // Global cooldown is in effect
66
+ | 'global_cooldown_active' // Global cooldown is in effect
67
+ | 'preflight_blocked'; // Preflight check blocked (idle/cooldown/quota)
67
68
 
68
69
  export interface SelectionDiagnostics {
69
70
  /** Total evaluable principles found */
@@ -262,8 +262,10 @@ export class NocturnalWorkflowManager implements WorkflowManager {
262
262
  painContext,
263
263
  // #244: Only skip preflight idle gate for manual/test triggers.
264
264
  // Automatic triggers must go through normal idle check.
265
+ // #292: Periodic triggers (source='nocturnal') also bypass idle check for debugging
265
266
  ...(((options.metadata)?.triggerSource === 'manual' ||
266
- (options.metadata)?.triggerSource === 'test')
267
+ (options.metadata)?.triggerSource === 'test' ||
268
+ (options.metadata)?.triggerSource === 'nocturnal')
267
269
  ? {
268
270
  idleCheckOverride: {
269
271
  isIdle: true,
@@ -272,7 +274,7 @@ export class NocturnalWorkflowManager implements WorkflowManager {
272
274
  userActiveSessions: 0,
273
275
  abandonedSessionIds: [],
274
276
  trajectoryGuardrailConfirmsIdle: true,
275
- reason: 'manual/test override',
277
+ reason: `${(options.metadata)?.triggerSource ?? 'manual'}.test override`,
276
278
  },
277
279
  }
278
280
  : {}),
@@ -293,7 +295,28 @@ export class NocturnalWorkflowManager implements WorkflowManager {
293
295
  this.completedWorkflows.set(workflowId, Date.now());
294
296
  } else {
295
297
  const reason = result.noTargetSelected ? 'no_target_selected' : 'validation_failed';
296
- this.logger.warn(`[PD:NocturnalWorkflow] [${workflowId}] Pipeline failed: reason=${reason}, noTargetSelected=${result.noTargetSelected}, skipReason=${result.skipReason ?? 'none'}, validationFailures=${result.validationFailures?.length ?? 0}`);
298
+ const failuresSummary = result.validationFailures?.length > 0
299
+ ? result.validationFailures.join('; ')
300
+ : (result.skipReason ?? 'none');
301
+ this.logger.warn(`[PD:NocturnalWorkflow] [${workflowId}] Pipeline failed: reason=${reason}, noTargetSelected=${result.noTargetSelected}, skipReason=${result.skipReason ?? 'none'}, validationFailures=${result.validationFailures?.length ?? 0}, details=${failuresSummary}`);
302
+
303
+ // Log full result structure for debugging
304
+ this.logger.warn(`[PD:NocturnalWorkflow] [${workflowId}] Full result: success=${result.success}, validationFailed=${result.validationFailed}, noTargetSelected=${result.noTargetSelected}`);
305
+
306
+ // Log diagnostics for debugging
307
+ if (result.diagnostics?.trinityResult) {
308
+ this.logger.warn(`[PD:NocturnalWorkflow] [${workflowId}] Trinity result: success=${result.diagnostics.trinityResult.success}, chainMode=${result.diagnostics.chainModeUsed ?? 'unknown'}`);
309
+ if (!result.diagnostics.trinityResult.success) {
310
+ this.logger.warn(`[PD:NocturnalWorkflow] [${workflowId}] Trinity failures: ${result.diagnostics.trinityResult.failures.map(f => `${f.stage}: ${f.reason}`).join('; ')}`);
311
+ }
312
+ }
313
+ if (result.diagnostics?.arbiterResult) {
314
+ this.logger.warn(`[PD:NocturnalWorkflow] [${workflowId}] Arbiter result: passed=${result.diagnostics.arbiterResult.passed}, failures=${result.diagnostics.arbiterResult.failures.map(f => f.reason).join('; ')}`);
315
+ }
316
+ if (result.diagnostics?.selection) {
317
+ this.logger.warn(`[PD:NocturnalWorkflow] [${workflowId}] Selection: decision=${result.diagnostics.selection.decision}, principleId=${result.diagnostics.selection.selectedPrincipleId ?? 'none'}, sessionId=${result.diagnostics.selection.selectedSessionId ?? 'none'}`);
318
+ }
319
+
297
320
  this.store.updateWorkflowState(workflowId, 'terminal_error');
298
321
  this.store.recordEvent(workflowId, 'nocturnal_failed', null, 'terminal_error', reason, {
299
322
  failures: result.validationFailures,