@hasna/mementos 0.8.0 → 0.9.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.
package/dist/cli/index.js CHANGED
@@ -2520,7 +2520,30 @@ var init_database = __esm(() => {
2520
2520
  CREATE INDEX IF NOT EXISTS idx_webhook_hooks_type ON webhook_hooks(type);
2521
2521
  CREATE INDEX IF NOT EXISTS idx_webhook_hooks_enabled ON webhook_hooks(enabled);
2522
2522
  INSERT OR IGNORE INTO _migrations (id) VALUES (10);
2523
- `
2523
+ `,
2524
+ `
2525
+ CREATE TABLE IF NOT EXISTS session_memory_jobs (
2526
+ id TEXT PRIMARY KEY,
2527
+ session_id TEXT NOT NULL,
2528
+ agent_id TEXT,
2529
+ project_id TEXT,
2530
+ source TEXT NOT NULL DEFAULT 'manual' CHECK(source IN ('claude-code','codex','manual','open-sessions')),
2531
+ status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','processing','completed','failed')),
2532
+ transcript TEXT NOT NULL,
2533
+ chunk_count INTEGER NOT NULL DEFAULT 0,
2534
+ memories_extracted INTEGER NOT NULL DEFAULT 0,
2535
+ error TEXT,
2536
+ metadata TEXT NOT NULL DEFAULT '{}',
2537
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
2538
+ started_at TEXT,
2539
+ completed_at TEXT
2540
+ );
2541
+ CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_status ON session_memory_jobs(status);
2542
+ CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_agent ON session_memory_jobs(agent_id);
2543
+ CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_project ON session_memory_jobs(project_id);
2544
+ CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_session ON session_memory_jobs(session_id);
2545
+ INSERT OR IGNORE INTO _migrations (id) VALUES (13);
2546
+ `
2524
2547
  ];
2525
2548
  });
2526
2549
 
@@ -6345,6 +6368,408 @@ var init_synthesis2 = __esm(() => {
6345
6368
  init_metrics();
6346
6369
  });
6347
6370
 
