@plures/praxis 1.2.12 → 1.2.41

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 (85) hide show
  1. package/README.md +63 -0
  2. package/dist/browser/{chunk-VOMLVI6V.js → chunk-BBP2F7TT.js} +70 -1
  3. package/dist/browser/{chunk-K377RW4V.js → chunk-FCEH7WMH.js} +1 -1
  4. package/dist/browser/{engine-YJZV4SLD.js → engine-65QDGCAN.js} +1 -1
  5. package/dist/browser/index.d.ts +104 -2
  6. package/dist/browser/index.js +181 -5
  7. package/dist/browser/integrations/svelte.d.ts +2 -2
  8. package/dist/browser/integrations/svelte.js +2 -2
  9. package/dist/browser/{reactive-engine.svelte-9aS0kTa8.d.ts → reactive-engine.svelte-Cqd8Mod2.d.ts} +56 -1
  10. package/dist/node/{chunk-PRPQO6R5.js → chunk-32YFEEML.js} +1 -1
  11. package/dist/node/{chunk-VOMLVI6V.js → chunk-BBP2F7TT.js} +70 -1
  12. package/dist/node/{chunk-5RH7UAQC.js → chunk-PTH6MD6P.js} +1 -0
  13. package/dist/node/cli/index.cjs +1553 -839
  14. package/dist/node/cli/index.js +39 -2
  15. package/dist/node/cloud/index.d.cts +1 -1
  16. package/dist/node/cloud/index.d.ts +1 -1
  17. package/dist/node/components/index.d.cts +2 -2
  18. package/dist/node/components/index.d.ts +2 -2
  19. package/dist/node/conversations-KQBXTP3N.js +596 -0
  20. package/dist/node/{engine-2DQBKBJC.js → engine-7CXQV6RC.js} +1 -1
  21. package/dist/node/index.cjs +408 -3
  22. package/dist/node/index.d.cts +308 -7
  23. package/dist/node/index.d.ts +308 -7
  24. package/dist/node/index.js +336 -6
  25. package/dist/node/integrations/svelte.cjs +70 -1
  26. package/dist/node/integrations/svelte.d.cts +3 -3
  27. package/dist/node/integrations/svelte.d.ts +3 -3
  28. package/dist/node/integrations/svelte.js +2 -2
  29. package/dist/node/{protocol-Qek7ebBl.d.ts → protocol-BocKczNv.d.cts} +1 -1
  30. package/dist/node/{protocol-Qek7ebBl.d.cts → protocol-BocKczNv.d.ts} +1 -1
  31. package/dist/node/{reactive-engine.svelte-CRNqHlbv.d.ts → reactive-engine.svelte-CGe8SpVE.d.cts} +57 -2
  32. package/dist/node/{reactive-engine.svelte-BFIZfawz.d.cts → reactive-engine.svelte-D-xTDxT5.d.ts} +57 -2
  33. package/dist/node/{terminal-adapter-B-UK_Vdz.d.ts → terminal-adapter-CvIvgTo4.d.ts} +1 -1
  34. package/dist/node/{terminal-adapter-BQSIF5bf.d.cts → terminal-adapter-Db-snPJ3.d.cts} +1 -1
  35. package/dist/node/{validate-CNHUULQE.js → validate-EN3M4FUR.js} +1 -1
  36. package/dist/node/{verify-KLJRXVJS.js → verify-7VZRP2WS.js} +2 -2
  37. package/docs/BOT_UPDATE_POLICY.md +125 -0
  38. package/docs/DOGFOODING_CHECKLIST.md +254 -0
  39. package/docs/DOGFOODING_INDEX.md +169 -0
  40. package/docs/DOGFOODING_QUICK_START.md +140 -0
  41. package/docs/KNO_ENG_EXTRACTION_PLAN.md +577 -0
  42. package/docs/PLURES_TOOLS_INVENTORY.md +170 -0
  43. package/docs/README.md +12 -0
  44. package/docs/TESTING_BOT_WORKFLOWS.md +154 -0
  45. package/docs/conversations/INTEGRATION_POINTS.md +719 -0
  46. package/docs/conversations/README.md +168 -0
  47. package/docs/core/extending-praxis-core.md +604 -0
  48. package/docs/core/praxis-core-api.md +385 -0
  49. package/docs/decision-ledger/contract-index.json +2 -2
  50. package/docs/decision-ledger/decisions/2026-02-01-monorepo-organization.md +130 -0
  51. package/docs/examples/DOGFOODING_WORKFLOW_EXAMPLE.md +295 -0
  52. package/docs/examples/README.md +41 -0
  53. package/docs/workflows/pr-overlap-guard.md +50 -0
  54. package/package.json +7 -2
  55. package/src/__tests__/chronicle.test.ts +512 -0
  56. package/src/__tests__/conversations.test.ts +312 -0
  57. package/src/__tests__/edge-cases.test.ts +1 -1
  58. package/src/__tests__/engine-dx.test.ts +355 -0
  59. package/src/cli/commands/conversations.ts +252 -0
  60. package/src/cli/index.ts +73 -0
  61. package/src/conversations/README.md +230 -0
  62. package/src/conversations/candidate.schema.json +123 -0
  63. package/src/conversations/candidates.ts +114 -0
  64. package/src/conversations/capture.ts +56 -0
  65. package/src/conversations/classify.ts +110 -0
  66. package/src/conversations/conversation.schema.json +106 -0
  67. package/src/conversations/emitters/fs.ts +65 -0
  68. package/src/conversations/emitters/github.ts +115 -0
  69. package/src/conversations/gate.ts +102 -0
  70. package/src/conversations/index.ts +28 -0
  71. package/src/conversations/normalize.ts +51 -0
  72. package/src/conversations/redact.ts +57 -0
  73. package/src/conversations/types.ts +96 -0
  74. package/src/core/chronicle/chronicle.ts +227 -0
  75. package/src/core/chronicle/context.ts +80 -0
  76. package/src/core/chronicle/index.ts +53 -0
  77. package/src/core/chronicle/mcp.ts +135 -0
  78. package/src/core/chronicle/types.ts +61 -0
  79. package/src/core/engine.ts +99 -1
  80. package/src/core/pluresdb/index.ts +22 -0
  81. package/src/core/pluresdb/store.ts +162 -5
  82. package/src/core/rules.ts +12 -0
  83. package/src/dsl/index.ts +6 -0
  84. package/src/index.ts +18 -0
  85. package/src/integrations/pluresdb.ts +22 -0
