baro-ai 0.50.0 → 0.50.1

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.
@@ -2,8 +2,374 @@
2
2
  import { createRequire as __baroCreateRequire } from "module";
3
3
  const require = __baroCreateRequire(import.meta.url);
4
4
 
5
+ // ../baro-memory/dist/vectra-store.js
6
+ import { LocalIndex } from "vectra";
7
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, renameSync, rmSync, readdirSync, lstatSync } from "fs";
8
+ import { join } from "path";
9
+ import { tmpdir, homedir } from "os";
10
+ var MAX_CONTENT_CHARS = 4e3;
11
+ var MAX_CACHE_FILE_BYTES = 5 * 1024 * 1024;
12
+ var MAX_TOTAL_CACHE_BYTES = 50 * 1024 * 1024;
13
+ var SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
14
+ var ALLOWED_SESSION_PARENTS = [".baro", "baro-memory", "tmp"];
15
+ var DEFAULTS = {
16
+ embeddingModel: "Xenova/all-MiniLM-L6-v2",
17
+ defaultMinSimilarity: 0.3,
18
+ defaultMaxResults: 10,
19
+ disabled: false,
20
+ sessionPath: ""
21
+ };
22
+ async function createMemoryStore(config) {
23
+ const cfg = { ...DEFAULTS, ...config };
24
+ if (cfg.defaultMinSimilarity < 0 || cfg.defaultMinSimilarity > 1) {
25
+ cfg.defaultMinSimilarity = DEFAULTS.defaultMinSimilarity;
26
+ }
27
+ if (cfg.defaultMaxResults < 1) {
28
+ cfg.defaultMaxResults = DEFAULTS.defaultMaxResults;
29
+ }
30
+ if (cfg.disabled) {
31
+ return new NoOpMemoryStore();
32
+ }
33
+ const sessionPath = cfg.sessionPath || join(tmpdir(), `baro-memory-${process.pid}-${Date.now()}`);
34
+ validateSessionPath(sessionPath);
35
+ mkdirSync(sessionPath, { recursive: true });
36
+ const indexPath = join(sessionPath, "index");
37
+ mkdirSync(indexPath, { recursive: true });
38
+ const index = new LocalIndex(indexPath);
39
+ if (!await index.isIndexCreated()) {
40
+ await index.createIndex({ version: 1 });
41
+ }
42
+ const transformers = await import("@xenova/transformers");
43
+ transformers.env.cacheDir = process.env.TRANSFORMERS_CACHE || join(homedir(), ".baro", "models");
44
+ const { pipeline } = transformers;
45
+ const extractor = await pipeline("feature-extraction", cfg.embeddingModel);
46
+ return new VectraMemoryStore(index, extractor, sessionPath, cfg);
47
+ }
48
+ function validateSessionPath(sessionPath) {
49
+ const resolved = join(sessionPath);
50
+ if (resolved.includes("..")) {
51
+ throw new Error(`Invalid session path (contains ..): ${resolved}`);
52
+ }
53
+ const dangerous = ["/etc", "/usr", "/bin", "/sbin", "/var/run", "/System", "/Library"];
54
+ for (const d of dangerous) {
55
+ if (resolved.startsWith(d + "/") || resolved === d) {
56
+ throw new Error(`Invalid session path (sensitive directory): ${resolved}`);
57
+ }
58
+ }
59
+ const normalizedPath = resolved.toLowerCase();
60
+ const isSafe = ALLOWED_SESSION_PARENTS.some((p) => normalizedPath.includes(p)) || normalizedPath.startsWith(tmpdir().toLowerCase());
61
+ if (!isSafe) {
62
+ throw new Error(`Invalid session path (must be under ~/.baro, tmpdir, or contain 'baro-memory'): ${resolved}`);
63
+ }
64
+ }
65
+ async function embed(extractor, text) {
66
+ if (!text || !text.trim()) {
67
+ throw new Error("Cannot embed empty text");
68
+ }
69
+ const output = await extractor(text, { pooling: "mean", normalize: true });
70
+ if (!output?.data) {
71
+ throw new Error("Embedding model returned no data");
72
+ }
73
+ return Array.from(output.data);
74
+ }
75
+ var VectraMemoryStore = class {
76
+ index;
77
+ extractor;
78
+ sessionPath;
79
+ cachePath;
80
+ lockPath;
81
+ config;
82
+ constructor(index, extractor, sessionPath, config) {
83
+ this.index = index;
84
+ this.extractor = extractor;
85
+ this.sessionPath = sessionPath;
86
+ this.cachePath = join(sessionPath, "cache.json");
87
+ this.lockPath = join(sessionPath, "cache.lock");
88
+ this.config = config;
89
+ }
90
+ // ── Semantic memory ──────────────────────────────────────────
91
+ async remember(finding) {
92
+ try {
93
+ if (!finding.content?.trim())
94
+ return false;
95
+ const id = this.generateId(finding);
96
+ const vector = await embed(this.extractor, finding.content);
97
+ const metadata = {
98
+ tool: finding.tool,
99
+ agentId: finding.agentId,
100
+ storyId: finding.storyId ?? "",
101
+ filePath: finding.filePath ?? "",
102
+ pattern: finding.pattern ?? "",
103
+ command: finding.command ?? "",
104
+ tags: finding.tags?.join(",") ?? "",
105
+ content: finding.content.slice(0, MAX_CONTENT_CHARS)
106
+ };
107
+ await this.index.upsertItem({ id, vector, metadata });
108
+ return true;
109
+ } catch {
110
+ return false;
111
+ }
112
+ }
113
+ async recall(query, options) {
114
+ try {
115
+ const maxResults = options?.maxResults ?? this.config.defaultMaxResults;
116
+ const minSimilarity = options?.minSimilarity ?? this.config.defaultMinSimilarity;
117
+ const excludeAgent = options?.excludeAgent;
118
+ const filterByTool = options?.filterByTool;
119
+ if (!query?.trim())
120
+ return [];
121
+ const vector = await embed(this.extractor, query);
122
+ const fetchK = Math.min(Math.max(maxResults * 3, 30), 200);
123
+ const results = await this.index.queryItems(vector, fetchK);
124
+ const output = [];
125
+ for (const result of results) {
126
+ if (result.score < minSimilarity)
127
+ continue;
128
+ if (excludeAgent && result.item.metadata.agentId === excludeAgent)
129
+ continue;
130
+ if (filterByTool?.length && !filterByTool.includes(result.item.metadata.tool))
131
+ continue;
132
+ output.push({
133
+ id: result.item.id,
134
+ content: result.item.metadata.content,
135
+ metadata: {
136
+ tool: result.item.metadata.tool,
137
+ agentId: result.item.metadata.agentId,
138
+ storyId: result.item.metadata.storyId,
139
+ filePath: result.item.metadata.filePath,
140
+ pattern: result.item.metadata.pattern,
141
+ command: result.item.metadata.command,
142
+ tags: result.item.metadata.tags
143
+ },
144
+ similarity: result.score
145
+ });
146
+ if (output.length >= maxResults)
147
+ break;
148
+ }
149
+ return output;
150
+ } catch {
151
+ return [];
152
+ }
153
+ }
154
+ async gatherContext(storyId, hints, maxChars = 2e4) {
155
+ const query = hints.join(" ");
156
+ if (!query.trim())
157
+ return null;
158
+ const results = await this.recall(query, {
159
+ maxResults: 20,
160
+ minSimilarity: 0.3,
161
+ excludeAgent: storyId
162
+ });
163
+ if (results.length === 0)
164
+ return null;
165
+ const lines = [
166
+ "## Codebase context (from parallel agents)",
167
+ "",
168
+ "Other agents in this run discovered the following.",
169
+ "Use this directly without re-reading files.",
170
+ ""
171
+ ];
172
+ let totalChars = 0;
173
+ for (const result of results) {
174
+ const header = `[${result.metadata.agentId}] ${result.metadata.tool}${result.metadata.filePath ? ` ${result.metadata.filePath}` : ""} (relevance: ${Math.round(result.similarity * 100)}%)`;
175
+ const entry = `${header}
176
+ ${result.content}
177
+ `;
178
+ if (totalChars + entry.length > maxChars) {
179
+ lines.push("[...truncated...]");
180
+ break;
181
+ }
182
+ lines.push(entry);
183
+ totalChars += entry.length;
184
+ }
185
+ return lines.join("\n");
186
+ }
187
+ // ── File cache ───────────────────────────────────────────────
188
+ // Simple JSON file (not vectorized -- exact key-value lookup).
189
+ // NOTE: Multi-process writes use merge-on-write to reduce data loss.
190
+ // This is best-effort — not ACID. For guaranteed consistency, use SQLite.
191
+ async cacheFile(path, content, agentId) {
192
+ if (content.length > MAX_CACHE_FILE_BYTES)
193
+ return;
194
+ const cache = this.loadCache();
195
+ const existing = cache[path];
196
+ if (!existing || existing.content !== content) {
197
+ cache[path] = { path, content, readByAgent: agentId, timestamp: Date.now() };
198
+ this.evictIfNeeded(cache);
199
+ this.saveCache(cache);
200
+ }
201
+ }
202
+ async getCachedFile(path) {
203
+ const cache = this.loadCache();
204
+ return cache[path]?.content ?? null;
205
+ }
206
+ async hasFile(path) {
207
+ const cache = this.loadCache();
208
+ return path in cache;
209
+ }
210
+ async getCachedPaths() {
211
+ const cache = this.loadCache();
212
+ return Object.keys(cache);
213
+ }
214
+ // ── Stats ────────────────────────────────────────────────────
215
+ async getStats() {
216
+ try {
217
+ const items = await this.index.listItems();
218
+ const tools = /* @__PURE__ */ new Set();
219
+ const agents = /* @__PURE__ */ new Set();
220
+ for (const item of items) {
221
+ tools.add(item.metadata.tool);
222
+ agents.add(item.metadata.agentId);
223
+ }
224
+ const cache = this.loadCache();
225
+ let cacheSizeBytes = 0;
226
+ for (const entry of Object.values(cache)) {
227
+ cacheSizeBytes += entry.content.length;
228
+ }
229
+ return {
230
+ totalFindings: items.length,
231
+ uniqueTools: tools.size,
232
+ uniqueAgents: agents.size,
233
+ toolsList: Array.from(tools),
234
+ agentsList: Array.from(agents),
235
+ cachedFiles: Object.keys(cache).length,
236
+ cacheSizeBytes
237
+ };
238
+ } catch {
239
+ return {
240
+ totalFindings: 0,
241
+ uniqueTools: 0,
242
+ uniqueAgents: 0,
243
+ toolsList: [],
244
+ agentsList: [],
245
+ cachedFiles: 0,
246
+ cacheSizeBytes: 0
247
+ };
248
+ }
249
+ }
250
+ async close() {
251
+ try {
252
+ rmSync(this.lockPath, { force: true });
253
+ } catch {
254
+ }
255
+ }
256
+ // ── Private helpers ──────────────────────────────────────────
257
+ /**
258
+ * Evict oldest cache entries until total size is under MAX_TOTAL_CACHE_BYTES.
259
+ * LRU-style: removes entries with oldest timestamps first.
260
+ */
261
+ evictIfNeeded(cache) {
262
+ let totalBytes = 0;
263
+ for (const entry of Object.values(cache)) {
264
+ totalBytes += entry.content.length;
265
+ }
266
+ if (totalBytes <= MAX_TOTAL_CACHE_BYTES)
267
+ return;
268
+ const entries = Object.entries(cache).sort((a, b) => a[1].timestamp - b[1].timestamp);
269
+ for (const [key, entry] of entries) {
270
+ if (totalBytes <= MAX_TOTAL_CACHE_BYTES)
271
+ break;
272
+ totalBytes -= entry.content.length;
273
+ delete cache[key];
274
+ }
275
+ }
276
+ generateId(finding) {
277
+ const parts = [finding.agentId, finding.tool];
278
+ if (finding.filePath)
279
+ parts.push(finding.filePath);
280
+ else if (finding.pattern)
281
+ parts.push(finding.pattern);
282
+ else if (finding.command)
283
+ parts.push(finding.command);
284
+ else {
285
+ let hash = 0;
286
+ const str = finding.content.slice(0, 100);
287
+ for (let i = 0; i < str.length; i++) {
288
+ hash = (hash << 5) - hash + str.charCodeAt(i) | 0;
289
+ }
290
+ parts.push(Math.abs(hash).toString(36));
291
+ }
292
+ return parts.join(":");
293
+ }
294
+ /**
295
+ * Load cache from disk. Reads are not locked — if another process is
296
+ * mid-write using the atomic rename strategy, we'll either get the old
297
+ * complete version or the new complete version (never a partial write).
298
+ */
299
+ loadCache() {
300
+ try {
301
+ if (existsSync(this.cachePath)) {
302
+ const raw = readFileSync(this.cachePath, "utf-8");
303
+ if (raw.trim()) {
304
+ return JSON.parse(raw);
305
+ }
306
+ }
307
+ } catch {
308
+ }
309
+ return {};
310
+ }
311
+ /**
312
+ * Save cache atomically (write to PID-scoped tmp file, then rename).
313
+ * The rename is atomic on POSIX, so concurrent readers see either the
314
+ * old or new version — never a partial write.
315
+ */
316
+ saveCache(cache) {
317
+ try {
318
+ writeFileSync(this.lockPath, String(process.pid), "utf-8");
319
+ const tmp = this.cachePath + `.${process.pid}.tmp`;
320
+ writeFileSync(tmp, JSON.stringify(cache), "utf-8");
321
+ renameSync(tmp, this.cachePath);
322
+ } catch {
323
+ try {
324
+ writeFileSync(this.cachePath, JSON.stringify(cache), "utf-8");
325
+ } catch {
326
+ }
327
+ } finally {
328
+ try {
329
+ if (existsSync(this.lockPath))
330
+ rmSync(this.lockPath);
331
+ } catch {
332
+ }
333
+ }
334
+ }
335
+ };
336
+ var NoOpMemoryStore = class {
337
+ async remember(_finding) {
338
+ return false;
339
+ }
340
+ async recall(_query, _options) {
341
+ return [];
342
+ }
343
+ async gatherContext(_storyId, _hints) {
344
+ return null;
345
+ }
346
+ async cacheFile(_path, _content, _agentId) {
347
+ }
348
+ async getCachedFile(_path) {
349
+ return null;
350
+ }
351
+ async hasFile(_path) {
352
+ return false;
353
+ }
354
+ async getCachedPaths() {
355
+ return [];
356
+ }
357
+ async getStats() {
358
+ return {
359
+ totalFindings: 0,
360
+ uniqueTools: 0,
361
+ uniqueAgents: 0,
362
+ toolsList: [],
363
+ agentsList: [],
364
+ cachedFiles: 0,
365
+ cacheSizeBytes: 0
366
+ };
367
+ }
368
+ async close() {
369
+ }
370
+ };
371
+
5
372
  // ../baro-orchestrator/scripts/baro-memory.ts
