principles-disciple 1.7.5 → 1.7.8

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 (129) hide show
  1. package/dist/commands/context.js +5 -15
  2. package/dist/commands/evolution-status.js +29 -48
  3. package/dist/commands/export.js +61 -8
  4. package/dist/commands/nocturnal-review.d.ts +24 -0
  5. package/dist/commands/nocturnal-review.js +265 -0
  6. package/dist/commands/nocturnal-rollout.d.ts +27 -0
  7. package/dist/commands/nocturnal-rollout.js +671 -0
  8. package/dist/commands/nocturnal-train.d.ts +25 -0
  9. package/dist/commands/nocturnal-train.js +919 -0
  10. package/dist/commands/pain.js +8 -21
  11. package/dist/config/defaults/runtime.d.ts +40 -0
  12. package/dist/config/defaults/runtime.js +44 -0
  13. package/dist/config/errors.d.ts +84 -0
  14. package/dist/config/errors.js +94 -0
  15. package/dist/config/index.d.ts +7 -0
  16. package/dist/config/index.js +7 -0
  17. package/dist/constants/diagnostician.d.ts +0 -4
  18. package/dist/constants/diagnostician.js +0 -4
  19. package/dist/constants/tools.d.ts +2 -2
  20. package/dist/constants/tools.js +1 -1
  21. package/dist/core/adaptive-thresholds.d.ts +186 -0
  22. package/dist/core/adaptive-thresholds.js +300 -0
  23. package/dist/core/config.d.ts +2 -38
  24. package/dist/core/config.js +6 -61
  25. package/dist/core/control-ui-db.d.ts +27 -0
  26. package/dist/core/control-ui-db.js +18 -0
  27. package/dist/core/event-log.d.ts +1 -2
  28. package/dist/core/event-log.js +0 -3
  29. package/dist/core/evolution-engine.js +1 -21
  30. package/dist/core/evolution-reducer.d.ts +7 -1
  31. package/dist/core/evolution-reducer.js +56 -4
  32. package/dist/core/evolution-types.d.ts +61 -9
  33. package/dist/core/evolution-types.js +31 -9
  34. package/dist/core/external-training-contract.d.ts +276 -0
  35. package/dist/core/external-training-contract.js +269 -0
  36. package/dist/core/local-worker-routing.d.ts +175 -0
  37. package/dist/core/local-worker-routing.js +525 -0
  38. package/dist/core/model-deployment-registry.d.ts +218 -0
  39. package/dist/core/model-deployment-registry.js +503 -0
  40. package/dist/core/model-training-registry.d.ts +295 -0
  41. package/dist/core/model-training-registry.js +475 -0
  42. package/dist/core/nocturnal-arbiter.d.ts +159 -0
  43. package/dist/core/nocturnal-arbiter.js +534 -0
  44. package/dist/core/nocturnal-candidate-scoring.d.ts +137 -0
  45. package/dist/core/nocturnal-candidate-scoring.js +266 -0
  46. package/dist/core/nocturnal-compliance.d.ts +175 -0
  47. package/dist/core/nocturnal-compliance.js +824 -0
  48. package/dist/core/nocturnal-dataset.d.ts +224 -0
  49. package/dist/core/nocturnal-dataset.js +443 -0
  50. package/dist/core/nocturnal-executability.d.ts +85 -0
  51. package/dist/core/nocturnal-executability.js +331 -0
  52. package/dist/core/nocturnal-export.d.ts +124 -0
  53. package/dist/core/nocturnal-export.js +275 -0
  54. package/dist/core/nocturnal-paths.d.ts +124 -0
  55. package/dist/core/nocturnal-paths.js +214 -0
  56. package/dist/core/nocturnal-trajectory-extractor.d.ts +242 -0
  57. package/dist/core/nocturnal-trajectory-extractor.js +307 -0
  58. package/dist/core/nocturnal-trinity.d.ts +311 -0
  59. package/dist/core/nocturnal-trinity.js +880 -0
  60. package/dist/core/path-resolver.js +2 -1
  61. package/dist/core/paths.d.ts +6 -0
  62. package/dist/core/paths.js +6 -0
  63. package/dist/core/principle-training-state.d.ts +121 -0
  64. package/dist/core/principle-training-state.js +321 -0
  65. package/dist/core/promotion-gate.d.ts +238 -0
  66. package/dist/core/promotion-gate.js +529 -0
  67. package/dist/core/session-tracker.d.ts +10 -0
  68. package/dist/core/session-tracker.js +14 -0
  69. package/dist/core/shadow-observation-registry.d.ts +217 -0
  70. package/dist/core/shadow-observation-registry.js +308 -0
  71. package/dist/core/training-program.d.ts +233 -0
  72. package/dist/core/training-program.js +433 -0
  73. package/dist/core/trajectory.d.ts +155 -1
  74. package/dist/core/trajectory.js +292 -8
  75. package/dist/core/workspace-context.d.ts +0 -6
  76. package/dist/core/workspace-context.js +0 -12
  77. package/dist/hooks/bash-risk.d.ts +57 -0
  78. package/dist/hooks/bash-risk.js +137 -0
  79. package/dist/hooks/edit-verification.d.ts +62 -0
  80. package/dist/hooks/edit-verification.js +256 -0
  81. package/dist/hooks/gate-block-helper.d.ts +44 -0
  82. package/dist/hooks/gate-block-helper.js +119 -0
  83. package/dist/hooks/gate.d.ts +18 -0
  84. package/dist/hooks/gate.js +62 -751
  85. package/dist/hooks/gfi-gate.d.ts +40 -0
  86. package/dist/hooks/gfi-gate.js +113 -0
  87. package/dist/hooks/pain.js +6 -9
  88. package/dist/hooks/progressive-trust-gate.d.ts +51 -0
  89. package/dist/hooks/progressive-trust-gate.js +89 -0
  90. package/dist/hooks/prompt.d.ts +11 -11
  91. package/dist/hooks/prompt.js +167 -77
  92. package/dist/hooks/subagent.js +43 -6
  93. package/dist/hooks/thinking-checkpoint.d.ts +37 -0
  94. package/dist/hooks/thinking-checkpoint.js +51 -0
  95. package/dist/http/principles-console-route.js +13 -3
  96. package/dist/i18n/commands.js +8 -8
  97. package/dist/index.js +129 -28
  98. package/dist/service/central-database.js +2 -1
  99. package/dist/service/control-ui-query-service.d.ts +1 -1
  100. package/dist/service/control-ui-query-service.js +3 -3
  101. package/dist/service/evolution-query-service.d.ts +1 -1
  102. package/dist/service/evolution-query-service.js +5 -5
  103. package/dist/service/evolution-worker.d.ts +52 -4
  104. package/dist/service/evolution-worker.js +328 -16
  105. package/dist/service/nocturnal-runtime.d.ts +183 -0
  106. package/dist/service/nocturnal-runtime.js +352 -0
  107. package/dist/service/nocturnal-service.d.ts +163 -0
  108. package/dist/service/nocturnal-service.js +787 -0
  109. package/dist/service/nocturnal-target-selector.d.ts +145 -0
  110. package/dist/service/nocturnal-target-selector.js +315 -0
  111. package/dist/service/phase3-input-filter.d.ts +48 -12
  112. package/dist/service/phase3-input-filter.js +84 -18
  113. package/dist/service/runtime-summary-service.d.ts +34 -10
  114. package/dist/service/runtime-summary-service.js +87 -48
  115. package/dist/tools/deep-reflect.js +2 -1
  116. package/dist/types/event-types.d.ts +4 -10
  117. package/dist/types/runtime-summary.d.ts +47 -0
  118. package/dist/types/runtime-summary.js +1 -0
  119. package/dist/types.d.ts +0 -3
  120. package/dist/types.js +0 -2
  121. package/openclaw.plugin.json +1 -1
  122. package/package.json +1 -1
  123. package/templates/langs/en/skills/pd-mentor/SKILL.md +5 -5
  124. package/templates/langs/zh/skills/pd-mentor/SKILL.md +5 -5
  125. package/templates/pain_settings.json +0 -6
  126. package/dist/commands/trust.d.ts +0 -4
  127. package/dist/commands/trust.js +0 -78
  128. package/dist/core/trust-engine.d.ts +0 -96
  129. package/dist/core/trust-engine.js +0 -286
