@stackmemoryai/stackmemory 1.6.2 → 1.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 (86) hide show
  1. package/dist/src/agents/core/agent-task-manager.js +3 -3
  2. package/dist/src/agents/verifiers/formatter-verifier.js +1 -1
  3. package/dist/src/agents/verifiers/llm-judge.js +2 -2
  4. package/dist/src/cli/commands/conductor-traces.js +314 -0
  5. package/dist/src/cli/commands/context-rehydrate.js +2 -2
  6. package/dist/src/cli/commands/log.js +1 -1
  7. package/dist/src/cli/commands/onboard.js +4 -4
  8. package/dist/src/cli/commands/orchestrate.js +328 -27
  9. package/dist/src/cli/commands/orchestrator.js +50 -3
  10. package/dist/src/cli/commands/search.js +3 -3
  11. package/dist/src/cli/commands/setup.js +1 -1
  12. package/dist/src/cli/commands/signup.js +1 -1
  13. package/dist/src/cli/commands/skills.js +2 -2
  14. package/dist/src/cli/commands/worktree.js +1 -1
  15. package/dist/src/core/config/config-manager.js +3 -1
  16. package/dist/src/core/config/types.js +7 -0
  17. package/dist/src/core/context/dual-stack-manager.js +2 -2
  18. package/dist/src/core/context/frame-database.js +38 -0
  19. package/dist/src/core/context/frame-enrichment.js +93 -0
  20. package/dist/src/core/context/frame-handoff-manager.js +1 -1
  21. package/dist/src/core/context/frame-manager.js +4 -4
  22. package/dist/src/core/context/rehydration.js +6 -6
  23. package/dist/src/core/database/sqlite-adapter.js +136 -15
  24. package/dist/src/core/digest/frame-digest-integration.js +1 -1
  25. package/dist/src/core/digest/hybrid-digest-generator.js +1 -1
  26. package/dist/src/core/digest/hybrid-digest.js +3 -3
  27. package/dist/src/core/errors/recovery.js +1 -1
  28. package/dist/src/core/extensions/loader.js +10 -10
  29. package/dist/src/core/extensions/plugin-system.js +1 -1
  30. package/dist/src/core/monitoring/logger.js +2 -2
  31. package/dist/src/core/monitoring/metrics.js +2 -2
  32. package/dist/src/core/monitoring/progress-tracker.js +2 -2
  33. package/dist/src/core/performance/lazy-context-loader.js +1 -1
  34. package/dist/src/core/performance/performance-benchmark.js +8 -8
  35. package/dist/src/core/performance/performance-profiler.js +2 -2
  36. package/dist/src/core/performance/streaming-jsonl-parser.js +1 -1
  37. package/dist/src/core/projects/project-isolation.js +1 -1
  38. package/dist/src/core/projects/project-manager.js +5 -5
  39. package/dist/src/core/session/clear-survival.js +3 -3
  40. package/dist/src/core/session/handoff-generator.js +1 -1
  41. package/dist/src/core/session/session-manager.js +4 -4
  42. package/dist/src/core/skills/skill-storage.js +2 -2
  43. package/dist/src/core/storage/remote-storage.js +2 -2
  44. package/dist/src/core/trace/cli-trace-wrapper.js +3 -3
  45. package/dist/src/core/trace/debug-trace.js +2 -2
  46. package/dist/src/core/trace/trace-detector.js +1 -1
  47. package/dist/src/core/worktree/worktree-manager.js +2 -2
  48. package/dist/src/features/analytics/api/analytics-api.js +1 -1
  49. package/dist/src/features/analytics/core/analytics-service.js +2 -2
  50. package/dist/src/features/analytics/queries/metrics-queries.js +1 -1
  51. package/dist/src/features/web/server/index.js +8 -8
  52. package/dist/src/integrations/anthropic/client.js +4 -4
  53. package/dist/src/integrations/claude-code/post-task-hooks.js +3 -3
  54. package/dist/src/integrations/claude-code/subagent-client.js +2 -2
  55. package/dist/src/integrations/diffmem/client.js +1 -1
  56. package/dist/src/integrations/linear/client.js +1 -1
  57. package/dist/src/integrations/linear/migration.js +1 -1
  58. package/dist/src/integrations/linear/oauth-server.js +2 -2
  59. package/dist/src/integrations/linear/sync.js +1 -1
  60. package/dist/src/integrations/linear/webhook-handler.js +2 -2
  61. package/dist/src/integrations/linear/webhook-server.js +2 -2
  62. package/dist/src/integrations/linear/webhook.js +2 -2
  63. package/dist/src/integrations/mcp/handlers/discovery-handlers.js +3 -3
  64. package/dist/src/integrations/mcp/handlers/linear-handlers.js +1 -1
  65. package/dist/src/integrations/mcp/handlers/skill-handlers.js +2 -2
  66. package/dist/src/integrations/mcp/handlers/task-handlers.js +2 -2
  67. package/dist/src/integrations/mcp/handlers/trace-handlers.js +1 -1
  68. package/dist/src/integrations/mcp/refactored-server.js +2 -2
  69. package/dist/src/integrations/mcp/remote-server.js +1 -1
  70. package/dist/src/integrations/mcp/server.js +4 -4
  71. package/dist/src/integrations/ralph/bridge/ralph-stackmemory-bridge.js +13 -13
  72. package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +1 -1
  73. package/dist/src/integrations/ralph/learning/pattern-learner.js +3 -3
  74. package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +1 -1
  75. package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +2 -2
  76. package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +19 -19
  77. package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +5 -5
  78. package/dist/src/integrations/ralph/performance/performance-optimizer.js +1 -1
  79. package/dist/src/integrations/ralph/recovery/crash-recovery.js +1 -1
  80. package/dist/src/integrations/ralph/state/state-reconciler.js +1 -1
  81. package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +4 -4
  82. package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +8 -8
  83. package/dist/src/integrations/ralph/visualization/ralph-debugger.js +3 -3
  84. package/dist/src/orchestrators/multimodal/harness.js +3 -3
  85. package/dist/src/servers/production/auth-middleware.js +2 -2
  86. package/package.json +1 -1
