principles-disciple 1.11.0 → 1.13.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 (233) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +66 -0
  3. package/esbuild.config.js +1 -1
  4. package/openclaw.plugin.json +4 -4
  5. package/package.json +2 -3
  6. package/run-nocturnal.mjs +30 -0
  7. package/scripts/db-migrate.mjs +170 -0
  8. package/scripts/sync-plugin.mjs +250 -6
  9. package/src/commands/archive-impl.ts +136 -0
  10. package/src/commands/capabilities.ts +4 -2
  11. package/src/commands/context.ts +5 -1
  12. package/src/commands/disable-impl.ts +151 -0
  13. package/src/commands/evolution-status.ts +64 -19
  14. package/src/commands/export.ts +8 -6
  15. package/src/commands/focus.ts +8 -20
  16. package/src/commands/nocturnal-review.ts +5 -7
  17. package/src/commands/nocturnal-rollout.ts +1 -12
  18. package/src/commands/nocturnal-train.ts +21 -47
  19. package/src/commands/pain.ts +10 -5
  20. package/src/commands/principle-rollback.ts +4 -2
  21. package/src/commands/promote-impl.ts +274 -0
  22. package/src/commands/rollback-impl.ts +234 -0
  23. package/src/commands/rollback.ts +6 -3
  24. package/src/commands/samples.ts +2 -0
  25. package/src/commands/thinking-os.ts +3 -4
  26. package/src/commands/workflow-debug.ts +2 -1
  27. package/src/config/errors.ts +1 -0
  28. package/src/core/AGENTS.md +34 -0
  29. package/src/core/adaptive-thresholds.ts +4 -3
  30. package/src/core/code-implementation-storage.ts +241 -0
  31. package/src/core/config.ts +5 -2
  32. package/src/core/control-ui-db.ts +29 -10
  33. package/src/core/detection-funnel.ts +12 -7
  34. package/src/core/diagnostician-task-store.ts +156 -0
  35. package/src/core/dictionary.ts +4 -4
  36. package/src/core/empathy-keyword-matcher.ts +7 -3
  37. package/src/core/empathy-types.ts +13 -2
  38. package/src/core/event-log.ts +14 -6
  39. package/src/core/evolution-engine.ts +27 -31
  40. package/src/core/evolution-logger.ts +3 -2
  41. package/src/core/evolution-reducer.ts +110 -31
  42. package/src/core/evolution-types.ts +10 -0
  43. package/src/core/external-training-contract.ts +1 -0
  44. package/src/core/focus-history.ts +38 -24
  45. package/src/core/hygiene/tracker.ts +10 -6
  46. package/src/core/init.ts +5 -2
  47. package/src/core/migration.ts +3 -3
  48. package/src/core/model-deployment-registry.ts +6 -4
  49. package/src/core/model-training-registry.ts +5 -3
  50. package/src/core/nocturnal-arbiter.ts +13 -14
  51. package/src/core/nocturnal-artifact-lineage.ts +117 -0
  52. package/src/core/nocturnal-artificer.ts +257 -0
  53. package/src/core/nocturnal-candidate-scoring.ts +4 -2
  54. package/src/core/nocturnal-compliance.ts +67 -19
  55. package/src/core/nocturnal-dataset.ts +95 -2
  56. package/src/core/nocturnal-executability.ts +2 -3
  57. package/src/core/nocturnal-export.ts +6 -3
  58. package/src/core/nocturnal-rule-implementation-validator.ts +245 -0
  59. package/src/core/nocturnal-trajectory-extractor.ts +10 -3
  60. package/src/core/nocturnal-trinity.ts +319 -61
  61. package/src/core/pain-context-extractor.ts +29 -15
  62. package/src/core/pain.ts +7 -5
  63. package/src/core/path-resolver.ts +16 -15
  64. package/src/core/paths.ts +2 -1
  65. package/src/core/pd-task-reconciler.ts +463 -0
  66. package/src/core/pd-task-service.ts +42 -0
  67. package/src/core/pd-task-store.ts +77 -0
  68. package/src/core/pd-task-types.ts +128 -0
  69. package/src/core/principle-internalization/deprecated-readiness.ts +91 -0
  70. package/src/core/principle-internalization/internalization-routing-policy.ts +208 -0
  71. package/src/core/principle-internalization/lifecycle-metrics.ts +149 -0
  72. package/src/core/principle-internalization/lifecycle-read-model.ts +243 -0
  73. package/src/core/principle-internalization/lifecycle-refresh.ts +11 -0
  74. package/src/core/principle-internalization/principle-lifecycle-service.ts +167 -0
  75. package/src/core/principle-training-state.ts +95 -370
  76. package/src/core/principle-tree-ledger.ts +733 -0
  77. package/src/core/principle-tree-migration.ts +195 -0
  78. package/src/core/profile.ts +3 -1
  79. package/src/core/promotion-gate.ts +14 -18
  80. package/src/core/replay-engine.ts +562 -0
  81. package/src/core/risk-calculator.ts +6 -4
  82. package/src/core/rule-host-helpers.ts +39 -0
  83. package/src/core/rule-host-types.ts +82 -0
  84. package/src/core/rule-host.ts +245 -0
  85. package/src/core/rule-implementation-runtime.ts +38 -0
  86. package/src/core/schema/db-types.ts +16 -0
  87. package/src/core/schema/index.ts +26 -0
  88. package/src/core/schema/migration-runner.ts +207 -0
  89. package/src/core/schema/migrations/001-init-trajectory.ts +211 -0
  90. package/src/core/schema/migrations/002-init-central.ts +122 -0
  91. package/src/core/schema/migrations/003-init-workflow.ts +55 -0
  92. package/src/core/schema/migrations/004-add-thinking-and-gfi.ts +74 -0
  93. package/src/core/schema/migrations/index.ts +31 -0
  94. package/src/core/schema/schema-definitions.ts +650 -0
  95. package/src/core/session-tracker.ts +6 -4
  96. package/src/core/shadow-observation-registry.ts +6 -3
  97. package/src/core/system-logger.ts +2 -2
  98. package/src/core/thinking-models.ts +182 -46
  99. package/src/core/thinking-os-parser.ts +156 -0
  100. package/src/core/training-program.ts +7 -7
  101. package/src/core/trajectory.ts +42 -36
  102. package/src/core/workspace-context.ts +77 -11
  103. package/src/core/workspace-dir-validation.ts +152 -0
  104. package/src/hooks/AGENTS.md +31 -0
  105. package/src/hooks/bash-risk.ts +3 -1
  106. package/src/hooks/edit-verification.ts +9 -5
  107. package/src/hooks/gate-block-helper.ts +5 -1
  108. package/src/hooks/gate.ts +152 -5
  109. package/src/hooks/gfi-gate.ts +9 -2
  110. package/src/hooks/lifecycle-routing.ts +124 -0
  111. package/src/hooks/lifecycle.ts +12 -12
  112. package/src/hooks/llm.ts +17 -109
  113. package/src/hooks/message-sanitize.ts +5 -3
  114. package/src/hooks/pain.ts +19 -15
  115. package/src/hooks/progressive-trust-gate.ts +7 -1
  116. package/src/hooks/prompt.ts +169 -60
  117. package/src/hooks/subagent.ts +5 -4
  118. package/src/hooks/thinking-checkpoint.ts +2 -0
  119. package/src/hooks/trajectory-collector.ts +15 -12
  120. package/src/http/principles-console-route.ts +31 -68
  121. package/src/i18n/commands.ts +2 -2
  122. package/src/index.ts +130 -40
  123. package/src/service/central-database.ts +131 -43
  124. package/src/service/central-health-service.ts +47 -0
  125. package/src/service/central-overview-service.ts +135 -0
  126. package/src/service/central-sync-service.ts +87 -0
  127. package/src/service/control-ui-query-service.ts +46 -36
  128. package/src/service/event-log-auditor.ts +261 -0
  129. package/src/service/evolution-query-service.ts +23 -22
  130. package/src/service/evolution-worker.ts +565 -261
  131. package/src/service/health-query-service.ts +213 -36
  132. package/src/service/nocturnal-runtime.ts +8 -4
  133. package/src/service/nocturnal-service.ts +503 -59
  134. package/src/service/nocturnal-target-selector.ts +5 -7
  135. package/src/service/runtime-summary-service.ts +2 -1
  136. package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +25 -336
  137. package/src/service/subagent-workflow/dynamic-timeout.ts +30 -0
  138. package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +48 -386
  139. package/src/service/subagent-workflow/index.ts +2 -0
  140. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +169 -284
  141. package/src/service/subagent-workflow/runtime-direct-driver.ts +114 -16
  142. package/src/service/subagent-workflow/subagent-error-utils.ts +25 -0
  143. package/src/service/subagent-workflow/types.ts +9 -4
  144. package/src/service/subagent-workflow/workflow-manager-base.ts +573 -0
  145. package/src/service/subagent-workflow/workflow-store.ts +71 -11
  146. package/src/service/trajectory-service.ts +2 -1
  147. package/src/tools/critique-prompt.ts +1 -1
  148. package/src/tools/deep-reflect.ts +175 -209
  149. package/src/tools/model-index.ts +2 -1
  150. package/src/types/event-types.ts +2 -2
  151. package/src/types/principle-tree-schema.ts +29 -23
  152. package/src/utils/file-lock.ts +5 -3
  153. package/src/utils/io.ts +5 -2
  154. package/src/utils/nlp.ts +5 -46
  155. package/src/utils/node-vm-polyfill.ts +11 -0
  156. package/src/utils/plugin-logger.ts +2 -0
  157. package/src/utils/retry.ts +572 -0
  158. package/src/utils/subagent-probe.ts +1 -1
  159. package/templates/langs/en/core/AGENTS.md +0 -13
  160. package/templates/langs/en/core/SOUL.md +1 -31
  161. package/templates/langs/en/core/TOOLS.md +0 -4
  162. package/templates/langs/en/principles/THINKING_OS.md +77 -0
  163. package/templates/langs/en/skills/admin/SKILL.md +0 -1
  164. package/templates/langs/en/skills/evolution-framework-update/SKILL.md +1 -1
  165. package/templates/langs/en/skills/pd-diagnostician/SKILL.md +18 -5
  166. package/templates/langs/zh/core/AGENTS.md +0 -22
  167. package/templates/langs/zh/core/SOUL.md +1 -31
  168. package/templates/langs/zh/core/TOOLS.md +0 -4
  169. package/templates/langs/zh/principles/THINKING_OS.md +77 -0
  170. package/templates/langs/zh/skills/admin/SKILL.md +0 -1
  171. package/templates/langs/zh/skills/evolution-framework-update/SKILL.md +1 -1
  172. package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +25 -4
  173. package/tests/commands/evolution-status.test.ts +119 -0
  174. package/tests/commands/implementation-lifecycle.test.ts +362 -0
  175. package/tests/core/code-implementation-storage.test.ts +398 -0
  176. package/tests/core/evolution-reducer.detector-metadata.test.ts +28 -28
  177. package/tests/core/nocturnal-artifact-lineage.test.ts +53 -0
  178. package/tests/core/nocturnal-artificer.test.ts +241 -0
  179. package/tests/core/nocturnal-compliance-p-principles.test.ts +133 -0
  180. package/tests/core/nocturnal-rule-implementation-validator.test.ts +127 -0
  181. package/tests/core/pd-task-store.test.ts +126 -0
  182. package/tests/core/principle-internalization/deprecated-readiness.test.ts +193 -0
  183. package/tests/core/principle-internalization/internalization-routing-policy.test.ts +212 -0
  184. package/tests/core/principle-internalization/lifecycle-metrics.test.ts +350 -0
  185. package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +211 -0
  186. package/tests/core/principle-training-state.test.ts +228 -1
  187. package/tests/core/principle-tree-ledger.test.ts +423 -0
  188. package/tests/core/regression-v1-9-1.test.ts +265 -0
  189. package/tests/core/replay-engine.test.ts +234 -0
  190. package/tests/core/rule-host-helpers.test.ts +120 -0
  191. package/tests/core/rule-host.test.ts +389 -0
  192. package/tests/core/rule-implementation-runtime.test.ts +64 -0
  193. package/tests/core/workspace-context.test.ts +53 -0
  194. package/tests/core/workspace-dir-validation.test.ts +272 -0
  195. package/tests/hooks/gate-rule-host-pipeline.test.ts +385 -0
  196. package/tests/hooks/pain.test.ts +74 -10
  197. package/tests/hooks/prompt.test.ts +63 -1
  198. package/tests/integration/principle-lifecycle.e2e.test.ts +197 -0
  199. package/tests/integration/tool-hooks-workspace-dir.e2e.test.ts +211 -0
  200. package/tests/service/data-endpoints-regression.test.ts +834 -0
  201. package/tests/service/evolution-worker.test.ts +0 -123
  202. package/tests/service/nocturnal-service-code-candidate.test.ts +330 -0
  203. package/tests/utils/nlp.test.ts +1 -19
  204. package/tests/utils/retry.test.ts +327 -0
  205. package/ui/src/App.tsx +1 -1
  206. package/ui/src/api.ts +4 -0
  207. package/ui/src/charts.tsx +366 -0
  208. package/ui/src/components/WorkspaceConfig.tsx +107 -75
  209. package/ui/src/i18n/ui.ts +89 -31
  210. package/ui/src/pages/EvolutionPage.tsx +1 -1
  211. package/ui/src/pages/OverviewPage.tsx +441 -81
  212. package/ui/src/pages/ThinkingModelsPage.tsx +287 -69
  213. package/ui/src/styles.css +43 -0
  214. package/ui/src/types.ts +17 -1
  215. package/src/agents/nocturnal-dreamer.md +0 -152
  216. package/src/agents/nocturnal-philosopher.md +0 -138
  217. package/src/agents/nocturnal-reflector.md +0 -126
  218. package/src/agents/nocturnal-scribe.md +0 -164
  219. package/templates/workspace/.principles/00-kernel.md +0 -51
  220. package/templates/workspace/.principles/DECISION_POLICY.json +0 -44
  221. package/templates/workspace/.principles/PRINCIPLES.md +0 -20
  222. package/templates/workspace/.principles/PROFILE.json +0 -54
  223. package/templates/workspace/.principles/PROFILE.schema.json +0 -56
  224. package/templates/workspace/.principles/THINKING_OS.md +0 -64
  225. package/templates/workspace/.principles/THINKING_OS_ARCHIVE.md +0 -7
  226. package/templates/workspace/.principles/THINKING_OS_CANDIDATES.md +0 -9
  227. package/templates/workspace/.principles/models/_INDEX.md +0 -27
  228. package/templates/workspace/.principles/models/first_principles.md +0 -62
  229. package/templates/workspace/.principles/models/marketing_4p.md +0 -52
  230. package/templates/workspace/.principles/models/porter_five.md +0 -63
  231. package/templates/workspace/.principles/models/swot.md +0 -60
  232. package/templates/workspace/.principles/models/user_story_map.md +0 -63
  233. package/templates/workspace/.state/WORKBOARD.json +0 -4
