jfl 0.7.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/commands/context-hub.d.ts.map +1 -1
  2. package/dist/commands/context-hub.js +26 -0
  3. package/dist/commands/context-hub.js.map +1 -1
  4. package/dist/commands/migrate-tenet.d.ts +25 -0
  5. package/dist/commands/migrate-tenet.d.ts.map +1 -0
  6. package/dist/commands/migrate-tenet.js +252 -0
  7. package/dist/commands/migrate-tenet.js.map +1 -0
  8. package/dist/commands/peter.d.ts.map +1 -1
  9. package/dist/commands/peter.js +47 -5
  10. package/dist/commands/peter.js.map +1 -1
  11. package/dist/commands/pi.d.ts +1 -0
  12. package/dist/commands/pi.d.ts.map +1 -1
  13. package/dist/commands/pi.js +5 -1
  14. package/dist/commands/pi.js.map +1 -1
  15. package/dist/commands/pivot.d.ts +28 -0
  16. package/dist/commands/pivot.d.ts.map +1 -0
  17. package/dist/commands/pivot.js +219 -0
  18. package/dist/commands/pivot.js.map +1 -0
  19. package/dist/commands/services-create.js +348 -0
  20. package/dist/commands/services-create.js.map +1 -1
  21. package/dist/dashboard-static/assets/index-BVrmW-ZI.js +154 -0
  22. package/dist/dashboard-static/assets/{index-CW8oWAdr.css → index-DtruPD44.css} +1 -1
  23. package/dist/dashboard-static/index.html +2 -2
  24. package/dist/index.js +24 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/lib/agent-generator.d.ts.map +1 -1
  27. package/dist/lib/agent-generator.js +15 -0
  28. package/dist/lib/agent-generator.js.map +1 -1
  29. package/dist/lib/counterfactual-engine.d.ts +136 -0
  30. package/dist/lib/counterfactual-engine.d.ts.map +1 -0
  31. package/dist/lib/counterfactual-engine.js +417 -0
  32. package/dist/lib/counterfactual-engine.js.map +1 -0
  33. package/dist/lib/dynamics-model.d.ts +107 -0
  34. package/dist/lib/dynamics-model.d.ts.map +1 -0
  35. package/dist/lib/dynamics-model.js +363 -0
  36. package/dist/lib/dynamics-model.js.map +1 -0
  37. package/dist/lib/eval-snapshot.d.ts.map +1 -1
  38. package/dist/lib/eval-snapshot.js +15 -4
  39. package/dist/lib/eval-snapshot.js.map +1 -1
  40. package/dist/lib/invariant-monitor.d.ts +50 -0
  41. package/dist/lib/invariant-monitor.d.ts.map +1 -0
  42. package/dist/lib/invariant-monitor.js +400 -0
  43. package/dist/lib/invariant-monitor.js.map +1 -0
  44. package/dist/lib/meta-orchestrator.d.ts +40 -3
  45. package/dist/lib/meta-orchestrator.d.ts.map +1 -1
  46. package/dist/lib/meta-orchestrator.js +181 -2
  47. package/dist/lib/meta-orchestrator.js.map +1 -1
  48. package/dist/lib/openclaw-sdk.d.ts +8 -0
  49. package/dist/lib/openclaw-sdk.d.ts.map +1 -1
  50. package/dist/lib/openclaw-sdk.js +11 -0
  51. package/dist/lib/openclaw-sdk.js.map +1 -1
  52. package/dist/lib/peter-parker-bridge.d.ts +37 -1
  53. package/dist/lib/peter-parker-bridge.d.ts.map +1 -1
  54. package/dist/lib/peter-parker-bridge.js +201 -1
  55. package/dist/lib/peter-parker-bridge.js.map +1 -1
  56. package/dist/lib/service-detector.d.ts +1 -1
  57. package/dist/lib/service-detector.d.ts.map +1 -1
  58. package/dist/lib/service-detector.js +26 -6
  59. package/dist/lib/service-detector.js.map +1 -1
  60. package/dist/lib/service-gtm.d.ts +1 -1
  61. package/dist/lib/service-gtm.d.ts.map +1 -1
  62. package/dist/lib/state-capture.d.ts +36 -0
  63. package/dist/lib/state-capture.d.ts.map +1 -0
  64. package/dist/lib/state-capture.js +541 -0
  65. package/dist/lib/state-capture.js.map +1 -0
  66. package/dist/lib/stratus-client.d.ts +78 -2
  67. package/dist/lib/stratus-client.d.ts.map +1 -1
  68. package/dist/lib/stratus-client.js +432 -1
  69. package/dist/lib/stratus-client.js.map +1 -1
  70. package/dist/lib/world-model-store.d.ts +172 -0
  71. package/dist/lib/world-model-store.d.ts.map +1 -0
  72. package/dist/lib/world-model-store.js +487 -0
  73. package/dist/lib/world-model-store.js.map +1 -0
  74. package/dist/types/world-model.d.ts +478 -0
  75. package/dist/types/world-model.d.ts.map +1 -0
  76. package/dist/types/world-model.js +80 -0
  77. package/dist/types/world-model.js.map +1 -0
  78. package/dist/utils/jfl-config.d.ts +5 -0
  79. package/dist/utils/jfl-config.d.ts.map +1 -1
  80. package/dist/utils/jfl-config.js +13 -1
  81. package/dist/utils/jfl-config.js.map +1 -1
  82. package/package.json +1 -1
  83. package/packages/pi/extensions/hud-tool.ts +2 -24
  84. package/packages/pi/extensions/index.ts +48 -25
  85. package/packages/pi/extensions/onboarding-v1.ts +455 -0
  86. package/packages/pi/extensions/onboarding-v2.ts +516 -0
  87. package/packages/pi/extensions/onboarding-v3.ts +675 -0
  88. package/packages/pi/extensions/pivot-tool.ts +59 -0
  89. package/packages/pi/extensions/session.ts +58 -0
  90. package/packages/pi/extensions/types.ts +2 -0
  91. package/packages/pi/skills/pivot/SKILL.md +91 -0
  92. package/template/.claude/settings.json +9 -0
  93. package/dist/dashboard-static/assets/index-Ck8f9dcM.js +0 -121
