mergen-server 1.4.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 (308) hide show
  1. package/LICENSE +57 -0
  2. package/README.md +96 -0
  3. package/dist/__stubs__/calibration.js +471 -0
  4. package/dist/__stubs__/causal.js +601 -0
  5. package/dist/__stubs__/closed-source.js +150 -0
  6. package/dist/__stubs__/license.js +35 -0
  7. package/dist/__stubs__/plans.js +197 -0
  8. package/dist/app.js +467 -0
  9. package/dist/cli.js +250 -0
  10. package/dist/commands/agent-identity.js +79 -0
  11. package/dist/commands/gate.js +107 -0
  12. package/dist/commands/github.js +632 -0
  13. package/dist/commands/incident.js +644 -0
  14. package/dist/commands/policy.js +710 -0
  15. package/dist/commands/scan.js +243 -0
  16. package/dist/commands/setup.js +1218 -0
  17. package/dist/commands/shared.js +109 -0
  18. package/dist/commands/team.js +809 -0
  19. package/dist/datadog/blame-attribution.js +228 -0
  20. package/dist/datadog/client.js +159 -0
  21. package/dist/datadog/compactor.js +169 -0
  22. package/dist/datadog/fingerprinter.js +34 -0
  23. package/dist/datadog/incident-state.js +21 -0
  24. package/dist/datadog/line-matcher.js +56 -0
  25. package/dist/datadog/memory-store.js +399 -0
  26. package/dist/datadog/otel-trace.js +67 -0
  27. package/dist/index.js +505 -0
  28. package/dist/intelligence/action-risk.js +132 -0
  29. package/dist/intelligence/activity-feed.js +40 -0
  30. package/dist/intelligence/agent-identity.js +115 -0
  31. package/dist/intelligence/agent-pipeline.js +329 -0
  32. package/dist/intelligence/agent-profiles.js +81 -0
  33. package/dist/intelligence/ai-commit.js +16 -0
  34. package/dist/intelligence/approval-analytics.js +42 -0
  35. package/dist/intelligence/approval-events.js +5 -0
  36. package/dist/intelligence/arch-boundaries.js +94 -0
  37. package/dist/intelligence/arch-graph.js +117 -0
  38. package/dist/intelligence/autonomy.js +397 -0
  39. package/dist/intelligence/azure-broker.js +57 -0
  40. package/dist/intelligence/azure-proxy-sessions.js +49 -0
  41. package/dist/intelligence/baseline.js +1 -0
  42. package/dist/intelligence/behavior-baseline.js +134 -0
  43. package/dist/intelligence/billing.js +1 -0
  44. package/dist/intelligence/blast-radius.js +224 -0
  45. package/dist/intelligence/bypass.js +157 -0
  46. package/dist/intelligence/calibration-classifier.js +77 -0
  47. package/dist/intelligence/calibration-git-sync.js +74 -0
  48. package/dist/intelligence/calibration.js +1 -0
  49. package/dist/intelligence/cascade-detector.js +58 -0
  50. package/dist/intelligence/case-study-generator.js +126 -0
  51. package/dist/intelligence/causal-graph.js +126 -0
  52. package/dist/intelligence/causal.js +1 -0
  53. package/dist/intelligence/change-risk.js +102 -0
  54. package/dist/intelligence/compliance-report.js +134 -0
  55. package/dist/intelligence/confidence-report.js +85 -0
  56. package/dist/intelligence/corpus-to-policy.js +140 -0
  57. package/dist/intelligence/credential-broker.js +112 -0
  58. package/dist/intelligence/credential-misuse.js +39 -0
  59. package/dist/intelligence/debug-sessions.js +108 -0
  60. package/dist/intelligence/decision-escalation.js +36 -0
  61. package/dist/intelligence/decision-memory.js +47 -0
  62. package/dist/intelligence/decision-similarity.js +85 -0
  63. package/dist/intelligence/default-safety-tests.js +53 -0
  64. package/dist/intelligence/degradation-watcher.js +108 -0
  65. package/dist/intelligence/detector-plugins.js +70 -0
  66. package/dist/intelligence/detectors.js +1 -0
  67. package/dist/intelligence/device-auth.js +138 -0
  68. package/dist/intelligence/diff-size.js +42 -0
  69. package/dist/intelligence/ebpf-verify.js +45 -0
  70. package/dist/intelligence/enterprise-policy-engine.js +1122 -0
  71. package/dist/intelligence/error-fingerprint.js +1 -0
  72. package/dist/intelligence/execution-gate.js +100 -0
  73. package/dist/intelligence/execution-mode.js +22 -0
  74. package/dist/intelligence/gate-analytics.js +256 -0
  75. package/dist/intelligence/gate-decision.js +64 -0
  76. package/dist/intelligence/gcp-broker.js +50 -0
  77. package/dist/intelligence/git-adr-sync.js +207 -0
  78. package/dist/intelligence/hitl-hold.js +460 -0
  79. package/dist/intelligence/hypothesis-history.js +93 -0
  80. package/dist/intelligence/idp-identity.js +83 -0
  81. package/dist/intelligence/impl-critic.js +216 -0
  82. package/dist/intelligence/incident-autopilot.js +622 -0
  83. package/dist/intelligence/incident-replay.js +162 -0
  84. package/dist/intelligence/incident-result-cache.js +33 -0
  85. package/dist/intelligence/incident-similarity.js +58 -0
  86. package/dist/intelligence/infra-detectors.js +312 -0
  87. package/dist/intelligence/license.js +185 -0
  88. package/dist/intelligence/llm-spokesperson.js +62 -0
  89. package/dist/intelligence/mcp-prompts.js +320 -0
  90. package/dist/intelligence/mcp-resources.js +171 -0
  91. package/dist/intelligence/normalize.js +108 -0
  92. package/dist/intelligence/notifications.js +83 -0
  93. package/dist/intelligence/oidc-issuer.js +87 -0
  94. package/dist/intelligence/override-corpus.js +597 -0
  95. package/dist/intelligence/planning-gate.js +84 -0
  96. package/dist/intelligence/plans.js +223 -0
  97. package/dist/intelligence/platt-scaling.js +164 -0
  98. package/dist/intelligence/policy-proposals.js +84 -0
  99. package/dist/intelligence/policy-suggester.js +161 -0
  100. package/dist/intelligence/policy-sync.js +103 -0
  101. package/dist/intelligence/policy-test-runner.js +35 -0
  102. package/dist/intelligence/postmortem-parser.js +181 -0
  103. package/dist/intelligence/postmortem-retrieval.js +224 -0
  104. package/dist/intelligence/postmortem-store.js +596 -0
  105. package/dist/intelligence/pr-commenter.js +81 -0
  106. package/dist/intelligence/pr-policy-gate.js +38 -0
  107. package/dist/intelligence/pr-shadow-analyzer.js +229 -0
  108. package/dist/intelligence/process-tree.js +108 -0
  109. package/dist/intelligence/prompts.js +1 -0
  110. package/dist/intelligence/replay.js +38 -0
  111. package/dist/intelligence/repro-steps.js +1 -0
  112. package/dist/intelligence/resource-extractors.js +0 -0
  113. package/dist/intelligence/resource-file-context.js +138 -0
  114. package/dist/intelligence/risk-score.js +22 -0
  115. package/dist/intelligence/rollback.js +137 -0
  116. package/dist/intelligence/runbook-updater.js +143 -0
  117. package/dist/intelligence/sandbox.js +194 -0
  118. package/dist/intelligence/script-trust.js +104 -0
  119. package/dist/intelligence/session-metrics.js +67 -0
  120. package/dist/intelligence/session-threat-tracker.js +231 -0
  121. package/dist/intelligence/shadow-digest-cron.js +81 -0
  122. package/dist/intelligence/shadow-log.js +284 -0
  123. package/dist/intelligence/shell-ast.js +145 -0
  124. package/dist/intelligence/siem-forward.js +104 -0
  125. package/dist/intelligence/slack-digest.js +151 -0
  126. package/dist/intelligence/slack-override-loop.js +217 -0
  127. package/dist/intelligence/slack-routing.js +67 -0
  128. package/dist/intelligence/slack.js +900 -0
  129. package/dist/intelligence/sql-ast.js +31 -0
  130. package/dist/intelligence/team.js +1 -0
  131. package/dist/intelligence/telemetry.js +76 -0
  132. package/dist/intelligence/threshold-optimizer.js +75 -0
  133. package/dist/intelligence/token-budget.js +66 -0
  134. package/dist/intelligence/tool-guard.js +708 -0
  135. package/dist/intelligence/tool-manifest 2.js +74 -0
  136. package/dist/intelligence/tool-manifest.js +125 -0
  137. package/dist/intelligence/tools-analysis.js +857 -0
  138. package/dist/intelligence/tools-arch.js +152 -0
  139. package/dist/intelligence/tools-autonomy.js +495 -0
  140. package/dist/intelligence/tools-blast-radius.js +140 -0
  141. package/dist/intelligence/tools-browser.js +431 -0
  142. package/dist/intelligence/tools-change-timeline.js +154 -0
  143. package/dist/intelligence/tools-credentials.js +117 -0
  144. package/dist/intelligence/tools-datadog.js +233 -0
  145. package/dist/intelligence/tools-debug-sessions.js +209 -0
  146. package/dist/intelligence/tools-discovery.js +284 -0
  147. package/dist/intelligence/tools-infra.js +612 -0
  148. package/dist/intelligence/tools-intent.js +128 -0
  149. package/dist/intelligence/tools-memory.js +514 -0
  150. package/dist/intelligence/tools-runbook.js +951 -0
  151. package/dist/intelligence/tools-sessions.js +107 -0
  152. package/dist/intelligence/tools-state.js +232 -0
  153. package/dist/intelligence/tools-utility.js +430 -0
  154. package/dist/intelligence/tools-validate.js +215 -0
  155. package/dist/intelligence/tools.js +1 -0
  156. package/dist/intelligence/trace-context.js +11 -0
  157. package/dist/intelligence/unclassified-clusters.js +87 -0
  158. package/dist/intelligence/usage.js +195 -0
  159. package/dist/routes/active-authors.js +63 -0
  160. package/dist/routes/activity-feed.js +21 -0
  161. package/dist/routes/adr.js +117 -0
  162. package/dist/routes/agent-activity.js +184 -0
  163. package/dist/routes/agent-blunders.js +163 -0
  164. package/dist/routes/agents.js +175 -0
  165. package/dist/routes/api-keys.js +65 -0
  166. package/dist/routes/arch.js +75 -0
  167. package/dist/routes/audit-export.js +93 -0
  168. package/dist/routes/billing-dashboard.js +158 -0
  169. package/dist/routes/billing-outcome.js +77 -0
  170. package/dist/routes/calibration.js +305 -0
  171. package/dist/routes/ci-gate-history.js +20 -0
  172. package/dist/routes/ci-gate.js +232 -0
  173. package/dist/routes/ci.js +293 -0
  174. package/dist/routes/compliance-report.js +24 -0
  175. package/dist/routes/confidence.js +30 -0
  176. package/dist/routes/credentials.js +119 -0
  177. package/dist/routes/dashboard.js +1407 -0
  178. package/dist/routes/decision-memory.js +42 -0
  179. package/dist/routes/demo.js +844 -0
  180. package/dist/routes/device-auth-stub.js +173 -0
  181. package/dist/routes/explain-why.js +39 -0
  182. package/dist/routes/gate-analytics.js +213 -0
  183. package/dist/routes/gate.js +28 -0
  184. package/dist/routes/github-webhook.js +364 -0
  185. package/dist/routes/habituation.js +30 -0
  186. package/dist/routes/health-integrations.js +243 -0
  187. package/dist/routes/heartbeats.js +45 -0
  188. package/dist/routes/hitl.js +364 -0
  189. package/dist/routes/impact-report.js +645 -0
  190. package/dist/routes/incident-webhook.js +63 -0
  191. package/dist/routes/incidents.js +366 -0
  192. package/dist/routes/layers.js +62 -0
  193. package/dist/routes/license.js +187 -0
  194. package/dist/routes/oidc-issuer.js +30 -0
  195. package/dist/routes/onboarding.js +170 -0
  196. package/dist/routes/otel.js +67 -0
  197. package/dist/routes/otlp-receiver.js +179 -0
  198. package/dist/routes/overrides.js +241 -0
  199. package/dist/routes/pagerduty.js +258 -0
  200. package/dist/routes/policies.js +809 -0
  201. package/dist/routes/policy-nl.js +169 -0
  202. package/dist/routes/postmortem.js +102 -0
  203. package/dist/routes/pr-shadow.js +47 -0
  204. package/dist/routes/rbac.js +61 -0
  205. package/dist/routes/replay.js +17 -0
  206. package/dist/routes/risk-report.js +164 -0
  207. package/dist/routes/runbooks.js +125 -0
  208. package/dist/routes/safety-test.js +174 -0
  209. package/dist/routes/sdk.js +222 -0
  210. package/dist/routes/sensor.js +819 -0
  211. package/dist/routes/sentry.js +140 -0
  212. package/dist/routes/sessions.js +43 -0
  213. package/dist/routes/setup-ui.js +594 -0
  214. package/dist/routes/shadow-report.js +107 -0
  215. package/dist/routes/slack-routing.js +171 -0
  216. package/dist/routes/team-usage.js +71 -0
  217. package/dist/routes/telemetry.js +28 -0
  218. package/dist/routes/tenants.js +199 -0
  219. package/dist/routes/tickets.js +167 -0
  220. package/dist/routes/validate.js +13 -0
  221. package/dist/routes/war-room.js +113 -0
  222. package/dist/scripts/check-arch.js +22 -0
  223. package/dist/seeds/community-corpus.js +176 -0
  224. package/dist/seeds/corpus.js +1224 -0
  225. package/dist/sensor/action-ledger.js +327 -0
  226. package/dist/sensor/adr-store.js +140 -0
  227. package/dist/sensor/agent-blunder-store.js +370 -0
  228. package/dist/sensor/agent-context-store.js +225 -0
  229. package/dist/sensor/agent-memory-store.js +231 -0
  230. package/dist/sensor/audit-fetch.js +23 -0
  231. package/dist/sensor/audit-log.js +273 -0
  232. package/dist/sensor/buffer-schemas.js +225 -0
  233. package/dist/sensor/buffer.js +599 -0
  234. package/dist/sensor/bypass-tracker.js +93 -0
  235. package/dist/sensor/ci-gate-history.js +70 -0
  236. package/dist/sensor/cloud-auth.js +183 -0
  237. package/dist/sensor/commit-context-store.js +235 -0
  238. package/dist/sensor/docker-log-stream.js +164 -0
  239. package/dist/sensor/docker-monitor.js +122 -0
  240. package/dist/sensor/extended-buffer.js +46 -0
  241. package/dist/sensor/feedback-token.js +41 -0
  242. package/dist/sensor/file-lock.js +6 -0
  243. package/dist/sensor/fs-watcher.js +106 -0
  244. package/dist/sensor/gate-heartbeat.js +122 -0
  245. package/dist/sensor/git-suspect.js +136 -0
  246. package/dist/sensor/habituation-store.js +77 -0
  247. package/dist/sensor/heartbeat-monitor.js +133 -0
  248. package/dist/sensor/incident-store.js +340 -0
  249. package/dist/sensor/infra-normalizer.js +513 -0
  250. package/dist/sensor/ingest.js +339 -0
  251. package/dist/sensor/jest-reporter.js +52 -0
  252. package/dist/sensor/k8s-events.js +132 -0
  253. package/dist/sensor/layer2-store.js +111 -0
  254. package/dist/sensor/layer3-store.js +230 -0
  255. package/dist/sensor/layer4-store.js +147 -0
  256. package/dist/sensor/logger.js +13 -0
  257. package/dist/sensor/otel-exporter.js +161 -0
  258. package/dist/sensor/paths.js +71 -0
  259. package/dist/sensor/policy-history.js +147 -0
  260. package/dist/sensor/pr-shadow-store.js +83 -0
  261. package/dist/sensor/process-watcher.js +162 -0
  262. package/dist/sensor/rbac.js +92 -0
  263. package/dist/sensor/redact.js +114 -0
  264. package/dist/sensor/redis-store.js +191 -0
  265. package/dist/sensor/route-reachability.js +64 -0
  266. package/dist/sensor/security-utils.js +18 -0
  267. package/dist/sensor/service-graph.js +170 -0
  268. package/dist/sensor/service-topology.js +227 -0
  269. package/dist/sensor/session-history.js +74 -0
  270. package/dist/sensor/session-persist.js +38 -0
  271. package/dist/sensor/shadow-promote.js +122 -0
  272. package/dist/sensor/sourcemap.js +281 -0
  273. package/dist/sensor/sqlite-store.js +205 -0
  274. package/dist/sensor/sso.js +164 -0
  275. package/dist/sensor/vitest-reporter.js +65 -0
  276. package/dist/sensor/watcher.js +48 -0
  277. package/dist/storage/interfaces.js +0 -0
  278. package/dist/storage/pg/pg-action-ledger.js +138 -0
  279. package/dist/storage/pg/pg-approval-store.js +107 -0
  280. package/dist/storage/pg/pg-blunder-store.js +193 -0
  281. package/dist/storage/pg/pg-ci-gate-history.js +83 -0
  282. package/dist/storage/pg/pg-client.js +24 -0
  283. package/dist/storage/pg/pg-event-store.js +63 -0
  284. package/dist/storage/pg/pg-incident-store.js +190 -0
  285. package/dist/storage/pg/pg-migrations.js +26 -0
  286. package/dist/storage/pg/pg-override-corpus.js +447 -0
  287. package/dist/storage/pg/pg-shadow-log.js +129 -0
  288. package/dist/storage/sqlite/sqlite-action-ledger.js +18 -0
  289. package/dist/storage/sqlite/sqlite-approval-store.js +37 -0
  290. package/dist/storage/sqlite/sqlite-blunder-store.js +27 -0
  291. package/dist/storage/sqlite/sqlite-ci-gate-history.js +19 -0
  292. package/dist/storage/sqlite/sqlite-event-store.js +29 -0
  293. package/dist/storage/sqlite/sqlite-incident-store.js +34 -0
  294. package/dist/storage/sqlite/sqlite-override-corpus.js +75 -0
  295. package/dist/storage/sqlite/sqlite-shadow-log.js +36 -0
  296. package/dist/storage/store-factory.js +55 -0
  297. package/dist/storage/store-registry.js +31 -0
  298. package/dist/update-checker.js +107 -0
  299. package/dist/workers/autopilot-worker.js +28 -0
  300. package/dist/workers/notification-worker.js +28 -0
  301. package/dist/workers/queues.js +58 -0
  302. package/dist/workers/validation-worker.js +24 -0
  303. package/dist/workers/worker-registry.js +18 -0
  304. package/package.json +123 -0
  305. package/sdk/mergen-inject.js +260 -0
  306. package/sdk/node.js +313 -0
  307. package/sdk/vite-plugin.ts +57 -0
  308. package/sdk/webpack-plugin.js +106 -0
