@hasna/mementos 0.1.1 → 0.2.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/dashboard/dist/assets/{index-C5oRjtKB.js → index-DqyMbv89.js} +20 -20
- package/dashboard/dist/assets/index-UBCddFo_.css +1 -0
- package/dashboard/dist/index.html +2 -2
- package/dist/cli/index.js +727 -105
- package/dist/db/agents.d.ts +6 -0
- package/dist/db/agents.d.ts.map +1 -1
- package/dist/db/memories.d.ts +1 -0
- package/dist/db/memories.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -0
- package/dist/lib/poll.d.ts +17 -0
- package/dist/lib/poll.d.ts.map +1 -0
- package/dist/lib/project-detect.d.ts +18 -0
- package/dist/lib/project-detect.d.ts.map +1 -0
- package/dist/mcp/index.js +291 -17
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +36 -0
- package/package.json +1 -1
- package/dashboard/dist/assets/index-C8vbNL_5.css +0 -1
package/dist/db/agents.d.ts
CHANGED
|
@@ -3,4 +3,10 @@ import type { Agent } from "../types/index.js";
|
|
|
3
3
|
export declare function registerAgent(name: string, description?: string, role?: string, db?: Database): Agent;
|
|
4
4
|
export declare function getAgent(idOrName: string, db?: Database): Agent | null;
|
|
5
5
|
export declare function listAgents(db?: Database): Agent[];
|
|
6
|
+
export declare function updateAgent(id: string, updates: {
|
|
7
|
+
name?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
role?: string;
|
|
10
|
+
metadata?: Record<string, unknown>;
|
|
11
|
+
}, db?: Database): Agent | null;
|
|
6
12
|
//# sourceMappingURL=agents.d.ts.map
|
package/dist/db/agents.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/db/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAe/C,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,EACb,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,CAqCP;AAED,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,MAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,GAAG,IAAI,CAsBd;AAED,wBAAgB,UAAU,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAMjD"}
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/db/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAe/C,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,EACb,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,CAqCP;AAED,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,MAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,GAAG,IAAI,CAsBd;AAED,wBAAgB,UAAU,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAMjD;AAED,wBAAgB,WAAW,CACzB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EACnG,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,GAAG,IAAI,CAkCd"}
|
package/dist/db/memories.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Database } from "bun:sqlite";
|
|
2
2
|
import type { CreateMemoryInput, DedupeMode, Memory, MemoryFilter, UpdateMemoryInput } from "../types/index.js";
|
|
3
|
+
export declare function parseMemoryRow(row: Record<string, unknown>): Memory;
|
|
3
4
|
export declare function createMemory(input: CreateMemoryInput, dedupeMode?: DedupeMode, db?: Database): Memory;
|
|
4
5
|
export declare function getMemory(id: string, db?: Database): Memory | null;
|
|
5
6
|
export declare function getMemoryByKey(key: string, scope?: string, agentId?: string, projectId?: string, sessionId?: string, db?: Database): Memory | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memories.d.ts","sourceRoot":"","sources":["../../src/db/memories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAyB,MAAM,YAAY,CAAC;AAC7D,OAAO,KAAK,EACV,iBAAiB,EACjB,UAAU,EACV,MAAM,EACN,YAAY,EACZ,iBAAiB,EAClB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"memories.d.ts","sourceRoot":"","sources":["../../src/db/memories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAyB,MAAM,YAAY,CAAC;AAC7D,OAAO,KAAK,EACV,iBAAiB,EACjB,UAAU,EACV,MAAM,EACN,YAAY,EACZ,iBAAiB,EAClB,MAAM,mBAAmB,CAAC;AAW3B,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAwBnE;AAMD,wBAAgB,YAAY,CAC1B,KAAK,EAAE,iBAAiB,EACxB,UAAU,GAAE,UAAoB,EAChC,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,CAoGR;AAMD,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAOlE;AAED,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,GAAG,IAAI,CA4Bf;AAMD,wBAAgB,YAAY,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,EAAE,CA4G3E;AAMD,wBAAgB,YAAY,CAC1B,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,iBAAiB,EACxB,EAAE,CAAC,EAAE,QAAQ,GACZ,MAAM,CAiER;AAMD,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAI/D;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAUvE;AAMD,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,CAM3D;AAMD,wBAAgB,oBAAoB,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAQ1D"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export type { Memory, MemoryWithRelations, MemoryScope, MemoryCategory, MemorySo
|
|
|
2
2
|
export { MemoryNotFoundError, DuplicateMemoryError, MemoryExpiredError, InvalidScopeError, VersionConflictError, } from "./types/index.js";
|
|
3
3
|
export { getDatabase, closeDatabase, resetDatabase, getDbPath, resolvePartialId, now, uuid, shortUuid, } from "./db/database.js";
|
|
4
4
|
export { createMemory, getMemory, getMemoryByKey, listMemories, updateMemory, deleteMemory, bulkDeleteMemories, touchMemory, cleanExpiredMemories, } from "./db/memories.js";
|
|
5
|
-
export { registerAgent, getAgent, listAgents, } from "./db/agents.js";
|
|
5
|
+
export { registerAgent, getAgent, listAgents, updateAgent, } from "./db/agents.js";
|
|
6
6
|
export { registerProject, getProject, listProjects, } from "./db/projects.js";
|
|
7
7
|
export { searchMemories } from "./lib/search.js";
|
|
8
8
|
export { loadConfig, DEFAULT_CONFIG } from "./lib/config.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EACV,MAAM,EACN,mBAAmB,EACnB,WAAW,EACX,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EACV,KAAK,EACL,OAAO,EACP,cAAc,EACd,aAAa,EACb,WAAW,EACX,UAAU,EACV,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,WAAW,EACX,aAAa,EACb,aAAa,EACb,SAAS,EACT,gBAAgB,EAChB,GAAG,EACH,IAAI,EACJ,SAAS,GACV,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,YAAY,EACZ,SAAS,EACT,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,aAAa,EACb,QAAQ,EACR,UAAU,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EACV,MAAM,EACN,mBAAmB,EACnB,WAAW,EACX,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EACV,KAAK,EACL,OAAO,EACP,cAAc,EACd,aAAa,EACb,WAAW,EACX,UAAU,EACV,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,WAAW,EACX,aAAa,EACb,aAAa,EACb,SAAS,EACT,gBAAgB,EAChB,GAAG,EACH,IAAI,EACJ,SAAS,GACV,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,YAAY,EACZ,SAAS,EACT,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,aAAa,EACb,QAAQ,EACR,UAAU,EACV,WAAW,GACZ,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,eAAe,EACf,UAAU,EACV,YAAY,GACb,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGjD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAG7D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAG1D,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAG7E,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -602,6 +602,31 @@ function listAgents(db) {
|
|
|
602
602
|
const rows = d.query("SELECT * FROM agents ORDER BY last_seen_at DESC").all();
|
|
603
603
|
return rows.map(parseAgentRow);
|
|
604
604
|
}
|
|
605
|
+
function updateAgent(id, updates, db) {
|
|
606
|
+
const d = db || getDatabase();
|
|
607
|
+
const agent = getAgent(id, d);
|
|
608
|
+
if (!agent)
|
|
609
|
+
return null;
|
|
610
|
+
const timestamp = now();
|
|
611
|
+
if (updates.name && updates.name !== agent.name) {
|
|
612
|
+
const existing = d.query("SELECT id FROM agents WHERE name = ? AND id != ?").get(updates.name, agent.id);
|
|
613
|
+
if (existing) {
|
|
614
|
+
throw new Error(`Agent name already taken: ${updates.name}`);
|
|
615
|
+
}
|
|
616
|
+
d.run("UPDATE agents SET name = ? WHERE id = ?", [updates.name, agent.id]);
|
|
617
|
+
}
|
|
618
|
+
if (updates.description !== undefined) {
|
|
619
|
+
d.run("UPDATE agents SET description = ? WHERE id = ?", [updates.description, agent.id]);
|
|
620
|
+
}
|
|
621
|
+
if (updates.role !== undefined) {
|
|
622
|
+
d.run("UPDATE agents SET role = ? WHERE id = ?", [updates.role, agent.id]);
|
|
623
|
+
}
|
|
624
|
+
if (updates.metadata !== undefined) {
|
|
625
|
+
d.run("UPDATE agents SET metadata = ? WHERE id = ?", [JSON.stringify(updates.metadata), agent.id]);
|
|
626
|
+
}
|
|
627
|
+
d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [timestamp, agent.id]);
|
|
628
|
+
return getAgent(agent.id, d);
|
|
629
|
+
}
|
|
605
630
|
// src/db/projects.ts
|
|
606
631
|
function parseProjectRow(row) {
|
|
607
632
|
return {
|
|
@@ -1136,6 +1161,7 @@ var defaultSyncAgents = ["claude", "codex", "gemini"];
|
|
|
1136
1161
|
export {
|
|
1137
1162
|
uuid,
|
|
1138
1163
|
updateMemory,
|
|
1164
|
+
updateAgent,
|
|
1139
1165
|
touchMemory,
|
|
1140
1166
|
syncMemories,
|
|
1141
1167
|
shortUuid,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
import type { Memory, MemoryScope, MemoryCategory } from "../types/index.js";
|
|
3
|
+
export interface PollOptions {
|
|
4
|
+
interval_ms?: number;
|
|
5
|
+
scope?: MemoryScope;
|
|
6
|
+
category?: MemoryCategory;
|
|
7
|
+
agent_id?: string;
|
|
8
|
+
project_id?: string;
|
|
9
|
+
on_memories: (memories: Memory[]) => void;
|
|
10
|
+
on_error?: (error: Error) => void;
|
|
11
|
+
db?: Database;
|
|
12
|
+
}
|
|
13
|
+
export interface PollHandle {
|
|
14
|
+
stop: () => void;
|
|
15
|
+
}
|
|
16
|
+
export declare function startPolling(opts: PollOptions): PollHandle;
|
|
17
|
+
//# sourceMappingURL=poll.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poll.d.ts","sourceRoot":"","sources":["../../src/lib/poll.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAQ7E,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC1C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAClC,EAAE,CAAC,EAAE,QAAQ,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB;AAMD,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,GAAG,UAAU,CA0F1D"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import type { Project } from "../types/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Auto-detect the current project from cwd's git root.
|
|
5
|
+
* - Finds the git root directory
|
|
6
|
+
* - Extracts the repo name from the directory basename
|
|
7
|
+
* - Checks if a project is already registered by path
|
|
8
|
+
* - If not, registers it automatically
|
|
9
|
+
* - Returns the Project object, or null if not in a git repo
|
|
10
|
+
*
|
|
11
|
+
* Results are cached for the lifetime of the process.
|
|
12
|
+
*/
|
|
13
|
+
export declare function detectProject(db?: Database): Project | null;
|
|
14
|
+
/**
|
|
15
|
+
* Reset the cached project (useful for tests).
|
|
16
|
+
*/
|
|
17
|
+
export declare function resetProjectCache(): void;
|
|
18
|
+
//# sourceMappingURL=project-detect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-detect.d.ts","sourceRoot":"","sources":["../../src/lib/project-detect.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAkBjD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,IAAI,CA0B3D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
|
package/dist/mcp/index.js
CHANGED
|
@@ -4577,6 +4577,31 @@ function listAgents(db) {
|
|
|
4577
4577
|
const rows = d.query("SELECT * FROM agents ORDER BY last_seen_at DESC").all();
|
|
4578
4578
|
return rows.map(parseAgentRow);
|
|
4579
4579
|
}
|
|
4580
|
+
function updateAgent(id, updates, db) {
|
|
4581
|
+
const d = db || getDatabase();
|
|
4582
|
+
const agent = getAgent(id, d);
|
|
4583
|
+
if (!agent)
|
|
4584
|
+
return null;
|
|
4585
|
+
const timestamp = now();
|
|
4586
|
+
if (updates.name && updates.name !== agent.name) {
|
|
4587
|
+
const existing = d.query("SELECT id FROM agents WHERE name = ? AND id != ?").get(updates.name, agent.id);
|
|
4588
|
+
if (existing) {
|
|
4589
|
+
throw new Error(`Agent name already taken: ${updates.name}`);
|
|
4590
|
+
}
|
|
4591
|
+
d.run("UPDATE agents SET name = ? WHERE id = ?", [updates.name, agent.id]);
|
|
4592
|
+
}
|
|
4593
|
+
if (updates.description !== undefined) {
|
|
4594
|
+
d.run("UPDATE agents SET description = ? WHERE id = ?", [updates.description, agent.id]);
|
|
4595
|
+
}
|
|
4596
|
+
if (updates.role !== undefined) {
|
|
4597
|
+
d.run("UPDATE agents SET role = ? WHERE id = ?", [updates.role, agent.id]);
|
|
4598
|
+
}
|
|
4599
|
+
if (updates.metadata !== undefined) {
|
|
4600
|
+
d.run("UPDATE agents SET metadata = ? WHERE id = ?", [JSON.stringify(updates.metadata), agent.id]);
|
|
4601
|
+
}
|
|
4602
|
+
d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [timestamp, agent.id]);
|
|
4603
|
+
return getAgent(agent.id, d);
|
|
4604
|
+
}
|
|
4580
4605
|
|
|
4581
4606
|
// src/db/projects.ts
|
|
4582
4607
|
function parseProjectRow(row) {
|
|
@@ -4622,11 +4647,211 @@ function listProjects(db) {
|
|
|
4622
4647
|
return rows.map(parseProjectRow);
|
|
4623
4648
|
}
|
|
4624
4649
|
|
|
4650
|
+
// src/lib/search.ts
|
|
4651
|
+
function parseMemoryRow2(row) {
|
|
4652
|
+
return {
|
|
4653
|
+
id: row["id"],
|
|
4654
|
+
key: row["key"],
|
|
4655
|
+
value: row["value"],
|
|
4656
|
+
category: row["category"],
|
|
4657
|
+
scope: row["scope"],
|
|
4658
|
+
summary: row["summary"] || null,
|
|
4659
|
+
tags: JSON.parse(row["tags"] || "[]"),
|
|
4660
|
+
importance: row["importance"],
|
|
4661
|
+
source: row["source"],
|
|
4662
|
+
status: row["status"],
|
|
4663
|
+
pinned: !!row["pinned"],
|
|
4664
|
+
agent_id: row["agent_id"] || null,
|
|
4665
|
+
project_id: row["project_id"] || null,
|
|
4666
|
+
session_id: row["session_id"] || null,
|
|
4667
|
+
metadata: JSON.parse(row["metadata"] || "{}"),
|
|
4668
|
+
access_count: row["access_count"],
|
|
4669
|
+
version: row["version"],
|
|
4670
|
+
expires_at: row["expires_at"] || null,
|
|
4671
|
+
created_at: row["created_at"],
|
|
4672
|
+
updated_at: row["updated_at"],
|
|
4673
|
+
accessed_at: row["accessed_at"] || null
|
|
4674
|
+
};
|
|
4675
|
+
}
|
|
4676
|
+
function determineMatchType(memory, queryLower) {
|
|
4677
|
+
if (memory.key.toLowerCase() === queryLower)
|
|
4678
|
+
return "exact";
|
|
4679
|
+
if (memory.tags.some((t) => t.toLowerCase() === queryLower))
|
|
4680
|
+
return "tag";
|
|
4681
|
+
return "fuzzy";
|
|
4682
|
+
}
|
|
4683
|
+
function computeScore(memory, queryLower) {
|
|
4684
|
+
let score = 0;
|
|
4685
|
+
const keyLower = memory.key.toLowerCase();
|
|
4686
|
+
if (keyLower === queryLower) {
|
|
4687
|
+
score += 10;
|
|
4688
|
+
} else if (keyLower.includes(queryLower)) {
|
|
4689
|
+
score += 7;
|
|
4690
|
+
}
|
|
4691
|
+
if (memory.tags.some((t) => t.toLowerCase() === queryLower)) {
|
|
4692
|
+
score += 6;
|
|
4693
|
+
}
|
|
4694
|
+
if (memory.summary && memory.summary.toLowerCase().includes(queryLower)) {
|
|
4695
|
+
score += 4;
|
|
4696
|
+
}
|
|
4697
|
+
if (memory.value.toLowerCase().includes(queryLower)) {
|
|
4698
|
+
score += 3;
|
|
4699
|
+
}
|
|
4700
|
+
return score;
|
|
4701
|
+
}
|
|
4702
|
+
function searchMemories(query, filter, db) {
|
|
4703
|
+
const d = db || getDatabase();
|
|
4704
|
+
const queryLower = query.toLowerCase();
|
|
4705
|
+
const queryParam = `%${query}%`;
|
|
4706
|
+
const conditions = [];
|
|
4707
|
+
const params = [];
|
|
4708
|
+
conditions.push("m.status = 'active'");
|
|
4709
|
+
conditions.push("(m.expires_at IS NULL OR m.expires_at >= datetime('now'))");
|
|
4710
|
+
conditions.push(`(m.key LIKE ? OR m.value LIKE ? OR m.summary LIKE ? OR m.id IN (SELECT memory_id FROM memory_tags WHERE tag LIKE ?))`);
|
|
4711
|
+
params.push(queryParam, queryParam, queryParam, queryParam);
|
|
4712
|
+
if (filter) {
|
|
4713
|
+
if (filter.scope) {
|
|
4714
|
+
if (Array.isArray(filter.scope)) {
|
|
4715
|
+
conditions.push(`m.scope IN (${filter.scope.map(() => "?").join(",")})`);
|
|
4716
|
+
params.push(...filter.scope);
|
|
4717
|
+
} else {
|
|
4718
|
+
conditions.push("m.scope = ?");
|
|
4719
|
+
params.push(filter.scope);
|
|
4720
|
+
}
|
|
4721
|
+
}
|
|
4722
|
+
if (filter.category) {
|
|
4723
|
+
if (Array.isArray(filter.category)) {
|
|
4724
|
+
conditions.push(`m.category IN (${filter.category.map(() => "?").join(",")})`);
|
|
4725
|
+
params.push(...filter.category);
|
|
4726
|
+
} else {
|
|
4727
|
+
conditions.push("m.category = ?");
|
|
4728
|
+
params.push(filter.category);
|
|
4729
|
+
}
|
|
4730
|
+
}
|
|
4731
|
+
if (filter.source) {
|
|
4732
|
+
if (Array.isArray(filter.source)) {
|
|
4733
|
+
conditions.push(`m.source IN (${filter.source.map(() => "?").join(",")})`);
|
|
4734
|
+
params.push(...filter.source);
|
|
4735
|
+
} else {
|
|
4736
|
+
conditions.push("m.source = ?");
|
|
4737
|
+
params.push(filter.source);
|
|
4738
|
+
}
|
|
4739
|
+
}
|
|
4740
|
+
if (filter.status) {
|
|
4741
|
+
conditions.shift();
|
|
4742
|
+
if (Array.isArray(filter.status)) {
|
|
4743
|
+
conditions.push(`m.status IN (${filter.status.map(() => "?").join(",")})`);
|
|
4744
|
+
params.push(...filter.status);
|
|
4745
|
+
} else {
|
|
4746
|
+
conditions.push("m.status = ?");
|
|
4747
|
+
params.push(filter.status);
|
|
4748
|
+
}
|
|
4749
|
+
}
|
|
4750
|
+
if (filter.project_id) {
|
|
4751
|
+
conditions.push("m.project_id = ?");
|
|
4752
|
+
params.push(filter.project_id);
|
|
4753
|
+
}
|
|
4754
|
+
if (filter.agent_id) {
|
|
4755
|
+
conditions.push("m.agent_id = ?");
|
|
4756
|
+
params.push(filter.agent_id);
|
|
4757
|
+
}
|
|
4758
|
+
if (filter.session_id) {
|
|
4759
|
+
conditions.push("m.session_id = ?");
|
|
4760
|
+
params.push(filter.session_id);
|
|
4761
|
+
}
|
|
4762
|
+
if (filter.min_importance) {
|
|
4763
|
+
conditions.push("m.importance >= ?");
|
|
4764
|
+
params.push(filter.min_importance);
|
|
4765
|
+
}
|
|
4766
|
+
if (filter.pinned !== undefined) {
|
|
4767
|
+
conditions.push("m.pinned = ?");
|
|
4768
|
+
params.push(filter.pinned ? 1 : 0);
|
|
4769
|
+
}
|
|
4770
|
+
if (filter.tags && filter.tags.length > 0) {
|
|
4771
|
+
for (const tag of filter.tags) {
|
|
4772
|
+
conditions.push("m.id IN (SELECT memory_id FROM memory_tags WHERE tag = ?)");
|
|
4773
|
+
params.push(tag);
|
|
4774
|
+
}
|
|
4775
|
+
}
|
|
4776
|
+
}
|
|
4777
|
+
const sql = `SELECT m.* FROM memories m WHERE ${conditions.join(" AND ")}`;
|
|
4778
|
+
const rows = d.query(sql).all(...params);
|
|
4779
|
+
const scored = [];
|
|
4780
|
+
for (const row of rows) {
|
|
4781
|
+
const memory = parseMemoryRow2(row);
|
|
4782
|
+
const rawScore = computeScore(memory, queryLower);
|
|
4783
|
+
if (rawScore === 0)
|
|
4784
|
+
continue;
|
|
4785
|
+
const weightedScore = rawScore * memory.importance / 10;
|
|
4786
|
+
const matchType = determineMatchType(memory, queryLower);
|
|
4787
|
+
scored.push({
|
|
4788
|
+
memory,
|
|
4789
|
+
score: weightedScore,
|
|
4790
|
+
match_type: matchType
|
|
4791
|
+
});
|
|
4792
|
+
}
|
|
4793
|
+
scored.sort((a, b) => {
|
|
4794
|
+
if (b.score !== a.score)
|
|
4795
|
+
return b.score - a.score;
|
|
4796
|
+
return b.memory.importance - a.memory.importance;
|
|
4797
|
+
});
|
|
4798
|
+
const offset = filter?.offset ?? 0;
|
|
4799
|
+
const limit = filter?.limit ?? scored.length;
|
|
4800
|
+
return scored.slice(offset, offset + limit);
|
|
4801
|
+
}
|
|
4802
|
+
|
|
4803
|
+
// src/lib/project-detect.ts
|
|
4804
|
+
import { existsSync as existsSync2 } from "fs";
|
|
4805
|
+
import { basename, dirname as dirname2, join as join2, resolve as resolve2 } from "path";
|
|
4806
|
+
function findGitRoot2(startDir) {
|
|
4807
|
+
let dir = resolve2(startDir);
|
|
4808
|
+
while (true) {
|
|
4809
|
+
if (existsSync2(join2(dir, ".git")))
|
|
4810
|
+
return dir;
|
|
4811
|
+
const parent = dirname2(dir);
|
|
4812
|
+
if (parent === dir)
|
|
4813
|
+
break;
|
|
4814
|
+
dir = parent;
|
|
4815
|
+
}
|
|
4816
|
+
return null;
|
|
4817
|
+
}
|
|
4818
|
+
var _cachedProject = undefined;
|
|
4819
|
+
function detectProject(db) {
|
|
4820
|
+
if (_cachedProject !== undefined)
|
|
4821
|
+
return _cachedProject;
|
|
4822
|
+
const d = db || getDatabase();
|
|
4823
|
+
const cwd = process.cwd();
|
|
4824
|
+
const gitRoot = findGitRoot2(cwd);
|
|
4825
|
+
if (!gitRoot) {
|
|
4826
|
+
_cachedProject = null;
|
|
4827
|
+
return null;
|
|
4828
|
+
}
|
|
4829
|
+
const repoName = basename(gitRoot);
|
|
4830
|
+
const absPath = resolve2(gitRoot);
|
|
4831
|
+
const existing = getProject(absPath, d);
|
|
4832
|
+
if (existing) {
|
|
4833
|
+
_cachedProject = existing;
|
|
4834
|
+
return existing;
|
|
4835
|
+
}
|
|
4836
|
+
const project = registerProject(repoName, absPath, undefined, undefined, d);
|
|
4837
|
+
_cachedProject = project;
|
|
4838
|
+
return project;
|
|
4839
|
+
}
|
|
4840
|
+
|
|
4625
4841
|
// src/mcp/index.ts
|
|
4626
4842
|
var server = new McpServer({
|
|
4627
4843
|
name: "mementos",
|
|
4628
4844
|
version: "0.1.0"
|
|
4629
4845
|
});
|
|
4846
|
+
var _autoProjectInitialized = false;
|
|
4847
|
+
function ensureAutoProject() {
|
|
4848
|
+
if (_autoProjectInitialized)
|
|
4849
|
+
return;
|
|
4850
|
+
_autoProjectInitialized = true;
|
|
4851
|
+
try {
|
|
4852
|
+
detectProject();
|
|
4853
|
+
} catch {}
|
|
4854
|
+
}
|
|
4630
4855
|
function formatError(error) {
|
|
4631
4856
|
if (error instanceof VersionConflictError)
|
|
4632
4857
|
return `Version conflict: ${error.message}`;
|
|
@@ -4686,17 +4911,18 @@ server.tool("memory_save", "Save a memory (create or upsert). Use scope 'global'
|
|
|
4686
4911
|
value: exports_external.string().describe("Memory content/value"),
|
|
4687
4912
|
scope: exports_external.enum(["global", "shared", "private"]).optional().describe("Memory scope (default: private)"),
|
|
4688
4913
|
category: exports_external.enum(["preference", "fact", "knowledge", "history"]).optional().describe("Memory category (default: knowledge)"),
|
|
4689
|
-
importance: exports_external.number().min(1).max(10).optional().describe("Importance 1-10 (default: 5)"),
|
|
4914
|
+
importance: exports_external.coerce.number().min(1).max(10).optional().describe("Importance 1-10 (default: 5)"),
|
|
4690
4915
|
tags: exports_external.array(exports_external.string()).optional().describe("Tags for categorization"),
|
|
4691
4916
|
summary: exports_external.string().optional().describe("Brief summary of the memory"),
|
|
4692
4917
|
agent_id: exports_external.string().optional().describe("Agent ID (for scoping)"),
|
|
4693
4918
|
project_id: exports_external.string().optional().describe("Project ID (for scoping)"),
|
|
4694
4919
|
session_id: exports_external.string().optional().describe("Session ID (for scoping)"),
|
|
4695
|
-
ttl_ms: exports_external.number().optional().describe("Time-to-live in milliseconds"),
|
|
4920
|
+
ttl_ms: exports_external.coerce.number().optional().describe("Time-to-live in milliseconds"),
|
|
4696
4921
|
source: exports_external.enum(["user", "agent", "system", "auto", "imported"]).optional().describe("Source of the memory"),
|
|
4697
4922
|
metadata: exports_external.record(exports_external.unknown()).optional().describe("Arbitrary metadata")
|
|
4698
4923
|
}, async (args) => {
|
|
4699
4924
|
try {
|
|
4925
|
+
ensureAutoProject();
|
|
4700
4926
|
const memory = createMemory(args);
|
|
4701
4927
|
return { content: [{ type: "text", text: `Memory saved:
|
|
4702
4928
|
${formatMemory(memory)}` }] };
|
|
@@ -4712,12 +4938,31 @@ server.tool("memory_recall", "Recall a memory by key. Returns the best matching
|
|
|
4712
4938
|
session_id: exports_external.string().optional()
|
|
4713
4939
|
}, async (args) => {
|
|
4714
4940
|
try {
|
|
4941
|
+
ensureAutoProject();
|
|
4715
4942
|
const memory = getMemoryByKey(args.key, args.scope, args.agent_id, args.project_id, args.session_id);
|
|
4716
|
-
if (
|
|
4717
|
-
|
|
4943
|
+
if (memory) {
|
|
4944
|
+
touchMemory(memory.id);
|
|
4945
|
+
return { content: [{ type: "text", text: formatMemory(memory) }] };
|
|
4718
4946
|
}
|
|
4719
|
-
|
|
4720
|
-
|
|
4947
|
+
const results = searchMemories(args.key, {
|
|
4948
|
+
scope: args.scope,
|
|
4949
|
+
agent_id: args.agent_id,
|
|
4950
|
+
project_id: args.project_id,
|
|
4951
|
+
session_id: args.session_id,
|
|
4952
|
+
limit: 1
|
|
4953
|
+
});
|
|
4954
|
+
if (results.length > 0) {
|
|
4955
|
+
const best = results[0];
|
|
4956
|
+
touchMemory(best.memory.id);
|
|
4957
|
+
return {
|
|
4958
|
+
content: [{
|
|
4959
|
+
type: "text",
|
|
4960
|
+
text: `No exact match for key "${args.key}", showing best result (score: ${best.score.toFixed(2)}, match: ${best.match_type}):
|
|
4961
|
+
${formatMemory(best.memory)}`
|
|
4962
|
+
}]
|
|
4963
|
+
};
|
|
4964
|
+
}
|
|
4965
|
+
return { content: [{ type: "text", text: `No memory found for key: ${args.key}` }] };
|
|
4721
4966
|
} catch (e) {
|
|
4722
4967
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
4723
4968
|
}
|
|
@@ -4726,14 +4971,14 @@ server.tool("memory_list", "List memories with optional filters", {
|
|
|
4726
4971
|
scope: exports_external.enum(["global", "shared", "private"]).optional(),
|
|
4727
4972
|
category: exports_external.enum(["preference", "fact", "knowledge", "history"]).optional(),
|
|
4728
4973
|
tags: exports_external.array(exports_external.string()).optional(),
|
|
4729
|
-
min_importance: exports_external.number().optional(),
|
|
4974
|
+
min_importance: exports_external.coerce.number().optional(),
|
|
4730
4975
|
pinned: exports_external.boolean().optional(),
|
|
4731
4976
|
agent_id: exports_external.string().optional(),
|
|
4732
4977
|
project_id: exports_external.string().optional(),
|
|
4733
4978
|
session_id: exports_external.string().optional(),
|
|
4734
4979
|
status: exports_external.enum(["active", "archived", "expired"]).optional(),
|
|
4735
|
-
limit: exports_external.number().optional().describe("Max results (default: 50)"),
|
|
4736
|
-
offset: exports_external.number().optional()
|
|
4980
|
+
limit: exports_external.coerce.number().optional().describe("Max results (default: 50)"),
|
|
4981
|
+
offset: exports_external.coerce.number().optional()
|
|
4737
4982
|
}, async (args) => {
|
|
4738
4983
|
try {
|
|
4739
4984
|
const filter = {
|
|
@@ -4757,14 +5002,14 @@ server.tool("memory_update", "Update a memory's metadata (value, importance, tag
|
|
|
4757
5002
|
value: exports_external.string().optional(),
|
|
4758
5003
|
category: exports_external.enum(["preference", "fact", "knowledge", "history"]).optional(),
|
|
4759
5004
|
scope: exports_external.enum(["global", "shared", "private"]).optional(),
|
|
4760
|
-
importance: exports_external.number().min(1).max(10).optional(),
|
|
5005
|
+
importance: exports_external.coerce.number().min(1).max(10).optional(),
|
|
4761
5006
|
tags: exports_external.array(exports_external.string()).optional(),
|
|
4762
5007
|
summary: exports_external.string().nullable().optional(),
|
|
4763
5008
|
pinned: exports_external.boolean().optional(),
|
|
4764
5009
|
status: exports_external.enum(["active", "archived", "expired"]).optional(),
|
|
4765
5010
|
metadata: exports_external.record(exports_external.unknown()).optional(),
|
|
4766
5011
|
expires_at: exports_external.string().nullable().optional(),
|
|
4767
|
-
version: exports_external.number().describe("Current version (for optimistic locking)")
|
|
5012
|
+
version: exports_external.coerce.number().describe("Current version (for optimistic locking)")
|
|
4768
5013
|
}, async (args) => {
|
|
4769
5014
|
try {
|
|
4770
5015
|
const id = resolveId(args.id);
|
|
@@ -4809,7 +5054,7 @@ server.tool("memory_search", "Search memories by keyword across key, value, summ
|
|
|
4809
5054
|
tags: exports_external.array(exports_external.string()).optional(),
|
|
4810
5055
|
agent_id: exports_external.string().optional(),
|
|
4811
5056
|
project_id: exports_external.string().optional(),
|
|
4812
|
-
limit: exports_external.number().optional().describe("Max results (default: 20)")
|
|
5057
|
+
limit: exports_external.coerce.number().optional().describe("Max results (default: 20)")
|
|
4813
5058
|
}, async (args) => {
|
|
4814
5059
|
try {
|
|
4815
5060
|
const filter = {
|
|
@@ -4898,7 +5143,7 @@ server.tool("memory_import", "Import memories from JSON array", {
|
|
|
4898
5143
|
value: exports_external.string(),
|
|
4899
5144
|
scope: exports_external.enum(["global", "shared", "private"]).optional(),
|
|
4900
5145
|
category: exports_external.enum(["preference", "fact", "knowledge", "history"]).optional(),
|
|
4901
|
-
importance: exports_external.number().optional(),
|
|
5146
|
+
importance: exports_external.coerce.number().optional(),
|
|
4902
5147
|
tags: exports_external.array(exports_external.string()).optional(),
|
|
4903
5148
|
summary: exports_external.string().optional(),
|
|
4904
5149
|
source: exports_external.enum(["user", "agent", "system", "auto", "imported"]).optional(),
|
|
@@ -4924,9 +5169,9 @@ server.tool("memory_inject", "Get formatted memory context for injection into ag
|
|
|
4924
5169
|
agent_id: exports_external.string().optional().describe("Agent ID for scope filtering"),
|
|
4925
5170
|
project_id: exports_external.string().optional().describe("Project ID for scope filtering"),
|
|
4926
5171
|
session_id: exports_external.string().optional().describe("Session ID for scope filtering"),
|
|
4927
|
-
max_tokens: exports_external.number().optional().describe("Max approximate token budget (default: 500)"),
|
|
5172
|
+
max_tokens: exports_external.coerce.number().optional().describe("Max approximate token budget (default: 500)"),
|
|
4928
5173
|
categories: exports_external.array(exports_external.enum(["preference", "fact", "knowledge", "history"])).optional(),
|
|
4929
|
-
min_importance: exports_external.number().optional().describe("Minimum importance threshold (default: 3)")
|
|
5174
|
+
min_importance: exports_external.coerce.number().optional().describe("Minimum importance threshold (default: 3)")
|
|
4930
5175
|
}, async (args) => {
|
|
4931
5176
|
try {
|
|
4932
5177
|
const maxTokens = args.max_tokens || 500;
|
|
@@ -5059,6 +5304,35 @@ Last seen: ${agent.last_seen_at}`
|
|
|
5059
5304
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
5060
5305
|
}
|
|
5061
5306
|
});
|
|
5307
|
+
server.tool("update_agent", "Update an agent's name, description, role, or metadata. Agents can update themselves.", {
|
|
5308
|
+
id: exports_external.string().describe("Agent ID or name"),
|
|
5309
|
+
name: exports_external.string().optional().describe("New agent name"),
|
|
5310
|
+
description: exports_external.string().optional().describe("New description"),
|
|
5311
|
+
role: exports_external.string().optional().describe("New role"),
|
|
5312
|
+
metadata: exports_external.record(exports_external.unknown()).optional().describe("Updated metadata")
|
|
5313
|
+
}, async (args) => {
|
|
5314
|
+
try {
|
|
5315
|
+
const { id, ...updates } = args;
|
|
5316
|
+
const agent = updateAgent(id, updates);
|
|
5317
|
+
if (!agent) {
|
|
5318
|
+
return { content: [{ type: "text", text: `Agent not found: ${id}` }] };
|
|
5319
|
+
}
|
|
5320
|
+
return {
|
|
5321
|
+
content: [{
|
|
5322
|
+
type: "text",
|
|
5323
|
+
text: `Agent updated:
|
|
5324
|
+
ID: ${agent.id}
|
|
5325
|
+
Name: ${agent.name}
|
|
5326
|
+
Description: ${agent.description || "-"}
|
|
5327
|
+
Role: ${agent.role || "agent"}
|
|
5328
|
+
Metadata: ${JSON.stringify(agent.metadata)}
|
|
5329
|
+
Last seen: ${agent.last_seen_at}`
|
|
5330
|
+
}]
|
|
5331
|
+
};
|
|
5332
|
+
} catch (e) {
|
|
5333
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
5334
|
+
}
|
|
5335
|
+
});
|
|
5062
5336
|
server.tool("register_project", "Register a project for memory scoping", {
|
|
5063
5337
|
name: exports_external.string().describe("Project name"),
|
|
5064
5338
|
path: exports_external.string().describe("Absolute path to project"),
|
|
@@ -5108,7 +5382,7 @@ server.tool("bulk_forget", "Delete multiple memories by IDs", {
|
|
|
5108
5382
|
});
|
|
5109
5383
|
server.tool("bulk_update", "Update multiple memories with the same changes", {
|
|
5110
5384
|
ids: exports_external.array(exports_external.string()).describe("Memory IDs to update"),
|
|
5111
|
-
importance: exports_external.number().min(1).max(10).optional(),
|
|
5385
|
+
importance: exports_external.coerce.number().min(1).max(10).optional(),
|
|
5112
5386
|
tags: exports_external.array(exports_external.string()).optional(),
|
|
5113
5387
|
pinned: exports_external.boolean().optional(),
|
|
5114
5388
|
category: exports_external.enum(["preference", "fact", "knowledge", "history"]).optional(),
|
|
@@ -5142,7 +5416,7 @@ server.tool("memory_context", "Get all memories relevant to the current context.
|
|
|
5142
5416
|
agent_id: exports_external.string().optional(),
|
|
5143
5417
|
project_id: exports_external.string().optional(),
|
|
5144
5418
|
scope: exports_external.enum(["global", "shared", "private"]).optional().describe("Limit to specific scope"),
|
|
5145
|
-
limit: exports_external.number().optional().describe("Max memories (default: 30)")
|
|
5419
|
+
limit: exports_external.coerce.number().optional().describe("Max memories (default: 30)")
|
|
5146
5420
|
}, async (args) => {
|
|
5147
5421
|
try {
|
|
5148
5422
|
const filter = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAslBH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAslBH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAoG9C"}
|
package/dist/server/index.js
CHANGED
|
@@ -1158,6 +1158,42 @@ function startServer(port) {
|
|
|
1158
1158
|
if (pathname === "/api/health" || pathname === "/health") {
|
|
1159
1159
|
return json({ status: "ok", version: "0.1.0" });
|
|
1160
1160
|
}
|
|
1161
|
+
if (pathname === "/api/memories/stream" && req.method === "GET") {
|
|
1162
|
+
const stream = new ReadableStream({
|
|
1163
|
+
start(controller) {
|
|
1164
|
+
const encoder = new TextEncoder;
|
|
1165
|
+
let lastSeen = new Date().toISOString();
|
|
1166
|
+
const send = (data) => {
|
|
1167
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
1168
|
+
|
|
1169
|
+
`));
|
|
1170
|
+
};
|
|
1171
|
+
send({ type: "connected", timestamp: lastSeen });
|
|
1172
|
+
const interval = setInterval(() => {
|
|
1173
|
+
try {
|
|
1174
|
+
const db = getDatabase();
|
|
1175
|
+
const rows = db.query("SELECT * FROM memories WHERE updated_at > ? OR created_at > ? ORDER BY updated_at DESC LIMIT 50").all(lastSeen, lastSeen);
|
|
1176
|
+
if (rows.length > 0) {
|
|
1177
|
+
lastSeen = new Date().toISOString();
|
|
1178
|
+
send({ type: "memories", data: rows, count: rows.length });
|
|
1179
|
+
}
|
|
1180
|
+
} catch {}
|
|
1181
|
+
}, 1000);
|
|
1182
|
+
req.signal.addEventListener("abort", () => {
|
|
1183
|
+
clearInterval(interval);
|
|
1184
|
+
controller.close();
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
});
|
|
1188
|
+
return new Response(stream, {
|
|
1189
|
+
headers: {
|
|
1190
|
+
"Content-Type": "text/event-stream",
|
|
1191
|
+
"Cache-Control": "no-cache",
|
|
1192
|
+
Connection: "keep-alive",
|
|
1193
|
+
...CORS_HEADERS
|
|
1194
|
+
}
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
1161
1197
|
const matched = matchRoute(req.method, pathname);
|
|
1162
1198
|
if (!matched) {
|
|
1163
1199
|
if (pathname.startsWith("/api/")) {
|