@plures/praxis 1.2.13 → 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.
- package/README.md +44 -0
- package/dist/browser/{chunk-VOMLVI6V.js → chunk-BBP2F7TT.js} +70 -1
- package/dist/browser/{chunk-K377RW4V.js → chunk-FCEH7WMH.js} +1 -1
- package/dist/browser/{engine-YJZV4SLD.js → engine-65QDGCAN.js} +1 -1
- package/dist/browser/index.d.ts +104 -2
- package/dist/browser/index.js +181 -5
- package/dist/browser/integrations/svelte.d.ts +2 -2
- package/dist/browser/integrations/svelte.js +2 -2
- package/dist/browser/{reactive-engine.svelte-9aS0kTa8.d.ts → reactive-engine.svelte-Cqd8Mod2.d.ts} +56 -1
- package/dist/node/{chunk-PRPQO6R5.js → chunk-32YFEEML.js} +1 -1
- package/dist/node/{chunk-VOMLVI6V.js → chunk-BBP2F7TT.js} +70 -1
- package/dist/node/{chunk-5RH7UAQC.js → chunk-PTH6MD6P.js} +1 -0
- package/dist/node/cli/index.cjs +1553 -839
- package/dist/node/cli/index.js +39 -2
- package/dist/node/cloud/index.d.cts +1 -1
- package/dist/node/cloud/index.d.ts +1 -1
- package/dist/node/components/index.d.cts +2 -2
- package/dist/node/components/index.d.ts +2 -2
- package/dist/node/conversations-KQBXTP3N.js +596 -0
- package/dist/node/{engine-2DQBKBJC.js → engine-7CXQV6RC.js} +1 -1
- package/dist/node/index.cjs +408 -3
- package/dist/node/index.d.cts +308 -7
- package/dist/node/index.d.ts +308 -7
- package/dist/node/index.js +336 -6
- package/dist/node/integrations/svelte.cjs +70 -1
- package/dist/node/integrations/svelte.d.cts +3 -3
- package/dist/node/integrations/svelte.d.ts +3 -3
- package/dist/node/integrations/svelte.js +2 -2
- package/dist/node/{protocol-Qek7ebBl.d.ts → protocol-BocKczNv.d.cts} +1 -1
- package/dist/node/{protocol-Qek7ebBl.d.cts → protocol-BocKczNv.d.ts} +1 -1
- package/dist/node/{reactive-engine.svelte-CRNqHlbv.d.ts → reactive-engine.svelte-CGe8SpVE.d.cts} +57 -2
- package/dist/node/{reactive-engine.svelte-BFIZfawz.d.cts → reactive-engine.svelte-D-xTDxT5.d.ts} +57 -2
- package/dist/node/{terminal-adapter-B-UK_Vdz.d.ts → terminal-adapter-CvIvgTo4.d.ts} +1 -1
- package/dist/node/{terminal-adapter-BQSIF5bf.d.cts → terminal-adapter-Db-snPJ3.d.cts} +1 -1
- package/dist/node/{validate-CNHUULQE.js → validate-EN3M4FUR.js} +1 -1
- package/dist/node/{verify-KLJRXVJS.js → verify-7VZRP2WS.js} +2 -2
- package/docs/BOT_UPDATE_POLICY.md +125 -0
- package/docs/DOGFOODING_CHECKLIST.md +254 -0
- package/docs/DOGFOODING_INDEX.md +169 -0
- package/docs/DOGFOODING_QUICK_START.md +140 -0
- package/docs/KNO_ENG_EXTRACTION_PLAN.md +577 -0
- package/docs/PLURES_TOOLS_INVENTORY.md +170 -0
- package/docs/README.md +12 -0
- package/docs/TESTING_BOT_WORKFLOWS.md +154 -0
- package/docs/conversations/INTEGRATION_POINTS.md +719 -0
- package/docs/conversations/README.md +168 -0
- package/docs/core/extending-praxis-core.md +604 -0
- package/docs/core/praxis-core-api.md +385 -0
- package/docs/decision-ledger/contract-index.json +2 -2
- package/docs/decision-ledger/decisions/2026-02-01-monorepo-organization.md +130 -0
- package/docs/examples/DOGFOODING_WORKFLOW_EXAMPLE.md +295 -0
- package/docs/examples/README.md +41 -0
- package/docs/workflows/pr-overlap-guard.md +50 -0
- package/package.json +7 -2
- package/src/__tests__/chronicle.test.ts +512 -0
- package/src/__tests__/conversations.test.ts +312 -0
- package/src/__tests__/edge-cases.test.ts +1 -1
- package/src/__tests__/engine-dx.test.ts +355 -0
- package/src/cli/commands/conversations.ts +252 -0
- package/src/cli/index.ts +73 -0
- package/src/conversations/README.md +230 -0
- package/src/conversations/candidate.schema.json +123 -0
- package/src/conversations/candidates.ts +114 -0
- package/src/conversations/capture.ts +56 -0
- package/src/conversations/classify.ts +110 -0
- package/src/conversations/conversation.schema.json +106 -0
- package/src/conversations/emitters/fs.ts +65 -0
- package/src/conversations/emitters/github.ts +115 -0
- package/src/conversations/gate.ts +102 -0
- package/src/conversations/index.ts +28 -0
- package/src/conversations/normalize.ts +51 -0
- package/src/conversations/redact.ts +57 -0
- package/src/conversations/types.ts +96 -0
- package/src/core/chronicle/chronicle.ts +227 -0
- package/src/core/chronicle/context.ts +80 -0
- package/src/core/chronicle/index.ts +53 -0
- package/src/core/chronicle/mcp.ts +135 -0
- package/src/core/chronicle/types.ts +61 -0
- package/src/core/engine.ts +99 -1
- package/src/core/pluresdb/index.ts +22 -0
- package/src/core/pluresdb/store.ts +162 -5
- package/src/core/rules.ts +12 -0
- package/src/dsl/index.ts +6 -0
- package/src/index.ts +18 -0
- package/src/integrations/pluresdb.ts +22 -0
package/dist/node/index.cjs
CHANGED
|
@@ -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:
|
|
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
|
-
|
|
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,
|