@@ -0,0 +1,172 @@
1
+ /**
2
+ * World Model Storage Layer
3
+ *
4
+ * File-based JSONL storage for state transitions, predictions, invariant violations,
5
+ * and state snapshots. Follows the eval-store.ts pattern for consistency.
6
+ *
7
+ * Storage structure:
8
+ * world-model-data/
9
+ * ├── transitions/
10
+ * │ ├── YYYY-MM-DD.jsonl # Daily transition logs
11
+ * │ └── index.json # Fast lookup index
12
+ * ├── states/
13
+ * │ ├── snapshots-hourly.jsonl
14
+ * │ └── current-state.json
15
+ * ├── predictions/
16
+ * │ ├── dynamics-cache.jsonl
17
+ * │ └── counterfactuals.jsonl
18
+ * └── invariants/
19
+ * ├── violations.jsonl
20
+ * └── recovery-log.jsonl
21
+ *
22
+ * @purpose JSONL storage layer for world model data — transitions, predictions, invariants
23
+ * @spec world-model-roadmap.md#storage
24
+ */
25
+ import type { StateTransition, WorldState, InvariantViolation, PredictionResult, CounterfactualScenario, AgentAction, CachedPrediction, StateSnapshot, WorldModelStorageConfig } from "../types/world-model.js";
26
+ /**
27
+ * WorldModelStore manages all persistent storage for the world model.
28
+ * Uses JSONL files for append-only logs and JSON for indexes/state.
29
+ */
30
+ export declare class WorldModelStore {
31
+ private projectRoot;
32
+ private dataDir;
33
+ private config;
34
+ /**
35
+ * Create a new WorldModelStore
36
+ * @param projectRoot - Project root directory (auto-detected if not provided)
37
+ * @param config - Optional configuration overrides
38
+ */
39
+ constructor(projectRoot?: string, config?: Partial<WorldModelStorageConfig>);
40
+ /**
41
+ * Record a new state transition
42
+ * @param transition - The transition to record (id will be generated if not provided)
43
+ * @returns The recorded transition with generated ID
44
+ */
45
+ recordTransition(transition: Omit<StateTransition, "id"> & {
46
+ id?: string;
47
+ }): StateTransition;
48
+ /**
49
+ * Get transitions with optional filtering
50
+ * @param options - Filter options
51
+ * @returns Array of matching transitions
52
+ */
53
+ getTransitions(options?: {
54
+ agentId?: string;
55
+ startTime?: number;
56
+ endTime?: number;
57
+ actionType?: string;
58
+ limit?: number;
59
+ dateRange?: {
60
+ start: string;
61
+ end: string;
62
+ };
63
+ }): StateTransition[];
64
+ /**
65
+ * Get a specific transition by ID
66
+ */
67
+ getTransitionById(id: string): StateTransition | null;
68
+ /**
69
+ * Update transition index for fast lookups
70
+ */
71
+ private updateTransitionIndex;
72
+ /**
73
+ * Read the transition index
74
+ */
75
+ private readTransitionIndex;
76
+ /**
77
+ * Save a state snapshot
78
+ * @param state - The world state to snapshot
79
+ * @param trigger - What triggered this snapshot
80
+ * @returns The saved snapshot with ID
81
+ */
82
+ snapshotState(state: WorldState, trigger?: "hourly" | "event" | "manual"): StateSnapshot;
83
+ /**
84
+ * Get the most recent state snapshot
85
+ */
86
+ getCurrentState(): WorldState | null;
87
+ /**
88
+ * Get recent state snapshots
89
+ */
90
+ getRecentSnapshots(limit?: number): StateSnapshot[];
91
+ /**
92
+ * Cache a prediction result
93
+ * @param state - The input state
94
+ * @param action - The input action
95
+ * @param prediction - The prediction result
96
+ */
97
+ cachePrediction(state: WorldState, action: AgentAction, prediction: PredictionResult): void;
98
+ /**
99
+ * Look up a cached prediction
100
+ * @param state - The input state
101
+ * @param action - The input action
102
+ * @returns Cached prediction if found and not expired
103
+ */
104
+ getCachedPrediction(state: WorldState, action: AgentAction): PredictionResult | null;
105
+ /**
106
+ * Get all cached predictions (for analysis/debugging)
107
+ */
108
+ getAllCachedPredictions(): CachedPrediction[];
109
+ /**
110
+ * Record an invariant violation
111
+ * @param violation - The violation to record
112
+ * @param transitionId - Optional transition ID that caused the violation
113
+ */
114
+ recordInvariantViolation(violation: InvariantViolation, transitionId?: string): void;
115
+ /**
116
+ * Get recent invariant violations
117
+ */
118
+ getRecentViolations(options?: {
119
+ invariantId?: string;
120
+ severity?: string;
121
+ since?: number;
122
+ limit?: number;
123
+ }): Array<InvariantViolation & {
124
+ transitionId?: string;
125
+ }>;
126
+ /**
127
+ * Record a recovery action taken after a violation
128
+ */
129
+ recordRecovery(violation: InvariantViolation, recoveryAction: string, success: boolean): void;
130
+ /**
131
+ * Store a counterfactual scenario
132
+ */
133
+ recordCounterfactual(scenario: CounterfactualScenario): void;
134
+ /**
135
+ * Get counterfactuals for a transition
136
+ */
137
+ getCounterfactuals(baseTransitionId: string): CounterfactualScenario[];
138
+ /**
139
+ * Get all counterfactuals (for analysis)
140
+ */
141
+ getAllCounterfactuals(): CounterfactualScenario[];
142
+ /**
143
+ * Get storage statistics
144
+ */
145
+ getStats(): {
146
+ transitionCount: number;
147
+ snapshotCount: number;
148
+ cachedPredictions: number;
149
+ violationCount: number;
150
+ counterfactualCount: number;
151
+ oldestTransition?: number;
152
+ newestTransition?: number;
153
+ };
154
+ /**
155
+ * Prune old data to keep storage bounded
156
+ */
157
+ pruneOldData(options?: {
158
+ maxAgeDays?: number;
159
+ maxTransitions?: number;
160
+ }): {
161
+ pruned: number;
162
+ };
163
+ /**
164
+ * Get the data directory path
165
+ */
166
+ getDataDir(): string;
167
+ /**
168
+ * Check if the store has any data
169
+ */
170
+ hasData(): boolean;
171
+ }
172
+ //# sourceMappingURL=world-model-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"world-model-store.d.ts","sourceRoot":"","sources":["../../src/lib/world-model-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,487 @@
1
+ /**
2
+ * World Model Storage Layer
3
+ *
4
+ * File-based JSONL storage for state transitions, predictions, invariant violations,
5
+ * and state snapshots. Follows the eval-store.ts pattern for consistency.
6
+ *
7
+ * Storage structure:
8
+ * world-model-data/
9
+ * ├── transitions/
10
+ * │ ├── YYYY-MM-DD.jsonl # Daily transition logs
11
+ * │ └── index.json # Fast lookup index
12
+ * ├── states/
13
+ * │ ├── snapshots-hourly.jsonl
14
+ * │ └── current-state.json
15
+ * ├── predictions/
16
+ * │ ├── dynamics-cache.jsonl
17
+ * │ └── counterfactuals.jsonl
18
+ * └── invariants/
19
+ * ├── violations.jsonl
20
+ * └── recovery-log.jsonl
21
+ *
22
+ * @purpose JSONL storage layer for world model data — transitions, predictions, invariants
23
+ * @spec world-model-roadmap.md#storage
24
+ */
25
+ import { existsSync, readFileSync, appendFileSync, writeFileSync, mkdirSync, readdirSync } from "fs";
26
+ import { join, dirname } from "path";
27
+ import { createHash } from "crypto";
28
+ // ============================================================================
29
+ // Constants
30
+ // ============================================================================
31
+ const DEFAULT_CONFIG = {
32
+ dataDir: "world-model-data",
33
+ maxTransitionsPerFile: 10000,
34
+ predictionCacheTtl: 3600000, // 1 hour
35
+ maxBufferSize: 50000,
36
+ };
37
+ // ============================================================================
38
+ // Helpers
39
+ // ============================================================================
40
+ /**
41
+ * Find project root by looking for .jfl directory
42
+ */
43
+ function findProjectRoot() {
44
+ let dir = process.cwd();
45
+ while (dir !== dirname(dir)) {
46
+ if (existsSync(join(dir, ".jfl", "config.json")))
47
+ return dir;
48
+ if (existsSync(join(dir, ".jfl")))
49
+ return dir;
50
+ dir = dirname(dir);
51
+ }
52
+ return process.cwd();
53
+ }
54
+ /**
55
+ * Get today's date in YYYY-MM-DD format
56
+ */
57
+ function getDateString(timestamp) {
58
+ const date = timestamp ? new Date(timestamp) : new Date();
59
+ return date.toISOString().split("T")[0];
60
+ }
61
+ /**
62
+ * Generate a unique ID from content hash
63
+ */
64
+ function generateId(prefix, ...contents) {
65
+ const hash = createHash("sha256")
66
+ .update(JSON.stringify(contents))
67
+ .digest("hex")
68
+ .slice(0, 16);
69
+ return `${prefix}_${hash}`;
70
+ }
71
+ /**
72
+ * Safely parse a JSONL file, skipping malformed lines
73
+ */
74
+ function parseJSONLFile(filePath) {
75
+ if (!existsSync(filePath))
76
+ return [];
77
+ const entries = [];
78
+ const lines = readFileSync(filePath, "utf-8").split("\n");
79
+ for (const line of lines) {
80
+ if (!line.trim())
81
+ continue;
82
+ try {
83
+ entries.push(JSON.parse(line));
84
+ }
85
+ catch {
86
+ // Skip malformed lines silently
87
+ }
88
+ }
89
+ return entries;
90
+ }
91
+ /**
92
+ * Ensure a directory exists, creating it if necessary
93
+ */
94
+ function ensureDir(dirPath) {
95
+ if (!existsSync(dirPath)) {
96
+ mkdirSync(dirPath, { recursive: true });
97
+ }
98
+ }
99
+ // ============================================================================
100
+ // WorldModelStore Class
101
+ // ============================================================================
102
+ /**
103
+ * WorldModelStore manages all persistent storage for the world model.
104
+ * Uses JSONL files for append-only logs and JSON for indexes/state.
105
+ */
106
+ export class WorldModelStore {
107
+ projectRoot;
108
+ dataDir;
109
+ config;
110
+ /**
111
+ * Create a new WorldModelStore
112
+ * @param projectRoot - Project root directory (auto-detected if not provided)
113
+ * @param config - Optional configuration overrides
114
+ */
115
+ constructor(projectRoot, config) {
116
+ this.projectRoot = projectRoot || findProjectRoot();
117
+ this.config = { ...DEFAULT_CONFIG, ...config };
118
+ this.dataDir = join(this.projectRoot, this.config.dataDir);
119
+ }
120
+ // ==========================================================================
121
+ // Transitions
122
+ // ==========================================================================
123
+ /**
124
+ * Record a new state transition
125
+ * @param transition - The transition to record (id will be generated if not provided)
126
+ * @returns The recorded transition with generated ID
127
+ */
128
+ recordTransition(transition) {
129
+ const id = transition.id || generateId("tr", transition.priorState.timestamp, transition.action.agentId, transition.action.actionType);
130
+ const full = { ...transition, id };
131
+ const transitionsDir = join(this.dataDir, "transitions");
132
+ ensureDir(transitionsDir);
133
+ const dateStr = getDateString(transition.posteriorState.timestamp);
134
+ const filePath = join(transitionsDir, `${dateStr}.jsonl`);
135
+ appendFileSync(filePath, JSON.stringify(full) + "\n");
136
+ // Update index
137
+ this.updateTransitionIndex(full);
138
+ return full;
139
+ }
140
+ /**
141
+ * Get transitions with optional filtering
142
+ * @param options - Filter options
143
+ * @returns Array of matching transitions
144
+ */
145
+ getTransitions(options = {}) {
146
+ const transitionsDir = join(this.dataDir, "transitions");
147
+ if (!existsSync(transitionsDir))
148
+ return [];
149
+ const files = readdirSync(transitionsDir)
150
+ .filter(f => f.endsWith(".jsonl"))
151
+ .sort();
152
+ // Filter files by date range if provided
153
+ let filteredFiles = files;
154
+ if (options.dateRange) {
155
+ filteredFiles = files.filter(f => {
156
+ const date = f.replace(".jsonl", "");
157
+ return date >= options.dateRange.start && date <= options.dateRange.end;
158
+ });
159
+ }
160
+ let transitions = [];
161
+ for (const file of filteredFiles) {
162
+ const filePath = join(transitionsDir, file);
163
+ const fileTransitions = parseJSONLFile(filePath);
164
+ transitions.push(...fileTransitions);
165
+ }
166
+ // Apply filters
167
+ if (options.agentId) {
168
+ transitions = transitions.filter(t => t.action.agentId === options.agentId);
169
+ }
170
+ if (options.startTime) {
171
+ transitions = transitions.filter(t => t.priorState.timestamp >= options.startTime);
172
+ }
173
+ if (options.endTime) {
174
+ transitions = transitions.filter(t => t.posteriorState.timestamp <= options.endTime);
175
+ }
176
+ if (options.actionType) {
177
+ transitions = transitions.filter(t => t.action.actionType === options.actionType);
178
+ }
179
+ // Sort by timestamp (newest first) and limit
180
+ transitions.sort((a, b) => b.posteriorState.timestamp - a.posteriorState.timestamp);
181
+ if (options.limit) {
182
+ transitions = transitions.slice(0, options.limit);
183
+ }
184
+ return transitions;
185
+ }
186
+ /**
187
+ * Get a specific transition by ID
188
+ */
189
+ getTransitionById(id) {
190
+ const index = this.readTransitionIndex();
191
+ const entry = index[id];
192
+ if (!entry)
193
+ return null;
194
+ const filePath = join(this.dataDir, "transitions", `${entry.date}.jsonl`);
195
+ const transitions = parseJSONLFile(filePath);
196
+ return transitions.find(t => t.id === id) || null;
197
+ }
198
+ /**
199
+ * Update transition index for fast lookups
200
+ */
201
+ updateTransitionIndex(transition) {
202
+ const indexPath = join(this.dataDir, "transitions", "index.json");
203
+ const index = this.readTransitionIndex();
204
+ index[transition.id] = {
205
+ date: getDateString(transition.posteriorState.timestamp),
206
+ agentId: transition.action.agentId,
207
+ actionType: transition.action.actionType,
208
+ timestamp: transition.posteriorState.timestamp,
209
+ };
210
+ writeFileSync(indexPath, JSON.stringify(index, null, 2));
211
+ }
212
+ /**
213
+ * Read the transition index
214
+ */
215
+ readTransitionIndex() {
216
+ const indexPath = join(this.dataDir, "transitions", "index.json");
217
+ if (!existsSync(indexPath))
218
+ return {};
219
+ try {
220
+ return JSON.parse(readFileSync(indexPath, "utf-8"));
221
+ }
222
+ catch {
223
+ return {};
224
+ }
225
+ }
226
+ // ==========================================================================
227
+ // State Snapshots
228
+ // ==========================================================================
229
+ /**
230
+ * Save a state snapshot
231
+ * @param state - The world state to snapshot
232
+ * @param trigger - What triggered this snapshot
233
+ * @returns The saved snapshot with ID
234
+ */
235
+ snapshotState(state, trigger = "manual") {
236
+ const snapshot = {
237
+ id: generateId("ss", state.timestamp, state.agentId),
238
+ timestamp: state.timestamp,
239
+ state,
240
+ trigger,
241
+ };
242
+ const statesDir = join(this.dataDir, "states");
243
+ ensureDir(statesDir);
244
+ // Append to hourly snapshots
245
+ const snapshotsPath = join(statesDir, "snapshots-hourly.jsonl");
246
+ appendFileSync(snapshotsPath, JSON.stringify(snapshot) + "\n");
247
+ // Update current state
248
+ const currentPath = join(statesDir, "current-state.json");
249
+ writeFileSync(currentPath, JSON.stringify(state, null, 2));
250
+ return snapshot;
251
+ }
252
+ /**
253
+ * Get the most recent state snapshot
254
+ */
255
+ getCurrentState() {
256
+ const currentPath = join(this.dataDir, "states", "current-state.json");
257
+ if (!existsSync(currentPath))
258
+ return null;
259
+ try {
260
+ return JSON.parse(readFileSync(currentPath, "utf-8"));
261
+ }
262
+ catch {
263
+ return null;
264
+ }
265
+ }
266
+ /**
267
+ * Get recent state snapshots
268
+ */
269
+ getRecentSnapshots(limit = 24) {
270
+ const snapshotsPath = join(this.dataDir, "states", "snapshots-hourly.jsonl");
271
+ const snapshots = parseJSONLFile(snapshotsPath);
272
+ return snapshots
273
+ .sort((a, b) => b.timestamp - a.timestamp)
274
+ .slice(0, limit);
275
+ }
276
+ // ==========================================================================
277
+ // Predictions Cache
278
+ // ==========================================================================
279
+ /**
280
+ * Cache a prediction result
281
+ * @param state - The input state
282
+ * @param action - The input action
283
+ * @param prediction - The prediction result
284
+ */
285
+ cachePrediction(state, action, prediction) {
286
+ const key = generateId("pc", state.timestamp, state.agentId, action.actionType);
287
+ const cached = {
288
+ key,
289
+ prediction,
290
+ cachedAt: Date.now(),
291
+ hitCount: 0,
292
+ };
293
+ const predictionsDir = join(this.dataDir, "predictions");
294
+ ensureDir(predictionsDir);
295
+ const cachePath = join(predictionsDir, "dynamics-cache.jsonl");
296
+ appendFileSync(cachePath, JSON.stringify(cached) + "\n");
297
+ }
298
+ /**
299
+ * Look up a cached prediction
300
+ * @param state - The input state
301
+ * @param action - The input action
302
+ * @returns Cached prediction if found and not expired
303
+ */
304
+ getCachedPrediction(state, action) {
305
+ const cachePath = join(this.dataDir, "predictions", "dynamics-cache.jsonl");
306
+ if (!existsSync(cachePath))
307
+ return null;
308
+ const entries = parseJSONLFile(cachePath);
309
+ const now = Date.now();
310
+ // Find matching entry that hasn't expired
311
+ const key = generateId("pc", state.timestamp, state.agentId, action.actionType);
312
+ const entry = entries.find(e => e.key === key &&
313
+ (now - e.cachedAt) < this.config.predictionCacheTtl);
314
+ return entry?.prediction || null;
315
+ }
316
+ /**
317
+ * Get all cached predictions (for analysis/debugging)
318
+ */
319
+ getAllCachedPredictions() {
320
+ const cachePath = join(this.dataDir, "predictions", "dynamics-cache.jsonl");
321
+ return parseJSONLFile(cachePath);
322
+ }
323
+ // ==========================================================================
324
+ // Invariant Violations
325
+ // ==========================================================================
326
+ /**
327
+ * Record an invariant violation
328
+ * @param violation - The violation to record
329
+ * @param transitionId - Optional transition ID that caused the violation
330
+ */
331
+ recordInvariantViolation(violation, transitionId) {
332
+ const entry = {
333
+ ...violation,
334
+ detectedAt: violation.detectedAt || Date.now(),
335
+ transitionId,
336
+ };
337
+ const invariantsDir = join(this.dataDir, "invariants");
338
+ ensureDir(invariantsDir);
339
+ const violationsPath = join(invariantsDir, "violations.jsonl");
340
+ appendFileSync(violationsPath, JSON.stringify(entry) + "\n");
341
+ }
342
+ /**
343
+ * Get recent invariant violations
344
+ */
345
+ getRecentViolations(options = {}) {
346
+ const violationsPath = join(this.dataDir, "invariants", "violations.jsonl");
347
+ let violations = parseJSONLFile(violationsPath);
348
+ if (options.invariantId) {
349
+ violations = violations.filter(v => v.invariantId === options.invariantId);
350
+ }
351
+ if (options.severity) {
352
+ violations = violations.filter(v => v.severity === options.severity);
353
+ }
354
+ if (options.since) {
355
+ violations = violations.filter(v => (v.detectedAt || 0) >= options.since);
356
+ }
357
+ violations.sort((a, b) => (b.detectedAt || 0) - (a.detectedAt || 0));
358
+ if (options.limit) {
359
+ violations = violations.slice(0, options.limit);
360
+ }
361
+ return violations;
362
+ }
363
+ /**
364
+ * Record a recovery action taken after a violation
365
+ */
366
+ recordRecovery(violation, recoveryAction, success) {
367
+ const entry = {
368
+ violationId: generateId("vr", violation.invariantId, violation.detectedAt),
369
+ invariantId: violation.invariantId,
370
+ recoveryAction,
371
+ success,
372
+ timestamp: Date.now(),
373
+ };
374
+ const invariantsDir = join(this.dataDir, "invariants");
375
+ ensureDir(invariantsDir);
376
+ const recoveryPath = join(invariantsDir, "recovery-log.jsonl");
377
+ appendFileSync(recoveryPath, JSON.stringify(entry) + "\n");
378
+ }
379
+ // ==========================================================================
380
+ // Counterfactuals
381
+ // ==========================================================================
382
+ /**
383
+ * Store a counterfactual scenario
384
+ */
385
+ recordCounterfactual(scenario) {
386
+ const predictionsDir = join(this.dataDir, "predictions");
387
+ ensureDir(predictionsDir);
388
+ const counterfactualsPath = join(predictionsDir, "counterfactuals.jsonl");
389
+ appendFileSync(counterfactualsPath, JSON.stringify(scenario) + "\n");
390
+ }
391
+ /**
392
+ * Get counterfactuals for a transition
393
+ */
394
+ getCounterfactuals(baseTransitionId) {
395
+ const counterfactualsPath = join(this.dataDir, "predictions", "counterfactuals.jsonl");
396
+ const all = parseJSONLFile(counterfactualsPath);
397
+ return all.filter(c => c.baseTransitionId === baseTransitionId);
398
+ }
399
+ /**
400
+ * Get all counterfactuals (for analysis)
401
+ */
402
+ getAllCounterfactuals() {
403
+ const counterfactualsPath = join(this.dataDir, "predictions", "counterfactuals.jsonl");
404
+ return parseJSONLFile(counterfactualsPath);
405
+ }
406
+ // ==========================================================================
407
+ // Statistics and Maintenance
408
+ // ==========================================================================
409
+ /**
410
+ * Get storage statistics
411
+ */
412
+ getStats() {
413
+ const transitions = this.getTransitions({ limit: 100000 });
414
+ const snapshots = this.getRecentSnapshots(100000);
415
+ const predictions = this.getAllCachedPredictions();
416
+ const violations = this.getRecentViolations({ limit: 100000 });
417
+ const counterfactuals = this.getAllCounterfactuals();
418
+ return {
419
+ transitionCount: transitions.length,
420
+ snapshotCount: snapshots.length,
421
+ cachedPredictions: predictions.length,
422
+ violationCount: violations.length,
423
+ counterfactualCount: counterfactuals.length,
424
+ oldestTransition: transitions.length > 0
425
+ ? Math.min(...transitions.map(t => t.priorState.timestamp))
426
+ : undefined,
427
+ newestTransition: transitions.length > 0
428
+ ? Math.max(...transitions.map(t => t.posteriorState.timestamp))
429
+ : undefined,
430
+ };
431
+ }
432
+ /**
433
+ * Prune old data to keep storage bounded
434
+ */
435
+ pruneOldData(options = {}) {
436
+ const maxAgeDays = options.maxAgeDays || 30;
437
+ const cutoffTime = Date.now() - (maxAgeDays * 24 * 60 * 60 * 1000);
438
+ const cutoffDate = getDateString(cutoffTime);
439
+ // Prune old transition files
440
+ const transitionsDir = join(this.dataDir, "transitions");
441
+ if (existsSync(transitionsDir)) {
442
+ const files = readdirSync(transitionsDir)
443
+ .filter(f => f.endsWith(".jsonl"));
444
+ let pruned = 0;
445
+ for (const file of files) {
446
+ const date = file.replace(".jsonl", "");
447
+ if (date < cutoffDate) {
448
+ const fs = require("fs");
449
+ fs.unlinkSync(join(transitionsDir, file));
450
+ pruned++;
451
+ }
452
+ }
453
+ // Rebuild index
454
+ if (pruned > 0) {
455
+ const remaining = this.getTransitions();
456
+ const index = {};
457
+ for (const t of remaining) {
458
+ index[t.id] = {
459
+ date: getDateString(t.posteriorState.timestamp),
460
+ agentId: t.action.agentId,
461
+ actionType: t.action.actionType,
462
+ timestamp: t.posteriorState.timestamp,
463
+ };
464
+ }
465
+ const indexPath = join(transitionsDir, "index.json");
466
+ writeFileSync(indexPath, JSON.stringify(index, null, 2));
467
+ return { pruned };
468
+ }
469
+ }
470
+ return { pruned: 0 };
471
+ }
472
+ /**
473
+ * Get the data directory path
474
+ */
475
+ getDataDir() {
476
+ return this.dataDir;
477
+ }
478
+ /**
479
+ * Check if the store has any data
480
+ */
481
+ hasData() {
482
+ return existsSync(this.dataDir) &&
483
+ (existsSync(join(this.dataDir, "transitions")) ||
484
+ existsSync(join(this.dataDir, "states")));
485
+ }
486
+ }
487
+ //# sourceMappingURL=world-model-store.js.map