principles-disciple 1.114.2 → 1.115.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@
2
2
  "id": "principles-disciple",
3
3
  "name": "Principles Disciple",
4
4
  "description": "Evolutionary programming agent framework with strategic guardrails and reflection loops.",
5
- "version": "1.114.2",
5
+ "version": "1.115.1",
6
6
  "activation": {
7
7
  "onCapabilities": [
8
8
  "hook"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "principles-disciple",
3
- "version": "1.114.2",
3
+ "version": "1.115.1",
4
4
  "description": "Native OpenClaw plugin for Principles Disciple",
5
5
  "type": "module",
6
6
  "main": "./dist/bundle.js",
@@ -68,6 +68,10 @@ export interface TrajectoryPainEventInput {
68
68
  confidence?: number | null;
69
69
  text?: string;
70
70
  createdAt?: string;
71
+ /** PRI-406: Canonical pain identity (e.g. manual_<ts>_<hash> or pain_<ts>_<hash>). */
72
+ canonicalPainId?: string;
73
+ /** PRI-406: Runtime V2 diagnostician task ID linked to this pain event. */
74
+ runtimeTaskId?: string;
71
75
  }
72
76
 
73
77
  export interface TrajectoryGateBlockInput {
@@ -241,10 +241,15 @@ export class TrajectoryDatabase {
241
241
  this.recordSession({ sessionId: input.sessionId, startedAt: input.createdAt });
242
242
  let insertedId = -1;
243
243
  this.withWrite(() => {
244
+ // Try INSERT; on UNIQUE constraint violation for canonical_pain_id, do UPDATE instead.
245
+ // SQLite UPSERT (ON CONFLICT) does not support partial unique indexes, so we
246
+ // handle the conflict manually.
247
+ try {
244
248
  const runResult = this.db.prepare(`
245
249
  INSERT INTO pain_events (
246
- session_id, source, score, reason, severity, origin, confidence, text, created_at
247
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
250
+ session_id, source, score, reason, severity, origin, confidence, text, created_at,
251
+ canonical_pain_id, runtime_task_id
252
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
248
253
  `).run(
249
254
  input.sessionId,
250
255
  input.source,
@@ -255,8 +260,33 @@ export class TrajectoryDatabase {
255
260
  input.confidence ?? null,
256
261
  input.text ?? null,
257
262
  input.createdAt ?? nowIso(),
263
+ input.canonicalPainId ?? null,
264
+ input.runtimeTaskId ?? null,
258
265
  );
259
- insertedId = runResult.lastInsertRowid as number;
266
+ insertedId = Number(runResult.lastInsertRowid);
267
+ } catch (insertErr: unknown) {
268
+ if (
269
+ input.canonicalPainId &&
270
+ insertErr instanceof Error &&
271
+ insertErr.message.includes('UNIQUE constraint failed') &&
272
+ insertErr.message.includes('canonical_pain_id')
273
+ ) {
274
+ this.db.prepare(`
275
+ UPDATE pain_events
276
+ SET runtime_task_id = COALESCE(?, runtime_task_id)
277
+ WHERE canonical_pain_id = ?
278
+ `).run(input.runtimeTaskId ?? null, input.canonicalPainId);
279
+ const rawRow = this.db.prepare('SELECT id FROM pain_events WHERE canonical_pain_id = ?').get(input.canonicalPainId);
280
+ // Runtime Contract #1/#2: validate DB row instead of `as` cast
281
+ if (rawRow && typeof rawRow === 'object' && Object.hasOwn(rawRow, 'id') && typeof (rawRow as Record<string, unknown>).id === 'number') {
282
+ insertedId = (rawRow as { id: number }).id;
283
+ } else {
284
+ insertedId = -1;
285
+ }
286
+ } else {
287
+ throw insertErr;
288
+ }
289
+ }
260
290
  });
261
291
  // FTS indexing is best-effort — run outside the transaction so it cannot
262
292
  // roll back the committed pain event row (MEM-03, MEM-04).
@@ -1332,6 +1362,28 @@ export class TrajectoryDatabase {
1332
1362
  }
1333
1363
  }
1334
1364
 
1365
+ // PRI-406: Add canonical_pain_id and runtime_task_id columns to pain_events
1366
+ for (const col of [
1367
+ { name: 'canonical_pain_id', type: 'TEXT' },
1368
+ { name: 'runtime_task_id', type: 'TEXT' },
1369
+ ]) {
1370
+ try {
1371
+ this.db.exec(`ALTER TABLE pain_events ADD COLUMN ${col.name} ${col.type}`);
1372
+ } catch (err: unknown) {
1373
+ const message = err instanceof Error ? err.message : String(err);
1374
+ if (!message.includes('duplicate column name') && !message.includes('no column named')) {
1375
+ throw err;
1376
+ }
1377
+ }
1378
+ }
1379
+
1380
+ // PRI-406: Partial unique index on canonical_pain_id (non-null only) for dedup
1381
+ this.db.exec(`
1382
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_pain_events_canonical_pain_id
1383
+ ON pain_events(canonical_pain_id)
1384
+ WHERE canonical_pain_id IS NOT NULL
1385
+ `);
1386
+
1335
1387
  // Create FTS5 virtual table for pain_events text search (MEM-04)
1336
1388
  this.db.exec(`
1337
1389
  CREATE VIRTUAL TABLE IF NOT EXISTS pain_events_fts USING fts5(