@@ -150,6 +150,44 @@ class FrameDatabase {
150
150
  -- Set initial schema version if not exists
151
151
  INSERT OR IGNORE INTO schema_version (version) VALUES (1);
152
152
  `);
153
+ try {
154
+ this.db.exec(
155
+ "ALTER TABLE frames ADD COLUMN access_count INTEGER DEFAULT 0"
156
+ );
157
+ } catch {
158
+ }
159
+ try {
160
+ this.db.exec("ALTER TABLE frames ADD COLUMN last_accessed INTEGER");
161
+ } catch {
162
+ }
163
+ this.db.exec(`
164
+ CREATE TABLE IF NOT EXISTS frame_access_log (
165
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
166
+ frame_id TEXT NOT NULL,
167
+ accessed_at INTEGER NOT NULL DEFAULT (unixepoch()),
168
+ FOREIGN KEY (frame_id) REFERENCES frames(frame_id) ON DELETE CASCADE
169
+ );
170
+ CREATE INDEX IF NOT EXISTS idx_frame_access_log_frame
171
+ ON frame_access_log(frame_id);
172
+ `);
173
+ this.db.exec(`
174
+ CREATE TABLE IF NOT EXISTS entity_states (
175
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
176
+ project_id TEXT NOT NULL,
177
+ entity_name TEXT NOT NULL,
178
+ relation TEXT NOT NULL,
179
+ value TEXT NOT NULL,
180
+ context TEXT,
181
+ source_frame_id TEXT,
182
+ valid_from INTEGER NOT NULL DEFAULT (unixepoch()),
183
+ superseded_at INTEGER,
184
+ FOREIGN KEY (source_frame_id) REFERENCES frames(frame_id)
185
+ );
186
+ CREATE INDEX IF NOT EXISTS idx_entity_name
187
+ ON entity_states(project_id, entity_name, relation);
188
+ CREATE INDEX IF NOT EXISTS idx_entity_temporal
189
+ ON entity_states(entity_name, valid_from DESC);
190
+ `);
153
191
  this.db.exec(`
154
192
  CREATE TABLE IF NOT EXISTS cord_tasks (
155
193
  task_id TEXT PRIMARY KEY,
@@ -0,0 +1,93 @@
1
+ import { fileURLToPath as __fileURLToPath } from 'url';
2
+ import { dirname as __pathDirname } from 'path';
3
+ const __filename = __fileURLToPath(import.meta.url);
4
+ const __dirname = __pathDirname(__filename);
5
+ import { logger } from "../monitoring/logger.js";
6
+ import { frameLifecycleHooks } from "./frame-lifecycle-hooks.js";
7
+ const ENRICHMENT_PROMPT = `You are a context enrichment engine. Given a frame digest and its parent context, do two things:
8
+ 1. Rewrite the digest to be fully self-contained \u2014 resolve all pronouns ("it", "that", "the project") using parent context.
9
+ 2. Extract entity-relation-value triples from the content.
10
+
11
+ Respond in JSON only:
12
+ {"enrichedDigest":"...","entities":[{"name":"...","relation":"...","value":"...","context":"..."}]}`;
13
+ function buildEnrichmentInput(frame, parents) {
14
+ const parentCtx = parents.map((p) => `[${p.name}]: ${p.digest_text ?? ""}`).join("\n");
15
+ return `Parent context:
16
+ ${parentCtx}
17
+
18
+ Current frame "${frame.name}":
19
+ ${frame.digest_text ?? ""}`;
20
+ }
21
+ async function enrichFrame(frame, parents, apiKey) {
22
+ try {
23
+ const Anthropic = (await import("@anthropic-ai/sdk")).default;
24
+ const client = new Anthropic({ apiKey });
25
+ const input = buildEnrichmentInput(frame, parents);
26
+ const response = await client.messages.create({
27
+ model: "claude-3-5-haiku-latest",
28
+ max_tokens: 1024,
29
+ system: ENRICHMENT_PROMPT,
30
+ messages: [{ role: "user", content: input }]
31
+ });
32
+ const text = response.content[0].type === "text" ? response.content[0].text : "";
33
+ return JSON.parse(text);
34
+ } catch (err) {
35
+ logger.warn("Frame enrichment LLM call failed", {
36
+ error: err instanceof Error ? err.message : String(err),
37
+ frameId: frame.frame_id
38
+ });
39
+ return void 0;
40
+ }
41
+ }
42
+ let unregister = null;
43
+ function registerEnrichmentHook(config, deps) {
44
+ if (unregister) unregister();
45
+ if (!config.enabled) return () => {
46
+ };
47
+ const apiKey = process.env.ANTHROPIC_API_KEY;
48
+ if (!apiKey) {
49
+ logger.warn("Enrichment enabled but ANTHROPIC_API_KEY not set");
50
+ return () => {
51
+ };
52
+ }
53
+ unregister = frameLifecycleHooks.onFrameClosed(
54
+ "frame-enrichment",
55
+ async (data) => {
56
+ if (!data.frame.digest_text) return;
57
+ const parents = await deps.getParentFrames(
58
+ data.frame.frame_id,
59
+ config.lookbackDepth
60
+ );
61
+ const result = await enrichFrame(data.frame, parents, apiKey);
62
+ if (!result) return;
63
+ await deps.updateDigest(data.frame.frame_id, result.enrichedDigest);
64
+ if (config.extractEntities) {
65
+ for (const entity of result.entities) {
66
+ try {
67
+ deps.recordEntity(
68
+ data.frame.project_id,
69
+ entity.name,
70
+ entity.relation,
71
+ entity.value,
72
+ entity.context,
73
+ data.frame.frame_id
74
+ );
75
+ } catch {
76
+ }
77
+ }
78
+ }
79
+ },
80
+ -10
81
+ // low priority — runs after other hooks
82
+ );
83
+ return () => {
84
+ if (unregister) {
85
+ unregister();
86
+ unregister = null;
87
+ }
88
+ };
89
+ }
90
+ export {
91
+ enrichFrame,
92
+ registerEnrichmentHook
93
+ };
@@ -489,7 +489,7 @@ class FrameHandoffManager {
489
489
  */
490
490
  async getHandoffMetrics(timeRange) {
491
491
  const handoffs = Array.from(this.activeHandoffs.values());
492
- const filteredHandoffs = timeRange ? handoffs.filter((h) => {
492
+ const filteredHandoffs = timeRange ? handoffs.filter((_h) => {
493
493
  return true;
494
494
  }) : handoffs;
495
495
  const completedHandoffs = filteredHandoffs.filter(
@@ -12,7 +12,7 @@ import {
12
12
  } from "../errors/index.js";
13
13
  import { FrameQueryMode } from "../session/index.js";
14
14
  import { frameLifecycleHooks } from "./frame-lifecycle-hooks.js";
15
- const MAX_FRAME_DEPTH = 100;
15
+ const _MAX_FRAME_DEPTH = 100;
16
16
  const DEFAULT_MAX_DEPTH = 100;
17
17
  import { FrameDatabase } from "./frame-database.js";
18
18
  import { FrameStack } from "./frame-stack.js";
@@ -167,7 +167,7 @@ class FrameManager {
167
167
  outputs: {},
168
168
  digest_json: {}
169
169
  };
170
- const createdFrame = this.frameDb.insertFrame(frame);
170
+ const _createdFrame = this.frameDb.insertFrame(frame);
171
171
  this.frameStack.pushFrame(frameId);
172
172
  logger.info("Created frame", {
173
173
  frameId,
@@ -275,7 +275,7 @@ class FrameManager {
275
275
  event_type: eventType,
276
276
  payload
277
277
  };
278
- const createdEvent = this.frameDb.insertEvent(event);
278
+ const _createdEvent = this.frameDb.insertEvent(event);
279
279
  logger.debug("Added event", {
280
280
  eventId,
281
281
  frameId: targetFrameId,
@@ -316,7 +316,7 @@ class FrameManager {
316
316
  priority,
317
317
  metadata
318
318
  };
319
- const createdAnchor = this.frameDb.insertAnchor(anchor);
319
+ const _createdAnchor = this.frameDb.insertAnchor(anchor);
320
320
  logger.debug("Added anchor", {
321
321
  anchorId,
322
322
  frameId: targetFrameId,
@@ -622,7 +622,7 @@ class EnhancedRehydrationManager {
622
622
  ];
623
623
  const files = await this.getDirectoryFiles(workingDir);
624
624
  for (const file of files) {
625
- const ext = path.extname(file);
625
+ const _ext = path.extname(file);
626
626
  const basename = path.basename(file);
627
627
  if (configPatterns.some(
628
628
  (pattern) => pattern.includes("*") ? basename.includes(pattern.replace("*", "")) : basename === pattern
@@ -825,10 +825,10 @@ Stack preview: ${trace.stack_frames.slice(0, 3).join("\n")}`,
825
825
  logger.info("Rich context injected successfully");
826
826
  }
827
827
  // Helper methods
828
- async getDirectoryFiles(dir) {
828
+ async getDirectoryFiles(_dir) {
829
829
  return [];
830
830
  }
831
- async getRecentlyModifiedFiles(dir) {
831
+ async getRecentlyModifiedFiles(_dir) {
832
832
  return [];
833
833
  }
834
834
  inferContextTags(filePath) {
@@ -917,7 +917,7 @@ Stack preview: ${trace.stack_frames.slice(0, 3).join("\n")}`,
917
917
  resolution_attempted: data.resolutionAttempts || [],
918
918
  resolution_status: data.resolved ? "resolved" : "pending"
919
919
  };
920
- } catch (error) {
920
+ } catch {
921
921
  return null;
922
922
  }
923
923
  }
@@ -944,7 +944,7 @@ Stack preview: ${trace.stack_frames.slice(0, 3).join("\n")}`,
944
944
  }
945
945
  return [...new Set(workflows)];
946
946
  }
947
- inferCurrentFocus(snapshots, context) {
947
+ inferCurrentFocus(snapshots, _context) {
948
948
  if (snapshots.some((s) => s.contextTags.includes("migration"))) {
949
949
  return "Data migration and transformation";
950
950
  }
@@ -953,7 +953,7 @@ Stack preview: ${trace.stack_frames.slice(0, 3).join("\n")}`,
953
953
  }
954
954
  return "Development";
955
955
  }
956
- createRecoveryAnchors(snapshots, context) {
956
+ createRecoveryAnchors(snapshots, _context) {
957
957
  const anchors = [];
958
958
  for (const snapshot of snapshots.slice(0, 3)) {
959
959
  anchors.push(
@@ -468,19 +468,11 @@ class SQLiteAdapter extends FeatureAwareDatabaseAdapter {
468
468
  };
469
469
  }
470
470
  /**
471
- * Compute importance score for a single frame.
472
- * Score range: [0.0, 1.0] — higher means more important, less likely to be GC'd.
471
+ * Compute structural salience for a frame (range 0.3-1.0).
472
+ * Based on anchors, events, digest, and children.
473
473
  */
474
- computeImportanceScore(frameId) {
475
- if (!this.db)
476
- throw new DatabaseError(
477
- "Database not connected",
478
- ErrorCode.DB_CONNECTION_FAILED
479
- );
480
- const frame = this.db.prepare(
481
- "SELECT frame_id, digest_text, created_at FROM frames WHERE frame_id = ?"
482
- ).get(frameId);
483
- if (!frame) return 0.3;
474
+ computeSalience(frameId, digestText) {
475
+ if (!this.db) return 0.3;
484
476
  let score = 0.3;
485
477
  const decisionCount = this.db.prepare(
486
478
  "SELECT COUNT(*) as count FROM anchors WHERE frame_id = ? AND type = 'DECISION'"
@@ -488,14 +480,61 @@ class SQLiteAdapter extends FeatureAwareDatabaseAdapter {
488
480
  if (decisionCount > 0) score += 0.15;
489
481
  const eventCount = this.db.prepare("SELECT COUNT(*) as count FROM events WHERE frame_id = ?").get(frameId).count;
490
482
  if (eventCount > 3) score += 0.1;
491
- if (frame.digest_text) score += 0.15;
483
+ if (digestText) score += 0.15;
492
484
  const childCount = this.db.prepare(
493
485
  "SELECT COUNT(*) as count FROM frames WHERE parent_frame_id = ?"
494
486
  ).get(frameId).count;
495
487
  if (childCount > 0) score += 0.1;
488
+ return Math.min(score, 1);
489
+ }
490
+ /**
491
+ * Compute importance score using Ebbinghaus retention decay + reinforcement.
492
+ *
493
+ * R(frame, t) = I_salience * e^(-lambda * dt) + sigma * sum(1 / (t - t_access_i))
494
+ *
495
+ * Score range: [0.05, 1.0] — never fully zero.
496
+ */
497
+ computeImportanceScore(frameId) {
498
+ if (!this.db)
499
+ throw new DatabaseError(
500
+ "Database not connected",
501
+ ErrorCode.DB_CONNECTION_FAILED
502
+ );
503
+ const frame = this.db.prepare(
504
+ "SELECT frame_id, digest_text, created_at FROM frames WHERE frame_id = ?"
505
+ ).get(frameId);
506
+ if (!frame) return 0.3;
507
+ const salience = this.computeSalience(frameId, frame.digest_text);
496
508
  const nowSec = Math.floor(Date.now() / 1e3);
497
- if (nowSec - frame.created_at < 86400) score += 0.1;
498
- return Math.round(Math.min(score, 1) * 100) / 100;
509
+ const dtDays = Math.max(0, (nowSec - frame.created_at) / 86400);
510
+ const lambda = 0.05;
511
+ const decayTerm = salience * Math.exp(-lambda * dtDays);
512
+ const sigma = 0.1;
513
+ const accesses = this.db.prepare("SELECT accessed_at FROM frame_access_log WHERE frame_id = ?").all(frameId);
514
+ let reinforcement = 0;
515
+ for (const a of accesses) {
516
+ const gap = nowSec - a.accessed_at;
517
+ if (gap > 0) reinforcement += 1 / gap;
518
+ }
519
+ const raw = decayTerm + sigma * reinforcement;
520
+ return Math.round(Math.min(Math.max(raw, 0.05), 1) * 100) / 100;
521
+ }
522
+ /**
523
+ * Record a frame access for retention decay scoring.
524
+ * Inserts into frame_access_log and bumps access_count/last_accessed.
525
+ */
526
+ recordFrameAccess(frameId) {
527
+ if (!this.db) return;
528
+ const nowSec = Math.floor(Date.now() / 1e3);
529
+ try {
530
+ this.db.prepare(
531
+ "INSERT INTO frame_access_log (frame_id, accessed_at) VALUES (?, ?)"
532
+ ).run(frameId, nowSec);
533
+ this.db.prepare(
534
+ "UPDATE frames SET access_count = COALESCE(access_count, 0) + 1, last_accessed = ? WHERE frame_id = ?"
535
+ ).run(nowSec, frameId);
536
+ } catch {
537
+ }
499
538
  }
500
539
  /**
501
540
  * Compress a frame by stripping data according to strategy.
@@ -1611,6 +1650,88 @@ class SQLiteAdapter extends FeatureAwareDatabaseAdapter {
1611
1650
  }
1612
1651
  });
1613
1652
  }
1653
+ // --- Entity State Methods ---
1654
+ recordEntityState(projectId, entityName, relation, value, context, sourceFrameId) {
1655
+ if (!this.db)
1656
+ throw new DatabaseError(
1657
+ "Database not connected",
1658
+ ErrorCode.DB_CONNECTION_FAILED
1659
+ );
1660
+ const nowSec = Math.floor(Date.now() / 1e3);
1661
+ this.db.prepare(
1662
+ `UPDATE entity_states
1663
+ SET superseded_at = ?
1664
+ WHERE entity_name = ? AND relation = ? AND superseded_at IS NULL`
1665
+ ).run(nowSec, entityName, relation);
1666
+ const result = this.db.prepare(
1667
+ `INSERT INTO entity_states
1668
+ (project_id, entity_name, relation, value, context, source_frame_id, valid_from)
1669
+ VALUES (?, ?, ?, ?, ?, ?, ?)`
1670
+ ).run(
1671
+ projectId,
1672
+ entityName,
1673
+ relation,
1674
+ value,
1675
+ context ?? null,
1676
+ sourceFrameId ?? null,
1677
+ nowSec
1678
+ );
1679
+ return Number(result.lastInsertRowid);
1680
+ }
1681
+ getEntityState(entityName, relation, asOf) {
1682
+ if (!this.db)
1683
+ throw new DatabaseError(
1684
+ "Database not connected",
1685
+ ErrorCode.DB_CONNECTION_FAILED
1686
+ );
1687
+ if (asOf != null) {
1688
+ const relFilter2 = relation ? " AND relation = ?" : "";
1689
+ const sql2 = `SELECT * FROM entity_states
1690
+ WHERE entity_name = ?${relFilter2}
1691
+ AND valid_from <= ?
1692
+ AND (superseded_at IS NULL OR superseded_at > ?)
1693
+ ORDER BY valid_from DESC`;
1694
+ const params2 = [entityName];
1695
+ if (relation) params2.push(relation);
1696
+ params2.push(asOf, asOf);
1697
+ return this.db.prepare(sql2).all(...params2);
1698
+ }
1699
+ const relFilter = relation ? " AND relation = ?" : "";
1700
+ const sql = `SELECT * FROM entity_states
1701
+ WHERE entity_name = ?${relFilter} AND superseded_at IS NULL
1702
+ ORDER BY valid_from DESC, id DESC`;
1703
+ const params = [entityName];
1704
+ if (relation) params.push(relation);
1705
+ return this.db.prepare(sql).all(...params);
1706
+ }
1707
+ getEntityHistory(entityName, relation) {
1708
+ if (!this.db)
1709
+ throw new DatabaseError(
1710
+ "Database not connected",
1711
+ ErrorCode.DB_CONNECTION_FAILED
1712
+ );
1713
+ const relFilter = relation ? " AND relation = ?" : "";
1714
+ const sql = `SELECT * FROM entity_states
1715
+ WHERE entity_name = ?${relFilter}
1716
+ ORDER BY valid_from DESC, id DESC`;
1717
+ const params = [entityName];
1718
+ if (relation) params.push(relation);
1719
+ return this.db.prepare(sql).all(...params);
1720
+ }
1721
+ getEntityDiff(entityName, since) {
1722
+ if (!this.db)
1723
+ throw new DatabaseError(
1724
+ "Database not connected",
1725
+ ErrorCode.DB_CONNECTION_FAILED
1726
+ );
1727
+ const sinceFilter = since != null ? " AND valid_from > ?" : "";
1728
+ const sql = `SELECT * FROM entity_states
1729
+ WHERE entity_name = ?${sinceFilter}
1730
+ ORDER BY valid_from DESC, id DESC`;
1731
+ const params = [entityName];
1732
+ if (since != null) params.push(since);
1733
+ return this.db.prepare(sql).all(...params);
1734
+ }
1614
1735
  }
1615
1736
  export {
1616
1737
  SQLiteAdapter
@@ -138,7 +138,7 @@ class FrameDigestIntegration {
138
138
  /**
139
139
  * Get frame anchors (wrapper for proper typing)
140
140
  */
141
- getFrameAnchors(frameId) {
141
+ getFrameAnchors(_frameId) {
142
142
  return [];
143
143
  }
144
144
  /**
@@ -313,7 +313,7 @@ class HybridDigestGenerator {
313
313
  * Determine priority based on frame characteristics
314
314
  */
315
315
  determinePriority(input) {
316
- const { frame, anchors, events } = input;
316
+ const { _frame, anchors, events } = input;
317
317
  const decisionCount = anchors.filter((a) => a.type === "DECISION").length;
318
318
  const errorCount = events.filter(
319
319
  (e) => e.payload?.error || e.payload?.success === false
@@ -119,7 +119,7 @@ class EnhancedHybridDigestGenerator extends HybridDigestGenerator {
119
119
  if (!this.llmProvider) {
120
120
  throw new Error("No LLM provider configured");
121
121
  }
122
- const prompt = this.buildEnhancedPrompt(input, deterministic);
122
+ const _prompt = this.buildEnhancedPrompt(input, deterministic);
123
123
  const response = await this.llmProvider.generateSummary(
124
124
  input,
125
125
  deterministic,
@@ -162,7 +162,7 @@ class EnhancedHybridDigestGenerator extends HybridDigestGenerator {
162
162
  /**
163
163
  * Extract key decisions from AI response
164
164
  */
165
- extractKeyDecisions(response) {
165
+ extractKeyDecisions(_response) {
166
166
  return [];
167
167
  }
168
168
  /**
@@ -178,7 +178,7 @@ class EnhancedHybridDigestGenerator extends HybridDigestGenerator {
178
178
  /**
179
179
  * Extract next steps from AI response
180
180
  */
181
- extractNextSteps(response) {
181
+ extractNextSteps(_response) {
182
182
  return [];
183
183
  }
184
184
  /**
@@ -114,7 +114,7 @@ class CircuitBreaker {
114
114
  throw error;
115
115
  }
116
116
  }
117
- handleFailure(error) {
117
+ handleFailure(_error) {
118
118
  this.failures++;
119
119
  this.lastFailTime = /* @__PURE__ */ new Date();
120
120
  if (this.state === "half_open" /* HALF_OPEN */) {
@@ -155,7 +155,7 @@ class ExtensionLoader {
155
155
  /**
156
156
  * Load extension from source based on type
157
157
  */
158
- async loadFromSource(type, uri, options) {
158
+ async loadFromSource(type, uri, _options) {
159
159
  switch (type) {
160
160
  case "url":
161
161
  return this.loadFromUrl(uri);
@@ -277,7 +277,7 @@ class ExtensionLoader {
277
277
  */
278
278
  async loadFromNpm(packageName) {
279
279
  try {
280
- const { name, version } = this.parseNpmPackage(packageName);
280
+ const { name, _version } = this.parseNpmPackage(packageName);
281
281
  let module;
282
282
  try {
283
283
  module = await import(name);
@@ -295,7 +295,7 @@ class ExtensionLoader {
295
295
  const entryPoint = packageJson.main || "index.js";
296
296
  const entryPath = path.join(nodeModulesPath, entryPoint);
297
297
  module = await import(pathToFileURL(entryPath).href);
298
- } catch (innerError) {
298
+ } catch {
299
299
  return {
300
300
  success: false,
301
301
  error: `Package not found: ${name}. Try running: npm install ${name}`
@@ -321,7 +321,7 @@ class ExtensionLoader {
321
321
  /**
322
322
  * Evaluate extension code (for URL sources)
323
323
  */
324
- async evaluateExtensionCode(code, sourceUrl) {
324
+ async evaluateExtensionCode(code, _sourceUrl) {
325
325
  const dataUrl = `data:text/javascript;base64,${Buffer.from(code).toString("base64")}`;
326
326
  try {
327
327
  const module = await import(dataUrl);
@@ -473,12 +473,12 @@ class ExtensionLoader {
473
473
  permissions,
474
474
  // State management (always available, scoped to extension)
475
475
  state: {
476
- get: async (key) => {
476
+ get: async (_key) => {
477
477
  return void 0;
478
478
  },
479
- set: async (key, value) => {
479
+ set: async (_key, _value) => {
480
480
  },
481
- delete: async (key) => {
481
+ delete: async (_key) => {
482
482
  }
483
483
  },
484
484
  // Sandboxed fetch (always available)
@@ -511,7 +511,7 @@ class ExtensionLoader {
511
511
  };
512
512
  if (permSet.has("frames:read") || permSet.has("frames:write")) {
513
513
  context.frames = {
514
- get: async (frameId) => {
514
+ get: async (_frameId) => {
515
515
  return void 0;
516
516
  },
517
517
  list: async () => {
@@ -519,10 +519,10 @@ class ExtensionLoader {
519
519
  }
520
520
  };
521
521
  if (permSet.has("frames:write")) {
522
- context.frames.create = async (options) => {
522
+ context.frames.create = async (_options) => {
523
523
  throw new Error("Frame creation not implemented");
524
524
  };
525
- context.frames.update = async (frameId, data) => {
525
+ context.frames.update = async (_frameId, _data) => {
526
526
  throw new Error("Frame update not implemented");
527
527
  };
528
528
  }
@@ -362,7 +362,7 @@ function createLinearPlugin() {
362
362
  context.logger.info(`Sync complete: ${frames.length} frames`);
363
363
  return frames;
364
364
  },
365
- watch(callback) {
365
+ watch(_callback) {
366
366
  context?.logger.info("Watch mode enabled");
367
367
  return () => {
368
368
  context?.logger.info("Watch mode disabled");
@@ -112,7 +112,7 @@ class Logger {
112
112
  if (!fs.existsSync(logDir)) {
113
113
  fs.mkdirSync(logDir, { recursive: true });
114
114
  }
115
- } catch (err) {
115
+ } catch {
116
116
  this.logFile = void 0;
117
117
  if (!this.fileLoggingDisabledNotified) {
118
118
  this.fileLoggingDisabledNotified = true;
@@ -131,7 +131,7 @@ class Logger {
131
131
  if (this.logFile) {
132
132
  try {
133
133
  fs.appendFileSync(this.logFile, logLine);
134
- } catch (err) {
134
+ } catch {
135
135
  this.logFile = void 0;
136
136
  if (!this.fileLoggingDisabledNotified) {
137
137
  this.fileLoggingDisabledNotified = true;
@@ -6,7 +6,7 @@ import { EventEmitter } from "events";
6
6
  import * as fs from "fs";
7
7
  import * as path from "path";
8
8
  import { logger } from "./logger.js";
9
- function getEnv(key, defaultValue) {
9
+ function _getEnv(key, defaultValue) {
10
10
  const value = process.env[key];
11
11
  if (value === void 0) {
12
12
  if (defaultValue !== void 0) return defaultValue;
@@ -14,7 +14,7 @@ function getEnv(key, defaultValue) {
14
14
  }
15
15
  return value;
16
16
  }
17
- function getOptionalEnv(key) {
17
+ function _getOptionalEnv(key) {
18
18
  return process.env[key];
19
19
  }
20
20
  class MetricsCollector extends EventEmitter {
@@ -4,7 +4,7 @@ const __filename = __fileURLToPath(import.meta.url);
4
4
  const __dirname = __pathDirname(__filename);
5
5
  import { existsSync, readFileSync, writeFileSync } from "fs";
6
6
  import { join } from "path";
7
- function getEnv(key, defaultValue) {
7
+ function _getEnv(key, defaultValue) {
8
8
  const value = process.env[key];
9
9
  if (value === void 0) {
10
10
  if (defaultValue !== void 0) return defaultValue;
@@ -12,7 +12,7 @@ function getEnv(key, defaultValue) {
12
12
  }
13
13
  return value;
14
14
  }
15
- function getOptionalEnv(key) {
15
+ function _getOptionalEnv(key) {
16
16
  return process.env[key];
17
17
  }
18
18
  class ProgressTracker {
@@ -232,7 +232,7 @@ class LazyContextLoader {
232
232
  ...row,
233
233
  metadata: JSON.parse(row.metadata || "{}")
234
234
  };
235
- } catch (error) {
235
+ } catch {
236
236
  if (frameId.startsWith("frame-")) {
237
237
  return {
238
238
  id: frameId,
@@ -89,14 +89,14 @@ class PerformanceBenchmark {
89
89
  }
90
90
  const populateDuration = performance.now() - populateStart;
91
91
  const accessStart = performance.now();
92
- let hits = 0;
93
- let misses = 0;
92
+ let _hits = 0;
93
+ let _misses = 0;
94
94
  for (let i = 0; i < accessPatterns; i++) {
95
95
  const index = Math.floor(Math.random() * itemCount * 1.2);
96
96
  const key = `item-${index}`;
97
97
  const result2 = cache.get(key);
98
- if (result2) hits++;
99
- else misses++;
98
+ if (result2) _hits++;
99
+ else _misses++;
100
100
  }
101
101
  const accessDuration = performance.now() - accessStart;
102
102
  const stats = cache.getStats();
@@ -134,7 +134,7 @@ class PerformanceBenchmark {
134
134
  let frames = [];
135
135
  try {
136
136
  frames = db.prepare("SELECT id FROM frames ORDER BY updated_at DESC LIMIT ?").all(frameCount);
137
- } catch (error) {
137
+ } catch {
138
138
  logger.warn("Frames table not found, using mock data for benchmark");
139
139
  frames = Array.from({ length: Math.min(frameCount, 10) }, (_, i) => ({
140
140
  id: `frame-${i}`
@@ -205,9 +205,9 @@ class PerformanceBenchmark {
205
205
  const suiteStart = performance.now();
206
206
  logger.info("Starting Performance Benchmark Suite");
207
207
  const tasksFile = join(projectRoot, ".stackmemory", "tasks.jsonl");
208
- const jsonlResult = await this.benchmarkJSONLParsing(tasksFile);
209
- const cacheResult = await this.benchmarkContextCache();
210
- const lazyResult = await this.benchmarkLazyLoading(db, projectId);
208
+ const _jsonlResult = await this.benchmarkJSONLParsing(tasksFile);
209
+ const _cacheResult = await this.benchmarkContextCache();
210
+ const _lazyResult = await this.benchmarkLazyLoading(db, projectId);
211
211
  const totalDuration = performance.now() - suiteStart;
212
212
  const averageImprovement = this.results.filter((r) => r.improvement !== void 0).reduce((sum, r) => sum + (r.improvement || 0), 0) / this.results.filter((r) => r.improvement !== void 0).length;
213
213
  const suite = {
@@ -4,7 +4,7 @@ const __filename = __fileURLToPath(import.meta.url);
4
4
  const __dirname = __pathDirname(__filename);
5
5
  import { logger } from "../monitoring/logger.js";
6
6
  import { getQueryStatistics } from "../trace/db-trace-wrapper.js";
7
- function getEnv(key, defaultValue) {
7
+ function _getEnv(key, defaultValue) {
8
8
  const value = process.env[key];
9
9
  if (value === void 0) {
10
10
  if (defaultValue !== void 0) return defaultValue;
@@ -12,7 +12,7 @@ function getEnv(key, defaultValue) {
12
12
  }
13
13
  return value;
14
14
  }
15
- function getOptionalEnv(key) {
15
+ function _getOptionalEnv(key) {
16
16
  return process.env[key];
17
17
  }
18
18
  class PerformanceProfiler {