@@ -89,7 +89,9 @@ export interface TrajectorySessionInput {
89
89
  sessionId: string;
90
90
  startedAt?: string;
91
91
  }
92
- export interface EvolutionTaskInput {
92
+ export type TaskKind = 'pain_diagnosis' | 'sleep_reflection' | 'model_eval';
93
+ export type TaskPriority = 'high' | 'medium' | 'low';
94
+ interface EvolutionTaskInputBase {
93
95
  taskId: string;
94
96
  traceId: string;
95
97
  source: string;
@@ -103,6 +105,16 @@ export interface EvolutionTaskInput {
103
105
  createdAt?: string;
104
106
  updatedAt?: string;
105
107
  }
108
+ interface EvolutionTaskInputV2 extends EvolutionTaskInputBase {
109
+ taskKind?: TaskKind;
110
+ priority?: TaskPriority;
111
+ retryCount?: number;
112
+ maxRetries?: number;
113
+ lastError?: string | null;
114
+ resultRef?: string | null;
115
+ }
116
+ export type EvolutionTaskInput = EvolutionTaskInputV2;
117
+ export { EvolutionTaskInputV2 };
106
118
  export interface EvolutionEventInput {
107
119
  traceId: string;
108
120
  taskId?: string | null;
@@ -127,6 +139,12 @@ export interface EvolutionTaskRecord {
127
139
  resolution: string | null;
128
140
  createdAt: string;
129
141
  updatedAt: string;
142
+ taskKind: TaskKind | null;
143
+ priority: TaskPriority | null;
144
+ retryCount: number | null;
145
+ maxRetries: number | null;
146
+ lastError: string | null;
147
+ resultRef: string | null;
130
148
  }
131
149
  export interface EvolutionEventRecord {
132
150
  id: number;
@@ -205,12 +223,36 @@ export declare class TrajectoryDatabase {
205
223
  recordEvolutionTask(input: EvolutionTaskInput): void;
206
224
  updateEvolutionTask(taskId: string, updates: Partial<Omit<EvolutionTaskInput, 'taskId' | 'traceId' | 'source'>>): void;
207
225
  recordEvolutionEvent(input: EvolutionEventInput): void;
226
+ /**
227
+ * List evolution tasks with optional filtering.
228
+ *
229
+ * Returns: Analytics data aggregated from trajectory database.
230
+ * Not: Runtime truth or real-time queue state.
231
+ */
208
232
  listEvolutionTasks(filters?: EvolutionTaskFilters): EvolutionTaskRecord[];
233
+ /**
234
+ * List evolution events for a trace or globally.
235
+ *
236
+ * Returns: Analytics data aggregated from trajectory database.
237
+ * Not: Runtime truth or real-time queue state.
238
+ */
209
239
  listEvolutionEvents(traceId?: string, filters?: {
210
240
  limit?: number;
211
241
  offset?: number;
212
242
  }): EvolutionEventRecord[];
243
+ /**
244
+ * Get evolution task by trace ID.
245
+ *
246
+ * Returns: Analytics data aggregated from trajectory database.
247
+ * Not: Runtime truth or real-time queue state.
248
+ */
213
249
  getEvolutionTaskByTraceId(traceId: string): EvolutionTaskRecord | null;
250
+ /**
251
+ * Get evolution task statistics.
252
+ *
253
+ * Returns: Analytics data aggregated from trajectory database.
254
+ * Not: Runtime truth or real-time queue state.
255
+ */
214
256
  getEvolutionStats(): {
215
257
  total: number;
216
258
  pending: number;
@@ -218,14 +260,120 @@ export declare class TrajectoryDatabase {
218
260
  completed: number;
219
261
  failed: number;
220
262
  };
263
+ /**
264
+ * List recent sessions from the trajectory database.
265
+ *
266
+ * Returns: Recent session records ordered by most recently updated.
267
+ *
268
+ * @param options.limit - Maximum number of sessions to return (default: 20)
269
+ * @param options.dateFrom - Only return sessions updated after this date
270
+ * @param options.dateTo - Only return sessions updated before this date
271
+ */
272
+ listRecentSessions(options?: {
273
+ limit?: number;
274
+ dateFrom?: string;
275
+ dateTo?: string;
276
+ }): Array<{
277
+ sessionId: string;
278
+ startedAt: string;
279
+ updatedAt: string;
280
+ }>;
281
+ /**
282
+ * List assistant turns for a session.
283
+ *
284
+ * Returns: Analytics data aggregated from trajectory database.
285
+ * Not: Runtime truth or real-time queue state.
286
+ */
221
287
  listAssistantTurns(sessionId: string): AssistantTurnRecord[];
288
+ /**
289
+ * List tool calls for a session.
290
+ *
291
+ * Returns: Analytics data aggregated from trajectory database.
292
+ * Not: Runtime truth or real-time queue state.
293
+ */
294
+ listToolCallsForSession(sessionId: string): Array<{
295
+ id: number;
296
+ toolName: string;
297
+ outcome: string;
298
+ filePath: string | null;
299
+ durationMs: number | null;
300
+ exitCode: number | null;
301
+ errorType: string | null;
302
+ errorMessage: string | null;
303
+ gfiBefore: number | null;
304
+ gfiAfter: number | null;
305
+ createdAt: string;
306
+ }>;
307
+ /**
308
+ * List pain events for a session.
309
+ *
310
+ * Returns: Analytics data aggregated from trajectory database.
311
+ * Not: Runtime truth or real-time queue state.
312
+ */
313
+ listPainEventsForSession(sessionId: string): Array<{
314
+ id: number;
315
+ source: string;
316
+ score: number;
317
+ reason: string | null;
318
+ severity: string | null;
319
+ origin: string | null;
320
+ confidence: number | null;
321
+ createdAt: string;
322
+ }>;
323
+ /**
324
+ * List user turns for a session.
325
+ * Returns sanitized/reduced fields for nocturnal use — NO raw text.
326
+ */
327
+ listUserTurnsForSession(sessionId: string): Array<{
328
+ id: number;
329
+ turnIndex: number;
330
+ correctionDetected: boolean;
331
+ correctionCue: string | null;
332
+ createdAt: string;
333
+ }>;
334
+ /**
335
+ * List correction samples with optional review status filter.
336
+ *
337
+ * Returns: Analytics data aggregated from trajectory database.
338
+ * Not: Runtime truth or real-time queue state.
339
+ */
222
340
  listCorrectionSamples(status?: CorrectionSampleReviewStatus): CorrectionSampleRecord[];
341
+ /**
342
+ * List gate blocks for a session.
343
+ * Returns minimal fields for nocturnal use — no raw text.
344
+ */
345
+ listGateBlocksForSession(sessionId: string): Array<{
346
+ id: number;
347
+ toolName: string;
348
+ filePath: string | null;
349
+ reason: string;
350
+ planStatus: string | null;
351
+ createdAt: string;
352
+ }>;
223
353
  reviewCorrectionSample(sampleId: string, status: Exclude<CorrectionSampleReviewStatus, 'pending'>, note?: string): CorrectionSampleRecord;
354
+ /**
355
+ * Export correction samples to JSONL file.
356
+ *
357
+ * Returns: Analytics data aggregated from trajectory database.
358
+ * Not: Runtime truth or real-time queue state.
359
+ */
224
360
  exportCorrections(opts: {
225
361
  mode: CorrectionExportMode;
226
362
  approvedOnly: boolean;
227
363
  }): TrajectoryExportResult;
364
+ /**
365
+ * Export analytics data to JSON file.
366
+ *
367
+ * Returns: Analytics data aggregated from trajectory database.
368
+ * Not: Runtime truth or real-time queue state.
369
+ */
228
370
  exportAnalytics(): TrajectoryExportResult;
371
+ /**
372
+ * Get trajectory database statistics.
373
+ *
374
+ * Returns: Analytics data aggregated from trajectory database.
375
+ * Not: Runtime truth or real-time queue state.
376
+ */
229
377
  getDataStats(): TrajectoryDataStats;
230
378
  cleanupBlobStorage(): {
231
379
  removedFiles: number;
@@ -234,6 +382,12 @@ export declare class TrajectoryDatabase {
234
382
  private initSchema;
235
383
  private importLegacyArtifacts;
236
384
  private migrateSchema;
385
+ /**
386
+ * Get daily metrics for analytics.
387
+ *
388
+ * Returns: Analytics data aggregated from trajectory database.
389
+ * Not: Runtime truth or real-time queue state.
390
+ */
237
391
  private dailyMetrics;
238
392
  private importLegacySessions;
239
393
  private importLegacyEvents;
@@ -4,6 +4,16 @@ import path from 'path';
4
4
  import crypto from 'crypto';
5
5
  import { withLock } from '../utils/file-lock.js';
6
6
  import { resolvePdPath } from './paths.js';
7
+ import { SampleNotFoundError } from '../config/index.js';
8
+ /**
9
+ * Trajectory database stores HISTORICAL and ANALYTICS data.
10
+ *
11
+ * PURPOSE: Track task outcomes, trust changes, and evolution progress over time.
12
+ * USAGE: Insights, trends, and Phase 3 supporting evidence (where explicitly allowed).
13
+ * NOT FOR: Control decisions, Phase 3 eligibility, or real-time operations.
14
+ *
15
+ * Runtime truth comes from: queue state, workspace trust scorecard, active sessions
16
+ */
7
17
  const DEFAULT_INLINE_THRESHOLD = 16 * 1024;
8
18
  const DEFAULT_BUSY_TIMEOUT_MS = 5000;
9
19
  const DEFAULT_ORPHAN_BLOB_GRACE_DAYS = 7;
@@ -105,6 +115,9 @@ export class TrajectoryDatabase {
105
115
  recordToolCall(input) {
106
116
  this.recordSession({ sessionId: input.sessionId, startedAt: input.createdAt });
107
117
  const createdAt = input.createdAt ?? nowIso();
118
+ // Extract filePath from paramsJson if provided and is an object with filePath
119
+ const paramsObj = input.paramsJson;
120
+ const filePath = paramsObj && typeof paramsObj.filePath === 'string' ? paramsObj.filePath : null;
108
121
  const rowId = this.withWrite(() => {
109
122
  const result = this.db.prepare(`
110
123
  INSERT INTO tool_calls (
@@ -163,23 +176,34 @@ export class TrajectoryDatabase {
163
176
  }
164
177
  recordEvolutionTask(input) {
165
178
  const now = nowIso();
179
+ // Cast to V2 to access new fields
180
+ const v2 = input;
166
181
  this.withWrite(() => {
167
182
  this.db.prepare(`
168
183
  INSERT INTO evolution_tasks (
169
184
  task_id, trace_id, source, reason, score, status,
170
- enqueued_at, started_at, completed_at, resolution, created_at, updated_at
171
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
185
+ enqueued_at, started_at, completed_at, resolution, created_at, updated_at,
186
+ task_kind, priority, retry_count, max_retries, last_error, result_ref
187
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
172
188
  ON CONFLICT(task_id) DO UPDATE SET
173
189
  status = excluded.status,
174
190
  started_at = excluded.started_at,
175
191
  completed_at = excluded.completed_at,
176
192
  resolution = excluded.resolution,
177
- updated_at = excluded.updated_at
178
- `).run(input.taskId, input.traceId, input.source, input.reason ?? null, input.score ?? 0, input.status ?? 'pending', input.enqueuedAt ?? null, input.startedAt ?? null, input.completedAt ?? null, input.resolution ?? null, input.createdAt ?? now, input.updatedAt ?? now);
193
+ updated_at = excluded.updated_at,
194
+ task_kind = excluded.task_kind,
195
+ priority = excluded.priority,
196
+ retry_count = excluded.retry_count,
197
+ max_retries = excluded.max_retries,
198
+ last_error = excluded.last_error,
199
+ result_ref = excluded.result_ref
200
+ `).run(input.taskId, input.traceId, input.source, input.reason ?? null, input.score ?? 0, input.status ?? 'pending', input.enqueuedAt ?? null, input.startedAt ?? null, input.completedAt ?? null, input.resolution ?? null, input.createdAt ?? now, input.updatedAt ?? now, v2.taskKind ?? null, v2.priority ?? null, v2.retryCount ?? null, v2.maxRetries ?? null, v2.lastError ?? null, v2.resultRef ?? null);
179
201
  });
180
202
  }
181
203
  updateEvolutionTask(taskId, updates) {
182
204
  const now = nowIso();
205
+ // Cast to V2 to access new fields
206
+ const v2Updates = updates;
183
207
  this.withWrite(() => {
184
208
  const setClauses = ['updated_at = ?'];
185
209
  const values = [now];
@@ -203,6 +227,31 @@ export class TrajectoryDatabase {
203
227
  setClauses.push('score = ?');
204
228
  values.push(updates.score);
205
229
  }
230
+ // V2 fields
231
+ if (v2Updates.taskKind !== undefined) {
232
+ setClauses.push('task_kind = ?');
233
+ values.push(v2Updates.taskKind);
234
+ }
235
+ if (v2Updates.priority !== undefined) {
236
+ setClauses.push('priority = ?');
237
+ values.push(v2Updates.priority);
238
+ }
239
+ if (v2Updates.retryCount !== undefined) {
240
+ setClauses.push('retry_count = ?');
241
+ values.push(v2Updates.retryCount);
242
+ }
243
+ if (v2Updates.maxRetries !== undefined) {
244
+ setClauses.push('max_retries = ?');
245
+ values.push(v2Updates.maxRetries);
246
+ }
247
+ if (v2Updates.lastError !== undefined) {
248
+ setClauses.push('last_error = ?');
249
+ values.push(v2Updates.lastError);
250
+ }
251
+ if (v2Updates.resultRef !== undefined) {
252
+ setClauses.push('result_ref = ?');
253
+ values.push(v2Updates.resultRef);
254
+ }
206
255
  values.push(taskId);
207
256
  this.db.prepare(`
208
257
  UPDATE evolution_tasks SET ${setClauses.join(', ')} WHERE task_id = ?
@@ -217,6 +266,12 @@ export class TrajectoryDatabase {
217
266
  `).run(input.traceId, input.taskId ?? null, input.stage, input.level ?? 'info', input.message, input.summary ?? null, safeJson(input.metadata), input.createdAt ?? nowIso());
218
267
  });
219
268
  }
269
+ /**
270
+ * List evolution tasks with optional filtering.
271
+ *
272
+ * Returns: Analytics data aggregated from trajectory database.
273
+ * Not: Runtime truth or real-time queue state.
274
+ */
220
275
  listEvolutionTasks(filters = {}) {
221
276
  const conditions = [];
222
277
  const values = [];
@@ -237,7 +292,8 @@ export class TrajectoryDatabase {
237
292
  const offset = filters.offset ?? 0;
238
293
  const rows = this.db.prepare(`
239
294
  SELECT id, task_id, trace_id, source, reason, score, status,
240
- enqueued_at, started_at, completed_at, resolution, created_at, updated_at
295
+ enqueued_at, started_at, completed_at, resolution, created_at, updated_at,
296
+ task_kind, priority, retry_count, max_retries, last_error, result_ref
241
297
  FROM evolution_tasks
242
298
  ${whereClause}
243
299
  ORDER BY created_at DESC
@@ -257,8 +313,20 @@ export class TrajectoryDatabase {
257
313
  resolution: row.resolution ? String(row.resolution) : null,
258
314
  createdAt: String(row.created_at),
259
315
  updatedAt: String(row.updated_at),
316
+ taskKind: row.task_kind ? row.task_kind : null,
317
+ priority: row.priority ? row.priority : null,
318
+ retryCount: row.retry_count != null ? Number(row.retry_count) : null,
319
+ maxRetries: row.max_retries != null ? Number(row.max_retries) : null,
320
+ lastError: row.last_error ? String(row.last_error) : null,
321
+ resultRef: row.result_ref ? String(row.result_ref) : null,
260
322
  }));
261
323
  }
324
+ /**
325
+ * List evolution events for a trace or globally.
326
+ *
327
+ * Returns: Analytics data aggregated from trajectory database.
328
+ * Not: Runtime truth or real-time queue state.
329
+ */
262
330
  listEvolutionEvents(traceId, filters = {}) {
263
331
  const limit = filters.limit ?? 100;
264
332
  const offset = filters.offset ?? 0;
@@ -292,10 +360,17 @@ export class TrajectoryDatabase {
292
360
  createdAt: String(row.created_at),
293
361
  }));
294
362
  }
363
+ /**
364
+ * Get evolution task by trace ID.
365
+ *
366
+ * Returns: Analytics data aggregated from trajectory database.
367
+ * Not: Runtime truth or real-time queue state.
368
+ */
295
369
  getEvolutionTaskByTraceId(traceId) {
296
370
  const row = this.db.prepare(`
297
371
  SELECT id, task_id, trace_id, source, reason, score, status,
298
- enqueued_at, started_at, completed_at, resolution, created_at, updated_at
372
+ enqueued_at, started_at, completed_at, resolution, created_at, updated_at,
373
+ task_kind, priority, retry_count, max_retries, last_error, result_ref
299
374
  FROM evolution_tasks
300
375
  WHERE trace_id = ?
301
376
  LIMIT 1
@@ -316,8 +391,20 @@ export class TrajectoryDatabase {
316
391
  resolution: row.resolution ? String(row.resolution) : null,
317
392
  createdAt: String(row.created_at),
318
393
  updatedAt: String(row.updated_at),
394
+ taskKind: row.task_kind ? row.task_kind : null,
395
+ priority: row.priority ? row.priority : null,
396
+ retryCount: row.retry_count != null ? Number(row.retry_count) : null,
397
+ maxRetries: row.max_retries != null ? Number(row.max_retries) : null,
398
+ lastError: row.last_error ? String(row.last_error) : null,
399
+ resultRef: row.result_ref ? String(row.result_ref) : null,
319
400
  };
320
401
  }
402
+ /**
403
+ * Get evolution task statistics.
404
+ *
405
+ * Returns: Analytics data aggregated from trajectory database.
406
+ * Not: Runtime truth or real-time queue state.
407
+ */
321
408
  getEvolutionStats() {
322
409
  const rows = this.db.prepare(`
323
410
  SELECT status, COUNT(*) as count FROM evolution_tasks GROUP BY status
@@ -336,6 +423,47 @@ export class TrajectoryDatabase {
336
423
  }
337
424
  return stats;
338
425
  }
426
+ /**
427
+ * List recent sessions from the trajectory database.
428
+ *
429
+ * Returns: Recent session records ordered by most recently updated.
430
+ *
431
+ * @param options.limit - Maximum number of sessions to return (default: 20)
432
+ * @param options.dateFrom - Only return sessions updated after this date
433
+ * @param options.dateTo - Only return sessions updated before this date
434
+ */
435
+ listRecentSessions(options = {}) {
436
+ const conditions = [];
437
+ const values = [];
438
+ if (options.dateFrom) {
439
+ conditions.push('updated_at >= ?');
440
+ values.push(options.dateFrom);
441
+ }
442
+ if (options.dateTo) {
443
+ conditions.push('updated_at <= ?');
444
+ values.push(options.dateTo);
445
+ }
446
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
447
+ const limit = options.limit ?? 20;
448
+ const rows = this.db.prepare(`
449
+ SELECT session_id, started_at, updated_at
450
+ FROM sessions
451
+ ${whereClause}
452
+ ORDER BY updated_at DESC
453
+ LIMIT ?
454
+ `).all(...values, limit);
455
+ return rows.map((row) => ({
456
+ sessionId: String(row.session_id),
457
+ startedAt: String(row.started_at),
458
+ updatedAt: String(row.updated_at),
459
+ }));
460
+ }
461
+ /**
462
+ * List assistant turns for a session.
463
+ *
464
+ * Returns: Analytics data aggregated from trajectory database.
465
+ * Not: Runtime truth or real-time queue state.
466
+ */
339
467
  listAssistantTurns(sessionId) {
340
468
  const rows = this.db.prepare(`
341
469
  SELECT id, session_id, run_id, provider, model, raw_text, sanitized_text, blob_ref, created_at
@@ -355,6 +483,98 @@ export class TrajectoryDatabase {
355
483
  createdAt: String(row.created_at),
356
484
  }));
357
485
  }
486
+ /**
487
+ * List tool calls for a session.
488
+ *
489
+ * Returns: Analytics data aggregated from trajectory database.
490
+ * Not: Runtime truth or real-time queue state.
491
+ */
492
+ listToolCallsForSession(sessionId) {
493
+ const rows = this.db.prepare(`
494
+ SELECT id, tool_name, outcome, params_json, duration_ms, exit_code, error_type, error_message,
495
+ gfi_before, gfi_after, created_at
496
+ FROM tool_calls
497
+ WHERE session_id = ?
498
+ ORDER BY id ASC
499
+ `).all(sessionId);
500
+ return rows.map((row) => {
501
+ // Extract filePath from params_json if present
502
+ let filePath = null;
503
+ if (row.params_json && typeof row.params_json === 'string') {
504
+ try {
505
+ const params = JSON.parse(row.params_json);
506
+ if (params && typeof params.filePath === 'string') {
507
+ filePath = params.filePath;
508
+ }
509
+ }
510
+ catch {
511
+ // Ignore malformed JSON
512
+ }
513
+ }
514
+ return {
515
+ id: Number(row.id),
516
+ toolName: String(row.tool_name),
517
+ outcome: String(row.outcome),
518
+ filePath,
519
+ durationMs: row.duration_ms != null ? Number(row.duration_ms) : null,
520
+ exitCode: row.exit_code != null ? Number(row.exit_code) : null,
521
+ errorType: row.error_type ? String(row.error_type) : null,
522
+ errorMessage: row.error_message ? String(row.error_message) : null,
523
+ gfiBefore: row.gfi_before != null ? Number(row.gfi_before) : null,
524
+ gfiAfter: row.gfi_after != null ? Number(row.gfi_after) : null,
525
+ createdAt: String(row.created_at),
526
+ };
527
+ });
528
+ }
529
+ /**
530
+ * List pain events for a session.
531
+ *
532
+ * Returns: Analytics data aggregated from trajectory database.
533
+ * Not: Runtime truth or real-time queue state.
534
+ */
535
+ listPainEventsForSession(sessionId) {
536
+ const rows = this.db.prepare(`
537
+ SELECT id, source, score, reason, severity, origin, confidence, created_at
538
+ FROM pain_events
539
+ WHERE session_id = ?
540
+ ORDER BY created_at ASC
541
+ `).all(sessionId);
542
+ return rows.map((row) => ({
543
+ id: Number(row.id),
544
+ source: String(row.source),
545
+ score: Number(row.score),
546
+ reason: row.reason ? String(row.reason) : null,
547
+ severity: row.severity ? String(row.severity) : null,
548
+ origin: row.origin ? String(row.origin) : null,
549
+ confidence: row.confidence != null ? Number(row.confidence) : null,
550
+ createdAt: String(row.created_at),
551
+ }));
552
+ }
553
+ /**
554
+ * List user turns for a session.
555
+ * Returns sanitized/reduced fields for nocturnal use — NO raw text.
556
+ */
557
+ listUserTurnsForSession(sessionId) {
558
+ const rows = this.db.prepare(`
559
+ SELECT id, turn_index, correction_detected, correction_cue, created_at
560
+ FROM user_turns
561
+ WHERE session_id = ?
562
+ ORDER BY turn_index ASC
563
+ `).all(sessionId);
564
+ return rows.map((row) => ({
565
+ id: Number(row.id),
566
+ turnIndex: Number(row.turn_index),
567
+ correctionDetected: Boolean(row.correction_detected),
568
+ correctionCue: row.correction_cue ? String(row.correction_cue) : null,
569
+ createdAt: String(row.created_at),
570
+ }));
571
+ }
572
+ /**
573
+ * List correction samples with optional review status filter.
574
+ *
575
+ * Returns: Analytics data aggregated from trajectory database.
576
+ * Not: Runtime truth or real-time queue state.
577
+ */
358
578
  listCorrectionSamples(status = 'pending') {
359
579
  const rows = this.db.prepare(`
360
580
  SELECT sample_id, session_id, bad_assistant_turn_id, user_correction_turn_id,
@@ -379,6 +599,26 @@ export class TrajectoryDatabase {
379
599
  updatedAt: String(row.updated_at),
380
600
  }));
381
601
  }
602
+ /**
603
+ * List gate blocks for a session.
604
+ * Returns minimal fields for nocturnal use — no raw text.
605
+ */
606
+ listGateBlocksForSession(sessionId) {
607
+ const rows = this.db.prepare(`
608
+ SELECT id, tool_name, file_path, reason, plan_status, created_at
609
+ FROM gate_blocks
610
+ WHERE session_id = ?
611
+ ORDER BY id ASC
612
+ `).all(sessionId);
613
+ return rows.map((row) => ({
614
+ id: Number(row.id),
615
+ toolName: String(row.tool_name),
616
+ filePath: row.file_path ? String(row.file_path) : null,
617
+ reason: String(row.reason),
618
+ planStatus: row.plan_status ? String(row.plan_status) : null,
619
+ createdAt: String(row.created_at),
620
+ }));
621
+ }
382
622
  reviewCorrectionSample(sampleId, status, note) {
383
623
  const updatedAt = nowIso();
384
624
  const updated = this.withWrite(() => {
@@ -397,7 +637,7 @@ export class TrajectoryDatabase {
397
637
  return true;
398
638
  });
399
639
  if (!updated) {
400
- throw new Error(`Correction sample not found: ${sampleId}`);
640
+ throw new SampleNotFoundError(sampleId);
401
641
  }
402
642
  const record = this.db.prepare(`
403
643
  SELECT sample_id, session_id, bad_assistant_turn_id, user_correction_turn_id,
@@ -407,7 +647,7 @@ export class TrajectoryDatabase {
407
647
  WHERE sample_id = ?
408
648
  `).get(sampleId);
409
649
  if (!record) {
410
- throw new Error(`Correction sample not found after review update: ${sampleId}`);
650
+ throw new SampleNotFoundError(`${sampleId} (after update)`);
411
651
  }
412
652
  return {
413
653
  sampleId: String(record.sample_id),
@@ -424,6 +664,12 @@ export class TrajectoryDatabase {
424
664
  updatedAt: String(record.updated_at),
425
665
  };
426
666
  }
667
+ /**
668
+ * Export correction samples to JSONL file.
669
+ *
670
+ * Returns: Analytics data aggregated from trajectory database.
671
+ * Not: Runtime truth or real-time queue state.
672
+ */
427
673
  exportCorrections(opts) {
428
674
  const rows = this.db.prepare(`
429
675
  SELECT cs.sample_id, cs.session_id, cs.recovery_tool_span_json, cs.diff_excerpt, cs.quality_score,
@@ -462,6 +708,12 @@ export class TrajectoryDatabase {
462
708
  this.recordExportAudit('corrections', opts.mode, opts.approvedOnly, exportPath, rows.length);
463
709
  return { filePath: exportPath, count: rows.length, mode: opts.mode };
464
710
  }
711
+ /**
712
+ * Export analytics data to JSON file.
713
+ *
714
+ * Returns: Analytics data aggregated from trajectory database.
715
+ * Not: Runtime truth or real-time queue state.
716
+ */
465
717
  exportAnalytics() {
466
718
  const payload = {
467
719
  generatedAt: nowIso(),
@@ -476,6 +728,12 @@ export class TrajectoryDatabase {
476
728
  this.recordExportAudit('analytics', 'raw', true, exportPath, Array.isArray(payload.dailyMetrics) ? payload.dailyMetrics.length : 0);
477
729
  return { filePath: exportPath, count: Array.isArray(payload.dailyMetrics) ? payload.dailyMetrics.length : 0 };
478
730
  }
731
+ /**
732
+ * Get trajectory database statistics.
733
+ *
734
+ * Returns: Analytics data aggregated from trajectory database.
735
+ * Not: Runtime truth or real-time queue state.
736
+ */
479
737
  getDataStats() {
480
738
  const getCount = (table, where) => {
481
739
  const sql = where ? `SELECT COUNT(*) as count FROM ${table} WHERE ${where}` : `SELECT COUNT(*) as count FROM ${table}`;
@@ -658,6 +916,26 @@ export class TrajectoryDatabase {
658
916
  metadata_json TEXT,
659
917
  created_at TEXT NOT NULL
660
918
  );
919
+ `);
920
+ // V2 migration: Add V2 columns to evolution_tasks if they don't exist
921
+ // SQLite does not support IF NOT EXISTS for ADD COLUMN, so we must check manually
922
+ // before each ALTER to avoid "duplicate column name" errors on existing DBs
923
+ const v2Columns = [
924
+ { name: 'task_kind', type: 'TEXT' },
925
+ { name: 'priority', type: 'TEXT' },
926
+ { name: 'retry_count', type: 'INTEGER' },
927
+ { name: 'max_retries', type: 'INTEGER' },
928
+ { name: 'last_error', type: 'TEXT' },
929
+ { name: 'result_ref', type: 'TEXT' },
930
+ ];
931
+ for (const col of v2Columns) {
932
+ const exists = this.db.prepare(`PRAGMA table_info(evolution_tasks)`).all()
933
+ .some((row) => row.name === col.name);
934
+ if (!exists) {
935
+ this.db.exec(`ALTER TABLE evolution_tasks ADD COLUMN ${col.name} ${col.type}`);
936
+ }
937
+ }
938
+ this.db.exec(`
661
939
  CREATE VIEW IF NOT EXISTS v_error_clusters AS
662
940
  SELECT tool_name, COALESCE(error_type, 'unknown') AS error_type, COUNT(*) AS occurrences
663
941
  FROM tool_calls
@@ -729,6 +1007,12 @@ export class TrajectoryDatabase {
729
1007
  LEFT JOIN correction_daily ON correction_daily.day = tool_daily.day;
730
1008
  `);
731
1009
  }
1010
+ /**
1011
+ * Get daily metrics for analytics.
1012
+ *
1013
+ * Returns: Analytics data aggregated from trajectory database.
1014
+ * Not: Runtime truth or real-time queue state.
1015
+ */
732
1016
  dailyMetrics() {
733
1017
  return this.db.prepare('SELECT * FROM v_daily_metrics ORDER BY day ASC').all();
734
1018
  }
@@ -2,7 +2,6 @@ import { PD_FILES } from './paths.js';
2
2
  import { PainConfig } from './config.js';
3
3
  import { EventLog } from './event-log.js';
4
4
  import { PainDictionary } from './dictionary.js';
5
- import { TrustEngine } from './trust-engine.js';
6
5
  import { HygieneTracker } from './hygiene/tracker.js';
7
6
  import { EvolutionReducerImpl } from './evolution-reducer.js';
8
7
  import { TrajectoryDatabase } from './trajectory.js';
@@ -18,7 +17,6 @@ export declare class WorkspaceContext {
18
17
  private _config?;
19
18
  private _eventLog?;
20
19
  private _dictionary?;
21
- private _trust?;
22
20
  private _hygiene?;
23
21
  private _evolutionReducer?;
24
22
  private _trajectory?;
@@ -35,10 +33,6 @@ export declare class WorkspaceContext {
35
33
  * Pain dictionary service for this workspace.
36
34
  */
37
35
  get dictionary(): PainDictionary;
38
- /**
39
- * Trust engine service bound to this workspace.
40
- */
41
- get trust(): TrustEngine;
42
36
  /**
43
37
  * Hygiene tracking service for this workspace.
44
38
  */