@@ -71,8 +71,12 @@ var init_engine = __esm({
71
71
  LogicEngine = class {
72
72
  state;
73
73
  registry;
74
+ factDedup;
75
+ maxFacts;
74
76
  constructor(options) {
75
77
  this.registry = options.registry;
78
+ this.factDedup = options.factDedup ?? "last-write-wins";
79
+ this.maxFacts = options.maxFacts ?? 1e3;
76
80
  this.state = {
77
81
  context: options.initialContext,
78
82
  facts: options.initialFacts ?? [],
@@ -128,6 +132,7 @@ var init_engine = __esm({
128
132
  const diagnostics = [];
129
133
  let newState = { ...this.state };
130
134
  const newFacts = [];
135
+ const eventTags = new Set(events.map((e) => e.tag));
131
136
  for (const ruleId of config.ruleIds) {
132
137
  const rule = this.registry.getRule(ruleId);
133
138
  if (!rule) {
@@ -138,6 +143,12 @@ var init_engine = __esm({
138
143
  });
139
144
  continue;
140
145
  }
146
+ if (rule.eventTypes) {
147
+ const filterTags = Array.isArray(rule.eventTypes) ? rule.eventTypes : [rule.eventTypes];
148
+ if (!filterTags.some((t) => eventTags.has(t))) {
149
+ continue;
150
+ }
151
+ }
141
152
  try {
142
153
  const ruleFacts = rule.impl(newState, events);
143
154
  newFacts.push(...ruleFacts);
@@ -149,9 +160,29 @@ var init_engine = __esm({
149
160
  });
150
161
  }
151
162
  }
163
+ let mergedFacts;
164
+ switch (this.factDedup) {
165
+ case "last-write-wins": {
166
+ const factMap = /* @__PURE__ */ new Map();
167
+ for (const f of newState.facts) factMap.set(f.tag, f);
168
+ for (const f of newFacts) factMap.set(f.tag, f);
169
+ mergedFacts = Array.from(factMap.values());
170
+ break;
171
+ }
172
+ case "append":
173
+ mergedFacts = [...newState.facts, ...newFacts];
174
+ break;
175
+ case "none":
176
+ default:
177
+ mergedFacts = [...newState.facts, ...newFacts];
178
+ break;
179
+ }
180
+ if (this.maxFacts > 0 && mergedFacts.length > this.maxFacts) {
181
+ mergedFacts = mergedFacts.slice(mergedFacts.length - this.maxFacts);
182
+ }
152
183
  newState = {
153
184
  ...newState,
154
- facts: [...newState.facts, ...newFacts]
185
+ facts: mergedFacts
155
186
  };
156
187
  for (const constraintId of config.constraintIds) {
157
188
  const constraint = this.registry.getConstraint(constraintId);
@@ -204,6 +235,29 @@ var init_engine = __esm({
204
235
  context: updater(this.state.context)
205
236
  };
206
237
  }
238
+ /**
239
+ * Atomically update context AND process events in a single call.
240
+ *
241
+ * This avoids the fragile pattern of calling updateContext() then step()
242
+ * separately, where rules could see stale context if the ordering is wrong.
243
+ *
244
+ * @param updater Function that produces new context from old context
245
+ * @param events Events to process after context is updated
246
+ * @returns Result with new state and diagnostics
247
+ *
248
+ * @example
249
+ * engine.stepWithContext(
250
+ * ctx => ({ ...ctx, sprintName: sprint.name, items: sprint.items }),
251
+ * [{ tag: 'sprint.update', payload: { name: sprint.name } }]
252
+ * );
253
+ */
254
+ stepWithContext(updater, events) {
255
+ this.state = {
256
+ ...this.state,
257
+ context: updater(this.state.context)
258
+ };
259
+ return this.step(events);
260
+ }
207
261
  /**
208
262
  * Add facts directly (for exceptional cases).
209
263
  * Generally, facts should be added through rules.
@@ -216,6 +270,21 @@ var init_engine = __esm({
216
270
  facts: [...this.state.facts, ...facts]
217
271
  };
218
272
  }
273
+ /**
274
+ * Check all constraints without processing any events.
275
+ *
276
+ * Useful for validation-only scenarios (e.g., form validation,
277
+ * pre-save checks) where you want constraint diagnostics without
278
+ * triggering any rules.
279
+ *
280
+ * @returns Array of constraint violation diagnostics (empty = all passing)
281
+ */
282
+ checkConstraints() {
283
+ return this.stepWithConfig([], {
284
+ ruleIds: [],
285
+ constraintIds: this.registry.getConstraintIds()
286
+ }).diagnostics;
287
+ }
219
288
  /**
220
289
  * Clear all facts
221
290
  */
@@ -405,6 +474,8 @@ __export(src_exports, {
405
474
  AcknowledgeContractGap: () => AcknowledgeContractGap,
406
475
  ActorManager: () => ActorManager,
407
476
  BehaviorLedger: () => BehaviorLedger,
477
+ CHRONICLE_PATHS: () => CHRONICLE_PATHS,
478
+ ChronicleContext: () => ChronicleContext,
408
479
  ContractAdded: () => ContractAdded,
409
480
  ContractGapAcknowledged: () => ContractGapAcknowledged,
410
481
  ContractMissing: () => ContractMissing,
@@ -417,6 +488,7 @@ __export(src_exports, {
417
488
  PRAXIS_PROTOCOL_VERSION: () => PRAXIS_PROTOCOL_VERSION,
418
489
  PluresDBGenerator: () => PluresDBGenerator,
419
490
  PluresDBPraxisAdapter: () => PluresDBPraxisAdapter,
491
+ PluresDbChronicle: () => PluresDbChronicle,
420
492
  PraxisDBStore: () => PraxisDBStore,
421
493
  PraxisRegistry: () => PraxisRegistry,
422
494
  PraxisSchemaRegistry: () => PraxisSchemaRegistry,
@@ -434,6 +506,8 @@ __export(src_exports, {
434
506
  canvasToYaml: () => canvasToYaml,
435
507
  createBehaviorLedger: () => createBehaviorLedger,
436
508
  createCanvasEditor: () => createCanvasEditor,
509
+ createChronicle: () => createChronicle,
510
+ createChronosMcpTools: () => createChronosMcpTools,
437
511
  createFrameworkAgnosticReactiveEngine: () => createReactiveEngine2,
438
512
  createInMemoryDB: () => createInMemoryDB,
439
513
  createIntrospector: () => createIntrospector,
@@ -1285,6 +1359,7 @@ function defineRule(options) {
1285
1359
  id: options.id,
1286
1360
  description: options.description,
1287
1361
  impl: options.impl,
1362
+ eventTypes: options.eventTypes,
1288
1363
  contract,
1289
1364
  meta
1290
1365
  };
@@ -2292,6 +2367,53 @@ async function loadSchemaFromFile(filePath, options = {}) {
2292
2367
  }
2293
2368
  }
2294
2369
 
2370
+ // src/core/chronicle/context.ts
2371
+ var ChronicleContext = class {
2372
+ static _stack = [];
2373
+ /**
2374
+ * Get the current active span, if any.
2375
+ */
2376
+ static get current() {
2377
+ return this._stack[this._stack.length - 1];
2378
+ }
2379
+ /**
2380
+ * Run a synchronous function within a causal span.
2381
+ * The span is automatically popped when the function returns.
2382
+ */
2383
+ static run(span, fn) {
2384
+ this._stack.push(span);
2385
+ try {
2386
+ return fn();
2387
+ } finally {
2388
+ this._stack.pop();
2389
+ }
2390
+ }
2391
+ /**
2392
+ * Run an async function within a causal span.
2393
+ * The span is popped after the promise settles.
2394
+ */
2395
+ static async runAsync(span, fn) {
2396
+ this._stack.push(span);
2397
+ try {
2398
+ return await fn();
2399
+ } finally {
2400
+ this._stack.pop();
2401
+ }
2402
+ }
2403
+ /**
2404
+ * Create a child span that inherits the current contextId.
2405
+ *
2406
+ * @param spanId ID for the new span
2407
+ * @returns A new ChronicleSpan with the current contextId
2408
+ */
2409
+ static childSpan(spanId) {
2410
+ return {
2411
+ spanId,
2412
+ contextId: this.current?.contextId
2413
+ };
2414
+ }
2415
+ };
2416
+
2295
2417
  // src/core/pluresdb/store.ts
2296
2418
  var PRAXIS_PATHS = {
2297
2419
  /** Base path for all Praxis data */
@@ -2327,12 +2449,32 @@ var PraxisDBStore = class {
2327
2449
  subscriptions = [];
2328
2450
  factWatchers = /* @__PURE__ */ new Map();
2329
2451
  onRuleError;
2452
+ chronicle;
2330
2453
  constructor(options) {
2331
2454
  this.db = options.db;
2332
2455
  this.registry = options.registry;
2333
2456
  this.context = options.initialContext ?? {};
2334
2457
  this.onRuleError = options.onRuleError ?? defaultErrorHandler;
2335
2458
  }
2459
+ /**
2460
+ * Attach a Chronicle observer to this store.
2461
+ *
2462
+ * Every subsequent `storeFact` and `appendEvent` call will be recorded as a
2463
+ * causal graph node in PluresDB, enabling full observability for free.
2464
+ *
2465
+ * @param chronicle Chronicle implementation to attach
2466
+ * @returns `this` for fluent chaining
2467
+ *
2468
+ * @example
2469
+ * ```typescript
2470
+ * const store = createPraxisDBStore(db, registry)
2471
+ * .withChronicle(createChronicle(db));
2472
+ * ```
2473
+ */
2474
+ withChronicle(chronicle) {
2475
+ this.chronicle = chronicle;
2476
+ return this;
2477
+ }
2336
2478
  /**
2337
2479
  * Store a fact in PluresDB
2338
2480
  *
@@ -2347,7 +2489,31 @@ var PraxisDBStore = class {
2347
2489
  if (!constraintResult.valid) {
2348
2490
  throw new Error(`Constraint violation: ${constraintResult.errors.join(", ")}`);
2349
2491
  }
2492
+ let before;
2493
+ if (this.chronicle) {
2494
+ const payload = fact.payload;
2495
+ const id = payload?.id;
2496
+ if (id) {
2497
+ before = await this.getFact(fact.tag, id);
2498
+ }
2499
+ }
2350
2500
  await this.persistFact(fact);
2501
+ if (this.chronicle) {
2502
+ const payload = fact.payload;
2503
+ const id = payload?.id ?? "";
2504
+ const span = ChronicleContext.current;
2505
+ try {
2506
+ await this.chronicle.record({
2507
+ path: getFactPath(fact.tag, id),
2508
+ before,
2509
+ after: fact,
2510
+ cause: span?.spanId,
2511
+ context: span?.contextId,
2512
+ metadata: { factTag: fact.tag, operation: "storeFact" }
2513
+ });
2514
+ } catch {
2515
+ }
2516
+ }
2351
2517
  await this.triggerRules([fact]);
2352
2518
  }
2353
2519
  /**
@@ -2361,7 +2527,31 @@ var PraxisDBStore = class {
2361
2527
  throw new Error(`Constraint violation: ${constraintResult.errors.join(", ")}`);
2362
2528
  }
2363
2529
  for (const fact of facts) {
2530
+ let before;
2531
+ if (this.chronicle) {
2532
+ const payload = fact.payload;
2533
+ const id = payload?.id;
2534
+ if (id) {
2535
+ before = await this.getFact(fact.tag, id);
2536
+ }
2537
+ }
2364
2538
  await this.persistFact(fact);
2539
+ if (this.chronicle) {
2540
+ const payload = fact.payload;
2541
+ const id = payload?.id ?? "";
2542
+ const span = ChronicleContext.current;
2543
+ try {
2544
+ await this.chronicle.record({
2545
+ path: getFactPath(fact.tag, id),
2546
+ before,
2547
+ after: fact,
2548
+ cause: span?.spanId,
2549
+ context: span?.contextId,
2550
+ metadata: { factTag: fact.tag, operation: "storeFacts" }
2551
+ });
2552
+ } catch {
2553
+ }
2554
+ }
2365
2555
  }
2366
2556
  await this.triggerRules(facts);
2367
2557
  }
@@ -2403,7 +2593,29 @@ var PraxisDBStore = class {
2403
2593
  };
2404
2594
  const newEvents = [...existingEvents, entry];
2405
2595
  await this.db.set(path3, newEvents);
2406
- await this.triggerRulesForEvents([event]);
2596
+ let eventNodeId;
2597
+ if (this.chronicle) {
2598
+ const span = ChronicleContext.current;
2599
+ try {
2600
+ const node = await this.chronicle.record({
2601
+ path: path3,
2602
+ before: existingEvents.length > 0 ? existingEvents[existingEvents.length - 1] : void 0,
2603
+ after: entry,
2604
+ cause: span?.spanId,
2605
+ context: span?.contextId,
2606
+ metadata: { eventTag: event.tag, sequence: String(entry.sequence), operation: "appendEvent" }
2607
+ });
2608
+ eventNodeId = node.id;
2609
+ } catch {
2610
+ }
2611
+ }
2612
+ const outerSpan = ChronicleContext.current;
2613
+ const ruleSpan = eventNodeId ? { spanId: eventNodeId, contextId: outerSpan?.contextId } : outerSpan;
2614
+ if (ruleSpan && this.chronicle) {
2615
+ await ChronicleContext.runAsync(ruleSpan, () => this.triggerRulesForEvents([event]));
2616
+ } else {
2617
+ await this.triggerRulesForEvents([event]);
2618
+ }
2407
2619
  }
2408
2620
  /**
2409
2621
  * Append multiple events to their respective streams
@@ -2416,6 +2628,7 @@ var PraxisDBStore = class {
2416
2628
  const existing = eventsByTag.get(event.tag) ?? [];
2417
2629
  eventsByTag.set(event.tag, [...existing, event]);
2418
2630
  }
2631
+ let lastEventNodeId;
2419
2632
  for (const [tag, tagEvents] of eventsByTag) {
2420
2633
  const path3 = getEventPath(tag);
2421
2634
  const existingEvents = await this.db.get(path3) ?? [];
@@ -2426,8 +2639,30 @@ var PraxisDBStore = class {
2426
2639
  sequence: sequence++
2427
2640
  }));
2428
2641
  await this.db.set(path3, [...existingEvents, ...newEntries]);
2642
+ if (this.chronicle) {
2643
+ const span = ChronicleContext.current;
2644
+ for (const entry of newEntries) {
2645
+ try {
2646
+ const node = await this.chronicle.record({
2647
+ path: path3,
2648
+ after: entry,
2649
+ cause: span?.spanId,
2650
+ context: span?.contextId,
2651
+ metadata: { eventTag: tag, sequence: String(entry.sequence), operation: "appendEvents" }
2652
+ });
2653
+ lastEventNodeId = node.id;
2654
+ } catch {
2655
+ }
2656
+ }
2657
+ }
2658
+ }
2659
+ const outerSpan = ChronicleContext.current;
2660
+ const ruleSpan = lastEventNodeId ? { spanId: lastEventNodeId, contextId: outerSpan?.contextId } : outerSpan;
2661
+ if (ruleSpan && this.chronicle) {
2662
+ await ChronicleContext.runAsync(ruleSpan, () => this.triggerRulesForEvents(events));
2663
+ } else {
2664
+ await this.triggerRulesForEvents(events);
2429
2665
  }
2430
- await this.triggerRulesForEvents(events);
2431
2666
  }
2432
2667
  /**
2433
2668
  * Get events from a stream
@@ -2540,6 +2775,21 @@ var PraxisDBStore = class {
2540
2775
  if (constraintResult.valid) {
2541
2776
  for (const fact of derivedFacts) {
2542
2777
  await this.persistFact(fact);
2778
+ if (this.chronicle) {
2779
+ const payload = fact.payload;
2780
+ const id = payload?.id ?? "";
2781
+ const span = ChronicleContext.current;
2782
+ try {
2783
+ await this.chronicle.record({
2784
+ path: getFactPath(fact.tag, id),
2785
+ after: fact,
2786
+ cause: span?.spanId,
2787
+ context: span?.contextId,
2788
+ metadata: { factTag: fact.tag, operation: "derivedFact" }
2789
+ });
2790
+ } catch {
2791
+ }
2792
+ }
2543
2793
  }
2544
2794
  }
2545
2795
  }
@@ -2833,6 +3083,156 @@ function createPluresDBGenerator(outputDir, options) {
2833
3083
  });
2834
3084
  }
2835
3085
 
3086
+ // src/core/chronicle/chronicle.ts
3087
+ var CHRONICLE_PATHS = {
3088
+ BASE: "/_praxis/chronos",
3089
+ NODES: "/_praxis/chronos/nodes",
3090
+ EDGES_OUT: "/_praxis/chronos/edges/out",
3091
+ EDGES_IN: "/_praxis/chronos/edges/in",
3092
+ CONTEXT: "/_praxis/chronos/context",
3093
+ INDEX: "/_praxis/chronos/index"
3094
+ };
3095
+ var _nodeCounter = 0;
3096
+ var PluresDbChronicle = class {
3097
+ db;
3098
+ constructor(db) {
3099
+ this.db = db;
3100
+ }
3101
+ async record(event) {
3102
+ const timestamp = Date.now();
3103
+ const id = `chronos:${timestamp}-${++_nodeCounter}`;
3104
+ const node = { id, timestamp, event };
3105
+ await this.db.set(`${CHRONICLE_PATHS.NODES}/${id}`, node);
3106
+ const index = await this.db.get(CHRONICLE_PATHS.INDEX) ?? [];
3107
+ await this.db.set(CHRONICLE_PATHS.INDEX, [...index, id]);
3108
+ if (event.cause) {
3109
+ await this.addEdge(event.cause, id, "causes");
3110
+ }
3111
+ if (event.context) {
3112
+ const contextPath = `${CHRONICLE_PATHS.CONTEXT}/${event.context}`;
3113
+ const contextNodes = await this.db.get(contextPath) ?? [];
3114
+ if (contextNodes.length > 0) {
3115
+ const prevId = contextNodes[contextNodes.length - 1];
3116
+ await this.addEdge(prevId, id, "follows");
3117
+ }
3118
+ await this.db.set(contextPath, [...contextNodes, id]);
3119
+ }
3120
+ return node;
3121
+ }
3122
+ async trace(nodeId, direction, maxDepth) {
3123
+ const visited = /* @__PURE__ */ new Set();
3124
+ const result = [];
3125
+ await this._traceRecursive(nodeId, direction, maxDepth, 0, visited, result);
3126
+ return result;
3127
+ }
3128
+ async range(start, end) {
3129
+ const index = await this.db.get(CHRONICLE_PATHS.INDEX) ?? [];
3130
+ const result = [];
3131
+ for (const id of index) {
3132
+ const node = await this.db.get(`${CHRONICLE_PATHS.NODES}/${id}`);
3133
+ if (node && node.timestamp >= start && node.timestamp <= end) {
3134
+ result.push(node);
3135
+ }
3136
+ }
3137
+ return result;
3138
+ }
3139
+ async subgraph(contextId) {
3140
+ const contextPath = `${CHRONICLE_PATHS.CONTEXT}/${contextId}`;
3141
+ const nodeIds = await this.db.get(contextPath) ?? [];
3142
+ const result = [];
3143
+ for (const id of nodeIds) {
3144
+ const node = await this.db.get(`${CHRONICLE_PATHS.NODES}/${id}`);
3145
+ if (node) {
3146
+ result.push(node);
3147
+ }
3148
+ }
3149
+ return result;
3150
+ }
3151
+ // ── Internal helpers ──────────────────────────────────────────────────────
3152
+ async addEdge(from, to, type) {
3153
+ const edge = { from, to, type };
3154
+ const outPath = `${CHRONICLE_PATHS.EDGES_OUT}/${from}`;
3155
+ const outEdges = await this.db.get(outPath) ?? [];
3156
+ await this.db.set(outPath, [...outEdges, edge]);
3157
+ const inPath = `${CHRONICLE_PATHS.EDGES_IN}/${to}`;
3158
+ const inEdges = await this.db.get(inPath) ?? [];
3159
+ await this.db.set(inPath, [...inEdges, edge]);
3160
+ }
3161
+ async _traceRecursive(nodeId, direction, maxDepth, depth, visited, result) {
3162
+ if (depth > maxDepth || visited.has(nodeId)) {
3163
+ return;
3164
+ }
3165
+ visited.add(nodeId);
3166
+ const node = await this.db.get(`${CHRONICLE_PATHS.NODES}/${nodeId}`);
3167
+ if (node) {
3168
+ result.push(node);
3169
+ }
3170
+ if (direction === "backward" || direction === "both") {
3171
+ const inEdges = await this.db.get(`${CHRONICLE_PATHS.EDGES_IN}/${nodeId}`) ?? [];
3172
+ for (const edge of inEdges) {
3173
+ await this._traceRecursive(edge.from, direction, maxDepth, depth + 1, visited, result);
3174
+ }
3175
+ }
3176
+ if (direction === "forward" || direction === "both") {
3177
+ const outEdges = await this.db.get(`${CHRONICLE_PATHS.EDGES_OUT}/${nodeId}`) ?? [];
3178
+ for (const edge of outEdges) {
3179
+ await this._traceRecursive(edge.to, direction, maxDepth, depth + 1, visited, result);
3180
+ }
3181
+ }
3182
+ }
3183
+ };
3184
+ function createChronicle(db) {
3185
+ return new PluresDbChronicle(db);
3186
+ }
3187
+
3188
+ // src/core/chronicle/mcp.ts
3189
+ function createChronosMcpTools(chronicle) {
3190
+ return {
3191
+ async trace(params) {
3192
+ try {
3193
+ const nodes = await chronicle.trace(
3194
+ params.nodeId,
3195
+ params.direction ?? "backward",
3196
+ params.maxDepth ?? 10
3197
+ );
3198
+ return { success: true, data: nodes };
3199
+ } catch (error) {
3200
+ return {
3201
+ success: false,
3202
+ error: error instanceof Error ? error.message : String(error)
3203
+ };
3204
+ }
3205
+ },
3206
+ async search(params) {
3207
+ try {
3208
+ const query = params.query.toLowerCase();
3209
+ let candidates;
3210
+ if (params.contextId) {
3211
+ candidates = await chronicle.subgraph(params.contextId);
3212
+ } else {
3213
+ candidates = await chronicle.range(params.since ?? 0, params.until ?? Date.now());
3214
+ }
3215
+ const filtered = candidates.filter((node) => {
3216
+ const inPath = node.event.path.toLowerCase().includes(query);
3217
+ const inMeta = Object.values(node.event.metadata).some(
3218
+ (v) => v.toLowerCase().includes(query)
3219
+ );
3220
+ const inAfter = JSON.stringify(node.event.after ?? "").toLowerCase().includes(query);
3221
+ const inBefore = JSON.stringify(node.event.before ?? "").toLowerCase().includes(query);
3222
+ return inPath || inMeta || inAfter || inBefore;
3223
+ });
3224
+ const limited = params.limit !== void 0 ? filtered.slice(0, params.limit) : filtered;
3225
+ return { success: true, data: limited };
3226
+ } catch (error) {
3227
+ return {
3228
+ success: false,
3229
+ error: error instanceof Error ? error.message : String(error)
3230
+ };
3231
+ }
3232
+ }
3233
+ };
3234
+ }
3235
+
2836
3236
  // src/integrations/pluresdb.ts
2837
3237
  function createPluresDBAdapter(options) {
2838
3238
  const store = createPraxisDBStore(options.db, options.registry, options.initialContext);
@@ -4258,6 +4658,8 @@ async function attachAllIntegrations(engine, registry, options = {}) {
4258
4658
  AcknowledgeContractGap,
4259
4659
  ActorManager,
4260
4660
  BehaviorLedger,
4661
+ CHRONICLE_PATHS,
4662
+ ChronicleContext,
4261
4663
  ContractAdded,
4262
4664
  ContractGapAcknowledged,
4263
4665
  ContractMissing,
@@ -4270,6 +4672,7 @@ async function attachAllIntegrations(engine, registry, options = {}) {
4270
4672
  PRAXIS_PROTOCOL_VERSION,
4271
4673
  PluresDBGenerator,
4272
4674
  PluresDBPraxisAdapter,
4675
+ PluresDbChronicle,
4273
4676
  PraxisDBStore,
4274
4677
  PraxisRegistry,
4275
4678
  PraxisSchemaRegistry,
@@ -4287,6 +4690,8 @@ async function attachAllIntegrations(engine, registry, options = {}) {
4287
4690
  canvasToYaml,
4288
4691
  createBehaviorLedger,
4289
4692
  createCanvasEditor,
4693
+ createChronicle,
4694
+ createChronosMcpTools,
4290
4695
  createFrameworkAgnosticReactiveEngine,
4291
4696
  createInMemoryDB,
4292
4697
  createIntrospector,