wyrm-mcp 7.12.0 → 8.0.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/bridge.js CHANGED
@@ -1 +1 @@
1
- import{readFileSync as y,writeFileSync as O,readdirSync as M,existsSync as _}from"fs";import{createHash as q}from"crypto";import{join as h}from"path";import{homedir as R}from"os";import{MemoryArtifacts as T}from"./memory-artifacts.js";import{recordWrite as g}from"./receipts.js";import{buildRenderPlan as B,renderToDisk as C,renderForClient as F,makeRenderDeps as I}from"./render-target.js";import{makeBridgeDeps as x,sweepProject as E}from"./reverse-bridge.js";function W(){return process.env.WYRM_BRIDGE_CONFIG??h(R(),".wyrm","bridge.json")}function P(){return process.env.WYRM_BRIDGE_STATE??h(R(),".wyrm","bridge-state.json")}function ee(){try{return JSON.parse(y(W(),"utf8"))}catch{return null}}function N(){try{return JSON.parse(y(P(),"utf8"))}catch{return{}}}function D(r){try{O(P(),JSON.stringify(r,null,1))}catch{}}function H(r,n,t){const o=t??h(R(),".wyrm","remember-distill-state.json");if(!_(o))return 0;let d=0;try{const e=JSON.parse(y(o,"utf8"));for(const[c,i]of Object.entries(e)){const s=`${r}:${c}`;n[s]||(n[s]=i,d++)}}catch{}return d}const J=r=>q("sha1").update(r).digest("hex");function L(r,n,t,o,d={}){const e={source:t.name,type:"remember",stored:0,dropped:0,queued:0,rendered:0},c=t.windowDays&&t.windowDays>=1?t.windowDays:7,i=t.maxPerRun&&t.maxPerRun>=1?t.maxPerRun:80,s=r.prepare("SELECT id FROM projects WHERE path = ?").get(t.projectPath);if(!s)return e.note=`no project at ${t.projectPath}`,e;const u=new Date(Date.now()-c*864e5);let p=[];try{p=M(t.dir).filter(a=>/^today-\d{4}-\d{2}-\d{2}(\.done)?\.md$/.test(a)).map(a=>({f:a,date:a.match(/\d{4}-\d{2}-\d{2}/)[0]})).filter(({date:a})=>new Date(a+"T00:00:00")>=u).sort((a,l)=>l.date.localeCompare(a.date))}catch{return e.note=`cannot read ${t.dir}`,e}let m=0;for(const{f:a,date:l}of p){const j=`${t.name}:today-${l}`,w=new Set(o[j]??[]);let S="";try{S=y(h(t.dir,a),"utf8")}catch{continue}const b=S.split(/^## /m).slice(1).map(f=>"## "+f.trim());for(const f of b){if(m>=i)break;if(f.length<30)continue;const $=J(f);if(w.has($))continue;if(d.dryRun){m++,e.stored++;continue}const k=`[bridge:${t.name} ${l}] ${f}`.slice(0,2400),v=n.add(s.id,{kind:"pattern",problem:k,confidence:.75,needsReview:0,createdBy:`bridge:${t.name}`});g(r,{tool:"wyrm_bridge",outcome:"stored",refTable:"mem",refId:v.id,source:`bridge:${t.name}`,projectId:s.id}),w.add($),m++,e.stored++}o[j]=[...w]}return m>=i&&(e.note=`hit per-run cap ${i} \u2014 remainder next run`),e}async function G(r,n,t,o={}){const d={source:n.name,type:"render",stored:0,dropped:0,queued:0,rendered:0},e=r.prepare("SELECT id, name, path FROM projects WHERE path = ?").get(n.projectPath);if(!e)return d.note=`no project at ${n.projectPath}`,d;const c=n.clients??[],i=I(r);try{const s=B(i,e,t),u={"MEMORY.md":s.memoryMd};for(const m of c){const a=F(m,s.model,t);u[a.relPath]=a.block}const p=await E(x(r),{id:e.id,path:e.path},u,{dryRun:o.dryRun});d.queued+=p.added,p.added>0&&g(r,{tool:"wyrm_bridge",outcome:"queued",reason:`render guard \u2014 ${p.added} human edit(s) harvested to review before overwrite`,source:`bridge:${n.name}`,projectId:e.id})}catch{}if(!o.dryRun){const{writes:s}=C(i,e,t,{clients:c});d.rendered=s.filter(u=>u.action!=="skipped").length}return d}async function Y(r,n,t={}){const o={source:n.name,type:"reverse",stored:0,dropped:0,queued:0,rendered:0},d=r.prepare("SELECT id, path FROM projects WHERE path = ?").get(n.projectPath);if(!d)return o.note=`no project at ${n.projectPath}`,o;const e=await E(x(r),d,{},{dryRun:t.dryRun});return o.queued=e.added,o.dropped=e.skipped,e.added>0&&g(r,{tool:"wyrm_bridge",outcome:"queued",reason:`reverse sweep \u2014 ${e.added} native-file edit(s) queued for review`,source:`bridge:${n.name}`,projectId:d.id}),o}async function te(r,n,t,o={}){const d=new T(r),e=N(),c=[];for(const i of n.sources)o.only&&i.name!==o.only||(i.type==="remember"?(H(i.name,e)>0&&D(e),c.push(L(r,d,i,e,o))):i.type==="render"?c.push(await G(r,i,t,o)):c.push(await Y(r,i,o)));return o.dryRun||D(e),c}export{H as adoptLegacyDistillState,W as configPath,ee as loadConfig,N as loadState,te as runBridge,L as runRememberSource,G as runRenderSource,Y as runReverseSource,D as saveState,P as statePath};
1
+ import{readFileSync as h,writeFileSync as D,readdirSync as k,existsSync as F}from"fs";import{createHash as _}from"crypto";import{join as y}from"path";import{homedir as g}from"os";import{MemoryArtifacts as I}from"./memory-artifacts.js";import{recordWrite as R}from"./receipts.js";import{buildRenderPlan as L,renderToDisk as b,renderForClient as q,makeRenderDeps as A}from"./render-target.js";import{makeBridgeDeps as x,sweepProject as $}from"./reverse-bridge.js";function B(){return process.env.WYRM_BRIDGE_CONFIG??y(g(),".wyrm","bridge.json")}function C(){return process.env.WYRM_BRIDGE_STATE??y(g(),".wyrm","bridge-state.json")}function ee(){try{return JSON.parse(h(B(),"utf8"))}catch{return null}}function W(){try{return JSON.parse(h(C(),"utf8"))}catch{return{}}}function v(t){try{D(C(),JSON.stringify(t,null,1))}catch{}}function H(t,i,r){const n=r??y(g(),".wyrm","remember-distill-state.json");if(!F(n))return 0;let c=0;try{const e=JSON.parse(h(n,"utf8"));for(const[d,a]of Object.entries(e)){const s=`${t}:${d}`;i[s]||(i[s]=a,c++)}}catch{}return c}const N=t=>_("sha1").update(t).digest("hex");function J(t,i,r,n,c={}){const e={source:r.name,type:"remember",stored:0,dropped:0,queued:0,rendered:0},d=r.windowDays&&r.windowDays>=1?r.windowDays:7,a=r.maxPerRun&&r.maxPerRun>=1?r.maxPerRun:80,s=t.prepare("SELECT id FROM projects WHERE path = ?").get(r.projectPath);if(!s)return e.note=`no project at ${r.projectPath}`,e;const p=new Date(Date.now()-d*864e5);let u=[];try{u=k(r.dir).filter(o=>/^today-\d{4}-\d{2}-\d{2}(\.done)?\.md$/.test(o)).map(o=>({f:o,date:o.match(/\d{4}-\d{2}-\d{2}/)[0]})).filter(({date:o})=>new Date(o+"T00:00:00")>=p).sort((o,m)=>m.date.localeCompare(o.date))}catch{return e.note=`cannot read ${r.dir}`,e}let f=0;for(const{f:o,date:m}of u){const S=`${r.name}:today-${m}`,w=new Set(n[S]??[]);let E="";try{E=h(y(r.dir,o),"utf8")}catch{continue}const O=E.split(/^## /m).slice(1).map(l=>"## "+l.trim());for(const l of O){if(f>=a)break;if(l.length<30)continue;const j=N(l);if(w.has(j))continue;if(c.dryRun){f++,e.stored++;continue}const P=`[bridge:${r.name} ${m}] ${l}`.slice(0,2400),T=i.add(s.id,{kind:"pattern",problem:P,confidence:.75,needsReview:0,createdBy:`bridge:${r.name}`});R(t,{tool:"wyrm_bridge",outcome:"stored",refTable:"mem",refId:T.id,source:`bridge:${r.name}`,projectId:s.id}),w.add(j),f++,e.stored++}n[S]=[...w]}return f>=a&&(e.note=`hit per-run cap ${a} \u2014 remainder next run`),e}async function M(t,i,r,n={}){const c={source:i.name,type:"render",stored:0,dropped:0,queued:0,rendered:0},e=t.prepare("SELECT id, name, path FROM projects WHERE path = ?").get(i.projectPath);if(!e)return c.note=`no project at ${i.projectPath}`,c;const d=i.clients??[],a=A(t);try{const s=L(a,e,r),p={"MEMORY.md":s.memoryMd};for(const f of d){const o=q(f,s.model,r);p[o.relPath]=o.block}const u=await $(x(t),{id:e.id,path:e.path},p,{dryRun:n.dryRun});c.queued+=u.added,u.added>0&&R(t,{tool:"wyrm_bridge",outcome:"queued",reason:`render guard \u2014 ${u.added} human edit(s) harvested to review before overwrite`,source:`bridge:${i.name}`,projectId:e.id})}catch{}if(!n.dryRun){const{writes:s}=b(a,e,r,{clients:d});c.rendered=s.filter(p=>p.action!=="skipped").length}return c}async function G(t,i,r={}){const n={source:i.name,type:"reverse",stored:0,dropped:0,queued:0,rendered:0},c=t.prepare("SELECT id, path FROM projects WHERE path = ?").get(i.projectPath);if(!c)return n.note=`no project at ${i.projectPath}`,n;const e=await $(x(t),c,{},{dryRun:r.dryRun});return n.queued=e.added,n.dropped=e.skipped,e.added>0&&R(t,{tool:"wyrm_bridge",outcome:"queued",reason:`reverse sweep \u2014 ${e.added} native-file edit(s) queued for review`,source:`bridge:${i.name}`,projectId:c.id}),n}async function te(t,i,r,n={}){const c=new I(t),e=W(),d=[];for(const a of i.sources)n.only&&a.name!==n.only||(a.type==="remember"?(H(a.name,e)>0&&v(e),d.push(J(t,c,a,e,n))):a.type==="render"?d.push(await M(t,a,r,n)):d.push(await G(t,a,n)));return n.dryRun||v(e),d}async function re(t,i,r,n={}){const c=Math.max(2,n.intervalSec??15)*1e3,e=i.sources.filter(p=>p.type==="render");if(e.length===0)return;const d=new Map;for(const p of e){const u=t.prepare("SELECT id FROM projects WHERE path = ?").get(p.projectPath);u&&d.set(p.name,u.id)}let a=n.fromCursor??t.prepare("SELECT COALESCE(MAX(cursor), 0) AS c FROM events").get().c,s=0;for(;n.iterations==null||s<n.iterations;){s++,await new Promise(o=>setTimeout(o,c));const p=t.prepare("SELECT DISTINCT project_id FROM events WHERE cursor > ?").all(a);if(a=t.prepare("SELECT COALESCE(MAX(cursor), 0) AS c FROM events").get().c,p.length===0)continue;const u=new Set(p.map(o=>String(o.project_id))),f=[];for(const o of e){const m=d.get(o.name);if(!(m==null||!u.has(String(m))))try{f.push(await M(t,o,r()))}catch{}}f.length>0&&n.onCycle?.(f)}}export{H as adoptLegacyDistillState,B as configPath,ee as loadConfig,W as loadState,te as runBridge,J as runRememberSource,M as runRenderSource,G as runReverseSource,v as saveState,C as statePath,re as watchBridge};
@@ -0,0 +1,5 @@
1
+ function w(a=process.env){const t=Number(a.WYRM_RECALL_GRAPH_WEIGHT);return Number.isFinite(t)?Math.max(0,Math.min(.5,t)):.15}function y(a,t,h){const c=new Set(h.toLowerCase().split(/[^a-z0-9]+/).filter(e=>e.length>=3));if(c.size===0)return null;const r=a.prepare("SELECT id, name FROM entities WHERE project_id = ? LIMIT 500").all(t);if(r.length===0)return null;const l=new Set(r.map(e=>e.id));let s=[];try{s=a.prepare("SELECT ea.entity_id AS entity_id, ea.alias AS alias FROM entity_aliases ea JOIN entities e ON e.id = ea.entity_id WHERE e.project_id = ? LIMIT 1000").all(t).filter(e=>l.has(e.entity_id))}catch{}const d=new Map(r.map(e=>[e.id,e.name.toLowerCase()])),n=new Map,u=e=>{const i=e.toLowerCase().split(/[^a-z0-9]+/).filter(o=>o.length>=3);return i.length>0&&i.every(o=>c.has(o))};for(const e of r){if(n.size>=5)break;u(e.name)&&n.set(e.id,e.name.toLowerCase())}for(const e of s){if(n.size>=5)break;if(!n.has(e.entity_id)&&u(e.alias)){const i=d.get(e.entity_id);i&&n.set(e.entity_id,i)}}if(n.size===0)return null;const f=[...n.keys()],m=f.map(()=>"?").join(","),M=a.prepare(`
2
+ SELECT source_entity_id AS s, target_entity_id AS t, MAX(weight) AS w FROM relationships
3
+ WHERE project_id = ? AND (source_entity_id IN (${m}) OR target_entity_id IN (${m}))
4
+ GROUP BY s, t LIMIT 50
5
+ `).all(t,...f,...f),p=new Map;for(const e of M){const i=n.has(e.s)?e.t:e.s;if(n.has(i))continue;const o=d.get(i);if(!o)continue;const _=Math.max(0,Math.min(1,e.w??1));(p.get(o)??0)<_&&p.set(o,_)}const g=[...p.entries()].slice(0,10).map(([e,i])=>({name:e,weight:i}));return{direct:[...n.values()],neighbors:g}}function E(a,t,h){const c=Math.max(0,Math.min(.5,h));if(!t||c===0)return 1;const r=a.toLowerCase();for(const s of t.direct)if(s.length>=3&&r.includes(s))return 1+c;let l=0;for(const s of t.neighbors)s.name.length>=3&&r.includes(s.name)&&(l=Math.max(l,s.weight));return l>0?1+c/2*l:1}export{E as graphMultiplier,y as queryGraphContext,w as recallGraphWeight};
@@ -1,9 +1,9 @@
1
- import{emitEvent as ee}from"./events.js";import{sanitizeFtsQuery as B,buildFtsMatchQuery as P}from"./security.js";import{getActor as te}from"./handlers/boundary.js";import{buildPage as ne,DEFAULT_PAGE_SIZE as se}from"./keyset.js";import{rerankConfigured as ie,rerankCandidates as re}from"./rerank.js";import{autoSummaryPenalty as K,recallRecencyWeight as Y,recallRecencyHalfLifeDays as j,recencyMultiplier as V,extractTemporalWindow as z,recallTemporalWeight as G,temporalMultiplier as X,recallUsefulnessWeight as J,usefulnessMultiplier as Q,looksInjectionShaped as ae,trustMarker as ce}from"./context-ranking.js";const Z=40,oe=10,de=.3;class Ee{db;vectorStore;setVectorStore(t){this.vectorStore=t}getVectorStore(){return this.vectorStore}constructor(t){this.db=t}indexArtifact(t){if(!this.vectorStore)return;const e=`${t.problem}${t.validated_fix?" "+t.validated_fix:""}`.slice(0,2e3);this.vectorStore.addVector(e,"artifact",t.id,t.project_id).catch(()=>{})}add(t,e){if(typeof e.problem!="string"||!e.problem.trim())throw new Error("memory.add: 'problem' is required (a non-empty string).");const n=e.tags?.length?e.tags.join(","):null,i=te(),r=e.createdBy??"local",a=e.sourceTrust??(r.startsWith("bridge:")?"operator":r.startsWith("import:")||(n??"").includes("imported_from:")?"imported":"agent"),c=this.db.prepare(`
1
+ import{emitEvent as ne}from"./events.js";import{sanitizeFtsQuery as P,buildFtsMatchQuery as K}from"./security.js";import{getActor as se}from"./handlers/boundary.js";import{buildPage as ie,DEFAULT_PAGE_SIZE as re}from"./keyset.js";import{rerankConfigured as ae,rerankCandidates as ce}from"./rerank.js";import{autoSummaryPenalty as Y,recallRecencyWeight as j,recallRecencyHalfLifeDays as G,recencyMultiplier as V,extractTemporalWindow as z,recallTemporalWeight as X,temporalMultiplier as J,recallUsefulnessWeight as Q,usefulnessMultiplier as Z,looksInjectionShaped as oe,trustMarker as de}from"./context-ranking.js";import{queryGraphContext as le,graphMultiplier as fe,recallGraphWeight as ue}from"./graph-recall.js";const q=40,me=10,_e=.3;class Ce{db;vectorStore;setVectorStore(t){this.vectorStore=t}getVectorStore(){return this.vectorStore}constructor(t){this.db=t}indexArtifact(t){if(!this.vectorStore)return;const e=`${t.problem}${t.validated_fix?" "+t.validated_fix:""}`.slice(0,2e3);this.vectorStore.addVector(e,"artifact",t.id,t.project_id).catch(()=>{})}add(t,e){if(typeof e.problem!="string"||!e.problem.trim())throw new Error("memory.add: 'problem' is required (a non-empty string).");const n=e.tags?.length?e.tags.join(","):null,s=se(),r=e.createdBy??"local",a=e.sourceTrust??(r.startsWith("bridge:")?"operator":r.startsWith("import:")||(n??"").includes("imported_from:")?"imported":"agent");let o=e.needsReview??0,i=null;if(o===0&&a==="agent"&&s.agent_id&&(process.env.WYRM_PROBATION??"")==="1"){const d=Number(process.env.WYRM_PROBATION_HOURS),D=Number.isFinite(d)&&d>=1?d:24,p=this.db.prepare("SELECT MIN(created_at) AS f FROM memory_artifacts WHERE agent_id = ?").get(s.agent_id),N=p.f?new Date(p.f.replace(" ","T")+"Z").getTime():NaN;(!Number.isFinite(N)||Date.now()-N<D*36e5)&&(o=1,i="probation")}const m=i?n?`${n},${i}`:i:n,_=this.db.prepare(`
2
2
  INSERT INTO memory_artifacts
3
3
  (project_id, kind, problem, constraints, validated_fix, why_it_worked,
4
4
  outcome, source_session_id, tags, confidence, needs_review, created_by, agent_id, run_id, source_trust)
5
5
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
6
- `).run(t,e.kind,e.problem.trim(),e.constraints?.trim()??null,e.validatedFix?.trim()??null,e.whyItWorked?.trim()??null,e.outcome??"neutral",e.sourceSessionId??null,n,e.confidence??1,e.needsReview??0,r,i.agent_id,i.run_id,a),s=this.get(c.lastInsertRowid);return e.needsReview||(ee(this.db,{projectId:t,kind:"capture",refTable:"memory_artifacts",refId:s.id,isShared:!!s.is_shared}),this.indexArtifact(s)),s}get(t){return this.db.prepare("SELECT * FROM memory_artifacts WHERE id = ?").get(t)??null}update(t,e){return this.db.prepare(`
6
+ `).run(t,e.kind,e.problem.trim(),e.constraints?.trim()??null,e.validatedFix?.trim()??null,e.whyItWorked?.trim()??null,e.outcome??"neutral",e.sourceSessionId??null,m,e.confidence??1,o,r,s.agent_id,s.run_id,a),c=this.get(_.lastInsertRowid);return o||(ne(this.db,{projectId:t,kind:"capture",refTable:"memory_artifacts",refId:c.id,isShared:!!c.is_shared}),this.indexArtifact(c)),c}get(t){return this.db.prepare("SELECT * FROM memory_artifacts WHERE id = ?").get(t)??null}update(t,e){return this.db.prepare(`
7
7
  UPDATE memory_artifacts SET
8
8
  confidence = COALESCE(?, confidence),
9
9
  validated_fix = COALESCE(?, validated_fix),
@@ -22,7 +22,7 @@ import{emitEvent as ee}from"./events.js";import{sanitizeFtsQuery as B,buildFtsMa
22
22
  END,
23
23
  updated_at = datetime('now')
24
24
  WHERE id = ?
25
- `).run(e?1:0,t)}recall(t,e,n={}){const i=n.limit??10,r=n.minConfidence??0,a=this.searchByFts(t,e,Z,n.kind,r),c=this.searchByTags(t,e,Z,n.kind,r),s=new Set,h=[];a.forEach((o,l)=>{s.add(o.id),h.push({artifact:o,inFts:!0,inTag:!1,ftsRank:l})});for(const o of c)if(!s.has(o.id))s.add(o.id),h.push({artifact:o,inFts:!1,inTag:!0});else{const l=h.find(v=>v.artifact.id===o.id);l&&(l.inTag=!0)}return h.filter(({artifact:o})=>o.confidence>=r).map(({artifact:o,inFts:l,inTag:v,ftsRank:g})=>{let A;if(l){const u=1-(g??0)/(a.length+1);A=(v?.7:.55)+.45*u}else A=.4;A*=o.confidence,o.outcome==="positive"&&(A*=1.1);const M=(Date.now()-new Date(o.last_validated_at).getTime())/(1e3*60*60*24),C=Math.max(.5,1-M/180);return A*=C,{artifact:o,relevance_score:Math.min(1,A),match_type:l&&v?"both":l?"fts":"tag"}}).sort((o,l)=>l.relevance_score-o.relevance_score).slice(0,i)}async recallHybrid(t,e,n={}){const i=n.limit??10,r=n.minConfidence??0,a=n.rerank===!0&&ie(),c=Math.min(Math.max(i*2,20),50),s=a?c:i;if(!this.vectorStore)return this.recall(t,e,n);const h=Math.max(50,s*5),_=Date.now(),o=this.searchByFts(t,e,h,n.kind,r),l=Date.now()-_,v=Date.now();let g=[];try{g=(await this.vectorStore.search(e,h,t,["artifact"])).map(p=>({id:p.content_id,s:p.similarity}))}catch{}const A=Date.now()-v;if(g.length===0)return this.recall(t,e,n);const M=Date.now(),C=60,u=Number(process.env.WYRM_RERANK_ALPHA),E=Number.isFinite(u)?Math.max(0,Math.min(1,u)):.7,R=new Map;if(process.env.WYRM_RERANK_FUSION==="rrf")o.forEach((f,p)=>R.set(f.id,(R.get(f.id)??0)+1/(C+p+1))),g.forEach((f,p)=>R.set(f.id,(R.get(f.id)??0)+1/(C+p+1)));else{const f=o.length+1;o.forEach((L,q)=>R.set(L.id,(R.get(L.id)??0)+(1-E)*(1-q/f)));const p=g.map(L=>L.s),y=Math.min(...p),F=Math.max(...p),T=F-y;g.forEach(L=>R.set(L.id,(R.get(L.id)??0)+E*(T>1e-9?(L.s-y)/T:1)))}const O=new Set(o.map(f=>f.id)),N=new Set(g.map(f=>f.id)),$=new Map(o.map(f=>[f.id,f])),w=[...R.entries()].sort((f,p)=>p[1]-f[1]),H=Y(),W=j(),d=G(),m=d>0?z(e):null,S=J(),k=new Date,b=Math.min(w.length,Math.max(s*4,50)),D=[];for(const[f,p]of w.slice(0,b)){const y=$.get(f)??this.get(f)??void 0;if(!y||y.confidence<r||y.needs_review===1||y.supersedes_id!=null||n.kind&&y.kind!==n.kind)continue;const F=y.updated_at??y.created_at;let T=p*V(F,H,W,k);m&&(T*=X(y.created_at,m,d,k)),T*=Q(y.reuse_count,y.reuse_success_count,S),D.push({id:f,s:T,art:y})}D.sort((f,p)=>p.s-f.s);const x=D[0]?.s??1,I=[];for(const{id:f,s:p,art:y}of D){if(I.length>=s)break;const F=O.has(f)&&N.has(f)?"hybrid":N.has(f)?"vector":"fts";I.push({artifact:y,relevance_score:Math.min(1,p/x),match_type:F})}const U=Date.now()-M;if(a&&I.length>0){const f=Date.now(),p=await this.rerankResults(e,I);return n.onStats?.({ftsMs:l,vectorMs:A,fusionMs:U,rerankMs:Date.now()-f,candidates:I.length}),p.slice(0,i)}return n.onStats?.({ftsMs:l,vectorMs:A,fusionMs:U,candidates:I.length}),I}async rerankResults(t,e){const n=e.map(s=>({id:s.artifact.id,text:`${s.artifact.problem}${s.artifact.validated_fix?" "+s.artifact.validated_fix:""}`.slice(0,2e3)})),i=await re(t,n);if(!i)return e;const r=new Map(e.map(s=>[s.artifact.id,s])),a=[],c=i.length;return i.forEach((s,h)=>{const _=r.get(s.id);_&&a.push({..._,relevance_score:c>0?(c-h)/c:_.relevance_score})}),a}async recallHybridGlobal(t,e={}){const n=e.limit??10,i=e.minConfidence??0,r=Math.max(50,n*5),a=this.searchByFtsGlobal(t,r,e.kind,i),c=()=>a.slice(0,n).map((d,m)=>({artifact:d,relevance_score:1-m/(a.length+1),match_type:"fts"}));if(!this.vectorStore)return c();let s=[];try{s=(await this.vectorStore.search(t,r,void 0,["artifact"])).map(m=>({id:m.content_id,s:m.similarity}))}catch{}if(s.length===0)return c();const h=60,_=Number(process.env.WYRM_RERANK_ALPHA),o=Number.isFinite(_)?Math.max(0,Math.min(1,_)):.7,l=new Map;if(process.env.WYRM_RERANK_FUSION==="rrf")a.forEach((d,m)=>l.set(d.id,(l.get(d.id)??0)+1/(h+m+1))),s.forEach((d,m)=>l.set(d.id,(l.get(d.id)??0)+1/(h+m+1)));else{const d=a.length+1;a.forEach((D,x)=>l.set(D.id,(l.get(D.id)??0)+(1-o)*(1-x/d)));const m=s.map(D=>D.s),S=Math.min(...m),k=Math.max(...m),b=k-S;s.forEach(D=>l.set(D.id,(l.get(D.id)??0)+o*(b>1e-9?(D.s-S)/b:1)))}const v=new Set(a.map(d=>d.id)),g=new Set(s.map(d=>d.id)),A=new Map(a.map(d=>[d.id,d])),M=[...l.entries()].sort((d,m)=>m[1]-d[1]),C=Y(),u=j(),E=G(),R=E>0?z(t):null,O=J(),N=new Date,$=Math.min(M.length,Math.max(n*4,50)),w=[];for(const[d,m]of M.slice(0,$)){const S=A.get(d)??this.get(d)??void 0;if(!S||S.confidence<i||S.needs_review===1||S.supersedes_id!=null||e.kind&&S.kind!==e.kind)continue;const k=S.updated_at??S.created_at;let b=m*V(k,C,u,N);R&&(b*=X(S.created_at,R,E,N)),b*=Q(S.reuse_count,S.reuse_success_count,O),w.push({id:d,s:b,art:S})}w.sort((d,m)=>m.s-d.s);const H=w[0]?.s??1,W=[];for(const{id:d,s:m,art:S}of w){if(W.length>=n)break;const k=v.has(d)&&g.has(d)?"hybrid":g.has(d)?"vector":"fts";W.push({artifact:S,relevance_score:Math.min(1,m/H),match_type:k})}return W}searchByFtsGlobal(t,e,n,i=0){if(!this.db)return[];let r="";try{const s=B(t);r=s?P(s):""}catch{return[]}if(!r)return[];const a=n?"AND a.kind = ?":"",c=[r,i];n&&c.push(n),c.push(e);try{return this.db.prepare(`
25
+ `).run(e?1:0,t)}recall(t,e,n={}){const s=n.limit??10,r=n.minConfidence??0,a=this.searchByFts(t,e,q,n.kind,r),o=this.searchByTags(t,e,q,n.kind,r),i=new Set,m=[];a.forEach((c,d)=>{i.add(c.id),m.push({artifact:c,inFts:!0,inTag:!1,ftsRank:d})});for(const c of o)if(!i.has(c.id))i.add(c.id),m.push({artifact:c,inFts:!1,inTag:!0});else{const d=m.find(D=>D.artifact.id===c.id);d&&(d.inTag=!0)}return m.filter(({artifact:c})=>c.confidence>=r).map(({artifact:c,inFts:d,inTag:D,ftsRank:p})=>{let N;if(d){const u=1-(p??0)/(a.length+1);N=(D?.7:.55)+.45*u}else N=.4;N*=c.confidence,c.outcome==="positive"&&(N*=1.1);const M=(Date.now()-new Date(c.last_validated_at).getTime())/(1e3*60*60*24),w=Math.max(.5,1-M/180);return N*=w,{artifact:c,relevance_score:Math.min(1,N),match_type:d&&D?"both":d?"fts":"tag"}}).sort((c,d)=>d.relevance_score-c.relevance_score).slice(0,s)}async recallHybrid(t,e,n={}){const s=n.limit??10,r=n.minConfidence??0,a=n.rerank===!0&&ae(),o=Math.min(Math.max(s*2,20),50),i=a?o:s;if(!this.vectorStore)return this.recall(t,e,n);const m=Math.max(50,i*5),_=Date.now(),c=this.searchByFts(t,e,m,n.kind,r),d=Date.now()-_,D=Date.now();let p=[];try{p=(await this.vectorStore.search(e,m,t,["artifact"])).map(E=>({id:E.content_id,s:E.similarity}))}catch{}const N=Date.now()-D;if(p.length===0)return this.recall(t,e,n);const M=Date.now(),w=60,u=Number(process.env.WYRM_RERANK_ALPHA),g=Number.isFinite(u)?Math.max(0,Math.min(1,u)):.7,A=new Map;if(process.env.WYRM_RERANK_FUSION==="rrf")c.forEach((f,E)=>A.set(f.id,(A.get(f.id)??0)+1/(w+E+1))),p.forEach((f,E)=>A.set(f.id,(A.get(f.id)??0)+1/(w+E+1)));else{const f=c.length+1;c.forEach((L,te)=>A.set(L.id,(A.get(L.id)??0)+(1-g)*(1-te/f)));const E=p.map(L=>L.s),R=Math.min(...E),F=Math.max(...E),I=F-R;p.forEach(L=>A.set(L.id,(A.get(L.id)??0)+g*(I>1e-9?(L.s-R)/I:1)))}const O=new Set(c.map(f=>f.id)),C=new Set(p.map(f=>f.id)),$=new Map(c.map(f=>[f.id,f])),y=[...A.entries()].sort((f,E)=>E[1]-f[1]),x=j(),W=G(),l=X(),h=l>0?z(e):null,S=Q(),k=ue(),b=k>0?le(this.db,t,e):null,v=new Date,U=Math.min(y.length,Math.max(i*4,50)),H=[];for(const[f,E]of y.slice(0,U)){const R=$.get(f)??this.get(f)??void 0;if(!R||R.confidence<r||R.needs_review===1||R.supersedes_id!=null||n.kind&&R.kind!==n.kind)continue;const F=R.updated_at??R.created_at;let I=E*V(F,x,W,v);h&&(I*=J(R.created_at,h,l,v)),I*=Z(R.reuse_count,R.reuse_success_count,S),b&&(I*=fe(`${R.problem} ${R.validated_fix??""}`,b,k)),H.push({id:f,s:I,art:R})}H.sort((f,E)=>E.s-f.s);const ee=H[0]?.s??1,T=[];for(const{id:f,s:E,art:R}of H){if(T.length>=i)break;const F=O.has(f)&&C.has(f)?"hybrid":C.has(f)?"vector":"fts";T.push({artifact:R,relevance_score:Math.min(1,E/ee),match_type:F})}const B=Date.now()-M;if(a&&T.length>0){const f=Date.now(),E=await this.rerankResults(e,T);return n.onStats?.({ftsMs:d,vectorMs:N,fusionMs:B,rerankMs:Date.now()-f,candidates:T.length}),E.slice(0,s)}return n.onStats?.({ftsMs:d,vectorMs:N,fusionMs:B,candidates:T.length}),T}async rerankResults(t,e){const n=e.map(i=>({id:i.artifact.id,text:`${i.artifact.problem}${i.artifact.validated_fix?" "+i.artifact.validated_fix:""}`.slice(0,2e3)})),s=await ce(t,n);if(!s)return e;const r=new Map(e.map(i=>[i.artifact.id,i])),a=[],o=s.length;return s.forEach((i,m)=>{const _=r.get(i.id);_&&a.push({..._,relevance_score:o>0?(o-m)/o:_.relevance_score})}),a}async recallHybridGlobal(t,e={}){const n=e.limit??10,s=e.minConfidence??0,r=Math.max(50,n*5),a=this.searchByFtsGlobal(t,r,e.kind,s),o=()=>a.slice(0,n).map((l,h)=>({artifact:l,relevance_score:1-h/(a.length+1),match_type:"fts"}));if(!this.vectorStore)return o();let i=[];try{i=(await this.vectorStore.search(t,r,void 0,["artifact"])).map(h=>({id:h.content_id,s:h.similarity}))}catch{}if(i.length===0)return o();const m=60,_=Number(process.env.WYRM_RERANK_ALPHA),c=Number.isFinite(_)?Math.max(0,Math.min(1,_)):.7,d=new Map;if(process.env.WYRM_RERANK_FUSION==="rrf")a.forEach((l,h)=>d.set(l.id,(d.get(l.id)??0)+1/(m+h+1))),i.forEach((l,h)=>d.set(l.id,(d.get(l.id)??0)+1/(m+h+1)));else{const l=a.length+1;a.forEach((v,U)=>d.set(v.id,(d.get(v.id)??0)+(1-c)*(1-U/l)));const h=i.map(v=>v.s),S=Math.min(...h),k=Math.max(...h),b=k-S;i.forEach(v=>d.set(v.id,(d.get(v.id)??0)+c*(b>1e-9?(v.s-S)/b:1)))}const D=new Set(a.map(l=>l.id)),p=new Set(i.map(l=>l.id)),N=new Map(a.map(l=>[l.id,l])),M=[...d.entries()].sort((l,h)=>h[1]-l[1]),w=j(),u=G(),g=X(),A=g>0?z(t):null,O=Q(),C=new Date,$=Math.min(M.length,Math.max(n*4,50)),y=[];for(const[l,h]of M.slice(0,$)){const S=N.get(l)??this.get(l)??void 0;if(!S||S.confidence<s||S.needs_review===1||S.supersedes_id!=null||e.kind&&S.kind!==e.kind)continue;const k=S.updated_at??S.created_at;let b=h*V(k,w,u,C);A&&(b*=J(S.created_at,A,g,C)),b*=Z(S.reuse_count,S.reuse_success_count,O),y.push({id:l,s:b,art:S})}y.sort((l,h)=>h.s-l.s);const x=y[0]?.s??1,W=[];for(const{id:l,s:h,art:S}of y){if(W.length>=n)break;const k=D.has(l)&&p.has(l)?"hybrid":p.has(l)?"vector":"fts";W.push({artifact:S,relevance_score:Math.min(1,h/x),match_type:k})}return W}searchByFtsGlobal(t,e,n,s=0){if(!this.db)return[];let r="";try{const i=P(t);r=i?K(i):""}catch{return[]}if(!r)return[];const a=n?"AND a.kind = ?":"",o=[r,s];n&&o.push(n),o.push(e);try{return this.db.prepare(`
26
26
  SELECT a.* FROM memory_artifacts a
27
27
  JOIN memory_artifacts_fts ON a.id = memory_artifacts_fts.rowid
28
28
  WHERE memory_artifacts_fts MATCH ?
@@ -32,7 +32,7 @@ import{emitEvent as ee}from"./events.js";import{sanitizeFtsQuery as B,buildFtsMa
32
32
  ${a}
33
33
  ORDER BY rank, a.confidence DESC
34
34
  LIMIT ?
35
- `).all(...c)}catch{return[]}}searchByFts(t,e,n,i,r=0){let a="";try{const h=B(e);a=h?P(h):""}catch{return this.listRecent(t,n,i)}if(!a)return this.listRecent(t,n,i);const c=i?"AND a.kind = ?":"",s=[a,t,r];i&&s.push(i),s.push(n);try{return this.db.prepare(`
35
+ `).all(...o)}catch{return[]}}searchByFts(t,e,n,s,r=0){let a="";try{const m=P(e);a=m?K(m):""}catch{return this.listRecent(t,n,s)}if(!a)return this.listRecent(t,n,s);const o=s?"AND a.kind = ?":"",i=[a,t,r];s&&i.push(s),i.push(n);try{return this.db.prepare(`
36
36
  SELECT a.* FROM memory_artifacts a
37
37
  JOIN memory_artifacts_fts ON a.id = memory_artifacts_fts.rowid
38
38
  WHERE memory_artifacts_fts MATCH ?
@@ -40,46 +40,46 @@ import{emitEvent as ee}from"./events.js";import{sanitizeFtsQuery as B,buildFtsMa
40
40
  AND a.confidence >= ?
41
41
  AND a.supersedes_id IS NULL
42
42
  AND a.needs_review = 0
43
- ${c}
43
+ ${o}
44
44
  ORDER BY rank, a.confidence DESC
45
45
  LIMIT ?
46
- `).all(...s)}catch{return this.listRecent(t,n,i)}}searchByTags(t,e,n,i,r=0){const a=e.toLowerCase().split(/\s+/).filter(l=>l.length>2);if(!a.length)return[];const c=l=>l.replace(/[%_\\]/g,"\\$&"),s=a.map(()=>"LOWER(tags) LIKE ? ESCAPE '\\'").join(" OR "),h=a.map(l=>`%${c(l)}%`),_=i?"AND kind = ?":"",o=[t,r,...h];return i&&o.push(i),o.push(n),this.db.prepare(`
46
+ `).all(...i)}catch{return this.listRecent(t,n,s)}}searchByTags(t,e,n,s,r=0){const a=e.toLowerCase().split(/\s+/).filter(d=>d.length>2);if(!a.length)return[];const o=d=>d.replace(/[%_\\]/g,"\\$&"),i=a.map(()=>"LOWER(tags) LIKE ? ESCAPE '\\'").join(" OR "),m=a.map(d=>`%${o(d)}%`),_=s?"AND kind = ?":"",c=[t,r,...m];return s&&c.push(s),c.push(n),this.db.prepare(`
47
47
  SELECT * FROM memory_artifacts
48
48
  WHERE project_id = ? AND confidence >= ? AND supersedes_id IS NULL
49
49
  AND needs_review = 0
50
- AND (${s})
50
+ AND (${i})
51
51
  ${_}
52
52
  ORDER BY confidence DESC, reuse_success_count DESC
53
53
  LIMIT ?
54
- `).all(...o)}listRecent(t,e=20,n){const i=n?"AND kind = ?":"",r=[t];return n&&r.push(n),r.push(e),this.db.prepare(`
54
+ `).all(...c)}listRecent(t,e=20,n){const s=n?"AND kind = ?":"",r=[t];return n&&r.push(n),r.push(e),this.db.prepare(`
55
55
  SELECT * FROM memory_artifacts
56
56
  WHERE project_id = ? AND supersedes_id IS NULL AND needs_review = 0
57
- ${i}
57
+ ${s}
58
58
  ORDER BY confidence DESC, created_at DESC
59
59
  LIMIT ?
60
- `).all(...r)}buildContextBrief(t,e,n={}){const i=n.kinds??["pattern","heuristic","reasoning_trace","lesson","anti_pattern"],r=n.maxItems??oe,a=n.minConfidence??de,c=this.recall(t,e,{limit:r*2,minConfidence:a});c.sort((u,E)=>(K(u.artifact.problem)>0?1:0)-(K(E.artifact.problem)>0?1:0));const s=[],_=c.filter(u=>{const E=u.artifact.source_trust;return(E==="imported"||E==="untrusted")&&ae(`${u.artifact.problem} ${u.artifact.validated_fix??""}`)?(s.push(u.artifact.id),!1):!0}).filter(u=>i.includes(u.artifact.kind)).slice(0,r),o=new Map;for(const u of _){const E=o.get(u.artifact.kind)??[];E.push(u),o.set(u.artifact.kind,E)}const l={pattern:"\u2705 Proven Patterns",heuristic:"\u{1F4A1} Heuristics",reasoning_trace:"\u{1F9E0} Past Reasoning",lesson:"\u{1F4DA} Lessons Learned",anti_pattern:"\u26A0\uFE0F Anti-Patterns to Avoid"},v=["pattern","heuristic","reasoning_trace","lesson","anti_pattern"],g=[],A=[];let M=0;for(const u of v){if(!i.includes(u))continue;const E=o.get(u)??[];if(!E.length)continue;const R=[];for(const O of E){if(M>=r)break;const N=O.artifact;let w=`${ce(N.source_trust)}**Problem:** ${N.problem}`;N.constraints&&(w+=`
61
- _Constraints:_ ${N.constraints}`),N.validated_fix&&(w+=`
62
- _Solution:_ ${N.validated_fix}`),N.why_it_worked&&(w+=`
63
- _Why it worked:_ ${N.why_it_worked}`),N.outcome==="negative"&&(w+=`
64
- _Note: This approach failed \u2014 avoid it_`),R.push(w),A.push(N.id),M++}R.length&&g.push({heading:l[u],items:R,source:u})}let C="";if(g.length>0){C+=`---
60
+ `).all(...r)}buildContextBrief(t,e,n={}){const s=n.kinds??["pattern","heuristic","reasoning_trace","lesson","anti_pattern"],r=n.maxItems??me,a=n.minConfidence??_e,o=this.recall(t,e,{limit:r*2,minConfidence:a});o.sort((u,g)=>(Y(u.artifact.problem)>0?1:0)-(Y(g.artifact.problem)>0?1:0));const i=[],_=o.filter(u=>{const g=u.artifact.source_trust;return(g==="imported"||g==="untrusted")&&oe(`${u.artifact.problem} ${u.artifact.validated_fix??""}`)?(i.push(u.artifact.id),!1):!0}).filter(u=>s.includes(u.artifact.kind)).slice(0,r),c=new Map;for(const u of _){const g=c.get(u.artifact.kind)??[];g.push(u),c.set(u.artifact.kind,g)}const d={pattern:"\u2705 Proven Patterns",heuristic:"\u{1F4A1} Heuristics",reasoning_trace:"\u{1F9E0} Past Reasoning",lesson:"\u{1F4DA} Lessons Learned",anti_pattern:"\u26A0\uFE0F Anti-Patterns to Avoid"},D=["pattern","heuristic","reasoning_trace","lesson","anti_pattern"],p=[],N=[];let M=0;for(const u of D){if(!s.includes(u))continue;const g=c.get(u)??[];if(!g.length)continue;const A=[];for(const O of g){if(M>=r)break;const C=O.artifact;let y=`${de(C.source_trust)}**Problem:** ${C.problem}`;C.constraints&&(y+=`
61
+ _Constraints:_ ${C.constraints}`),C.validated_fix&&(y+=`
62
+ _Solution:_ ${C.validated_fix}`),C.why_it_worked&&(y+=`
63
+ _Why it worked:_ ${C.why_it_worked}`),C.outcome==="negative"&&(y+=`
64
+ _Note: This approach failed \u2014 avoid it_`),A.push(y),N.push(C.id),M++}A.length&&p.push({heading:d[u],items:A,source:u})}let w="";if(p.length>0){w+=`---
65
65
  ## \u{F115D} Memory Brief
66
66
  _Relevant past knowledge from Wyrm:_
67
67
 
68
- `;for(const u of g){C+=`### ${u.heading}
69
- `;for(const E of u.items)C+=`- ${E}
70
- `;C+=`
71
- `}C+=`---
72
- `}return{sections:g,text:C,sourceIds:A}}getStats(t){const e=this.db.prepare("SELECT COUNT(*) as n FROM memory_artifacts WHERE project_id = ? AND supersedes_id IS NULL").get(t).n,n=this.db.prepare("SELECT COUNT(*) as n FROM memory_artifacts WHERE project_id = ? AND supersedes_id IS NOT NULL").get(t).n,i=this.db.prepare("SELECT AVG(confidence) as v FROM memory_artifacts WHERE project_id = ? AND supersedes_id IS NULL").get(t).v??0,r=this.db.prepare("SELECT kind, COUNT(*) as cnt FROM memory_artifacts WHERE project_id = ? AND supersedes_id IS NULL GROUP BY kind").all(t),a={};for(const c of r)a[c.kind]=c.cnt;return{total:e,byKind:a,avgConfidence:Math.round(i*100)/100,supersededCount:n}}listAll(t,e={}){const n=e.kind?"AND kind = ?":"",i=e.includeSuperseded?"":"AND supersedes_id IS NULL",r=[t];return e.kind&&r.push(e.kind),r.push(e.limit??50),this.db.prepare(`
68
+ `;for(const u of p){w+=`### ${u.heading}
69
+ `;for(const g of u.items)w+=`- ${g}
70
+ `;w+=`
71
+ `}w+=`---
72
+ `}return{sections:p,text:w,sourceIds:N}}getStats(t){const e=this.db.prepare("SELECT COUNT(*) as n FROM memory_artifacts WHERE project_id = ? AND supersedes_id IS NULL").get(t).n,n=this.db.prepare("SELECT COUNT(*) as n FROM memory_artifacts WHERE project_id = ? AND supersedes_id IS NOT NULL").get(t).n,s=this.db.prepare("SELECT AVG(confidence) as v FROM memory_artifacts WHERE project_id = ? AND supersedes_id IS NULL").get(t).v??0,r=this.db.prepare("SELECT kind, COUNT(*) as cnt FROM memory_artifacts WHERE project_id = ? AND supersedes_id IS NULL GROUP BY kind").all(t),a={};for(const o of r)a[o.kind]=o.cnt;return{total:e,byKind:a,avgConfidence:Math.round(s*100)/100,supersededCount:n}}listAll(t,e={}){const n=e.kind?"AND kind = ?":"",s=e.includeSuperseded?"":"AND supersedes_id IS NULL",r=[t];return e.kind&&r.push(e.kind),r.push(e.limit??50),this.db.prepare(`
73
73
  SELECT * FROM memory_artifacts
74
- WHERE project_id = ? ${n} ${i}
74
+ WHERE project_id = ? ${n} ${s}
75
75
  ORDER BY confidence DESC, created_at DESC
76
76
  LIMIT ?
77
- `).all(...r)}listPage(t,e={pageSize:se}){const n=e.kind?"AND kind = ?":"",i=e.includeSuperseded?"":"AND supersedes_id IS NULL",r="AND needs_review = 0",a=[t];e.kind&&a.push(e.kind);let c="";if(e.after){const _=Number(e.after.sortKey);Number.isFinite(_)&&(c="AND (confidence < ? OR (confidence = ? AND id < ?))",a.push(_,_,e.after.id))}const s=e.pageSize+1;a.push(s);const h=this.db.prepare(`
77
+ `).all(...r)}listPage(t,e={pageSize:re}){const n=e.kind?"AND kind = ?":"",s=e.includeSuperseded?"":"AND supersedes_id IS NULL",r="AND needs_review = 0",a=[t];e.kind&&a.push(e.kind);let o="";if(e.after){const _=Number(e.after.sortKey);Number.isFinite(_)&&(o="AND (confidence < ? OR (confidence = ? AND id < ?))",a.push(_,_,e.after.id))}const i=e.pageSize+1;a.push(i);const m=this.db.prepare(`
78
78
  SELECT * FROM memory_artifacts
79
- WHERE project_id = ? ${n} ${i} ${r} ${c}
79
+ WHERE project_id = ? ${n} ${s} ${r} ${o}
80
80
  ORDER BY confidence DESC, id DESC
81
81
  LIMIT ?
82
- `).all(...a);return ne(h,e.pageSize,_=>({sortKey:String(_.confidence),id:_.id}))}pruneStale(t={}){const e=t.minConfidence??.3,n=t.olderThanDays??90,i=t.dryRun??!0,r=t.projectId!=null,a=[e,`-${n} days`];r&&a.push(t.projectId);const c=this.db.prepare(`
82
+ `).all(...a);return ie(m,e.pageSize,_=>({sortKey:String(_.confidence),id:_.id}))}pruneStale(t={}){const e=t.minConfidence??.3,n=t.olderThanDays??90,s=t.dryRun??!0,r=t.projectId!=null,a=[e,`-${n} days`];r&&a.push(t.projectId);const o=this.db.prepare(`
83
83
  SELECT id, kind, problem, confidence, last_accessed_at
84
84
  FROM memory_artifacts
85
85
  WHERE confidence < ?
@@ -89,4 +89,4 @@ _Relevant past knowledge from Wyrm:_
89
89
  ${r?"AND project_id = ?":""}
90
90
  ORDER BY confidence ASC
91
91
  LIMIT 500
92
- `).all(...a);return i||c.length===0?{candidates:c,deleted:0,dryRun:i}:{candidates:c,deleted:this.deleteArtifacts(c.map(s=>s.id)),dryRun:i}}deleteArtifacts(t){if(t.length===0)return 0;const e=t.map(()=>"?").join(",");return this.db.prepare(`DELETE FROM memory_artifacts WHERE id IN (${e}) AND needs_review = 0`).run(...t).changes}}export{Ee as MemoryArtifacts};
92
+ `).all(...a);return s||o.length===0?{candidates:o,deleted:0,dryRun:s}:{candidates:o,deleted:this.deleteArtifacts(o.map(i=>i.id)),dryRun:s}}deleteArtifacts(t){if(t.length===0)return 0;const e=t.map(()=>"?").join(",");return this.db.prepare(`DELETE FROM memory_artifacts WHERE id IN (${e}) AND needs_review = 0`).run(...t).changes}}export{Ce as MemoryArtifacts};
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "contract": "wyrm-cloud-memory-v2",
3
- "version": "7.12.0",
3
+ "version": "8.0.0",
4
4
  "derivedFrom": "standard tier MINUS device-local/egress/subprocess (src/cloud-profile.ts)",
5
5
  "count": 19,
6
6
  "tools": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "contract": "wyrm-cloud-memory-v1",
3
- "version": "7.12.0",
3
+ "version": "8.0.0",
4
4
  "tools": [
5
5
  {
6
6
  "name": "wyrm_recall",
package/dist/wyrm-cli.js CHANGED
@@ -49,7 +49,7 @@ import{join as q,dirname as ne}from"path";import{homedir as Te}from"os";import{e
49
49
  `);return}let g=f.briefing_markdown;t>0&&g.length>t&&(g=g.slice(0,t)+`
50
50
 
51
51
  _... brief truncated at ${t} chars, run \`wyrm show session:${f.session_id}\` for the full record._`),process.stdout.write(g+`
52
- `)}finally{i.close(),console.log=s}}async function Ze(l){const{flags:a}=C(l),{runMetabolize:e}=await import("./metabolize.js"),r=_(),o=console.log;try{let t;if(typeof a.project=="string"){const s=x(r,a.project);if(!s){m(`Project not found: ${a.project}`),process.exitCode=1;return}t=s.id}const n=e(r.getDatabase(),{projectId:t,dryRun:a["dry-run"]===!0,near:a["no-near"]===!0?!1:void 0});console.log(`metabolize${n.dryRun?" (dry-run)":""} \u2014 scanned ${n.scanned} \xB7 exact-merged ${n.exactMerged} (superseded, not deleted) \xB7 near-dup candidates queued ${n.nearCandidatesQueued} \xB7 decayed ${n.decayed}`)}finally{r.close(),console.log=o}}async function Xe(l){const{positional:a,flags:e}=C(l),r=a[0],o=await import("./bridge.js");if(r==="init"){const{existsSync:t,writeFileSync:n}=await import("node:fs"),s=o.configPath();if(t(s)&&e.force!==!0){m(`${s} already exists (use --force to overwrite).`),process.exitCode=1;return}const i=(await import("node:os")).homedir(),p={sources:[{type:"remember",name:"remember",dir:`${i}/.remember`,projectPath:i,windowDays:7}]};n(s,JSON.stringify(p,null,2)),b(`Wrote ${s} (1 source: remember). Add render/reverse sources per project as needed.`);return}if(r==="status"){const t=o.loadConfig();if(!t){m(`No bridge config at ${o.configPath()} \u2014 run \`wyrm bridge init\`.`),process.exitCode=1;return}const n=o.loadState();S("Bridge sources");for(const s of t.sources){const i=Object.keys(n).filter(c=>c.startsWith(s.name+":")),p=i.reduce((c,f)=>c+n[f].length,0);console.log(` ${s.name} [${s.type}] \u2192 ${"projectPath"in s?s.projectPath:""}${s.type==="remember"?` (${i.length} files tracked, ${p} sections bridged)`:""}`)}return}if(r==="run"){const t=o.loadConfig();if(!t){m(`No bridge config at ${o.configPath()} \u2014 run \`wyrm bridge init\`.`),process.exitCode=1;return}const n=_(),s=console.log;try{const i={wyrm_version:M().version??"unknown",compiled_at:new Date().toISOString()},p=await o.runBridge(n.getDatabase(),t,i,{only:typeof e.source=="string"?e.source:void 0,dryRun:e["dry-run"]===!0});for(const c of p)console.log(`bridge:${c.source} [${c.type}]${e["dry-run"]===!0?" (dry-run)":""} \u2014 stored ${c.stored} \xB7 queued ${c.queued} \xB7 dropped ${c.dropped} \xB7 rendered ${c.rendered}${c.note?` \xB7 ${c.note}`:""}`);return}finally{n.close(),console.log=s}}m("Usage: wyrm bridge <init|status|run> [--source <name>] [--dry-run] [--force]"),process.exitCode=1}async function et(l){const{positional:a,flags:e}=C(l);if(a[0]!=="log"){m("Usage: wyrm session log [--path <dir>] [--run <id>] [--objectives <t>] [--completed <t>] [--issues <t>] [--commits <t>] [--notes <t>]"),process.exitCode=1;return}const o=c=>typeof e[c]=="string"?e[c]:void 0,t=o("path"),n=o("project"),s=o("run")?.slice(0,64),i=console.log;console.log=()=>{};const p=_();try{let c=t?p.getProject(t):void 0;if(!c&&n&&(c=x(p,n)??void 0),c||(c=p.getProject(process.cwd())),!c){process.stderr.write(`wyrm session log: no Wyrm project for this directory, nothing recorded.
52
+ `)}finally{i.close(),console.log=s}}async function Ze(l){const{flags:a}=C(l),{runMetabolize:e}=await import("./metabolize.js"),r=_(),o=console.log;try{let t;if(typeof a.project=="string"){const s=x(r,a.project);if(!s){m(`Project not found: ${a.project}`),process.exitCode=1;return}t=s.id}const n=e(r.getDatabase(),{projectId:t,dryRun:a["dry-run"]===!0,near:a["no-near"]===!0?!1:void 0});console.log(`metabolize${n.dryRun?" (dry-run)":""} \u2014 scanned ${n.scanned} \xB7 exact-merged ${n.exactMerged} (superseded, not deleted) \xB7 near-dup candidates queued ${n.nearCandidatesQueued} \xB7 decayed ${n.decayed}`)}finally{r.close(),console.log=o}}async function Xe(l){const{positional:a,flags:e}=C(l),r=a[0],o=await import("./bridge.js");if(r==="init"){const{existsSync:t,writeFileSync:n}=await import("node:fs"),s=o.configPath();if(t(s)&&e.force!==!0){m(`${s} already exists (use --force to overwrite).`),process.exitCode=1;return}const i=(await import("node:os")).homedir(),p={sources:[{type:"remember",name:"remember",dir:`${i}/.remember`,projectPath:i,windowDays:7}]};n(s,JSON.stringify(p,null,2)),b(`Wrote ${s} (1 source: remember). Add render/reverse sources per project as needed.`);return}if(r==="status"){const t=o.loadConfig();if(!t){m(`No bridge config at ${o.configPath()} \u2014 run \`wyrm bridge init\`.`),process.exitCode=1;return}const n=o.loadState();S("Bridge sources");for(const s of t.sources){const i=Object.keys(n).filter(c=>c.startsWith(s.name+":")),p=i.reduce((c,f)=>c+n[f].length,0);console.log(` ${s.name} [${s.type}] \u2192 ${"projectPath"in s?s.projectPath:""}${s.type==="remember"?` (${i.length} files tracked, ${p} sections bridged)`:""}`)}return}if(r==="run"){const t=o.loadConfig();if(!t){m(`No bridge config at ${o.configPath()} \u2014 run \`wyrm bridge init\`.`),process.exitCode=1;return}const n=_(),s=console.log;try{const i={wyrm_version:M().version??"unknown",compiled_at:new Date().toISOString()},p=await o.runBridge(n.getDatabase(),t,i,{only:typeof e.source=="string"?e.source:void 0,dryRun:e["dry-run"]===!0});for(const c of p)console.log(`bridge:${c.source} [${c.type}]${e["dry-run"]===!0?" (dry-run)":""} \u2014 stored ${c.stored} \xB7 queued ${c.queued} \xB7 dropped ${c.dropped} \xB7 rendered ${c.rendered}${c.note?` \xB7 ${c.note}`:""}`);if(e.watch===!0){const c=Number(e.interval)>=2?Number(e.interval):15;console.log(`bridge watch \u2014 re-rendering on write events (poll ${c}s, Ctrl-C to stop)`),await o.watchBridge(n.getDatabase(),t,()=>({wyrm_version:M().version??"unknown",compiled_at:new Date().toISOString()}),{intervalSec:c,onCycle:f=>{for(const g of f)console.log(`bridge:${g.source} re-rendered (${g.rendered} file(s), ${g.queued} edit(s) harvested)`)}})}return}finally{n.close(),console.log=s}}m("Usage: wyrm bridge <init|status|run> [--source <name>] [--dry-run] [--force]"),process.exitCode=1}async function et(l){const{positional:a,flags:e}=C(l);if(a[0]!=="log"){m("Usage: wyrm session log [--path <dir>] [--run <id>] [--objectives <t>] [--completed <t>] [--issues <t>] [--commits <t>] [--notes <t>]"),process.exitCode=1;return}const o=c=>typeof e[c]=="string"?e[c]:void 0,t=o("path"),n=o("project"),s=o("run")?.slice(0,64),i=console.log;console.log=()=>{};const p=_();try{let c=t?p.getProject(t):void 0;if(!c&&n&&(c=x(p,n)??void 0),c||(c=p.getProject(process.cwd())),!c){process.stderr.write(`wyrm session log: no Wyrm project for this directory, nothing recorded.
53
53
  `);return}const f={};for(const h of["objectives","completed","issues","commits","notes"]){const y=o(h);y&&(f[h]=y.slice(0,8e3))}const g=p.getDatabase();let u;s&&(u=g.prepare("SELECT id FROM sessions WHERE project_id = ? AND run_id = ? ORDER BY id DESC LIMIT 1").get(c.id,s)?.id),u?p.updateSession(u,f):(u=p.createSession(c.id,f).id,s&&g.prepare("UPDATE sessions SET run_id = ? WHERE id = ?").run(s,u)),process.stdout.write(`session:${u}
54
54
  `)}finally{p.close(),console.log=i}}async function tt(l){const{positional:a,flags:e}=C(l),r=a[0],o=p=>typeof e[p]=="string"?e[p]:void 0,{AgentPresence:t,processStartTime:n,presenceLiveness:s}=await import("./presence.js"),i=_();try{const p=new t(i.getDatabase());if(r==="announce"){const c=o("agent");if(!c){m("Usage: wyrm presence announce --agent <id> [--kind <kind>] [--project <name>|--path <dir>] [--quest <n>] [--pid <n>|--auto-pid] [--ttl <sec>] [--role <r>]"),process.exitCode=1;return}const f=o("path");let g=f?i.getProject(f):void 0;!g&&o("project")&&(g=x(i,o("project"))??void 0);const u=e["auto-pid"]===!0?process.ppid:Number(o("pid")),h={};let y="";if(Number.isInteger(u)&&u>0){const R=n(u);R?(h.pid=u,h.pid_start=R,y=` \xB7 pid ${u} (start ${R})`):y=` \xB7 pid ${u} unreadable \u2014 TTL fallback`}const w=Number(o("ttl")),$=p.announce({agent_id:c,agent_kind:o("kind")??"cli",project_id:g?.id??null,current_quest_id:o("quest")?Number(o("quest")):null,ttl_seconds:Number.isFinite(w)&&w>0?w:h.pid?86400:300,metadata:Object.keys(h).length?h:void 0,role:o("role")??null});process.stdout.write(`presence:${$.id} ${c}${y}
55
55
  `);return}if(r==="list"||r==="ls"||r===void 0){const c=i.getDatabase().prepare("SELECT * FROM agent_presence ORDER BY last_heartbeat DESC").all();if(c.length===0){process.stdout.write(`No agents on the board.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "contract": "wyrm-cloud-memory-v1",
3
- "version": "7.12.0",
3
+ "version": "8.0.0",
4
4
  "generatedBy": "scripts/gen-tool-manifest.mjs (from the dist/ registry modules — never hand-edited)",
5
5
  "tiers": {
6
6
  "core": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wyrm-mcp",
3
- "version": "7.12.0",
3
+ "version": "8.0.0",
4
4
  "mcpName": "lk.ghosts/wyrm",
5
5
  "description": "Local-first persistent memory for AI agents over MCP. Ground truths, negative learning (recorded failures block repeats), decision causality, hybrid recall, live memory streams, run-attributed fleet memory — a structured SQLite memory on your machine, no cloud or LLM required. Claude / Copilot / Cursor / Windsurf / Codex.",
6
6
  "type": "module",