@stackmemoryai/stackmemory 1.10.4 → 1.12.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 (95) hide show
  1. package/README.md +104 -23
  2. package/dist/src/cli/claude-sm.js +266 -84
  3. package/dist/src/cli/codex-sm.js +185 -33
  4. package/dist/src/cli/commands/bench.js +209 -2
  5. package/dist/src/cli/commands/cache.js +126 -0
  6. package/dist/src/cli/commands/daemon.js +41 -0
  7. package/dist/src/cli/commands/handoff.js +40 -9
  8. package/dist/src/cli/commands/onboard.js +70 -3
  9. package/dist/src/cli/commands/optimize.js +117 -0
  10. package/dist/src/cli/commands/orchestrate.js +230 -5
  11. package/dist/src/cli/commands/orchestrator.js +312 -24
  12. package/dist/src/cli/commands/pack.js +322 -0
  13. package/dist/src/cli/commands/search.js +40 -1
  14. package/dist/src/cli/commands/setup.js +177 -7
  15. package/dist/src/cli/commands/skills.js +10 -1
  16. package/dist/src/cli/commands/state.js +265 -0
  17. package/dist/src/cli/commands/wiki.js +33 -0
  18. package/dist/src/cli/gemini-sm.js +19 -29
  19. package/dist/src/cli/index.js +90 -29
  20. package/dist/src/cli/opencode-sm.js +38 -21
  21. package/dist/src/cli/utils/determinism-watcher.js +66 -0
  22. package/dist/src/cli/utils/real-cli-bin.js +44 -0
  23. package/dist/src/core/cache/content-cache.js +238 -0
  24. package/dist/src/core/cache/index.js +11 -0
  25. package/dist/src/core/cache/token-estimator.js +16 -0
  26. package/dist/src/core/context/frame-database.js +38 -30
  27. package/dist/src/core/cross-search/cross-project-search.js +269 -0
  28. package/dist/src/core/{merge → cross-search}/index.js +6 -4
  29. package/dist/src/core/database/sqlite-adapter.js +0 -83
  30. package/dist/src/core/extensions/provider-adapter.js +5 -0
  31. package/dist/src/core/models/model-router.js +22 -2
  32. package/dist/src/core/monitoring/logger.js +2 -1
  33. package/dist/src/core/optimization/trace-optimizer.js +413 -0
  34. package/dist/src/core/provenance/confidence-scorer.js +128 -0
  35. package/dist/src/core/provenance/index.js +40 -0
  36. package/dist/src/core/provenance/provenance-store.js +194 -0
  37. package/dist/src/core/provenance/types.js +82 -0
  38. package/dist/src/core/session/project-handoff.js +64 -0
  39. package/dist/src/core/session/session-manager.js +28 -0
  40. package/dist/src/core/shared-state/canonical-store.js +564 -0
  41. package/dist/src/core/skill-packs/index.js +18 -0
  42. package/dist/src/core/skill-packs/parser.js +42 -0
  43. package/dist/src/core/skill-packs/registry.js +224 -0
  44. package/dist/src/core/skill-packs/types.js +66 -0
  45. package/dist/src/core/trace/trace-event-store.js +282 -0
  46. package/dist/src/core/trace/trace-event.js +4 -0
  47. package/dist/src/core/wiki/wiki-compiler.js +219 -0
  48. package/dist/src/daemon/daemon-config.js +7 -0
  49. package/dist/src/daemon/services/github-service.js +126 -0
  50. package/dist/src/daemon/unified-daemon.js +30 -0
  51. package/dist/src/features/sweep/pty-wrapper.js +13 -5
  52. package/dist/src/hooks/schemas.js +2 -0
  53. package/dist/src/integrations/claude-code/subagent-client.js +89 -0
  54. package/dist/src/integrations/github/pr-state.js +158 -0
  55. package/dist/src/integrations/linear/client.js +4 -1
  56. package/dist/src/integrations/mcp/handlers/cross-search-handlers.js +188 -0
  57. package/dist/src/integrations/mcp/handlers/index.js +40 -59
  58. package/dist/src/integrations/mcp/server.js +425 -311
  59. package/dist/src/integrations/mcp/tool-alias-registry.js +370 -0
  60. package/dist/src/integrations/mcp/tool-definitions.js +98 -229
  61. package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +3 -40
  62. package/dist/src/integrations/ralph/learning/pattern-learner.js +1 -20
  63. package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -2
  64. package/dist/src/mcp/stackmemory-mcp-server.js +315 -0
  65. package/dist/src/orchestrators/multimodal/determinism.js +243 -0
  66. package/dist/src/orchestrators/multimodal/harness.js +147 -77
  67. package/dist/src/orchestrators/multimodal/providers.js +44 -3
  68. package/dist/src/utils/hook-installer.js +8 -8
  69. package/package.json +10 -1
  70. package/packs/coding/python-fastapi/instructions.md +60 -0
  71. package/packs/coding/python-fastapi/pack.yaml +28 -0
  72. package/packs/coding/typescript-react/instructions.md +47 -0
  73. package/packs/coding/typescript-react/pack.yaml +28 -0
  74. package/packs/core/commands/capture.md +32 -0
  75. package/packs/core/commands/learn.md +73 -0
  76. package/packs/core/commands/next.md +36 -0
  77. package/packs/core/commands/restart.md +58 -0
  78. package/packs/core/commands/restore.md +29 -0
  79. package/packs/core/commands/start.md +57 -0
  80. package/packs/core/commands/stop.md +65 -0
  81. package/packs/core/commands/summary.md +40 -0
  82. package/packs/core/manifest.json +24 -0
  83. package/packs/ops/decision-recovery/instructions.md +65 -0
  84. package/packs/ops/decision-recovery/pack.yaml +89 -0
  85. package/templates/claude-hooks/doc-ingest.js +76 -0
  86. package/dist/src/cli/commands/team.js +0 -168
  87. package/dist/src/core/context/shared-context-layer.js +0 -620
  88. package/dist/src/core/context/stack-merge-resolver.js +0 -748
  89. package/dist/src/core/merge/conflict-detector.js +0 -430
  90. package/dist/src/core/merge/resolution-engine.js +0 -557
  91. package/dist/src/core/merge/stack-diff.js +0 -531
  92. package/dist/src/core/merge/unified-merge-resolver.js +0 -302
  93. package/dist/src/integrations/mcp/handlers/cord-handlers.js +0 -397
  94. package/dist/src/integrations/mcp/handlers/team-handlers.js +0 -211
  95. /package/dist/src/core/{merge → cache}/types.js +0 -0