6371
+ // src/db/session-jobs.ts
6372
+ var exports_session_jobs = {};
6373
+ __export(exports_session_jobs, {
6374
+ updateSessionJob: () => updateSessionJob,
6375
+ listSessionJobs: () => listSessionJobs,
6376
+ getSessionJob: () => getSessionJob,
6377
+ getNextPendingJob: () => getNextPendingJob,
6378
+ createSessionJob: () => createSessionJob
6379
+ });
6380
+ function parseJobRow(row) {
6381
+ return {
6382
+ id: row["id"],
6383
+ session_id: row["session_id"],
6384
+ agent_id: row["agent_id"] || null,
6385
+ project_id: row["project_id"] || null,
6386
+ source: row["source"],
6387
+ status: row["status"],
6388
+ transcript: row["transcript"],
6389
+ chunk_count: row["chunk_count"],
6390
+ memories_extracted: row["memories_extracted"],
6391
+ error: row["error"] || null,
6392
+ metadata: JSON.parse(row["metadata"] || "{}"),
6393
+ created_at: row["created_at"],
6394
+ started_at: row["started_at"] || null,
6395
+ completed_at: row["completed_at"] || null
6396
+ };
6397
+ }
6398
+ function createSessionJob(input, db) {
6399
+ const d = db || getDatabase();
6400
+ const id = uuid();
6401
+ const timestamp = now();
6402
+ const source = input.source ?? "manual";
6403
+ const metadata = JSON.stringify(input.metadata ?? {});
6404
+ d.run(`INSERT INTO session_memory_jobs
6405
+ (id, session_id, agent_id, project_id, source, status, transcript, chunk_count, memories_extracted, metadata, created_at)
6406
+ VALUES (?, ?, ?, ?, ?, 'pending', ?, 0, 0, ?, ?)`, [
6407
+ id,
6408
+ input.session_id,
6409
+ input.agent_id ?? null,
6410
+ input.project_id ?? null,
6411
+ source,
6412
+ input.transcript,
6413
+ metadata,
6414
+ timestamp
6415
+ ]);
6416
+ return getSessionJob(id, d);
6417
+ }
6418
+ function getSessionJob(id, db) {
6419
+ const d = db || getDatabase();
6420
+ const row = d.query("SELECT * FROM session_memory_jobs WHERE id = ?").get(id);
6421
+ if (!row)
6422
+ return null;
6423
+ return parseJobRow(row);
6424
+ }
6425
+ function listSessionJobs(filter, db) {
6426
+ const d = db || getDatabase();
6427
+ const conditions = [];
6428
+ const params = [];
6429
+ if (filter?.agent_id) {
6430
+ conditions.push("agent_id = ?");
6431
+ params.push(filter.agent_id);
6432
+ }
6433
+ if (filter?.project_id) {
6434
+ conditions.push("project_id = ?");
6435
+ params.push(filter.project_id);
6436
+ }
6437
+ if (filter?.status) {
6438
+ conditions.push("status = ?");
6439
+ params.push(filter.status);
6440
+ }
6441
+ if (filter?.session_id) {
6442
+ conditions.push("session_id = ?");
6443
+ params.push(filter.session_id);
6444
+ }
6445
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
6446
+ const limit = filter?.limit ?? 20;
6447
+ const offset = filter?.offset ?? 0;
6448
+ const rows = d.query(`SELECT * FROM session_memory_jobs ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`).all(...params, limit, offset);
6449
+ return rows.map(parseJobRow);
6450
+ }
6451
+ function updateSessionJob(id, updates, db) {
6452
+ const d = db || getDatabase();
6453
+ const setClauses = [];
6454
+ const params = [];
6455
+ if (updates.status !== undefined) {
6456
+ setClauses.push("status = ?");
6457
+ params.push(updates.status);
6458
+ }
6459
+ if (updates.chunk_count !== undefined) {
6460
+ setClauses.push("chunk_count = ?");
6461
+ params.push(updates.chunk_count);
6462
+ }
6463
+ if (updates.memories_extracted !== undefined) {
6464
+ setClauses.push("memories_extracted = ?");
6465
+ params.push(updates.memories_extracted);
6466
+ }
6467
+ if ("error" in updates) {
6468
+ setClauses.push("error = ?");
6469
+ params.push(updates.error ?? null);
6470
+ }
6471
+ if ("started_at" in updates) {
6472
+ setClauses.push("started_at = ?");
6473
+ params.push(updates.started_at ?? null);
6474
+ }
6475
+ if ("completed_at" in updates) {
6476
+ setClauses.push("completed_at = ?");
6477
+ params.push(updates.completed_at ?? null);
6478
+ }
6479
+ if (setClauses.length === 0)
6480
+ return getSessionJob(id, d);
6481
+ params.push(id);
6482
+ d.run(`UPDATE session_memory_jobs SET ${setClauses.join(", ")} WHERE id = ?`, params);
6483
+ return getSessionJob(id, d);
6484
+ }
6485
+ function getNextPendingJob(db) {
6486
+ const d = db || getDatabase();
6487
+ const row = d.query("SELECT * FROM session_memory_jobs WHERE status = 'pending' ORDER BY created_at ASC LIMIT 1").get();
6488
+ if (!row)
6489
+ return null;
6490
+ return parseJobRow(row);
6491
+ }
6492
+ var init_session_jobs = __esm(() => {
6493
+ init_database();
6494
+ });
6495
+
6496
+ // src/lib/session-processor.ts
6497
+ function chunkTranscript(transcript, chunkSize = 2000, overlap = 200) {
6498
+ if (!transcript || transcript.length === 0)
6499
+ return [];
6500
+ if (transcript.length <= chunkSize)
6501
+ return [transcript];
6502
+ const chunks = [];
6503
+ let start = 0;
6504
+ while (start < transcript.length) {
6505
+ const end = Math.min(start + chunkSize, transcript.length);
6506
+ chunks.push(transcript.slice(start, end));
6507
+ if (end === transcript.length)
6508
+ break;
6509
+ start += chunkSize - overlap;
6510
+ }
6511
+ return chunks;
6512
+ }
6513
+ async function extractMemoriesFromChunk(chunk, context, db) {
6514
+ const provider = providerRegistry.getAvailable();
6515
+ if (!provider)
6516
+ return 0;
6517
+ try {
6518
+ const extracted = await provider.extractMemories(SESSION_EXTRACTION_USER_TEMPLATE(chunk, context.sessionId), {
6519
+ sessionId: context.sessionId,
6520
+ agentId: context.agentId,
6521
+ projectId: context.projectId
6522
+ });
6523
+ let savedCount = 0;
6524
+ const sourceTag = context.source ? `source:${context.source}` : "source:manual";
6525
+ for (const memory of extracted) {
6526
+ if (!memory.content || !memory.content.trim())
6527
+ continue;
6528
+ try {
6529
+ createMemory({
6530
+ key: memory.content.slice(0, 120).replace(/\s+/g, "-").toLowerCase(),
6531
+ value: memory.content,
6532
+ category: memory.category,
6533
+ scope: memory.suggestedScope ?? "shared",
6534
+ importance: memory.importance,
6535
+ tags: [
6536
+ ...memory.tags,
6537
+ "session-extracted",
6538
+ sourceTag,
6539
+ `session:${context.sessionId}`
6540
+ ],
6541
+ source: "auto",
6542
+ agent_id: context.agentId,
6543
+ project_id: context.projectId,
6544
+ session_id: context.sessionId,
6545
+ metadata: {
6546
+ auto_extracted: true,
6547
+ session_source: context.source ?? "manual",
6548
+ extracted_at: new Date().toISOString(),
6549
+ reasoning: memory.reasoning
6550
+ }
6551
+ }, "merge", db);
6552
+ savedCount++;
6553
+ } catch {}
6554
+ }
6555
+ return savedCount;
6556
+ } catch {
6557
+ try {
6558
+ const fallbacks = providerRegistry.getFallbacks();
6559
+ for (const fallback of fallbacks) {
6560
+ try {
6561
+ const extracted = await fallback.extractMemories(SESSION_EXTRACTION_USER_TEMPLATE(chunk, context.sessionId), {
6562
+ sessionId: context.sessionId,
6563
+ agentId: context.agentId,
6564
+ projectId: context.projectId
6565
+ });
6566
+ let savedCount = 0;
6567
+ const sourceTag = context.source ? `source:${context.source}` : "source:manual";
6568
+ for (const memory of extracted) {
6569
+ if (!memory.content || !memory.content.trim())
6570
+ continue;
6571
+ try {
6572
+ createMemory({
6573
+ key: memory.content.slice(0, 120).replace(/\s+/g, "-").toLowerCase(),
6574
+ value: memory.content,
6575
+ category: memory.category,
6576
+ scope: memory.suggestedScope ?? "shared",
6577
+ importance: memory.importance,
6578
+ tags: [
6579
+ ...memory.tags,
6580
+ "session-extracted",
6581
+ sourceTag,
6582
+ `session:${context.sessionId}`
6583
+ ],
6584
+ source: "auto",
6585
+ agent_id: context.agentId,
6586
+ project_id: context.projectId,
6587
+ session_id: context.sessionId,
6588
+ metadata: {
6589
+ auto_extracted: true,
6590
+ session_source: context.source ?? "manual",
6591
+ extracted_at: new Date().toISOString(),
6592
+ reasoning: memory.reasoning
6593
+ }
6594
+ }, "merge", db);
6595
+ savedCount++;
6596
+ } catch {}
6597
+ }
6598
+ if (savedCount > 0)
6599
+ return savedCount;
6600
+ } catch {
6601
+ continue;
6602
+ }
6603
+ }
6604
+ } catch {}
6605
+ return 0;
6606
+ }
6607
+ }
6608
+ async function processSessionJob(jobId, db) {
6609
+ const result = {
6610
+ jobId,
6611
+ chunksProcessed: 0,
6612
+ memoriesExtracted: 0,
6613
+ errors: []
6614
+ };
6615
+ let job;
6616
+ try {
6617
+ job = getSessionJob(jobId, db);
6618
+ if (!job) {
6619
+ result.errors.push(`Job not found: ${jobId}`);
6620
+ return result;
6621
+ }
6622
+ } catch (e) {
6623
+ result.errors.push(`Failed to fetch job: ${String(e)}`);
6624
+ return result;
6625
+ }
6626
+ try {
6627
+ updateSessionJob(jobId, { status: "processing", started_at: new Date().toISOString() }, db);
6628
+ } catch (e) {
6629
+ result.errors.push(`Failed to mark job as processing: ${String(e)}`);
6630
+ return result;
6631
+ }
6632
+ const chunks = chunkTranscript(job.transcript);
6633
+ try {
6634
+ updateSessionJob(jobId, { chunk_count: chunks.length }, db);
6635
+ } catch {}
6636
+ let totalMemories = 0;
6637
+ for (let i = 0;i < chunks.length; i++) {
6638
+ const chunk = chunks[i];
6639
+ try {
6640
+ const count = await extractMemoriesFromChunk(chunk, {
6641
+ sessionId: job.session_id,
6642
+ agentId: job.agent_id ?? undefined,
6643
+ projectId: job.project_id ?? undefined,
6644
+ source: job.source
6645
+ }, db);
6646
+ totalMemories += count;
6647
+ result.chunksProcessed++;
6648
+ } catch (e) {
6649
+ result.errors.push(`Chunk ${i} failed: ${String(e)}`);
6650
+ }
6651
+ }
6652
+ result.memoriesExtracted = totalMemories;
6653
+ try {
6654
+ if (result.errors.length > 0 && result.chunksProcessed === 0) {
6655
+ updateSessionJob(jobId, {
6656
+ status: "failed",
6657
+ error: result.errors.join("; "),
6658
+ completed_at: new Date().toISOString(),
6659
+ memories_extracted: totalMemories,
6660
+ chunk_count: chunks.length
6661
+ }, db);
6662
+ } else {
6663
+ updateSessionJob(jobId, {
6664
+ status: "completed",
6665
+ completed_at: new Date().toISOString(),
6666
+ memories_extracted: totalMemories,
6667
+ chunk_count: chunks.length
6668
+ }, db);
6669
+ }
6670
+ } catch (e) {
6671
+ result.errors.push(`Failed to update job status: ${String(e)}`);
6672
+ }
6673
+ return result;
6674
+ }
6675
+ var SESSION_EXTRACTION_USER_TEMPLATE = (chunk, sessionId) => `Extract memories from this session chunk (session: ${sessionId}):
6676
+
6677
+ ${chunk}
6678
+
6679
+ Return JSON array: [{"key": "...", "value": "...", "category": "knowledge|fact|preference|history", "importance": 1-10, "tags": [...]}]`;
6680
+ var init_session_processor = __esm(() => {
6681
+ init_memories();
6682
+ init_registry();
6683
+ init_session_jobs();
6684
+ });
6685
+
6686
+ // src/lib/session-queue.ts
6687
+ var exports_session_queue = {};
6688
+ __export(exports_session_queue, {
6689
+ startSessionQueueWorker: () => startSessionQueueWorker,
6690
+ getSessionQueueStats: () => getSessionQueueStats,
6691
+ enqueueSessionJob: () => enqueueSessionJob
6692
+ });
6693
+ function enqueueSessionJob(jobId) {
6694
+ _pendingQueue.add(jobId);
6695
+ if (!_isProcessing) {
6696
+ _processNext();
6697
+ }
6698
+ }
6699
+ function getSessionQueueStats() {
6700
+ try {
6701
+ const db = getDatabase();
6702
+ const rows = db.query("SELECT status, COUNT(*) as count FROM session_memory_jobs GROUP BY status").all();
6703
+ const stats = {
6704
+ pending: 0,
6705
+ processing: 0,
6706
+ completed: 0,
6707
+ failed: 0
6708
+ };
6709
+ for (const row of rows) {
6710
+ if (row.status === "pending")
6711
+ stats.pending = row.count;
6712
+ else if (row.status === "processing")
6713
+ stats.processing = row.count;
6714
+ else if (row.status === "completed")
6715
+ stats.completed = row.count;
6716
+ else if (row.status === "failed")
6717
+ stats.failed = row.count;
6718
+ }
6719
+ return stats;
6720
+ } catch {
6721
+ return {
6722
+ pending: _pendingQueue.size,
6723
+ processing: _isProcessing ? 1 : 0,
6724
+ completed: 0,
6725
+ failed: 0
6726
+ };
6727
+ }
6728
+ }
6729
+ function startSessionQueueWorker() {
6730
+ if (_workerStarted)
6731
+ return;
6732
+ _workerStarted = true;
6733
+ setInterval(() => {
6734
+ _processNext();
6735
+ }, 5000);
6736
+ }
6737
+ async function _processNext() {
6738
+ if (_isProcessing)
6739
+ return;
6740
+ let jobId;
6741
+ if (_pendingQueue.size > 0) {
6742
+ jobId = [..._pendingQueue][0];
6743
+ _pendingQueue.delete(jobId);
6744
+ } else {
6745
+ try {
6746
+ const job = getNextPendingJob();
6747
+ if (job)
6748
+ jobId = job.id;
6749
+ } catch {
6750
+ return;
6751
+ }
6752
+ }
6753
+ if (!jobId)
6754
+ return;
6755
+ _isProcessing = true;
6756
+ try {
6757
+ await processSessionJob(jobId);
6758
+ } catch {} finally {
6759
+ _isProcessing = false;
6760
+ if (_pendingQueue.size > 0) {
6761
+ _processNext();
6762
+ }
6763
+ }
6764
+ }
6765
+ var _pendingQueue, _isProcessing = false, _workerStarted = false;
6766
+ var init_session_queue = __esm(() => {
6767
+ init_database();
6768
+ init_session_jobs();
6769
+ init_session_processor();
6770
+ _pendingQueue = new Set;
6771
+ });
6772
+
6348
6773
  // node_modules/commander/esm.mjs
