@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 +508 -1
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/session-jobs.d.ts +49 -0
- package/dist/db/session-jobs.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/lib/open-sessions-connector.d.ts +60 -0
- package/dist/lib/open-sessions-connector.d.ts.map +1 -0
- package/dist/lib/session-auto-resolve.d.ts +28 -0
- package/dist/lib/session-auto-resolve.d.ts.map +1 -0
- package/dist/lib/session-processor.d.ts +38 -0
- package/dist/lib/session-processor.d.ts.map +1 -0
- package/dist/lib/session-queue.d.ts +28 -0
- package/dist/lib/session-queue.d.ts.map +1 -0
- package/dist/mcp/index.js +413 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +518 -1
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -490,7 +490,30 @@ var init_database = __esm(() => {
|
|
|
490
490
|
CREATE INDEX IF NOT EXISTS idx_webhook_hooks_type ON webhook_hooks(type);
|
|
491
491
|
CREATE INDEX IF NOT EXISTS idx_webhook_hooks_enabled ON webhook_hooks(enabled);
|
|
492
492
|
INSERT OR IGNORE INTO _migrations (id) VALUES (10);
|
|
493
|
-
|
|
493
|
+
`,
|
|
494
|
+
`
|
|
495
|
+
CREATE TABLE IF NOT EXISTS session_memory_jobs (
|
|
496
|
+
id TEXT PRIMARY KEY,
|
|
497
|
+
session_id TEXT NOT NULL,
|
|
498
|
+
agent_id TEXT,
|
|
499
|
+
project_id TEXT,
|
|
500
|
+
source TEXT NOT NULL DEFAULT 'manual' CHECK(source IN ('claude-code','codex','manual','open-sessions')),
|
|
501
|
+
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','processing','completed','failed')),
|
|
502
|
+
transcript TEXT NOT NULL,
|
|
503
|
+
chunk_count INTEGER NOT NULL DEFAULT 0,
|
|
504
|
+
memories_extracted INTEGER NOT NULL DEFAULT 0,
|
|
505
|
+
error TEXT,
|
|
506
|
+
metadata TEXT NOT NULL DEFAULT '{}',
|
|
507
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
508
|
+
started_at TEXT,
|
|
509
|
+
completed_at TEXT
|
|
510
|
+
);
|
|
511
|
+
CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_status ON session_memory_jobs(status);
|
|
512
|
+
CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_agent ON session_memory_jobs(agent_id);
|
|
513
|
+
CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_project ON session_memory_jobs(project_id);
|
|
514
|
+
CREATE INDEX IF NOT EXISTS idx_session_memory_jobs_session ON session_memory_jobs(session_id);
|
|
515
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (13);
|
|
516
|
+
`
|
|
494
517
|
];
|
|
495
518
|
});
|
|
496
519
|
|
|
@@ -4499,6 +4522,457 @@ function getSynthesisStatus(runId, projectId, db) {
|
|
|
4499
4522
|
|
|
4500
4523
|
// src/server/index.ts
|
|
4501
4524
|
init_synthesis();
|
|
4525
|
+
|
|
4526
|
+
// src/db/session-jobs.ts
|
|
4527
|
+
init_database();
|
|
4528
|
+
function parseJobRow(row) {
|
|
4529
|
+
return {
|
|
4530
|
+
id: row["id"],
|
|
4531
|
+
session_id: row["session_id"],
|
|
4532
|
+
agent_id: row["agent_id"] || null,
|
|
4533
|
+
project_id: row["project_id"] || null,
|
|
4534
|
+
source: row["source"],
|
|
4535
|
+
status: row["status"],
|
|
4536
|
+
transcript: row["transcript"],
|
|
4537
|
+
chunk_count: row["chunk_count"],
|
|
4538
|
+
memories_extracted: row["memories_extracted"],
|
|
4539
|
+
error: row["error"] || null,
|
|
4540
|
+
metadata: JSON.parse(row["metadata"] || "{}"),
|
|
4541
|
+
created_at: row["created_at"],
|
|
4542
|
+
started_at: row["started_at"] || null,
|
|
4543
|
+
completed_at: row["completed_at"] || null
|
|
4544
|
+
};
|
|
4545
|
+
}
|
|
4546
|
+
function createSessionJob(input, db) {
|
|
4547
|
+
const d = db || getDatabase();
|
|
4548
|
+
const id = uuid();
|
|
4549
|
+
const timestamp = now();
|
|
4550
|
+
const source = input.source ?? "manual";
|
|
4551
|
+
const metadata = JSON.stringify(input.metadata ?? {});
|
|
4552
|
+
d.run(`INSERT INTO session_memory_jobs
|
|
4553
|
+
(id, session_id, agent_id, project_id, source, status, transcript, chunk_count, memories_extracted, metadata, created_at)
|
|
4554
|
+
VALUES (?, ?, ?, ?, ?, 'pending', ?, 0, 0, ?, ?)`, [
|
|
4555
|
+
id,
|
|
4556
|
+
input.session_id,
|
|
4557
|
+
input.agent_id ?? null,
|
|
4558
|
+
input.project_id ?? null,
|
|
4559
|
+
source,
|
|
4560
|
+
input.transcript,
|
|
4561
|
+
metadata,
|
|
4562
|
+
timestamp
|
|
4563
|
+
]);
|
|
4564
|
+
return getSessionJob(id, d);
|
|
4565
|
+
}
|
|
4566
|
+
function getSessionJob(id, db) {
|
|
4567
|
+
const d = db || getDatabase();
|
|
4568
|
+
const row = d.query("SELECT * FROM session_memory_jobs WHERE id = ?").get(id);
|
|
4569
|
+
if (!row)
|
|
4570
|
+
return null;
|
|
4571
|
+
return parseJobRow(row);
|
|
4572
|
+
}
|
|
4573
|
+
function listSessionJobs(filter, db) {
|
|
4574
|
+
const d = db || getDatabase();
|
|
4575
|
+
const conditions = [];
|
|
4576
|
+
const params = [];
|
|
4577
|
+
if (filter?.agent_id) {
|
|
4578
|
+
conditions.push("agent_id = ?");
|
|
4579
|
+
params.push(filter.agent_id);
|
|
4580
|
+
}
|
|
4581
|
+
if (filter?.project_id) {
|
|
4582
|
+
conditions.push("project_id = ?");
|
|
4583
|
+
params.push(filter.project_id);
|
|
4584
|
+
}
|
|
4585
|
+
if (filter?.status) {
|
|
4586
|
+
conditions.push("status = ?");
|
|
4587
|
+
params.push(filter.status);
|
|
4588
|
+
}
|
|
4589
|
+
if (filter?.session_id) {
|
|
4590
|
+
conditions.push("session_id = ?");
|
|
4591
|
+
params.push(filter.session_id);
|
|
4592
|
+
}
|
|
4593
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
4594
|
+
const limit = filter?.limit ?? 20;
|
|
4595
|
+
const offset = filter?.offset ?? 0;
|
|
4596
|
+
const rows = d.query(`SELECT * FROM session_memory_jobs ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`).all(...params, limit, offset);
|
|
4597
|
+
return rows.map(parseJobRow);
|
|
4598
|
+
}
|
|
4599
|
+
function updateSessionJob(id, updates, db) {
|
|
4600
|
+
const d = db || getDatabase();
|
|
4601
|
+
const setClauses = [];
|
|
4602
|
+
const params = [];
|
|
4603
|
+
if (updates.status !== undefined) {
|
|
4604
|
+
setClauses.push("status = ?");
|
|
4605
|
+
params.push(updates.status);
|
|
4606
|
+
}
|
|
4607
|
+
if (updates.chunk_count !== undefined) {
|
|
4608
|
+
setClauses.push("chunk_count = ?");
|
|
4609
|
+
params.push(updates.chunk_count);
|
|
4610
|
+
}
|
|
4611
|
+
if (updates.memories_extracted !== undefined) {
|
|
4612
|
+
setClauses.push("memories_extracted = ?");
|
|
4613
|
+
params.push(updates.memories_extracted);
|
|
4614
|
+
}
|
|
4615
|
+
if ("error" in updates) {
|
|
4616
|
+
setClauses.push("error = ?");
|
|
4617
|
+
params.push(updates.error ?? null);
|
|
4618
|
+
}
|
|
4619
|
+
if ("started_at" in updates) {
|
|
4620
|
+
setClauses.push("started_at = ?");
|
|
4621
|
+
params.push(updates.started_at ?? null);
|
|
4622
|
+
}
|
|
4623
|
+
if ("completed_at" in updates) {
|
|
4624
|
+
setClauses.push("completed_at = ?");
|
|
4625
|
+
params.push(updates.completed_at ?? null);
|
|
4626
|
+
}
|
|
4627
|
+
if (setClauses.length === 0)
|
|
4628
|
+
return getSessionJob(id, d);
|
|
4629
|
+
params.push(id);
|
|
4630
|
+
d.run(`UPDATE session_memory_jobs SET ${setClauses.join(", ")} WHERE id = ?`, params);
|
|
4631
|
+
return getSessionJob(id, d);
|
|
4632
|
+
}
|
|
4633
|
+
function getNextPendingJob(db) {
|
|
4634
|
+
const d = db || getDatabase();
|
|
4635
|
+
const row = d.query("SELECT * FROM session_memory_jobs WHERE status = 'pending' ORDER BY created_at ASC LIMIT 1").get();
|
|
4636
|
+
if (!row)
|
|
4637
|
+
return null;
|
|
4638
|
+
return parseJobRow(row);
|
|
4639
|
+
}
|
|
4640
|
+
|
|
4641
|
+
// src/lib/session-queue.ts
|
|
4642
|
+
init_database();
|
|
4643
|
+
|
|
4644
|
+
// src/lib/session-processor.ts
|
|
4645
|
+
init_memories();
|
|
4646
|
+
init_registry();
|
|
4647
|
+
var SESSION_EXTRACTION_USER_TEMPLATE = (chunk, sessionId) => `Extract memories from this session chunk (session: ${sessionId}):
|
|
4648
|
+
|
|
4649
|
+
${chunk}
|
|
4650
|
+
|
|
4651
|
+
Return JSON array: [{"key": "...", "value": "...", "category": "knowledge|fact|preference|history", "importance": 1-10, "tags": [...]}]`;
|
|
4652
|
+
function chunkTranscript(transcript, chunkSize = 2000, overlap = 200) {
|
|
4653
|
+
if (!transcript || transcript.length === 0)
|
|
4654
|
+
return [];
|
|
4655
|
+
if (transcript.length <= chunkSize)
|
|
4656
|
+
return [transcript];
|
|
4657
|
+
const chunks = [];
|
|
4658
|
+
let start = 0;
|
|
4659
|
+
while (start < transcript.length) {
|
|
4660
|
+
const end = Math.min(start + chunkSize, transcript.length);
|
|
4661
|
+
chunks.push(transcript.slice(start, end));
|
|
4662
|
+
if (end === transcript.length)
|
|
4663
|
+
break;
|
|
4664
|
+
start += chunkSize - overlap;
|
|
4665
|
+
}
|
|
4666
|
+
return chunks;
|
|
4667
|
+
}
|
|
4668
|
+
async function extractMemoriesFromChunk(chunk, context, db) {
|
|
4669
|
+
const provider = providerRegistry.getAvailable();
|
|
4670
|
+
if (!provider)
|
|
4671
|
+
return 0;
|
|
4672
|
+
try {
|
|
4673
|
+
const extracted = await provider.extractMemories(SESSION_EXTRACTION_USER_TEMPLATE(chunk, context.sessionId), {
|
|
4674
|
+
sessionId: context.sessionId,
|
|
4675
|
+
agentId: context.agentId,
|
|
4676
|
+
projectId: context.projectId
|
|
4677
|
+
});
|
|
4678
|
+
let savedCount = 0;
|
|
4679
|
+
const sourceTag = context.source ? `source:${context.source}` : "source:manual";
|
|
4680
|
+
for (const memory of extracted) {
|
|
4681
|
+
if (!memory.content || !memory.content.trim())
|
|
4682
|
+
continue;
|
|
4683
|
+
try {
|
|
4684
|
+
createMemory({
|
|
4685
|
+
key: memory.content.slice(0, 120).replace(/\s+/g, "-").toLowerCase(),
|
|
4686
|
+
value: memory.content,
|
|
4687
|
+
category: memory.category,
|
|
4688
|
+
scope: memory.suggestedScope ?? "shared",
|
|
4689
|
+
importance: memory.importance,
|
|
4690
|
+
tags: [
|
|
4691
|
+
...memory.tags,
|
|
4692
|
+
"session-extracted",
|
|
4693
|
+
sourceTag,
|
|
4694
|
+
`session:${context.sessionId}`
|
|
4695
|
+
],
|
|
4696
|
+
source: "auto",
|
|
4697
|
+
agent_id: context.agentId,
|
|
4698
|
+
project_id: context.projectId,
|
|
4699
|
+
session_id: context.sessionId,
|
|
4700
|
+
metadata: {
|
|
4701
|
+
auto_extracted: true,
|
|
4702
|
+
session_source: context.source ?? "manual",
|
|
4703
|
+
extracted_at: new Date().toISOString(),
|
|
4704
|
+
reasoning: memory.reasoning
|
|
4705
|
+
}
|
|
4706
|
+
}, "merge", db);
|
|
4707
|
+
savedCount++;
|
|
4708
|
+
} catch {}
|
|
4709
|
+
}
|
|
4710
|
+
return savedCount;
|
|
4711
|
+
} catch {
|
|
4712
|
+
try {
|
|
4713
|
+
const fallbacks = providerRegistry.getFallbacks();
|
|
4714
|
+
for (const fallback of fallbacks) {
|
|
4715
|
+
try {
|
|
4716
|
+
const extracted = await fallback.extractMemories(SESSION_EXTRACTION_USER_TEMPLATE(chunk, context.sessionId), {
|
|
4717
|
+
sessionId: context.sessionId,
|
|
4718
|
+
agentId: context.agentId,
|
|
4719
|
+
projectId: context.projectId
|
|
4720
|
+
});
|
|
4721
|
+
let savedCount = 0;
|
|
4722
|
+
const sourceTag = context.source ? `source:${context.source}` : "source:manual";
|
|
4723
|
+
for (const memory of extracted) {
|
|
4724
|
+
if (!memory.content || !memory.content.trim())
|
|
4725
|
+
continue;
|
|
4726
|
+
try {
|
|
4727
|
+
createMemory({
|
|
4728
|
+
key: memory.content.slice(0, 120).replace(/\s+/g, "-").toLowerCase(),
|
|
4729
|
+
value: memory.content,
|
|
4730
|
+
category: memory.category,
|
|
4731
|
+
scope: memory.suggestedScope ?? "shared",
|
|
4732
|
+
importance: memory.importance,
|
|
4733
|
+
tags: [
|
|
4734
|
+
...memory.tags,
|
|
4735
|
+
"session-extracted",
|
|
4736
|
+
sourceTag,
|
|
4737
|
+
`session:${context.sessionId}`
|
|
4738
|
+
],
|
|
4739
|
+
source: "auto",
|
|
4740
|
+
agent_id: context.agentId,
|
|
4741
|
+
project_id: context.projectId,
|
|
4742
|
+
session_id: context.sessionId,
|
|
4743
|
+
metadata: {
|
|
4744
|
+
auto_extracted: true,
|
|
4745
|
+
session_source: context.source ?? "manual",
|
|
4746
|
+
extracted_at: new Date().toISOString(),
|
|
4747
|
+
reasoning: memory.reasoning
|
|
4748
|
+
}
|
|
4749
|
+
}, "merge", db);
|
|
4750
|
+
savedCount++;
|
|
4751
|
+
} catch {}
|
|
4752
|
+
}
|
|
4753
|
+
if (savedCount > 0)
|
|
4754
|
+
return savedCount;
|
|
4755
|
+
} catch {
|
|
4756
|
+
continue;
|
|
4757
|
+
}
|
|
4758
|
+
}
|
|
4759
|
+
} catch {}
|
|
4760
|
+
return 0;
|
|
4761
|
+
}
|
|
4762
|
+
}
|
|
4763
|
+
async function processSessionJob(jobId, db) {
|
|
4764
|
+
const result = {
|
|
4765
|
+
jobId,
|
|
4766
|
+
chunksProcessed: 0,
|
|
4767
|
+
memoriesExtracted: 0,
|
|
4768
|
+
errors: []
|
|
4769
|
+
};
|
|
4770
|
+
let job;
|
|
4771
|
+
try {
|
|
4772
|
+
job = getSessionJob(jobId, db);
|
|
4773
|
+
if (!job) {
|
|
4774
|
+
result.errors.push(`Job not found: ${jobId}`);
|
|
4775
|
+
return result;
|
|
4776
|
+
}
|
|
4777
|
+
} catch (e) {
|
|
4778
|
+
result.errors.push(`Failed to fetch job: ${String(e)}`);
|
|
4779
|
+
return result;
|
|
4780
|
+
}
|
|
4781
|
+
try {
|
|
4782
|
+
updateSessionJob(jobId, { status: "processing", started_at: new Date().toISOString() }, db);
|
|
4783
|
+
} catch (e) {
|
|
4784
|
+
result.errors.push(`Failed to mark job as processing: ${String(e)}`);
|
|
4785
|
+
return result;
|
|
4786
|
+
}
|
|
4787
|
+
const chunks = chunkTranscript(job.transcript);
|
|
4788
|
+
try {
|
|
4789
|
+
updateSessionJob(jobId, { chunk_count: chunks.length }, db);
|
|
4790
|
+
} catch {}
|
|
4791
|
+
let totalMemories = 0;
|
|
4792
|
+
for (let i = 0;i < chunks.length; i++) {
|
|
4793
|
+
const chunk = chunks[i];
|
|
4794
|
+
try {
|
|
4795
|
+
const count = await extractMemoriesFromChunk(chunk, {
|
|
4796
|
+
sessionId: job.session_id,
|
|
4797
|
+
agentId: job.agent_id ?? undefined,
|
|
4798
|
+
projectId: job.project_id ?? undefined,
|
|
4799
|
+
source: job.source
|
|
4800
|
+
}, db);
|
|
4801
|
+
totalMemories += count;
|
|
4802
|
+
result.chunksProcessed++;
|
|
4803
|
+
} catch (e) {
|
|
4804
|
+
result.errors.push(`Chunk ${i} failed: ${String(e)}`);
|
|
4805
|
+
}
|
|
4806
|
+
}
|
|
4807
|
+
result.memoriesExtracted = totalMemories;
|
|
4808
|
+
try {
|
|
4809
|
+
if (result.errors.length > 0 && result.chunksProcessed === 0) {
|
|
4810
|
+
updateSessionJob(jobId, {
|
|
4811
|
+
status: "failed",
|
|
4812
|
+
error: result.errors.join("; "),
|
|
4813
|
+
completed_at: new Date().toISOString(),
|
|
4814
|
+
memories_extracted: totalMemories,
|
|
4815
|
+
chunk_count: chunks.length
|
|
4816
|
+
}, db);
|
|
4817
|
+
} else {
|
|
4818
|
+
updateSessionJob(jobId, {
|
|
4819
|
+
status: "completed",
|
|
4820
|
+
completed_at: new Date().toISOString(),
|
|
4821
|
+
memories_extracted: totalMemories,
|
|
4822
|
+
chunk_count: chunks.length
|
|
4823
|
+
}, db);
|
|
4824
|
+
}
|
|
4825
|
+
} catch (e) {
|
|
4826
|
+
result.errors.push(`Failed to update job status: ${String(e)}`);
|
|
4827
|
+
}
|
|
4828
|
+
return result;
|
|
4829
|
+
}
|
|
4830
|
+
|
|
4831
|
+
// src/lib/session-queue.ts
|
|
4832
|
+
var _pendingQueue = new Set;
|
|
4833
|
+
var _isProcessing = false;
|
|
4834
|
+
var _workerStarted = false;
|
|
4835
|
+
function enqueueSessionJob(jobId) {
|
|
4836
|
+
_pendingQueue.add(jobId);
|
|
4837
|
+
if (!_isProcessing) {
|
|
4838
|
+
_processNext();
|
|
4839
|
+
}
|
|
4840
|
+
}
|
|
4841
|
+
function getSessionQueueStats() {
|
|
4842
|
+
try {
|
|
4843
|
+
const db = getDatabase();
|
|
4844
|
+
const rows = db.query("SELECT status, COUNT(*) as count FROM session_memory_jobs GROUP BY status").all();
|
|
4845
|
+
const stats = {
|
|
4846
|
+
pending: 0,
|
|
4847
|
+
processing: 0,
|
|
4848
|
+
completed: 0,
|
|
4849
|
+
failed: 0
|
|
4850
|
+
};
|
|
4851
|
+
for (const row of rows) {
|
|
4852
|
+
if (row.status === "pending")
|
|
4853
|
+
stats.pending = row.count;
|
|
4854
|
+
else if (row.status === "processing")
|
|
4855
|
+
stats.processing = row.count;
|
|
4856
|
+
else if (row.status === "completed")
|
|
4857
|
+
stats.completed = row.count;
|
|
4858
|
+
else if (row.status === "failed")
|
|
4859
|
+
stats.failed = row.count;
|
|
4860
|
+
}
|
|
4861
|
+
return stats;
|
|
4862
|
+
} catch {
|
|
4863
|
+
return {
|
|
4864
|
+
pending: _pendingQueue.size,
|
|
4865
|
+
processing: _isProcessing ? 1 : 0,
|
|
4866
|
+
completed: 0,
|
|
4867
|
+
failed: 0
|
|
4868
|
+
};
|
|
4869
|
+
}
|
|
4870
|
+
}
|
|
4871
|
+
function startSessionQueueWorker() {
|
|
4872
|
+
if (_workerStarted)
|
|
4873
|
+
return;
|
|
4874
|
+
_workerStarted = true;
|
|
4875
|
+
setInterval(() => {
|
|
4876
|
+
_processNext();
|
|
4877
|
+
}, 5000);
|
|
4878
|
+
}
|
|
4879
|
+
async function _processNext() {
|
|
4880
|
+
if (_isProcessing)
|
|
4881
|
+
return;
|
|
4882
|
+
let jobId;
|
|
4883
|
+
if (_pendingQueue.size > 0) {
|
|
4884
|
+
jobId = [..._pendingQueue][0];
|
|
4885
|
+
_pendingQueue.delete(jobId);
|
|
4886
|
+
} else {
|
|
4887
|
+
try {
|
|
4888
|
+
const job = getNextPendingJob();
|
|
4889
|
+
if (job)
|
|
4890
|
+
jobId = job.id;
|
|
4891
|
+
} catch {
|
|
4892
|
+
return;
|
|
4893
|
+
}
|
|
4894
|
+
}
|
|
4895
|
+
if (!jobId)
|
|
4896
|
+
return;
|
|
4897
|
+
_isProcessing = true;
|
|
4898
|
+
try {
|
|
4899
|
+
await processSessionJob(jobId);
|
|
4900
|
+
} catch {} finally {
|
|
4901
|
+
_isProcessing = false;
|
|
4902
|
+
if (_pendingQueue.size > 0) {
|
|
4903
|
+
_processNext();
|
|
4904
|
+
}
|
|
4905
|
+
}
|
|
4906
|
+
}
|
|
4907
|
+
|
|
4908
|
+
// src/lib/session-auto-resolve.ts
|
|
4909
|
+
function autoResolveAgentProject(metadata, db) {
|
|
4910
|
+
let agentId = null;
|
|
4911
|
+
let projectId = null;
|
|
4912
|
+
let confidence = "none";
|
|
4913
|
+
const methods = [];
|
|
4914
|
+
if (metadata.agentName) {
|
|
4915
|
+
try {
|
|
4916
|
+
const agent = getAgent(metadata.agentName, db);
|
|
4917
|
+
if (agent) {
|
|
4918
|
+
agentId = agent.id;
|
|
4919
|
+
confidence = "high";
|
|
4920
|
+
methods.push(`agent-by-name:${metadata.agentName}`);
|
|
4921
|
+
if (agent.active_project_id && !projectId) {
|
|
4922
|
+
projectId = agent.active_project_id;
|
|
4923
|
+
methods.push("project-from-agent-active");
|
|
4924
|
+
}
|
|
4925
|
+
}
|
|
4926
|
+
} catch {}
|
|
4927
|
+
}
|
|
4928
|
+
if (metadata.workingDir && !projectId) {
|
|
4929
|
+
try {
|
|
4930
|
+
const project = getProject(metadata.workingDir, db);
|
|
4931
|
+
if (project) {
|
|
4932
|
+
projectId = project.id;
|
|
4933
|
+
if (confidence !== "high")
|
|
4934
|
+
confidence = "high";
|
|
4935
|
+
methods.push(`project-by-path:${metadata.workingDir}`);
|
|
4936
|
+
} else {
|
|
4937
|
+
const allProjects = listProjects(db);
|
|
4938
|
+
for (const p of allProjects) {
|
|
4939
|
+
if (p.path && metadata.workingDir.startsWith(p.path)) {
|
|
4940
|
+
projectId = p.id;
|
|
4941
|
+
if (confidence !== "high")
|
|
4942
|
+
confidence = "high";
|
|
4943
|
+
methods.push(`project-by-path-prefix:${p.path}`);
|
|
4944
|
+
break;
|
|
4945
|
+
}
|
|
4946
|
+
}
|
|
4947
|
+
}
|
|
4948
|
+
} catch {}
|
|
4949
|
+
}
|
|
4950
|
+
if (metadata.gitRemote && !projectId) {
|
|
4951
|
+
try {
|
|
4952
|
+
const repoName = metadata.gitRemote.replace(/\.git$/, "").split(/[/:]/).filter(Boolean).pop();
|
|
4953
|
+
if (repoName) {
|
|
4954
|
+
const project = getProject(repoName, db);
|
|
4955
|
+
if (project) {
|
|
4956
|
+
projectId = project.id;
|
|
4957
|
+
if (confidence === "none")
|
|
4958
|
+
confidence = "low";
|
|
4959
|
+
methods.push(`project-by-git-remote:${repoName}`);
|
|
4960
|
+
}
|
|
4961
|
+
}
|
|
4962
|
+
} catch {}
|
|
4963
|
+
}
|
|
4964
|
+
if (methods.length === 0) {
|
|
4965
|
+
confidence = "none";
|
|
4966
|
+
}
|
|
4967
|
+
return {
|
|
4968
|
+
agentId,
|
|
4969
|
+
projectId,
|
|
4970
|
+
confidence,
|
|
4971
|
+
method: methods.join(", ") || "none"
|
|
4972
|
+
};
|
|
4973
|
+
}
|
|
4974
|
+
|
|
4975
|
+
// src/server/index.ts
|
|
4502
4976
|
var DEFAULT_PORT = 19428;
|
|
4503
4977
|
function parsePort() {
|
|
4504
4978
|
const envPort = process.env["PORT"];
|
|
@@ -5600,8 +6074,51 @@ addRoute("POST", "/api/synthesis/rollback/:run_id", async (_req, _url, params) =
|
|
|
5600
6074
|
const result = await rollbackSynthesis(params["run_id"]);
|
|
5601
6075
|
return json(result);
|
|
5602
6076
|
});
|
|
6077
|
+
addRoute("POST", "/api/sessions/ingest", async (req) => {
|
|
6078
|
+
const body = await readJson(req) ?? {};
|
|
6079
|
+
const { transcript, session_id, agent_id, project_id, source, metadata } = body;
|
|
6080
|
+
if (!transcript || typeof transcript !== "string")
|
|
6081
|
+
return errorResponse("transcript is required", 400);
|
|
6082
|
+
if (!session_id || typeof session_id !== "string")
|
|
6083
|
+
return errorResponse("session_id is required", 400);
|
|
6084
|
+
let resolvedAgentId = agent_id;
|
|
6085
|
+
let resolvedProjectId = project_id;
|
|
6086
|
+
if (!resolvedAgentId || !resolvedProjectId) {
|
|
6087
|
+
const resolved = autoResolveAgentProject(metadata ?? {});
|
|
6088
|
+
if (!resolvedAgentId && resolved.agentId)
|
|
6089
|
+
resolvedAgentId = resolved.agentId;
|
|
6090
|
+
if (!resolvedProjectId && resolved.projectId)
|
|
6091
|
+
resolvedProjectId = resolved.projectId;
|
|
6092
|
+
}
|
|
6093
|
+
const job = createSessionJob({
|
|
6094
|
+
session_id,
|
|
6095
|
+
transcript,
|
|
6096
|
+
source: source ?? "manual",
|
|
6097
|
+
agent_id: resolvedAgentId,
|
|
6098
|
+
project_id: resolvedProjectId,
|
|
6099
|
+
metadata: metadata ?? {}
|
|
6100
|
+
});
|
|
6101
|
+
enqueueSessionJob(job.id);
|
|
6102
|
+
return json({ job_id: job.id, status: "queued", message: "Session queued for memory extraction" }, 202);
|
|
6103
|
+
});
|
|
6104
|
+
addRoute("GET", "/api/sessions/jobs", (_req, url) => {
|
|
6105
|
+
const agentId = url.searchParams.get("agent_id") ?? undefined;
|
|
6106
|
+
const projectId = url.searchParams.get("project_id") ?? undefined;
|
|
6107
|
+
const status = url.searchParams.get("status") ?? undefined;
|
|
6108
|
+
const limit = url.searchParams.get("limit") ? parseInt(url.searchParams.get("limit")) : 20;
|
|
6109
|
+
const jobs = listSessionJobs({ agent_id: agentId, project_id: projectId, status, limit });
|
|
6110
|
+
return json({ jobs, count: jobs.length });
|
|
6111
|
+
});
|
|
6112
|
+
addRoute("GET", "/api/sessions/jobs/:id", (_req, _url, params) => {
|
|
6113
|
+
const job = getSessionJob(params["id"]);
|
|
6114
|
+
if (!job)
|
|
6115
|
+
return errorResponse("Session job not found", 404);
|
|
6116
|
+
return json(job);
|
|
6117
|
+
});
|
|
6118
|
+
addRoute("GET", "/api/sessions/queue/stats", () => json(getSessionQueueStats()));
|
|
5603
6119
|
function startServer(port) {
|
|
5604
6120
|
loadWebhooksFromDb();
|
|
6121
|
+
startSessionQueueWorker();
|
|
5605
6122
|
const hostname = process.env["MEMENTOS_HOST"] ?? "127.0.0.1";
|
|
5606
6123
|
Bun.serve({
|
|
5607
6124
|
port,
|