@@ -0,0 +1,231 @@
1
+ import initSqlJs from "sql.js";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+ import { createRequire } from "module";
6
+ import { DATA_DIR } from "./paths.js";
7
+ import logger from "./logger.js";
8
+ import { randomUUID } from "crypto";
9
+ const MEMORY_DB = path.join(DATA_DIR, "agent-memory.db");
10
+ class AgentMemoryStore {
11
+ db = null;
12
+ resolveWasmPath() {
13
+ if (process.env.MERGEN_WASM_PATH) return process.env.MERGEN_WASM_PATH;
14
+ const moduleDir = path.dirname(fileURLToPath(import.meta.url));
15
+ const fromModule = path.resolve(moduleDir, "../../node_modules/sql.js/dist/sql-wasm.wasm");
16
+ if (fs.existsSync(fromModule)) return fromModule;
17
+ try {
18
+ const req = createRequire(import.meta.url);
19
+ const resolved = path.join(path.dirname(req.resolve("sql.js")), "sql-wasm.wasm");
20
+ if (fs.existsSync(resolved)) return resolved;
21
+ } catch {
22
+ }
23
+ return fromModule;
24
+ }
25
+ async init() {
26
+ try {
27
+ const wasmBinary = fs.readFileSync(this.resolveWasmPath());
28
+ const SQL = await initSqlJs({ wasmBinary });
29
+ fs.mkdirSync(DATA_DIR, { recursive: true });
30
+ let fileBuffer;
31
+ if (fs.existsSync(MEMORY_DB)) {
32
+ try {
33
+ fileBuffer = fs.readFileSync(MEMORY_DB);
34
+ } catch {
35
+ }
36
+ }
37
+ this.db = fileBuffer ? new SQL.Database(fileBuffer) : new SQL.Database();
38
+ this.db.run(`
39
+ CREATE TABLE IF NOT EXISTS agent_memory (
40
+ id TEXT PRIMARY KEY,
41
+ agent_id TEXT NOT NULL DEFAULT '',
42
+ key TEXT NOT NULL,
43
+ value TEXT NOT NULL,
44
+ stored_at INTEGER NOT NULL,
45
+ ttl_ms INTEGER NOT NULL DEFAULT 0,
46
+ service TEXT NOT NULL DEFAULT '',
47
+ error_fingerprint TEXT NOT NULL DEFAULT ''
48
+ );
49
+ CREATE INDEX IF NOT EXISTS idx_agent_memory_agent_key ON agent_memory (agent_id, key);
50
+ CREATE INDEX IF NOT EXISTS idx_agent_memory_episodic ON agent_memory (service, error_fingerprint, stored_at);
51
+ `);
52
+ this.flush();
53
+ try {
54
+ this.db.run(`ALTER TABLE agent_memory ADD COLUMN service TEXT NOT NULL DEFAULT ''`);
55
+ } catch {
56
+ }
57
+ try {
58
+ this.db.run(`ALTER TABLE agent_memory ADD COLUMN error_fingerprint TEXT NOT NULL DEFAULT ''`);
59
+ } catch {
60
+ }
61
+ try {
62
+ this.db.run(`CREATE INDEX IF NOT EXISTS idx_agent_memory_episodic ON agent_memory (service, error_fingerprint, stored_at)`);
63
+ } catch {
64
+ }
65
+ logger.debug({ path: MEMORY_DB }, "agent-memory-store: initialized");
66
+ this.scheduleCleanup();
67
+ } catch (err) {
68
+ logger.error({ err }, "agent-memory-store: init failed");
69
+ }
70
+ }
71
+ scheduleCleanup() {
72
+ const run = () => {
73
+ if (!this.db) return;
74
+ try {
75
+ const now = Date.now();
76
+ this.db.run("DELETE FROM agent_memory WHERE ttl_ms > 0 AND stored_at + ttl_ms < ?", [now]);
77
+ this.flush();
78
+ try {
79
+ const stat = fs.statSync(MEMORY_DB);
80
+ if (stat.size > 100 * 1024 * 1024) {
81
+ this.db.run("DELETE FROM agent_memory WHERE id IN (SELECT id FROM agent_memory ORDER BY stored_at ASC LIMIT 1000)");
82
+ this.flush();
83
+ logger.warn("agent-memory-store: DB exceeded 100 MB cap \u2014 pruned oldest 1000 entries");
84
+ }
85
+ } catch {
86
+ }
87
+ } catch (err) {
88
+ logger.warn({ err }, "agent-memory-store: cleanup failed");
89
+ }
90
+ };
91
+ run();
92
+ setInterval(run, 60 * 6e4).unref();
93
+ }
94
+ flush() {
95
+ if (!this.db) return;
96
+ try {
97
+ const data = this.db.export();
98
+ fs.writeFileSync(MEMORY_DB, Buffer.from(data));
99
+ } catch (err) {
100
+ logger.warn({ err }, "agent-memory-store: flush failed");
101
+ }
102
+ }
103
+ /**
104
+ * Store or overwrite a memory entry.
105
+ * If an entry with the same (agentId, key) already exists it is replaced.
106
+ * Optional `service` and `errorFingerprint` enable episodic recall.
107
+ */
108
+ store(agentId, key, value, ttlMs = 0, service = "", errorFingerprint = "") {
109
+ if (!this.db) throw new Error("agent-memory-store: not initialized");
110
+ const now = Date.now();
111
+ const existing = this.recall(agentId, key, 1);
112
+ const id = existing[0]?.id ?? randomUUID();
113
+ this.db.run(
114
+ `INSERT OR REPLACE INTO agent_memory
115
+ (id, agent_id, key, value, stored_at, ttl_ms, service, error_fingerprint)
116
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
117
+ [id, agentId, key, value, now, ttlMs, service, errorFingerprint]
118
+ );
119
+ this.flush();
120
+ return { id, agentId, key, value, storedAt: now, ttlMs, service, errorFingerprint };
121
+ }
122
+ /**
123
+ * Retrieve memory entries filtered by agentId, key, service, and/or errorFingerprint.
124
+ * Expired entries are excluded. Results are ordered newest-first.
125
+ */
126
+ recall(agentId, key, limit = 20, service, errorFingerprint) {
127
+ if (!this.db) return [];
128
+ const now = Date.now();
129
+ const clauses = ["(ttl_ms = 0 OR stored_at + ttl_ms > ?)"];
130
+ const params = [now];
131
+ if (agentId) {
132
+ clauses.push("agent_id = ?");
133
+ params.push(agentId);
134
+ }
135
+ if (key) {
136
+ clauses.push("key = ?");
137
+ params.push(key);
138
+ }
139
+ if (service) {
140
+ clauses.push("service = ?");
141
+ params.push(service);
142
+ }
143
+ if (errorFingerprint) {
144
+ clauses.push("error_fingerprint = ?");
145
+ params.push(errorFingerprint);
146
+ }
147
+ const sql = `SELECT * FROM agent_memory WHERE ${clauses.join(" AND ")} ORDER BY stored_at DESC LIMIT ?`;
148
+ params.push(limit);
149
+ try {
150
+ const stmt = this.db.prepare(sql);
151
+ stmt.bind(params);
152
+ const rows = [];
153
+ while (stmt.step()) {
154
+ const r = stmt.getAsObject();
155
+ rows.push({
156
+ id: r["id"],
157
+ agentId: r["agent_id"],
158
+ key: r["key"],
159
+ value: r["value"],
160
+ storedAt: r["stored_at"],
161
+ ttlMs: r["ttl_ms"],
162
+ service: r["service"] ?? "",
163
+ errorFingerprint: r["error_fingerprint"] ?? ""
164
+ });
165
+ }
166
+ stmt.free();
167
+ return rows;
168
+ } catch (err) {
169
+ logger.warn({ err }, "agent-memory-store: recall failed");
170
+ return [];
171
+ }
172
+ }
173
+ /**
174
+ * Episodic recall: return memories for a specific (service, errorFingerprint) context,
175
+ * ordered by recency. This is the associative lookup LeCun-style models use.
176
+ */
177
+ recallEpisodic(service, errorFingerprint, limit = 10) {
178
+ return this.recall(void 0, void 0, limit, service, errorFingerprint);
179
+ }
180
+ /** Returns true when the SQLite database initialised successfully. */
181
+ isHealthy() {
182
+ return this.db !== null;
183
+ }
184
+ /**
185
+ * List all non-expired keys stored by an agent, grouped for discovery.
186
+ * Lets an agent enumerate what it has stored without knowing keys in advance.
187
+ */
188
+ listKeys(agentId) {
189
+ if (!this.db) return [];
190
+ const now = Date.now();
191
+ const clauses = ["(ttl_ms = 0 OR stored_at + ttl_ms > ?)"];
192
+ const params = [now];
193
+ if (agentId) {
194
+ clauses.push("agent_id = ?");
195
+ params.push(agentId);
196
+ }
197
+ const sql = `SELECT agent_id, key, MAX(stored_at) AS last_at
198
+ FROM agent_memory
199
+ WHERE ${clauses.join(" AND ")}
200
+ GROUP BY agent_id, key
201
+ ORDER BY last_at DESC`;
202
+ try {
203
+ const stmt = this.db.prepare(sql);
204
+ stmt.bind(params);
205
+ const rows = [];
206
+ while (stmt.step()) {
207
+ const r = stmt.getAsObject();
208
+ rows.push({
209
+ agentId: r["agent_id"],
210
+ key: r["key"],
211
+ lastStoredAt: r["last_at"]
212
+ });
213
+ }
214
+ stmt.free();
215
+ return rows;
216
+ } catch (err) {
217
+ logger.warn({ err }, "agent-memory-store: listKeys failed");
218
+ return [];
219
+ }
220
+ }
221
+ /** Remove a specific memory by (agentId, key). */
222
+ forget(agentId, key) {
223
+ if (!this.db) return;
224
+ this.db.run("DELETE FROM agent_memory WHERE agent_id = ? AND key = ?", [agentId, key]);
225
+ this.flush();
226
+ }
227
+ }
228
+ const agentMemoryStore = new AgentMemoryStore();
229
+ export {
230
+ agentMemoryStore
231
+ };
@@ -0,0 +1,23 @@
1
+ import { getStores } from "../storage/store-registry.js";
2
+ import { getAuditLog } from "./audit-log.js";
3
+ async function fetchBlunderEntries(from, to) {
4
+ const blunders = await getStores().blunders.list();
5
+ return blunders.filter((b) => b.recordedAt >= from && b.recordedAt <= to);
6
+ }
7
+ async function fetchHttpAuditEntries(from, to, limit = 5e4) {
8
+ return getAuditLog(limit).filter((e) => {
9
+ const ts = new Date(e.ts).getTime();
10
+ return ts >= from && ts <= to;
11
+ });
12
+ }
13
+ async function fetchChainVerification() {
14
+ const blunderStore = getStores().blunders;
15
+ const hasEntries = (await blunderStore.list()).length > 0;
16
+ if (!hasEntries) return { valid: true, verified: 0 };
17
+ return blunderStore.verifyChain();
18
+ }
19
+ export {
20
+ fetchBlunderEntries,
21
+ fetchChainVerification,
22
+ fetchHttpAuditEntries
23
+ };
@@ -0,0 +1,273 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+ import { createRequire } from "module";
5
+ import initSqlJs from "sql.js";
6
+ import { AUDIT_LOG, AUDIT_DB, DATA_DIR } from "./paths.js";
7
+ import logger from "./logger.js";
8
+ const MAX_AUDIT_BYTES = 10 * 1024 * 1024;
9
+ const SKIP_PATHS = /* @__PURE__ */ new Set(["/", "/health", "/metrics", "/dashboard", "/local-secret"]);
10
+ class ComplianceLedgerStore {
11
+ db = null;
12
+ resolveWasmPath() {
13
+ if (process.env.MERGEN_WASM_PATH) return process.env.MERGEN_WASM_PATH;
14
+ const moduleDir = path.dirname(fileURLToPath(import.meta.url));
15
+ const fromModule = path.resolve(moduleDir, "../../node_modules/sql.js/dist/sql-wasm.wasm");
16
+ if (fs.existsSync(fromModule)) return fromModule;
17
+ try {
18
+ const req = createRequire(import.meta.url);
19
+ const resolved = path.join(path.dirname(req.resolve("sql.js")), "sql-wasm.wasm");
20
+ if (fs.existsSync(resolved)) return resolved;
21
+ } catch {
22
+ }
23
+ return fromModule;
24
+ }
25
+ async init() {
26
+ try {
27
+ const wasmBinary = fs.readFileSync(this.resolveWasmPath());
28
+ const SQL = await initSqlJs({ wasmBinary });
29
+ fs.mkdirSync(DATA_DIR, { recursive: true });
30
+ let fileBuffer;
31
+ if (fs.existsSync(AUDIT_DB)) {
32
+ try {
33
+ fileBuffer = fs.readFileSync(AUDIT_DB);
34
+ } catch {
35
+ }
36
+ }
37
+ this.db = fileBuffer ? new SQL.Database(fileBuffer) : new SQL.Database();
38
+ this.db.run(`
39
+ CREATE TABLE IF NOT EXISTS http_audit (
40
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
41
+ ts TEXT NOT NULL,
42
+ actor TEXT NOT NULL,
43
+ method TEXT NOT NULL,
44
+ path TEXT NOT NULL,
45
+ status INTEGER NOT NULL,
46
+ duration_ms INTEGER NOT NULL,
47
+ ip TEXT NOT NULL
48
+ );
49
+ CREATE TABLE IF NOT EXISTS execution_audit (
50
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
51
+ ts TEXT NOT NULL,
52
+ event TEXT NOT NULL,
53
+ actor TEXT NOT NULL,
54
+ cmd TEXT NOT NULL,
55
+ ok INTEGER NOT NULL,
56
+ exit_code INTEGER NOT NULL,
57
+ duration_ms INTEGER NOT NULL,
58
+ blocked INTEGER NOT NULL,
59
+ block_reason TEXT NOT NULL,
60
+ timed_out INTEGER NOT NULL
61
+ );
62
+ CREATE INDEX IF NOT EXISTS idx_http_audit_ts ON http_audit (ts);
63
+ CREATE INDEX IF NOT EXISTS idx_execution_audit_ts ON execution_audit (ts);
64
+ `);
65
+ this.flush();
66
+ logger.info({ path: AUDIT_DB }, "Compliance ledger SQLite store initialised");
67
+ } catch (err) {
68
+ logger.error({ err }, "Compliance ledger store failed to initialize");
69
+ }
70
+ }
71
+ flush() {
72
+ if (!this.db) return;
73
+ try {
74
+ const data = this.db.export();
75
+ fs.writeFileSync(AUDIT_DB, Buffer.from(data));
76
+ } catch (err) {
77
+ logger.warn({ err }, "Compliance ledger store flush failed");
78
+ }
79
+ }
80
+ insertHttp(entry) {
81
+ if (!this.db) return;
82
+ try {
83
+ this.db.run(
84
+ `INSERT INTO http_audit (ts, actor, method, path, status, duration_ms, ip) VALUES (?, ?, ?, ?, ?, ?, ?)`,
85
+ [entry.ts, entry.actor, entry.method, entry.path, entry.status, entry.durationMs, entry.ip]
86
+ );
87
+ this.flush();
88
+ } catch (err) {
89
+ logger.warn({ err }, "Failed to insert HTTP audit record");
90
+ }
91
+ }
92
+ insertExecution(entry) {
93
+ if (!this.db) return;
94
+ try {
95
+ this.db.run(
96
+ `INSERT INTO execution_audit (ts, event, actor, cmd, ok, exit_code, duration_ms, blocked, block_reason, timed_out)
97
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
98
+ [
99
+ entry.ts,
100
+ entry.event,
101
+ entry.actor,
102
+ entry.cmd,
103
+ entry.ok ? 1 : 0,
104
+ entry.exitCode,
105
+ entry.durationMs,
106
+ entry.blocked ? 1 : 0,
107
+ entry.blockReason,
108
+ entry.timedOut ? 1 : 0
109
+ ]
110
+ );
111
+ this.flush();
112
+ } catch (err) {
113
+ logger.warn({ err }, "Failed to insert execution audit record");
114
+ }
115
+ }
116
+ getHttpEntries(limit = 200) {
117
+ if (!this.db) return [];
118
+ try {
119
+ const res = this.db.exec(`SELECT ts, actor, method, path, status, duration_ms, ip FROM http_audit ORDER BY ts DESC LIMIT ?`, [limit]);
120
+ if (res.length === 0) return [];
121
+ return res[0].values.map((row) => ({
122
+ ts: String(row[0]),
123
+ actor: String(row[1]),
124
+ method: String(row[2]),
125
+ path: String(row[3]),
126
+ status: Number(row[4]),
127
+ durationMs: Number(row[5]),
128
+ ip: String(row[6])
129
+ }));
130
+ } catch (err) {
131
+ logger.warn({ err }, "Failed to query HTTP audit entries");
132
+ return [];
133
+ }
134
+ }
135
+ getExecutionEntries(limit = 200) {
136
+ if (!this.db) return [];
137
+ try {
138
+ const res = this.db.exec(
139
+ `SELECT ts, event, actor, cmd, ok, exit_code, duration_ms, blocked, block_reason, timed_out
140
+ FROM execution_audit ORDER BY ts DESC LIMIT ?`,
141
+ [limit]
142
+ );
143
+ if (res.length === 0) return [];
144
+ return res[0].values.map((row) => ({
145
+ ts: String(row[0]),
146
+ event: String(row[1]),
147
+ actor: String(row[2]),
148
+ cmd: String(row[3]),
149
+ ok: Number(row[4]) === 1,
150
+ exitCode: Number(row[5]),
151
+ durationMs: Number(row[6]),
152
+ blocked: Number(row[7]) === 1,
153
+ blockReason: String(row[8]),
154
+ timedOut: Number(row[9]) === 1
155
+ }));
156
+ } catch (err) {
157
+ logger.warn({ err }, "Failed to query execution audit entries");
158
+ return [];
159
+ }
160
+ }
161
+ }
162
+ const complianceLedgerStore = new ComplianceLedgerStore();
163
+ function resolveActor(req) {
164
+ const member = req.headers["x-mergen-member"];
165
+ if (member) return member;
166
+ return req.ip ?? req.socket.remoteAddress ?? "unknown";
167
+ }
168
+ let _writeQueue = Promise.resolve();
169
+ let _lastAuditWriteError = null;
170
+ let _auditWriteOk = true;
171
+ function getAuditHealth() {
172
+ return { ok: _auditWriteOk, lastError: _lastAuditWriteError };
173
+ }
174
+ async function appendEntryAsync(entry) {
175
+ try {
176
+ await fs.promises.mkdir(DATA_DIR, { recursive: true });
177
+ try {
178
+ const stat = await fs.promises.stat(AUDIT_LOG);
179
+ if (stat.size >= MAX_AUDIT_BYTES) {
180
+ const rotated = AUDIT_LOG + ".1";
181
+ try {
182
+ await fs.promises.unlink(rotated);
183
+ } catch {
184
+ }
185
+ await fs.promises.rename(AUDIT_LOG, rotated);
186
+ }
187
+ } catch {
188
+ }
189
+ await fs.promises.appendFile(AUDIT_LOG, JSON.stringify(entry) + "\n", "utf8");
190
+ _auditWriteOk = true;
191
+ _lastAuditWriteError = null;
192
+ } catch (err) {
193
+ _auditWriteOk = false;
194
+ _lastAuditWriteError = err instanceof Error ? err.message : String(err);
195
+ logger.error({ err }, "audit-log: write failed \u2014 record dropped");
196
+ }
197
+ }
198
+ function appendEntry(entry) {
199
+ complianceLedgerStore.insertHttp(entry);
200
+ _writeQueue = _writeQueue.then(() => appendEntryAsync(entry)).catch((err) => {
201
+ _auditWriteOk = false;
202
+ _lastAuditWriteError = err instanceof Error ? err.message : String(err);
203
+ logger.error({ err }, "audit-log: write queue error \u2014 record dropped");
204
+ });
205
+ }
206
+ function auditMiddleware(req, res, next) {
207
+ if (SKIP_PATHS.has(req.path) || req.path.startsWith("/dashboard")) {
208
+ next();
209
+ return;
210
+ }
211
+ const start = Date.now();
212
+ res.on("finish", () => {
213
+ appendEntry({
214
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
215
+ actor: resolveActor(req),
216
+ method: req.method,
217
+ path: req.path,
218
+ status: res.statusCode,
219
+ durationMs: Date.now() - start,
220
+ ip: req.ip ?? req.socket.remoteAddress ?? "unknown"
221
+ });
222
+ });
223
+ next();
224
+ }
225
+ function getAuditLog(limit = 200) {
226
+ const sqliteEntries = complianceLedgerStore.getHttpEntries(limit);
227
+ if (sqliteEntries.length > 0) return sqliteEntries;
228
+ try {
229
+ const raw = fs.readFileSync(AUDIT_LOG, "utf8");
230
+ const lines = raw.trim().split("\n").filter(Boolean);
231
+ return lines.slice(-Math.min(limit, 2e3)).map((l) => JSON.parse(l)).reverse();
232
+ } catch {
233
+ return [];
234
+ }
235
+ }
236
+ function recordExecutionAudit(entry) {
237
+ complianceLedgerStore.insertExecution(entry);
238
+ const fileEntry = JSON.stringify({
239
+ t: entry.ts,
240
+ event: entry.event,
241
+ actor: entry.actor,
242
+ cmd: entry.cmd,
243
+ ok: entry.ok,
244
+ exitCode: entry.exitCode,
245
+ durationMs: entry.durationMs,
246
+ blocked: entry.blocked,
247
+ blockReason: entry.blockReason,
248
+ timedOut: entry.timedOut
249
+ });
250
+ _writeQueue = _writeQueue.then(async () => {
251
+ try {
252
+ await fs.promises.mkdir(DATA_DIR, { recursive: true });
253
+ await fs.promises.appendFile(AUDIT_LOG, fileEntry + "\n", "utf8");
254
+ _auditWriteOk = true;
255
+ _lastAuditWriteError = null;
256
+ } catch (err) {
257
+ _auditWriteOk = false;
258
+ _lastAuditWriteError = err instanceof Error ? err.message : String(err);
259
+ logger.error({ err }, "audit-log: execution record write failed \u2014 record dropped");
260
+ }
261
+ }).catch((err) => {
262
+ _auditWriteOk = false;
263
+ _lastAuditWriteError = err instanceof Error ? err.message : String(err);
264
+ logger.error({ err }, "audit-log: write queue error on execution record");
265
+ });
266
+ }
267
+ export {
268
+ auditMiddleware,
269
+ complianceLedgerStore,
270
+ getAuditHealth,
271
+ getAuditLog,
272
+ recordExecutionAudit
273
+ };