principles-disciple 1.42.0 → 1.44.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.
Files changed (83) hide show
  1. package/openclaw.plugin.json +1 -1
  2. package/package.json +1 -1
  3. package/src/commands/archive-impl.ts +5 -3
  4. package/src/commands/capabilities.ts +1 -1
  5. package/src/commands/context.ts +1 -0
  6. package/src/commands/disable-impl.ts +1 -1
  7. package/src/commands/evolution-status.ts +2 -2
  8. package/src/commands/export.ts +6 -6
  9. package/src/commands/nocturnal-train.ts +1 -1
  10. package/src/commands/principle-rollback.ts +1 -1
  11. package/src/commands/rollback.ts +0 -1
  12. package/src/commands/samples.ts +1 -1
  13. package/src/commands/thinking-os.ts +1 -0
  14. package/src/commands/workflow-debug.ts +1 -1
  15. package/src/core/adaptive-thresholds.ts +3 -3
  16. package/src/core/config.ts +2 -1
  17. package/src/core/dictionary.ts +1 -0
  18. package/src/core/event-log.ts +3 -3
  19. package/src/core/evolution-engine.ts +1 -1
  20. package/src/core/external-training-contract.ts +1 -1
  21. package/src/core/merge-gate-audit.ts +3 -3
  22. package/src/core/nocturnal-arbiter.ts +1 -1
  23. package/src/core/nocturnal-compliance.ts +28 -28
  24. package/src/core/nocturnal-executability.ts +1 -1
  25. package/src/core/nocturnal-reasoning-deriver.ts +4 -4
  26. package/src/core/nocturnal-rule-implementation-validator.ts +1 -1
  27. package/src/core/nocturnal-snapshot-contract.ts +1 -1
  28. package/src/core/pain-context-extractor.ts +2 -2
  29. package/src/core/path-resolver.ts +1 -0
  30. package/src/core/pd-task-reconciler.ts +1 -0
  31. package/src/core/pd-task-service.ts +1 -1
  32. package/src/core/pd-task-store.ts +1 -0
  33. package/src/core/principle-internalization/deprecated-readiness.ts +1 -1
  34. package/src/core/principle-internalization/principle-lifecycle-service.ts +1 -1
  35. package/src/core/principle-training-state.ts +2 -2
  36. package/src/core/principle-tree-migration.ts +1 -1
  37. package/src/core/profile.ts +1 -1
  38. package/src/core/promotion-gate.ts +0 -2
  39. package/src/core/replay-engine.ts +1 -0
  40. package/src/core/risk-calculator.ts +2 -1
  41. package/src/core/rule-host.ts +1 -1
  42. package/src/core/session-tracker.ts +1 -0
  43. package/src/core/shadow-observation-registry.ts +1 -1
  44. package/src/core/thinking-models.ts +1 -1
  45. package/src/core/thinking-os-parser.ts +1 -1
  46. package/src/core/trajectory.ts +2 -4
  47. package/src/core/workspace-context.ts +1 -1
  48. package/src/hooks/bash-risk.ts +2 -2
  49. package/src/hooks/edit-verification.ts +4 -4
  50. package/src/hooks/gate.ts +8 -8
  51. package/src/hooks/gfi-gate.ts +2 -2
  52. package/src/hooks/lifecycle-routing.ts +1 -1
  53. package/src/hooks/pain.ts +2 -2
  54. package/src/hooks/progressive-trust-gate.ts +3 -3
  55. package/src/hooks/prompt.ts +3 -5
  56. package/src/hooks/thinking-checkpoint.ts +1 -1
  57. package/src/index.ts +8 -8
  58. package/src/service/central-database.ts +3 -2
  59. package/src/service/central-health-service.ts +2 -1
  60. package/src/service/central-overview-service.ts +3 -2
  61. package/src/service/control-ui-query-service.ts +2 -2
  62. package/src/service/event-log-auditor.ts +2 -2
  63. package/src/service/evolution-query-service.ts +3 -3
  64. package/src/service/evolution-worker.ts +11 -18
  65. package/src/service/health-query-service.ts +11 -10
  66. package/src/service/monitoring-query-service.ts +4 -4
  67. package/src/service/nocturnal-runtime.ts +0 -3
  68. package/src/service/nocturnal-service.ts +8 -8
  69. package/src/service/nocturnal-target-selector.ts +2 -2
  70. package/src/service/queue-io.ts +5 -5
  71. package/src/service/runtime-summary-service.ts +1 -1
  72. package/src/service/sleep-cycle.ts +1 -1
  73. package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +1 -1
  74. package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +1 -0
  75. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +1 -1
  76. package/src/service/subagent-workflow/subagent-error-utils.ts +1 -1
  77. package/src/service/subagent-workflow/workflow-store.ts +3 -2
  78. package/src/service/workflow-watchdog.ts +1 -1
  79. package/src/tools/critique-prompt.ts +1 -1
  80. package/src/tools/model-index.ts +1 -1
  81. package/src/utils/file-lock.ts +1 -1
  82. package/src/utils/io.ts +1 -1
  83. package/src/utils/retry.ts +2 -2