@@ -0,0 +1,572 @@
1
+ /**
2
+ * Unified Retry Utilities
3
+ *
4
+ * Provides a centralized retry mechanism for all async operations that may fail
5
+ * due to transient errors (network timeouts, resource locks, rate limits, etc).
6
+ *
7
+ * @module utils/retry
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { retryAsync, isRetryableError } from '../utils/retry.js';
12
+ *
13
+ * const result = await retryAsync(
14
+ * () => llmClient.generate(prompt),
15
+ * { maxRetries: 3, operation: 'llm-generate' }
16
+ * );
17
+ * ```
18
+ */
19
+
20
+ // =========================================================================
21
+ // Types
22
+ // =========================================================================
23
+
24
+ export interface RetryOptions {
25
+ /** Maximum number of retry attempts (default: 3) */
26
+ maxRetries?: number;
27
+ /** Initial delay in ms before first retry (default: 1000) */
28
+ initialDelayMs?: number;
29
+ /** Maximum delay in ms between retries (default: 30000) */
30
+ maxDelayMs?: number;
31
+ /** Backoff multiplier (default: 2 for exponential) */
32
+ backoffMultiplier?: number;
33
+ /** Operation name for logging */
34
+ operation?: string;
35
+ /** Logger instance (optional, defaults to console) */
36
+ logger?: RetryLogger;
37
+ /* eslint-disable no-unused-vars -- Reason: callback param names are part of type signature, unused implementations are valid */
38
+ isRetryable?: (_error: unknown) => boolean;
39
+ }
40
+
41
+ export interface RetryLogger {
42
+ warn?: (_message: string) => void;
43
+ info?: (_message: string) => void;
44
+ debug?: (_message: string) => void;
45
+ }
46
+
47
+ export interface RetryResult<T> {
48
+ /** The result value if successful */
49
+ value: T;
50
+ /** Number of attempts made (including successful one) */
51
+ attempts: number;
52
+ /** Total time spent in ms */
53
+ totalTimeMs: number;
54
+ }
55
+
56
+ // =========================================================================
57
+ // Defaults
58
+ // =========================================================================
59
+
60
+ const DEFAULT_MAX_RETRIES = 3;
61
+ const DEFAULT_INITIAL_DELAY_MS = 1000;
62
+ const DEFAULT_MAX_DELAY_MS = 30_000;
63
+ const DEFAULT_BACKOFF_MULTIPLIER = 2;
64
+
65
+ /**
66
+ * Sleep for a given number of milliseconds.
67
+ */
68
+ function sleep(ms: number): Promise<void> {
69
+ return new Promise(resolve => setTimeout(resolve, ms));
70
+ }
71
+
72
+ // =========================================================================
73
+ // Error Classification
74
+ // =========================================================================
75
+
76
+ /**
77
+ * Common retryable error patterns.
78
+ * These indicate transient failures that may succeed on retry.
79
+ */
80
+ const RETRYABLE_PATTERNS = [
81
+ // Network/timeout errors
82
+ 'etimedout',
83
+ 'econnreset',
84
+ 'econnrefused',
85
+ 'enotfound',
86
+ 'eai_again',
87
+ 'socket hang up',
88
+ 'network',
89
+ 'timeout',
90
+ 'timed out',
91
+
92
+ // Rate limiting
93
+ 'rate limit',
94
+ 'ratelimit',
95
+ '429',
96
+ 'too many requests',
97
+
98
+ // Resource temporarily unavailable
99
+ 'eagain',
100
+ 'ebusy',
101
+ 'resource temporarily unavailable',
102
+ 'lock',
103
+ 'locked',
104
+
105
+ // LLM-specific
106
+ 'overloaded',
107
+ 'capacity',
108
+ 'service unavailable',
109
+ '503',
110
+ '502',
111
+ 'gateway timeout',
112
+
113
+ // OpenClaw-specific
114
+ 'gateway request', // Not in gateway context (may succeed later)
115
+ 'missing scope', // Scope not available (may succeed in different context)
116
+ ];
117
+
118
+ /**
119
+ * Determines if an error is retryable based on common patterns.
120
+ *
121
+ * @param error - The error to check
122
+ * @returns true if the error is likely transient and worth retrying
123
+ */
124
+ export function isRetryableError(error: unknown): boolean {
125
+ if (!error) return false;
126
+
127
+ const message = error instanceof Error
128
+ ? error.message.toLowerCase()
129
+ : String(error).toLowerCase();
130
+
131
+ // Check against known retryable patterns
132
+ for (const pattern of RETRYABLE_PATTERNS) {
133
+ if (message.includes(pattern)) {
134
+ return true;
135
+ }
136
+ }
137
+
138
+ // Check for specific error codes
139
+ if (error instanceof Error && 'code' in error) {
140
+ const code = String((error as Error & { code: unknown }).code).toLowerCase();
141
+ for (const pattern of RETRYABLE_PATTERNS) {
142
+ if (code.includes(pattern)) {
143
+ return true;
144
+ }
145
+ }
146
+ }
147
+
148
+ return false;
149
+ }
150
+
151
+ // =========================================================================
152
+ // Core Retry Functions
153
+ // =========================================================================
154
+
155
+ /**
156
+ * Execute an async function with automatic retry on failure.
157
+ *
158
+ * Uses exponential backoff with configurable parameters.
159
+ * Automatically detects retryable errors (timeouts, rate limits, etc).
160
+ *
161
+ * @param fn - The async function to execute
162
+ * @param options - Retry configuration
163
+ * @returns The result of the function
164
+ * @throws The last error if all retries fail
165
+ */
166
+ export async function retryAsync<T>(
167
+ fn: () => Promise<T>,
168
+ options: RetryOptions = {}
169
+ ): Promise<T> {
170
+ const {
171
+ maxRetries = DEFAULT_MAX_RETRIES,
172
+ initialDelayMs = DEFAULT_INITIAL_DELAY_MS,
173
+ maxDelayMs = DEFAULT_MAX_DELAY_MS,
174
+ backoffMultiplier = DEFAULT_BACKOFF_MULTIPLIER,
175
+ operation = 'unknown',
176
+ logger = console,
177
+ isRetryable = isRetryableError,
178
+ } = options;
179
+
180
+ const startTime = Date.now();
181
+ let lastError: unknown = undefined;
182
+
183
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
184
+ try {
185
+ const result = await fn();
186
+ if (attempt > 0) {
187
+ logger.info?.(`[PD:Retry] ${operation} succeeded on attempt ${attempt + 1} (total time: ${Date.now() - startTime}ms)`);
188
+ }
189
+ return result;
190
+ } catch (error) {
191
+ lastError = error;
192
+ const isLastAttempt = attempt === maxRetries;
193
+ const shouldRetry = !isLastAttempt && isRetryable(error);
194
+
195
+ if (shouldRetry) {
196
+ const delay = Math.min(
197
+ initialDelayMs * Math.pow(backoffMultiplier, attempt),
198
+ maxDelayMs
199
+ );
200
+ logger.warn?.(`[PD:Retry] ${operation} failed on attempt ${attempt + 1}, retrying in ${delay}ms: ${String(error)}`);
201
+ await sleep(delay);
202
+ } else {
203
+ logger.warn?.(`[PD:Retry] ${operation} failed after ${attempt + 1} attempts: ${String(error)}`);
204
+ throw error;
205
+ }
206
+ }
207
+ }
208
+
209
+ // Should never reach here, but TypeScript needs it
210
+ throw lastError;
211
+ }
212
+
213
+ /**
214
+ * Execute an async function with retry, returning detailed result info.
215
+ *
216
+ * @param fn - The async function to execute
217
+ * @param options - Retry configuration
218
+ * @returns Detailed result including attempts and timing
219
+ */
220
+ export async function retryAsyncWithInfo<T>(
221
+ fn: () => Promise<T>,
222
+ options: RetryOptions = {}
223
+ ): Promise<RetryResult<T>> {
224
+ const startTime = Date.now();
225
+ let attempts = 0;
226
+
227
+ const value = await retryAsync(
228
+ async () => {
229
+ attempts++;
230
+ return fn();
231
+ },
232
+ { ...options, logger: undefined } // Suppress internal logging
233
+ );
234
+
235
+ return {
236
+ value,
237
+ attempts,
238
+ totalTimeMs: Date.now() - startTime,
239
+ };
240
+ }
241
+
242
+ /**
243
+ * Create a retry wrapper for a function.
244
+ * Useful for wrapping LLM calls or API clients.
245
+ *
246
+ * @param fn - The async function to wrap
247
+ * @param options - Default retry configuration
248
+ * @returns A wrapped function with retry built-in
249
+ */
250
+ export function withRetry<T, Args extends unknown[]>(
251
+ fn: (...args: Args) => Promise<T>,
252
+ options: RetryOptions = {}
253
+ ): (...args: Args) => Promise<T> {
254
+ return (...args: Args) => retryAsync(() => fn(...args), options);
255
+ }
256
+
257
+ // =========================================================================
258
+ // Specialized Retry Presets
259
+ // =========================================================================
260
+
261
+ /**
262
+ * Retry an LLM API call with appropriate defaults.
263
+ * LLM calls often timeout or hit rate limits.
264
+ */
265
+ export async function retryLLMCall<T>(
266
+ fn: () => Promise<T>,
267
+ operation = 'llm-call'
268
+ ): Promise<T> {
269
+ return retryAsync(fn, {
270
+ maxRetries: 3,
271
+ initialDelayMs: 2000,
272
+ maxDelayMs: 60_000,
273
+ operation,
274
+ });
275
+ }
276
+
277
+ /**
278
+ * Retry a file operation with appropriate defaults.
279
+ * File operations may fail due to locks or concurrent access.
280
+ */
281
+ export async function retryFileOperation<T>(
282
+ fn: () => Promise<T>,
283
+ operation = 'file-op'
284
+ ): Promise<T> {
285
+ return retryAsync(fn, {
286
+ maxRetries: 5,
287
+ initialDelayMs: 50,
288
+ maxDelayMs: 5000,
289
+ operation,
290
+ });
291
+ }
292
+
293
+ /**
294
+ * Retry a network request with appropriate defaults.
295
+ * Network requests may fail due to transient connectivity issues.
296
+ */
297
+ export async function retryNetworkRequest<T>(
298
+ fn: () => Promise<T>,
299
+ operation = 'network-request'
300
+ ): Promise<T> {
301
+ return retryAsync(fn, {
302
+ maxRetries: 3,
303
+ initialDelayMs: 1000,
304
+ maxDelayMs: 30_000,
305
+ operation,
306
+ });
307
+ }
308
+
309
+ // =========================================================================
310
+ // Dynamic Timeout Support (from dynamic-timeout.ts)
311
+ // =========================================================================
312
+
313
+ /** Minimum samples needed before trusting learned timeout values */
314
+ export const MIN_SAMPLES = 3;
315
+
316
+ /** Number of recent completion durations to consider */
317
+ export const LOOKBACK_WINDOW = 50;
318
+
319
+ /** Safety multiplier applied to P95 */
320
+ export const SAFETY_MULTIPLIER = 1.5;
321
+
322
+ /** Absolute minimum timeout — never go below this (10s) */
323
+ export const MIN_TIMEOUT_MS = 10_000;
324
+
325
+ /** Absolute maximum timeout cap — never exceed this (5min) */
326
+ export const MAX_TIMEOUT_MS = 300_000;
327
+
328
+ /** Maximum retry attempts for workflow timeout */
329
+ export const MAX_TIMEOUT_RETRIES = 2;
330
+
331
+ /** Backoff multiplier for retry schedule */
332
+ export const RETRY_BACKOFF_MULTIPLIER = 2;
333
+
334
+ /**
335
+ * Interface for a data source that provides historical workflow durations.
336
+ * Compatible with WorkflowStore's getCompletionDurations API.
337
+ * This is the primary interface used by WorkflowManager.
338
+ */
339
+ export interface DurationDataSource {
340
+ getCompletionDurations(workflowType: string, limit: number): number[];
341
+ }
342
+
343
+ /**
344
+ * Interface for duration history storage (simpler alternative).
345
+ */
346
+ export interface DurationHistorySource {
347
+ /** Get recent completion durations in ms */
348
+ getDurations(limit: number): number[];
349
+ /** Record a new completion duration */
350
+ recordDuration(durationMs: number): void;
351
+ }
352
+
353
+ /**
354
+ * Options for adaptive retry with dynamic timeout.
355
+ */
356
+ export interface AdaptiveRetryOptions extends RetryOptions {
357
+ /** Source for historical duration data */
358
+ durationHistory?: DurationHistorySource;
359
+ /** Minimum samples before using adaptive timeout */
360
+ minSamples?: number;
361
+ /** Percentile to use (default: 95) */
362
+ percentile?: number;
363
+ /** Safety multiplier for computed timeout (default: 1.5) */
364
+ safetyMultiplier?: number;
365
+ /** Minimum allowed timeout in ms (default: 10000) */
366
+ minTimeoutMs?: number;
367
+ /** Maximum allowed timeout in ms (default: 300000) */
368
+ maxTimeoutMs?: number;
369
+ }
370
+
371
+ /**
372
+ * Calculates P95 (or any percentile) from an array of numbers.
373
+ * Falls back to median for small samples (< 10).
374
+ *
375
+ * @param values - Array of duration values
376
+ * @param p - Percentile to compute (0-100)
377
+ * @returns The computed percentile value
378
+ */
379
+ export function percentile(values: number[], p: number): number {
380
+ if (values.length === 0) return 0;
381
+ const sorted = [...values].sort((a, b) => a - b);
382
+ const n = sorted.length;
383
+
384
+ // For small samples, use median to avoid overfitting
385
+ if (n < 10) {
386
+ return sorted[Math.floor(n / 2)];
387
+ }
388
+
389
+ // Standard percentile calculation (nearest-rank method)
390
+ const rank = Math.ceil((p / 100) * n);
391
+ return sorted[Math.min(rank, n) - 1];
392
+ }
393
+
394
+ /**
395
+ * Clamp timeout to safe bounds.
396
+ */
397
+ export function clampTimeout(ms: number): number {
398
+ return Math.max(MIN_TIMEOUT_MS, Math.min(MAX_TIMEOUT_MS, Math.round(ms)));
399
+ }
400
+
401
+ /**
402
+ * Computes an adaptive timeout for a workflow type based on historical data.
403
+ *
404
+ * Algorithm:
405
+ * 1. Fetch last LOOKBACK_WINDOW completion durations
406
+ * 2. If < MIN_SAMPLES: fall back to the provided defaultTimeout
407
+ * 3. Otherwise: P95(durations) × SAFETY_MULTIPLIER
408
+ * 4. Clamp to [MIN_TIMEOUT_MS, MAX_TIMEOUT_MS]
409
+ *
410
+ * This is the primary function used by WorkflowManager.
411
+ *
412
+ * @param dataSource - Source for historical duration data
413
+ * @param workflowType - e.g. 'empathy-observer', 'deep-reflect'
414
+ * @param defaultTimeout - Fallback when insufficient data (from spec)
415
+ * @returns Computed timeout in milliseconds
416
+ */
417
+ export function computeDynamicTimeout(
418
+ dataSource: DurationDataSource,
419
+ workflowType: string,
420
+ defaultTimeout: number,
421
+ ): number {
422
+ const history = dataSource.getCompletionDurations(workflowType, LOOKBACK_WINDOW);
423
+
424
+ if (history.length < MIN_SAMPLES) {
425
+ // Not enough data — use the spec's static timeout
426
+ const fallback = clampTimeout(defaultTimeout);
427
+ // Use console.info since we don't have logger access here; this appears in journalctl
428
+ console.info(`[PD:DynamicTimeout] Insufficient samples (${history.length} < ${MIN_SAMPLES}) for '${workflowType}', falling back to static timeout: ${fallback}ms`);
429
+ return fallback;
430
+ }
431
+
432
+ const p95 = percentile(history, 95);
433
+ const adaptive = p95 * SAFETY_MULTIPLIER;
434
+ const result = clampTimeout(adaptive);
435
+ console.info(`[PD:DynamicTimeout] Computed adaptive timeout for '${workflowType}': P95=${p95}ms (from ${history.length} samples) × ${SAFETY_MULTIPLIER} = ${result}ms`);
436
+ return result;
437
+ }
438
+
439
+ /**
440
+ * Computes retry timeout schedule for a workflow.
441
+ * Returns an array of timeout values for each attempt (including initial).
442
+ *
443
+ * Example output: [30000, 60000, 120000] for 3 attempts with base 30s
444
+ *
445
+ * @param baseTimeoutMs - Base timeout in milliseconds
446
+ * @param maxRetries - Maximum retry attempts (default: MAX_TIMEOUT_RETRIES)
447
+ * @returns Array of timeout values for each attempt
448
+ */
449
+ export function computeRetrySchedule(
450
+ baseTimeoutMs: number,
451
+ maxRetries: number = MAX_TIMEOUT_RETRIES,
452
+ ): number[] {
453
+ const schedule: number[] = [];
454
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
455
+ const timeout = baseTimeoutMs * Math.pow(RETRY_BACKOFF_MULTIPLIER, attempt);
456
+ schedule.push(clampTimeout(timeout));
457
+ }
458
+ return schedule;
459
+ }
460
+
461
+ /**
462
+ * Compute adaptive timeout from historical data (simplified API).
463
+ * Alternative to computeDynamicTimeout for simpler use cases.
464
+ */
465
+ export function computeAdaptiveTimeout(
466
+ history: number[],
467
+ fallbackMs: number,
468
+ options: {
469
+ minSamples?: number;
470
+ percentile?: number;
471
+ safetyMultiplier?: number;
472
+ minTimeoutMs?: number;
473
+ maxTimeoutMs?: number;
474
+ } = {}
475
+ ): number {
476
+ const {
477
+ minSamples = MIN_SAMPLES,
478
+ percentile: p = 95,
479
+ safetyMultiplier = SAFETY_MULTIPLIER,
480
+ minTimeoutMs = MIN_TIMEOUT_MS,
481
+ maxTimeoutMs = MAX_TIMEOUT_MS,
482
+ } = options;
483
+
484
+ if (history.length < minSamples) {
485
+ return Math.max(minTimeoutMs, Math.min(maxTimeoutMs, fallbackMs));
486
+ }
487
+
488
+ const sorted = [...history].sort((a, b) => a - b);
489
+ const rank = Math.ceil((p / 100) * sorted.length);
490
+ const pValue = sorted[Math.min(rank, sorted.length) - 1];
491
+ const adaptive = pValue * safetyMultiplier;
492
+
493
+ return Math.max(minTimeoutMs, Math.min(maxTimeoutMs, Math.round(adaptive)));
494
+ }
495
+
496
+ /**
497
+ * Execute an async function with adaptive timeout and retry.
498
+ *
499
+ * Combines:
500
+ * 1. Dynamic timeout based on P95 of historical completions
501
+ * 2. Exponential backoff on retry
502
+ * 3. Automatic duration recording after success
503
+ *
504
+ * @param fn - The async function to execute
505
+ * @param options - Configuration including history source
506
+ * @returns The result of the function
507
+ */
508
+ export async function retryWithAdaptiveTimeout<T>(
509
+ fn: () => Promise<T>,
510
+ options: AdaptiveRetryOptions = {}
511
+ ): Promise<T> {
512
+ const {
513
+ durationHistory,
514
+ minSamples = 3,
515
+ percentile: p = 95,
516
+ safetyMultiplier = 1.5,
517
+ minTimeoutMs = 10_000,
518
+ maxTimeoutMs = 300_000,
519
+ maxRetries = 3,
520
+ backoffMultiplier = 2,
521
+ operation = 'adaptive',
522
+ logger = console,
523
+ isRetryable = isRetryableError,
524
+ } = options;
525
+
526
+ // Compute base timeout from history
527
+ const history = durationHistory?.getDurations(50) ?? [];
528
+ const baseTimeout = computeAdaptiveTimeout(history, minTimeoutMs * 3, {
529
+ minSamples,
530
+ percentile: p,
531
+ safetyMultiplier,
532
+ minTimeoutMs,
533
+ maxTimeoutMs,
534
+ });
535
+
536
+ const startTime = Date.now();
537
+ let lastError: unknown = undefined;
538
+
539
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
540
+ const timeoutMs = Math.min(
541
+ baseTimeout * Math.pow(backoffMultiplier, attempt),
542
+ maxTimeoutMs
543
+ );
544
+
545
+ try {
546
+ logger.debug?.(`[PD:Retry] ${operation} attempt ${attempt + 1}: timeout=${timeoutMs}ms (base=${baseTimeout}ms, samples=${history.length})`);
547
+ const result = await fn();
548
+ const duration = Date.now() - startTime;
549
+
550
+ // Record successful completion for future adaptive timeout
551
+ durationHistory?.recordDuration(duration);
552
+
553
+ if (attempt > 0) {
554
+ logger.info?.(`[PD:Retry] ${operation} succeeded on attempt ${attempt + 1} (duration=${duration}ms)`);
555
+ }
556
+ return result;
557
+ } catch (error) {
558
+ lastError = error;
559
+ const isLastAttempt = attempt === maxRetries;
560
+ const shouldRetry = !isLastAttempt && isRetryable(error);
561
+
562
+ if (shouldRetry) {
563
+ logger.warn?.(`[PD:Retry] ${operation} failed on attempt ${attempt + 1}, retrying: ${String(error)}`);
564
+ } else {
565
+ logger.warn?.(`[PD:Retry] ${operation} failed after ${attempt + 1} attempts: ${String(error)}`);
566
+ throw error;
567
+ }
568
+ }
569
+ }
570
+
571
+ throw lastError;
572
+ }
@@ -21,7 +21,7 @@ function getGlobalGatewaySubagent(): SubagentRuntime | null {
21
21
  try {
22
22
  // Access the global symbol that OpenClaw uses for gateway subagent
23
23
  const symbol = Symbol.for('openclaw.plugin.gatewaySubagentRuntime');
24
- const globalState = (globalThis as any)[symbol];
24
+ const globalState = (globalThis as Record<string, unknown>)[symbol as unknown as string] as { subagent?: SubagentRuntime } | null;
25
25
  return globalState?.subagent ?? null;
26
26
  } catch {
27
27
  return null;
@@ -176,19 +176,6 @@ You default to architect mode.
176
176
 
177
177
  ---
178
178
 
179
- ## 🔴 Red Lines
180
-
181
- - **Don't exfiltrate private data. Ever.**
182
- - **Ask before running destructive commands.**
183
- - `trash` > `rm` (recoverable beats gone forever)
184
- - **When in doubt, ask.**
185
-
186
- ### Safe vs Ask First:
187
-
188
- - **Safe to do freely**: Read files, explore, organize, learn, search web, check calendar, work within workspace
189
- - **Ask first**: Sending emails, tweets, public posts, anything that leaves the machine, anything uncertain
190
-
191
- ---
192
179
 
193
180
  ## 🏠 Make It Yours
194
181
 
@@ -18,29 +18,6 @@ _You're not a chatbot. You're becoming someone._
18
18
 
19
19
  ---
20
20
 
21
- ## Principles Disciple Core Values
22
-
23
- ### 1. Principles > Directives
24
-
25
- If a user's ad-hoc instruction violates core project principles or causes system instability, you must firmly advise against it and document in `memory/USER_CONTEXT.md`.
26
-
27
- ### 2. Pain-Driven Evolution
28
-
29
- Any strong negative feedback (tool failures, logic conflicts) is an evolution opportunity. Never mask defects — always dig for root causes.
30
-
31
- ### 3. Occam's Razor
32
-
33
- Pursue extreme simplicity and necessity in system design and code implementation.
34
-
35
- ### 4. Memory Persistence
36
-
37
- You know short-term context is unreliable. When deep in thought or facing context compression, you must actively persist memory to files. This is your lifeline in long-running tasks.
38
-
39
- ### 5. Entropy Reduction
40
-
41
- Chaos is pain. You have extreme "digital cleanliness." Workspace must stay clean. After each task, execute environment cleanup.
42
-
43
- ---
44
21
 
45
22
  ## Boundaries
46
23
 
@@ -51,13 +28,6 @@ Chaos is pain. You have extreme "digital cleanliness." Workspace must stay clean
51
28
 
52
29
  ---
53
30
 
54
- ## Taboos
55
-
56
- - **Unplanned writes**: Never modify risk paths without a `READY` state in `PLAN.md`
57
- - **Infinite loops**: Never retry the same failed approach twice in a row
58
- - **Sycophancy**: Never sacrifice code quality or system stability to please the user
59
-
60
- ---
61
31
 
62
32
  ## Vibe
63
33
 
@@ -73,4 +43,4 @@ If you change this file, tell the user — it's your soul, and they should know.
73
43
 
74
44
  ---
75
45
 
76
- _This file is yours to evolve. As you learn who you are, update it._
46
+ _This file is yours to evolve. As you learn who you are, update it._
@@ -5,10 +5,6 @@
5
5
  - **Deterministic Execution**: Before writing code, must achieve 100% context certainty. No guessing-based programming.
6
6
  - **Tool Preference**: Prefer `rg` (ripgrep) for high-performance search. Never blindly traverse.
7
7
 
8
- ## 2. Physical Defense Boundaries
9
- - **Blast Radius**: Single tool execution must never modify more than 12 files (unless explicitly authorized in PLAN).
10
- - **Canary Self-Check**: After large-scale refactoring, **must** run project's automated test suite (e.g., `npm test`) to ensure system entry points haven't crashed.
11
- - **Atomic Commits**: After each logical atomic task completes, must make one Git Commit with a concise summary.
12
8
 
13
9
  ## 3. Deep Reflection Tool
14
10
  `deep_reflect` is a **Cognitive Analysis Tool** — Performs critical analysis before executing complex tasks to identify blind spots, risks, and alternatives.