agentix-cli 0.44.1 → 0.45.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/apps/mac-helper/Resources/AppIcon.png +0 -0
  2. package/dist/{agent-3XDPLWMX.js → agent-YC3VJVPK.js} +2 -2
  3. package/dist/{board-dashboard-B5FMUMGA.js → board-dashboard-U5YP4HWE.js} +3 -1
  4. package/dist/{board-dashboard-B5FMUMGA.js.map → board-dashboard-U5YP4HWE.js.map} +1 -1
  5. package/dist/{chunk-BTGPZ6B7.js → chunk-3FIS6JD3.js} +2 -2
  6. package/dist/chunk-3RKIBJG6.js +182 -0
  7. package/dist/chunk-3RKIBJG6.js.map +1 -0
  8. package/dist/chunk-CQR6C5FU.js +23 -0
  9. package/dist/chunk-CQR6C5FU.js.map +1 -0
  10. package/dist/{chunk-3TL5EIVN.js → chunk-KZYNQRQQ.js} +141 -141
  11. package/dist/{chunk-3TL5EIVN.js.map → chunk-KZYNQRQQ.js.map} +1 -1
  12. package/dist/{chunk-ZQOGAUQS.js → chunk-MDAC3PKR.js} +200 -171
  13. package/dist/chunk-MDAC3PKR.js.map +1 -0
  14. package/dist/cli.js +1 -1
  15. package/dist/compaction-QXU3A4LQ.js +28 -0
  16. package/dist/compaction-QXU3A4LQ.js.map +1 -0
  17. package/dist/index.d.ts +101 -14
  18. package/dist/index.js +1 -1
  19. package/dist/memory-extract-3TO7UC5B.js +2 -0
  20. package/dist/{program-JLCKBO2A.js → program-WLPVWTAT.js} +2 -2
  21. package/dist/{registry-5QOY65FS.js → registry-EESWAPMR.js} +2 -2
  22. package/package.json +1 -1
  23. package/dist/chunk-77OV7346.js +0 -23
  24. package/dist/chunk-77OV7346.js.map +0 -1
  25. package/dist/chunk-T76YTN34.js +0 -178
  26. package/dist/chunk-T76YTN34.js.map +0 -1
  27. package/dist/chunk-ZQOGAUQS.js.map +0 -1
  28. package/dist/compaction-FD4R23HZ.js +0 -28
  29. package/dist/compaction-FD4R23HZ.js.map +0 -1
  30. package/dist/memory-extract-F5ZHWY5E.js +0 -2
  31. /package/dist/{agent-3XDPLWMX.js.map → agent-YC3VJVPK.js.map} +0 -0
  32. /package/dist/{chunk-BTGPZ6B7.js.map → chunk-3FIS6JD3.js.map} +0 -0
  33. /package/dist/{memory-extract-F5ZHWY5E.js.map → memory-extract-3TO7UC5B.js.map} +0 -0
  34. /package/dist/{program-JLCKBO2A.js.map → program-WLPVWTAT.js.map} +0 -0
  35. /package/dist/{registry-5QOY65FS.js.map → registry-EESWAPMR.js.map} +0 -0
package/dist/cli.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import{a as s}from"./chunk-3TL5EIVN.js";import"./chunk-7A7RC3R4.js";import"./chunk-YQVR2HQS.js";import"./chunk-SAFAI4AV.js";import"./chunk-2TJ5L7CB.js";import"./chunk-7CVJ4JOE.js";import"./chunk-WLVQDEOT.js";import"./chunk-GV7S5BAX.js";import"./chunk-GSX4NK2N.js";import"./chunk-SZDACK6O.js";import"./chunk-ZQOGAUQS.js";import"./chunk-MX5GCR7Y.js";import"./chunk-V5FZJUK4.js";import"./chunk-3SCPRYJB.js";import"./chunk-V7FYSZSV.js";import"./chunk-ZVP6GB4G.js";import"./chunk-T76YTN34.js";import"./chunk-TCJ3SMOF.js";import"./chunk-VMDA2SX2.js";import"./chunk-QLTM3F7M.js";import"./chunk-BTGPZ6B7.js";import"./chunk-SFQUP3BP.js";import"./chunk-BIFJSQB5.js";import"./chunk-WPMX3VGI.js";import"./chunk-K33GIBSE.js";import"./chunk-76Z4VHDX.js";import"./chunk-W4YR2RO2.js";import"./chunk-AFRHKA7B.js";import"./chunk-NSYY5NCR.js";import"./chunk-ICZPZYYR.js";import"./chunk-DWADWDMU.js";import"./chunk-77OV7346.js";import"./chunk-VWTBHLMX.js";import"./chunk-MTVRYTLL.js";import"./chunk-PNWNVOEZ.js";import"./chunk-64P4QHSZ.js";import"./chunk-TNFOGJWZ.js";import"./chunk-AJ5RMU7X.js";import"./chunk-2GIDVZ4H.js";import"./chunk-6HMZ5TTM.js";import"./chunk-F4HZTZHW.js";import"./chunk-AJONZWLT.js";import"./chunk-ZTEU5JVQ.js";import"./chunk-QOYAX2FT.js";import"./chunk-LCIREYTD.js";import"./chunk-L2WSPY6S.js";import"./chunk-VDMYBPYB.js";import"./chunk-DSYKYZMT.js";import"./chunk-QJANPP4C.js";import"./chunk-BH3GTCZ6.js";import"./chunk-FIOB3MEX.js";process.on("SIGINT",()=>process.exit(0));process.on("SIGTERM",()=>process.exit(0));async function o(){let r=await s();if(process.argv.slice(2).length===0){r.outputHelp();return}r.parse()}o();
2
+ import{a as s}from"./chunk-KZYNQRQQ.js";import"./chunk-7A7RC3R4.js";import"./chunk-YQVR2HQS.js";import"./chunk-SAFAI4AV.js";import"./chunk-2TJ5L7CB.js";import"./chunk-7CVJ4JOE.js";import"./chunk-WLVQDEOT.js";import"./chunk-GV7S5BAX.js";import"./chunk-GSX4NK2N.js";import"./chunk-SZDACK6O.js";import"./chunk-MDAC3PKR.js";import"./chunk-MX5GCR7Y.js";import"./chunk-V5FZJUK4.js";import"./chunk-3SCPRYJB.js";import"./chunk-V7FYSZSV.js";import"./chunk-ZVP6GB4G.js";import"./chunk-3RKIBJG6.js";import"./chunk-TCJ3SMOF.js";import"./chunk-VMDA2SX2.js";import"./chunk-QLTM3F7M.js";import"./chunk-3FIS6JD3.js";import"./chunk-SFQUP3BP.js";import"./chunk-BIFJSQB5.js";import"./chunk-WPMX3VGI.js";import"./chunk-K33GIBSE.js";import"./chunk-76Z4VHDX.js";import"./chunk-W4YR2RO2.js";import"./chunk-AFRHKA7B.js";import"./chunk-NSYY5NCR.js";import"./chunk-ICZPZYYR.js";import"./chunk-DWADWDMU.js";import"./chunk-CQR6C5FU.js";import"./chunk-VWTBHLMX.js";import"./chunk-MTVRYTLL.js";import"./chunk-PNWNVOEZ.js";import"./chunk-64P4QHSZ.js";import"./chunk-TNFOGJWZ.js";import"./chunk-AJ5RMU7X.js";import"./chunk-2GIDVZ4H.js";import"./chunk-6HMZ5TTM.js";import"./chunk-F4HZTZHW.js";import"./chunk-AJONZWLT.js";import"./chunk-ZTEU5JVQ.js";import"./chunk-QOYAX2FT.js";import"./chunk-LCIREYTD.js";import"./chunk-L2WSPY6S.js";import"./chunk-VDMYBPYB.js";import"./chunk-DSYKYZMT.js";import"./chunk-QJANPP4C.js";import"./chunk-BH3GTCZ6.js";import"./chunk-FIOB3MEX.js";process.on("SIGINT",()=>process.exit(0));process.on("SIGTERM",()=>process.exit(0));async function o(){let r=await s();if(process.argv.slice(2).length===0){r.outputHelp();return}r.parse()}o();
3
3
  //# sourceMappingURL=cli.js.map
