@membank/core 0.11.0 → 0.11.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.
- package/dist/index.cjs +29 -10
- package/dist/index.d.cts +6 -6
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +29 -10
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -185,20 +185,39 @@ CREATE INDEX IF NOT EXISTS idx_extraction_runs_status
|
|
|
185
185
|
JOIN projects p ON p.id = mp.project_id
|
|
186
186
|
WHERE m.type = ? AND p.scope_hash = ?
|
|
187
187
|
ORDER BY similarity DESC LIMIT 1`).get(r,t,n),i===void 0?[]:[{id:i.id,similarity:i.similarity}]}create(e){let{id:t,content:n,type:r,tags:i,sourceHarness:a,embedding:o,projectScope:s}=e,c=new Date().toISOString(),l=Buffer.from(o.buffer);if(this.#e.db.prepare(`INSERT INTO memories (id, content, type, tags, source, access_count, pinned, created_at, updated_at)
|
|
188
|
-
VALUES (?, ?, ?, ?, ?, 0, 0, ?, ?)`).run(t,n,r,JSON.stringify(i),a,c,c),this.#e.db.prepare(`INSERT INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`).run(l,t),s!==void 0){let e=this.#t.upsertByHash(s.hash,s.name);this.#t.addAssociation(t,e.id)}return M(A.parse(this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(t)),this.#t.getProjectsForMemories([t]).get(t)??[])}overwrite(e,t,n){let r=new Date().toISOString(),i=Buffer.from(n.buffer);this.#e.db.prepare(`UPDATE memories SET content = ?, updated_at = ? WHERE id = ?`).run(t,r,e);let a=this.#e.db.prepare(`SELECT rowid FROM memories WHERE id = ?`).get(e)?.rowid;a!==void 0&&this.#e.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(i,a);let o=A.parse(this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(e)),s=this.#t.getProjectsForMemories([e]),c=this.#
|
|
188
|
+
VALUES (?, ?, ?, ?, ?, 0, 0, ?, ?)`).run(t,n,r,JSON.stringify(i),a,c,c),this.#e.db.prepare(`INSERT INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`).run(l,t),s!==void 0){let e=this.#t.upsertByHash(s.hash,s.name);this.#t.addAssociation(t,e.id)}return M(A.parse(this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(t)),this.#t.getProjectsForMemories([t]).get(t)??[])}overwrite(e,t,n){let r=new Date().toISOString(),i=Buffer.from(n.buffer);this.#e.db.prepare(`UPDATE memories SET content = ?, updated_at = ? WHERE id = ?`).run(t,r,e);let a=this.#e.db.prepare(`SELECT rowid FROM memories WHERE id = ?`).get(e)?.rowid;a!==void 0&&this.#e.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(i,a);let o=A.parse(this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(e)),s=this.#t.getProjectsForMemories([e]),c=this.#r([e]);return M(o,s.get(e)??[],c.get(e)??[])}findById(e){let t=this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(e);if(t===void 0)return;let n=this.#t.getProjectsForMemories([e]),r=this.#r([e]);return M(t,n.get(e)??[],r.get(e)??[])}update(e,t,n){let{content:r,tags:i,type:a}=O.parse(t),o=this.#e.db.prepare(`SELECT m.rowid, m.* FROM memories m WHERE m.id = ?`).get(e);if(o===void 0)throw Error(`Memory not found: ${e}`);let s=new Date().toISOString(),c=[`updated_at = ?`],l=[s];if(r!==void 0&&(c.push(`content = ?`),l.push(r)),i!==void 0&&(c.push(`tags = ?`),l.push(JSON.stringify(i))),a!==void 0&&(c.push(`type = ?`),l.push(a)),l.push(e),this.#e.db.prepare(`UPDATE memories SET ${c.join(`, `)} WHERE id = ?`).run(...l),n!==void 0){let e=Buffer.from(n.buffer);this.#e.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(e,o.rowid)}let u=A.parse(this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(e)),d=this.#t.getProjectsForMemories([e]),f=this.#r([e]);return M(u,d.get(e)??[],f.get(e)??[])}delete(e){let t=this.#e.db.prepare(`SELECT rowid FROM memories WHERE id = ?`).get(e);t!==void 0&&this.#e.db.prepare(`DELETE FROM embeddings WHERE rowid = ?`).run(t.rowid),this.#e.db.prepare(`DELETE FROM memory_projects WHERE memory_id = ?`).run(e),this.#e.db.prepare(`DELETE FROM memories WHERE id = ?`).run(e)}list(e){let t=[],n=[];e?.type!==void 0&&(t.push(`m.type = ?`),n.push(e.type)),e?.pinned===!0&&t.push(`m.pinned = 1`),e?.needsReview===!0&&t.push(`EXISTS (SELECT 1 FROM memory_review_events e WHERE e.memory_id = m.id AND e.resolved_at IS NULL)`),e?.projectId===`global`?t.push(`m.id NOT IN (SELECT memory_id FROM memory_projects)`):e?.projectId!==void 0&&(t.push(`m.id IN (SELECT memory_id FROM memory_projects WHERE project_id = ?)`),n.push(e.projectId));let r=t.length>0?`WHERE ${t.join(` AND `)}`:``,i=this.#e.db.prepare(`SELECT m.* FROM memories m ${r} ORDER BY m.created_at DESC`).all(...n);if(i.length===0)return[];let a=i.map(e=>e.id),o=this.#t.getProjectsForMemories(a),s=this.#r(a);return i.map(e=>M(e,o.get(e.id)??[],s.get(e.id)??[]))}listPinnedGlobal(){return this.#e.db.prepare(`SELECT * FROM memories
|
|
189
189
|
WHERE id NOT IN (SELECT memory_id FROM memory_projects)
|
|
190
190
|
AND pinned = 1`).all().map(e=>M(e,[]))}listPinnedForProject(e){return this.#e.db.prepare(`SELECT m.* FROM memories m
|
|
191
191
|
JOIN memory_projects mp ON mp.memory_id = m.id
|
|
192
192
|
JOIN projects p ON p.id = mp.project_id
|
|
193
|
-
WHERE p.scope_hash = ? AND m.pinned = 1`).all(e).map(e=>M(e,[]))}listFlagged(){let e
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
193
|
+
WHERE p.scope_hash = ? AND m.pinned = 1`).all(e).map(e=>M(e,[]))}listFlagged(e){let t;if(t=e===void 0?this.#e.db.prepare(`SELECT * FROM memories
|
|
194
|
+
WHERE EXISTS (
|
|
195
|
+
SELECT 1 FROM memory_review_events e
|
|
196
|
+
WHERE e.memory_id = memories.id AND e.resolved_at IS NULL
|
|
197
|
+
)
|
|
198
|
+
ORDER BY created_at DESC`).all():this.#e.db.prepare(`SELECT * FROM memories
|
|
199
|
+
WHERE EXISTS (
|
|
200
|
+
SELECT 1 FROM memory_review_events e
|
|
201
|
+
WHERE e.memory_id = memories.id AND e.resolved_at IS NULL
|
|
202
|
+
)
|
|
203
|
+
AND ${this.#n()}
|
|
204
|
+
ORDER BY created_at DESC`).all(e),t.length===0)return[];let n=t.map(e=>e.id),r=this.#t.getProjectsForMemories(n),i=this.#r(n,{unresolvedOnly:!0});return t.map(e=>M(e,r.get(e.id)??[],i.get(e.id)??[]))}listReviewEvents(e,t){let n=t?.unresolvedOnly===!0?`WHERE memory_id = ? AND resolved_at IS NULL`:`WHERE memory_id = ?`;return this.#e.db.prepare(`SELECT * FROM memory_review_events ${n} ORDER BY created_at DESC`).all(e).map(e=>N(w.parse(e)))}createReviewEvent(e){let t=new Date().toISOString();this.#e.db.prepare(`INSERT INTO memory_review_events
|
|
199
205
|
(id, memory_id, conflicting_memory_id, similarity, conflict_content_snapshot, reason, created_at)
|
|
200
|
-
VALUES (?, ?, ?, ?, ?, 'similarity_dedup', ?)`).run((0,g.randomUUID)(),e.memoryId,e.conflictingMemoryId,e.similarity,e.conflictContentSnapshot,t)}resolveReviewEvents(e){let t=new Date().toISOString();this.#e.db.prepare(`UPDATE memory_review_events SET resolved_at = ? WHERE memory_id = ? AND resolved_at IS NULL`).run(t,e)}getPinnedCharCount(){return(this.#e.db.prepare(`SELECT COALESCE(SUM(LENGTH(content)), 0) as total FROM memories WHERE pinned = 1`).get()??{total:0}).total
|
|
201
|
-
|
|
206
|
+
VALUES (?, ?, ?, ?, ?, 'similarity_dedup', ?)`).run((0,g.randomUUID)(),e.memoryId,e.conflictingMemoryId,e.similarity,e.conflictContentSnapshot,t)}resolveReviewEvents(e){let t=new Date().toISOString();this.#e.db.prepare(`UPDATE memory_review_events SET resolved_at = ? WHERE memory_id = ? AND resolved_at IS NULL`).run(t,e)}getPinnedCharCount(e){return e===void 0?(this.#e.db.prepare(`SELECT COALESCE(SUM(LENGTH(content)), 0) as total FROM memories WHERE pinned = 1`).get()??{total:0}).total:(this.#e.db.prepare(`SELECT COALESCE(SUM(LENGTH(content)), 0) as total FROM memories
|
|
207
|
+
WHERE pinned = 1 AND ${this.#n()}`).get(e)??{total:0}).total}stats(e){let t=Object.fromEntries(v.map(e=>[e,0]));if(e!==void 0){let n=this.#e.db.prepare(`SELECT type, COUNT(*) as count FROM memories
|
|
208
|
+
WHERE ${this.#n()}
|
|
209
|
+
GROUP BY type`).all(e);for(let e of n){let n=y.safeParse(e.type);n.success&&(t[n.data]=e.count)}let r=this.#e.db.prepare(`SELECT COUNT(*) as total, SUM(pinned) as pinned,
|
|
210
|
+
COALESCE(SUM(CASE WHEN pinned = 1 THEN LENGTH(content) ELSE 0 END), 0) as pinBudgetChars
|
|
211
|
+
FROM memories WHERE ${this.#n()}`).get(e)??{total:0,pinned:0,pinBudgetChars:0},i=this.#e.db.prepare(`SELECT COUNT(DISTINCT e.memory_id) as needsReview
|
|
212
|
+
FROM memory_review_events e
|
|
213
|
+
JOIN memories m ON m.id = e.memory_id
|
|
214
|
+
WHERE e.resolved_at IS NULL
|
|
215
|
+
AND ${this.#n(`m`)}`).get(e)??{needsReview:0};return{byType:t,total:r.total,pinned:r.pinned??0,needsReview:i.needsReview,pinBudgetChars:r.pinBudgetChars}}let n=this.#e.db.prepare(`SELECT type, COUNT(*) as count FROM memories GROUP BY type`).all();for(let e of n){let n=y.safeParse(e.type);n.success&&(t[n.data]=e.count)}let r=this.#e.db.prepare(`SELECT COUNT(*) as total, SUM(pinned) as pinned FROM memories`).get()??{total:0,pinned:0},i=this.#e.db.prepare(`SELECT COUNT(DISTINCT memory_id) as needsReview FROM memory_review_events WHERE resolved_at IS NULL`).get()??{needsReview:0};return{byType:t,total:r.total,pinned:r.pinned??0,needsReview:i.needsReview,pinBudgetChars:this.getPinnedCharCount()}}setPin(e,t){if(this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(e)===void 0)throw Error(`Memory not found: ${e}`);let n=new Date().toISOString();this.#e.db.prepare(`UPDATE memories SET pinned = ?, updated_at = ? WHERE id = ?`).run(+!!t,n,e);let r=A.parse(this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(e)),i=this.#t.getProjectsForMemories([e]),a=this.#r([e]);return M(r,i.get(e)??[],a.get(e)??[])}incrementAccessCount(e){this.#e.db.prepare(`UPDATE memories SET access_count = access_count + 1 WHERE id = ?`).run(e)}exportAll(){return this.#e.db.prepare(`SELECT m.*, e.embedding FROM memories m LEFT JOIN embeddings e ON e.rowid = m.rowid ORDER BY m.created_at DESC`).all().map(e=>({id:e.id,content:e.content,type:y.parse(e.type),tags:b.parse(JSON.parse(e.tags)),sourceHarness:e.source,accessCount:e.access_count,pinned:e.pinned!==0,createdAt:e.created_at,updatedAt:e.updated_at,embedding:e.embedding===null?null:new Float32Array(e.embedding.buffer,e.embedding.byteOffset,e.embedding.byteLength/4)}))}importAll(e){let t=this.#e.db.prepare(`INSERT OR REPLACE INTO memories (id, content, type, tags, source, access_count, pinned, created_at, updated_at)
|
|
216
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`),n=this.#e.db.prepare(`INSERT OR REPLACE INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`);this.#e.db.transaction(()=>{for(let r of e)t.run(r.id,r.content,r.type,JSON.stringify(r.tags),r.sourceHarness,r.accessCount,+!!r.pinned,r.createdAt,r.updatedAt),r.embedding!==null&&n.run(Buffer.from(r.embedding.buffer,r.embedding.byteOffset,r.embedding.byteLength),r.id)})()}#n(e=`memories`){return`(${e}.id NOT IN (SELECT memory_id FROM memory_projects)
|
|
217
|
+
OR EXISTS (
|
|
218
|
+
SELECT 1 FROM memory_projects mp JOIN projects p ON p.id = mp.project_id
|
|
219
|
+
WHERE mp.memory_id = ${e}.id AND p.scope_hash = ?
|
|
220
|
+
))`}#r(e,t){if(e.length===0)return new Map;let n=e.map(()=>`?`).join(`, `),r=t?.unresolvedOnly===!0?`AND resolved_at IS NULL`:``,i=this.#e.db.prepare(`SELECT * FROM memory_review_events
|
|
202
221
|
WHERE memory_id IN (${n}) ${r}
|
|
203
222
|
ORDER BY created_at DESC`).all(...e),a=new Map;for(let e of i){let t=N(w.parse(e)),n=a.get(t.memoryId)??[];n.push(t),a.set(t.memoryId,n)}return a}};function we(e,t){return new K(e,t)}const q=(0,ne.promisify)(_.execFile);function J(e){return(0,g.createHash)(`sha256`).update(e).digest(`hex`).slice(0,16)}async function Y(){try{let{stdout:e}=await q(`git`,[`remote`,`get-url`,`origin`]),t=e.trim();if(t){let e=J(t);return{hash:e,name:t.split(`/`).pop()?.replace(/\.git$/,``)??e.slice(0,8)}}}catch{}let e=process.cwd(),t=J(e);return{hash:t,name:e.split(/[/\\]/).filter(Boolean).pop()??t.slice(0,8)}}async function Te(){try{let{stdout:e}=await q(`git`,[`remote`,`get-url`,`origin`]),t=e.trim();if(t)return J(t)}catch{}return J(process.cwd())}const Ee=[{name:`scope-to-projects`,description:`Rename the auto-migrated project for the current directory from its generic hash-derived name to the resolved repo/directory name.`}];async function De(e){let t=await Y(),n=e.getByHash(t.hash);if(n===void 0)return null;let r=n.name,i=e.countMemories(n.id);return e.rename(n.id,t.name),{migration:`scope-to-projects`,oldName:r,newName:t.name,memoryCount:i}}var Oe=class{#e;constructor(e){this.#e=e}upsertByHash(e,t){if(!/^[0-9a-f]{16}$/.test(e))throw Error(`Invalid scope hash "${e}": expected 16 lowercase hex characters`);let n=new Date().toISOString(),r=(0,g.randomUUID)();return this.#e.db.prepare(`INSERT OR IGNORE INTO projects (id, name, scope_hash, created_at, updated_at) VALUES (?, ?, ?, ?, ?)`).run(r,t,e,n,n),P(j.parse(this.#e.db.prepare(`SELECT * FROM projects WHERE scope_hash = ?`).get(e)))}rename(e,t){let n=new Date().toISOString();this.#e.db.prepare(`UPDATE projects SET name = ?, updated_at = ? WHERE id = ?`).run(t,n,e);let r=this.#e.db.prepare(`SELECT * FROM projects WHERE id = ?`).get(e);if(r===void 0)throw Error(`Project not found: ${e}`);return P(r)}list(){return this.#e.db.prepare(`SELECT * FROM projects ORDER BY name ASC`).all().map(P)}getByHash(e){let t=this.#e.db.prepare(`SELECT * FROM projects WHERE scope_hash = ?`).get(e);return t===void 0?void 0:P(t)}getByName(e){let t=this.#e.db.prepare(`SELECT * FROM projects WHERE name = ? LIMIT 1`).get(e);return t===void 0?void 0:P(t)}addAssociation(e,t){this.#e.db.prepare(`INSERT OR IGNORE INTO memory_projects (memory_id, project_id) VALUES (?, ?)`).run(e,t)}removeAssociation(e,t){this.#e.db.prepare(`DELETE FROM memory_projects WHERE memory_id = ? AND project_id = ?`).run(e,t)}countMemories(e){return this.#e.db.prepare(`SELECT COUNT(*) AS count FROM memory_projects WHERE project_id = ?`).get(e)?.count??0}getProjectsForMemories(e){if(e.length===0)return new Map;let t=e.map(()=>`?`).join(`,`),n=this.#e.db.prepare(`SELECT p.*, mp.memory_id FROM projects p
|
|
204
223
|
JOIN memory_projects mp ON mp.project_id = p.id
|
|
@@ -207,7 +226,7 @@ CREATE INDEX IF NOT EXISTS idx_extraction_runs_status
|
|
|
207
226
|
FROM memories m JOIN embeddings e ON e.rowid = m.rowid
|
|
208
227
|
${s}
|
|
209
228
|
${c}
|
|
210
|
-
`;return this.#e.db.prepare(l).all(...o).map(e=>({...M(e,[]),cosineSim:e.cosine_sim}))}},Me=class{#e;#t;#n;constructor(e,t,n){this.#e=e,this.#t=t,this.#n=n}async query(e){return Z(e,{adapter:new je(this.#e),repo:this.#n,embedder:this.#t})}};function Q(e,t){let n=t.repo.stats();return e.synthesis!==void 0&&e.synthesis.length>0?{stats:n.byType,pinnedGlobal:[],pinnedProject:[],synthesis:e.synthesis}:{stats:n.byType,pinnedGlobal:t.repo.listPinnedGlobal(),pinnedProject:t.repo.listPinnedForProject(e.projectHash)}}function Ne(){return[...v]}var Pe=class{#e;constructor(e){this.#e=e}getSessionContext(e,t){return Q({projectHash:e,synthesis:t},{repo:this.#e})}},Fe=class{#e;#t;#n;#r=new Set;#i=new Map;#a=!1;#o;#s=new Map;constructor(e,t,n){this.#e=e,this.#t=t,this.#n=n}async init(){this.#e.clearStaleInFlight(this.#t.inFlightTimeoutMs??12e4),this.#e.expireStale();let e=this.#e.getExpiredOrDirtyScopes();for(let{scope:t}of e)this.#r.add(t);this.#a=!0,await this.#l()}shutdown(){this.#a=!1,this.#o!==void 0&&(clearTimeout(this.#o),this.#o=void 0);let e=[...this.#s.values()];return e.length===0?Promise.resolve():Promise.race([Promise.allSettled(e).then(()=>void 0),new Promise(e=>setTimeout(e,5e3))])}markDirty(e){this.#r.add(e)}#c(){if(!this.#a)return;let e=this.#t.debounceMs??45e3;this.#o=setTimeout(()=>{this.#l()},e)}async#l(){let e=[...this.#r];for(let t of e){let e=this.#t.inFlightTimeoutMs??12e4,n=this.#e.getSynthesis(t);if(n?.inFlightSince!==null&&n?.inFlightSince!==void 0){if(Date.now()-new Date(n.inFlightSince).getTime()<e)continue;this.#e.clearInFlight(t)}this.#r.delete(t);let r=this.#u(t).finally(()=>{this.#s.delete(t)});this.#s.set(t,r)}this.#c()}async#u(e){this.#e.markInFlight(e);try{let t=e===`global`?void 0:e,n=await this.#n.run(e,t),r=this.#e.computeSourceMemoryHash(e);this.#e.saveSynthesis(e,n,r),this.#i.delete(e)}catch(t){let n=(this.#i.get(e)??0)+1;this.#i.set(e,n);let r=Math.min(n,5),i=(this.#t.debounceMs??45e3)*r;process.stderr.write(`membank synthesis: error for scope=${e} failures=${n} backoff=${i}ms: ${t instanceof Error?t.message:String(t)}\n`),setTimeout(()=>{this.#r.add(e)},i),this.#e.clearInFlight(e)}}};async function Ie(e,t){let n=e===`global`?void 0:e;t.synthRepo.markInFlight(e);try{let[r,i]=await Promise.all([t.agentRunner.run(e,n),Promise.resolve(t.synthRepo.computeSourceMemoryHash(e))]);return t.synthRepo.saveSynthesis(e,r,i),r}catch(n){throw t.synthRepo.clearInFlight(e),n}}var Le=class{#e;constructor(e,t){this.#e=e}async run(e,t){let n=(0,h.createSdkMcpServer)({name:`membank-synthesis-tools`,version:`1.0.0`,tools:[(0,h.tool)(`query_memory`,`Search memories by semantic similarity`,{query:d.z.string().describe(`Search text`),limit:d.z.number().optional().describe(`Maximum results to return`),global:d.z.boolean().optional().describe(`Query global memories only when true, otherwise current project scope`)},async({query:e,limit:n,global:r})=>({content:[{type:`text`,text:await this.#e.queryMemory({query:e,limit:n,global:r,projectHash:t})}]}),{annotations:{readOnlyHint:!0}}),(0,h.tool)(`get_memory_summary`,`Returns aggregate stats: total memories, counts by type, pinned count, review queue size`,{},async()=>({content:[{type:`text`,text:await this.#e.getMemorySummary()}]}),{annotations:{readOnlyHint:!0}})]}),r=`Synthesize the memories for ${e===`global`?`global (across all projects)`:`project scope: ${e}`}. Use get_memory_summary first to understand the overall state, then use query_memory to retrieve relevant memories (query with broad terms like "preferences", "corrections", "decisions", "key facts"). After gathering information, produce a concise synthesis of the most important things to remember about this user. Output only the synthesis text — no preamble, no metadata.`,i=Date.now(),a=Object.fromEntries(Object.entries(process.env).filter(e=>e[1]!==void 0)),o=(0,h.query)({prompt:r,options:{model:`claude-haiku-4-5-20251001`,systemPrompt:`You are a memory synthesizer. Your job is to read the user's stored memories and produce a concise, well-structured summary of what's most important to remember about this user — their preferences, corrections, decisions, and key facts. Pinned memories are higher fidelity and should be weighted more heavily. Exclude transient or ephemeral details. Output plain text suitable for injection into an LLM context window. Be concise — target 200-400 words.`,mcpServers:{"membank-synthesis-tools":n},allowedTools:[`mcp__membank-synthesis-tools__query_memory`,`mcp__membank-synthesis-tools__get_memory_summary`],disallowedTools:[`mcp__membank__*`],settingSources:[],permissionMode:`bypassPermissions`,env:a}}),s=``;for await(let e of o)if(e.type===`result`)if(e.subtype===`success`)s=e.result;else{let t=`errors`in e&&Array.isArray(e.errors)?`: ${e.errors.join(`; `)}`:``;throw Error(`Synthesis agent failed: ${e.subtype}${t}`)}let c=Date.now()-i;if(process.stderr.write(`membank synthesis: scope=${e} duration=${c}ms\n`),s===``)throw Error(`Synthesis agent returned empty result`);return s}};function Re(e,t){return new Le(e,t)}function $(e){return k.parse({id:e.id,scope:e.scope,content:e.content,sourceMemoryHash:e.source_memory_hash,synthesizedAt:e.synthesized_at,expiresAt:e.expires_at,inFlightSince:e.in_flight_since,createdAt:e.created_at,updatedAt:e.updated_at})}var ze=class{#e;constructor(e){this.#e=e}saveSynthesis(e,t,n){let r=new Date().toISOString(),i=new Date(Date.now()+720*60*60*1e3).toISOString();if(this.#e.db.prepare(`SELECT id FROM syntheses WHERE scope = ?`).get(e)!==void 0)this.#e.db.prepare(`UPDATE syntheses
|
|
229
|
+
`;return this.#e.db.prepare(l).all(...o).map(e=>({...M(e,[]),cosineSim:e.cosine_sim}))}},Me=class{#e;#t;#n;constructor(e,t,n){this.#e=e,this.#t=t,this.#n=n}async query(e){return Z(e,{adapter:new je(this.#e),repo:this.#n,embedder:this.#t})}};function Q(e,t){let n=t.repo.stats(e.projectHash);return e.synthesis!==void 0&&e.synthesis.length>0?{stats:n.byType,pinnedGlobal:[],pinnedProject:[],synthesis:e.synthesis}:{stats:n.byType,pinnedGlobal:t.repo.listPinnedGlobal(),pinnedProject:t.repo.listPinnedForProject(e.projectHash)}}function Ne(){return[...v]}var Pe=class{#e;constructor(e){this.#e=e}getSessionContext(e,t){return Q({projectHash:e,synthesis:t},{repo:this.#e})}},Fe=class{#e;#t;#n;#r=new Set;#i=new Map;#a=!1;#o;#s=new Map;constructor(e,t,n){this.#e=e,this.#t=t,this.#n=n}async init(){this.#e.clearStaleInFlight(this.#t.inFlightTimeoutMs??12e4),this.#e.expireStale();let e=this.#e.getExpiredOrDirtyScopes();for(let{scope:t}of e)this.#r.add(t);this.#a=!0,await this.#l()}shutdown(){this.#a=!1,this.#o!==void 0&&(clearTimeout(this.#o),this.#o=void 0);let e=[...this.#s.values()];return e.length===0?Promise.resolve():Promise.race([Promise.allSettled(e).then(()=>void 0),new Promise(e=>setTimeout(e,5e3))])}markDirty(e){this.#r.add(e)}#c(){if(!this.#a)return;let e=this.#t.debounceMs??45e3;this.#o=setTimeout(()=>{this.#l()},e)}async#l(){let e=[...this.#r];for(let t of e){let e=this.#t.inFlightTimeoutMs??12e4,n=this.#e.getSynthesis(t);if(n?.inFlightSince!==null&&n?.inFlightSince!==void 0){if(Date.now()-new Date(n.inFlightSince).getTime()<e)continue;this.#e.clearInFlight(t)}this.#r.delete(t);let r=this.#u(t).finally(()=>{this.#s.delete(t)});this.#s.set(t,r)}this.#c()}async#u(e){this.#e.markInFlight(e);try{let t=e===`global`?void 0:e,n=await this.#n.run(e,t),r=this.#e.computeSourceMemoryHash(e);this.#e.saveSynthesis(e,n,r),this.#i.delete(e)}catch(t){let n=(this.#i.get(e)??0)+1;this.#i.set(e,n);let r=Math.min(n,5),i=(this.#t.debounceMs??45e3)*r;process.stderr.write(`membank synthesis: error for scope=${e} failures=${n} backoff=${i}ms: ${t instanceof Error?t.message:String(t)}\n`),setTimeout(()=>{this.#r.add(e)},i),this.#e.clearInFlight(e)}}};async function Ie(e,t){let n=e===`global`?void 0:e;t.synthRepo.markInFlight(e);try{let[r,i]=await Promise.all([t.agentRunner.run(e,n),Promise.resolve(t.synthRepo.computeSourceMemoryHash(e))]);return t.synthRepo.saveSynthesis(e,r,i),r}catch(n){throw t.synthRepo.clearInFlight(e),n}}var Le=class{#e;constructor(e,t){this.#e=e}async run(e,t){let n=(0,h.createSdkMcpServer)({name:`membank-synthesis-tools`,version:`1.0.0`,tools:[(0,h.tool)(`query_memory`,`Search memories by semantic similarity`,{query:d.z.string().describe(`Search text`),limit:d.z.number().optional().describe(`Maximum results to return`),global:d.z.boolean().optional().describe(`Query global memories only when true, otherwise current project scope`)},async({query:e,limit:n,global:r})=>({content:[{type:`text`,text:await this.#e.queryMemory({query:e,limit:n,global:r,projectHash:t})}]}),{annotations:{readOnlyHint:!0}}),(0,h.tool)(`get_memory_summary`,`Returns aggregate stats: total memories, counts by type, pinned count, review queue size`,{},async()=>({content:[{type:`text`,text:await this.#e.getMemorySummary()}]}),{annotations:{readOnlyHint:!0}})]}),r=`Synthesize the memories for ${e===`global`?`global (across all projects)`:`project scope: ${e}`}. Use get_memory_summary first to understand the overall state, then use query_memory to retrieve relevant memories (query with broad terms like "preferences", "corrections", "decisions", "key facts"). After gathering information, produce a concise synthesis of the most important things to remember about this user. Output only the synthesis text — no preamble, no metadata.`,i=Date.now(),a=Object.fromEntries(Object.entries(process.env).filter(e=>e[1]!==void 0)),o=(0,h.query)({prompt:r,options:{model:`claude-haiku-4-5-20251001`,systemPrompt:`You are a memory synthesizer. Your job is to read the user's stored memories and produce a concise, well-structured summary of what's most important to remember about this user — their preferences, corrections, decisions, and key facts. Pinned memories are higher fidelity and should be weighted more heavily. Exclude transient or ephemeral details. Output plain text suitable for injection into an LLM context window. Be concise — target 200-400 words.`,mcpServers:{"membank-synthesis-tools":n},allowedTools:[`mcp__membank-synthesis-tools__query_memory`,`mcp__membank-synthesis-tools__get_memory_summary`],disallowedTools:[`mcp__membank__*`],settingSources:[],permissionMode:`bypassPermissions`,env:a}}),s=``;for await(let e of o)if(e.type===`result`)if(e.subtype===`success`)s=e.result;else{let t=`errors`in e&&Array.isArray(e.errors)?`: ${e.errors.join(`; `)}`:``;throw Error(`Synthesis agent failed: ${e.subtype}${t}`)}let c=Date.now()-i;if(process.stderr.write(`membank synthesis: scope=${e} duration=${c}ms\n`),s===``)throw Error(`Synthesis agent returned empty result`);return s}};function Re(e,t){return new Le(e,t)}function $(e){return k.parse({id:e.id,scope:e.scope,content:e.content,sourceMemoryHash:e.source_memory_hash,synthesizedAt:e.synthesized_at,expiresAt:e.expires_at,inFlightSince:e.in_flight_since,createdAt:e.created_at,updatedAt:e.updated_at})}var ze=class{#e;constructor(e){this.#e=e}saveSynthesis(e,t,n){let r=new Date().toISOString(),i=new Date(Date.now()+720*60*60*1e3).toISOString();if(this.#e.db.prepare(`SELECT id FROM syntheses WHERE scope = ?`).get(e)!==void 0)this.#e.db.prepare(`UPDATE syntheses
|
|
211
230
|
SET content = ?, source_memory_hash = ?, synthesized_at = ?, expires_at = ?,
|
|
212
231
|
in_flight_since = NULL, updated_at = ?
|
|
213
232
|
WHERE scope = ?`).run(t,n,r,i,r,e);else{let a=(0,g.randomUUID)();this.#e.db.prepare(`INSERT INTO syntheses
|
package/dist/index.d.cts
CHANGED
|
@@ -350,12 +350,12 @@ interface MemoryRepository {
|
|
|
350
350
|
}): Memory[];
|
|
351
351
|
listPinnedGlobal(): Memory[];
|
|
352
352
|
listPinnedForProject(projectHash: string): Memory[];
|
|
353
|
-
listFlagged(): Memory[];
|
|
353
|
+
listFlagged(projectHash?: string): Memory[];
|
|
354
354
|
listReviewEvents(memoryId: string, opts?: {
|
|
355
355
|
unresolvedOnly?: boolean;
|
|
356
356
|
}): ReviewEvent[];
|
|
357
|
-
getPinnedCharCount(): number;
|
|
358
|
-
stats(): StatsResult;
|
|
357
|
+
getPinnedCharCount(projectHash?: string): number;
|
|
358
|
+
stats(projectHash?: string): StatsResult;
|
|
359
359
|
create(opts: CreateMemoryOpts): Memory;
|
|
360
360
|
overwrite(id: string, content: string, embedding: Float32Array): Memory;
|
|
361
361
|
update(id: string, patch: MemoryPatch, embedding?: Float32Array): Memory;
|
|
@@ -528,14 +528,14 @@ declare class SqliteMemoryRepository implements MemoryRepository {
|
|
|
528
528
|
}): Memory[];
|
|
529
529
|
listPinnedGlobal(): Memory[];
|
|
530
530
|
listPinnedForProject(projectHash: string): Memory[];
|
|
531
|
-
listFlagged(): Memory[];
|
|
531
|
+
listFlagged(projectHash?: string): Memory[];
|
|
532
532
|
listReviewEvents(memoryId: string, opts?: {
|
|
533
533
|
unresolvedOnly?: boolean;
|
|
534
534
|
}): ReviewEvent[];
|
|
535
535
|
createReviewEvent(opts: CreateReviewEventOpts): void;
|
|
536
536
|
resolveReviewEvents(memoryId: string): void;
|
|
537
|
-
getPinnedCharCount(): number;
|
|
538
|
-
stats(): StatsResult;
|
|
537
|
+
getPinnedCharCount(projectHash?: string): number;
|
|
538
|
+
stats(projectHash?: string): StatsResult;
|
|
539
539
|
setPin(id: string, pinned: boolean): Memory;
|
|
540
540
|
incrementAccessCount(id: string): void;
|
|
541
541
|
exportAll(): MemoryExportRecord[];
|
package/dist/index.d.mts
CHANGED
|
@@ -350,12 +350,12 @@ interface MemoryRepository {
|
|
|
350
350
|
}): Memory[];
|
|
351
351
|
listPinnedGlobal(): Memory[];
|
|
352
352
|
listPinnedForProject(projectHash: string): Memory[];
|
|
353
|
-
listFlagged(): Memory[];
|
|
353
|
+
listFlagged(projectHash?: string): Memory[];
|
|
354
354
|
listReviewEvents(memoryId: string, opts?: {
|
|
355
355
|
unresolvedOnly?: boolean;
|
|
356
356
|
}): ReviewEvent[];
|
|
357
|
-
getPinnedCharCount(): number;
|
|
358
|
-
stats(): StatsResult;
|
|
357
|
+
getPinnedCharCount(projectHash?: string): number;
|
|
358
|
+
stats(projectHash?: string): StatsResult;
|
|
359
359
|
create(opts: CreateMemoryOpts): Memory;
|
|
360
360
|
overwrite(id: string, content: string, embedding: Float32Array): Memory;
|
|
361
361
|
update(id: string, patch: MemoryPatch, embedding?: Float32Array): Memory;
|
|
@@ -528,14 +528,14 @@ declare class SqliteMemoryRepository implements MemoryRepository {
|
|
|
528
528
|
}): Memory[];
|
|
529
529
|
listPinnedGlobal(): Memory[];
|
|
530
530
|
listPinnedForProject(projectHash: string): Memory[];
|
|
531
|
-
listFlagged(): Memory[];
|
|
531
|
+
listFlagged(projectHash?: string): Memory[];
|
|
532
532
|
listReviewEvents(memoryId: string, opts?: {
|
|
533
533
|
unresolvedOnly?: boolean;
|
|
534
534
|
}): ReviewEvent[];
|
|
535
535
|
createReviewEvent(opts: CreateReviewEventOpts): void;
|
|
536
536
|
resolveReviewEvents(memoryId: string): void;
|
|
537
|
-
getPinnedCharCount(): number;
|
|
538
|
-
stats(): StatsResult;
|
|
537
|
+
getPinnedCharCount(projectHash?: string): number;
|
|
538
|
+
stats(projectHash?: string): StatsResult;
|
|
539
539
|
setPin(id: string, pinned: boolean): Memory;
|
|
540
540
|
incrementAccessCount(id: string): void;
|
|
541
541
|
exportAll(): MemoryExportRecord[];
|
package/dist/index.mjs
CHANGED
|
@@ -185,20 +185,39 @@ CREATE INDEX IF NOT EXISTS idx_extraction_runs_status
|
|
|
185
185
|
JOIN projects p ON p.id = mp.project_id
|
|
186
186
|
WHERE m.type = ? AND p.scope_hash = ?
|
|
187
187
|
ORDER BY similarity DESC LIMIT 1`).get(r,t,n),i===void 0?[]:[{id:i.id,similarity:i.similarity}]}create(e){let{id:t,content:n,type:r,tags:i,sourceHarness:a,embedding:o,projectScope:s}=e,c=new Date().toISOString(),l=Buffer.from(o.buffer);if(this.#e.db.prepare(`INSERT INTO memories (id, content, type, tags, source, access_count, pinned, created_at, updated_at)
|
|
188
|
-
VALUES (?, ?, ?, ?, ?, 0, 0, ?, ?)`).run(t,n,r,JSON.stringify(i),a,c,c),this.#e.db.prepare(`INSERT INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`).run(l,t),s!==void 0){let e=this.#t.upsertByHash(s.hash,s.name);this.#t.addAssociation(t,e.id)}return j(k.parse(this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(t)),this.#t.getProjectsForMemories([t]).get(t)??[])}overwrite(e,t,n){let r=new Date().toISOString(),i=Buffer.from(n.buffer);this.#e.db.prepare(`UPDATE memories SET content = ?, updated_at = ? WHERE id = ?`).run(t,r,e);let a=this.#e.db.prepare(`SELECT rowid FROM memories WHERE id = ?`).get(e)?.rowid;a!==void 0&&this.#e.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(i,a);let o=k.parse(this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(e)),s=this.#t.getProjectsForMemories([e]),c=this.#
|
|
188
|
+
VALUES (?, ?, ?, ?, ?, 0, 0, ?, ?)`).run(t,n,r,JSON.stringify(i),a,c,c),this.#e.db.prepare(`INSERT INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`).run(l,t),s!==void 0){let e=this.#t.upsertByHash(s.hash,s.name);this.#t.addAssociation(t,e.id)}return j(k.parse(this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(t)),this.#t.getProjectsForMemories([t]).get(t)??[])}overwrite(e,t,n){let r=new Date().toISOString(),i=Buffer.from(n.buffer);this.#e.db.prepare(`UPDATE memories SET content = ?, updated_at = ? WHERE id = ?`).run(t,r,e);let a=this.#e.db.prepare(`SELECT rowid FROM memories WHERE id = ?`).get(e)?.rowid;a!==void 0&&this.#e.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(i,a);let o=k.parse(this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(e)),s=this.#t.getProjectsForMemories([e]),c=this.#r([e]);return j(o,s.get(e)??[],c.get(e)??[])}findById(e){let t=this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(e);if(t===void 0)return;let n=this.#t.getProjectsForMemories([e]),r=this.#r([e]);return j(t,n.get(e)??[],r.get(e)??[])}update(e,t,n){let{content:r,tags:i,type:a}=D.parse(t),o=this.#e.db.prepare(`SELECT m.rowid, m.* FROM memories m WHERE m.id = ?`).get(e);if(o===void 0)throw Error(`Memory not found: ${e}`);let s=new Date().toISOString(),c=[`updated_at = ?`],l=[s];if(r!==void 0&&(c.push(`content = ?`),l.push(r)),i!==void 0&&(c.push(`tags = ?`),l.push(JSON.stringify(i))),a!==void 0&&(c.push(`type = ?`),l.push(a)),l.push(e),this.#e.db.prepare(`UPDATE memories SET ${c.join(`, `)} WHERE id = ?`).run(...l),n!==void 0){let e=Buffer.from(n.buffer);this.#e.db.prepare(`UPDATE embeddings SET embedding = ? WHERE rowid = ?`).run(e,o.rowid)}let u=k.parse(this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(e)),d=this.#t.getProjectsForMemories([e]),f=this.#r([e]);return j(u,d.get(e)??[],f.get(e)??[])}delete(e){let t=this.#e.db.prepare(`SELECT rowid FROM memories WHERE id = ?`).get(e);t!==void 0&&this.#e.db.prepare(`DELETE FROM embeddings WHERE rowid = ?`).run(t.rowid),this.#e.db.prepare(`DELETE FROM memory_projects WHERE memory_id = ?`).run(e),this.#e.db.prepare(`DELETE FROM memories WHERE id = ?`).run(e)}list(e){let t=[],n=[];e?.type!==void 0&&(t.push(`m.type = ?`),n.push(e.type)),e?.pinned===!0&&t.push(`m.pinned = 1`),e?.needsReview===!0&&t.push(`EXISTS (SELECT 1 FROM memory_review_events e WHERE e.memory_id = m.id AND e.resolved_at IS NULL)`),e?.projectId===`global`?t.push(`m.id NOT IN (SELECT memory_id FROM memory_projects)`):e?.projectId!==void 0&&(t.push(`m.id IN (SELECT memory_id FROM memory_projects WHERE project_id = ?)`),n.push(e.projectId));let r=t.length>0?`WHERE ${t.join(` AND `)}`:``,i=this.#e.db.prepare(`SELECT m.* FROM memories m ${r} ORDER BY m.created_at DESC`).all(...n);if(i.length===0)return[];let a=i.map(e=>e.id),o=this.#t.getProjectsForMemories(a),s=this.#r(a);return i.map(e=>j(e,o.get(e.id)??[],s.get(e.id)??[]))}listPinnedGlobal(){return this.#e.db.prepare(`SELECT * FROM memories
|
|
189
189
|
WHERE id NOT IN (SELECT memory_id FROM memory_projects)
|
|
190
190
|
AND pinned = 1`).all().map(e=>j(e,[]))}listPinnedForProject(e){return this.#e.db.prepare(`SELECT m.* FROM memories m
|
|
191
191
|
JOIN memory_projects mp ON mp.memory_id = m.id
|
|
192
192
|
JOIN projects p ON p.id = mp.project_id
|
|
193
|
-
WHERE p.scope_hash = ? AND m.pinned = 1`).all(e).map(e=>j(e,[]))}listFlagged(){let e
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
193
|
+
WHERE p.scope_hash = ? AND m.pinned = 1`).all(e).map(e=>j(e,[]))}listFlagged(e){let t;if(t=e===void 0?this.#e.db.prepare(`SELECT * FROM memories
|
|
194
|
+
WHERE EXISTS (
|
|
195
|
+
SELECT 1 FROM memory_review_events e
|
|
196
|
+
WHERE e.memory_id = memories.id AND e.resolved_at IS NULL
|
|
197
|
+
)
|
|
198
|
+
ORDER BY created_at DESC`).all():this.#e.db.prepare(`SELECT * FROM memories
|
|
199
|
+
WHERE EXISTS (
|
|
200
|
+
SELECT 1 FROM memory_review_events e
|
|
201
|
+
WHERE e.memory_id = memories.id AND e.resolved_at IS NULL
|
|
202
|
+
)
|
|
203
|
+
AND ${this.#n()}
|
|
204
|
+
ORDER BY created_at DESC`).all(e),t.length===0)return[];let n=t.map(e=>e.id),r=this.#t.getProjectsForMemories(n),i=this.#r(n,{unresolvedOnly:!0});return t.map(e=>j(e,r.get(e.id)??[],i.get(e.id)??[]))}listReviewEvents(e,t){let n=t?.unresolvedOnly===!0?`WHERE memory_id = ? AND resolved_at IS NULL`:`WHERE memory_id = ?`;return this.#e.db.prepare(`SELECT * FROM memory_review_events ${n} ORDER BY created_at DESC`).all(e).map(e=>M(C.parse(e)))}createReviewEvent(e){let t=new Date().toISOString();this.#e.db.prepare(`INSERT INTO memory_review_events
|
|
199
205
|
(id, memory_id, conflicting_memory_id, similarity, conflict_content_snapshot, reason, created_at)
|
|
200
|
-
VALUES (?, ?, ?, ?, ?, 'similarity_dedup', ?)`).run(g(),e.memoryId,e.conflictingMemoryId,e.similarity,e.conflictContentSnapshot,t)}resolveReviewEvents(e){let t=new Date().toISOString();this.#e.db.prepare(`UPDATE memory_review_events SET resolved_at = ? WHERE memory_id = ? AND resolved_at IS NULL`).run(t,e)}getPinnedCharCount(){return(this.#e.db.prepare(`SELECT COALESCE(SUM(LENGTH(content)), 0) as total FROM memories WHERE pinned = 1`).get()??{total:0}).total
|
|
201
|
-
|
|
206
|
+
VALUES (?, ?, ?, ?, ?, 'similarity_dedup', ?)`).run(g(),e.memoryId,e.conflictingMemoryId,e.similarity,e.conflictContentSnapshot,t)}resolveReviewEvents(e){let t=new Date().toISOString();this.#e.db.prepare(`UPDATE memory_review_events SET resolved_at = ? WHERE memory_id = ? AND resolved_at IS NULL`).run(t,e)}getPinnedCharCount(e){return e===void 0?(this.#e.db.prepare(`SELECT COALESCE(SUM(LENGTH(content)), 0) as total FROM memories WHERE pinned = 1`).get()??{total:0}).total:(this.#e.db.prepare(`SELECT COALESCE(SUM(LENGTH(content)), 0) as total FROM memories
|
|
207
|
+
WHERE pinned = 1 AND ${this.#n()}`).get(e)??{total:0}).total}stats(e){let t=Object.fromEntries(_.map(e=>[e,0]));if(e!==void 0){let n=this.#e.db.prepare(`SELECT type, COUNT(*) as count FROM memories
|
|
208
|
+
WHERE ${this.#n()}
|
|
209
|
+
GROUP BY type`).all(e);for(let e of n){let n=v.safeParse(e.type);n.success&&(t[n.data]=e.count)}let r=this.#e.db.prepare(`SELECT COUNT(*) as total, SUM(pinned) as pinned,
|
|
210
|
+
COALESCE(SUM(CASE WHEN pinned = 1 THEN LENGTH(content) ELSE 0 END), 0) as pinBudgetChars
|
|
211
|
+
FROM memories WHERE ${this.#n()}`).get(e)??{total:0,pinned:0,pinBudgetChars:0},i=this.#e.db.prepare(`SELECT COUNT(DISTINCT e.memory_id) as needsReview
|
|
212
|
+
FROM memory_review_events e
|
|
213
|
+
JOIN memories m ON m.id = e.memory_id
|
|
214
|
+
WHERE e.resolved_at IS NULL
|
|
215
|
+
AND ${this.#n(`m`)}`).get(e)??{needsReview:0};return{byType:t,total:r.total,pinned:r.pinned??0,needsReview:i.needsReview,pinBudgetChars:r.pinBudgetChars}}let n=this.#e.db.prepare(`SELECT type, COUNT(*) as count FROM memories GROUP BY type`).all();for(let e of n){let n=v.safeParse(e.type);n.success&&(t[n.data]=e.count)}let r=this.#e.db.prepare(`SELECT COUNT(*) as total, SUM(pinned) as pinned FROM memories`).get()??{total:0,pinned:0},i=this.#e.db.prepare(`SELECT COUNT(DISTINCT memory_id) as needsReview FROM memory_review_events WHERE resolved_at IS NULL`).get()??{needsReview:0};return{byType:t,total:r.total,pinned:r.pinned??0,needsReview:i.needsReview,pinBudgetChars:this.getPinnedCharCount()}}setPin(e,t){if(this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(e)===void 0)throw Error(`Memory not found: ${e}`);let n=new Date().toISOString();this.#e.db.prepare(`UPDATE memories SET pinned = ?, updated_at = ? WHERE id = ?`).run(+!!t,n,e);let r=k.parse(this.#e.db.prepare(`SELECT * FROM memories WHERE id = ?`).get(e)),i=this.#t.getProjectsForMemories([e]),a=this.#r([e]);return j(r,i.get(e)??[],a.get(e)??[])}incrementAccessCount(e){this.#e.db.prepare(`UPDATE memories SET access_count = access_count + 1 WHERE id = ?`).run(e)}exportAll(){return this.#e.db.prepare(`SELECT m.*, e.embedding FROM memories m LEFT JOIN embeddings e ON e.rowid = m.rowid ORDER BY m.created_at DESC`).all().map(e=>({id:e.id,content:e.content,type:v.parse(e.type),tags:y.parse(JSON.parse(e.tags)),sourceHarness:e.source,accessCount:e.access_count,pinned:e.pinned!==0,createdAt:e.created_at,updatedAt:e.updated_at,embedding:e.embedding===null?null:new Float32Array(e.embedding.buffer,e.embedding.byteOffset,e.embedding.byteLength/4)}))}importAll(e){let t=this.#e.db.prepare(`INSERT OR REPLACE INTO memories (id, content, type, tags, source, access_count, pinned, created_at, updated_at)
|
|
216
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`),n=this.#e.db.prepare(`INSERT OR REPLACE INTO embeddings (rowid, embedding) SELECT m.rowid, ? FROM memories m WHERE m.id = ?`);this.#e.db.transaction(()=>{for(let r of e)t.run(r.id,r.content,r.type,JSON.stringify(r.tags),r.sourceHarness,r.accessCount,+!!r.pinned,r.createdAt,r.updatedAt),r.embedding!==null&&n.run(Buffer.from(r.embedding.buffer,r.embedding.byteOffset,r.embedding.byteLength),r.id)})()}#n(e=`memories`){return`(${e}.id NOT IN (SELECT memory_id FROM memory_projects)
|
|
217
|
+
OR EXISTS (
|
|
218
|
+
SELECT 1 FROM memory_projects mp JOIN projects p ON p.id = mp.project_id
|
|
219
|
+
WHERE mp.memory_id = ${e}.id AND p.scope_hash = ?
|
|
220
|
+
))`}#r(e,t){if(e.length===0)return new Map;let n=e.map(()=>`?`).join(`, `),r=t?.unresolvedOnly===!0?`AND resolved_at IS NULL`:``,i=this.#e.db.prepare(`SELECT * FROM memory_review_events
|
|
202
221
|
WHERE memory_id IN (${n}) ${r}
|
|
203
222
|
ORDER BY created_at DESC`).all(...e),a=new Map;for(let e of i){let t=M(C.parse(e)),n=a.get(t.memoryId)??[];n.push(t),a.set(t.memoryId,n)}return a}};function Se(e,t){return new q(e,t)}const J=ne(te);function Y(e){return h(`sha256`).update(e).digest(`hex`).slice(0,16)}async function X(){try{let{stdout:e}=await J(`git`,[`remote`,`get-url`,`origin`]),t=e.trim();if(t){let e=Y(t);return{hash:e,name:t.split(`/`).pop()?.replace(/\.git$/,``)??e.slice(0,8)}}}catch{}let e=process.cwd(),t=Y(e);return{hash:t,name:e.split(/[/\\]/).filter(Boolean).pop()??t.slice(0,8)}}async function Ce(){try{let{stdout:e}=await J(`git`,[`remote`,`get-url`,`origin`]),t=e.trim();if(t)return Y(t)}catch{}return Y(process.cwd())}const we=[{name:`scope-to-projects`,description:`Rename the auto-migrated project for the current directory from its generic hash-derived name to the resolved repo/directory name.`}];async function Te(e){let t=await X(),n=e.getByHash(t.hash);if(n===void 0)return null;let r=n.name,i=e.countMemories(n.id);return e.rename(n.id,t.name),{migration:`scope-to-projects`,oldName:r,newName:t.name,memoryCount:i}}var Ee=class{#e;constructor(e){this.#e=e}upsertByHash(e,t){if(!/^[0-9a-f]{16}$/.test(e))throw Error(`Invalid scope hash "${e}": expected 16 lowercase hex characters`);let n=new Date().toISOString(),r=g();return this.#e.db.prepare(`INSERT OR IGNORE INTO projects (id, name, scope_hash, created_at, updated_at) VALUES (?, ?, ?, ?, ?)`).run(r,t,e,n,n),N(A.parse(this.#e.db.prepare(`SELECT * FROM projects WHERE scope_hash = ?`).get(e)))}rename(e,t){let n=new Date().toISOString();this.#e.db.prepare(`UPDATE projects SET name = ?, updated_at = ? WHERE id = ?`).run(t,n,e);let r=this.#e.db.prepare(`SELECT * FROM projects WHERE id = ?`).get(e);if(r===void 0)throw Error(`Project not found: ${e}`);return N(r)}list(){return this.#e.db.prepare(`SELECT * FROM projects ORDER BY name ASC`).all().map(N)}getByHash(e){let t=this.#e.db.prepare(`SELECT * FROM projects WHERE scope_hash = ?`).get(e);return t===void 0?void 0:N(t)}getByName(e){let t=this.#e.db.prepare(`SELECT * FROM projects WHERE name = ? LIMIT 1`).get(e);return t===void 0?void 0:N(t)}addAssociation(e,t){this.#e.db.prepare(`INSERT OR IGNORE INTO memory_projects (memory_id, project_id) VALUES (?, ?)`).run(e,t)}removeAssociation(e,t){this.#e.db.prepare(`DELETE FROM memory_projects WHERE memory_id = ? AND project_id = ?`).run(e,t)}countMemories(e){return this.#e.db.prepare(`SELECT COUNT(*) AS count FROM memory_projects WHERE project_id = ?`).get(e)?.count??0}getProjectsForMemories(e){if(e.length===0)return new Map;let t=e.map(()=>`?`).join(`,`),n=this.#e.db.prepare(`SELECT p.*, mp.memory_id FROM projects p
|
|
204
223
|
JOIN memory_projects mp ON mp.project_id = p.id
|
|
@@ -207,7 +226,7 @@ CREATE INDEX IF NOT EXISTS idx_extraction_runs_status
|
|
|
207
226
|
FROM memories m JOIN embeddings e ON e.rowid = m.rowid
|
|
208
227
|
${s}
|
|
209
228
|
${c}
|
|
210
|
-
`;return this.#e.db.prepare(l).all(...o).map(e=>({...j(e,[]),cosineSim:e.cosine_sim}))}},je=class{#e;#t;#n;constructor(e,t,n){this.#e=e,this.#t=t,this.#n=n}async query(e){return Z(e,{adapter:new Ae(this.#e),repo:this.#n,embedder:this.#t})}};function Q(e,t){let n=t.repo.stats();return e.synthesis!==void 0&&e.synthesis.length>0?{stats:n.byType,pinnedGlobal:[],pinnedProject:[],synthesis:e.synthesis}:{stats:n.byType,pinnedGlobal:t.repo.listPinnedGlobal(),pinnedProject:t.repo.listPinnedForProject(e.projectHash)}}function Me(){return[..._]}var Ne=class{#e;constructor(e){this.#e=e}getSessionContext(e,t){return Q({projectHash:e,synthesis:t},{repo:this.#e})}},Pe=class{#e;#t;#n;#r=new Set;#i=new Map;#a=!1;#o;#s=new Map;constructor(e,t,n){this.#e=e,this.#t=t,this.#n=n}async init(){this.#e.clearStaleInFlight(this.#t.inFlightTimeoutMs??12e4),this.#e.expireStale();let e=this.#e.getExpiredOrDirtyScopes();for(let{scope:t}of e)this.#r.add(t);this.#a=!0,await this.#l()}shutdown(){this.#a=!1,this.#o!==void 0&&(clearTimeout(this.#o),this.#o=void 0);let e=[...this.#s.values()];return e.length===0?Promise.resolve():Promise.race([Promise.allSettled(e).then(()=>void 0),new Promise(e=>setTimeout(e,5e3))])}markDirty(e){this.#r.add(e)}#c(){if(!this.#a)return;let e=this.#t.debounceMs??45e3;this.#o=setTimeout(()=>{this.#l()},e)}async#l(){let e=[...this.#r];for(let t of e){let e=this.#t.inFlightTimeoutMs??12e4,n=this.#e.getSynthesis(t);if(n?.inFlightSince!==null&&n?.inFlightSince!==void 0){if(Date.now()-new Date(n.inFlightSince).getTime()<e)continue;this.#e.clearInFlight(t)}this.#r.delete(t);let r=this.#u(t).finally(()=>{this.#s.delete(t)});this.#s.set(t,r)}this.#c()}async#u(e){this.#e.markInFlight(e);try{let t=e===`global`?void 0:e,n=await this.#n.run(e,t),r=this.#e.computeSourceMemoryHash(e);this.#e.saveSynthesis(e,n,r),this.#i.delete(e)}catch(t){let n=(this.#i.get(e)??0)+1;this.#i.set(e,n);let r=Math.min(n,5),i=(this.#t.debounceMs??45e3)*r;process.stderr.write(`membank synthesis: error for scope=${e} failures=${n} backoff=${i}ms: ${t instanceof Error?t.message:String(t)}\n`),setTimeout(()=>{this.#r.add(e)},i),this.#e.clearInFlight(e)}}};async function Fe(e,t){let n=e===`global`?void 0:e;t.synthRepo.markInFlight(e);try{let[r,i]=await Promise.all([t.agentRunner.run(e,n),Promise.resolve(t.synthRepo.computeSourceMemoryHash(e))]);return t.synthRepo.saveSynthesis(e,r,i),r}catch(n){throw t.synthRepo.clearInFlight(e),n}}var Ie=class{#e;constructor(e,t){this.#e=e}async run(e,t){let n=f({name:`membank-synthesis-tools`,version:`1.0.0`,tools:[m(`query_memory`,`Search memories by semantic similarity`,{query:s.string().describe(`Search text`),limit:s.number().optional().describe(`Maximum results to return`),global:s.boolean().optional().describe(`Query global memories only when true, otherwise current project scope`)},async({query:e,limit:n,global:r})=>({content:[{type:`text`,text:await this.#e.queryMemory({query:e,limit:n,global:r,projectHash:t})}]}),{annotations:{readOnlyHint:!0}}),m(`get_memory_summary`,`Returns aggregate stats: total memories, counts by type, pinned count, review queue size`,{},async()=>({content:[{type:`text`,text:await this.#e.getMemorySummary()}]}),{annotations:{readOnlyHint:!0}})]}),r=`Synthesize the memories for ${e===`global`?`global (across all projects)`:`project scope: ${e}`}. Use get_memory_summary first to understand the overall state, then use query_memory to retrieve relevant memories (query with broad terms like "preferences", "corrections", "decisions", "key facts"). After gathering information, produce a concise synthesis of the most important things to remember about this user. Output only the synthesis text — no preamble, no metadata.`,i=Date.now(),a=Object.fromEntries(Object.entries(process.env).filter(e=>e[1]!==void 0)),o=p({prompt:r,options:{model:`claude-haiku-4-5-20251001`,systemPrompt:`You are a memory synthesizer. Your job is to read the user's stored memories and produce a concise, well-structured summary of what's most important to remember about this user — their preferences, corrections, decisions, and key facts. Pinned memories are higher fidelity and should be weighted more heavily. Exclude transient or ephemeral details. Output plain text suitable for injection into an LLM context window. Be concise — target 200-400 words.`,mcpServers:{"membank-synthesis-tools":n},allowedTools:[`mcp__membank-synthesis-tools__query_memory`,`mcp__membank-synthesis-tools__get_memory_summary`],disallowedTools:[`mcp__membank__*`],settingSources:[],permissionMode:`bypassPermissions`,env:a}}),c=``;for await(let e of o)if(e.type===`result`)if(e.subtype===`success`)c=e.result;else{let t=`errors`in e&&Array.isArray(e.errors)?`: ${e.errors.join(`; `)}`:``;throw Error(`Synthesis agent failed: ${e.subtype}${t}`)}let l=Date.now()-i;if(process.stderr.write(`membank synthesis: scope=${e} duration=${l}ms\n`),c===``)throw Error(`Synthesis agent returned empty result`);return c}};function Le(e,t){return new Ie(e,t)}function $(e){return O.parse({id:e.id,scope:e.scope,content:e.content,sourceMemoryHash:e.source_memory_hash,synthesizedAt:e.synthesized_at,expiresAt:e.expires_at,inFlightSince:e.in_flight_since,createdAt:e.created_at,updatedAt:e.updated_at})}var Re=class{#e;constructor(e){this.#e=e}saveSynthesis(e,t,n){let r=new Date().toISOString(),i=new Date(Date.now()+720*60*60*1e3).toISOString();if(this.#e.db.prepare(`SELECT id FROM syntheses WHERE scope = ?`).get(e)!==void 0)this.#e.db.prepare(`UPDATE syntheses
|
|
229
|
+
`;return this.#e.db.prepare(l).all(...o).map(e=>({...j(e,[]),cosineSim:e.cosine_sim}))}},je=class{#e;#t;#n;constructor(e,t,n){this.#e=e,this.#t=t,this.#n=n}async query(e){return Z(e,{adapter:new Ae(this.#e),repo:this.#n,embedder:this.#t})}};function Q(e,t){let n=t.repo.stats(e.projectHash);return e.synthesis!==void 0&&e.synthesis.length>0?{stats:n.byType,pinnedGlobal:[],pinnedProject:[],synthesis:e.synthesis}:{stats:n.byType,pinnedGlobal:t.repo.listPinnedGlobal(),pinnedProject:t.repo.listPinnedForProject(e.projectHash)}}function Me(){return[..._]}var Ne=class{#e;constructor(e){this.#e=e}getSessionContext(e,t){return Q({projectHash:e,synthesis:t},{repo:this.#e})}},Pe=class{#e;#t;#n;#r=new Set;#i=new Map;#a=!1;#o;#s=new Map;constructor(e,t,n){this.#e=e,this.#t=t,this.#n=n}async init(){this.#e.clearStaleInFlight(this.#t.inFlightTimeoutMs??12e4),this.#e.expireStale();let e=this.#e.getExpiredOrDirtyScopes();for(let{scope:t}of e)this.#r.add(t);this.#a=!0,await this.#l()}shutdown(){this.#a=!1,this.#o!==void 0&&(clearTimeout(this.#o),this.#o=void 0);let e=[...this.#s.values()];return e.length===0?Promise.resolve():Promise.race([Promise.allSettled(e).then(()=>void 0),new Promise(e=>setTimeout(e,5e3))])}markDirty(e){this.#r.add(e)}#c(){if(!this.#a)return;let e=this.#t.debounceMs??45e3;this.#o=setTimeout(()=>{this.#l()},e)}async#l(){let e=[...this.#r];for(let t of e){let e=this.#t.inFlightTimeoutMs??12e4,n=this.#e.getSynthesis(t);if(n?.inFlightSince!==null&&n?.inFlightSince!==void 0){if(Date.now()-new Date(n.inFlightSince).getTime()<e)continue;this.#e.clearInFlight(t)}this.#r.delete(t);let r=this.#u(t).finally(()=>{this.#s.delete(t)});this.#s.set(t,r)}this.#c()}async#u(e){this.#e.markInFlight(e);try{let t=e===`global`?void 0:e,n=await this.#n.run(e,t),r=this.#e.computeSourceMemoryHash(e);this.#e.saveSynthesis(e,n,r),this.#i.delete(e)}catch(t){let n=(this.#i.get(e)??0)+1;this.#i.set(e,n);let r=Math.min(n,5),i=(this.#t.debounceMs??45e3)*r;process.stderr.write(`membank synthesis: error for scope=${e} failures=${n} backoff=${i}ms: ${t instanceof Error?t.message:String(t)}\n`),setTimeout(()=>{this.#r.add(e)},i),this.#e.clearInFlight(e)}}};async function Fe(e,t){let n=e===`global`?void 0:e;t.synthRepo.markInFlight(e);try{let[r,i]=await Promise.all([t.agentRunner.run(e,n),Promise.resolve(t.synthRepo.computeSourceMemoryHash(e))]);return t.synthRepo.saveSynthesis(e,r,i),r}catch(n){throw t.synthRepo.clearInFlight(e),n}}var Ie=class{#e;constructor(e,t){this.#e=e}async run(e,t){let n=f({name:`membank-synthesis-tools`,version:`1.0.0`,tools:[m(`query_memory`,`Search memories by semantic similarity`,{query:s.string().describe(`Search text`),limit:s.number().optional().describe(`Maximum results to return`),global:s.boolean().optional().describe(`Query global memories only when true, otherwise current project scope`)},async({query:e,limit:n,global:r})=>({content:[{type:`text`,text:await this.#e.queryMemory({query:e,limit:n,global:r,projectHash:t})}]}),{annotations:{readOnlyHint:!0}}),m(`get_memory_summary`,`Returns aggregate stats: total memories, counts by type, pinned count, review queue size`,{},async()=>({content:[{type:`text`,text:await this.#e.getMemorySummary()}]}),{annotations:{readOnlyHint:!0}})]}),r=`Synthesize the memories for ${e===`global`?`global (across all projects)`:`project scope: ${e}`}. Use get_memory_summary first to understand the overall state, then use query_memory to retrieve relevant memories (query with broad terms like "preferences", "corrections", "decisions", "key facts"). After gathering information, produce a concise synthesis of the most important things to remember about this user. Output only the synthesis text — no preamble, no metadata.`,i=Date.now(),a=Object.fromEntries(Object.entries(process.env).filter(e=>e[1]!==void 0)),o=p({prompt:r,options:{model:`claude-haiku-4-5-20251001`,systemPrompt:`You are a memory synthesizer. Your job is to read the user's stored memories and produce a concise, well-structured summary of what's most important to remember about this user — their preferences, corrections, decisions, and key facts. Pinned memories are higher fidelity and should be weighted more heavily. Exclude transient or ephemeral details. Output plain text suitable for injection into an LLM context window. Be concise — target 200-400 words.`,mcpServers:{"membank-synthesis-tools":n},allowedTools:[`mcp__membank-synthesis-tools__query_memory`,`mcp__membank-synthesis-tools__get_memory_summary`],disallowedTools:[`mcp__membank__*`],settingSources:[],permissionMode:`bypassPermissions`,env:a}}),c=``;for await(let e of o)if(e.type===`result`)if(e.subtype===`success`)c=e.result;else{let t=`errors`in e&&Array.isArray(e.errors)?`: ${e.errors.join(`; `)}`:``;throw Error(`Synthesis agent failed: ${e.subtype}${t}`)}let l=Date.now()-i;if(process.stderr.write(`membank synthesis: scope=${e} duration=${l}ms\n`),c===``)throw Error(`Synthesis agent returned empty result`);return c}};function Le(e,t){return new Ie(e,t)}function $(e){return O.parse({id:e.id,scope:e.scope,content:e.content,sourceMemoryHash:e.source_memory_hash,synthesizedAt:e.synthesized_at,expiresAt:e.expires_at,inFlightSince:e.in_flight_since,createdAt:e.created_at,updatedAt:e.updated_at})}var Re=class{#e;constructor(e){this.#e=e}saveSynthesis(e,t,n){let r=new Date().toISOString(),i=new Date(Date.now()+720*60*60*1e3).toISOString();if(this.#e.db.prepare(`SELECT id FROM syntheses WHERE scope = ?`).get(e)!==void 0)this.#e.db.prepare(`UPDATE syntheses
|
|
211
230
|
SET content = ?, source_memory_hash = ?, synthesized_at = ?, expires_at = ?,
|
|
212
231
|
in_flight_since = NULL, updated_at = ?
|
|
213
232
|
WHERE scope = ?`).run(t,n,r,i,r,e);else{let a=g();this.#e.db.prepare(`INSERT INTO syntheses
|