wyrm-mcp 9.2.0 → 9.2.2
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/README.md +2 -0
- package/dist/auto-runs.js +1 -0
- package/dist/autoconfig.js +3 -3
- package/dist/capabilities.js +3 -3
- package/dist/causality.js +13 -8
- package/dist/cli.js +25 -22
- package/dist/cloud/cli.js +3 -3
- package/dist/cloud/sync-daemon-entrypoint.js +2 -2
- package/dist/cloud/sync-daemon.js +1 -1
- package/dist/color.js +1 -0
- package/dist/database.js +3 -3
- package/dist/deprecations.js +2 -2
- package/dist/events.js +6 -6
- package/dist/federation.js +10 -10
- package/dist/handlers/arg-keys.js +1 -0
- package/dist/handlers/capture.js +58 -58
- package/dist/handlers/causality.js +8 -8
- package/dist/handlers/cloud.js +85 -83
- package/dist/handlers/companion.js +10 -10
- package/dist/handlers/entity.js +1 -1
- package/dist/handlers/events.js +12 -12
- package/dist/handlers/failure.js +12 -12
- package/dist/handlers/goals.js +4 -4
- package/dist/handlers/intelligence.js +63 -63
- package/dist/handlers/mcpclient.js +1 -1
- package/dist/handlers/presence.js +3 -3
- package/dist/handlers/project-arg.js +1 -0
- package/dist/handlers/project.js +1 -1
- package/dist/handlers/quest.js +5 -3
- package/dist/handlers/recall.js +18 -18
- package/dist/handlers/registry.js +1 -1
- package/dist/handlers/resources.js +1 -1
- package/dist/handlers/review.js +3 -3
- package/dist/handlers/run.js +3 -3
- package/dist/handlers/search.js +3 -3
- package/dist/handlers/session.js +24 -24
- package/dist/handlers/share.js +5 -5
- package/dist/handlers/shims.js +1 -1
- package/dist/handlers/thread.js +2 -2
- package/dist/http-auth.js +1 -1
- package/dist/http-fast.js +15 -15
- package/dist/index.js +2 -2
- package/dist/logger.js +2 -2
- package/dist/receipt.js +1 -1
- package/dist/rehydration.js +24 -9
- package/dist/render.js +3 -3
- package/dist/setup.js +2 -3
- package/dist/sqlite-busy.js +1 -1
- package/dist/stale-servers.js +2 -0
- package/dist/stdio-lifecycle.js +1 -0
- package/dist/storage/index.js +3 -3
- package/dist/tool-manifest-v2.json +1 -1
- package/dist/tool-manifest.json +1 -1
- package/dist/ui-dashboard.js +9 -8
- package/dist/validate.js +1 -1
- package/dist/vault-exec.js +4 -0
- package/dist/vault.js +1 -1
- package/dist/wyrm-cli.js +105 -142
- package/dist/wyrm-manifest.json +3 -3
- package/dist/wyrm-ui.js +14 -7
- package/dist/wyrm.js +2 -0
- package/package.json +6 -4
- package/scripts/hooks/wyrm-citation-audit.mjs +155 -46
- package/scripts/hooks/wyrm-prompt-context.mjs +300 -0
- package/scripts/hooks/wyrm-run-auto.mjs +148 -37
- package/scripts/hooks/wyrm-session-rehydrate.mjs +142 -24
package/dist/handlers/goals.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{TOOL_ANNOTATIONS as
|
|
2
|
-
${
|
|
3
|
-
`)}`})}},{name:"wyrm_goal_complete",description:"Manually mark a goal completed (e.g. when the agent reports done or the operator finishes it).",inputSchema:{type:"object",properties:{id:{type:"number"},note:{type:"string",description:"Final outcome note"}},required:["id"]},outputSchema:{type:"object",properties:{id:{type:"integer"},title:{type:"string"},status:{type:"string",enum:["completed"]}},required:["id","title","status"]},annotations:
|
|
4
|
-
${JSON.stringify(e.iterations,null,2)}`)}}];export{
|
|
1
|
+
import{TOOL_ANNOTATIONS as l}from"../tool-annotations.js";import{renderResult as u,withGlyph as p}from"../render.js";import{ValidationError as g,asId as _,asInt as h}from"../validate.js";function c(r){const o=_("id",r.id??r.goal_id,"goal");if(o===void 0)throw new g("id","is required (id or goal_id)");return o}const f=[{name:"wyrm_goal_set",description:"Set a persistent goal Wyrm will pursue across sessions. Goals carry title, success_criteria, deadline, priority, and a max_iterations cap. The wyrm-loop daemon picks the highest-priority active goal each tick.",inputSchema:{type:"object",properties:{title:{type:"string",description:"Short, action-oriented title"},description:{type:"string",description:"Longer context"},success_criteria:{type:"string",description:"How Wyrm knows when the goal is done \u2014 checked each iteration"},deadline:{type:"string",description:"Optional ISO timestamp (YYYY-MM-DD or full)"},priority:{type:"string",enum:["critical","high","medium","low"],description:"Default: medium"},max_iterations:{type:"number",description:"Cap on OODA iterations (default 20, max 200) \u2014 guard against infinite spinning"},projectPath:{type:"string",description:"Scope to a project (optional)"}},required:["title"]},outputSchema:{type:"object",properties:{id:{type:"integer"},title:{type:"string"},priority:{type:"string",enum:["critical","high","medium","low"]},status:{type:"string",enum:["active","paused","completed","abandoned"]},project_id:{type:["integer","null"]}},required:["id","title","priority","status","project_id"]},annotations:l.wyrm_goal_set,aliases:[],handler:(r,{projects:o,goals:i})=>{const t=r,a=t.projectPath?o.getProject(t.projectPath):null,e=i.set({project_id:a?.id??null,title:t.title,description:t.description,success_criteria:t.success_criteria,deadline:t.deadline,priority:t.priority,max_iterations:t.max_iterations});return u({id:e.id,title:e.title,priority:e.priority,status:e.status,project_id:e.project_id},(d,n)=>p(n.brand,`Goal #${d.id} set [${d.priority}]: ${d.title}`))}},{name:"wyrm_goal_list",description:"List goals, optionally filtered by status / project. Default: active goals, prioritized.",inputSchema:{type:"object",properties:{status:{type:"string",enum:["active","paused","completed","abandoned"]},projectPath:{type:"string"},limit:{type:"number"}}},outputSchema:{type:"object",properties:{count:{type:"integer"},goals:{type:"array",items:{type:"object",properties:{id:{type:"integer"},title:{type:"string"},priority:{type:"string",enum:["critical","high","medium","low"]},status:{type:"string",enum:["active","paused","completed","abandoned"]},iterations_count:{type:"integer"},max_iterations:{type:"integer"},progress_pct:{type:"integer",minimum:0},last_outcome:{type:["string","null"]}},required:["id","title","priority","status","iterations_count","max_iterations","progress_pct","last_outcome"]}}},required:["count","goals"]},annotations:l.wyrm_goal_list,aliases:[],handler:(r,{projects:o,goals:i})=>{const t=r,a=t.projectPath?o.getProject(t.projectPath):null,e=i.list({status:t.status,project_id:a?.id,limit:t.limit}),d={count:e.length,goals:e.map(n=>({id:n.id,title:n.title,priority:n.priority,status:n.status,iterations_count:n.iterations_count,max_iterations:n.max_iterations,progress_pct:Math.round(100*n.iterations_count/Math.max(1,n.max_iterations)),last_outcome:n.last_outcome}))};return u(d,(n,m)=>{if(n.count===0)return p(m.brand,"No goals.");const y=n.goals.map(s=>`${m.bullet} #${s.id} [${s.priority}/${s.status}] ${s.title} -- ${s.iterations_count}/${s.max_iterations} iter (${s.progress_pct}%)`+(s.last_outcome?` last:${s.last_outcome}`:""));return`${p(m.brand,`${n.count} goal(s):`)}
|
|
2
|
+
${y.join(`
|
|
3
|
+
`)}`})}},{name:"wyrm_goal_complete",description:"Manually mark a goal completed (e.g. when the agent reports done or the operator finishes it).",inputSchema:{type:"object",properties:{id:{type:"number"},note:{type:"string",description:"Final outcome note"}},required:["id"]},outputSchema:{type:"object",properties:{id:{type:"integer"},title:{type:"string"},status:{type:"string",enum:["completed"]}},required:["id","title","status"]},annotations:l.wyrm_goal_complete,aliases:[],handler:(r,{goals:o})=>{const i={id:c(r),note:r.note},t=o.complete(i.id,i.note);return t?u({id:t.id,title:t.title,status:"completed"},(a,e)=>p(e.ok,`Goal #${a.id} completed.`)):{content:[{type:"text",text:`Goal #${i.id} not found.`}],isError:!0}}},{name:"wyrm_goal_pause",description:"Pause a goal \u2014 wyrm-loop will skip it until resumed.",inputSchema:{type:"object",properties:{id:{type:"number"}},required:["id"]},outputSchema:{type:"object",properties:{id:{type:"integer"},title:{type:"string"},status:{type:"string",enum:["paused"]}},required:["id","title","status"]},annotations:l.wyrm_goal_pause,aliases:[],handler:(r,{goals:o})=>{const i={id:c(r)},t=o.pause(i.id);return t?u({id:t.id,title:t.title,status:"paused"},(a,e)=>p(e.paused,`Goal #${a.id} paused.`)):{content:[{type:"text",text:`Goal #${i.id} not found.`}],isError:!0}}},{name:"wyrm_goal_resume",description:"Resume a paused goal.",inputSchema:{type:"object",properties:{id:{type:"number"}},required:["id"]},outputSchema:{type:"object",properties:{id:{type:"integer"},title:{type:"string"},status:{type:"string",enum:["active"]}},required:["id","title","status"]},annotations:l.wyrm_goal_resume,aliases:[],handler:(r,{goals:o})=>{const i={id:c(r)},t=o.resume(i.id);return t?u({id:t.id,title:t.title,status:"active"},(a,e)=>p(e.resumed,`Goal #${a.id} resumed.`)):{content:[{type:"text",text:`Goal #${i.id} not found.`}],isError:!0}}},{name:"wyrm_goal_abandon",description:"Abandon a goal (won't be auto-pursued; record kept for audit).",inputSchema:{type:"object",properties:{id:{type:"number"},reason:{type:"string"}},required:["id"]},outputSchema:{type:"object",properties:{id:{type:"integer"},title:{type:"string"},status:{type:"string",enum:["abandoned"]}},required:["id","title","status"]},annotations:l.wyrm_goal_abandon,aliases:[],handler:(r,{goals:o})=>{const i={id:c(r),reason:r.reason},t=o.abandon(i.id,i.reason);return t?u({id:t.id,title:t.title,status:"abandoned"},(a,e)=>p(e.brand,`Goal #${a.id} abandoned.`)):{content:[{type:"text",text:`Goal #${i.id} not found.`}],isError:!0}}},{name:"wyrm_goal_iterations",description:"List the OODA iterations Wyrm has run for a goal \u2014 what it observed, decided, and resulted.",inputSchema:{type:"object",properties:{goal_id:{type:"number"},limit:{type:"number"}},required:["goal_id"]},outputSchema:{type:"object",properties:{goal_id:{type:"integer"},count:{type:"integer"},iterations:{type:"array",items:{type:"object",properties:{id:{type:"integer"},iteration_num:{type:"integer"},observe_summary:{type:["string","null"]},decided_action:{type:["string","null"]},action_result:{type:["string","null"]},outcome:{type:"string",enum:["progressed","blocked","done","error"]},ran_at:{type:"string"}},required:["id","iteration_num","outcome","ran_at"]}}},required:["goal_id","count","iterations"]},annotations:l.wyrm_goal_iterations,aliases:[],handler:(r,{goals:o})=>{const i={goal_id:c(r),limit:h("limit",r.limit,{min:1})},t=o.iterationsFor(i.goal_id,Math.min(100,i.limit??20)),a={goal_id:i.goal_id,count:t.length,iterations:t};return u(a,(e,d)=>e.count===0?p(d.brand,`Goal #${e.goal_id} has no iterations yet.`):`${p(d.brand,`${e.count} iteration(s) for goal #${e.goal_id}:`)}
|
|
4
|
+
${JSON.stringify(e.iterations,null,2)}`)}}];export{f as goalToolSpecs};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{TOOL_ANNOTATIONS as _}from"../tool-annotations.js";import{currentReadCacheKey as D}from"./boundary.js";import{resolvePolicy as
|
|
1
|
+
import{TOOL_ANNOTATIONS as _}from"../tool-annotations.js";import{currentReadCacheKey as D}from"./boundary.js";import{resolvePolicy as K,policyHash as z}from"../recall-policy.js";import{autoConfigureAll as G,findWyrmServerPath as Y,getDefaultDbPath as Q,getStatusSummary as Z,removeFromAll as J}from"../autoconfig.js";import{daemonOr as X}from"../daemon-writer.js";import{renderFailureStats as ee}from"../failure-patterns.js";import{computeStaleness as te}from"../intelligence.js";import{logger as ne}from"../logger.js";import{runMaintenance as oe}from"../maintenance.js";import{cache as S}from"../performance.js";import{createProvider as re}from"../providers/embedding-provider.js";import{reindexProjects as se}from"../reindex.js";import{asEnum as ae,asNumber as ie,requireBool as ce,requireInt as de,requireString as O}from"../validate.js";import{requireProjectFromArg as F}from"./project-arg.js";import{createVectorStore as le}from"../vectors.js";import{recordWrite as W,buildWriteDigest as ue}from"../receipts.js";import{renderResult as pe}from"../render.js";import{getActor as me}from"./boundary.js";const Ae=[{name:"wyrm_feedback",description:"Record whether a recalled knowledge artifact was useful. Adjusts confidence over time \u2014 successful reuse boosts it, failure lowers it. Call after applying a recalled pattern or lesson.",inputSchema:{type:"object",properties:{artifactId:{type:"number",description:"ID of the memory artifact (from wyrm_recall results)"},success:{type:"boolean",description:"Was this artifact helpful for the task?"}},required:["artifactId","success"]},annotations:_.wyrm_feedback,aliases:[],handler:async(p,f)=>{const{memory:l}=f,d=p,i=de("artifactId",d.artifactId,{min:1}),c=ce("success",d.success),r=l.get(i);if(!r)return{content:[{type:"text",text:`Artifact #${i} not found.`}],isError:!0};l.recordFeedback(i,c);const n=l.get(i),o={status:"captured",mode:"feedback",refs:[`mem:${i}`],artifact_id:i,success:c,problem:r.problem.slice(0,60),confidence:n.confidence,reuse_count:n.reuse_count,reuse_success_count:n.reuse_success_count,reuse_failure_count:n.reuse_failure_count};return pe(o,t=>`\u{F115D} Feedback recorded ${t.success?"\u2705":"\u274C"}
|
|
2
2
|
|
|
3
|
-
- Artifact #${
|
|
3
|
+
- Artifact #${t.artifact_id}: "${t.problem}"
|
|
4
4
|
- New confidence: ${(t.confidence*100).toFixed(0)}%
|
|
5
|
-
- Total uses: ${t.reuse_count} (\u2705 ${t.reuse_success_count} / \u274C ${t.reuse_failure_count})`}
|
|
5
|
+
- Total uses: ${t.reuse_count} (\u2705 ${t.reuse_success_count} / \u274C ${t.reuse_failure_count})`)}},{name:"wyrm_context_build",description:"Use to build a compact context brief for the current task under a token budget - relevant patterns, lessons, and truths in one formatted block; max_tokens elides low-score items to recallable stubs.",inputSchema:{type:"object",properties:{projectPath:{type:"string"},task:{type:"string",description:"The current task"},maxItems:{type:"number",description:"Default 10, max 20"},kinds:{type:"array",items:{type:"string"},description:"Artifact kinds (see wyrm_recall)"},minConfidence:{type:"number",description:"Default 0.3"},max_tokens:{type:"number",description:"Token budget; low-score items elide to stubs"},session_id:{type:"number",description:"Already-seen dedup"},strict_budget:{type:"boolean",description:"Also elide truths over budget"}},required:["projectPath","task"]},annotations:_.wyrm_context_build,aliases:[],handler:async(p,f)=>{const{cachedResponse:l,db:d,groundTruths:i,memory:c,runBudgetedContextBuild:r,scaffoldLib:n}=f,o=p||{},t=D(),e=me().run_id,s=K(d.getDatabase(),e),m=s.mode==="full"&&s.mutedIds.length===0&&s.challengedTruthIds.length===0,u=t&&!m?`${t}::${z(s)}`:t,{projectPath:h,task:a,maxItems:$,kinds:x,minConfidence:w,max_tokens:b,session_id:P,strict_budget:g}=p,y=F(d,d.getDatabase(),h);if(!y)return{content:[{type:"text",text:`Project not found: ${h}`}],isError:!0};if(!a||typeof a!="string"||!a.trim())return{content:[{type:"text",text:"wyrm_context_build: 'task' is required \u2014 pass a short description of the current task (not 'query')."}],isError:!0};if(b!=null&&!process.env.WYRM_DISABLE_TOKEN_BUDGET)return r({project:y,task:a,maxTokens:b,sessionId:P,strictBudget:g===!0,kinds:x,minConfidence:w,runId:e});const v=[],C=i.formatForContext(y.id);C&&v.push(C.slice(0,1200));const k=n.findBest(a,y.id);if(k){const I=n.formatForContext(k);v.push(I.slice(0,1500))}const j=c.buildContextBrief(y.id,a,{maxItems:Math.min($??10,20),kinds:x,minConfidence:w,runId:e});if(j.sections.length>0){const I=j.text.slice(0,2e3);v.push(I)}if(v.length===0)return{content:[{type:"text",text:`\u{F115D} **Context Brief**
|
|
6
6
|
|
|
7
7
|
No relevant memory found for this task yet.
|
|
8
8
|
|
|
@@ -12,9 +12,9 @@ As you work, use \`wyrm_remember\` to store:
|
|
|
12
12
|
- \u{1F4A1} Heuristics and shortcuts
|
|
13
13
|
- \u{1F9E0} Reasoning traces from complex problems
|
|
14
14
|
|
|
15
|
-
Future context briefs will become richer over time.`}]};const
|
|
15
|
+
Future context briefs will become richer over time.`}]};const E=c.getStats(y.id),L=i.getStats(y.id),B=`\u{F115D} **Context Brief** \u2014 "${a}"
|
|
16
16
|
|
|
17
|
-
`,R=[];
|
|
17
|
+
`,R=[];C&&R.push(C.slice(0,1200)),k&&R.push(n.formatForContext(k).slice(0,1500));const M=R.length>0?`<!-- cache-stable -->
|
|
18
18
|
${R.join(`
|
|
19
19
|
|
|
20
20
|
---
|
|
@@ -24,107 +24,107 @@ ${R.join(`
|
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
|
-
`:"",
|
|
27
|
+
`:"",V=j.sections.length>0?j.text.slice(0,2e3):"";let A=`${B}${M}${V}`;A+=`
|
|
28
28
|
|
|
29
|
-
_Brief: ${
|
|
30
|
-
- **Auto-cascade:** ${b.invalidated} downstream decision edge(s) invalidated (rested on the superseded value \u2014 review with \`wyrm_decision_downstream\`)`:"";return
|
|
29
|
+
_Brief: ${L.current} ground truth${L.current!==1?"s":""}, scaffold: ${k?k.scaffold.problem_type:"none"}, ${E.total} memory artifact${E.total!==1?"s":""}.`,j.sourceIds.length>0?A+=` Memory IDs: [${j.sourceIds.join(", ")}]; rate them with \`wyrm_capture\` mode=feedback._`:A+="_";try{const{logSavings:I}=await import("../statusline.js"),U=d.getDatabase(),H=U.prepare("SELECT id FROM sessions WHERE project_id = ? ORDER BY created_at DESC LIMIT 1").get(y.id),q=Math.round(M.length/4);q>0&&I(U,"wyrm_context_build","cached_preamble",Math.round(q*.9),H?.id)}catch{}const N=l(A);return u&&S.set(u,N,1e4),N}},{name:"wyrm_truth_set",description:"Use to lock in a confirmed fact or constraint as project ground truth - architecture, conventions, key decisions. Injected first into every brief; re-setting the same category+key supersedes (history kept).",inputSchema:{type:"object",properties:{projectPath:{type:"string"},category:{type:"string",description:"e.g. architecture, conventions"},key:{type:"string",description:"snake_case key"},value:{type:"string",description:"The statement"},rationale:{type:"string",description:"Why this is true"},source:{type:"string",description:"'user'|'derived'|'observed'|'confirmed'"},confidence:{type:"number",description:"0-1 (default 1.0)"},ttl_days:{type:"number",description:"Stale after N days"}},required:["projectPath","category","key","value"]},annotations:_.wyrm_truth_set,aliases:[],handler:async(p,f)=>{const{db:l,groundTruths:d,presence:i}=f,{projectPath:c,category:r,key:n,value:o,rationale:t,source:e,confidence:s,ttl_days:m}=p,u=O("category",r,{maxLen:200});O("key",n,{minLen:1}),O("value",o,{minLen:1});const h=ie("confidence",s,{min:0,max:1}),a=F(l,l.getDatabase(),c);if(!a)return{content:[{type:"text",text:`Project not found: ${c}`}],isError:!0};const{isConfigShapedTruth:$}=await import("../metabolize.js");if(process.env.WYRM_NO_TRUTH_LINT!=="1"&&$(n,o)){const g=(await import("../memory-artifacts.js")).MemoryArtifacts,T=new g(l.getDatabase()).add(a.id,{kind:"pattern",problem:`TRUTH-SHAPE LINT: "${n}" looks like a config assignment, not a ground truth: ${o.slice(0,120)}`,validatedFix:"If this really is a durable truth, re-set it as a full statement with a subject (or bypass once with WYRM_NO_TRUTH_LINT=1). If it is scan/tool config, it belongs in the data lake, not ground truths.",tags:["metabolize","truth-lint"],confidence:.5,needsReview:1}),v=W(l.getDatabase(),{tool:"wyrm_truth_set",outcome:"queued",refTable:"mem",refId:T.id,reason:"truth-shape lint \u2014 config-assignment shape queued for review instead of stored as a bare truth",source:e,projectId:a.id});return{content:[{type:"text",text:`\u{F115D} **Truth-shape lint** \u2014 "${n}" looks like a config assignment (\`ident = value\`), not a ground truth. Queued for review as mem:${T.id} instead of storing (receipt: ${v.outcome}). Re-phrase as a full statement, or set WYRM_NO_TRUTH_LINT=1 to bypass once.`}]}}const x={category:u,key:n,value:o,rationale:t,source:e,confidence:h??1,ttl_days:m};d.lastCascade=null;const w=await X("truth_set",a.id,x,()=>d.set(a.id,x));S.invalidate("wyrm_truth_get"),S.invalidate("wyrm_context_build");const b=d.lastCascade,P=b&&b.invalidated>0?`
|
|
30
|
+
- **Auto-cascade:** ${b.invalidated} downstream decision edge(s) invalidated (rested on the superseded value \u2014 review with \`wyrm_decision_downstream\`)`:"";return W(l.getDatabase(),{tool:"wyrm_truth_set",outcome:"stored",refTable:"truth",refId:w.id,reason:b&&b.invalidated>0?`supersede cascaded \u2014 ${b.invalidated} downstream decision edge(s) invalidated`:void 0,source:e,projectId:a.id}),{content:[{type:"text",text:`\u{F115D} **Ground Truth Set** \u2705
|
|
31
31
|
|
|
32
|
-
- **Category:** ${
|
|
32
|
+
- **Category:** ${r}
|
|
33
33
|
- **Key:** ${n}
|
|
34
|
-
- **Value:** ${o}${
|
|
35
|
-
- **Rationale:** ${
|
|
34
|
+
- **Value:** ${o}${t?`
|
|
35
|
+
- **Rationale:** ${t}`:""}${m?`
|
|
36
36
|
- **TTL:** ${m} days`:""}
|
|
37
|
-
- **ID:** ${
|
|
37
|
+
- **ID:** ${w.id}${P}
|
|
38
38
|
|
|
39
|
-
This truth will be injected at the top of every \`wyrm_context_build\` response for this project.`}]}}},{name:"wyrm_truth_get",description:"Use to read the established facts and ground rules for a codebase - validated truths the AI should treat as baseline, optionally filtered by category.",inputSchema:{type:"object",properties:{projectPath:{type:"string"},category:{type:"string",description:"Category filter"}},required:["projectPath"]},annotations:_.wyrm_truth_get,aliases:[],handler:async(p,f)=>{const{cachedResponse:
|
|
39
|
+
This truth will be injected at the top of every \`wyrm_context_build\` response for this project.`}]}}},{name:"wyrm_truth_get",description:"Use to read the established facts and ground rules for a codebase - validated truths the AI should treat as baseline, optionally filtered by category.",inputSchema:{type:"object",properties:{projectPath:{type:"string"},category:{type:"string",description:"Category filter"}},required:["projectPath"]},annotations:_.wyrm_truth_get,aliases:[],handler:async(p,f)=>{const{cachedResponse:l,db:d,groundTruths:i}=f,c=D(),{projectPath:r,category:n}=p,o=F(d,d.getDatabase(),r);if(!o)return{content:[{type:"text",text:`Project not found: ${r}`}],isError:!0};const t=i.getCurrent(o.id,n);if(t.length===0)return{content:[{type:"text",text:`\u{F115D} **Ground Truths**
|
|
40
40
|
|
|
41
|
-
${n?`No ground truths in category "${n}" for this project.`:"No ground truths stored yet. Use `wyrm_truth_set` to add validated facts."}`}]};const e=new Map;for(const u of
|
|
41
|
+
${n?`No ground truths in category "${n}" for this project.`:"No ground truths stored yet. Use `wyrm_truth_set` to add validated facts."}`}]};const e=new Map;for(const u of t)e.has(u.category)||e.set(u.category,[]),e.get(u.category).push(u);let s=`\u{F115D} **Ground Truths** \u2014 ${t.length} active
|
|
42
42
|
|
|
43
|
-
`;for(const[u,
|
|
44
|
-
`;for(const
|
|
45
|
-
_${
|
|
46
|
-
_staleness: ${(
|
|
47
|
-
`}
|
|
48
|
-
`}const m=
|
|
43
|
+
`;for(const[u,h]of e){s+=`### ${u}
|
|
44
|
+
`;for(const a of h){const $=te(a),w=$!==null&&$>.7?"[\u26A0\uFE0F STALE] ":"";s+=`- ${w}**${a.key}:** ${a.value}`,a.confidence<1&&(s+=` _(confidence: ${(a.confidence*100).toFixed(0)}%)_`),a.rationale&&(s+=`
|
|
45
|
+
_${a.rationale}_`),$!==null&&(s+=`
|
|
46
|
+
_staleness: ${($*100).toFixed(0)}% (TTL: ${a.ttl_days}d)_`),s+=`
|
|
47
|
+
`}s+=`
|
|
48
|
+
`}const m=l(s);return c&&S.set(c,m,15e3),m}},{name:"wyrm_scaffold_get",description:"Find the best reasoning scaffold for a task description. Returns the most relevant structured checklist, or null if no match meets the confidence threshold.",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project path (searches project + global)"},task:{type:"string",description:"Task description to match against scaffolds"},minConfidence:{type:"number",description:"Minimum match confidence 0-1 (default: 0.3)"}},required:["task"]},annotations:_.wyrm_scaffold_get,aliases:[],handler:async(p,f)=>{const{cachedResponse:l,db:d,scaffoldLib:i}=f,c=D(),{projectPath:r,task:n,minConfidence:o}=p,t=r?d.getProject(r)?.id??void 0:void 0,e=i.findBest(n,t,o);if(!e)return{content:[{type:"text",text:`\u{F115D} **Reasoning Scaffold**
|
|
49
49
|
|
|
50
50
|
No scaffold matched for: "${n}"
|
|
51
51
|
|
|
52
|
-
Use \`wyrm_scaffold_save\` to add scaffolds for this problem type.`}]};const
|
|
52
|
+
Use \`wyrm_scaffold_save\` to add scaffolds for this problem type.`}]};const s=i.formatForContext(e),m=l(`\u{F115D} **Reasoning Scaffold Match** (${(e.matchScore*100).toFixed(0)}% confidence)
|
|
53
53
|
|
|
54
|
-
${
|
|
54
|
+
${s}`);return c&&S.set(c,m,15e3),m}},{name:"wyrm_sync",description:"Sync database with .wyrm folders in all projects",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Sync specific project, or all if not specified"},direction:{type:"string",enum:["import","export","both"],description:"Sync direction"}}},annotations:_.wyrm_sync,aliases:[],handler:async(p,f)=>{const{db:l,sync:d}=f,{projectPath:i,direction:c}=p,r=c||"both";let n=0;if(i)l.getProject(i)&&((r==="import"||r==="both")&&d.importFromFolder(i),(r==="export"||r==="both")&&d.exportToFolder(i),n=1);else{const o=l.getAllProjects(1e3);for(const t of o)try{(r==="import"||r==="both")&&d.importFromFolder(t.path),(r==="export"||r==="both")&&d.exportToFolder(t.path),n++}catch{}}return{content:[{type:"text",text:`\u{F115D} Synced ${n} project(s)`}]}}},{name:"wyrm_stats",description:"Use for a health snapshot of the memory database - row counts, vector index coverage, usage.",inputSchema:{type:"object",properties:{view:{type:"string",enum:["failures"],description:"'failures' = prevented-repeat analytics"},projectPath:{type:"string",description:"failures view scope"}}},annotations:_.wyrm_stats,aliases:[],handler:async(p,f)=>{const{WRITE_TOOLS:l,analytics:d,cachedResponse:i,db:c,failures:r,getUsageStats:n,orchestrator:o,vectorStore:t}=f,e=p||{},s=D(),{view:m,projectPath:u}=p;if(ae("view",m,["failures"])==="failures"){const g=u?c.getProject(u):null,y=r.failureStats({projectId:g?.id??null});return{content:[{type:"text",text:ee(y)}],structuredContent:y}}const a=c.getStats(),$=S.stats(),x=n();let w="";if(t){const g=t.getStats(),y=g.byType||{},T=(y.artifact||0)+(y.quest||0)+(y.session||0),v=a.artifacts+a.quests+a.sessions,C=v>0?Math.min(100,T/v*100).toFixed(1):"0";w=`
|
|
55
55
|
**Vectors:**
|
|
56
|
-
- **Provider:** ${
|
|
57
|
-
- **Embeddings:** ${
|
|
58
|
-
- **Coverage:** ${
|
|
59
|
-
- **By Type:** ${Object.entries(
|
|
60
|
-
`}let b="";try{const
|
|
61
|
-
**Writes (24h):** ${Object.entries(
|
|
62
|
-
**Review queue:** ${
|
|
63
|
-
`}catch{}const
|
|
64
|
-
|
|
65
|
-
- **Projects:** ${
|
|
66
|
-
- **Sessions:** ${
|
|
67
|
-
- **Quests:** ${
|
|
68
|
-
- **Data Points:** ${
|
|
69
|
-
- **Active Tokens:** ~${
|
|
70
|
-
- **Database Size:** ${
|
|
71
|
-
|
|
72
|
-
**Cache:** ${
|
|
73
|
-
**Usage:** ${
|
|
74
|
-
|
|
75
|
-
Couldn't connect to ${m.name}. Check your configuration and try again.`}],isError:!0};const
|
|
56
|
+
- **Provider:** ${g.provider} (${g.model})
|
|
57
|
+
- **Embeddings:** ${g.total} vectors
|
|
58
|
+
- **Coverage:** ${C}% of embeddable content
|
|
59
|
+
- **By Type:** ${Object.entries(g.byType).map(([k,j])=>`${k}: ${j}`).join(", ")}
|
|
60
|
+
`}let b="";try{const g=ue(c.getDatabase(),1);b=`
|
|
61
|
+
**Writes (24h):** ${Object.entries(g.byOutcome).map(([T,v])=>`${T}: ${v}`).join(", ")||"none"}
|
|
62
|
+
**Review queue:** ${g.reviewQueueDepth} awaiting wyrm_review${g.reviewQueueDepth>0?" (invisible to recall until approved)":""}
|
|
63
|
+
`}catch{}const P=i(`\u{F115D} **Wyrm Statistics**
|
|
64
|
+
|
|
65
|
+
- **Projects:** ${a.projects}
|
|
66
|
+
- **Sessions:** ${a.sessions}
|
|
67
|
+
- **Quests:** ${a.quests}
|
|
68
|
+
- **Data Points:** ${a.dataPoints}
|
|
69
|
+
- **Active Tokens:** ~${a.totalTokens.toLocaleString()}
|
|
70
|
+
- **Database Size:** ${a.dbSize}
|
|
71
|
+
`+w+b+`
|
|
72
|
+
**Cache:** ${$.size} entries | Hit rate: ${x.cacheHitRate}
|
|
73
|
+
**Usage:** ${x.totalCalls} calls | ~${x.tokensSaved.toLocaleString()} tokens saved by cache`);return s&&S.set(s,P,15e3),P}},{name:"wyrm_vector_setup",description:"Configure embedding provider and download models. Supports local, OpenAI, and Ollama.",inputSchema:{type:"object",properties:{provider:{type:"string",enum:["auto","local","openai","ollama","none"],description:"Provider: auto-detect, local hash, OpenAI API, Ollama, or none"},model:{type:"string",description:"Model name (e.g., 'text-embedding-3-small' for OpenAI, 'nomic-embed-text' for Ollama)"},apiKey:{type:"string",description:"OpenAI API key (if using openai provider)"},ollamaUrl:{type:"string",description:"Ollama URL (default: http://localhost:11434)"}}},annotations:_.wyrm_vector_setup,aliases:[],handler:async(p,f)=>{const{db:l,indexingPipeline:d,memory:i,setVectorStore:c,vectorStore:r}=f,{provider:n,model:o,apiKey:t,ollamaUrl:e}=p,s={provider:n||"auto",model:o,apiKey:t,ollamaUrl:e};try{const m=re(s),u=await m.isReady();if(!u&&s.provider!=="none")return{content:[{type:"text",text:`\u274C **Provider Not Ready**
|
|
74
|
+
|
|
75
|
+
Couldn't connect to ${m.name}. Check your configuration and try again.`}],isError:!0};const h=le(s,l.getDatabase());c(h),i.setVectorStore(h),d&&d.updateStore(h);const a=h.getStats();return{content:[{type:"text",text:`\u2705 **Vector Setup Complete**
|
|
76
76
|
|
|
77
77
|
- **Provider:** ${m.name}
|
|
78
78
|
- **Model:** ${m.model}
|
|
79
79
|
- **Dimensions:** ${m.dimensions}
|
|
80
80
|
- **Status:** ${u?"Ready":"Ready (will auto-connect)"}
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
_Generate embeddings with \`wyrm index rebuild\` (CLI)._`}]}}catch(m){return{content:[{type:"text",text:`\u274C **Vector Setup Failed**
|
|
83
83
|
|
|
84
|
-
${String(m)}`}],isError:!0}}}},{name:"wyrm_reindex",description:"Rebuild embeddings for a project or all projects. Regenerates vectors for sessions, quests, data, and memory artifacts (the latter power hybrid recall).",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project to reindex, or leave empty for all projects"},dryRun:{type:"boolean",description:"Show what would be indexed without making changes"}}},annotations:_.wyrm_reindex,aliases:[],handler:async(p,f)=>{const{db:
|
|
84
|
+
${String(m)}`}],isError:!0}}}},{name:"wyrm_reindex",description:"Rebuild embeddings for a project or all projects. Regenerates vectors for sessions, quests, data, and memory artifacts (the latter power hybrid recall).",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project to reindex, or leave empty for all projects"},dryRun:{type:"boolean",description:"Show what would be indexed without making changes"}}},annotations:_.wyrm_reindex,aliases:[],handler:async(p,f)=>{const{db:l,failures:d,vectorStore:i}=f,{projectPath:c,dryRun:r}=p;if(!i)return{content:[{type:"text",text:`\u274C **Vector Indexing Disabled**
|
|
85
85
|
|
|
86
|
-
Run wyrm_vector_setup first to enable vector search.`}],isError:!0};let n=[];if(
|
|
86
|
+
Run wyrm_vector_setup first to enable vector search.`}],isError:!0};let n=[];if(c){const e=l.getProject(c);if(!e)return{content:[{type:"text",text:`\u274C **Project Not Found:** ${c}`}],isError:!0};n=[e.id]}else n=l.getAllProjects(1e3).map(s=>s.id);const{indexed:o,skipped:t}=await se(l.getDatabase(),i,n,{dryRun:r,onError:(e,s)=>ne.error(e,s)});return{content:[{type:"text",text:`\u{F115D} **Reindexing ${r?"(Dry Run)":"Complete"}**
|
|
87
87
|
|
|
88
88
|
- **Projects:** ${n.length}
|
|
89
89
|
- **Indexed:** ${o}
|
|
90
|
-
- **Skipped:** ${
|
|
90
|
+
- **Skipped:** ${t}
|
|
91
91
|
|
|
92
|
-
`+(
|
|
92
|
+
`+(r?"_Run without dryRun to actually index._":"_Vectors are now up to date._")}]}}},{name:"wyrm_maintenance",description:"Use for periodic upkeep of the memory store - archive old sessions, vacuum, prune stale events, sweep run quarantine. Admin-gated, destructive; fleets run it between waves.",inputSchema:{type:"object",properties:{vacuum:{type:"boolean",description:"Reclaim space"},archiveDays:{type:"number",description:"Archive sessions > N days"}}},annotations:_.wyrm_maintenance,aliases:[],handler:async(p,f)=>{const{db:l,failures:d,presence:i,sessionSeen:c}=f,r=p||{},{vacuum:n,archiveDays:o}=p,t=oe({db:l,sessionSeen:c(),failures:d,presence:i},{vacuum:n,archiveDays:o});return{content:[{type:"text",text:`\u{F115D} **Maintenance Complete**
|
|
93
93
|
|
|
94
|
-
`+
|
|
94
|
+
`+t.lines.map(s=>`- ${s}
|
|
95
95
|
`).join("")+`
|
|
96
|
-
**New Database Size:** ${
|
|
96
|
+
**New Database Size:** ${t.dbSize}`}]}}},{name:"wyrm_setup",description:"Auto-detect installed AI clients (VS Code, Claude Desktop, Cursor, Windsurf, Zed) and configure Wyrm's MCP server in all of them. Run this to connect Wyrm to a new AI or after switching providers.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["configure","check","remove"],description:"Action: configure (default), check status, or remove from all"},serverPath:{type:"string",description:"Override Wyrm server path (auto-detected if empty)"},dbPath:{type:"string",description:"Override database path (default: ~/.wyrm/wyrm.db)"}}},annotations:_.wyrm_setup,aliases:[],handler:async(p,f)=>{const{action:l,serverPath:d,dbPath:i}=p,c=l||"configure";if(c==="check")return{content:[{type:"text",text:Z()}]};if(c==="remove"){const e=J();return{content:[{type:"text",text:`\u{F115D} **Wyrm Removed**
|
|
97
97
|
|
|
98
98
|
Removed from ${e.filter(u=>u.action==="configured").length} AI client(s):
|
|
99
99
|
`+e.map(u=>`- ${u.client.icon} ${u.client.name}: ${u.message}`).join(`
|
|
100
100
|
`)+`
|
|
101
101
|
|
|
102
|
-
Run wyrm_setup again to reconnect.`}]}}const
|
|
102
|
+
Run wyrm_setup again to reconnect.`}]}}const r=G({serverPath:d||void 0,dbPath:i||void 0}),n=r.filter(e=>e.action==="configured"||e.action==="updated"),o=r.filter(e=>e.action==="failed");let t=`\u{F115D} **Wyrm Auto-Configure Complete**
|
|
103
103
|
|
|
104
|
-
`;
|
|
105
|
-
`;for(const e of
|
|
106
|
-
`}return o.length>0&&(
|
|
107
|
-
\u26A0\uFE0F ${o.length} client(s) failed. Check errors above.`),
|
|
104
|
+
`;t+=`Connected to ${n.length} AI client(s):
|
|
105
|
+
`;for(const e of r){const s=e.action==="configured"?"\u2705":e.action==="updated"?"\u{1F504}":e.action==="skipped"?"\u25CB":"\u274C";t+=`- ${s} ${e.client.icon} ${e.client.name}: ${e.message}
|
|
106
|
+
`}return o.length>0&&(t+=`
|
|
107
|
+
\u26A0\uFE0F ${o.length} client(s) failed. Check errors above.`),t+=`
|
|
108
108
|
|
|
109
|
-
Server: ${
|
|
110
|
-
DB: ${
|
|
109
|
+
Server: ${Y()}
|
|
110
|
+
DB: ${Q()}`,t+=`
|
|
111
111
|
|
|
112
|
-
_Switch AIs anytime \u2014 run wyrm_setup again to reconnect._`,{content:[{type:"text",text:
|
|
112
|
+
_Switch AIs anytime \u2014 run wyrm_setup again to reconnect._`,{content:[{type:"text",text:t}]}}},{name:"wyrm_usage",description:"View token usage stats, cache hit rates, and estimated cost savings. Helps monitor and optimize AI credit consumption.",inputSchema:{type:"object",properties:{last:{type:"number",description:"Show stats for last N calls (default: all)"},reset:{type:"boolean",description:"Reset usage counters"}}},annotations:_.wyrm_usage,aliases:[],handler:async(p,f)=>{const{cachedResponse:l,getUsageStats:d,responseFingerprints:i,usageLog:c}=f,{last:r,reset:n}=p;if(n)return c.length=0,i.clear(),S.invalidate(),{content:[{type:"text",text:"\u{F115D} Usage counters reset, caches cleared."}]};const o=d(r),t=S.stats();let e=`\u{F115D} **Wyrm Usage Report**
|
|
113
113
|
|
|
114
|
-
`;if(e+=`## Overview${
|
|
114
|
+
`;if(e+=`## Overview${r?` (last ${r} calls)`:""}
|
|
115
115
|
`,e+=`- **Total Calls:** ${o.totalCalls}
|
|
116
116
|
`,e+=`- **Cache Hits:** ${o.cachedCalls} (${o.cacheHitRate})
|
|
117
117
|
`,e+=`- **Tokens In:** ~${o.totalTokensIn.toLocaleString()}
|
|
118
118
|
`,e+=`- **Tokens Out:** ~${o.totalTokensOut.toLocaleString()}
|
|
119
119
|
`,e+=`- **Tokens Saved (cache):** ~${o.tokensSaved.toLocaleString()}
|
|
120
120
|
`,e+=`- **Avg Response:** ${o.avgResponseMs}ms
|
|
121
|
-
`,e+=`- **Active Cache Entries:** ${
|
|
121
|
+
`,e+=`- **Active Cache Entries:** ${t.size}
|
|
122
122
|
|
|
123
123
|
`,o.topTools.length>0){e+=`## Top Tools by Token Usage
|
|
124
|
-
`;for(const
|
|
124
|
+
`;for(const h of o.topTools)e+=`- **${h.tool}:** ${h.calls} calls, ~${h.tokens.toLocaleString()} tokens
|
|
125
125
|
`;e+=`
|
|
126
|
-
`}const
|
|
127
|
-
`,e+=`- **Input:** $${
|
|
126
|
+
`}const s=o.totalTokensIn/1e6*15,m=o.totalTokensOut/1e6*75,u=o.tokensSaved/1e6*75;return e+=`## Estimated Cost (Claude Opus rates)
|
|
127
|
+
`,e+=`- **Input:** $${s.toFixed(4)}
|
|
128
128
|
`,e+=`- **Output:** $${m.toFixed(4)}
|
|
129
129
|
`,e+=`- **Saved by cache:** $${u.toFixed(4)}
|
|
130
|
-
`,e+=`- **Net cost:** $${(
|
|
130
|
+
`,e+=`- **Net cost:** $${(s+m-u).toFixed(4)}`,l(e,!0)}}];export{Ae as intelligenceToolSpecs};
|
|
@@ -3,4 +3,4 @@ import{TOOL_ANNOTATIONS as c}from"../tool-annotations.js";const d=[{name:"wyrm_m
|
|
|
3
3
|
${t}`}]}}},{name:"wyrm_mcp_tools",description:"List the tools a registered server exposes (one-shot tools/list call over MCP).",inputSchema:{type:"object",properties:{server_name:{type:"string"}},required:["server_name"]},annotations:c.wyrm_mcp_tools,aliases:[],handler:async(o,n)=>{const{mcpClient:r,server:s}=n,e=o,t=await r.listTools(e.server_name);if(!t)return{content:[{type:"text",text:`Could not reach server '${e.server_name}' (not registered, disabled, or spawn failed).`}],isError:!0};const a=t.map(i=>`\u2022 ${i.name}${i.description?` \u2014 ${i.description.slice(0,120)}`:""}`).join(`
|
|
4
4
|
`);return{content:[{type:"text",text:`\u{F115D} ${t.length} tool(s) on ${e.server_name}:
|
|
5
5
|
${a}`}]}}},{name:"wyrm_mcp_disable",description:"Disable an MCP server config (kept in DB but won't be spawned).",inputSchema:{type:"object",properties:{server_name:{type:"string"}},required:["server_name"]},annotations:c.wyrm_mcp_disable,aliases:[],handler:async(o,n)=>{const{mcpClient:r}=n,s=o,e=r.disable(s.server_name);return{content:[{type:"text",text:e?`\u{F115D} ${s.server_name} disabled.`:`${s.server_name} not found.`}],isError:!e}}},{name:"wyrm_call_external",description:"Use to call a tool on an outbound MCP server registered via wyrm_mcp - lazily spawned over stdio, result returned and logged to external_call_log.",inputSchema:{type:"object",properties:{server:{type:"string",description:"Registered server_name"},tool:{type:"string",description:"Tool name to invoke"},args:{type:"object",description:"Tool arguments"}},required:["server","tool"]},annotations:c.wyrm_call_external,aliases:[],handler:async(o,n)=>{const{mcpClient:r,server:s}=n,e=o,t=await r.call(e.server,e.tool,e.args??{});return t.ok?{content:[{type:"text",text:`\u{F115D} ${e.server}.${e.tool} (${t.latency_ms}ms):
|
|
6
|
-
${JSON.stringify(t.result,null,2)}`}]}:{content:[{type:"text",text:`\u274C ${e.server}.${e.tool}: ${t.error}`}],isError:!0}}},{name:"wyrm_tool_analytics",description:"Wyrm watches Wyrm \u2014 per-tool usage stats (volume, error rate, p50/p95 latency) over a configurable window. Use to find under-performing tools and tune retrieval/ranking. Pass mode='overview' for the total summary, 'usage' for per-tool, 'errors' for recent error sample.",inputSchema:{type:"object",properties:{mode:{type:"string",enum:["overview","usage","errors"],description:"Which view (default: usage)"},days:{type:"number",description:"Window in days (default 30)"},tool_name:{type:"string",description:"Required when mode='errors'"},limit:{type:"number",description:"Max rows (default 100 for usage, 10 for errors)"}}},annotations:c.wyrm_tool_analytics,aliases:[],handler:async(o,n)=>{const{toolAnalytics:r}=n,{mode:s,days:e,tool_name:t,limit:a}=o,i=s??"usage";if(i==="overview")return{content:[{type:"text",text:JSON.stringify(r.overview({days:e}),null,2)}]};if(i==="errors"){if(!t)return{content:[{type:"text",text:"tool_name required for mode=errors"}],isError:!0};const m=r.recentErrors(t,a??10);return{content:[{type:"text",text:JSON.stringify(m,null,2)}]}}const l=r.usageStats({days:e,limit:a??100});return{content:[{type:"text",text:JSON.stringify(l,null,2)}]}}}];export{d as mcpclientToolSpecs};
|
|
6
|
+
${JSON.stringify(t.result,null,2)}`}]}:{content:[{type:"text",text:`\u274C ${e.server}.${e.tool}: ${String(t.error).replace(/\bwyrm_mcp_register\b/g,"wyrm_mcp action=register")}`}],isError:!0}}},{name:"wyrm_tool_analytics",description:"Wyrm watches Wyrm \u2014 per-tool usage stats (volume, error rate, p50/p95 latency) over a configurable window. Use to find under-performing tools and tune retrieval/ranking. Pass mode='overview' for the total summary, 'usage' for per-tool, 'errors' for recent error sample.",inputSchema:{type:"object",properties:{mode:{type:"string",enum:["overview","usage","errors"],description:"Which view (default: usage)"},days:{type:"number",description:"Window in days (default 30)"},tool_name:{type:"string",description:"Required when mode='errors'"},limit:{type:"number",description:"Max rows (default 100 for usage, 10 for errors)"}}},annotations:c.wyrm_tool_analytics,aliases:[],handler:async(o,n)=>{const{toolAnalytics:r}=n,{mode:s,days:e,tool_name:t,limit:a}=o,i=s??"usage";if(i==="overview")return{content:[{type:"text",text:JSON.stringify(r.overview({days:e}),null,2)}]};if(i==="errors"){if(!t)return{content:[{type:"text",text:"tool_name required for mode=errors"}],isError:!0};const m=r.recentErrors(t,a??10);return{content:[{type:"text",text:JSON.stringify(m,null,2)}]}}const l=r.usageStats({days:e,limit:a??100});return{content:[{type:"text",text:JSON.stringify(l,null,2)}]}}}];export{d as mcpclientToolSpecs};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{TOOL_ANNOTATIONS as d}from"../tool-annotations.js";import{asString as
|
|
2
|
-
`);return{content:[{type:"text",text:`\u{F115D} ${
|
|
3
|
-
${p}`}]}}},{name:"wyrm_presence_release",description:"Use on clean agent exit: removes the presence row and drops every quest claim the agent holds, so queued work is immediately re-claimable by the rest of the fleet instead of waiting out the TTL. Idempotent - safe to call twice.",inputSchema:{type:"object",properties:{agent_id:{type:"string",description:"Agent ID to release"}},required:["agent_id"]},annotations:d.wyrm_presence_release,aliases:[],handler:async(n,
|
|
1
|
+
import{TOOL_ANNOTATIONS as d}from"../tool-annotations.js";import{asString as f,requireString as u}from"../validate.js";const j=[{name:"wyrm_presence_announce",description:"Use from every fleet agent to announce presence and heartbeat liveness: upserts a TTL row that orchestrators and sibling agents poll before assigning or stealing work, stamped with the ambient run_id and the agent's fleet role so the board reads per-run. Re-announce to heartbeat; dead sessions expire by TTL and are reaped by wyrm_maintenance. Announce BEFORE wyrm_quest_claim - claims require a presence row.",inputSchema:{type:"object",properties:{agent_id:{type:"string",description:"Stable per-session identifier (e.g. 'claude-pid12345')"},agent_kind:{type:"string",description:"'claude', 'codex', 'cursor', 'human', etc."},projectPath:{type:"string",description:"Project path (optional)"},current_file:{type:"string",description:"File currently being edited"},current_symbol:{type:"string",description:"Symbol currently focused"},current_quest_id:{type:"number",description:"Quest currently being worked on"},ttl_seconds:{type:"number",description:"Heartbeat TTL \u2014 agent is considered live for this long after announce (default 300)"},metadata:{type:"object",description:"Caller-defined extras"},role:{type:"string",description:"Fleet role (default: run_agents membership)"}},required:["agent_id","agent_kind"]},annotations:d.wyrm_presence_announce,aliases:[],handler:async(n,s)=>{const{db:i,presence:t}=s,c=n||{},{agent_id:r,agent_kind:a,projectPath:o,current_file:p,current_symbol:e,current_quest_id:l,ttl_seconds:g,metadata:_}=n,m=f("role",n.role,{maxLen:200});u("agent_id",r,{minLen:1,maxLen:200}),u("agent_kind",a,{minLen:1,maxLen:200});const y=o?i.getProject(o):null,h=t.announce({agent_id:r,agent_kind:a,project_id:y?.id??null,current_file:p,current_symbol:e,current_quest_id:l,ttl_seconds:g,metadata:_,role:m});return{content:[{type:"text",text:`\u{F115D} Presence announced \u2014 ${a}:${r} (TTL ${h.ttl_seconds}s)`}]}}},{name:"wyrm_presence_list",description:"Use from an orchestrator to see which agents are live right now (heartbeat within TTL) before dispatching work - optionally scoped to a project or to agents touching a specific file, so file-level collisions surface before they happen.",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project path (optional)"},on_file:{type:"string",description:"Filter to agents working on this file (optional)"}}},annotations:d.wyrm_presence_list,aliases:[],handler:async(n,s)=>{const{db:i,presence:t}=s,{projectPath:c,on_file:r}=n,a=c?i.getProject(c):null,o=r?t.agentsOnFile(r):t.liveAgents(a?.id??null);if(o.length===0)return{content:[{type:"text",text:"\u{F115D} No live agents."}]};const p=o.map(e=>{const l=[e.current_file&&`file:${e.current_file}`,e.current_symbol&&`sym:${e.current_symbol}`,e.current_quest_id&&`quest:${e.current_quest_id}`].filter(Boolean).join(" ");return`\u2022 ${e.agent_kind}:${e.agent_id}${l?` \u2014 ${l}`:""} (last ${e.last_heartbeat})`}).join(`
|
|
2
|
+
`);return{content:[{type:"text",text:`\u{F115D} ${o.length} live agent(s):
|
|
3
|
+
${p}`}]}}},{name:"wyrm_presence_release",description:"Use on clean agent exit: removes the presence row and drops every quest claim the agent holds, so queued work is immediately re-claimable by the rest of the fleet instead of waiting out the TTL. Idempotent - safe to call twice.",inputSchema:{type:"object",properties:{agent_id:{type:"string",description:"Agent ID to release"}},required:["agent_id"]},annotations:d.wyrm_presence_release,aliases:[],handler:async(n,s)=>{const{presence:i}=s,{agent_id:t}=n;return{content:[{type:"text",text:i.release(t)?`\u{F115D} ${t} released.`:`\u{F115D} ${t} not present (already released).`}]}}}];export{j as presenceToolSpecs};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{resolveProjectForPath as c}from"../project-resolve.js";import{ValidationError as n}from"../validate.js";const i=r=>r==null||r==="";function l(r,o,e){if(i(e)){let t=null;try{({root:t}=c(o,process.cwd()))}catch{t=null}return t?r.getProject(t)??null:null}if(typeof e!="string")throw new n("projectPath","must be a string");return r.getProject(e)??null}function d(r,o,e){const t=l(r,o,e);if(!t&&i(e))throw new n("projectPath","is required (the working directory is not inside a registered project)");return t}export{l as projectFromArg,d as requireProjectFromArg};
|
package/dist/handlers/project.js
CHANGED
|
@@ -13,7 +13,7 @@ Added to watch list for future auto-scans.`:""}`}]}}},{name:"wyrm_list_projects"
|
|
|
13
13
|
|
|
14
14
|
`),s=r(`\u{F115D} **${e.length} Projects**
|
|
15
15
|
|
|
16
|
-
${u}`);return o&&y.set(o,s,6e4),s}},{name:"wyrm_project_context",description:"Get full context for a specific project including recent sessions, quests, and stored context",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project path"},projectName:{type:"string",description:"Or project name"}}},annotations:m.wyrm_project_context,aliases:[],handler:async(p,l)=>{const{cachedResponse:r,db:t}=l,o=h(),{projectPath:a,projectName:d}=p;let e=a?t.getProject(a):void 0;if(!e&&d&&(e=t.getProjectByName(d)),!e)return{content:[{type:"text",text:"Project not found.
|
|
16
|
+
${u}`);return o&&y.set(o,s,6e4),s}},{name:"wyrm_project_context",description:"Get full context for a specific project including recent sessions, quests, and stored context",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project path"},projectName:{type:"string",description:"Or project name"}}},annotations:m.wyrm_project_context,aliases:[],handler:async(p,l)=>{const{cachedResponse:r,db:t}=l,o=h(),{projectPath:a,projectName:d}=p;let e=a?t.getProject(a):void 0;if(!e&&d&&(e=t.getProjectByName(d)),!e)return{content:[{type:"text",text:"Project not found. Register it first (wyrm_project action=scan)."}],isError:!0};const u=t.getRecentSessions(e.id,10),s=t.getPendingQuests(e.id).map(j=>({title:j.title,priority:j.priority})),i=t.getAllContext(e.id),n=g({name:e.name,stack:e.stack},i,u,s),c=r(`\u{F115D} **Context for ${e.name}**
|
|
17
17
|
|
|
18
18
|
${n}`);return o&&y.set(o,c,3e4),c}},{name:"wyrm_global_context",description:"Get overview of all projects, pending quests across projects, and global context",inputSchema:{type:"object",properties:{includeQuests:{type:"boolean",description:"Include all pending quests"},maxProjects:{type:"number",description:"Max projects to include"}}},annotations:m.wyrm_global_context,aliases:[],handler:async(p,l)=>{const{cachedResponse:r,db:t}=l,o=h(),{includeQuests:a,maxProjects:d}=p,e=t.getAllProjects(d||20),u=t.getAllGlobalContext();let s=`\u{F115D} **Wyrm Global Overview**
|
|
19
19
|
|
package/dist/handlers/quest.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import{TOOL_ANNOTATIONS as
|
|
2
|
-
`)
|
|
1
|
+
import{TOOL_ANNOTATIONS as m}from"../tool-annotations.js";import{renderResult as y,withGlyph as _,cacheKeyFor as b}from"../render.js";import{ValidationError as S,asEnum as f,asId as T,asInt as j,requireString as q}from"../validate.js";import{recordWrite as I}from"../receipts.js";import{refreshThread as h}from"../thread-assemble.js";import{projectFromArg as Q}from"./project-arg.js";function w(e){const s=e.quest_id??e.questId??e.id,o=T("quest_id",s,"quest");if(o===void 0)throw new S("quest_id",'is required (quest_id, questId, id, or a "quest:N" ref)');return o}const x=50,F=[{name:"wyrm_quest_add",description:"Add a quest (task) to a project",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project path"},title:{type:"string",description:"Quest title"},description:{type:"string",description:"Quest description"},priority:{type:"string",enum:["critical","high","medium","low"]},tags:{type:"string",description:"Comma-separated tags"}},required:["projectPath","title"]},outputSchema:{type:"object",properties:{id:{type:"integer"},title:{type:"string"},priority:{type:"string",enum:["critical","high","medium","low"]},project:{type:"string"}},required:["id","title","priority","project"]},annotations:m.wyrm_quest_add,aliases:[],handler:(e,{store:s,indexing:o,raw:t})=>{const{projectPath:n,description:r,priority:a,tags:p}=e,d=q("title",e.title,{minLen:1}),i=f("priority",a,["critical","high","medium","low"],"medium"),u=Q(s,t(),n);if(!u)return{content:[{type:"text",text:"Project not found"}],isError:!0};const l=s.addQuest(u.id,d,r,i,p);o()?.enqueue("quest",l.id,u.id);const g=I(t(),{tool:"wyrm_quest_add",outcome:"stored",refTable:"quest",refId:l.id,projectId:u.id});return h(t(),u.id),y({id:l.id,title:d,priority:i,project:u.name,receipt:g},(c,$)=>_($.brand,`Quest #${c.id} added: ${c.title}`))}},{name:"wyrm_quest_complete",description:"Mark a quest as completed",inputSchema:{type:"object",properties:{questId:{type:"number",description:"Quest ID"}},required:["questId"]},outputSchema:{type:"object",properties:{id:{type:"integer"},title:{type:"string"},status:{type:"string",enum:["completed"]}},required:["id","title","status"]},annotations:m.wyrm_quest_complete,aliases:[],handler:(e,{store:s,raw:o})=>{const t=w(e),n=s.updateQuest(t,"completed");return n?(h(o(),n.project_id),y({id:t,title:n.title,status:"completed"},(r,a)=>_(a.brand,`Quest #${r.id} completed: ${r.title}`))):{content:[{type:"text",text:`Quest #${t} not found.`}],isError:!0}}},{name:"wyrm_all_quests",description:"Get all pending quests across all projects",inputSchema:{type:"object",properties:{priority:{type:"string",enum:["critical","high","medium","low"],description:"Filter by priority"}}},outputSchema:{type:"object",properties:{count:{type:"integer"},quests:{type:"array",items:{type:"object",properties:{id:{type:"integer"},title:{type:"string"},priority:{type:"string",enum:["critical","high","medium","low"]},project:{type:["string","null"]}},required:["id","title","priority","project"]}}},required:["count","quests"]},annotations:m.wyrm_all_quests,aliases:[],handler:(e,{store:s,cache:o})=>{const t=f("priority",e.priority,["critical","high","medium","low"]),n=j("limit",e.limit,{min:1,max:1e3,default:x});let r=s.getAllPendingQuests();t&&(r=r.filter(i=>i.priority===t));const a=r.slice(0,n),p={count:a.length,total:r.length,quests:a.map(i=>({id:i.id,title:i.title,priority:i.priority,project:i.project_name??null}))},d=y(p,(i,u)=>{const l=i.quests.map(c=>`${u.bullet} [${c.priority}]${c.project?` [${c.project}]`:""} #${c.id}: ${c.title}`).join(`
|
|
2
|
+
`),g=i.total>i.count?`
|
|
3
3
|
|
|
4
|
-
${
|
|
4
|
+
${i.total-i.count} more not shown (raise limit to see them).`:"";return`${_(u.brand,`**${i.total} Pending Quests**`)}
|
|
5
|
+
|
|
6
|
+
${l}${g}`});return o.set(b("wyrm_all_quests",JSON.stringify(e)),d,3e4),d}},{name:"wyrm_quest_claim",description:"Use from a fleet subagent to take an exclusive TTL lease on a quest before working it, so no two agents double-work: atomic CAS on the (quest, agent) key - a losing claimer gets a visible already-claimed error, never a partial write. The claim row is stamped with the ambient run_id and the agent's run role for orchestrator audit (wyrm_run action=status counts claims per run). Stale leases are evicted inline at claim time and by wyrm_maintenance. Requires a prior wyrm_presence announce; release with wyrm_quest_release.",inputSchema:{type:"object",properties:{quest_id:{type:"number",description:"Quest to claim"},agent_id:{type:"string",description:"Claiming agent ID (must be announced first)"},ttl_seconds:{type:"number",description:"Claim TTL (default 1800 = 30 min)"}},required:["quest_id","agent_id"]},outputSchema:{type:"object",properties:{quest_id:{type:"integer"},agent_id:{type:"string"},ttl_seconds:{type:"integer"}},required:["quest_id","agent_id","ttl_seconds"]},annotations:m.wyrm_quest_claim,aliases:[],handler:(e,{presence:s,raw:o})=>{const t=w(e),n=q("agent_id",e.agent_id,{minLen:1}),r=j("ttl_seconds",e.ttl_seconds,{min:1});if(!s.get(n))return{content:[{type:"text",text:`Agent '${n}' is not on the presence board \u2014 announce first (wyrm_presence action=announce, or \`wyrm presence announce --agent ${n}\`), then claim.`}],isError:!0};const a=s.claimQuest(t,n,r);if(!a){const d=s.getClaim(t);return{content:[{type:"text",text:`Quest #${t} already claimed by ${d?.agent_id??"another agent"}.`}],isError:!0}}const p=o().prepare("SELECT project_id FROM quests WHERE id = ?").get(t);return p&&h(o(),p.project_id),y({quest_id:t,agent_id:n,ttl_seconds:a.claim_ttl_seconds},(d,i)=>_(i.ok,`${d.agent_id} claimed quest #${d.quest_id} for ${d.ttl_seconds}s.`))}},{name:"wyrm_quest_release",description:"Use when a subagent finishes or abandons a claimed quest: drops the TTL lease (only if held by this agent) so the orchestrator can reassign the quest immediately instead of waiting for expiry; wyrm_run action=end bulk-releases a whole run's claims, and wyrm_maintenance reaps expired ones.",inputSchema:{type:"object",properties:{quest_id:{type:"number"},agent_id:{type:"string"}},required:["quest_id","agent_id"]},outputSchema:{type:"object",properties:{quest_id:{type:"integer"},agent_id:{type:"string"},released:{type:"boolean"}},required:["quest_id","agent_id","released"]},annotations:m.wyrm_quest_release,aliases:[],handler:(e,{presence:s})=>{const o=w(e),t=q("agent_id",e.agent_id,{minLen:1}),n=s.releaseClaim(o,t);return y({quest_id:o,agent_id:t,released:n},(r,a)=>_(a.brand,r.released?`Quest #${r.quest_id} released by ${r.agent_id}.`:`No claim by ${r.agent_id} on quest #${r.quest_id}.`))}}];export{F as questToolSpecs};
|
package/dist/handlers/recall.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{autoEnrollProject as
|
|
1
|
+
import{autoEnrollProject as ee}from"../project-resolve.js";import{queuedForReview as te}from"../memory-artifacts.js";import{TOOL_ANNOTATIONS as N}from"../tool-annotations.js";import{renderResult as O,withGlyph as R}from"../render.js";import{memoryUri as re,resourceLink as ne}from"./resources.js";import{buildDeterminismReceipt as ie,attestation as oe}from"../receipt.js";import{rerankConfigured as se,rerankEgressHost as ae}from"../rerank.js";import{resolvePolicy as ce,RECALL_POLICY_MODES as V,classifyArtifact as de}from"../recall-policy.js";import{ValidationError as P,asEnum as C,requireString as Y,asInt as L,asNumber as ue}from"../validate.js";const le=["reasoning_trace","lesson","pattern","anti_pattern","heuristic"];import{sanitizeActorId as K,getActor as pe}from"./boundary.js";function B(t,e){const n=new Set(t),s=[...t];for(const c of e)n.has(c)||(n.add(c),s.push(c));return s}function me(t,e){const n=ce(t,e.runId),s=e.mode??n.mode,c=B(n.mutedIds,e.muteIds??[]),h=B(n.challengedTruthIds,e.challengeTruthIds??[]),m=Date.now();return t.prepare(`
|
|
2
2
|
INSERT INTO recall_policy (run_id, mode, muted_ids, challenged_truth_ids, created_at, updated_at)
|
|
3
3
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
4
4
|
ON CONFLICT(run_id) DO UPDATE SET
|
|
@@ -6,29 +6,29 @@ import{autoEnrollProject as z}from"../project-resolve.js";import{queuedForReview
|
|
|
6
6
|
muted_ids = excluded.muted_ids,
|
|
7
7
|
challenged_truth_ids = excluded.challenged_truth_ids,
|
|
8
8
|
updated_at = excluded.updated_at
|
|
9
|
-
`).run(e.runId,
|
|
9
|
+
`).run(e.runId,s,JSON.stringify(c),JSON.stringify(h),m,m),{mode:s,mutedIds:c,challengedTruthIds:h}}function J(t,e){if(e!=null){if(!Array.isArray(e))throw new P(t,"must be an array of integers");return e.map((n,s)=>{const c=typeof n=="number"?n:typeof n=="string"&&/^[+-]?\d+$/.test(n.trim())?Number(n.trim()):NaN;if(!Number.isInteger(c))throw new P(`${t}[${s}]`,"must be an integer");return c})}}const A=10,D=50;function U(t){if(!t)return 0;const e=Date.now()-new Date(t).getTime();return Number.isFinite(e)?Math.max(0,Math.round(e/864e5)):0}function F(t,e){return e==null?null:t.getProjectById(e)?.name??null}function ye(t){return[...new Set(t.toLowerCase().split(/\s+/).map(e=>e.replace(/[^\p{L}\p{N}]/gu,"")).filter(e=>e.length>2))]}function z(t,e){if(e.length===0)return!1;const n=t.toLowerCase();return e.some(s=>n.includes(s))}const fe={low:.25,medium:.5,high:.75,critical:1};function _e(t){return fe[t]??.5}function he(t,e){return t.quarantine_scope!=="run"||t.run_id===e}function ge(t,e){return{id:e.id,kind:e.kind,outcome:e.outcome,age_days:U(e.updated_at??e.created_at),project:F(t,e.project_id),confidence:e.confidence,summary:e.problem.slice(0,160)}}function we(t,e){return{id:e.id,kind:"ground_truth",outcome:"neutral",age_days:U(e.updated_at??e.created_at),project:F(t,e.project_id),confidence:e.confidence,summary:`${e.category}.${e.key} = ${e.value}`.slice(0,160)}}function ve(t,e){return{id:e.id,kind:"failure_pattern",outcome:"negative",age_days:U(e.last_seen??e.first_seen),project:F(t,e.project_id),confidence:_e(e.severity),summary:`${e.scope}:${e.target} -- ${e.description}`.slice(0,160)}}async function Ie(t,e){const{store:n,raw:s,memory:c,truths:h,failures:m}=t,d=Math.min(Math.max(e.limit??A,1),D),u=e.projectId??null,g=e.runId??null,l=ye(e.query),y=u!=null?await c.recallHybrid(u,e.query,{limit:d}):await c.recallHybridGlobal(e.query,{limit:d}),p=[],v=[];for(const o of y){const $=ge(n,o.artifact);de(o.artifact)==="prescription"?p.push($):v.push($)}const f=(u!=null?h.getCurrent(u):s().prepare("SELECT * FROM ground_truths WHERE is_current = 1 LIMIT ?").all(d*4)).filter(o=>z(`${o.category} ${o.key} ${o.value} ${o.rationale??""}`,l)).slice(0,d).map(o=>we(n,o)),x=m.list(u,d*4).filter(o=>he(o,g)).filter(o=>z(`${o.target} ${o.description} ${o.why_failed??""}`,l)).slice(0,d).map(o=>ve(n,o));return{prescriptions:p,firewall:[...v,...x],truths:f}}const H={type:"object",properties:{id:{type:"integer"},kind:{type:"string"},outcome:{type:"string"},age_days:{type:"integer"},project:{type:["string","null"]},confidence:{type:"number"},summary:{type:"string"}},required:["id","kind","outcome","age_days","project","confidence","summary"]},Ne=[{name:"wyrm_recall",description:"Use before starting any non-trivial task - what do we know about this? Pull up relevant lessons learned, proven patterns, anti-patterns, and reasoning traces from past work. Hybrid lexical+semantic retrieval, fully local, no LLM. Rate a result with wyrm_capture mode=feedback.",inputSchema:{type:"object",properties:{projectPath:{type:"string"},query:{type:"string",description:"The task or problem"},kind:{type:"string",enum:["reasoning_trace","lesson","pattern","anti_pattern","heuristic"]},limit:{type:"number",description:"Default 10"},minConfidence:{type:"number",description:"0-1 (default 0)"},detail:{type:"string",enum:["full","link"],description:"Default full"}},required:["projectPath","query"]},outputSchema:{type:"object",properties:{query:{type:"string"},count:{type:"integer"},queued_for_review:{type:"integer"},results:{type:"array",items:{type:"object",properties:{id:{type:"integer"},kind:{type:"string"},relevance:{type:"number"},problem:{type:"string"},validated_fix:{type:["string","null"]},confidence:{type:"number"},reuse_count:{type:"integer"}},required:["id","kind","relevance","problem","confidence","reuse_count"]}}},required:["query","count","results"]},annotations:N.wyrm_recall,aliases:[],handler:async(t,{store:e,raw:n,memory:s,vectors:c,clientSupportsResources:h})=>{const{projectPath:m}=t,d=t,u=Y("query",d.query,{minLen:1}),g=C("kind",d.kind,le),l=L("limit",d.limit,{min:1}),y=ue("minConfidence",d.minConfidence,{min:0,max:1}),p=C("detail",d.detail,["full","link"]),v=e.getProject(m)??ee(e,m||process.cwd());if(!v)return{content:[{type:"text",text:`Project not found: ${m??"(no projectPath given, and the working directory is not a git repository)"}. Pass projectPath as an absolute path to a project, or run this from inside the repository you mean. Wyrm enrols a git repository automatically the first time it sees one.`}],isError:!0};const k=!!c(),f=k&&se()&&/^(1|true|yes|on)$/i.test(process.env.WYRM_RERANK??"");let _;const x={kind:g,limit:l??10,minConfidence:y,rerank:f,onStats:i=>{_=i},runId:pe().run_id},o=k?await s.recallHybrid(v.id,u,x):s.recall(v.id,u,x),$=_!==void 0,j=_?.rerankMs!=null,E=c(),S=typeof E?.getProvider=="function"?E.getProvider():void 0;let b;if(typeof E?.indexCoverage=="function")try{b=E.indexCoverage()}catch{}let T=null;if(!$){const i=S?.name;if(!k||i==="none"){const{resolveEmbeddingState:r}=await import("../providers/embedding-provider.js");T=r().reason||"vector_store_unavailable"}else if(b?.stale)T="stale_index";else{let r=-1;if(typeof E?.getStats=="function")try{r=E.getStats().total}catch{}T=r===0?"empty_index":"embed_error"}}let W=null;if($&&b)try{const i=n().prepare("SELECT COUNT(*) AS n FROM memory_artifacts WHERE needs_review = 0 AND supersedes_id IS NULL").get().n;W={indexed:b.activeCount,eligible:i}}catch{}const Q=ie({coverage:W,vectorsAvailable:$,rerankActive:j,embedModel:$?S?.model??(process.env.WYRM_EMBED_MODEL||"nomic-embed-text"):null,rerankModel:j&&process.env.WYRM_RERANK_MODEL||null,embedEgressHost:$?S?.remoteHost??null:null,rerankEgressHost:j?ae():null,stageMs:_?{fts5:_.ftsMs,vector:_.vectorMs,fusion:_.fusionMs,..._.rerankMs!=null?{rerank:_.rerankMs}:{}}:void 0,indexStale:b?.stale?{activeModel:b.activeModel,indexedUnder:Object.keys(b.otherModels)}:void 0,ftsOnlyReason:T});if(o.length>0){const i=o.map(w=>w.artifact.id),r=i.map(()=>"?").join(",");n().prepare(`UPDATE memory_artifacts SET last_accessed_at = datetime('now'), access_count = access_count + 1 WHERE id IN (${r})`).run(...i)}const G=i=>Math.round(i*1e4)/1e4,X=o.length===0?te(n(),v.id):0,q={query:u,count:o.length,queued_for_review:X,results:o.map(i=>{const r=i.artifact,w={id:r.id,kind:r.kind,relevance:G(i.relevance_score),problem:r.problem,outcome:r.outcome,confidence:r.confidence==null?r.confidence:G(r.confidence),reuse_count:r.reuse_count,reuse_success_count:r.reuse_success_count};return r.constraints!=null&&(w.constraints=r.constraints),r.validated_fix!=null&&(w.validated_fix=r.validated_fix),r.why_it_worked!=null&&(w.why_it_worked=r.why_it_worked),w}),receipt:Q},M=O(q,(i,r)=>{const w=`
|
|
10
10
|
|
|
11
|
-
_${
|
|
11
|
+
_${oe(i.receipt)}_`;if(i.count===0){const a=i.queued_for_review??0;return a>0?R(r.brand,`No relevant memory found for: "${i.query}"`)+`
|
|
12
12
|
|
|
13
|
-
${a} stored ${a===1?"memory is":"memories are"} held for review and excluded from recall. Run \`wyrm review\` to approve ${a===1?"it":"them"}, or \`wyrm review --auto on\` to stop queueing.`+
|
|
13
|
+
${a} stored ${a===1?"memory is":"memories are"} held for review and excluded from recall. Run \`wyrm review\` to approve ${a===1?"it":"them"}, or \`wyrm review --auto on\` to stop queueing.`+w:R(r.brand,`No relevant memory found for: "${i.query}"`)+"\n\nUse `wyrm_remember` to store knowledge as you work."+w}let I=R(r.brand,`**Memory Recall** -- "${i.query}" (${i.count} found)`)+`
|
|
14
14
|
|
|
15
|
-
`;for(const a of
|
|
16
|
-
`,
|
|
17
|
-
`,a.constraints&&(
|
|
18
|
-
`),a.validated_fix&&(
|
|
19
|
-
`),a.why_it_worked&&(
|
|
20
|
-
`),a.outcome==="negative"&&(
|
|
21
|
-
`);const
|
|
15
|
+
`;for(const a of i.results){I+=`### ${a.kind} (ID: ${a.id}) -- relevance ${(a.relevance*100).toFixed(0)}%
|
|
16
|
+
`,I+=`**Problem:** ${a.problem}
|
|
17
|
+
`,a.constraints&&(I+=`**Constraints:** ${a.constraints}
|
|
18
|
+
`),a.validated_fix&&(I+=`**Solution:** ${a.validated_fix}
|
|
19
|
+
`),a.why_it_worked&&(I+=`**Why:** ${a.why_it_worked}
|
|
20
|
+
`),a.outcome==="negative"&&(I+=R(r.warn,"_This approach failed -- see solution for what to do instead_")+`
|
|
21
|
+
`);const Z=a.confidence==null?"unknown":`${(a.confidence*100).toFixed(0)}%`;I+=`_Confidence: ${Z} | Used ${a.reuse_count}x | Success rate: ${a.reuse_count>0?(a.reuse_success_count/a.reuse_count*100).toFixed(0)+"%":"not yet used"}_
|
|
22
22
|
|
|
23
|
-
`}return
|
|
23
|
+
`}return I+="_Use `wyrm_capture` mode=feedback (artifactId, success) to record whether these were helpful._",I+w},{exemptStructured:!0});if((p==="link"||p!=="full"&&!!h?.())&&q.count>0){const i=q.results.map(r=>ne(re(r.id),`memory #${r.id} (${r.kind})`,{description:r.problem.slice(0,120),mimeType:"application/json"}));return{...M,content:[...M.content,...i]}}return M}},{name:"wyrm_recall_set",description:"Negotiate THIS run's recall policy (Task NR-4, negotiated-recall v1): mute the prescriptive memory set (mode='clean' drops kind IN ('pattern','heuristic') OR outcome='positive' \u2014 anti_pattern/negative/failure_patterns/ground_truths are never muted) or veto specific artifact/ground-truth ids. Enforced at every recall/context-build/session-prime chokepoint for this run_id (NR-3). `mode`, when given, REPLACES the stored mode; `mute_ids`/`challenge_truth_ids` MERGE into whatever is already set \u2014 calling this twice accumulates, never duplicates. Returns the resulting policy.",inputSchema:{type:"object",properties:{run_id:{type:"string",description:"The fleet run this policy applies to (<=64 printable-ASCII chars, no ';')."},mode:{type:"string",enum:[...V],description:"'full' = unfiltered (default). 'clean' = mute the prescriptive set. 'custom' = only the explicit mute_ids/challenge_truth_ids vetoes apply. Omit to leave the stored mode unchanged."},mute_ids:{type:"array",items:{type:"integer"},description:"memory_artifacts ids to veto from recall \u2014 merged (appended, deduped) into the existing muted set."},challenge_truth_ids:{type:"array",items:{type:"integer"},description:"ground_truths ids to omit from wyrm_context_build \u2014 merged (appended, deduped) into the existing challenged set."}},required:["run_id"]},outputSchema:{type:"object",properties:{run_id:{type:"string"},mode:{type:"string"},mutedIds:{type:"array",items:{type:"integer"}},challengedTruthIds:{type:"array",items:{type:"integer"}}},required:["run_id","mode","mutedIds","challengedTruthIds"]},annotations:N.wyrm_recall_set,aliases:[],handler:(t,{raw:e,cache:n})=>{const s=t,c=K(s.run_id);if(c===null)throw new P("run_id",'is required and must be <=64 printable-ASCII chars (no ";")');const h=C("mode",s.mode,V),m=J("mute_ids",s.mute_ids),d=J("challenge_truth_ids",s.challenge_truth_ids),u=me(e(),{runId:c,mode:h,muteIds:m,challengeTruthIds:d});n.invalidate("wyrm_session_prime"),n.invalidate("wyrm_context_build");const g={run_id:c,mode:u.mode,mutedIds:u.mutedIds,challengedTruthIds:u.challengedTruthIds};return O(g,(l,y)=>{let p=R(y.brand,`**Recall policy** for run ${l.run_id}: mode=${l.mode}`)+`
|
|
24
24
|
|
|
25
|
-
`;return
|
|
26
|
-
`,
|
|
27
|
-
`,
|
|
25
|
+
`;return p+=`**Muted ids:** ${l.mutedIds.length?l.mutedIds.join(", "):"(none)"}
|
|
26
|
+
`,p+=`**Challenged truth ids:** ${l.challengedTruthIds.length?l.challengedTruthIds.join(", "):"(none)"}
|
|
27
|
+
`,p})}},{name:"wyrm_recall_propose",description:"Review THIS query's full recall candidate set as three GROUPED buckets \u2014 prescriptions (kind IN ('pattern','heuristic') OR outcome='positive', the set mode='clean' would mute), firewall (anti_pattern/negative memory + failure_patterns hits, never muted), truths (matching ground_truths) \u2014 without injecting anything as authoritative context (Task NR-5, negotiated-recall v1). A review list, not a brief: nothing here is ranked-into-a-summary. Use it BEFORE wyrm_recall_set to see what a clean-mode mute would actually drop, then negotiate with wyrm_recall_set.",inputSchema:{type:"object",properties:{query:{type:"string",description:"The task or problem to review candidates for."},project_id:{type:"integer",description:"Scope to one project. Omitted = search across every project."},run_id:{type:"string",description:"Scopes visibility of 'run'-quarantined failure_patterns rows to this run. Never applied as a mute filter \u2014 propose always shows the full candidate set."},limit:{type:"integer",description:`Per-bucket candidate depth, default ${A}, max ${D}.`}},required:["query"]},outputSchema:{type:"object",properties:{query:{type:"string"},prescriptions:{type:"array",items:H},firewall:{type:"array",items:H},truths:{type:"array",items:H}},required:["query","prescriptions","firewall","truths"]},annotations:N.wyrm_recall_propose,aliases:[],handler:async(t,e)=>{const n=t,s=Y("query",n.query,{minLen:1,maxLen:2e3}),c=L("project_id",n.project_id,{min:1}),h=n.run_id===void 0?null:K(n.run_id),m=L("limit",n.limit,{min:1,max:D,default:A}),d=await Ie(e,{query:s,projectId:c,runId:h,limit:m}),u={query:s,...d};return O(u,(g,l)=>{let y=R(l.brand,`**Recall proposal** -- "${g.query}" (review only, nothing injected)`)+`
|
|
28
28
|
|
|
29
|
-
`;const
|
|
29
|
+
`;const p=(v,k)=>{if(y+=`### ${v} (${k.length})
|
|
30
30
|
`,k.length===0){y+=`_(none)_
|
|
31
31
|
|
|
32
|
-
`;return}for(const
|
|
32
|
+
`;return}for(const f of k){const _=f.project?`, ${f.project}`:"";y+=`- #${f.id} [${f.kind}/${f.outcome}] ${f.summary} _(conf ${(f.confidence*100).toFixed(0)}%, ${f.age_days}d old${_})_
|
|
33
33
|
`}y+=`
|
|
34
|
-
`};return
|
|
34
|
+
`};return p("Prescriptions -- muted by clean mode",g.prescriptions),p("Firewall -- never muted",g.firewall),p("Ground truths",g.truths),y+="_Use `wyrm_recall_set` to mute/veto specific ids after reviewing._",y})}}];export{Ie as buildRecallProposal,Ne as recallToolSpecs,me as upsertRecallPolicy};
|