principles-disciple 1.30.0 → 1.32.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.32.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.32.0",
4
4
  "description": "Native OpenClaw plugin for Principles Disciple",
5
5
  "type": "module",
6
6
  "main": "./dist/bundle.js",
@@ -1722,6 +1722,9 @@ async function processEvolutionQueue(wctx: WorkspaceContext, logger: PluginLogge
1722
1722
  taskId: sleepTask.id,
1723
1723
  painContext: sleepTask.recentPainContext,
1724
1724
  triggerSource: sleepTask.source,
1725
+ // #297: Configure which preflight gates to skip.
1726
+ // sleep_reflection uses periodic trigger which bypasses idle by design.
1727
+ skipPreflightGates: ['idle'],
1725
1728
  },
1726
1729
  });
1727
1730
  sleepTask.resultRef = workflowHandle.workflowId;
@@ -2166,8 +2169,8 @@ export const EvolutionWorkerService: ExtendedEvolutionWorkerService = {
2166
2169
  shouldTrySleepReflection = true;
2167
2170
  }
2168
2171
 
2169
- // Path 2: Periodic trigger (bypasses idle requirement for debugging)
2170
- if (!idleResult.isIdle && sleepConfig.trigger_mode === 'periodic') {
2172
+ // Path 2: Periodic trigger (fires regardless of idle state)
2173
+ if (sleepConfig.trigger_mode === 'periodic') {
2171
2174
  if (heartbeatCounter >= sleepConfig.period_heartbeats) {
2172
2175
  logger?.info?.(`[PD:EvolutionWorker] Periodic trigger: heartbeatCounter=${heartbeatCounter} >= period_heartbeats=${sleepConfig.period_heartbeats}`);
2173
2176
  shouldTrySleepReflection = true;
@@ -2179,6 +2182,7 @@ export const EvolutionWorkerService: ExtendedEvolutionWorkerService = {
2179
2182
 
2180
2183
  if (shouldTrySleepReflection) {
2181
2184
  const cooldown = checkCooldown(wctx.stateDir, undefined, {
2185
+ globalCooldownMs: sleepConfig.cooldown_ms,
2182
2186
  maxRunsPerWindow: sleepConfig.max_runs_per_day,
2183
2187
  quotaWindowMs: 24 * 60 * 60 * 1000,
2184
2188
  });
@@ -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 */
@@ -260,10 +260,9 @@ export class NocturnalWorkflowManager implements WorkflowManager {
260
260
  },
261
261
  // Pass painContext for Selector ranking bias
262
262
  painContext,
263
- // #244: Only skip preflight idle gate for manual/test triggers.
264
- // Automatic triggers must go through normal idle check.
265
- ...(((options.metadata)?.triggerSource === 'manual' ||
266
- (options.metadata)?.triggerSource === 'test')
263
+ // #244: Skip preflight gates as configured by caller (e.g. manual/test/sleep_reflection).
264
+ // Gates not in skipPreflightGates go through normal checks.
265
+ ...(((options.metadata)?.skipPreflightGates as string[] | undefined)?.includes('idle')
267
266
  ? {
268
267
  idleCheckOverride: {
269
268
  isIdle: true,
@@ -272,7 +271,7 @@ export class NocturnalWorkflowManager implements WorkflowManager {
272
271
  userActiveSessions: 0,
273
272
  abandonedSessionIds: [],
274
273
  trajectoryGuardrailConfirmsIdle: true,
275
- reason: 'manual/test override',
274
+ reason: 'skipPreflightGates override',
276
275
  },
277
276
  }
278
277
  : {}),
@@ -293,7 +292,28 @@ export class NocturnalWorkflowManager implements WorkflowManager {
293
292
  this.completedWorkflows.set(workflowId, Date.now());
294
293
  } else {
295
294
  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}`);
295
+ const failuresSummary = result.validationFailures?.length > 0
296
+ ? result.validationFailures.join('; ')
297
+ : (result.skipReason ?? 'none');
298
+ this.logger.warn(`[PD:NocturnalWorkflow] [${workflowId}] Pipeline failed: reason=${reason}, noTargetSelected=${result.noTargetSelected}, skipReason=${result.skipReason ?? 'none'}, validationFailures=${result.validationFailures?.length ?? 0}, details=${failuresSummary}`);
299
+
300
+ // Log full result structure for debugging
301
+ this.logger.warn(`[PD:NocturnalWorkflow] [${workflowId}] Full result: success=${result.success}, validationFailed=${result.validationFailed}, noTargetSelected=${result.noTargetSelected}`);
302
+
303
+ // Log diagnostics for debugging
304
+ if (result.diagnostics?.trinityResult) {
305
+ this.logger.warn(`[PD:NocturnalWorkflow] [${workflowId}] Trinity result: success=${result.diagnostics.trinityResult.success}, chainMode=${result.diagnostics.chainModeUsed ?? 'unknown'}`);
306
+ if (!result.diagnostics.trinityResult.success) {
307
+ this.logger.warn(`[PD:NocturnalWorkflow] [${workflowId}] Trinity failures: ${result.diagnostics.trinityResult.failures.map(f => `${f.stage}: ${f.reason}`).join('; ')}`);
308
+ }
309
+ }
310
+ if (result.diagnostics?.arbiterResult) {
311
+ this.logger.warn(`[PD:NocturnalWorkflow] [${workflowId}] Arbiter result: passed=${result.diagnostics.arbiterResult.passed}, failures=${result.diagnostics.arbiterResult.failures.map(f => f.reason).join('; ')}`);
312
+ }
313
+ if (result.diagnostics?.selection) {
314
+ this.logger.warn(`[PD:NocturnalWorkflow] [${workflowId}] Selection: decision=${result.diagnostics.selection.decision}, principleId=${result.diagnostics.selection.selectedPrincipleId ?? 'none'}, sessionId=${result.diagnostics.selection.selectedSessionId ?? 'none'}`);
315
+ }
316
+
297
317
  this.store.updateWorkflowState(workflowId, 'terminal_error');
298
318
  this.store.recordEvent(workflowId, 'nocturnal_failed', null, 'terminal_error', reason, {
299
319
  failures: result.validationFailures,