6349
6774
  var import__ = __toESM(require_commander(), 1);
6350
6775
  var {
@@ -9993,4 +10418,86 @@ synthesisCmd.command("rollback <runId>").description("Roll back a synthesis run"
9993
10418
  console.log(chalk.red(` Errors: ${result.errors.join(", ")}`));
9994
10419
  }
9995
10420
  });
10421
+ var sessionCmd = program2.command("session").description("Session auto-memory \u2014 ingest session transcripts for memory extraction");
10422
+ sessionCmd.command("ingest <transcriptFile>").description("Ingest a session transcript file for memory extraction").option("--session-id <id>", "Session ID (default: auto-generated)").option("--agent <id>", "Agent ID").option("--project <id>", "Project ID").option("--source <source>", "Source (claude-code, codex, manual, open-sessions)", "manual").action(async (transcriptFile, opts) => {
10423
+ const { readFileSync: readFileSync3 } = await import("fs");
10424
+ const { createSessionJob: createSessionJob2 } = await Promise.resolve().then(() => (init_session_jobs(), exports_session_jobs));
10425
+ const { enqueueSessionJob: enqueueSessionJob2 } = await Promise.resolve().then(() => (init_session_queue(), exports_session_queue));
10426
+ const transcript = readFileSync3(transcriptFile, "utf-8");
10427
+ const sessionId = opts.sessionId ?? `cli-${Date.now()}`;
10428
+ const job = createSessionJob2({
10429
+ session_id: sessionId,
10430
+ transcript,
10431
+ source: opts.source,
10432
+ agent_id: opts.agent,
10433
+ project_id: opts.project
10434
+ });
10435
+ enqueueSessionJob2(job.id);
10436
+ console.log(chalk.green(`\u2713 Session queued: ${chalk.cyan(job.id)}`));
10437
+ console.log(` Session: ${sessionId}`);
10438
+ console.log(` Length: ${transcript.length} chars`);
10439
+ });
10440
+ sessionCmd.command("status <jobId>").description("Check status of a session extraction job").action(async (jobId) => {
10441
+ const { getSessionJob: getSessionJob2 } = await Promise.resolve().then(() => (init_session_jobs(), exports_session_jobs));
10442
+ const job = getSessionJob2(jobId);
10443
+ if (!job) {
10444
+ console.error(chalk.red(`Job not found: ${jobId}`));
10445
+ process.exit(1);
10446
+ }
10447
+ const statusColor = job.status === "completed" ? chalk.green : job.status === "failed" ? chalk.red : chalk.yellow;
10448
+ console.log(`${chalk.cyan(job.id)} [${statusColor(job.status)}]`);
10449
+ console.log(` Session: ${job.session_id}`);
10450
+ console.log(` Chunks: ${job.chunk_count}`);
10451
+ console.log(` Extracted: ${job.memories_extracted} memories`);
10452
+ if (job.error)
10453
+ console.log(chalk.red(` Error: ${job.error}`));
10454
+ });
10455
+ sessionCmd.command("list").description("List session extraction jobs").option("--agent <id>", "Filter by agent").option("--project <id>", "Filter by project").option("--status <status>", "Filter by status").option("--limit <n>", "Max results", "20").action(async (opts) => {
10456
+ const { listSessionJobs: listSessionJobs2 } = await Promise.resolve().then(() => (init_session_jobs(), exports_session_jobs));
10457
+ const jobs = listSessionJobs2({
10458
+ agent_id: opts.agent,
10459
+ project_id: opts.project,
10460
+ status: opts.status,
10461
+ limit: parseInt(opts.limit)
10462
+ });
10463
+ if (jobs.length === 0) {
10464
+ console.log(chalk.gray("No session jobs found."));
10465
+ return;
10466
+ }
10467
+ for (const job of jobs) {
10468
+ const statusColor = job.status === "completed" ? chalk.green : job.status === "failed" ? chalk.red : chalk.yellow;
10469
+ console.log(`${chalk.cyan(job.id.slice(0, 8))} [${statusColor(job.status)}] ${job.memories_extracted} memories | ${job.created_at.slice(0, 10)}`);
10470
+ }
10471
+ });
10472
+ sessionCmd.command("setup-hook").description("Install mementos session hook into Claude Code or Codex").option("--claude", "Install Claude Code stop hook").option("--codex", "Install Codex session hook").option("--show", "Print hook script instead of installing").action(async (opts) => {
10473
+ const { resolve: resolve4 } = await import("path");
10474
+ const hookPath = resolve4(import.meta.dirname, "../../scripts/hooks");
10475
+ if (opts.claude) {
10476
+ const script = `${hookPath}/claude-stop-hook.ts`;
10477
+ if (opts.show) {
10478
+ const { readFileSync: readFileSync3 } = await import("fs");
10479
+ console.log(readFileSync3(script, "utf-8"));
10480
+ return;
10481
+ }
10482
+ console.log(chalk.bold("Claude Code stop hook installation:"));
10483
+ console.log("");
10484
+ console.log("Add to your .claude/settings.json:");
10485
+ console.log(chalk.cyan(JSON.stringify({
10486
+ hooks: {
10487
+ Stop: [{ matcher: "", hooks: [{ type: "command", command: `bun ${script}` }] }]
10488
+ }
10489
+ }, null, 2)));
10490
+ console.log("");
10491
+ console.log(`Or run: ${chalk.cyan(`claude hooks add Stop "bun ${script}"`)}`);
10492
+ } else if (opts.codex) {
10493
+ const script = `${hookPath}/codex-stop-hook.ts`;
10494
+ console.log(chalk.bold("Codex session hook installation:"));
10495
+ console.log("");
10496
+ console.log("Add to ~/.codex/config.toml:");
10497
+ console.log(chalk.cyan(`[hooks]
10498
+ session_end = "bun ${script}"`));
10499
+ } else {
10500
+ console.log("Usage: mementos session setup-hook --claude | --codex");
10501
+ }
10502
+ });
9996
10503
  program2.parse(process.argv);
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAmCtC,wBAAgB,SAAS,IAAI,MAAM,CAkBlC;AA4WD,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAerD;AA+BD,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,GAAG,IAAI,MAAM,CAE5B;AAED,wBAAgB,IAAI,IAAI,MAAM,CAE7B;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAef"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAmCtC,wBAAgB,SAAS,IAAI,MAAM,CAkBlC;AAsYD,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAerD;AA+BD,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,GAAG,IAAI,MAAM,CAE5B;AAED,wBAAgB,IAAI,IAAI,MAAM,CAE7B;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAef"}
@@ -0,0 +1,49 @@
1
+ import { Database } from "bun:sqlite";
2
+ export type SessionJobSource = "claude-code" | "codex" | "manual" | "open-sessions";
3
+ export type SessionJobStatus = "pending" | "processing" | "completed" | "failed";
4
+ export interface SessionMemoryJob {
5
+ id: string;
6
+ session_id: string;
7
+ agent_id: string | null;
8
+ project_id: string | null;
9
+ source: SessionJobSource;
10
+ status: SessionJobStatus;
11
+ transcript: string;
12
+ chunk_count: number;
13
+ memories_extracted: number;
14
+ error: string | null;
15
+ metadata: Record<string, unknown>;
16
+ created_at: string;
17
+ started_at: string | null;
18
+ completed_at: string | null;
19
+ }
20
+ export interface CreateSessionJobInput {
21
+ session_id: string;
22
+ transcript: string;
23
+ source?: SessionJobSource;
24
+ agent_id?: string;
25
+ project_id?: string;
26
+ metadata?: Record<string, unknown>;
27
+ }
28
+ export interface SessionJobFilter {
29
+ agent_id?: string;
30
+ project_id?: string;
31
+ status?: SessionJobStatus;
32
+ session_id?: string;
33
+ limit?: number;
34
+ offset?: number;
35
+ }
36
+ export interface UpdateSessionJobInput {
37
+ status?: SessionJobStatus;
38
+ chunk_count?: number;
39
+ memories_extracted?: number;
40
+ error?: string | null;
41
+ started_at?: string | null;
42
+ completed_at?: string | null;
43
+ }
44
+ export declare function createSessionJob(input: CreateSessionJobInput, db?: Database): SessionMemoryJob;
45
+ export declare function getSessionJob(id: string, db?: Database): SessionMemoryJob | null;
46
+ export declare function listSessionJobs(filter?: SessionJobFilter, db?: Database): SessionMemoryJob[];
47
+ export declare function updateSessionJob(id: string, updates: UpdateSessionJobInput, db?: Database): SessionMemoryJob | null;
48
+ export declare function getNextPendingJob(db?: Database): SessionMemoryJob | null;
49
+ //# sourceMappingURL=session-jobs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-jobs.d.ts","sourceRoot":"","sources":["../../src/db/session-jobs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAyB,MAAM,YAAY,CAAC;AAG7D,MAAM,MAAM,gBAAgB,GAAG,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,eAAe,CAAC;AACpF,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;AAEjF,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AA6BD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,qBAAqB,EAC5B,EAAE,CAAC,EAAE,QAAQ,GACZ,gBAAgB,CAwBlB;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,gBAAgB,GAAG,IAAI,CAOhF;AAED,wBAAgB,eAAe,CAC7B,MAAM,CAAC,EAAE,gBAAgB,EACzB,EAAE,CAAC,EAAE,QAAQ,GACZ,gBAAgB,EAAE,CAiCpB;AAED,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,qBAAqB,EAC9B,EAAE,CAAC,EAAE,QAAQ,GACZ,gBAAgB,GAAG,IAAI,CAwCzB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,gBAAgB,GAAG,IAAI,CASxE"}
package/dist/index.js CHANGED
@@ -434,7 +434,30 @@ var MIGRATIONS = [
434
434
  CREATE INDEX IF NOT EXISTS idx_webhook_hooks_type ON webhook_hooks(type);
435
435
  CREATE INDEX IF NOT EXISTS idx_webhook_hooks_enabled ON webhook_hooks(enabled);
436
436
  INSERT OR IGNORE INTO _migrations (id) VALUES (10);
437
+ `,
437
438
  `
439
+ CREATE TABLE IF NOT EXISTS session_memory_jobs (
440
+ id TEXT PRIMARY KEY,
441
+ session_id TEXT NOT NULL,
442
+ agent_id TEXT,
443
+ project_id TEXT,
444
+ source TEXT NOT NULL DEFAULT 'manual' CHECK(source IN ('claude-code','codex','manual','open-sessions')),
445
+ status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','processing','completed','failed')),
446
+ transcript TEXT NOT NULL,
447
+ chunk_count INTEGER NOT NULL DEFAULT 0,
448
+ memories_extracted INTEGER NOT NULL DEFAULT 0,
449
+ error TEXT,
450
+ metadata TEXT NOT NULL DEFAULT '{}',
451
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
452
+ started_at TEXT,
453
+ completed_at TEXT
454
+ );
455
+ CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_status ON session_memory_jobs(status);
456
+ CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_agent ON session_memory_jobs(agent_id);
457
+ CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_project ON session_memory_jobs(project_id);
458
+ CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_session ON session_memory_jobs(session_id);
459
+ INSERT OR IGNORE INTO _migrations (id) VALUES (13);
460
+ `
438
461
  ];
439
462
  var _db = null;
440
463
  function getDatabase(dbPath) {
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Open-sessions connector — bridge between open-sessions REST API and mementos.
3
+ *
4
+ * When open-sessions ingests a new session, this connector fetches the session
5
+ * transcript and submits it to mementos for memory extraction.
6
+ *
7
+ * Usage:
8
+ * const connector = new OpenSessionsConnector({ openSessionsUrl, mementosUrl });
9
+ * await connector.ingestSession(sessionId, { agentId, projectId });
10
+ * await connector.syncRecentSessions({ since: "2026-03-17", limit: 10 });
11
+ */
12
+ export interface OpenSessionsConnectorConfig {
13
+ /** open-sessions REST API base URL */
14
+ openSessionsUrl: string;
15
+ /** mementos REST API base URL */
16
+ mementosUrl: string;
17
+ /** Optional auth token for open-sessions */
18
+ openSessionsToken?: string;
19
+ /** Default agent ID for extracted memories */
20
+ defaultAgentId?: string;
21
+ /** Default project ID for extracted memories */
22
+ defaultProjectId?: string;
23
+ }
24
+ export interface IngestResult {
25
+ sessionId: string;
26
+ jobId: string;
27
+ status: "queued" | "skipped" | "error";
28
+ message: string;
29
+ }
30
+ export declare class OpenSessionsConnector {
31
+ private config;
32
+ constructor(config: OpenSessionsConnectorConfig);
33
+ /**
34
+ * Fetch a session transcript from open-sessions and ingest into mementos.
35
+ */
36
+ ingestSession(sessionId: string, options?: {
37
+ agentId?: string;
38
+ projectId?: string;
39
+ }): Promise<IngestResult>;
40
+ /**
41
+ * Fetch and ingest multiple recent sessions.
42
+ */
43
+ syncRecentSessions(options?: {
44
+ since?: string;
45
+ limit?: number;
46
+ agentId?: string;
47
+ projectId?: string;
48
+ }): Promise<IngestResult[]>;
49
+ private fetchSessionTranscript;
50
+ private listRecentSessionIds;
51
+ }
52
+ /**
53
+ * Create a connector from environment variables.
54
+ *
55
+ * OPEN_SESSIONS_URL — open-sessions server URL
56
+ * OPEN_SESSIONS_TOKEN — optional auth token
57
+ * MEMENTOS_URL — mementos server URL (default: http://localhost:19428)
58
+ */
59
+ export declare function connectorFromEnv(): OpenSessionsConnector;
60
+ //# sourceMappingURL=open-sessions-connector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-sessions-connector.d.ts","sourceRoot":"","sources":["../../src/lib/open-sessions-connector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,2BAA2B;IAC1C,sCAAsC;IACtC,eAAe,EAAE,MAAM,CAAC;IACxB,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8CAA8C;IAC9C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gDAAgD;IAChD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,qBAAqB;IAChC,OAAO,CAAC,MAAM,CAA8B;gBAEhC,MAAM,EAAE,2BAA2B;IAI/C;;OAEG;IACG,aAAa,CACjB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GACrD,OAAO,CAAC,YAAY,CAAC;IAwCxB;;OAEG;IACG,kBAAkB,CAAC,OAAO,GAAE;QAChC,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;KACf,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;YAelB,sBAAsB;YAiBtB,oBAAoB;CAqBnC;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,IAAI,qBAAqB,CAWxD"}