@@ -0,0 +1,224 @@
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 Database from "better-sqlite3";
6
+ import * as path from "path";
7
+ import * as fs from "fs";
8
+ import { logger } from "../monitoring/logger.js";
9
+ import { SkillPackManifestSchema } from "./types.js";
10
+ const SCHEMA_VERSION = 1;
11
+ const SCHEMA_SQL = `
12
+ CREATE TABLE IF NOT EXISTS schema_version (
13
+ version INTEGER PRIMARY KEY
14
+ );
15
+
16
+ CREATE TABLE IF NOT EXISTS packs (
17
+ name TEXT PRIMARY KEY,
18
+ version TEXT NOT NULL,
19
+ manifest TEXT NOT NULL,
20
+ instructions TEXT,
21
+ installed_at TEXT NOT NULL,
22
+ source TEXT
23
+ );
24
+
25
+ CREATE VIRTUAL TABLE IF NOT EXISTS packs_fts USING fts5(
26
+ name,
27
+ description,
28
+ instructions,
29
+ content='packs',
30
+ content_rowid='rowid'
31
+ );
32
+
33
+ CREATE TRIGGER IF NOT EXISTS packs_ai AFTER INSERT ON packs BEGIN
34
+ INSERT INTO packs_fts(rowid, name, description, instructions)
35
+ VALUES (new.rowid, new.name,
36
+ json_extract(new.manifest, '$.description'),
37
+ COALESCE(new.instructions, ''));
38
+ END;
39
+
40
+ CREATE TRIGGER IF NOT EXISTS packs_ad AFTER DELETE ON packs BEGIN
41
+ INSERT INTO packs_fts(packs_fts, rowid, name, description, instructions)
42
+ VALUES ('delete', old.rowid, old.name,
43
+ json_extract(old.manifest, '$.description'),
44
+ COALESCE(old.instructions, ''));
45
+ END;
46
+
47
+ CREATE TRIGGER IF NOT EXISTS packs_au AFTER UPDATE ON packs BEGIN
48
+ INSERT INTO packs_fts(packs_fts, rowid, name, description, instructions)
49
+ VALUES ('delete', old.rowid, old.name,
50
+ json_extract(old.manifest, '$.description'),
51
+ COALESCE(old.instructions, ''));
52
+ INSERT INTO packs_fts(rowid, name, description, instructions)
53
+ VALUES (new.rowid, new.name,
54
+ json_extract(new.manifest, '$.description'),
55
+ COALESCE(new.instructions, ''));
56
+ END;
57
+ `;
58
+ function getDefaultDbPath() {
59
+ const home = process.env["HOME"] || process.env["USERPROFILE"] || "/tmp";
60
+ return path.join(home, ".stackmemory", "skill-packs.db");
61
+ }
62
+ function rowToPack(row) {
63
+ const manifest = SkillPackManifestSchema.parse(
64
+ JSON.parse(row["manifest"])
65
+ );
66
+ const source = row["source"];
67
+ const metadata = {
68
+ installedAt: row["installed_at"],
69
+ ...source ? { source } : {}
70
+ };
71
+ return {
72
+ manifest,
73
+ instructions: row["instructions"] || void 0,
74
+ metadata
75
+ };
76
+ }
77
+ class SkillPackRegistry {
78
+ db;
79
+ dbPath;
80
+ constructor(dbPath) {
81
+ this.dbPath = dbPath || getDefaultDbPath();
82
+ const dir = path.dirname(this.dbPath);
83
+ if (!fs.existsSync(dir)) {
84
+ fs.mkdirSync(dir, { recursive: true });
85
+ }
86
+ this.db = new Database(this.dbPath);
87
+ this.db.pragma("journal_mode = WAL");
88
+ this.db.pragma("busy_timeout = 5000");
89
+ this.initSchema();
90
+ }
91
+ initSchema() {
92
+ const versionRow = (() => {
93
+ try {
94
+ return this.db.prepare(
95
+ "SELECT name FROM sqlite_master WHERE type='table' AND name='schema_version'"
96
+ ).get();
97
+ } catch {
98
+ return void 0;
99
+ }
100
+ })();
101
+ if (!versionRow) {
102
+ this.db.exec(SCHEMA_SQL);
103
+ this.db.prepare("INSERT OR REPLACE INTO schema_version (version) VALUES (?)").run(SCHEMA_VERSION);
104
+ logger.debug("SkillPackRegistry: created schema v" + SCHEMA_VERSION);
105
+ }
106
+ }
107
+ // ============================================================
108
+ // CRUD
109
+ // ============================================================
110
+ /**
111
+ * Install or update a skill pack. Upserts by name.
112
+ */
113
+ install(pack) {
114
+ const now = (/* @__PURE__ */ new Date()).toISOString();
115
+ const manifestJson = JSON.stringify(pack.manifest);
116
+ this.db.prepare(
117
+ `INSERT INTO packs (name, version, manifest, instructions, installed_at, source)
118
+ VALUES (?, ?, ?, ?, ?, ?)
119
+ ON CONFLICT(name) DO UPDATE SET
120
+ version = excluded.version,
121
+ manifest = excluded.manifest,
122
+ instructions = excluded.instructions,
123
+ installed_at = excluded.installed_at,
124
+ source = excluded.source`
125
+ ).run(
126
+ pack.manifest.name,
127
+ pack.manifest.version,
128
+ manifestJson,
129
+ pack.instructions ?? null,
130
+ pack.metadata?.installedAt ?? now,
131
+ pack.metadata?.source ?? null
132
+ );
133
+ logger.debug(
134
+ `SkillPackRegistry: installed ${pack.manifest.name}@${pack.manifest.version}`
135
+ );
136
+ }
137
+ /**
138
+ * Uninstall a pack by name.
139
+ */
140
+ uninstall(name) {
141
+ const result = this.db.prepare("DELETE FROM packs WHERE name = ?").run(name);
142
+ return result.changes > 0;
143
+ }
144
+ /**
145
+ * Get a single pack by name.
146
+ */
147
+ get(name) {
148
+ const row = this.db.prepare("SELECT * FROM packs WHERE name = ?").get(name);
149
+ return row ? rowToPack(row) : void 0;
150
+ }
151
+ /**
152
+ * List packs with optional filters.
153
+ */
154
+ list(query) {
155
+ const conditions = [];
156
+ const params = [];
157
+ if (query?.namespace) {
158
+ conditions.push("name LIKE ? || '/%'");
159
+ params.push(query.namespace);
160
+ }
161
+ if (query?.runtime) {
162
+ conditions.push("json_extract(manifest, '$.runtime.type') = ?");
163
+ params.push(query.runtime);
164
+ }
165
+ const where = conditions.length ? "WHERE " + conditions.join(" AND ") : "";
166
+ const sql = `SELECT * FROM packs ${where} ORDER BY name`;
167
+ const rows = this.db.prepare(sql).all(...params);
168
+ return rows.map(rowToPack);
169
+ }
170
+ /**
171
+ * Find the pack that provides a given MCP tool name.
172
+ */
173
+ getByTool(toolName) {
174
+ const rows = this.db.prepare("SELECT * FROM packs").all();
175
+ for (const row of rows) {
176
+ const manifest = JSON.parse(
177
+ row["manifest"]
178
+ );
179
+ const tools = manifest.mcp?.tools ?? [];
180
+ if (tools.some((t) => t.name === toolName)) {
181
+ return rowToPack(row);
182
+ }
183
+ }
184
+ return void 0;
185
+ }
186
+ /**
187
+ * Full-text search across pack name, description, and instructions.
188
+ */
189
+ search(query) {
190
+ const sanitized = query.replace(/[^\w\s/-]/g, "").split(/\s+/).filter(Boolean).map((t) => `"${t}"`).join(" ");
191
+ if (!sanitized) return [];
192
+ const rows = this.db.prepare(
193
+ `SELECT p.* FROM packs p
194
+ JOIN packs_fts f ON p.rowid = f.rowid
195
+ WHERE packs_fts MATCH ?
196
+ ORDER BY rank`
197
+ ).all(sanitized);
198
+ return rows.map(rowToPack);
199
+ }
200
+ // ============================================================
201
+ // LIFECYCLE
202
+ // ============================================================
203
+ close() {
204
+ this.db.close();
205
+ }
206
+ }
207
+ let registryInstance;
208
+ function getSkillPackRegistry(dbPath) {
209
+ if (!registryInstance) {
210
+ registryInstance = new SkillPackRegistry(dbPath);
211
+ }
212
+ return registryInstance;
213
+ }
214
+ function resetSkillPackRegistry() {
215
+ if (registryInstance) {
216
+ registryInstance.close();
217
+ registryInstance = void 0;
218
+ }
219
+ }
220
+ export {
221
+ SkillPackRegistry,
222
+ getSkillPackRegistry,
223
+ resetSkillPackRegistry
224
+ };
@@ -0,0 +1,66 @@
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 { z } from "zod";
6
+ const SemverSchema = z.string().regex(
7
+ /^\d+\.\d+\.\d+(-[\w.]+)?(\+[\w.]+)?$/,
8
+ "version must be valid semver (e.g. 1.0.0)"
9
+ );
10
+ const SkillPackRuntimeTypeSchema = z.enum([
11
+ "local",
12
+ "e2b",
13
+ "cua",
14
+ "modal"
15
+ ]);
16
+ const SkillPackRuntimeSchema = z.object({
17
+ type: SkillPackRuntimeTypeSchema.default("local"),
18
+ template: z.string().optional()
19
+ });
20
+ const SkillPackIngestionSchema = z.object({
21
+ sources: z.array(z.string()).default([]),
22
+ scope: z.string().optional()
23
+ });
24
+ const SkillPackOntologySchema = z.object({
25
+ entities: z.array(z.string()).default([]),
26
+ relations: z.array(z.string()).default([])
27
+ });
28
+ const SkillPackMcpToolSchema = z.object({
29
+ name: z.string().min(1),
30
+ description: z.string().min(1),
31
+ inputSchema: z.record(z.unknown()).optional()
32
+ });
33
+ const SkillPackMcpSchema = z.object({
34
+ tools: z.array(SkillPackMcpToolSchema).default([])
35
+ });
36
+ const SkillPackExampleSchema = z.object({
37
+ input: z.string().min(1),
38
+ output: z.string().min(1)
39
+ });
40
+ const PackNameSchema = z.string().min(1).regex(
41
+ /^[\w-]+\/[\w-]+$/,
42
+ 'name must be namespace/pack-name (e.g. "coding/typescript-react")'
43
+ );
44
+ const SkillPackManifestSchema = z.object({
45
+ name: PackNameSchema,
46
+ version: SemverSchema,
47
+ description: z.string().min(1),
48
+ author: z.string().min(1),
49
+ license: z.string().default("MIT"),
50
+ runtime: SkillPackRuntimeSchema.optional(),
51
+ ingestion: SkillPackIngestionSchema.optional(),
52
+ ontology: SkillPackOntologySchema.optional(),
53
+ mcp: SkillPackMcpSchema.optional(),
54
+ examples: z.array(SkillPackExampleSchema).optional(),
55
+ instructions: z.string().optional()
56
+ });
57
+ export {
58
+ SkillPackExampleSchema,
59
+ SkillPackIngestionSchema,
60
+ SkillPackManifestSchema,
61
+ SkillPackMcpSchema,
62
+ SkillPackMcpToolSchema,
63
+ SkillPackOntologySchema,
64
+ SkillPackRuntimeSchema,
65
+ SkillPackRuntimeTypeSchema
66
+ };
@@ -0,0 +1,282 @@
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 { v4 as uuidv4 } from "uuid";
6
+ import { logger } from "../monitoring/logger.js";
7
+ class TraceEventStore {
8
+ db;
9
+ constructor(db) {
10
+ this.db = db;
11
+ this.initSchema();
12
+ }
13
+ initSchema() {
14
+ this.db.exec(`
15
+ CREATE TABLE IF NOT EXISTS trace_events (
16
+ id TEXT PRIMARY KEY,
17
+ timestamp TEXT NOT NULL,
18
+ session_id TEXT NOT NULL,
19
+ trace_id TEXT NOT NULL,
20
+ parent_trace_id TEXT,
21
+ tenant_id TEXT NOT NULL DEFAULT 'local',
22
+ actor_host TEXT NOT NULL DEFAULT 'unknown',
23
+ actor_agent TEXT NOT NULL DEFAULT 'stackmemory-mcp',
24
+ actor_user TEXT NOT NULL DEFAULT 'anonymous',
25
+ operation TEXT NOT NULL,
26
+ inputs TEXT NOT NULL DEFAULT '{}',
27
+ outputs TEXT NOT NULL DEFAULT '{}',
28
+ tokens_in INTEGER NOT NULL DEFAULT 0,
29
+ tokens_out INTEGER NOT NULL DEFAULT 0,
30
+ cost_usd REAL NOT NULL DEFAULT 0,
31
+ duration_ms INTEGER NOT NULL DEFAULT 0,
32
+ score REAL,
33
+ feedback TEXT,
34
+ provenance TEXT NOT NULL DEFAULT '{"sources":[],"derivation":[],"confidence":1}',
35
+ error TEXT,
36
+ tags TEXT
37
+ );
38
+
39
+ CREATE INDEX IF NOT EXISTS idx_te_session ON trace_events(session_id);
40
+ CREATE INDEX IF NOT EXISTS idx_te_trace ON trace_events(trace_id);
41
+ CREATE INDEX IF NOT EXISTS idx_te_operation ON trace_events(operation);
42
+ CREATE INDEX IF NOT EXISTS idx_te_timestamp ON trace_events(timestamp);
43
+ CREATE INDEX IF NOT EXISTS idx_te_score ON trace_events(score) WHERE score IS NOT NULL;
44
+ `);
45
+ }
46
+ // ------------------------------------------------------------------
47
+ // Write
48
+ // ------------------------------------------------------------------
49
+ /**
50
+ * Record a trace event. Generates ID if not present in trace_id.
51
+ */
52
+ record(event) {
53
+ const id = uuidv4();
54
+ this.db.prepare(
55
+ `INSERT INTO trace_events (
56
+ id, timestamp, session_id, trace_id, parent_trace_id, tenant_id,
57
+ actor_host, actor_agent, actor_user,
58
+ operation, inputs, outputs,
59
+ tokens_in, tokens_out, cost_usd, duration_ms,
60
+ score, feedback, provenance, error, tags
61
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
62
+ ).run(
63
+ id,
64
+ event.timestamp,
65
+ event.session_id,
66
+ event.trace_id,
67
+ event.parent_trace_id ?? null,
68
+ event.tenant_id,
69
+ event.actor.host,
70
+ event.actor.agent,
71
+ event.actor.user,
72
+ event.operation,
73
+ JSON.stringify(event.inputs),
74
+ JSON.stringify(event.outputs),
75
+ event.tokens_in,
76
+ event.tokens_out,
77
+ event.cost_usd,
78
+ event.duration_ms,
79
+ event.score ?? null,
80
+ event.feedback ?? null,
81
+ JSON.stringify(event.provenance),
82
+ event.error ?? null,
83
+ event.tags ? JSON.stringify(event.tags) : null
84
+ );
85
+ logger.debug(`TraceEvent recorded: ${event.operation} [${id}]`);
86
+ return id;
87
+ }
88
+ /**
89
+ * Record multiple events in a single transaction.
90
+ */
91
+ recordBatch(events) {
92
+ const ids = [];
93
+ this.db.transaction(() => {
94
+ for (const event of events) {
95
+ ids.push(this.record(event));
96
+ }
97
+ })();
98
+ return ids;
99
+ }
100
+ /**
101
+ * Add score and/or feedback to an existing event.
102
+ */
103
+ annotate(id, annotation) {
104
+ const sets = [];
105
+ const params = [];
106
+ if (annotation.score !== void 0) {
107
+ sets.push("score = ?");
108
+ params.push(annotation.score);
109
+ }
110
+ if (annotation.feedback !== void 0) {
111
+ sets.push("feedback = ?");
112
+ params.push(annotation.feedback);
113
+ }
114
+ if (sets.length === 0) return false;
115
+ params.push(id);
116
+ const result = this.db.prepare(`UPDATE trace_events SET ${sets.join(", ")} WHERE id = ?`).run(...params);
117
+ return result.changes > 0;
118
+ }
119
+ // ------------------------------------------------------------------
120
+ // Read
121
+ // ------------------------------------------------------------------
122
+ /**
123
+ * Get a single event by ID.
124
+ */
125
+ get(id) {
126
+ const row = this.db.prepare("SELECT * FROM trace_events WHERE id = ?").get(id);
127
+ return row ? this.rowToEvent(row) : void 0;
128
+ }
129
+ /**
130
+ * Query events with filters.
131
+ */
132
+ query(filter = {}) {
133
+ const conditions = [];
134
+ const params = [];
135
+ if (filter.session_id) {
136
+ conditions.push("session_id = ?");
137
+ params.push(filter.session_id);
138
+ }
139
+ if (filter.operation) {
140
+ conditions.push("operation = ?");
141
+ params.push(filter.operation);
142
+ }
143
+ if (filter.min_score !== void 0) {
144
+ conditions.push("score >= ?");
145
+ params.push(filter.min_score);
146
+ }
147
+ if (filter.has_feedback) {
148
+ conditions.push("feedback IS NOT NULL");
149
+ }
150
+ if (filter.since) {
151
+ conditions.push("timestamp >= ?");
152
+ params.push(filter.since);
153
+ }
154
+ if (filter.until) {
155
+ conditions.push("timestamp <= ?");
156
+ params.push(filter.until);
157
+ }
158
+ const where = conditions.length ? "WHERE " + conditions.join(" AND ") : "";
159
+ const limit = filter.limit ?? 100;
160
+ const offset = filter.offset ?? 0;
161
+ const rows = this.db.prepare(
162
+ `SELECT * FROM trace_events ${where} ORDER BY timestamp DESC LIMIT ? OFFSET ?`
163
+ ).all(...params, limit, offset);
164
+ return rows.map((r) => this.rowToEvent(r));
165
+ }
166
+ /**
167
+ * Get events for a specific session.
168
+ */
169
+ getBySession(sessionId) {
170
+ return this.query({ session_id: sessionId, limit: 1e3 });
171
+ }
172
+ /**
173
+ * Get events with scores (for GEPA consumption).
174
+ */
175
+ getScoredEvents(minScore) {
176
+ return this.query({
177
+ min_score: minScore ?? 0,
178
+ limit: 500
179
+ });
180
+ }
181
+ /**
182
+ * Get events with feedback (for GEPA ASI consumption).
183
+ */
184
+ getFeedbackEvents() {
185
+ return this.query({ has_feedback: true, limit: 500 });
186
+ }
187
+ // ------------------------------------------------------------------
188
+ // Stats
189
+ // ------------------------------------------------------------------
190
+ /**
191
+ * Aggregate statistics across all events.
192
+ */
193
+ getStats(filter) {
194
+ const conditions = [];
195
+ const params = [];
196
+ if (filter?.session_id) {
197
+ conditions.push("session_id = ?");
198
+ params.push(filter.session_id);
199
+ }
200
+ if (filter?.since) {
201
+ conditions.push("timestamp >= ?");
202
+ params.push(filter.since);
203
+ }
204
+ const where = conditions.length ? "WHERE " + conditions.join(" AND ") : "";
205
+ const agg = this.db.prepare(
206
+ `SELECT
207
+ COUNT(*) as total_events,
208
+ COALESCE(SUM(tokens_in), 0) as total_tokens_in,
209
+ COALESCE(SUM(tokens_out), 0) as total_tokens_out,
210
+ COALESCE(SUM(cost_usd), 0) as total_cost_usd,
211
+ AVG(score) as avg_score,
212
+ SUM(CASE WHEN feedback IS NOT NULL THEN 1 ELSE 0 END) as events_with_feedback,
213
+ SUM(CASE WHEN error IS NOT NULL THEN 1 ELSE 0 END) as events_with_errors
214
+ FROM trace_events ${where}`
215
+ ).get(...params);
216
+ const opRows = this.db.prepare(
217
+ `SELECT operation, COUNT(*) as cnt FROM trace_events ${where} GROUP BY operation ORDER BY cnt DESC`
218
+ ).all(...params);
219
+ const hostRows = this.db.prepare(
220
+ `SELECT actor_host, COUNT(*) as cnt FROM trace_events ${where} GROUP BY actor_host ORDER BY cnt DESC`
221
+ ).all(...params);
222
+ const operations = {};
223
+ for (const r of opRows) operations[r.operation] = r.cnt;
224
+ const hosts = {};
225
+ for (const r of hostRows) hosts[r.actor_host] = r.cnt;
226
+ return {
227
+ total_events: agg["total_events"] || 0,
228
+ total_tokens_in: agg["total_tokens_in"] || 0,
229
+ total_tokens_out: agg["total_tokens_out"] || 0,
230
+ total_cost_usd: agg["total_cost_usd"] || 0,
231
+ avg_score: agg["avg_score"],
232
+ events_with_feedback: agg["events_with_feedback"] || 0,
233
+ events_with_errors: agg["events_with_errors"] || 0,
234
+ operations,
235
+ hosts
236
+ };
237
+ }
238
+ // ------------------------------------------------------------------
239
+ // Lifecycle
240
+ // ------------------------------------------------------------------
241
+ /**
242
+ * Delete events older than the given ISO timestamp.
243
+ */
244
+ evict(olderThan) {
245
+ const result = this.db.prepare("DELETE FROM trace_events WHERE timestamp < ?").run(olderThan);
246
+ return result.changes;
247
+ }
248
+ // ------------------------------------------------------------------
249
+ // Helpers
250
+ // ------------------------------------------------------------------
251
+ rowToEvent(row) {
252
+ const provenance = JSON.parse(row.provenance);
253
+ const actor = {
254
+ host: row.actor_host,
255
+ agent: row.actor_agent,
256
+ user: row.actor_user
257
+ };
258
+ return {
259
+ timestamp: row.timestamp,
260
+ session_id: row.session_id,
261
+ trace_id: row.trace_id,
262
+ parent_trace_id: row.parent_trace_id ?? void 0,
263
+ tenant_id: row.tenant_id,
264
+ actor,
265
+ operation: row.operation,
266
+ inputs: JSON.parse(row.inputs),
267
+ outputs: JSON.parse(row.outputs),
268
+ tokens_in: row.tokens_in,
269
+ tokens_out: row.tokens_out,
270
+ cost_usd: row.cost_usd,
271
+ duration_ms: row.duration_ms,
272
+ score: row.score ?? void 0,
273
+ feedback: row.feedback ?? void 0,
274
+ provenance,
275
+ error: row.error ?? void 0,
276
+ tags: row.tags ? JSON.parse(row.tags) : void 0
277
+ };
278
+ }
279
+ }
280
+ export {
281
+ TraceEventStore
282
+ };
@@ -0,0 +1,4 @@
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);