wyrm-mcp 8.5.3 → 8.5.4

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.
@@ -1,8 +1,8 @@
1
- import{basename as X}from"path";import{createHash as Z}from"crypto";import{blockText as Q}from"./types.js";import{TOOL_ANNOTATIONS as T}from"../tool-annotations.js";import{renderResult as v,withGlyph as N,cacheKeyFor as O}from"../render.js";import{computeStaleness as ee}from"../intelligence.js";import{ValidationError as te,asInt as se,asString as ne}from"../validate.js";import{sanitizeActorId as ie}from"./boundary.js";import{makeEstimator as oe}from"../token-budget.js";function G(e){return!e||!e.thread.trim()?null:`### \u{1F9F5} Where We Left Off _(the living thread \u2014 what we were working on and thinking about)_
1
+ import{basename as X}from"path";import{createHash as Z}from"crypto";import{blockText as Q}from"./types.js";import{TOOL_ANNOTATIONS as T}from"../tool-annotations.js";import{renderResult as v,withGlyph as N,cacheKeyFor as O}from"../render.js";import{computeStaleness as ee}from"../intelligence.js";import{ValidationError as te,asInt as se,asString as ne}from"../validate.js";import{sanitizeActorId as ie}from"./boundary.js";import{makeEstimator as oe}from"../token-budget.js";import{resolveProjectForPath as re}from"../project-resolve.js";function G(e){return!e||!e.thread.trim()?null:`### \u{1F9F5} Where We Left Off _(the living thread \u2014 what we were working on and thinking about)_
2
2
  `+`_updated ${e.updated_at}${e.updated_by?" \xB7 "+e.updated_by:""} \xB7 ${e.turn_count} turn(s)_`+`
3
3
 
4
- `+e.thread.slice(-1800)}const re={tech_stack:"\u{1F6E0}\uFE0F Tech Stack",architecture:"\u{1F3D7}\uFE0F Architecture",constraint:"\u{1F6A7} Constraints",decision:"\u{1F4CB} Decisions",contact:"\u{1F464} Contacts",other:"\u{1F4CC} Other Facts"};function J(e,t){let s=`### \u{1F4DA} Project Ground Truths _(treat as authoritative \u2014 do not infer or contradict)_
5
- `;const n=new Map;for(const i of e)n.has(i.category)||n.set(i.category,[]),n.get(i.category).push(i);for(const[i,r]of n){s+=`**${re[i]??i}:**
4
+ `+e.thread.slice(-1800)}const ce={tech_stack:"\u{1F6E0}\uFE0F Tech Stack",architecture:"\u{1F3D7}\uFE0F Architecture",constraint:"\u{1F6A7} Constraints",decision:"\u{1F4CB} Decisions",contact:"\u{1F464} Contacts",other:"\u{1F4CC} Other Facts"};function J(e,t){let s=`### \u{1F4DA} Project Ground Truths _(treat as authoritative \u2014 do not infer or contradict)_
5
+ `;const n=new Map;for(const i of e)n.has(i.category)||n.set(i.category,[]),n.get(i.category).push(i);for(const[i,r]of n){s+=`**${ce[i]??i}:**
6
6
  `;for(const a of r){let p="";if(t){const l=ee(a);l!==null&&l>.7&&(p="[\u26A0\uFE0F STALE] ")}s+=`- ${p}**${a.key}:** ${a.value}${a.rationale?` _(${a.rationale})_`:""}
7
7
  `}}return s}const V=`
8
8
  SELECT * FROM quests
@@ -13,11 +13,11 @@ import{basename as X}from"path";import{createHash as Z}from"crypto";import{block
13
13
  LIMIT 5
14
14
  `;function Y(e){const t={critical:"\u{1F534}",high:"\u{1F7E0}",medium:"\u{1F7E1}",low:"\u{1F7E2}"};let s=`### \u{1F3AF} Active Quests
15
15
  `;for(const n of e)s+=`- ${t[n.priority]??"\u2022"} #${n.id}: ${n.title}
16
- `;return s}function q(e,t,s,n){return s?e.getProjectById(s):n?t.prepare("SELECT * FROM projects WHERE LOWER(name) LIKE LOWER(?) LIMIT 1").get(`%${n}%`):t.prepare(`
16
+ `;return s}function P(e,t,s,n){if(s)return e.getProjectById(s);if(n)return t.prepare("SELECT * FROM projects WHERE LOWER(name) LIKE LOWER(?) LIMIT 1").get(`%${n}%`);const{id:i}=re(t,process.cwd());return i!=null?e.getProjectById(i):t.prepare(`
17
17
  SELECT p.* FROM projects p
18
18
  JOIN sessions s ON s.project_id = p.id
19
19
  ORDER BY s.created_at DESC LIMIT 1
20
- `).get()}function P(e){const t=e.getAllProjects(20);return t.length===0?{content:[{type:"text",text:"No projects found. Run `wyrm_scan_projects` first."}],isError:!0}:{content:[{type:"text",text:`Project not found. Available projects:
20
+ `).get()}function q(e){const t=e.getAllProjects(20);return t.length===0?{content:[{type:"text",text:"No projects found. Run `wyrm_scan_projects` first."}],isError:!0}:{content:[{type:"text",text:`Project not found. Available projects:
21
21
  ${t.map(n=>`- ${n.name} (ID: ${n.id})`).join(`
22
22
  `)}`}],isError:!0}}const K=(e,t)=>{const s=e.session_id!==null?`
23
23
  Session logged: #${e.session_id}`:"";return N(t.brand,`Wyrm primed for **${e.project_name}** | ${e.truth_count} truth${e.truth_count!==1?"s":""} - ${e.quest_count} quest${e.quest_count!==1?"s":""} - ${e.memory_count} memor${e.memory_count!==1?"ies":"y"}${s}`)},L=(e,t)=>{const s=K(e,t);return e.sections.length>0?e.sections.map(n=>n.text).join(`
@@ -28,17 +28,17 @@ Session logged: #${e.session_id}`:"";return N(t.brand,`Wyrm primed for **${e.pro
28
28
 
29
29
  `+s:s+`
30
30
 
31
- _No memory stored yet -- start working and Wyrm will learn._`},A={summary:K};async function F(e,t,s){try{const{logSavings:n}=await import("../statusline.js"),i=Q(s.content[0]).length,r=Math.round(i/4),a=e.prepare("SELECT id FROM sessions WHERE project_id = ? ORDER BY created_at DESC LIMIT 1").get(t);n(e,"wyrm_session_prime","recovered_context",r,a?.id)}catch{}}const ce={continuity:{path:"continuity",tool:"wyrm_session_prime"},truths:{path:"truths",tool:"wyrm_truth_get"},scaffold:{path:"scaffolds",tool:"wyrm_context_build"},memory:{path:"memory",tool:"wyrm_recall"},quests:{path:"quests",tool:"wyrm_all_quests"}},ae=48,de=1200;async function pe(e,{store:t,raw:s,memory:n,truths:i,scaffolds:r,cache:a}){const{project_id:p,project_name:l,task:d,log_session:y}=e,c=ie(e.run_id);if(c===null)throw new te("run_id",'must be <=64 printable-ASCII chars (no ";")');const u=ne("role",e.role,{maxLen:200})??"",j=se("token_budget",e.token_budget,{min:1})??de,_=s(),w=()=>_.prepare("SELECT body_json FROM run_briefs WHERE run_id = ? AND role = ?").get(c,u),h=p||l?q(t,_,p,l):void 0;if((p||l)&&!h)return P(t);let S=w();if(S&&h){const o=JSON.parse(S.body_json);if(o.project_id!==h.id)return{content:[{type:"text",text:`Fleet brief for (run ${c}, role '${u}') is pinned to project '${o.project_name}' (ID: ${o.project_id}) \u2014 refusing to serve it for requested project '${h.name}' (ID: ${h.id}). Use a distinct role for a second project, or prime without run_id.`}],isError:!0}}if(!S){const o=h??q(t,_,p,l);if(!o)return P(t);const m=[],$=G(t.getContinuity(o.id));$&&m.push({kind:"continuity",text:$});const k=i.getCurrent(o.id);k.length>0&&m.push({kind:"truths",text:J(k,!1).slice(0,1200)});const x=d??(u||void 0);if(x){const b=r.findBest(x,o.id,.3);b&&m.push({kind:"scaffold",text:r.formatForContext(b,1500)})}const M=n.buildContextBrief(o.id,x??"general",{maxItems:10,minConfidence:.3});M.text&&m.push({kind:"memory",text:M.text.slice(0,2e3)});const R=_.prepare(V).all(o.id);R.length>0&&m.push({kind:"quests",text:Y(R).slice(0,800)});const D=oe(),I=[];let C=ae;for(const b of m){const W=D.count(b.text)+2;if(C+W<=j)I.push(b),C+=W;else{const H=ce[b.kind]??{path:b.kind,tool:"wyrm_search"},U=`[beyond token_budget] wyrm://project/${o.id}/${H.path} -- fetch via ${H.tool}`;I.push({kind:b.kind,text:U}),C+=D.count(U)+2}}let B=null;y&&(B=t.createSession(o.id,{objectives:d??"Session started via wyrm_session_prime",summary:""}).id);const z={project_id:o.id,project_name:o.name,truth_count:i.getStats(o.id).current,quest_count:R.length,memory_count:n.getStats(o.id).total,session_id:B,sections:I,run_id:c,role:u,token_budget:j};_.prepare("INSERT OR IGNORE INTO run_briefs (run_id, role, body_json, token_budget) VALUES (?, ?, ?, ?)").run(c,u,JSON.stringify(z),j),S=w()}const g=JSON.parse(S.body_json),E=v(g,L,A);if(e.for_spawn===!0){const o=v(g,L,{...A,exemptStructured:!0}),m=Q(o.content[0]),$=Z("sha256").update(m,"utf8").digest("hex"),k={project_id:g.project_id,project_name:g.project_name,session_id:g.session_id??null,sections:g.sections,for_spawn:!0,run_id:c,role:u,brief:m,brief_hash:$,brief_chars:m.length,cache_key:{table:"run_briefs",run_id:c,role:u}};await F(_,g.project_id,o);const x={content:[{type:"text",text:m}],structuredContent:k};return a.set(O("wyrm_session_prime",JSON.stringify(e)),x,3e4),x}return await F(_,g.project_id,E),a.set(O("wyrm_session_prime",JSON.stringify(e)),E,3e4),E}const Se=[{name:"wyrm_session_start",description:"Start or continue a session for a project",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project path"},objectives:{type:"string",description:"Session objectives"}},required:["projectPath"]},outputSchema:{type:"object",properties:{session_id:{type:"integer"},project:{type:"string"},date:{type:"string"},objectives:{type:["string","null"]}},required:["session_id","project","date","objectives"]},annotations:T.wyrm_session_start,aliases:[],handler:(e,{store:t})=>{const{projectPath:s,objectives:n}=e;let i=t.getProject(s);i||(i=t.registerProject(X(s),s));let r=t.getTodaySession(i.id);r?n&&(r=t.updateSession(r.id,{objectives:r.objectives?`${r.objectives}
31
+ _No memory stored yet -- start working and Wyrm will learn._`},A={summary:K};async function F(e,t,s){try{const{logSavings:n}=await import("../statusline.js"),i=Q(s.content[0]).length,r=Math.round(i/4),a=e.prepare("SELECT id FROM sessions WHERE project_id = ? ORDER BY created_at DESC LIMIT 1").get(t);n(e,"wyrm_session_prime","recovered_context",r,a?.id)}catch{}}const ae={continuity:{path:"continuity",tool:"wyrm_session_prime"},truths:{path:"truths",tool:"wyrm_truth_get"},scaffold:{path:"scaffolds",tool:"wyrm_context_build"},memory:{path:"memory",tool:"wyrm_recall"},quests:{path:"quests",tool:"wyrm_all_quests"}},de=48,pe=1200;async function ue(e,{store:t,raw:s,memory:n,truths:i,scaffolds:r,cache:a}){const{project_id:p,project_name:l,task:d,log_session:y}=e,c=ie(e.run_id);if(c===null)throw new te("run_id",'must be <=64 printable-ASCII chars (no ";")');const u=ne("role",e.role,{maxLen:200})??"",j=se("token_budget",e.token_budget,{min:1})??pe,_=s(),w=()=>_.prepare("SELECT body_json FROM run_briefs WHERE run_id = ? AND role = ?").get(c,u),h=p||l?P(t,_,p,l):void 0;if((p||l)&&!h)return q(t);let S=w();if(S&&h){const o=JSON.parse(S.body_json);if(o.project_id!==h.id)return{content:[{type:"text",text:`Fleet brief for (run ${c}, role '${u}') is pinned to project '${o.project_name}' (ID: ${o.project_id}) \u2014 refusing to serve it for requested project '${h.name}' (ID: ${h.id}). Use a distinct role for a second project, or prime without run_id.`}],isError:!0}}if(!S){const o=h??P(t,_,p,l);if(!o)return q(t);const m=[],$=G(t.getContinuity(o.id));$&&m.push({kind:"continuity",text:$});const k=i.getCurrent(o.id);k.length>0&&m.push({kind:"truths",text:J(k,!1).slice(0,1200)});const x=d??(u||void 0);if(x){const b=r.findBest(x,o.id,.3);b&&m.push({kind:"scaffold",text:r.formatForContext(b,1500)})}const M=n.buildContextBrief(o.id,x??"general",{maxItems:10,minConfidence:.3});M.text&&m.push({kind:"memory",text:M.text.slice(0,2e3)});const I=_.prepare(V).all(o.id);I.length>0&&m.push({kind:"quests",text:Y(I).slice(0,800)});const D=oe(),R=[];let C=de;for(const b of m){const W=D.count(b.text)+2;if(C+W<=j)R.push(b),C+=W;else{const H=ae[b.kind]??{path:b.kind,tool:"wyrm_search"},U=`[beyond token_budget] wyrm://project/${o.id}/${H.path} -- fetch via ${H.tool}`;R.push({kind:b.kind,text:U}),C+=D.count(U)+2}}let B=null;y&&(B=t.createSession(o.id,{objectives:d??"Session started via wyrm_session_prime",summary:""}).id);const z={project_id:o.id,project_name:o.name,truth_count:i.getStats(o.id).current,quest_count:I.length,memory_count:n.getStats(o.id).total,session_id:B,sections:R,run_id:c,role:u,token_budget:j};_.prepare("INSERT OR IGNORE INTO run_briefs (run_id, role, body_json, token_budget) VALUES (?, ?, ?, ?)").run(c,u,JSON.stringify(z),j),S=w()}const g=JSON.parse(S.body_json),E=v(g,L,A);if(e.for_spawn===!0){const o=v(g,L,{...A,exemptStructured:!0}),m=Q(o.content[0]),$=Z("sha256").update(m,"utf8").digest("hex"),k={project_id:g.project_id,project_name:g.project_name,session_id:g.session_id??null,sections:g.sections,for_spawn:!0,run_id:c,role:u,brief:m,brief_hash:$,brief_chars:m.length,cache_key:{table:"run_briefs",run_id:c,role:u}};await F(_,g.project_id,o);const x={content:[{type:"text",text:m}],structuredContent:k};return a.set(O("wyrm_session_prime",JSON.stringify(e)),x,3e4),x}return await F(_,g.project_id,E),a.set(O("wyrm_session_prime",JSON.stringify(e)),E,3e4),E}const Ee=[{name:"wyrm_session_start",description:"Start or continue a session for a project",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project path"},objectives:{type:"string",description:"Session objectives"}},required:["projectPath"]},outputSchema:{type:"object",properties:{session_id:{type:"integer"},project:{type:"string"},date:{type:"string"},objectives:{type:["string","null"]}},required:["session_id","project","date","objectives"]},annotations:T.wyrm_session_start,aliases:[],handler:(e,{store:t})=>{const{projectPath:s,objectives:n}=e;let i=t.getProject(s);i||(i=t.registerProject(X(s),s));let r=t.getTodaySession(i.id);r?n&&(r=t.updateSession(r.id,{objectives:r.objectives?`${r.objectives}
32
32
  ${n}`:n})):r=t.createSession(i.id,{objectives:n||""}),t.archiveOldSessions(i.id,10);const a={session_id:r.id,project:i.name,date:r.date,objectives:r.objectives||null};return v(a,(p,l)=>N(l.brand,`Session ${p.session_id} for ${p.project}`)+`
33
33
  **Date:** ${p.date}
34
34
  **Objectives:** ${p.objectives??"None set"}`)}},{name:"wyrm_session_update",description:"Update the current session with completed work, issues, or notes",inputSchema:{type:"object",properties:{projectPath:{type:"string"},completed:{type:"string",description:"What was completed"},issues:{type:"string"},commits:{type:"string",description:"Git commits made"},notes:{type:"string"}},required:["projectPath"]},outputSchema:{type:"object",properties:{session_id:{type:"integer"},project:{type:"string"},updated:{type:"array",items:{type:"string",enum:["completed","issues","commits","notes"]}}},required:["session_id","project","updated"]},annotations:T.wyrm_session_update,aliases:[],handler:(e,{store:t,indexing:s})=>{const{projectPath:n,completed:i,issues:r,commits:a,notes:p}=e,l=t.getProject(n);if(!l)return{content:[{type:"text",text:"Project not found"}],isError:!0};let d=t.getTodaySession(l.id);d||(d=t.createSession(l.id,{}));const y={};i&&(y.completed=d.completed?`${d.completed}
35
35
  ${i}`:i),r&&(y.issues=d.issues?`${d.issues}
36
36
  ${r}`:r),a&&(y.commits=d.commits?`${d.commits}
37
37
  ${a}`:a),p&&(y.notes=d.notes?`${d.notes}
38
- ${p}`:p),d=t.updateSession(d.id,y);const c=[i,r,p].filter(Boolean).join(" \xB7 ");c&&t.updateContinuity(l.id,c,"session_update"),s()?.enqueue("session",d.id,l.id);const u={session_id:d.id,project:l.name,updated:Object.keys(y)};return v(u,(j,_)=>N(_.brand,`Session updated for ${j.project}`))}},{name:"wyrm_session_rehydrate",description:"Lossless session rehydration. Given a past session ID, returns a complete briefing markdown a fresh AI agent can ingest to inherit prior state: objectives, completed work, notes, summary, current ground truths, open quests, validated patterns (artifacts), unresolved failure patterns. Cross-session continuity no other AI memory tool offers.",inputSchema:{type:"object",properties:{session_id:{type:"number",description:"ID of the session to rehydrate"},include_artifacts:{type:"boolean",description:"Include memory artifacts (default true)"},include_failures:{type:"boolean",description:"Include unresolved failure patterns (default true)"},max_truth_chars:{type:"number",description:"Cap on chars used for ground truths (default 2000)"}},required:["session_id"]},outputSchema:{type:"object",properties:{session_id:{type:"integer"},project_name:{type:"string"},project_path:{type:"string"},briefing_markdown:{type:"string"},context_chars:{type:"integer"},attached:{type:"object",properties:{quests:{type:"integer"},truths:{type:"integer"},artifacts:{type:"integer"},failures:{type:"integer"}},required:["quests","truths","artifacts","failures"]}},required:["session_id","project_name","project_path","briefing_markdown","context_chars","attached"]},annotations:T.wyrm_session_rehydrate,aliases:[],handler:(e,{rehydration:t})=>{const{session_id:s,include_artifacts:n,include_failures:i,max_truth_chars:r}=e,a=t.rehydrate(s,{include_artifacts:n,include_failures:i,max_truth_chars:r});return a?v({...a},p=>p.briefing_markdown,{exemptStructured:!0}):{content:[{type:"text",text:`Session #${s} not found.`}],isError:!0}}},{name:"wyrm_session_prime",description:"Use first at session start - load everything you need to know before starting work, and catch me up on where we left off last time: ground truths, memory brief, reasoning scaffold, and active quests in one call. First call every agent makes in a run.",inputSchema:{type:"object",properties:{project_id:{type:"number"},project_name:{type:"string"},task:{type:"string",description:"Drives scaffold + memory selection"},log_session:{type:"boolean",description:"Also log a session row"},run_id:{type:"string",description:"Fleet: brief cached per (run_id, role)"},role:{type:"string"},token_budget:{type:"number"},for_spawn:{type:"boolean"}}},outputSchema:{type:"object",properties:{project_id:{type:"integer"},project_name:{type:"string"},session_id:{type:["integer","null"]},sections:{type:"array",items:{type:"object",properties:{kind:{type:"string"},text:{type:"string"}},required:["kind","text"]}}},required:["project_id","project_name","session_id","sections"]},annotations:T.wyrm_session_prime,aliases:[],handler:async(e,{store:t,raw:s,memory:n,truths:i,scaffolds:r,cache:a})=>{const{project_id:p,project_name:l,task:d,log_session:y}=e;if(e.run_id!==void 0&&e.run_id!==null)return pe(e,{store:t,raw:s,memory:n,truths:i,scaffolds:r,cache:a});const c=q(t,s(),p,l);if(!c)return P(t);const u=[],j=G(t.getContinuity(c.id));j&&u.push({kind:"continuity",text:j});const _=i.getCurrent(c.id);if(_.length>0&&u.push({kind:"truths",text:J(_,!0).slice(0,1200)}),d){const f=r.findBest(d,c.id,.3);f&&u.push({kind:"scaffold",text:r.formatForContext(f,1500)})}const w=n.buildContextBrief(c.id,d??"general",{maxItems:10,minConfidence:.3});w.text&&u.push({kind:"memory",text:w.text.slice(0,2e3)});const h=s().prepare(V).all(c.id);h.length>0&&u.push({kind:"quests",text:Y(h).slice(0,800)});try{const f=s().prepare("SELECT COUNT(*) AS n FROM memory_artifacts WHERE project_id = ? AND needs_review = 1").get(c.id).n;f>0&&u.push({kind:"review_queue",text:`### \u{1F4E5} Review Queue
38
+ ${p}`:p),d=t.updateSession(d.id,y);const c=[i,r,p].filter(Boolean).join(" \xB7 ");c&&t.updateContinuity(l.id,c,"session_update"),s()?.enqueue("session",d.id,l.id);const u={session_id:d.id,project:l.name,updated:Object.keys(y)};return v(u,(j,_)=>N(_.brand,`Session updated for ${j.project}`))}},{name:"wyrm_session_rehydrate",description:"Lossless session rehydration. Given a past session ID, returns a complete briefing markdown a fresh AI agent can ingest to inherit prior state: objectives, completed work, notes, summary, current ground truths, open quests, validated patterns (artifacts), unresolved failure patterns. Cross-session continuity no other AI memory tool offers.",inputSchema:{type:"object",properties:{session_id:{type:"number",description:"ID of the session to rehydrate"},include_artifacts:{type:"boolean",description:"Include memory artifacts (default true)"},include_failures:{type:"boolean",description:"Include unresolved failure patterns (default true)"},max_truth_chars:{type:"number",description:"Cap on chars used for ground truths (default 2000)"}},required:["session_id"]},outputSchema:{type:"object",properties:{session_id:{type:"integer"},project_name:{type:"string"},project_path:{type:"string"},briefing_markdown:{type:"string"},context_chars:{type:"integer"},attached:{type:"object",properties:{quests:{type:"integer"},truths:{type:"integer"},artifacts:{type:"integer"},failures:{type:"integer"}},required:["quests","truths","artifacts","failures"]}},required:["session_id","project_name","project_path","briefing_markdown","context_chars","attached"]},annotations:T.wyrm_session_rehydrate,aliases:[],handler:(e,{rehydration:t})=>{const{session_id:s,include_artifacts:n,include_failures:i,max_truth_chars:r}=e,a=t.rehydrate(s,{include_artifacts:n,include_failures:i,max_truth_chars:r});return a?v({...a},p=>p.briefing_markdown,{exemptStructured:!0}):{content:[{type:"text",text:`Session #${s} not found.`}],isError:!0}}},{name:"wyrm_session_prime",description:"Use first at session start - load everything you need to know before starting work, and catch me up on where we left off last time: ground truths, memory brief, reasoning scaffold, and active quests in one call. First call every agent makes in a run.",inputSchema:{type:"object",properties:{project_id:{type:"number"},project_name:{type:"string"},task:{type:"string",description:"Drives scaffold + memory selection"},log_session:{type:"boolean",description:"Also log a session row"},run_id:{type:"string",description:"Fleet: brief cached per (run_id, role)"},role:{type:"string"},token_budget:{type:"number"},for_spawn:{type:"boolean"}}},outputSchema:{type:"object",properties:{project_id:{type:"integer"},project_name:{type:"string"},session_id:{type:["integer","null"]},sections:{type:"array",items:{type:"object",properties:{kind:{type:"string"},text:{type:"string"}},required:["kind","text"]}}},required:["project_id","project_name","session_id","sections"]},annotations:T.wyrm_session_prime,aliases:[],handler:async(e,{store:t,raw:s,memory:n,truths:i,scaffolds:r,cache:a})=>{const{project_id:p,project_name:l,task:d,log_session:y}=e;if(e.run_id!==void 0&&e.run_id!==null)return ue(e,{store:t,raw:s,memory:n,truths:i,scaffolds:r,cache:a});const c=P(t,s(),p,l);if(!c)return q(t);const u=[],j=G(t.getContinuity(c.id));j&&u.push({kind:"continuity",text:j});const _=i.getCurrent(c.id);if(_.length>0&&u.push({kind:"truths",text:J(_,!0).slice(0,1200)}),d){const f=r.findBest(d,c.id,.3);f&&u.push({kind:"scaffold",text:r.formatForContext(f,1500)})}const w=n.buildContextBrief(c.id,d??"general",{maxItems:10,minConfidence:.3});w.text&&u.push({kind:"memory",text:w.text.slice(0,2e3)});const h=s().prepare(V).all(c.id);h.length>0&&u.push({kind:"quests",text:Y(h).slice(0,800)});try{const f=s().prepare("SELECT COUNT(*) AS n FROM memory_artifacts WHERE project_id = ? AND needs_review = 1").get(c.id).n;f>0&&u.push({kind:"review_queue",text:`### \u{1F4E5} Review Queue
39
39
  ${f} captured memor${f===1?"y awaits":"ies await"} review (wyrm_review) \u2014 queued memory is invisible to recall until approved.`})}catch{}try{const{resolveEmbeddingState:f}=await import("../providers/embedding-provider.js"),o=f();let m=-1;try{m=s().prepare("SELECT COUNT(*) AS n FROM vectors").get().n}catch{m=0}o.resolved==="none"?u.push({kind:"vector_health",text:`### [!] Semantic Recall is OFF
40
40
  No embedding provider is active (${o.reason||"no provider"}) - recall is FTS5 keyword-only (~59.9% vs 72.2% hybrid recall@10). Fix: ${o.fix}`}):o.resolved==="local"?u.push({kind:"vector_health",text:`### [!] Semantic Recall is NON-SEMANTIC
41
41
  Provider 'local' (hash-384) is a test-grade fallback, not a real embedder - recall quality is at the FTS floor. Fix: ${o.fix}`}):m===0&&u.push({kind:"vector_health",text:`### [!] Vector Index is EMPTY
42
42
  Provider '${o.resolved}' (${o.model}) is configured but nothing is indexed - recall is running FTS5-only until the index is built. Fix: wyrm index rebuild`})}catch{}try{const{vaultAdvisory:f}=await import("../vault.js"),o=f();o.length>0&&u.push({kind:"vault",text:`### \u{1F510} Credential Vault
43
43
  `+o.map(m=>`- ${m}`).join(`
44
- `)})}catch{}let S=null;y&&(S=t.createSession(c.id,{objectives:d??"Session started via wyrm_session_prime",summary:""}).id);const g={project_id:c.id,project_name:c.name,truth_count:i.getStats(c.id).current,quest_count:h.length,memory_count:n.getStats(c.id).total,session_id:S,sections:u},E=v(g,L,A);return await F(s(),c.id,E),a.set(O("wyrm_session_prime",JSON.stringify(e)),E,3e4),E}}];export{Se as sessionToolSpecs};
44
+ `)})}catch{}let S=null;y&&(S=t.createSession(c.id,{objectives:d??"Session started via wyrm_session_prime",summary:""}).id);const g={project_id:c.id,project_name:c.name,truth_count:i.getStats(c.id).current,quest_count:h.length,memory_count:n.getStats(c.id).total,session_id:S,sections:u},E=v(g,L,A);return await F(s(),c.id,E),a.set(O("wyrm_session_prime",JSON.stringify(e)),E,3e4),E}}];export{Ee as sessionToolSpecs};
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "contract": "wyrm-cloud-memory-v2",
3
- "version": "8.5.3",
3
+ "version": "8.5.4",
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": "8.5.3",
3
+ "version": "8.5.4",
4
4
  "tools": [
5
5
  {
6
6
  "name": "wyrm_recall",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "contract": "wyrm-cloud-memory-v1",
3
- "version": "8.5.3",
3
+ "version": "8.5.4",
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": "8.5.3",
3
+ "version": "8.5.4",
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",