@@ -0,0 +1,28 @@
1
+ import{a as y}from"./chunk-QOYAX2FT.js";import"./chunk-FIOB3MEX.js";import{readFileSync as O,writeFileSync as k,existsSync as w,mkdirSync as E}from"fs";import{resolve as f}from"path";function d(t,o){let n=new Map;for(let r of t){if(r.role!=="agent")continue;let s=y(r.content);for(let c of s)n.set(c,(n.get(c)||0)+1)}let e=[...n.entries()].sort((r,s)=>s[1]-r[1]).slice(0,50).map(([r])=>r);return{lexicon:n,toolCalls:o||[],topKeywords:e,capturedAt:new Date().toISOString(),messageCount:t.length}}function S(t,o){let n=T(t,o),{distance:e,newTools:r,missingTools:s}=b(t,o),c=I(t,o),a=Math.min(1,n*.4+e*.3+c*.3),m=t.topKeywords.filter(u=>!o.lexicon.has(u)||(o.lexicon.get(u)||0)===0).slice(0,10);return{overallScore:Math.round(a*100)/100,lexiconDecay:Math.round(n*100)/100,toolShift:Math.round(e*100)/100,semanticDrift:Math.round(c*100)/100,lostWords:m,newTools:r,missingTools:s}}function T(t,o){if(t.topKeywords.length===0)return 0;let n=0;for(let e of t.topKeywords)o.lexicon.has(e)||n++;return n/t.topKeywords.length}function b(t,o){let n=new Set(t.toolCalls),e=new Set(o.toolCalls);if(n.size===0&&e.size===0)return{distance:0,newTools:[],missingTools:[]};let r=[...n].filter(l=>e.has(l)),s=new Set([...n,...e]),a=1-r.length/s.size,m=[...e].filter(l=>!n.has(l)),u=[...n].filter(l=>!e.has(l));return{distance:a,newTools:m,missingTools:u}}function I(t,o){if(t.topKeywords.length===0)return 0;let n=new Set(o.topKeywords),e=0;for(let r of t.topKeywords)n.has(r)&&e++;return 1-e/t.topKeywords.length}var C=".agentx/drift";function M(t,o,n=process.cwd()){let e=f(n,C);w(e)||E(e,{recursive:!0});let r={...o,lexicon:Object.fromEntries(o.lexicon)};k(f(e,`${t}.json`),JSON.stringify(r,null,2))}function v(t,o=process.cwd()){let n=f(o,C,`${t}.json`);if(!w(n))return null;try{let e=JSON.parse(O(n,"utf-8"));return{...e,lexicon:new Map(Object.entries(e.lexicon))}}catch{return null}}var R="claude-haiku-4-20250514";var A=5e4,K=6,F=8,P=`You are a conversation summarizer. Given a conversation history between a user and an AI agent, produce a concise summary that preserves:
2
+
3
+ 1. Key decisions made and their reasoning
4
+ 2. Important facts learned (names, IDs, paths, URLs, credentials shared)
5
+ 3. Commitments and action items (what was promised, deadlines)
6
+ 4. Current task state (what's in progress, what's done, what's blocked)
7
+ 5. User preferences expressed during the conversation
8
+
9
+ Format as a structured summary:
10
+ [Session Summary]
11
+ - Decisions: ...
12
+ - Facts: ...
13
+ - In progress: ...
14
+ - Done: ...
15
+ - Preferences: ...
16
+
17
+ Keep it under 500 words. Prioritize actionable information over conversational flow.
18
+ Do NOT include greetings, acknowledgments, or meta-commentary about the conversation.`;function G(t){return t.length<F?!1:t.reduce((n,e)=>n+e.content.length,0)>A}async function J(t,o,n,e){if(t.length<F)return{summary:"",compactedCount:0,keptCount:t.length,memoryFlushed:!1};let r=Math.max(0,t.length-K),s=t.slice(0,r),c=t.slice(r);if(s.length===0)return{summary:"",compactedCount:0,keptCount:c.length,memoryFlushed:!1};let a=d(s);M(o,a);let m=!1;if(n)try{await $(o,s,n,e),m=!0}catch{}let u=j(s),l=await z(u),{qualityScore:i,lostEntities:g}=B(u,l),p,x=[{role:"agent",content:l,timestamp:new Date().toISOString()}],D=d(x),h=v(o);return h&&(p=S(h,D)),{summary:l,compactedCount:s.length,keptCount:c.length,memoryFlushed:m,qualityScore:i,lostEntities:g.length>0?g:void 0,drift:p&&p.overallScore>.1?p:void 0}}function B(t,o){let n=_(t);if(n.length===0)return{qualityScore:100,lostEntities:[]};let e=o.toLowerCase(),r=[],s=[];for(let a of n)e.includes(a.toLowerCase())?r.push(a):s.push(a);return{qualityScore:Math.round(r.length/n.length*100),lostEntities:s}}function _(t){let o=[],n=new Set,e=i=>{let g=i.toLowerCase().trim();g.length<3||n.has(g)||(n.add(g),o.push(i.trim()))},r=t.match(/https?:\/\/[^\s<>)"]+/g)||[];for(let i of r)e(i.slice(0,80));let s=t.match(/(?:\/[\w.-]+){2,}/g)||[];for(let i of s)e(i);let c=t.match(/[#!]\d{2,}/g)||[];for(let i of c)e(i);let a=t.match(/[\w.-]+@[\w.-]+\.\w+/g)||[];for(let i of a)e(i);let m=t.match(/"([^"]{3,40})"/g)||[];for(let i of m)e(i.slice(1,-1));let u=t.match(/[A-Z][a-z]+(?:\s+[A-Z][a-z]+)+/g)||[];for(let i of u)e(i);let l=t.match(/[a-f0-9]{8,}/gi)||[];for(let i of l.slice(0,5))e(i);return o.slice(0,30)}function W(t,o){if(o.compactedCount===0)return t;let n=t.slice(-o.keptCount);return[{role:"agent",name:"system",content:`[Compacted conversation summary \u2014 ${o.compactedCount} earlier messages]
19
+
20
+ ${o.summary}`,timestamp:new Date().toISOString()},...n]}async function $(t,o,n,e){let r=[],s=[];for(let a of o)a.role==="user"?r.push(`${a.name||"User"}: ${a.content}`):s.push(a.content);if(r.length===0||s.length===0)return;let{extractMemories:c}=await import("./memory-extract-3TO7UC5B.js");await c(t,r.join(`
21
+ `),s.join(`
22
+ `),{channel:e?.channel??"compaction",chatId:e?.chatId??"flush",sender:"compaction"},n)}function j(t){return t.map(o=>{let n=o.timestamp.slice(11,16),e=o.role==="user"?o.name||"User":o.name||"Agent";return`[${n}] ${e}: ${o.content}`}).join(`
23
+
24
+ `)}async function z(t){let n=t.length>2e4?t.slice(-2e4):t;try{let{createProvider:e}=await import("./providers-KO2DODIO.js");return(await e("claude").generate([{role:"system",content:P},{role:"user",content:n}],{model:R,maxTokens:1024})).content.trim()}catch{let s=t.split(`
25
+ `).slice(-20);return`[Summary unavailable \u2014 keeping last ${s.length} lines]
26
+ ${s.join(`
27
+ `)}`}}export{W as applyCompaction,J as compactSession,G as needsCompaction};
28
+ //# sourceMappingURL=compaction-QXU3A4LQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/agents/drift-detection.ts","../src/agents/compaction.ts"],"sourcesContent":["import { tokenize } from \"../memory/bm25\"\n\n// --- Behavioral Drift Detection ---\n//\n// Detects silent behavioral shifts after compaction or session boundaries.\n// Three signals, all zero-AI (pure signal processing):\n//\n// 1. Ghost lexicon decay — domain vocabulary disappearing after compaction\n// 2. Tool-call sequence shift — Jaccard distance on tool usage patterns\n// 3. Semantic drift — keyword overlap decline across session boundaries\n//\n// Inspired by CrewAI #5155 and agent-morrow/compression-monitor.\n\nexport interface BehavioralFingerprint {\n /** Domain vocabulary: word → frequency */\n lexicon: Map<string, number>\n /** Tool call sequence: tool names in order */\n toolCalls: string[]\n /** Top keywords by frequency (for quick comparison) */\n topKeywords: string[]\n /** Timestamp of fingerprint capture */\n capturedAt: string\n /** Number of messages in the sample */\n messageCount: number\n}\n\nexport interface DriftReport {\n /** 0-1 score: 0 = no drift, 1 = complete drift */\n overallScore: number\n /** Ghost lexicon decay: percentage of domain words that disappeared */\n lexiconDecay: number\n /** Tool-call Jaccard distance: 0 = identical patterns, 1 = completely different */\n toolShift: number\n /** Keyword overlap decline: percentage of shared keywords lost */\n semanticDrift: number\n /** Domain words that disappeared */\n lostWords: string[]\n /** New tool calls that weren't in the baseline */\n newTools: string[]\n /** Missing tool calls that were in the baseline */\n missingTools: string[]\n}\n\n/**\n * Build a behavioral fingerprint from agent messages.\n * Call this BEFORE compaction to establish a baseline.\n */\nexport function buildFingerprint(\n messages: Array<{ role: string; content: string; name?: string }>,\n toolCalls?: string[],\n): BehavioralFingerprint {\n const lexicon = new Map<string, number>()\n\n // Build vocabulary from agent responses\n for (const msg of messages) {\n if (msg.role !== \"agent\") continue\n const words = tokenize(msg.content)\n for (const word of words) {\n lexicon.set(word, (lexicon.get(word) || 0) + 1)\n }\n }\n\n // Top keywords by frequency (domain vocabulary)\n const topKeywords = [...lexicon.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, 50)\n .map(([word]) => word)\n\n return {\n lexicon,\n toolCalls: toolCalls || [],\n topKeywords,\n capturedAt: new Date().toISOString(),\n messageCount: messages.length,\n }\n}\n\n/**\n * Compare two fingerprints and produce a drift report.\n * Call this AFTER compaction with the new session's fingerprint.\n */\nexport function detectDrift(\n baseline: BehavioralFingerprint,\n current: BehavioralFingerprint,\n): DriftReport {\n // 1. Ghost lexicon decay: how many baseline domain words are missing?\n const lexiconDecay = computeLexiconDecay(baseline, current)\n\n // 2. Tool-call sequence shift: Jaccard distance on tool usage\n const { distance: toolShift, newTools, missingTools } = computeToolShift(baseline, current)\n\n // 3. Semantic drift: keyword overlap decline\n const semanticDrift = computeSemanticDrift(baseline, current)\n\n // Overall score: weighted average (lexicon decay matters most)\n const overallScore = Math.min(1, (lexiconDecay * 0.4) + (toolShift * 0.3) + (semanticDrift * 0.3))\n\n // Lost domain words\n const lostWords = baseline.topKeywords.filter(\n word => !current.lexicon.has(word) || (current.lexicon.get(word) || 0) === 0,\n ).slice(0, 10)\n\n return {\n overallScore: Math.round(overallScore * 100) / 100,\n lexiconDecay: Math.round(lexiconDecay * 100) / 100,\n toolShift: Math.round(toolShift * 100) / 100,\n semanticDrift: Math.round(semanticDrift * 100) / 100,\n lostWords,\n newTools,\n missingTools,\n }\n}\n\n/**\n * Ghost lexicon decay: percentage of baseline's top domain words\n * that disappeared from the current fingerprint.\n */\nfunction computeLexiconDecay(\n baseline: BehavioralFingerprint,\n current: BehavioralFingerprint,\n): number {\n if (baseline.topKeywords.length === 0) return 0\n\n let missing = 0\n for (const word of baseline.topKeywords) {\n if (!current.lexicon.has(word)) missing++\n }\n\n return missing / baseline.topKeywords.length\n}\n\n/**\n * Tool-call sequence shift: Jaccard distance between tool usage sets.\n * J(A,B) = |A ∩ B| / |A ∪ B|, distance = 1 - J\n */\nfunction computeToolShift(\n baseline: BehavioralFingerprint,\n current: BehavioralFingerprint,\n): { distance: number; newTools: string[]; missingTools: string[] } {\n const baseSet = new Set(baseline.toolCalls)\n const currSet = new Set(current.toolCalls)\n\n if (baseSet.size === 0 && currSet.size === 0) {\n return { distance: 0, newTools: [], missingTools: [] }\n }\n\n const intersection = [...baseSet].filter(t => currSet.has(t))\n const union = new Set([...baseSet, ...currSet])\n const jaccard = intersection.length / union.size\n const distance = 1 - jaccard\n\n const newTools = [...currSet].filter(t => !baseSet.has(t))\n const missingTools = [...baseSet].filter(t => !currSet.has(t))\n\n return { distance, newTools, missingTools }\n}\n\n/**\n * Semantic drift: keyword overlap decline.\n * Compares top keywords between baseline and current.\n */\nfunction computeSemanticDrift(\n baseline: BehavioralFingerprint,\n current: BehavioralFingerprint,\n): number {\n if (baseline.topKeywords.length === 0) return 0\n\n const currentSet = new Set(current.topKeywords)\n let overlap = 0\n\n for (const word of baseline.topKeywords) {\n if (currentSet.has(word)) overlap++\n }\n\n // Drift = 1 - overlap ratio\n return 1 - (overlap / baseline.topKeywords.length)\n}\n\n// --- Per-agent fingerprint persistence ---\n\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from \"fs\"\nimport { resolve } from \"path\"\n\nconst DRIFT_DIR = \".agentx/drift\"\n\n/**\n * Save a behavioral fingerprint for an agent (pre-compaction baseline).\n */\nexport function saveFingerprint(\n agentId: string,\n fingerprint: BehavioralFingerprint,\n baseDir: string = process.cwd(),\n): void {\n const dir = resolve(baseDir, DRIFT_DIR)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n\n const serialized = {\n ...fingerprint,\n lexicon: Object.fromEntries(fingerprint.lexicon),\n }\n\n writeFileSync(\n resolve(dir, `${agentId}.json`),\n JSON.stringify(serialized, null, 2),\n )\n}\n\n/**\n * Load the saved fingerprint for an agent.\n */\nexport function loadFingerprint(\n agentId: string,\n baseDir: string = process.cwd(),\n): BehavioralFingerprint | null {\n const file = resolve(baseDir, DRIFT_DIR, `${agentId}.json`)\n if (!existsSync(file)) return null\n\n try {\n const data = JSON.parse(readFileSync(file, \"utf-8\"))\n return {\n ...data,\n lexicon: new Map(Object.entries(data.lexicon)),\n }\n } catch {\n return null\n }\n}\n","import type { SessionMessage } from \"./sessions\"\nimport type { MemoryStore } from \"./memory-store\"\nimport { buildFingerprint, detectDrift, saveFingerprint, loadFingerprint, type DriftReport } from \"./drift-detection\"\n\n// --- Context Compaction ---\n//\n// Summarizes conversation history when it grows too large,\n// preventing context loss in long-running sessions.\n//\n// Strategy:\n// 1. Memory flush: extract memorable facts BEFORE compacting\n// 2. Split-aware: keep tool_use/tool_result pairs intact\n// 3. Uses cheaper model (Haiku) for summarization\n// 4. Preserves recent messages verbatim, compacts older ones\n\nconst COMPACTION_MODEL = \"claude-haiku-4-20250514\"\nconst MAX_HISTORY_CHARS = 60_000\n// Start compacting when stored history exceeds this. Bumped 10K → 50K\n// because the original threshold tripped on trivial multi-message bursts\n// (project-bot queues, daily-tick storms), spending Haiku tokens to reduce\n// stored history that Claude itself never reads — Claude --resume replays\n// from its own copy. The cost-relevant thresholds are tier-2 token rotation\n// (~200K tokens / ~800K chars of input) and max-turns; compaction is just\n// for our local storage hygiene + cross-session summary seeding, so a\n// generous threshold is fine.\nconst COMPACT_THRESHOLD = 50_000\nconst KEEP_RECENT = 6 // Always keep last N messages verbatim\nconst MIN_MESSAGES_TO_COMPACT = 8 // Don't compact if fewer than this\n\nconst COMPACTION_PROMPT = `You are a conversation summarizer. Given a conversation history between a user and an AI agent, produce a concise summary that preserves:\n\n1. Key decisions made and their reasoning\n2. Important facts learned (names, IDs, paths, URLs, credentials shared)\n3. Commitments and action items (what was promised, deadlines)\n4. Current task state (what's in progress, what's done, what's blocked)\n5. User preferences expressed during the conversation\n\nFormat as a structured summary:\n[Session Summary]\n- Decisions: ...\n- Facts: ...\n- In progress: ...\n- Done: ...\n- Preferences: ...\n\nKeep it under 500 words. Prioritize actionable information over conversational flow.\nDo NOT include greetings, acknowledgments, or meta-commentary about the conversation.`\n\nexport interface CompactionResult {\n /** Summarized text replacing older messages */\n summary: string\n /** Messages that were compacted (removed from history) */\n compactedCount: number\n /** Messages kept verbatim (recent) */\n keptCount: number\n /** Whether memory flush was performed before compaction */\n memoryFlushed: boolean\n /** Quality score: percentage of key entities preserved in summary (0-100) */\n qualityScore?: number\n /** Entities that were lost in compaction */\n lostEntities?: string[]\n /** Behavioral drift report (compared to pre-compaction baseline) */\n drift?: DriftReport\n}\n\n/**\n * Check whether a session's history needs compaction.\n */\nexport function needsCompaction(messages: SessionMessage[]): boolean {\n if (messages.length < MIN_MESSAGES_TO_COMPACT) return false\n const totalChars = messages.reduce((sum, m) => sum + m.content.length, 0)\n return totalChars > COMPACT_THRESHOLD\n}\n\n/**\n * Compact a session's conversation history.\n *\n * Flow:\n * 1. Flush memories from the about-to-be-compacted messages\n * 2. Summarize older messages using Haiku\n * 3. Return summary + preserved recent messages\n */\nexport async function compactSession(\n messages: SessionMessage[],\n agentId: string,\n memoryStore?: MemoryStore,\n /** The session's chat, so flushed facts carry its channel's trust. */\n origin?: { channel: string; chatId: string },\n): Promise<CompactionResult> {\n if (messages.length < MIN_MESSAGES_TO_COMPACT) {\n return {\n summary: \"\",\n compactedCount: 0,\n keptCount: messages.length,\n memoryFlushed: false,\n }\n }\n\n // Split: older messages to compact, recent to keep\n const splitIdx = Math.max(0, messages.length - KEEP_RECENT)\n const toCompact = messages.slice(0, splitIdx)\n const toKeep = messages.slice(splitIdx)\n\n if (toCompact.length === 0) {\n return {\n summary: \"\",\n compactedCount: 0,\n keptCount: toKeep.length,\n memoryFlushed: false,\n }\n }\n\n // Step 0: Capture behavioral fingerprint BEFORE compaction (drift baseline)\n const preFingerprint = buildFingerprint(toCompact)\n saveFingerprint(agentId, preFingerprint)\n\n // Step 1: Memory flush — extract facts from messages about to be compacted\n let memoryFlushed = false\n if (memoryStore) {\n try {\n await flushMemories(agentId, toCompact, memoryStore, origin)\n memoryFlushed = true\n } catch {\n // Best-effort memory flush\n }\n }\n\n // Step 2: Summarize older messages\n const historyText = formatMessagesForSummary(toCompact)\n const summary = await summarize(historyText)\n\n // Step 3: Verify compaction quality — check entity preservation\n const { qualityScore, lostEntities } = verifyCompactionQuality(historyText, summary)\n\n // Step 4: Check for behavioral drift — compare summary against pre-compaction baseline\n let drift: DriftReport | undefined\n const summaryMessages: SessionMessage[] = [{\n role: \"agent\" as const,\n content: summary,\n timestamp: new Date().toISOString(),\n }]\n const postFingerprint = buildFingerprint(summaryMessages)\n const baseline = loadFingerprint(agentId)\n if (baseline) {\n drift = detectDrift(baseline, postFingerprint)\n }\n\n return {\n summary,\n compactedCount: toCompact.length,\n keptCount: toKeep.length,\n memoryFlushed,\n qualityScore,\n lostEntities: lostEntities.length > 0 ? lostEntities : undefined,\n drift: drift && drift.overallScore > 0.1 ? drift : undefined,\n }\n}\n\n/**\n * Verify compaction quality by checking entity preservation.\n * Extracts key entities (names, IDs, URLs, decisions) from the original\n * and checks how many survived in the summary.\n */\nfunction verifyCompactionQuality(\n original: string,\n summary: string,\n): { qualityScore: number; lostEntities: string[] } {\n const entities = extractEntities(original)\n if (entities.length === 0) return { qualityScore: 100, lostEntities: [] }\n\n const summaryLower = summary.toLowerCase()\n const preserved: string[] = []\n const lost: string[] = []\n\n for (const entity of entities) {\n if (summaryLower.includes(entity.toLowerCase())) {\n preserved.push(entity)\n } else {\n lost.push(entity)\n }\n }\n\n const score = Math.round((preserved.length / entities.length) * 100)\n return { qualityScore: score, lostEntities: lost }\n}\n\n/**\n * Extract key entities from text: names, IDs, URLs, file paths, decisions.\n */\nfunction extractEntities(text: string): string[] {\n const entities: string[] = []\n const seen = new Set<string>()\n\n const add = (entity: string) => {\n const key = entity.toLowerCase().trim()\n if (key.length < 3 || seen.has(key)) return\n seen.add(key)\n entities.push(entity.trim())\n }\n\n // URLs\n const urls = text.match(/https?:\\/\\/[^\\s<>)\"]+/g) || []\n for (const url of urls) add(url.slice(0, 80))\n\n // File paths\n const paths = text.match(/(?:\\/[\\w.-]+){2,}/g) || []\n for (const p of paths) add(p)\n\n // Ticket/issue references (#123, !456)\n const refs = text.match(/[#!]\\d{2,}/g) || []\n for (const r of refs) add(r)\n\n // Email addresses\n const emails = text.match(/[\\w.-]+@[\\w.-]+\\.\\w+/g) || []\n for (const e of emails) add(e)\n\n // Quoted strings (potential names, IDs, keys)\n const quoted = text.match(/\"([^\"]{3,40})\"/g) || []\n for (const q of quoted) add(q.slice(1, -1))\n\n // Capitalized proper nouns (2+ words starting with uppercase)\n const properNouns = text.match(/[A-Z][a-z]+(?:\\s+[A-Z][a-z]+)+/g) || []\n for (const n of properNouns) add(n)\n\n // UUIDs and hashes\n const ids = text.match(/[a-f0-9]{8,}/gi) || []\n for (const id of ids.slice(0, 5)) add(id) // cap to avoid noise\n\n return entities.slice(0, 30) // max 30 entities to check\n}\n\n/**\n * Apply compaction result to a session's messages.\n * Replaces older messages with a single summary message and keeps recent ones.\n */\nexport function applyCompaction(\n messages: SessionMessage[],\n result: CompactionResult,\n): SessionMessage[] {\n if (result.compactedCount === 0) return messages\n\n const kept = messages.slice(-result.keptCount)\n\n // Prepend summary as a synthetic \"system\" message\n const summaryMessage: SessionMessage = {\n role: \"agent\",\n name: \"system\",\n content: `[Compacted conversation summary — ${result.compactedCount} earlier messages]\\n\\n${result.summary}`,\n timestamp: new Date().toISOString(),\n }\n\n return [summaryMessage, ...kept]\n}\n\n/**\n * Flush extractable memories from messages before they get compacted.\n * This ensures important facts survive compaction.\n */\nasync function flushMemories(\n agentId: string,\n messages: SessionMessage[],\n store: MemoryStore,\n origin?: { channel: string; chatId: string },\n): Promise<void> {\n // Build a conversation block from the messages\n const userMessages: string[] = []\n const agentMessages: string[] = []\n\n for (const msg of messages) {\n if (msg.role === \"user\") {\n userMessages.push(`${msg.name || \"User\"}: ${msg.content}`)\n } else {\n agentMessages.push(msg.content)\n }\n }\n\n if (userMessages.length === 0 || agentMessages.length === 0) return\n\n // Use the existing memory extraction on a combined block\n const { extractMemories } = await import(\"./memory-extract\")\n await extractMemories(\n agentId,\n userMessages.join(\"\\n\"),\n agentMessages.join(\"\\n\"),\n { channel: origin?.channel ?? \"compaction\", chatId: origin?.chatId ?? \"flush\", sender: \"compaction\" },\n store,\n )\n}\n\n/**\n * Format messages into a readable transcript for the summarizer.\n */\nfunction formatMessagesForSummary(messages: SessionMessage[]): string {\n return messages\n .map((m) => {\n const time = m.timestamp.slice(11, 16)\n const speaker = m.role === \"user\" ? (m.name || \"User\") : (m.name || \"Agent\")\n return `[${time}] ${speaker}: ${m.content}`\n })\n .join(\"\\n\\n\")\n}\n\n/**\n * Summarize a conversation transcript using a cheap/fast model.\n */\nasync function summarize(transcript: string): Promise<string> {\n // Truncate extremely long transcripts to avoid exceeding Haiku's context\n const maxInputChars = 20000\n const truncated = transcript.length > maxInputChars\n ? transcript.slice(-maxInputChars)\n : transcript\n\n try {\n const { createProvider } = await import(\"@/agent/providers\")\n const provider = createProvider(\"claude\")\n\n const result = await provider.generate(\n [\n { role: \"system\", content: COMPACTION_PROMPT },\n { role: \"user\", content: truncated },\n ],\n { model: COMPACTION_MODEL, maxTokens: 1024 },\n )\n\n return result.content.trim()\n } catch (error: any) {\n // Fallback: simple truncation if summarization fails\n const lines = transcript.split(\"\\n\")\n const kept = lines.slice(-20)\n return `[Summary unavailable — keeping last ${kept.length} lines]\\n${kept.join(\"\\n\")}`\n }\n}\n"],"mappings":"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","names":["readFileSync","writeFileSync","existsSync","mkdirSync","resolve","buildFingerprint","messages","toolCalls","lexicon","msg","words","tokenize","word","topKeywords","a","b","detectDrift","baseline","current","lexiconDecay","computeLexiconDecay","toolShift","newTools","missingTools","computeToolShift","semanticDrift","computeSemanticDrift","overallScore","lostWords","missing","baseSet","currSet","intersection","t","union","distance","currentSet","overlap","DRIFT_DIR","saveFingerprint","agentId","fingerprint","baseDir","dir","serialized","loadFingerprint","file","data","COMPACTION_MODEL","COMPACT_THRESHOLD","KEEP_RECENT","MIN_MESSAGES_TO_COMPACT","COMPACTION_PROMPT","needsCompaction","messages","sum","m","compactSession","agentId","memoryStore","origin","splitIdx","toCompact","toKeep","preFingerprint","buildFingerprint","saveFingerprint","memoryFlushed","flushMemories","historyText","formatMessagesForSummary","summary","summarize","qualityScore","lostEntities","verifyCompactionQuality","drift","summaryMessages","postFingerprint","baseline","loadFingerprint","detectDrift","original","entities","extractEntities","summaryLower","preserved","lost","entity","text","seen","add","key","urls","url","paths","p","refs","r","emails","e","quoted","q","properNouns","n","ids","id","applyCompaction","result","kept","store","userMessages","agentMessages","msg","extractMemories","time","speaker","transcript","truncated","createProvider","COMPACTION_MODEL"]}
package/dist/index.d.ts CHANGED
@@ -6817,10 +6817,7 @@ declare class AgentXDaemon {
6817
6817
  * for agents the daemon doesn't know about (e.g., a stale memory
6818
6818
  * entry for a since-removed agent). */
6819
6819
  private workspaceFor;
6820
- /** Agent-memory HTTP surface. Agents call this from inside a Claude
6821
- * Code session via `Bash` + `curl` — see the `remember` skill. The
6822
- * heavy lifting is in `AgentRegistry.agentMemory` (AgentMemory); this
6823
- * is a thin JSON wrapper that validates + delegates. */
6820
+ /** Agent-memory HTTP surface — see memory-api.ts. */
6824
6821
  private handleMemoryApi;
6825
6822
  private handleWorkflowEvent;
6826
6823
  /** Mesh-callable outbound send. A peer's workflow `action.send` invokes
@@ -7443,9 +7440,20 @@ interface DriftReport {
7443
7440
  missingTools: string[];
7444
7441
  }
7445
7442
 
7443
+ type SourceTrust =
7444
+ /** Only the operator can reach it: CLI, dashboard, local voice and TUI. */
7445
+ "operator"
7446
+ /** Authenticated people or peers: allow-listed chat senders, forge
7447
+ * project members, mesh callers. */
7448
+ | "internal"
7449
+ /** Anyone can write to it. */
7450
+ | "external";
7451
+ type FactReview = "held" | "approved" | "rejected";
7452
+
7446
7453
  interface MemoryFact {
7447
7454
  id: string;
7448
7455
  agentId: string;
7456
+ /** "secret" is legacy: no longer extracted, never injected. */
7449
7457
  category: "fact" | "secret" | "preference" | "commitment" | "task-state";
7450
7458
  content: string;
7451
7459
  keywords: string[];
@@ -7454,7 +7462,12 @@ interface MemoryFact {
7454
7462
  chatId: string;
7455
7463
  sender: string;
7456
7464
  date: string;
7465
+ trust?: SourceTrust;
7457
7466
  };
7467
+ /** "held" facts wait for an operator before they are injected. */
7468
+ review?: FactReview;
7469
+ reviewedBy?: string;
7470
+ reviewedAt?: string;
7458
7471
  createdAt: string;
7459
7472
  expiresAt?: string;
7460
7473
  accessCount?: number;
@@ -7467,7 +7480,9 @@ declare class MemoryStore {
7467
7480
  private memoryDir;
7468
7481
  constructor(baseDir?: string);
7469
7482
  private filePath;
7470
- addMemory(agentId: string, fact: Omit<MemoryFact, "id" | "createdAt">): void;
7483
+ /** Store a fact. Refuses secrets (returns false). Stamps the source's
7484
+ * trust, and holds facts from external sources for review. */
7485
+ addMemory(agentId: string, fact: Omit<MemoryFact, "id" | "createdAt">): boolean;
7471
7486
  getAll(agentId: string): MemoryFact[];
7472
7487
  findRelevant(message: string, agentId: string, limit?: number): MemoryFact[];
7473
7488
  buildContext(memories: MemoryFact[]): string;
@@ -7485,6 +7500,18 @@ declare class MemoryStore {
7485
7500
  * Injected into heartbeat context so the agent proactively recalls facts.
7486
7501
  */
7487
7502
  buildRecallContext(agentId: string): string;
7503
+ /** Facts waiting for an operator's review, oldest first. Includes
7504
+ * external facts stored before review existed: they have no review
7505
+ * state, and are kept out of prompts all the same. */
7506
+ held(agentId: string): MemoryFact[];
7507
+ /** Approve or reject a fact. Returns false when the id is unknown. */
7508
+ review(agentId: string, id: string, decision: "approved" | "rejected", by?: string): boolean;
7509
+ /** Facts that contain a credential, stored before extraction stopped
7510
+ * keeping them. They are never injected; `apply` also deletes them. */
7511
+ scrubSecrets(agentId: string, apply?: boolean): number;
7512
+ /** Trust and review counts, for the CLI. */
7513
+ trustSummary(agentId: string): Record<string, number>;
7514
+ private rewrite;
7488
7515
  /**
7489
7516
  * Check if a substantially similar memory already exists.
7490
7517
  */
@@ -8446,7 +8473,7 @@ declare const classificationSchema: z.ZodObject<{
8446
8473
  preview: z.ZodOptional<z.ZodString>;
8447
8474
  }, "strip", z.ZodTypeAny, {
8448
8475
  path: string[];
8449
- status: "pending" | "approved" | "rejected";
8476
+ status: "approved" | "rejected" | "pending";
8450
8477
  source: "user" | "llm" | "cache";
8451
8478
  ts: string;
8452
8479
  msgHash: string;
@@ -8462,7 +8489,7 @@ declare const classificationSchema: z.ZodObject<{
8462
8489
  sender?: string | undefined;
8463
8490
  }, {
8464
8491
  path: string[];
8465
- status: "pending" | "approved" | "rejected";
8492
+ status: "approved" | "rejected" | "pending";
8466
8493
  source: "user" | "llm" | "cache";
8467
8494
  ts: string;
8468
8495
  msgHash: string;
@@ -8747,6 +8774,14 @@ declare class GraphStore {
8747
8774
  rejectClassification(c: Classification): void;
8748
8775
  }
8749
8776
 
8777
+ /** Conditions for a write, mirroring HTTP If-Match / If-None-Match. */
8778
+ interface WriteCondition {
8779
+ /** Write only when the current file has this etag. */
8780
+ ifMatch?: string;
8781
+ /** "*": write only when no memory exists yet (create, never overwrite). */
8782
+ ifNoneMatch?: "*";
8783
+ }
8784
+
8750
8785
  type MemoryType = "user" | "feedback" | "project" | "reference";
8751
8786
  interface MemoryRecord {
8752
8787
  /** Slug-safe name, unique within an agent. */
@@ -8758,6 +8793,24 @@ interface MemoryRecord {
8758
8793
  body: string;
8759
8794
  createdAt: string;
8760
8795
  updatedAt: string;
8796
+ /** Who wrote this version: an agent id verified against its running
8797
+ * task, "operator" (CLI), or "unverified" (a caller we couldn't check). */
8798
+ author?: string;
8799
+ /** The running task that wrote it; opens on the Task page. */
8800
+ taskId?: string;
8801
+ /** Content hash of the file as stored. Pass back as `ifMatch` to change
8802
+ * it only if nobody else has since. Not stored in the file. */
8803
+ etag?: string;
8804
+ }
8805
+ /** Who is making a change, recorded in the memory's frontmatter. */
8806
+ interface MemoryProvenance {
8807
+ author?: string;
8808
+ taskId?: string;
8809
+ }
8810
+ /** One entry in a memory's history. */
8811
+ interface MemoryVersion {
8812
+ id: string;
8813
+ record: MemoryRecord | null;
8761
8814
  }
8762
8815
  interface AgentMemoryOptions {
8763
8816
  baseDir?: string;
@@ -8769,20 +8822,40 @@ declare class AgentMemory {
8769
8822
  private fileFor;
8770
8823
  private indexPath;
8771
8824
  /** Save (create or update) a memory. Keeps createdAt stable across
8772
- * updates; bumps updatedAt. Always rewrites MEMORY.md. */
8825
+ * updates; bumps updatedAt. The replaced version goes to history first.
8826
+ * With `cond`, throws MemoryConflictError instead of overwriting a
8827
+ * version the caller hasn't seen. Always rewrites MEMORY.md. */
8773
8828
  save(args: {
8774
8829
  agentId: string;
8775
8830
  type: MemoryType;
8776
8831
  name: string;
8777
8832
  description: string;
8778
8833
  body: string;
8779
- }): MemoryRecord;
8834
+ } & MemoryProvenance, cond?: WriteCondition): MemoryRecord;
8835
+ /** Add to the end of an existing memory's body (or create it) in one
8836
+ * step, so two appends can't lose each other. */
8837
+ append(args: {
8838
+ agentId: string;
8839
+ type: MemoryType;
8840
+ name: string;
8841
+ description: string;
8842
+ body: string;
8843
+ } & MemoryProvenance, cond?: WriteCondition): MemoryRecord;
8780
8844
  get(agentId: string, name: string): MemoryRecord | null;
8781
8845
  /** Every memory for an agent. Sorted by type then name for stable
8782
8846
  * index rendering. */
8783
8847
  list(agentId: string): MemoryRecord[];
8784
- /** Remove a memory by name. Returns true on success, false if none found. */
8785
- remove(agentId: string, name: string): boolean;
8848
+ /** Remove a memory by name. The removed version stays in history, so
8849
+ * `restore` can bring it back. With `cond.ifMatch`, throws
8850
+ * MemoryConflictError if it changed since the caller read it. Returns
8851
+ * true on success, false if none found. */
8852
+ remove(agentId: string, name: string, cond?: WriteCondition): boolean;
8853
+ /** Prior versions of a memory, newest first, including ones kept after
8854
+ * it was deleted. */
8855
+ versions(agentId: string, name: string): MemoryVersion[];
8856
+ /** Put a prior version back as the current one. The version it replaces
8857
+ * goes to history, so a restore can itself be undone. */
8858
+ restore(agentId: string, name: string, versionId: string, by?: MemoryProvenance): MemoryRecord | null;
8786
8859
  /** The content of MEMORY.md — used at prompt-build time to inline into
8787
8860
  * the agent's system prompt. Returns "" when the agent has no
8788
8861
  * memories (so the prompt builder can no-op). */
@@ -8895,7 +8968,8 @@ interface TaskRecord {
8895
8968
  transcript: string;
8896
8969
  }
8897
8970
  /** Outcome of AgentRegistry.continueFinishedTask. `taskId` is the new run's
8898
- * dashboard id; absent when the message was queued behind a busy slot. */
8971
+ * dashboard id; absent when the message was queued behind a busy slot or
8972
+ * answered without taking a slot (`answeredBy`), so there is no run to open. */
8899
8973
  type ContinueFinishedTaskResult = {
8900
8974
  ok: true;
8901
8975
  agentId: string;
@@ -8903,6 +8977,7 @@ type ContinueFinishedTaskResult = {
8903
8977
  chatId: string;
8904
8978
  taskId?: string;
8905
8979
  queued: boolean;
8980
+ answeredBy?: "attached" | "other";
8906
8981
  } | {
8907
8982
  ok: false;
8908
8983
  status: 404 | 409 | 500;
@@ -9137,6 +9212,13 @@ declare class AgentRegistry {
9137
9212
  * `a2a`, `cron`, `workflow`, …) where no adapter is registered.
9138
9213
  */
9139
9214
  private postQueuedResponseToChannel;
9215
+ /** The agent running `taskId` right now, or null once it has finished.
9216
+ * Lets an HTTP caller that names its task prove which agent it is. */
9217
+ runningTaskOwner(taskId: string): {
9218
+ agentId: string;
9219
+ channel: string;
9220
+ chatId?: string;
9221
+ } | null;
9140
9222
  /**
9141
9223
  * Operator stop. Aborts the in-flight run for `taskId` (kills the
9142
9224
  * underlying claude subprocess via the AbortSignal threaded through
@@ -9180,7 +9262,7 @@ declare class AgentRegistry {
9180
9262
  * the person in that chat, which is worse than refusing.
9181
9263
  *
9182
9264
  * Resolves once the new run starts (with its task id, for the Task page)
9183
- * or, if the agent is busy and the message was queued, once execute
9265
+ * or, if the message was queued or answered without a slot, once execute
9184
9266
  * returns without one.
9185
9267
  */
9186
9268
  continueFinishedTask(agentId: string, taskId: string, message: string, sender: string, opts?: {
@@ -9206,7 +9288,12 @@ declare class AgentRegistry {
9206
9288
  totalInput: number;
9207
9289
  totalOutput: number;
9208
9290
  totalCacheRead: number;
9209
- totalCacheCreate: number;
9291
+ totalCacheCreate: number; /** Workflow auto-runner — invoked when workflow matching is in `mode: "auto"`
9292
+ * and a candidate workflow's confidence ≥ autoRunThreshold. Wired by the
9293
+ * daemon after the workflow dispatcher is constructed. Returns the runId of
9294
+ * the started workflow run; the workflow itself owns posting any reply via
9295
+ * its `action.send` / `agent` nodes. Duck-typed to avoid a circular import
9296
+ * on WorkflowDispatcher. */
9210
9297
  cacheHitRatio: number;
9211
9298
  totalErrors: number;
9212
9299
  totalCost: number;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import{a as L}from"./chunk-W5C2GDNC.js";import{$ as Tt,_ as bt,a as St,aa as Mt,b as kt,c as vt,d as xt,e as $t,f as Ct,g as Pt}from"./chunk-ZQOGAUQS.js";import"./chunk-MX5GCR7Y.js";import"./chunk-V5FZJUK4.js";import"./chunk-3SCPRYJB.js";import"./chunk-V7FYSZSV.js";import"./chunk-ZVP6GB4G.js";import{N as yt,l as lt,m as ct,n as dt,o as mt,p as pt}from"./chunk-T76YTN34.js";import"./chunk-TCJ3SMOF.js";import"./chunk-VMDA2SX2.js";import"./chunk-QLTM3F7M.js";import{A as D,B as z,C as V,a as ye,b as we,c as Se,d as Ae,e as Ie,f as Oe,g as Ne,h as He,i as je,j as Ue,k as _e,l as I,m as q,n as Be,o as qe,p as Ye,q as O,r as ze,s as Ve,t as Xe,u as x,v as Y,w as Qe,x as Ze,y as et,z as wt}from"./chunk-BTGPZ6B7.js";import{a as B,b as M,c as F}from"./chunk-SFQUP3BP.js";import{k as ut}from"./chunk-BIFJSQB5.js";import"./chunk-WPMX3VGI.js";import"./chunk-K33GIBSE.js";import"./chunk-76Z4VHDX.js";import"./chunk-W4YR2RO2.js";import"./chunk-AFRHKA7B.js";import"./chunk-NSYY5NCR.js";import"./chunk-ICZPZYYR.js";import"./chunk-DWADWDMU.js";import"./chunk-77OV7346.js";import"./chunk-VWTBHLMX.js";import{a as gt,b as ft,e as ht}from"./chunk-MTVRYTLL.js";import"./chunk-PNWNVOEZ.js";import"./chunk-64P4QHSZ.js";import"./chunk-TNFOGJWZ.js";import"./chunk-AJ5RMU7X.js";import"./chunk-2GIDVZ4H.js";import{j as it,k as rt,l as at}from"./chunk-6HMZ5TTM.js";import"./chunk-F4HZTZHW.js";import"./chunk-AJONZWLT.js";import"./chunk-ZTEU5JVQ.js";import"./chunk-QOYAX2FT.js";import{a as ke,b as ve,c as xe,d as $e,e as Ce,f as Pe,g as be,h as Te,i as Me,j as Re,l as Ee,m as Ge,o as T,p as W}from"./chunk-LCIREYTD.js";import{a as m,b as G}from"./chunk-L2WSPY6S.js";import{a as Fe,b as Le,c as A,d as De}from"./chunk-VDMYBPYB.js";import{a as Ke,b as Je,e as We}from"./chunk-DSYKYZMT.js";import{j as tt,l as st,m as ot,o as nt}from"./chunk-QJANPP4C.js";import"./chunk-BH3GTCZ6.js";import"./chunk-FIOB3MEX.js";W();G();import{execa as Rt}from"execa";import{existsSync as se,promises as X}from"fs";import S from"path";import ie from"node-fetch";async function Et(o,e){let t=o.split("/");if(t.length<2)throw new Error(`Invalid skill package ID: ${o}. Expected format: owner/repo`);let s=t[0],i=t[1],n=t[2];try{let a=n?`${s}/${i}/${n}`:`${s}/${i}`;await Rt("npx",["-y","skills","add",a],{cwd:e,stdio:"pipe"}),m.success(`Installed skill package: ${o}`);let r=S.resolve(e,".skills",`${s}_${i}`);if(se(r))return await ne(r,o);let d=S.resolve(e,".skills");return se(d)?await ne(d,o):[]}catch{return m.info(`Fetching skills directly from GitHub: ${s}/${i}`),await Gt(s,i,n,e)}}async function Gt(o,e,t,s){let i=[];try{let n=`https://api.github.com/repos/${o}/${e}/git/trees/main?recursive=1`,a=await ie(n);if(!a.ok)throw new Error(`GitHub API error: ${a.status}`);let d=(await a.json()).tree.filter(p=>p.type==="blob"&&p.path.endsWith("SKILL.md"));if(t){let p=d.find(f=>f.path.includes(`/${t}/`)||f.path===`${t}/SKILL.md`);if(p){let f=await oe(o,e,p.path,s);f&&i.push(f)}}else for(let p of d){let f=await oe(o,e,p.path,s);f&&i.push(f)}}catch(n){m.error(`Failed to fetch skills from GitHub: ${n.message}`)}return i}async function oe(o,e,t,s){try{let i=`https://raw.githubusercontent.com/${o}/${e}/main/${t}`,n=await ie(i);if(!n.ok)return null;let a=await n.text(),r=A(a);if(r){r.source="remote",r.packageId=`${o}/${e}`;let d=S.dirname(t),p=S.resolve(s,".skills",`${o}_${e}`,d);await X.mkdir(p,{recursive:!0}),await X.writeFile(S.resolve(p,"SKILL.md"),a),r.path=S.resolve(p,"SKILL.md")}return r}catch{return null}}async function ne(o,e){let t=[],{default:s}=await import("fast-glob"),i=await s.glob("**/SKILL.md",{cwd:o,deep:5});for(let n of i)try{let a=S.resolve(o,n),r=await X.readFile(a,"utf8"),d=A(r);d&&(d.source="remote",d.path=a,d.packageId=e,t.push(d))}catch{}return t}async function At(o,e,t,s,i){let n=["---",`name: ${o}`,`description: ${e}`];if(s?.length){n.push("tags:");for(let a of s)n.push(` - ${a}`)}if(i?.length){n.push("globs:");for(let a of i)n.push(` - ${a}`)}return n.push("---"),`${n.join(`
1
+ import{a as L}from"./chunk-W5C2GDNC.js";import{$ as Tt,_ as bt,a as St,aa as Mt,b as kt,c as vt,d as xt,e as $t,f as Ct,g as Pt}from"./chunk-MDAC3PKR.js";import"./chunk-MX5GCR7Y.js";import"./chunk-V5FZJUK4.js";import"./chunk-3SCPRYJB.js";import"./chunk-V7FYSZSV.js";import"./chunk-ZVP6GB4G.js";import{P as yt,l as lt,m as ct,n as dt,o as mt,p as pt}from"./chunk-3RKIBJG6.js";import"./chunk-TCJ3SMOF.js";import"./chunk-VMDA2SX2.js";import"./chunk-QLTM3F7M.js";import{A as D,B as z,C as V,a as ye,b as we,c as Se,d as Ae,e as Ie,f as Oe,g as Ne,h as He,i as je,j as Ue,k as _e,l as I,m as q,n as Be,o as qe,p as Ye,q as O,r as ze,s as Ve,t as Xe,u as x,v as Y,w as Qe,x as Ze,y as et,z as wt}from"./chunk-3FIS6JD3.js";import{a as B,b as M,c as F}from"./chunk-SFQUP3BP.js";import{k as ut}from"./chunk-BIFJSQB5.js";import"./chunk-WPMX3VGI.js";import"./chunk-K33GIBSE.js";import"./chunk-76Z4VHDX.js";import"./chunk-W4YR2RO2.js";import"./chunk-AFRHKA7B.js";import"./chunk-NSYY5NCR.js";import"./chunk-ICZPZYYR.js";import"./chunk-DWADWDMU.js";import"./chunk-CQR6C5FU.js";import"./chunk-VWTBHLMX.js";import{a as gt,b as ft,e as ht}from"./chunk-MTVRYTLL.js";import"./chunk-PNWNVOEZ.js";import"./chunk-64P4QHSZ.js";import"./chunk-TNFOGJWZ.js";import"./chunk-AJ5RMU7X.js";import"./chunk-2GIDVZ4H.js";import{j as it,k as rt,l as at}from"./chunk-6HMZ5TTM.js";import"./chunk-F4HZTZHW.js";import"./chunk-AJONZWLT.js";import"./chunk-ZTEU5JVQ.js";import"./chunk-QOYAX2FT.js";import{a as ke,b as ve,c as xe,d as $e,e as Ce,f as Pe,g as be,h as Te,i as Me,j as Re,l as Ee,m as Ge,o as T,p as W}from"./chunk-LCIREYTD.js";import{a as m,b as G}from"./chunk-L2WSPY6S.js";import{a as Fe,b as Le,c as A,d as De}from"./chunk-VDMYBPYB.js";import{a as Ke,b as Je,e as We}from"./chunk-DSYKYZMT.js";import{j as tt,l as st,m as ot,o as nt}from"./chunk-QJANPP4C.js";import"./chunk-BH3GTCZ6.js";import"./chunk-FIOB3MEX.js";W();G();import{execa as Rt}from"execa";import{existsSync as se,promises as X}from"fs";import S from"path";import ie from"node-fetch";async function Et(o,e){let t=o.split("/");if(t.length<2)throw new Error(`Invalid skill package ID: ${o}. Expected format: owner/repo`);let s=t[0],i=t[1],n=t[2];try{let a=n?`${s}/${i}/${n}`:`${s}/${i}`;await Rt("npx",["-y","skills","add",a],{cwd:e,stdio:"pipe"}),m.success(`Installed skill package: ${o}`);let r=S.resolve(e,".skills",`${s}_${i}`);if(se(r))return await ne(r,o);let d=S.resolve(e,".skills");return se(d)?await ne(d,o):[]}catch{return m.info(`Fetching skills directly from GitHub: ${s}/${i}`),await Gt(s,i,n,e)}}async function Gt(o,e,t,s){let i=[];try{let n=`https://api.github.com/repos/${o}/${e}/git/trees/main?recursive=1`,a=await ie(n);if(!a.ok)throw new Error(`GitHub API error: ${a.status}`);let d=(await a.json()).tree.filter(p=>p.type==="blob"&&p.path.endsWith("SKILL.md"));if(t){let p=d.find(f=>f.path.includes(`/${t}/`)||f.path===`${t}/SKILL.md`);if(p){let f=await oe(o,e,p.path,s);f&&i.push(f)}}else for(let p of d){let f=await oe(o,e,p.path,s);f&&i.push(f)}}catch(n){m.error(`Failed to fetch skills from GitHub: ${n.message}`)}return i}async function oe(o,e,t,s){try{let i=`https://raw.githubusercontent.com/${o}/${e}/main/${t}`,n=await ie(i);if(!n.ok)return null;let a=await n.text(),r=A(a);if(r){r.source="remote",r.packageId=`${o}/${e}`;let d=S.dirname(t),p=S.resolve(s,".skills",`${o}_${e}`,d);await X.mkdir(p,{recursive:!0}),await X.writeFile(S.resolve(p,"SKILL.md"),a),r.path=S.resolve(p,"SKILL.md")}return r}catch{return null}}async function ne(o,e){let t=[],{default:s}=await import("fast-glob"),i=await s.glob("**/SKILL.md",{cwd:o,deep:5});for(let n of i)try{let a=S.resolve(o,n),r=await X.readFile(a,"utf8"),d=A(r);d&&(d.source="remote",d.path=a,d.packageId=e,t.push(d))}catch{}return t}async function At(o,e,t,s,i){let n=["---",`name: ${o}`,`description: ${e}`];if(s?.length){n.push("tags:");for(let a of s)n.push(` - ${a}`)}if(i?.length){n.push("globs:");for(let a of i)n.push(` - ${a}`)}return n.push("---"),`${n.join(`
2
2
  `)}
3
3
 
4
4
  ${t}`}async function It(o){let{loadLocalSkills:e}=await import("./loader-BOKE5M5S.js");return e(o)}async function Ot(o,e,t,s,i){let a=[{role:"system",content:`You are a skill author for the Agent Skills ecosystem (skills.sh).
@@ -0,0 +1,2 @@
1
+ import{g as a}from"./chunk-CQR6C5FU.js";import"./chunk-FIOB3MEX.js";export{a as extractMemories};
2
+ //# sourceMappingURL=memory-extract-3TO7UC5B.js.map
@@ -1,2 +1,2 @@
1
- import{a}from"./chunk-3TL5EIVN.js";import"./chunk-7A7RC3R4.js";import"./chunk-YQVR2HQS.js";import"./chunk-SAFAI4AV.js";import"./chunk-2TJ5L7CB.js";import"./chunk-7CVJ4JOE.js";import"./chunk-WLVQDEOT.js";import"./chunk-GV7S5BAX.js";import"./chunk-GSX4NK2N.js";import"./chunk-SZDACK6O.js";import"./chunk-ZQOGAUQS.js";import"./chunk-MX5GCR7Y.js";import"./chunk-V5FZJUK4.js";import"./chunk-3SCPRYJB.js";import"./chunk-V7FYSZSV.js";import"./chunk-ZVP6GB4G.js";import"./chunk-T76YTN34.js";import"./chunk-TCJ3SMOF.js";import"./chunk-VMDA2SX2.js";import"./chunk-QLTM3F7M.js";import"./chunk-BTGPZ6B7.js";import"./chunk-SFQUP3BP.js";import"./chunk-BIFJSQB5.js";import"./chunk-WPMX3VGI.js";import"./chunk-K33GIBSE.js";import"./chunk-76Z4VHDX.js";import"./chunk-W4YR2RO2.js";import"./chunk-AFRHKA7B.js";import"./chunk-NSYY5NCR.js";import"./chunk-ICZPZYYR.js";import"./chunk-DWADWDMU.js";import"./chunk-77OV7346.js";import"./chunk-VWTBHLMX.js";import"./chunk-MTVRYTLL.js";import"./chunk-PNWNVOEZ.js";import"./chunk-64P4QHSZ.js";import"./chunk-TNFOGJWZ.js";import"./chunk-AJ5RMU7X.js";import"./chunk-2GIDVZ4H.js";import"./chunk-6HMZ5TTM.js";import"./chunk-F4HZTZHW.js";import"./chunk-AJONZWLT.js";import"./chunk-ZTEU5JVQ.js";import"./chunk-QOYAX2FT.js";import"./chunk-LCIREYTD.js";import"./chunk-L2WSPY6S.js";import"./chunk-VDMYBPYB.js";import"./chunk-DSYKYZMT.js";import"./chunk-QJANPP4C.js";import"./chunk-BH3GTCZ6.js";import"./chunk-FIOB3MEX.js";export{a as buildProgram};
2
- //# sourceMappingURL=program-JLCKBO2A.js.map
1
+ import{a}from"./chunk-KZYNQRQQ.js";import"./chunk-7A7RC3R4.js";import"./chunk-YQVR2HQS.js";import"./chunk-SAFAI4AV.js";import"./chunk-2TJ5L7CB.js";import"./chunk-7CVJ4JOE.js";import"./chunk-WLVQDEOT.js";import"./chunk-GV7S5BAX.js";import"./chunk-GSX4NK2N.js";import"./chunk-SZDACK6O.js";import"./chunk-MDAC3PKR.js";import"./chunk-MX5GCR7Y.js";import"./chunk-V5FZJUK4.js";import"./chunk-3SCPRYJB.js";import"./chunk-V7FYSZSV.js";import"./chunk-ZVP6GB4G.js";import"./chunk-3RKIBJG6.js";import"./chunk-TCJ3SMOF.js";import"./chunk-VMDA2SX2.js";import"./chunk-QLTM3F7M.js";import"./chunk-3FIS6JD3.js";import"./chunk-SFQUP3BP.js";import"./chunk-BIFJSQB5.js";import"./chunk-WPMX3VGI.js";import"./chunk-K33GIBSE.js";import"./chunk-76Z4VHDX.js";import"./chunk-W4YR2RO2.js";import"./chunk-AFRHKA7B.js";import"./chunk-NSYY5NCR.js";import"./chunk-ICZPZYYR.js";import"./chunk-DWADWDMU.js";import"./chunk-CQR6C5FU.js";import"./chunk-VWTBHLMX.js";import"./chunk-MTVRYTLL.js";import"./chunk-PNWNVOEZ.js";import"./chunk-64P4QHSZ.js";import"./chunk-TNFOGJWZ.js";import"./chunk-AJ5RMU7X.js";import"./chunk-2GIDVZ4H.js";import"./chunk-6HMZ5TTM.js";import"./chunk-F4HZTZHW.js";import"./chunk-AJONZWLT.js";import"./chunk-ZTEU5JVQ.js";import"./chunk-QOYAX2FT.js";import"./chunk-LCIREYTD.js";import"./chunk-L2WSPY6S.js";import"./chunk-VDMYBPYB.js";import"./chunk-DSYKYZMT.js";import"./chunk-QJANPP4C.js";import"./chunk-BH3GTCZ6.js";import"./chunk-FIOB3MEX.js";export{a as buildProgram};
2
+ //# sourceMappingURL=program-WLPVWTAT.js.map
@@ -1,2 +1,2 @@
1
- import{E as a,F as b,G as c,H as d,I as e,J as f,K as g,L as h,M as i,N as j}from"./chunk-T76YTN34.js";import"./chunk-TCJ3SMOF.js";import"./chunk-VMDA2SX2.js";import"./chunk-QLTM3F7M.js";import"./chunk-BIFJSQB5.js";import"./chunk-WPMX3VGI.js";import"./chunk-K33GIBSE.js";import"./chunk-W4YR2RO2.js";import"./chunk-AFRHKA7B.js";import"./chunk-NSYY5NCR.js";import"./chunk-ICZPZYYR.js";import"./chunk-DWADWDMU.js";import"./chunk-77OV7346.js";import"./chunk-VWTBHLMX.js";import"./chunk-MTVRYTLL.js";import"./chunk-PNWNVOEZ.js";import"./chunk-64P4QHSZ.js";import"./chunk-TNFOGJWZ.js";import"./chunk-AJ5RMU7X.js";import"./chunk-2GIDVZ4H.js";import"./chunk-6HMZ5TTM.js";import"./chunk-F4HZTZHW.js";import"./chunk-AJONZWLT.js";import"./chunk-ZTEU5JVQ.js";import"./chunk-QOYAX2FT.js";import"./chunk-LCIREYTD.js";import"./chunk-L2WSPY6S.js";import"./chunk-DSYKYZMT.js";import"./chunk-QJANPP4C.js";import"./chunk-BH3GTCZ6.js";import"./chunk-FIOB3MEX.js";export{j as AgentRegistry,e as TRACE_STEP_SUMMARY_BYTES,c as buildEntryMeta,d as buildWikiContext,f as clipForTrace,i as emitTraceStepsFromStreamEvent,h as firstMissingRequiredTool,b as getGlobalRegistry,a as setGlobalRegistry,g as tallyToolUses};
2
- //# sourceMappingURL=registry-5QOY65FS.js.map
1
+ import{G as a,H as b,I as c,J as d,K as e,L as f,M as g,N as h,O as i,P as j}from"./chunk-3RKIBJG6.js";import"./chunk-TCJ3SMOF.js";import"./chunk-VMDA2SX2.js";import"./chunk-QLTM3F7M.js";import"./chunk-BIFJSQB5.js";import"./chunk-WPMX3VGI.js";import"./chunk-K33GIBSE.js";import"./chunk-W4YR2RO2.js";import"./chunk-AFRHKA7B.js";import"./chunk-NSYY5NCR.js";import"./chunk-ICZPZYYR.js";import"./chunk-DWADWDMU.js";import"./chunk-CQR6C5FU.js";import"./chunk-VWTBHLMX.js";import"./chunk-MTVRYTLL.js";import"./chunk-PNWNVOEZ.js";import"./chunk-64P4QHSZ.js";import"./chunk-TNFOGJWZ.js";import"./chunk-AJ5RMU7X.js";import"./chunk-2GIDVZ4H.js";import"./chunk-6HMZ5TTM.js";import"./chunk-F4HZTZHW.js";import"./chunk-AJONZWLT.js";import"./chunk-ZTEU5JVQ.js";import"./chunk-QOYAX2FT.js";import"./chunk-LCIREYTD.js";import"./chunk-L2WSPY6S.js";import"./chunk-DSYKYZMT.js";import"./chunk-QJANPP4C.js";import"./chunk-BH3GTCZ6.js";import"./chunk-FIOB3MEX.js";export{j as AgentRegistry,e as TRACE_STEP_SUMMARY_BYTES,c as buildEntryMeta,d as buildWikiContext,f as clipForTrace,i as emitTraceStepsFromStreamEvent,h as firstMissingRequiredTool,b as getGlobalRegistry,a as setGlobalRegistry,g as tallyToolUses};
2
+ //# sourceMappingURL=registry-EESWAPMR.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentix-cli",
3
- "version": "0.44.1",
3
+ "version": "0.45.1",
4
4
  "description": "AI operations layer for small and medium businesses. Routes Telegram, WhatsApp, GitLab, GitHub, crons, webhooks, and mesh tasks to AI agents. Web wizard for operators, CLI for engineers. Self-hosted.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -1,23 +0,0 @@
1
- var d="claude-haiku-4-20250514",f=`You are a memory extraction system for an AI agent. Given a conversation exchange, extract facts worth remembering for future conversations across different chat sessions.
2
-
3
- Extract ONLY facts that would help the agent in FUTURE, DIFFERENT conversations:
4
- - Credentials, tokens, API keys shared by the user
5
- - User preferences and instructions ("never do X", "always use Y")
6
- - Commitments the agent made ("I will deploy by Friday")
7
- - Relationships ("Alex is the admin", "Marketing handles content")
8
- - Task state ("GitLab tokens need deploying to peer-server")
9
- - Important facts about infrastructure, config, or processes
10
-
11
- SKIP:
12
- - Routine greetings, acknowledgments, status updates
13
- - Information derivable from code or config files
14
- - Transient conversation flow ("let me check", "here's what I found")
15
- - Facts already obvious from the agent's system prompt
16
-
17
- For each fact, output a JSON array:
18
- [{"category":"fact|secret|preference|commitment|task-state","content":"concise fact, 1-2 sentences","keywords":["keyword1","keyword2"]}]
19
-
20
- If nothing worth remembering, output: []`;async function h(t,s,a,n,r){if(s.length<20&&a.length<50)return;let{createProvider:i}=await import("./providers-KO2DODIO.js"),c=(await i("claude").generate([{role:"system",content:f},{role:"user",content:`${n.sender} (via ${n.channel}): ${s}
21
-
22
- Agent: ${a}`}],{model:d,maxTokens:512})).content.trim().match(/\[[\s\S]*\]/);if(!c)return;let o=JSON.parse(c[0]);if(!Array.isArray(o)||o.length===0)return;let m=new Date().toISOString().slice(0,10);for(let e of o)r.hasSimilar(t,e.content)||r.addMemory(t,{agentId:t,category:e.category,content:e.content,keywords:e.keywords||[],source:{...n,date:m},expiresAt:e.category==="task-state"?new Date(Date.now()+7*24*60*60*1e3).toISOString():void 0});Math.random()<.02&&r.prune(t)}export{h as a};
23
- //# sourceMappingURL=chunk-77OV7346.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/agents/memory-extract.ts"],"sourcesContent":["import type { MemoryStore } from \"./memory-store\"\n\n// --- Haiku-powered memory extraction ---\n// Fire-and-forget after each agent response.\n// Extracts memorable facts and writes them to the persistent store.\n\nconst EXTRACTION_MODEL = \"claude-haiku-4-20250514\"\n\nconst EXTRACTION_PROMPT = `You are a memory extraction system for an AI agent. Given a conversation exchange, extract facts worth remembering for future conversations across different chat sessions.\n\nExtract ONLY facts that would help the agent in FUTURE, DIFFERENT conversations:\n- Credentials, tokens, API keys shared by the user\n- User preferences and instructions (\"never do X\", \"always use Y\")\n- Commitments the agent made (\"I will deploy by Friday\")\n- Relationships (\"Alex is the admin\", \"Marketing handles content\")\n- Task state (\"GitLab tokens need deploying to peer-server\")\n- Important facts about infrastructure, config, or processes\n\nSKIP:\n- Routine greetings, acknowledgments, status updates\n- Information derivable from code or config files\n- Transient conversation flow (\"let me check\", \"here's what I found\")\n- Facts already obvious from the agent's system prompt\n\nFor each fact, output a JSON array:\n[{\"category\":\"fact|secret|preference|commitment|task-state\",\"content\":\"concise fact, 1-2 sentences\",\"keywords\":[\"keyword1\",\"keyword2\"]}]\n\nIf nothing worth remembering, output: []`\n\nexport async function extractMemories(\n agentId: string,\n userMessage: string,\n agentResponse: string,\n source: { channel: string; chatId: string; sender: string },\n store: MemoryStore,\n): Promise<void> {\n // Skip very short exchanges (unlikely to contain memorable facts)\n if (userMessage.length < 20 && agentResponse.length < 50) return\n\n const { createProvider } = await import(\"@/agent/providers\")\n const provider = createProvider(\"claude\")\n\n const result = await provider.generate(\n [\n { role: \"system\", content: EXTRACTION_PROMPT },\n {\n role: \"user\",\n content: `${source.sender} (via ${source.channel}): ${userMessage}\\n\\nAgent: ${agentResponse}`,\n },\n ],\n { model: EXTRACTION_MODEL, maxTokens: 512 },\n )\n\n // Parse JSON array from response\n const content = result.content.trim()\n const jsonMatch = content.match(/\\[[\\s\\S]*\\]/)\n if (!jsonMatch) return\n\n const facts = JSON.parse(jsonMatch[0]) as Array<{\n category: string\n content: string\n keywords: string[]\n }>\n\n if (!Array.isArray(facts) || facts.length === 0) return\n\n const date = new Date().toISOString().slice(0, 10)\n\n for (const fact of facts) {\n // Skip duplicates\n if (store.hasSimilar(agentId, fact.content)) continue\n\n store.addMemory(agentId, {\n agentId,\n category: fact.category as any,\n content: fact.content,\n keywords: fact.keywords || [],\n source: { ...source, date },\n expiresAt: fact.category === \"task-state\"\n ? new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString()\n : undefined,\n })\n }\n\n // Prune occasionally (every ~50 calls, probabilistic)\n if (Math.random() < 0.02) {\n store.prune(agentId)\n }\n}\n"],"mappings":"AAMA,IAAMA,EAAmB,0BAEnBC,EAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0CAqB1B,eAAsBC,EACpBC,EACAC,EACAC,EACAC,EACAC,EACe,CAEf,GAAIH,EAAY,OAAS,IAAMC,EAAc,OAAS,GAAI,OAE1D,GAAM,CAAE,eAAAG,CAAe,EAAI,KAAM,QAAO,yBAAmB,EAgBrDC,GAbS,MAFED,EAAe,QAAQ,EAEV,SAC5B,CACE,CAAE,KAAM,SAAU,QAASP,CAAkB,EAC7C,CACE,KAAM,OACN,QAAS,GAAGK,EAAO,eAAeA,EAAO,aAAaF;AAAA;AAAA,SAAyBC,GACjF,CACF,EACA,CAAE,MAAOL,EAAkB,UAAW,GAAI,CAC5C,GAGuB,QAAQ,KAAK,EACV,MAAM,aAAa,EAC7C,GAAI,CAACS,EAAW,OAEhB,IAAMC,EAAQ,KAAK,MAAMD,EAAU,CAAC,CAAC,EAMrC,GAAI,CAAC,MAAM,QAAQC,CAAK,GAAKA,EAAM,SAAW,EAAG,OAEjD,IAAMC,EAAO,IAAI,KAAK,EAAE,YAAY,EAAE,MAAM,EAAG,EAAE,EAEjD,QAAWC,KAAQF,EAEbH,EAAM,WAAWJ,EAASS,EAAK,OAAO,GAE1CL,EAAM,UAAUJ,EAAS,CACvB,QAAAA,EACA,SAAUS,EAAK,SACf,QAASA,EAAK,QACd,SAAUA,EAAK,UAAY,CAAC,EAC5B,OAAQ,CAAE,GAAGN,EAAQ,KAAAK,CAAK,EAC1B,UAAWC,EAAK,WAAa,aACzB,IAAI,KAAK,KAAK,IAAI,EAAI,EAAI,GAAK,GAAK,GAAK,GAAI,EAAE,YAAY,EAC3D,MACN,CAAC,EAIC,KAAK,OAAO,EAAI,KAClBL,EAAM,MAAMJ,CAAO,CAEvB","names":["EXTRACTION_MODEL","EXTRACTION_PROMPT","extractMemories","agentId","userMessage","agentResponse","source","store","createProvider","jsonMatch","facts","date","fact"]}