6
- import { createMemoryStore } from "@baro/memory";
7
373
  var args = process.argv.slice(2);
8
374
  var command = args[0];
9
375
  function getFlag(name) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../baro-orchestrator/scripts/baro-memory.ts"],"sourcesContent":["/**\n * baro-memory CLI - Query and store findings in the shared Vectra memory.\n *\n * Connects to the same Vectra index as the orchestrator via the\n * BARO_MEMORY_PATH environment variable. Story agents can use this\n * mid-flight to query context and store findings for sibling agents.\n *\n * Usage:\n * baro-memory query \"JWT authentication\" [--top 5] [--agent story-1]\n * baro-memory store \"found auth pattern\" --tool Read --file src/auth.ts --agent story-1\n * baro-memory cache list\n * baro-memory cache get src/auth.ts\n * baro-memory stats\n *\n * Environment:\n * BARO_MEMORY_PATH - Path to the shared session memory directory.\n * Set automatically by the baro orchestrator.\n */\n\nimport { createMemoryStore, type MemoryStore } from \"@baro/memory\"\n\nconst args = process.argv.slice(2)\nconst command = args[0]\n\nfunction getFlag(name: string): string | undefined {\n const idx = args.indexOf(`--${name}`)\n if (idx < 0 || idx + 1 >= args.length) return undefined\n const value = args[idx + 1]\n // Don't treat the next flag as a value\n if (value.startsWith('--')) return undefined\n return value\n}\n\nfunction getPositionalArg(index: number): string | undefined {\n const val = args[index]\n if (!val || val.startsWith('--')) return undefined\n return val\n}\n\nasync function main() {\n // Read session path from env (set by orchestrator) or --path flag\n const sessionPath = getFlag(\"path\") || process.env.BARO_MEMORY_PATH\n\n if (!sessionPath) {\n console.error(\n \"Error: No memory session path found.\\n\\n\" +\n \"Set BARO_MEMORY_PATH environment variable or pass --path <dir>.\\n\" +\n \"This is normally set automatically by the baro orchestrator.\\n\"\n )\n process.exit(1)\n }\n\n let store: MemoryStore | null = null\n\n try {\n store = await createMemoryStore({ sessionPath })\n\n switch (command) {\n case \"query\": {\n const query = getPositionalArg(1)\n if (!query) {\n console.error(\"Usage: baro-memory query <text> [--top N] [--agent id]\")\n process.exit(1)\n }\n const topRaw = getFlag(\"top\")\n const top = topRaw ? parseInt(topRaw, 10) : 5\n if (isNaN(top) || top < 1) {\n console.error(\"Error: --top must be a positive integer\")\n process.exit(1)\n }\n const agent = getFlag(\"agent\")\n\n const results = await store.recall(query, {\n maxResults: top,\n minSimilarity: 0.3,\n excludeAgent: agent,\n })\n\n if (results.length === 0) {\n console.log(\"No relevant findings found.\")\n } else {\n console.log(`Found ${results.length} relevant findings:\\n`)\n for (const r of results) {\n console.log(`[${r.metadata.tool}] ${r.metadata.agentId} (similarity: ${r.similarity.toFixed(2)})`)\n if (r.metadata.filePath) console.log(` File: ${r.metadata.filePath}`)\n console.log(` ${r.content.slice(0, 200)}${r.content.length > 200 ? '...' : ''}`)\n console.log()\n }\n }\n break\n }\n\n case \"store\": {\n const content = getPositionalArg(1)\n if (!content) {\n console.error(\"Usage: baro-memory store <content> --tool <tool> [--file <path>] [--agent <id>]\")\n process.exit(1)\n }\n const tool = getFlag(\"tool\") || \"Bash\"\n const file = getFlag(\"file\")\n const agent = getFlag(\"agent\") || \"manual\"\n\n const stored = await store.remember({\n tool,\n agentId: agent,\n content,\n filePath: file,\n })\n\n if (stored) {\n console.log(`Stored: ${tool} ${file || \"\"} from ${agent}`)\n } else {\n console.error(\"Failed to store finding (empty content or embedding error)\")\n process.exit(1)\n }\n break\n }\n\n case \"cache\": {\n const sub = getPositionalArg(1)\n if (sub === \"list\") {\n const paths = await store.getCachedPaths()\n if (paths.length === 0) {\n console.log(\"No cached files.\")\n } else {\n console.log(`Cached files (${paths.length}):`)\n for (const p of paths) {\n console.log(` ${p}`)\n }\n }\n } else if (sub === \"get\") {\n const path = getPositionalArg(2)\n if (!path) {\n console.error(\"Usage: baro-memory cache get <path>\")\n process.exit(1)\n }\n const content = await store.getCachedFile(path)\n if (content) {\n console.log(content)\n } else {\n console.error(`Not cached: ${path}`)\n process.exit(1)\n }\n } else {\n console.error(\"Usage: baro-memory cache [list|get <path>]\")\n process.exit(1)\n }\n break\n }\n\n case \"stats\": {\n const memStats = await store.getStats()\n console.log(\"Memory Stats:\")\n console.log(` Findings: ${memStats.totalFindings}`)\n console.log(` Cached files: ${memStats.cachedFiles}`)\n console.log(` Cache size: ${memStats.cacheSizeBytes} bytes`)\n console.log(` Tools: ${memStats.toolsList.join(\", \") || \"(none)\"}`)\n console.log(` Agents: ${memStats.agentsList.join(\", \") || \"(none)\"}`)\n break\n }\n\n default:\n console.log(\"baro-memory - Shared semantic memory for baro agents\\n\")\n console.log(\"Commands:\")\n console.log(\" query <text> [--top N] [--agent id] Search for relevant findings\")\n console.log(\" store <content> --tool <tool> Store a finding\")\n console.log(\" cache list List cached files\")\n console.log(\" cache get <path> Get cached file content\")\n console.log(\" stats Show memory statistics\")\n console.log(\"\")\n console.log(\"Environment:\")\n console.log(\" BARO_MEMORY_PATH Session memory directory (set by orchestrator)\")\n }\n } finally {\n // Always close store to release resources\n if (store) await store.close()\n }\n}\n\nmain().catch(err => {\n console.error(`baro-memory error: ${err.message || err}`)\n process.exit(1)\n})\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../baro-memory/src/vectra-store.ts","../../baro-orchestrator/scripts/baro-memory.ts"],"sourcesContent":["/**\n * Vectra-backed semantic memory store for baro agents.\n *\n * Architecture:\n * - Vectra LocalIndex for vector storage + similarity search (persisted to disk)\n * - @xenova/transformers ONNX model for embedding generation (CPU-only)\n * - Separate cache.json for file content dedup (not vectorized)\n *\n * Cross-process sharing: Vectra reads/writes index.json on every operation,\n * so the orchestrator's writes are immediately visible to CLI invocations.\n *\n * ID strategy: IDs are deterministic (agent:tool:file/pattern/command).\n * This means repeated reads of the same file by the same agent upsert\n * (update in place) rather than accumulate duplicate entries.\n */\n\nimport { LocalIndex } from 'vectra'\nimport type { QueryResult } from 'vectra'\nimport { readFileSync, writeFileSync, mkdirSync, existsSync, renameSync, rmSync, readdirSync, statSync, lstatSync } from 'fs'\nimport { join } from 'path'\nimport { tmpdir, homedir } from 'os'\n\nimport type {\n CachedFile,\n Finding,\n FindingMetadata,\n MemoryStore,\n MemoryStats,\n MemoryStoreConfig,\n RecalledFinding,\n RecallOptions,\n} from './types.js'\n\n// ── Constants ────────────────────────────────────────────────────────────\n\n/** Maximum characters stored per finding content. */\nconst MAX_CONTENT_CHARS = 4000\n\n/** Maximum bytes for a single cached file (5MB). */\nconst MAX_CACHE_FILE_BYTES = 5 * 1024 * 1024\n\n/** Maximum total cache size (50MB). Beyond this, oldest entries are evicted. */\nconst MAX_TOTAL_CACHE_BYTES = 50 * 1024 * 1024\n\n/** Stale session threshold: 24 hours. */\nconst SESSION_TTL_MS = 24 * 60 * 60 * 1000\n\n/** Allowed parent directories for session paths (prevent path traversal). */\nconst ALLOWED_SESSION_PARENTS = ['.baro', 'baro-memory', 'tmp']\n\n// ── Defaults ─────────────────────────────────────────────────────────────\n\nconst DEFAULTS = {\n embeddingModel: 'Xenova/all-MiniLM-L6-v2',\n defaultMinSimilarity: 0.3,\n defaultMaxResults: 10,\n disabled: false,\n sessionPath: '',\n} satisfies Required<MemoryStoreConfig>\n\n// ── Types ────────────────────────────────────────────────────────────────\n\n/** Typed embedding pipeline from @xenova/transformers. */\ntype EmbeddingPipeline = (\n text: string,\n opts: { pooling: string; normalize: boolean },\n) => Promise<{ data: ArrayLike<number> }>\n\n/** Metadata stored alongside each vector in Vectra. */\ninterface VectraItemMetadata {\n [key: string]: string // Index signature for Vectra compatibility\n tool: string\n agentId: string\n storyId: string\n filePath: string\n pattern: string\n command: string\n tags: string\n /** The original text content (stored for retrieval). */\n content: string\n}\n\n// ── Factory ──────────────────────────────────────────────────────────────\n\n/**\n * Create a memory store backed by Vectra (local vector DB).\n *\n * - If `sessionPath` is set, the index + cache persist there.\n * - If not set, a temp directory is used (ephemeral, single-process).\n * - If `disabled`, returns a no-op store.\n *\n * @param config - Optional configuration\n * @returns MemoryStore instance\n */\nexport async function createMemoryStore(\n config?: MemoryStoreConfig,\n): Promise<MemoryStore> {\n const cfg = { ...DEFAULTS, ...config }\n\n // Validate config\n if (cfg.defaultMinSimilarity < 0 || cfg.defaultMinSimilarity > 1) {\n cfg.defaultMinSimilarity = DEFAULTS.defaultMinSimilarity\n }\n if (cfg.defaultMaxResults < 1) {\n cfg.defaultMaxResults = DEFAULTS.defaultMaxResults\n }\n\n if (cfg.disabled) {\n return new NoOpMemoryStore()\n }\n\n // Resolve and validate session path (prevent path traversal)\n const sessionPath = cfg.sessionPath || join(tmpdir(), `baro-memory-${process.pid}-${Date.now()}`)\n validateSessionPath(sessionPath)\n mkdirSync(sessionPath, { recursive: true })\n\n // Initialize Vectra index\n const indexPath = join(sessionPath, 'index')\n mkdirSync(indexPath, { recursive: true })\n const index = new LocalIndex<VectraItemMetadata>(indexPath)\n\n if (!(await index.isIndexCreated())) {\n await index.createIndex({ version: 1 })\n }\n\n // Load ONNX embedding model (cached after first load by transformers.js).\n // Pin the cache to a writable, persistent baro-owned dir: the default is\n // `node_modules/@xenova/transformers/.cache`, which on a global install\n // (e.g. /usr/local/lib) can be read-only — the model would fail to\n // download. ~/.baro/models is user-writable and survives across runs so\n // MiniLM is fetched only once. Override with TRANSFORMERS_CACHE if set.\n const transformers = await import('@xenova/transformers')\n transformers.env.cacheDir = process.env.TRANSFORMERS_CACHE || join(homedir(), '.baro', 'models')\n const { pipeline } = transformers\n const extractor = await pipeline('feature-extraction', cfg.embeddingModel) as unknown as EmbeddingPipeline\n\n return new VectraMemoryStore(index, extractor, sessionPath, cfg)\n}\n\n/**\n * Validate that sessionPath is safe (not a sensitive system directory).\n * Prevents path traversal attacks via BARO_MEMORY_PATH env var.\n */\nfunction validateSessionPath(sessionPath: string): void {\n const resolved = join(sessionPath) // normalize\n // Reject paths containing '..' traversal\n if (resolved.includes('..')) {\n throw new Error(`Invalid session path (contains ..): ${resolved}`)\n }\n // Reject obvious sensitive directories\n const dangerous = ['/etc', '/usr', '/bin', '/sbin', '/var/run', '/System', '/Library']\n for (const d of dangerous) {\n if (resolved.startsWith(d + '/') || resolved === d) {\n throw new Error(`Invalid session path (sensitive directory): ${resolved}`)\n }\n }\n // Must contain a baro-related segment or be in tmpdir\n const normalizedPath = resolved.toLowerCase()\n const isSafe = ALLOWED_SESSION_PARENTS.some(p => normalizedPath.includes(p)) ||\n normalizedPath.startsWith(tmpdir().toLowerCase())\n if (!isSafe) {\n throw new Error(\n `Invalid session path (must be under ~/.baro, tmpdir, or contain 'baro-memory'): ${resolved}`\n )\n }\n}\n\n/**\n * Prune stale session directories older than SESSION_TTL_MS.\n * Call on orchestrator startup to prevent unbounded growth.\n * Uses lstatSync to avoid following symlinks (prevents symlink attacks).\n */\nexport function pruneOldSessions(sessionsDir: string): void {\n try {\n if (!existsSync(sessionsDir)) return\n const now = Date.now()\n for (const entry of readdirSync(sessionsDir)) {\n if (!entry.startsWith('run-')) continue\n const entryPath = join(sessionsDir, entry)\n try {\n const stat = lstatSync(entryPath)\n // Skip symlinks entirely (potential attack vector)\n if (stat.isSymbolicLink()) continue\n if (stat.isDirectory() && now - stat.mtimeMs > SESSION_TTL_MS) {\n rmSync(entryPath, { recursive: true, force: true })\n }\n } catch { /* skip entries we can't stat */ }\n }\n } catch { /* non-critical — don't crash if cleanup fails */ }\n}\n\n// ── Embedding helper ─────────────────────────────────────────────────────\n\n/**\n * Generate a normalized embedding vector for text.\n * @throws Error if embedding generation fails (empty text, model error)\n */\nasync function embed(extractor: EmbeddingPipeline, text: string): Promise<number[]> {\n if (!text || !text.trim()) {\n throw new Error('Cannot embed empty text')\n }\n const output = await extractor(text, { pooling: 'mean', normalize: true })\n if (!output?.data) {\n throw new Error('Embedding model returned no data')\n }\n return Array.from(output.data) as number[]\n}\n\n// ── VectraMemoryStore ────────────────────────────────────────────────────\n\nclass VectraMemoryStore implements MemoryStore {\n private readonly index: LocalIndex<VectraItemMetadata>\n private readonly extractor: EmbeddingPipeline\n private readonly sessionPath: string\n private readonly cachePath: string\n private readonly lockPath: string\n private readonly config: Required<MemoryStoreConfig>\n\n constructor(\n index: LocalIndex<VectraItemMetadata>,\n extractor: EmbeddingPipeline,\n sessionPath: string,\n config: Required<MemoryStoreConfig>,\n ) {\n this.index = index\n this.extractor = extractor\n this.sessionPath = sessionPath\n this.cachePath = join(sessionPath, 'cache.json')\n this.lockPath = join(sessionPath, 'cache.lock')\n this.config = config\n }\n\n // ── Semantic memory ──────────────────────────────────────────\n\n async remember(finding: Finding): Promise<boolean> {\n try {\n if (!finding.content?.trim()) return false\n\n const id = this.generateId(finding)\n const vector = await embed(this.extractor, finding.content)\n const metadata: VectraItemMetadata = {\n tool: finding.tool,\n agentId: finding.agentId,\n storyId: finding.storyId ?? '',\n filePath: finding.filePath ?? '',\n pattern: finding.pattern ?? '',\n command: finding.command ?? '',\n tags: finding.tags?.join(',') ?? '',\n content: finding.content.slice(0, MAX_CONTENT_CHARS),\n }\n\n await this.index.upsertItem({ id, vector, metadata })\n return true\n } catch {\n // Graceful degradation: don't crash if embedding/storage fails\n return false\n }\n }\n\n async recall(query: string, options?: RecallOptions): Promise<RecalledFinding[]> {\n try {\n const maxResults = options?.maxResults ?? this.config.defaultMaxResults\n const minSimilarity = options?.minSimilarity ?? this.config.defaultMinSimilarity\n const excludeAgent = options?.excludeAgent\n const filterByTool = options?.filterByTool\n\n if (!query?.trim()) return []\n\n const vector = await embed(this.extractor, query)\n\n // Query more than needed so we can post-filter\n const fetchK = Math.min(Math.max(maxResults * 3, 30), 200)\n const results: QueryResult<VectraItemMetadata>[] = await this.index.queryItems(vector, fetchK)\n\n // Post-filter and collect\n const output: RecalledFinding[] = []\n\n for (const result of results) {\n if (result.score < minSimilarity) continue\n if (excludeAgent && result.item.metadata.agentId === excludeAgent) continue\n if (filterByTool?.length && !filterByTool.includes(result.item.metadata.tool)) continue\n\n output.push({\n id: result.item.id,\n content: result.item.metadata.content,\n metadata: {\n tool: result.item.metadata.tool,\n agentId: result.item.metadata.agentId,\n storyId: result.item.metadata.storyId,\n filePath: result.item.metadata.filePath,\n pattern: result.item.metadata.pattern,\n command: result.item.metadata.command,\n tags: result.item.metadata.tags,\n },\n similarity: result.score,\n })\n\n if (output.length >= maxResults) break\n }\n\n return output\n } catch {\n // Graceful degradation: return empty on failure\n return []\n }\n }\n\n async gatherContext(\n storyId: string,\n hints: string[],\n maxChars: number = 20000,\n ): Promise<string | null> {\n const query = hints.join(' ')\n if (!query.trim()) return null\n\n const results = await this.recall(query, {\n maxResults: 20,\n minSimilarity: 0.3,\n excludeAgent: storyId,\n })\n\n if (results.length === 0) return null\n\n const lines: string[] = [\n '## Codebase context (from parallel agents)',\n '',\n 'Other agents in this run discovered the following.',\n 'Use this directly without re-reading files.',\n '',\n ]\n\n let totalChars = 0\n for (const result of results) {\n const header = `[${result.metadata.agentId}] ${result.metadata.tool}${\n result.metadata.filePath ? ` ${result.metadata.filePath}` : ''\n } (relevance: ${Math.round(result.similarity * 100)}%)`\n\n const entry = `${header}\\n${result.content}\\n`\n\n if (totalChars + entry.length > maxChars) {\n lines.push('[...truncated...]')\n break\n }\n\n lines.push(entry)\n totalChars += entry.length\n }\n\n return lines.join('\\n')\n }\n\n // ── File cache ───────────────────────────────────────────────\n // Simple JSON file (not vectorized -- exact key-value lookup).\n // NOTE: Multi-process writes use merge-on-write to reduce data loss.\n // This is best-effort — not ACID. For guaranteed consistency, use SQLite.\n\n async cacheFile(path: string, content: string, agentId: string): Promise<void> {\n // Skip excessively large files\n if (content.length > MAX_CACHE_FILE_BYTES) return\n\n // Merge-on-write: reload fresh state before modifying (reduces race window)\n const cache = this.loadCache()\n const existing = cache[path]\n if (!existing || existing.content !== content) {\n cache[path] = { path, content, readByAgent: agentId, timestamp: Date.now() }\n // Evict oldest entries if total cache exceeds limit\n this.evictIfNeeded(cache)\n this.saveCache(cache)\n }\n }\n\n async getCachedFile(path: string): Promise<string | null> {\n const cache = this.loadCache()\n return cache[path]?.content ?? null\n }\n\n async hasFile(path: string): Promise<boolean> {\n const cache = this.loadCache()\n return path in cache\n }\n\n async getCachedPaths(): Promise<string[]> {\n const cache = this.loadCache()\n return Object.keys(cache)\n }\n\n // ── Stats ────────────────────────────────────────────────────\n\n async getStats(): Promise<MemoryStats> {\n try {\n const items = await this.index.listItems<VectraItemMetadata>()\n const tools = new Set<string>()\n const agents = new Set<string>()\n\n for (const item of items) {\n tools.add(item.metadata.tool)\n agents.add(item.metadata.agentId)\n }\n\n const cache = this.loadCache()\n let cacheSizeBytes = 0\n for (const entry of Object.values(cache)) {\n cacheSizeBytes += entry.content.length\n }\n\n return {\n totalFindings: items.length,\n uniqueTools: tools.size,\n uniqueAgents: agents.size,\n toolsList: Array.from(tools),\n agentsList: Array.from(agents),\n cachedFiles: Object.keys(cache).length,\n cacheSizeBytes,\n }\n } catch {\n return {\n totalFindings: 0, uniqueTools: 0, uniqueAgents: 0,\n toolsList: [], agentsList: [], cachedFiles: 0, cacheSizeBytes: 0,\n }\n }\n }\n\n async close(): Promise<void> {\n // Vectra auto-persists; clean up lockfile if present\n try { rmSync(this.lockPath, { force: true }) } catch {}\n }\n\n // ── Private helpers ──────────────────────────────────────────\n\n /**\n * Evict oldest cache entries until total size is under MAX_TOTAL_CACHE_BYTES.\n * LRU-style: removes entries with oldest timestamps first.\n */\n private evictIfNeeded(cache: Record<string, CachedFile>): void {\n let totalBytes = 0\n for (const entry of Object.values(cache)) {\n totalBytes += entry.content.length\n }\n if (totalBytes <= MAX_TOTAL_CACHE_BYTES) return\n\n // Sort by timestamp ascending (oldest first)\n const entries = Object.entries(cache).sort((a, b) => a[1].timestamp - b[1].timestamp)\n for (const [key, entry] of entries) {\n if (totalBytes <= MAX_TOTAL_CACHE_BYTES) break\n totalBytes -= entry.content.length\n delete cache[key]\n }\n }\n\n private generateId(finding: Finding): string {\n const parts = [finding.agentId, finding.tool]\n if (finding.filePath) parts.push(finding.filePath)\n else if (finding.pattern) parts.push(finding.pattern)\n else if (finding.command) parts.push(finding.command)\n else {\n // For generic findings (no file/pattern/command), use a short\n // content hash to maintain deterministic dedup while avoiding\n // collisions. Same content = same ID = upsert (not duplicate).\n let hash = 0\n const str = finding.content.slice(0, 100)\n for (let i = 0; i < str.length; i++) {\n hash = ((hash << 5) - hash + str.charCodeAt(i)) | 0\n }\n parts.push(Math.abs(hash).toString(36))\n }\n return parts.join(':')\n }\n\n /**\n * Load cache from disk. Reads are not locked — if another process is\n * mid-write using the atomic rename strategy, we'll either get the old\n * complete version or the new complete version (never a partial write).\n */\n private loadCache(): Record<string, CachedFile> {\n try {\n if (existsSync(this.cachePath)) {\n const raw = readFileSync(this.cachePath, 'utf-8')\n if (raw.trim()) {\n return JSON.parse(raw)\n }\n }\n } catch {\n // Corrupted or being written -- return empty (safe default)\n }\n return {}\n }\n\n /**\n * Save cache atomically (write to PID-scoped tmp file, then rename).\n * The rename is atomic on POSIX, so concurrent readers see either the\n * old or new version — never a partial write.\n */\n private saveCache(cache: Record<string, CachedFile>): void {\n try {\n // Write lockfile (advisory — best effort)\n writeFileSync(this.lockPath, String(process.pid), 'utf-8')\n\n const tmp = this.cachePath + `.${process.pid}.tmp`\n writeFileSync(tmp, JSON.stringify(cache), 'utf-8')\n renameSync(tmp, this.cachePath)\n } catch {\n // Fallback: direct write (may be read mid-write by other processes)\n try {\n writeFileSync(this.cachePath, JSON.stringify(cache), 'utf-8')\n } catch { /* disk full or permission denied -- data loss accepted */ }\n } finally {\n try { if (existsSync(this.lockPath)) rmSync(this.lockPath) } catch {}\n }\n }\n}\n\n// ── NoOp store ───────────────────────────────────────────────────────────\n\n/**\n * No-op implementation for when memory is disabled.\n */\nclass NoOpMemoryStore implements MemoryStore {\n async remember(_finding: Finding): Promise<boolean> { return false }\n async recall(_query: string, _options?: RecallOptions): Promise<RecalledFinding[]> { return [] }\n async gatherContext(_storyId: string, _hints: string[]): Promise<string | null> { return null }\n async cacheFile(_path: string, _content: string, _agentId: string): Promise<void> {}\n async getCachedFile(_path: string): Promise<string | null> { return null }\n async hasFile(_path: string): Promise<boolean> { return false }\n async getCachedPaths(): Promise<string[]> { return [] }\n async getStats(): Promise<MemoryStats> {\n return {\n totalFindings: 0, uniqueTools: 0, uniqueAgents: 0,\n toolsList: [], agentsList: [], cachedFiles: 0, cacheSizeBytes: 0,\n }\n }\n async close(): Promise<void> {}\n}\n","/**\n * baro-memory CLI - Query and store findings in the shared Vectra memory.\n *\n * Connects to the same Vectra index as the orchestrator via the\n * BARO_MEMORY_PATH environment variable. Story agents can use this\n * mid-flight to query context and store findings for sibling agents.\n *\n * Usage:\n * baro-memory query \"JWT authentication\" [--top 5] [--agent story-1]\n * baro-memory store \"found auth pattern\" --tool Read --file src/auth.ts --agent story-1\n * baro-memory cache list\n * baro-memory cache get src/auth.ts\n * baro-memory stats\n *\n * Environment:\n * BARO_MEMORY_PATH - Path to the shared session memory directory.\n * Set automatically by the baro orchestrator.\n */\n\nimport { createMemoryStore, type MemoryStore } from \"@baro/memory\"\n\nconst args = process.argv.slice(2)\nconst command = args[0]\n\nfunction getFlag(name: string): string | undefined {\n const idx = args.indexOf(`--${name}`)\n if (idx < 0 || idx + 1 >= args.length) return undefined\n const value = args[idx + 1]\n // Don't treat the next flag as a value\n if (value.startsWith('--')) return undefined\n return value\n}\n\nfunction getPositionalArg(index: number): string | undefined {\n const val = args[index]\n if (!val || val.startsWith('--')) return undefined\n return val\n}\n\nasync function main() {\n // Read session path from env (set by orchestrator) or --path flag\n const sessionPath = getFlag(\"path\") || process.env.BARO_MEMORY_PATH\n\n if (!sessionPath) {\n console.error(\n \"Error: No memory session path found.\\n\\n\" +\n \"Set BARO_MEMORY_PATH environment variable or pass --path <dir>.\\n\" +\n \"This is normally set automatically by the baro orchestrator.\\n\"\n )\n process.exit(1)\n }\n\n let store: MemoryStore | null = null\n\n try {\n store = await createMemoryStore({ sessionPath })\n\n switch (command) {\n case \"query\": {\n const query = getPositionalArg(1)\n if (!query) {\n console.error(\"Usage: baro-memory query <text> [--top N] [--agent id]\")\n process.exit(1)\n }\n const topRaw = getFlag(\"top\")\n const top = topRaw ? parseInt(topRaw, 10) : 5\n if (isNaN(top) || top < 1) {\n console.error(\"Error: --top must be a positive integer\")\n process.exit(1)\n }\n const agent = getFlag(\"agent\")\n\n const results = await store.recall(query, {\n maxResults: top,\n minSimilarity: 0.3,\n excludeAgent: agent,\n })\n\n if (results.length === 0) {\n console.log(\"No relevant findings found.\")\n } else {\n console.log(`Found ${results.length} relevant findings:\\n`)\n for (const r of results) {\n console.log(`[${r.metadata.tool}] ${r.metadata.agentId} (similarity: ${r.similarity.toFixed(2)})`)\n if (r.metadata.filePath) console.log(` File: ${r.metadata.filePath}`)\n console.log(` ${r.content.slice(0, 200)}${r.content.length > 200 ? '...' : ''}`)\n console.log()\n }\n }\n break\n }\n\n case \"store\": {\n const content = getPositionalArg(1)\n if (!content) {\n console.error(\"Usage: baro-memory store <content> --tool <tool> [--file <path>] [--agent <id>]\")\n process.exit(1)\n }\n const tool = getFlag(\"tool\") || \"Bash\"\n const file = getFlag(\"file\")\n const agent = getFlag(\"agent\") || \"manual\"\n\n const stored = await store.remember({\n tool,\n agentId: agent,\n content,\n filePath: file,\n })\n\n if (stored) {\n console.log(`Stored: ${tool} ${file || \"\"} from ${agent}`)\n } else {\n console.error(\"Failed to store finding (empty content or embedding error)\")\n process.exit(1)\n }\n break\n }\n\n case \"cache\": {\n const sub = getPositionalArg(1)\n if (sub === \"list\") {\n const paths = await store.getCachedPaths()\n if (paths.length === 0) {\n console.log(\"No cached files.\")\n } else {\n console.log(`Cached files (${paths.length}):`)\n for (const p of paths) {\n console.log(` ${p}`)\n }\n }\n } else if (sub === \"get\") {\n const path = getPositionalArg(2)\n if (!path) {\n console.error(\"Usage: baro-memory cache get <path>\")\n process.exit(1)\n }\n const content = await store.getCachedFile(path)\n if (content) {\n console.log(content)\n } else {\n console.error(`Not cached: ${path}`)\n process.exit(1)\n }\n } else {\n console.error(\"Usage: baro-memory cache [list|get <path>]\")\n process.exit(1)\n }\n break\n }\n\n case \"stats\": {\n const memStats = await store.getStats()\n console.log(\"Memory Stats:\")\n console.log(` Findings: ${memStats.totalFindings}`)\n console.log(` Cached files: ${memStats.cachedFiles}`)\n console.log(` Cache size: ${memStats.cacheSizeBytes} bytes`)\n console.log(` Tools: ${memStats.toolsList.join(\", \") || \"(none)\"}`)\n console.log(` Agents: ${memStats.agentsList.join(\", \") || \"(none)\"}`)\n break\n }\n\n default:\n console.log(\"baro-memory - Shared semantic memory for baro agents\\n\")\n console.log(\"Commands:\")\n console.log(\" query <text> [--top N] [--agent id] Search for relevant findings\")\n console.log(\" store <content> --tool <tool> Store a finding\")\n console.log(\" cache list List cached files\")\n console.log(\" cache get <path> Get cached file content\")\n console.log(\" stats Show memory statistics\")\n console.log(\"\")\n console.log(\"Environment:\")\n console.log(\" BARO_MEMORY_PATH Session memory directory (set by orchestrator)\")\n }\n } finally {\n // Always close store to release resources\n if (store) await store.close()\n }\n}\n\nmain().catch(err => {\n console.error(`baro-memory error: ${err.message || err}`)\n process.exit(1)\n})\n"],"mappings":"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","names":[]}