@@ -1,3 +1,4 @@
1
+
1
2
  import * as fs from 'fs';
2
3
  import * as path from 'path';
3
4
  import { readPainFlagData } from '../core/pain.js';
@@ -554,7 +555,7 @@ export class HealthQueryService {
554
555
  const streamPath = resolvePdPath(this.workspaceDir, 'EVOLUTION_STREAM');
555
556
  if (!fs.existsSync(streamPath)) return [];
556
557
 
557
- // eslint-disable-next-line @typescript-eslint/init-declarations
558
+
558
559
  let lines: string[];
559
560
  try {
560
561
  const raw = fs.readFileSync(streamPath, 'utf8').trim();
@@ -567,7 +568,7 @@ export class HealthQueryService {
567
568
  const records: RecentPrincipleChange[] = [];
568
569
  for (const line of lines) {
569
570
 
570
- // eslint-disable-next-line @typescript-eslint/init-declarations
571
+
571
572
  let event: EvolutionStreamRecord | null;
572
573
  try {
573
574
  event = JSON.parse(line) as EvolutionStreamRecord;
@@ -788,7 +789,7 @@ export class HealthQueryService {
788
789
 
789
790
 
790
791
 
791
- // eslint-disable-next-line @typescript-eslint/class-methods-use-this -- complexity 15, refactor candidate
792
+
792
793
  private getEventDedupKey(entry: EventLogEntry): string {
793
794
  const eventId = typeof entry.data?.eventId === 'string' ? entry.data.eventId : null;
794
795
  if (eventId) {
@@ -860,7 +861,7 @@ export class HealthQueryService {
860
861
 
861
862
 
862
863
 
863
- // eslint-disable-next-line @typescript-eslint/class-methods-use-this
864
+
864
865
  private resolveGateType(row: GateBlockRow): string {
865
866
  if (typeof row.gate_type === 'string' && row.gate_type.trim().length > 0) {
866
867
  return row.gate_type;
@@ -885,7 +886,7 @@ export class HealthQueryService {
885
886
  }
886
887
 
887
888
 
888
- // eslint-disable-next-line @typescript-eslint/class-methods-use-this
889
+
889
890
  private scoreToStatus(score: number): string {
890
891
  if (score >= 70) return 'healthy';
891
892
  if (score >= 40) return 'warning';
@@ -893,7 +894,7 @@ export class HealthQueryService {
893
894
  }
894
895
 
895
896
 
896
- // eslint-disable-next-line @typescript-eslint/class-methods-use-this
897
+
897
898
  private evolutionToStatus(tier: string, points: number): string {
898
899
  const lower = tier.toLowerCase();
899
900
  if (lower === 'forest' || lower === 'tree') return 'healthy';
@@ -902,7 +903,7 @@ export class HealthQueryService {
902
903
  }
903
904
 
904
905
 
905
- // eslint-disable-next-line @typescript-eslint/class-methods-use-this
906
+
906
907
  private safeListFiles(dirPath: string, predicate: (_name: string) => boolean): string[] {
907
908
  if (!fs.existsSync(dirPath)) return [];
908
909
  try {
@@ -915,7 +916,7 @@ export class HealthQueryService {
915
916
  }
916
917
 
917
918
 
918
- // eslint-disable-next-line @typescript-eslint/class-methods-use-this
919
+
919
920
  private readJsonFile<T>(filePath: string, fallback: T): T {
920
921
  if (!fs.existsSync(filePath)) return fallback;
921
922
  try {
@@ -926,13 +927,13 @@ export class HealthQueryService {
926
927
  }
927
928
 
928
929
 
929
- // eslint-disable-next-line @typescript-eslint/class-methods-use-this
930
+
930
931
  private asNumber(value: unknown, fallback: number): number {
931
932
  return Number.isFinite(value) ? Number(value) : fallback;
932
933
  }
933
934
 
934
935
 
935
- // eslint-disable-next-line @typescript-eslint/class-methods-use-this
936
+
936
937
  private asNullableNumber(value: unknown): number | null {
937
938
  if (Number.isFinite(value)) return Number(value);
938
939
  if (typeof value === 'string' && value.trim().length > 0) {
@@ -36,7 +36,7 @@ export class MonitoringQueryService {
36
36
  const now = Date.now();
37
37
  const workflowsWithStuckDetection = workflows.map(wf => {
38
38
  // Parse metadata for timeout configuration
39
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
39
+
40
40
  const metadata = parseWorkflowMetadata(wf.metadata_json);
41
41
  const timeoutMs = metadata.timeoutMs ?? 15 * 60 * 1000; // Default 15 minutes
42
42
 
@@ -85,10 +85,10 @@ export class MonitoringQueryService {
85
85
 
86
86
  // Determine status
87
87
 
88
- // eslint-disable-next-line @typescript-eslint/init-declarations
88
+
89
89
  let status: 'pending' | 'running' | 'completed' | 'failed';
90
90
 
91
- // eslint-disable-next-line @typescript-eslint/init-declarations
91
+
92
92
  let reason: string | undefined;
93
93
 
94
94
  if (!startEvent) {
@@ -110,7 +110,7 @@ export class MonitoringQueryService {
110
110
 
111
111
  // Calculate duration if stage started and completed/failed
112
112
 
113
- // eslint-disable-next-line @typescript-eslint/init-declarations
113
+
114
114
  let duration: number | undefined;
115
115
  if (startEvent && (completeEvent || failedEvent)) {
116
116
  const endEvent = completeEvent || failedEvent;
@@ -390,9 +390,6 @@ export function checkCooldown(
390
390
  } = {}
391
391
  ): CooldownCheckResult {
392
392
  const {
393
-
394
- globalCooldownMs: _globalCooldownMs = DEFAULT_GLOBAL_COOLDOWN_MS,
395
- principleCooldownMs: _principleCooldownMs = DEFAULT_PRINCIPLE_COOLDOWN_MS,
396
393
  maxRunsPerWindow = DEFAULT_MAX_RUNS_PER_WINDOW,
397
394
  quotaWindowMs = DEFAULT_QUOTA_WINDOW_MS,
398
395
  } = options;
@@ -116,7 +116,7 @@ function incrementGeneratedSampleCount(stateDir: string, principleId: string): v
116
116
  state.generatedSampleCount += 1;
117
117
  setPrincipleState(stateDir, state);
118
118
  } catch (err) {
119
- // eslint-disable-next-line no-console -- Non-critical warning in helper function
119
+
120
120
  console.warn(`[nocturnal-service] Failed to sync generatedSampleCount for ${principleId}:`, err instanceof Error ? err.stack : err);
121
121
  }
122
122
  }
@@ -522,7 +522,7 @@ function persistCodeCandidate(
522
522
  try {
523
523
  refreshPrincipleLifecycle(workspaceDir, stateDir);
524
524
  } catch (err) {
525
- // eslint-disable-next-line no-console -- Non-critical warning in helper function
525
+
526
526
  console.warn('[nocturnal-service] Lifecycle refresh failed after code candidate persistence:', err instanceof Error ? err.stack : err);
527
527
  }
528
528
  return {
@@ -702,7 +702,7 @@ export function executeNocturnalReflection(
702
702
  ): NocturnalRunResult {
703
703
  // Use provided logger or fallback to console
704
704
  const logger = options.logger;
705
- // eslint-disable-next-line no-console -- Intentional console fallback when no logger provided
705
+
706
706
  const warn = logger?.warn?.bind(logger) ?? console.warn.bind(console);
707
707
 
708
708
  const diagnostics: NocturnalRunDiagnostics = {
@@ -882,7 +882,7 @@ export function executeNocturnalReflection(
882
882
  diagnostics,
883
883
  };
884
884
  }
885
- trinityArtifact = trinityResult.artifact!; // eslint-disable-line @typescript-eslint/no-non-null-assertion -- Reason: artifact is validated by validateTrinityDraft which returns valid: true when artifact exists
885
+ trinityArtifact = trinityResult.artifact!;
886
886
  // Convert Trinity draft to arbiter-compatible artifact
887
887
  const artifactData = draftToArtifact(trinityArtifact);
888
888
  rawJson = JSON.stringify(artifactData);
@@ -933,7 +933,7 @@ export function executeNocturnalReflection(
933
933
  diagnostics,
934
934
  };
935
935
  }
936
- trinityArtifact = trinityResult.artifact!; // eslint-disable-line @typescript-eslint/no-non-null-assertion -- Reason: artifact is validated by validateTrinityDraft which returns valid: true when artifact exists
936
+ trinityArtifact = trinityResult.artifact!;
937
937
  // Convert Trinity draft to arbiter-compatible artifact
938
938
  const artifactData = draftToArtifact(trinityArtifact);
939
939
  rawJson = JSON.stringify(artifactData);
@@ -1187,7 +1187,7 @@ async function executeNocturnalReflectionWithAdapter(
1187
1187
  ): Promise<NocturnalRunResult> {
1188
1188
  // Use provided logger or fallback to console
1189
1189
  const logger = options.logger;
1190
- // eslint-disable-next-line no-console -- Intentional console fallback when no logger provided
1190
+
1191
1191
  const warn = logger?.warn?.bind(logger) ?? console.warn.bind(console);
1192
1192
 
1193
1193
  const diagnostics: NocturnalRunDiagnostics = {
@@ -1387,7 +1387,7 @@ async function executeNocturnalReflectionWithAdapter(
1387
1387
  adjustThresholdsFromSignals(stateDir, { malformedRate: 1.0, arbiterRejectRate: 0.0, executabilityRejectRate: 0.0, qualityDelta: 0.0 });
1388
1388
  return { success: false, noTargetSelected: false, validationFailed: true, validationFailures: [`Trinity override failed: ${failures.join('; ')}`], snapshot, diagnostics };
1389
1389
  }
1390
- trinityArtifact = trinityResult.artifact!; // eslint-disable-line @typescript-eslint/no-non-null-assertion -- Reason: artifact is validated by validateTrinityDraft which returns valid: true when artifact exists
1390
+ trinityArtifact = trinityResult.artifact!;
1391
1391
  const artifactData = draftToArtifact(trinityArtifact);
1392
1392
  rawJson = JSON.stringify(artifactData);
1393
1393
  } else {
@@ -1414,7 +1414,7 @@ async function executeNocturnalReflectionWithAdapter(
1414
1414
  adjustThresholdsFromSignals(stateDir, { malformedRate: 1.0, arbiterRejectRate: 0.0, executabilityRejectRate: 0.0, qualityDelta: 0.0 });
1415
1415
  return { success: false, noTargetSelected: false, validationFailed: true, validationFailures: failures, snapshot, diagnostics };
1416
1416
  }
1417
- trinityArtifact = trinityResult.artifact!; // eslint-disable-line @typescript-eslint/no-non-null-assertion -- Reason: artifact is validated by validateTrinityDraft which returns valid: true when artifact exists
1417
+ trinityArtifact = trinityResult.artifact!;
1418
1418
  const artifactData = draftToArtifact(trinityArtifact);
1419
1419
  rawJson = JSON.stringify(artifactData);
1420
1420
  } else {
@@ -288,7 +288,7 @@ export class NocturnalTargetSelector {
288
288
  };
289
289
 
290
290
 
291
- // eslint-disable-next-line @typescript-eslint/max-params
291
+
292
292
  constructor(
293
293
  workspaceDir: string,
294
294
  stateDir: string,
@@ -533,7 +533,7 @@ export class NocturnalTargetSelector {
533
533
  * This is a convenience wrapper for the common case.
534
534
  */
535
535
 
536
- // eslint-disable-next-line @typescript-eslint/max-params
536
+
537
537
  export function selectNocturnalTarget(
538
538
  workspaceDir: string,
539
539
  stateDir: string,
@@ -160,9 +160,9 @@ export function readRecentPainContext(wctx: WorkspaceContext): RecentPainContext
160
160
  }
161
161
  } catch (err) {
162
162
  // Best effort — non-fatal, but surface unexpected errors
163
- /* eslint-disable no-console */
163
+
164
164
  console.warn(`[queue-io] Failed to read pain context (non-fatal): ${String(err)}`);
165
- /* eslint-enable no-console */
165
+
166
166
  }
167
167
 
168
168
  return { mostRecent: null, recentPainCount: 0, recentMaxPainScore: 0 };
@@ -185,7 +185,7 @@ export function shouldSkipForDedup(
185
185
  const recentSimilarReflection = hasRecentSimilarReflection(queue, painSourceKey, now);
186
186
 
187
187
  if (recentSimilarReflection) {
188
- const completedTime = new Date(recentSimilarReflection.completed_at!).getTime(); /* eslint-disable-line @typescript-eslint/no-non-null-assertion */
188
+ const completedTime = new Date(recentSimilarReflection.completed_at!).getTime();
189
189
  logger?.debug?.(`[PD:EvolutionWorker] Skipping sleep_reflection — similar reflection completed ${Math.round((now - completedTime) / 60000)}min ago (same pain pattern: ${painSourceKey})`);
190
190
  return true;
191
191
  }
@@ -358,9 +358,9 @@ export function loadEvolutionQueue(queuePath: string): EvolutionQueueItem[] {
358
358
  rawQueue = [];
359
359
  } else {
360
360
  // Corrupted JSON or other read error — warn and recover with empty queue
361
- /* eslint-disable no-console */
361
+
362
362
  console.warn(`[queue-io] Failed to load evolution queue (recovering with empty): ${String(err)}`);
363
- /* eslint-enable no-console */
363
+
364
364
  rawQueue = [];
365
365
  }
366
366
  }
@@ -420,7 +420,7 @@ export class RuntimeSummaryService {
420
420
  * Queue is the only authoritative execution truth source.
421
421
  */
422
422
 
423
- // eslint-disable-next-line @typescript-eslint/max-params
423
+
424
424
  private static buildDirectiveSummary(
425
425
  queue: QueueItem[] | null,
426
426
  directive: DirectiveFile | null,
@@ -71,7 +71,7 @@ export interface CycleOptions {
71
71
  * @param options.heartbeatCounterRef — mutable counter, incremented by runCycle
72
72
  */
73
73
  export async function runCycle(options: CycleOptions): Promise<WorkerStatusReport> {
74
- const { wctx, logger, eventLog: _eventLog, api: _api, heartbeatCounterRef } = options;
74
+ const { wctx, logger, api: _api, heartbeatCounterRef } = options;
75
75
  const cycleStart = Date.now();
76
76
  heartbeatCounterRef.value++;
77
77
 
@@ -130,7 +130,7 @@ export const deepReflectWorkflowSpec: SubagentWorkflowSpec<DeepReflectResult> =
130
130
  } else if (Array.isArray(lastMessage?.content)) {
131
131
  insights = (lastMessage.content as { type?: string; text?: string }[])
132
132
  .filter((c) => c?.type === 'text' && typeof c.text === 'string')
133
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Reason: filter ensures c.text is a string
133
+
134
134
  .map((c) => c.text!)
135
135
  .join('\n');
136
136
  }
@@ -1,3 +1,4 @@
1
+
1
2
  import type { PluginLogger } from '../../openclaw-sdk.js';
2
3
  import type {
3
4
  SubagentWorkflowSpec,
@@ -453,7 +453,7 @@ export class NocturnalWorkflowManager implements WorkflowManager {
453
453
 
454
454
  async sweepExpiredWorkflows(
455
455
  maxAgeMs = 30 * 60 * 1000,
456
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Reason: subagentRuntime param is intentionally any for backward compatibility with callers
456
+
457
457
  subagentRuntime?: any,
458
458
 
459
459
  agentSession?: {
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Callers should suppress warnings for these errors — they are not real failures.
7
7
  */
8
- // eslint-disable-next-line complexity -- complexity 13, refactor candidate
8
+
9
9
  export function isExpectedSubagentError(err: unknown): boolean {
10
10
  const msg = String(err);
11
11
  return (
@@ -1,3 +1,4 @@
1
+
1
2
  import Database from 'better-sqlite3';
2
3
  import * as fs from 'fs';
3
4
  import * as path from 'path';
@@ -233,7 +234,7 @@ export class WorkflowStore {
233
234
  }
234
235
 
235
236
 
236
- // eslint-disable-next-line @typescript-eslint/max-params
237
+
237
238
  recordEvent(
238
239
  workflowId: string,
239
240
  eventType: string,
@@ -270,7 +271,7 @@ export class WorkflowStore {
270
271
  * same idempotency_key already exists, this is a no-op (idempotent).
271
272
  */
272
273
 
273
- // eslint-disable-next-line @typescript-eslint/max-params
274
+
274
275
  recordStageOutput(
275
276
  workflowId: string,
276
277
  stage: 'dreamer' | 'philosopher',
@@ -30,7 +30,7 @@ export interface WatchdogResult {
30
30
  scanError?: string;
31
31
  }
32
32
 
33
- /* eslint-disable complexity */
33
+
34
34
  export async function runWorkflowWatchdog(
35
35
  wctx: WorkspaceContext,
36
36
  api: OpenClawPluginApi | null,
@@ -17,7 +17,7 @@ const DEPTH_INSTRUCTIONS = {
17
17
  * 严格按照测试用例的调用习惯和断言要求进行重写。
18
18
  * 增加 OpenClaw 兼容性路径解析。
19
19
  */
20
- // eslint-disable-next-line complexity -- complexity 12, refactor candidate
20
+
21
21
  export function buildCritiquePromptV2(
22
22
  params: {
23
23
  context: string;
@@ -62,7 +62,7 @@ export function loadModelIndex(
62
62
  const customConfig = loadCustomConfig(wctx);
63
63
 
64
64
 
65
- // eslint-disable-next-line @typescript-eslint/init-declarations
65
+
66
66
  let modelsDir: string;
67
67
  if (customConfig?.modelsDir) {
68
68
  modelsDir = path.isAbsolute(customConfig.modelsDir)
@@ -352,7 +352,7 @@ export async function withAsyncLock<T>(
352
352
  try {
353
353
  return await fn();
354
354
  } finally {
355
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Reason: resolveRelease is assigned in Promise constructor before finally runs
355
+
356
356
  resolveRelease!();
357
357
  // 清理已完成的队列
358
358
  if (asyncLockQueues.get(lockPath) === currentQueue) {
package/src/utils/io.ts CHANGED
@@ -126,7 +126,7 @@ export function parseKvLines(text: string): Record<string, string> {
126
126
  return result;
127
127
  }
128
128
 
129
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Reason: serializeKvLines handles arbitrary object shapes for kv line serialization
129
+
130
130
  export function serializeKvLines(data: Record<string, any>): string {
131
131
  const lines: string[] = [];
132
132
  const keys = Object.keys(data).sort();
@@ -424,7 +424,7 @@ export function computeDynamicTimeout(
424
424
  if (history.length < MIN_SAMPLES) {
425
425
  // Not enough data — use the spec's static timeout
426
426
  const fallback = clampTimeout(defaultTimeout);
427
- // eslint-disable-next-line no-console -- Monitoring output for fallback diagnostics
427
+
428
428
  console.info(`[PD:DynamicTimeout] Insufficient samples (${history.length} < ${MIN_SAMPLES}) for '${workflowType}', falling back to static timeout: ${fallback}ms`);
429
429
  return fallback;
430
430
  }
@@ -432,7 +432,7 @@ export function computeDynamicTimeout(
432
432
  const p95 = percentile(history, 95);
433
433
  const adaptive = p95 * SAFETY_MULTIPLIER;
434
434
  const result = clampTimeout(adaptive);
435
- // eslint-disable-next-line no-console -- Monitoring output for adaptive timeout diagnostics
435
+
436
436
  console.info(`[PD:DynamicTimeout] Computed adaptive timeout for '${workflowType}': P95=${p95}ms (from ${history.length} samples) × ${SAFETY_MULTIPLIER} = ${result}ms`);
437
437
  return result;
438
438
  }