agentix-cli 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-RDVGL2TW.js +2 -0
- package/dist/audit-Z47AQFFD.js +3 -0
- package/dist/audit-Z47AQFFD.js.map +1 -0
- package/dist/board-dashboard-KX2CNOSY.js +7282 -0
- package/dist/board-dashboard-KX2CNOSY.js.map +1 -0
- package/dist/chunk-34NG7UDX.js +11 -0
- package/dist/chunk-34NG7UDX.js.map +1 -0
- package/dist/{chunk-6MKAXEE5.js → chunk-4ITQP3XL.js} +2 -2
- package/dist/{chunk-6MKAXEE5.js.map → chunk-4ITQP3XL.js.map} +1 -1
- package/dist/chunk-53NRX2NA.js +2 -0
- package/dist/chunk-53NRX2NA.js.map +1 -0
- package/dist/chunk-6PPIFDOQ.js +1742 -0
- package/dist/chunk-6PPIFDOQ.js.map +1 -0
- package/dist/chunk-AFRHKA7B.js +2 -0
- package/dist/chunk-AFRHKA7B.js.map +1 -0
- package/dist/chunk-ASSJ4Z6Z.js +164 -0
- package/dist/chunk-ASSJ4Z6Z.js.map +1 -0
- package/dist/chunk-BLUXL6HR.js +854 -0
- package/dist/chunk-BLUXL6HR.js.map +1 -0
- package/dist/chunk-BTAEYL3P.js +72 -0
- package/dist/chunk-BTAEYL3P.js.map +1 -0
- package/dist/chunk-CDNWCYF7.js +2 -0
- package/dist/chunk-CDNWCYF7.js.map +1 -0
- package/dist/chunk-DJEZ2HVU.js +5 -0
- package/dist/chunk-DJEZ2HVU.js.map +1 -0
- package/dist/{chunk-FLOGK2U7.js → chunk-DSCMCVTV.js} +3 -3
- package/dist/chunk-DSCMCVTV.js.map +1 -0
- package/dist/chunk-EW27SCCC.js +2 -0
- package/dist/chunk-EW27SCCC.js.map +1 -0
- package/dist/chunk-HPDHNIA3.js +2 -0
- package/dist/chunk-IM376SD6.js +13 -0
- package/dist/chunk-IM376SD6.js.map +1 -0
- package/dist/chunk-J4WKMKTP.js +5 -0
- package/dist/{chunk-YI2T5QYB.js.map → chunk-J4WKMKTP.js.map} +1 -1
- package/dist/chunk-JGXE2YNV.js +46 -0
- package/dist/chunk-JGXE2YNV.js.map +1 -0
- package/dist/chunk-JXEPJ3YD.js +118 -0
- package/dist/chunk-JXEPJ3YD.js.map +1 -0
- package/dist/chunk-LY7JGDV7.js +6 -0
- package/dist/chunk-LY7JGDV7.js.map +1 -0
- package/dist/chunk-N67JZAXU.js +2 -0
- package/dist/chunk-N67JZAXU.js.map +1 -0
- package/dist/chunk-SR3X25TN.js +3 -0
- package/dist/chunk-SR3X25TN.js.map +1 -0
- package/dist/{chunk-7MBTMCKY.js → chunk-TRQSP67X.js} +2 -2
- package/dist/chunk-U364GNQX.js +719 -0
- package/dist/chunk-U364GNQX.js.map +1 -0
- package/dist/chunk-VJUTQAPJ.js +2 -0
- package/dist/chunk-VJUTQAPJ.js.map +1 -0
- package/dist/{chunk-HI24KAEY.js → chunk-WJXU5F4Z.js} +3 -4
- package/dist/{chunk-HI24KAEY.js.map → chunk-WJXU5F4Z.js.map} +1 -1
- package/dist/{chunk-QZP65I2H.js → chunk-ZE2YFX4K.js} +2 -2
- package/dist/cli.js +1 -320
- package/dist/cli.js.map +1 -1
- package/dist/{compaction-JBZZ7I7Y.js → compaction-S5H6DTOR.js} +6 -6
- package/dist/compaction-S5H6DTOR.js.map +1 -0
- package/dist/config-UOHEG4BW.js +2 -0
- package/dist/config-mutator-KSEBWKJH.js +2 -0
- package/dist/context-planner-2RGHJHSO.js +17 -0
- package/dist/context-planner-2RGHJHSO.js.map +1 -0
- package/dist/debug-TGAP7E7W.js +2 -0
- package/dist/discover-OATU7SHI.js +4 -0
- package/dist/discover-OATU7SHI.js.map +1 -0
- package/dist/editor-chat-FUBQLMAQ.js +137 -0
- package/dist/editor-chat-FUBQLMAQ.js.map +1 -0
- package/dist/heal-YP7QI7D3.js +2 -0
- package/dist/inbox-O5UB3YQN.js +315 -0
- package/dist/inbox-O5UB3YQN.js.map +1 -0
- package/dist/index.d.ts +3390 -1814
- package/dist/index.js +23 -53
- package/dist/index.js.map +1 -1
- package/dist/ingest-whatsapp-C3O5UPBX.js +6 -0
- package/dist/ingest-whatsapp-C3O5UPBX.js.map +1 -0
- package/dist/loader-BGWA7IRY.js +2 -0
- package/dist/loader-LBHKSXBK.js +2 -0
- package/dist/memory-extract-KKPKAOPH.js +2 -0
- package/dist/processes-7W27G3J3.js +256 -0
- package/dist/processes-7W27G3J3.js.map +1 -0
- package/dist/program-2MDELQLX.js +2 -0
- package/dist/providers-WCQSKTNP.js +2 -0
- package/dist/query-FZAL7AQD.js +2 -0
- package/dist/recipes-LZ56TA3X.js +2 -0
- package/dist/registry-ONJSILH4.js +2 -0
- package/dist/scheduler-XRM2RJIV.js +2 -0
- package/dist/slack-D27BE37Q.js +3 -0
- package/dist/slack-D27BE37Q.js.map +1 -0
- package/dist/store-4UCO56K2.js +2 -0
- package/dist/store-4UCO56K2.js.map +1 -0
- package/dist/{subagent-2SJHSSJS.js → subagent-EPOT3QDM.js} +2 -2
- package/dist/task-store-3NUWLYRY.js +2 -0
- package/dist/task-store-3NUWLYRY.js.map +1 -0
- package/dist/telegram-UWV67KWG.js +3 -0
- package/dist/telegram-UWV67KWG.js.map +1 -0
- package/dist/timers-QB4KYNAE.js +2 -0
- package/dist/timers-QB4KYNAE.js.map +1 -0
- package/dist/types-ECMEC5BZ.js +2 -0
- package/dist/types-ECMEC5BZ.js.map +1 -0
- package/dist/types-TTB6DJUE.js +2 -0
- package/dist/types-TTB6DJUE.js.map +1 -0
- package/dist/usage-dashboard-FQGPPABD.js +803 -0
- package/dist/usage-dashboard-FQGPPABD.js.map +1 -0
- package/dist/web/workflow-editor.global.js +1450 -0
- package/dist/web/workflow-editor.global.js.map +1 -0
- package/dist/whatsapp-KJ2NRKKQ.js +3 -0
- package/dist/whatsapp-KJ2NRKKQ.js.map +1 -0
- package/dist/{whatsapp-state-6D6FHHD2.js → whatsapp-state-3ZVQ6MQD.js} +2 -2
- package/dist/{wiki-UDPYFJBL.js → wiki-OUYKM7MB.js} +2 -2
- package/dist/wiki-OUYKM7MB.js.map +1 -0
- package/dist/workspace-setup-6GDBEORT.js +2 -0
- package/dist/workspace-setup-6GDBEORT.js.map +1 -0
- package/package.json +19 -2
- package/dist/agent-BCDPSPKU.js +0 -2
- package/dist/board-dashboard-LA7WW56M.js +0 -5668
- package/dist/board-dashboard-LA7WW56M.js.map +0 -1
- package/dist/chunk-6U2XOQHU.js +0 -7
- package/dist/chunk-6U2XOQHU.js.map +0 -1
- package/dist/chunk-7FJQGD6U.js +0 -6
- package/dist/chunk-7FJQGD6U.js.map +0 -1
- package/dist/chunk-ABDP7TDL.js +0 -71
- package/dist/chunk-ABDP7TDL.js.map +0 -1
- package/dist/chunk-CXY2RYNI.js +0 -11
- package/dist/chunk-CXY2RYNI.js.map +0 -1
- package/dist/chunk-FIOB3MEX.js +0 -2
- package/dist/chunk-FLOGK2U7.js.map +0 -1
- package/dist/chunk-HVC6FJNP.js +0 -108
- package/dist/chunk-HVC6FJNP.js.map +0 -1
- package/dist/chunk-K7BE4E35.js +0 -138
- package/dist/chunk-K7BE4E35.js.map +0 -1
- package/dist/chunk-KXJSLLSQ.js +0 -199
- package/dist/chunk-KXJSLLSQ.js.map +0 -1
- package/dist/chunk-KXZMYLHQ.js +0 -46
- package/dist/chunk-KXZMYLHQ.js.map +0 -1
- package/dist/chunk-M7HKBG3V.js +0 -2
- package/dist/chunk-M7HKBG3V.js.map +0 -1
- package/dist/chunk-MCJUAE6V.js +0 -6
- package/dist/chunk-MCJUAE6V.js.map +0 -1
- package/dist/chunk-RB7265LO.js +0 -12
- package/dist/chunk-RB7265LO.js.map +0 -1
- package/dist/chunk-YI2T5QYB.js +0 -5
- package/dist/compaction-JBZZ7I7Y.js.map +0 -1
- package/dist/config-LN6JEHMF.js +0 -2
- package/dist/config-mutator-24TH7PHM.js +0 -2
- package/dist/debug-3MEUO2P2.js +0 -2
- package/dist/heal-WKY5I4KB.js +0 -2
- package/dist/loader-PTCXLJEX.js +0 -2
- package/dist/memory-extract-AD7KMRPC.js +0 -2
- package/dist/providers-VUIWO3ZK.js +0 -2
- package/dist/query-PKGYE5HR.js +0 -2
- package/dist/registry-ASC2QS5K.js +0 -2
- package/dist/scheduler-A3TQOCKT.js +0 -2
- package/dist/types-OIFU3KSQ.js +0 -2
- package/dist/usage-dashboard-QX3CF6KK.js +0 -404
- package/dist/usage-dashboard-QX3CF6KK.js.map +0 -1
- package/dist/workspace-setup-ZWTT42XT.js +0 -2
- /package/dist/{agent-BCDPSPKU.js.map → agent-RDVGL2TW.js.map} +0 -0
- /package/dist/{chunk-FIOB3MEX.js.map → chunk-HPDHNIA3.js.map} +0 -0
- /package/dist/{chunk-7MBTMCKY.js.map → chunk-TRQSP67X.js.map} +0 -0
- /package/dist/{chunk-QZP65I2H.js.map → chunk-ZE2YFX4K.js.map} +0 -0
- /package/dist/{config-LN6JEHMF.js.map → config-UOHEG4BW.js.map} +0 -0
- /package/dist/{config-mutator-24TH7PHM.js.map → config-mutator-KSEBWKJH.js.map} +0 -0
- /package/dist/{debug-3MEUO2P2.js.map → debug-TGAP7E7W.js.map} +0 -0
- /package/dist/{heal-WKY5I4KB.js.map → heal-YP7QI7D3.js.map} +0 -0
- /package/dist/{loader-PTCXLJEX.js.map → loader-BGWA7IRY.js.map} +0 -0
- /package/dist/{memory-extract-AD7KMRPC.js.map → loader-LBHKSXBK.js.map} +0 -0
- /package/dist/{providers-VUIWO3ZK.js.map → memory-extract-KKPKAOPH.js.map} +0 -0
- /package/dist/{query-PKGYE5HR.js.map → program-2MDELQLX.js.map} +0 -0
- /package/dist/{registry-ASC2QS5K.js.map → providers-WCQSKTNP.js.map} +0 -0
- /package/dist/{scheduler-A3TQOCKT.js.map → query-FZAL7AQD.js.map} +0 -0
- /package/dist/{types-OIFU3KSQ.js.map → recipes-LZ56TA3X.js.map} +0 -0
- /package/dist/{wiki-UDPYFJBL.js.map → registry-ONJSILH4.js.map} +0 -0
- /package/dist/{workspace-setup-ZWTT42XT.js.map → scheduler-XRM2RJIV.js.map} +0 -0
- /package/dist/{subagent-2SJHSSJS.js.map → subagent-EPOT3QDM.js.map} +0 -0
- /package/dist/{whatsapp-state-6D6FHHD2.js.map → whatsapp-state-3ZVQ6MQD.js.map} +0 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import{readFileSync as D,writeFileSync as v,appendFileSync as K,existsSync as O,mkdirSync as j}from"fs";import{resolve as S}from"path";var w={"claude-opus-4-6":{input:5,output:25,cacheRead:.5,cacheCreate:6.25},"claude-opus-4-5":{input:5,output:25,cacheRead:.5,cacheCreate:6.25},"claude-opus":{input:15,output:75,cacheRead:1.5,cacheCreate:18.75},"claude-sonnet":{input:3,output:15,cacheRead:.3,cacheCreate:3.75},"claude-haiku":{input:1,output:5,cacheRead:.1,cacheCreate:1.25}},H=2e5,F=1.5;function U(R){let e=R.toLowerCase();return e.includes("opus")?e.includes("4-6")||e.includes("4.6")||e.includes("opus-4-6")?"claude-opus-4-6":e.includes("4-5")||e.includes("4.5")||e.includes("opus-4-5")?"claude-opus-4-5":"claude-opus":e.includes("haiku")?"claude-haiku":"claude-sonnet"}var $=500,W=".agentx/pricing/custom.json",_=class{dir;cache=null;pricing;constructor(e=S(process.cwd(),".agentx/usage")){this.dir=e,j(this.dir,{recursive:!0}),this.pricing=G()}getPricing(){return this.pricing}static calculateCost(e,o,s=w){let c=U(o||e.model||"claude-sonnet"),a=s[c]||s["claude-sonnet"],i=e.tier2InputTokens||0,k=e.tier2OutputTokens||0,h=e.tier2CacheReadTokens||0,n=e.tier2CacheCreateTokens||0,l=(e.inputTokens||0)/1e6*a.input+(e.outputTokens||0)/1e6*a.output+(e.cacheReadTokens||0)/1e6*a.cacheRead+(e.cacheCreateTokens||0)/1e6*a.cacheCreate,t=i/1e6*a.input+k/1e6*a.output+h/1e6*a.cacheRead+n/1e6*a.cacheCreate;return l+t*F}cost(e,o){return _.calculateCost(e,o,this.pricing)}record(e,o,s,c,a,i,k,h,n){let l=this.today(),t=l.agents[e]||{tasks:0,inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreateTokens:0,tier2InputTokens:0,tier2OutputTokens:0,tier2CacheReadTokens:0,tier2CacheCreateTokens:0,totalDuration:0,errors:0,byChannel:{},sessionKeys:[]};t.tasks++,t.totalDuration+=o,k&&(t.model=k);let g=0,b=0,T=0,C=0,u=0,m=0,r=0,f=0;s?s.inputTokens+s.cacheReadTokens+s.cacheCreateTokens>H?(u=s.inputTokens,m=s.outputTokens,r=s.cacheReadTokens,f=s.cacheCreateTokens):(g=s.inputTokens,b=s.outputTokens,T=s.cacheReadTokens,C=s.cacheCreateTokens):c!==void 0&&a!==void 0&&(g=Math.ceil(c/4),b=Math.ceil(a/4)),t.inputTokens+=g,t.outputTokens+=b,t.cacheReadTokens+=T,t.cacheCreateTokens+=C,t.tier2InputTokens=(t.tier2InputTokens||0)+u,t.tier2OutputTokens=(t.tier2OutputTokens||0)+m,t.tier2CacheReadTokens=(t.tier2CacheReadTokens||0)+r,t.tier2CacheCreateTokens=(t.tier2CacheCreateTokens||0)+f,i&&t.errors++,t.byChannel||(t.byChannel={});let y=(h||"other").toLowerCase(),p=t.byChannel[y]||{tasks:0,inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreateTokens:0,tier2InputTokens:0,tier2OutputTokens:0,tier2CacheReadTokens:0,tier2CacheCreateTokens:0,totalDuration:0,errors:0};p.tasks++,p.totalDuration+=o,p.inputTokens+=g,p.outputTokens+=b,p.cacheReadTokens+=T,p.cacheCreateTokens+=C,p.tier2InputTokens+=u,p.tier2OutputTokens+=m,p.tier2CacheReadTokens+=r,p.tier2CacheCreateTokens+=f,i&&p.errors++,t.byChannel[y]=p,n&&(t.sessionKeys||(t.sessionKeys=[]),t.sessionKeys.includes(n)||(t.sessionKeys.push(n),t.sessionKeys.length>$&&t.sessionKeys.splice(0,t.sessionKeys.length-$))),l.agents[e]=t,this.save(l)}today(){let e=new Date().toISOString().slice(0,10);if(this.cache?.date===e)return this.cache;let o=this.filePath(e);if(O(o))try{return this.cache=JSON.parse(D(o,"utf-8")),this.cache}catch{}return this.cache={date:e,agents:{}},this.cache}getDate(e){let o=this.filePath(e);if(!O(o))return null;try{return JSON.parse(D(o,"utf-8"))}catch{return null}}generateDailyReport(e){let o=e||new Date().toISOString().slice(0,10),s=this.getDate(o);if(!s)return null;let c=0,a=0,i=0,k=0,h=0,n=0,l="",t=0,g={},b={},T={};for(let[C,u]of Object.entries(s.agents)){let m=this.cost(u);g[C]=m,c+=u.tasks,a+=(u.inputTokens||0)+(u.tier2InputTokens||0),i+=(u.outputTokens||0)+(u.tier2OutputTokens||0),k+=(u.cacheReadTokens||0)+(u.tier2CacheReadTokens||0),h+=(u.cacheCreateTokens||0)+(u.tier2CacheCreateTokens||0),n+=m,m>t&&(t=m,l=C);let r=u.sessionKeys?.length||0;b[C]={sessions:r,avgTasksPerSession:r>0?u.tasks/r:u.tasks};for(let[f,y]of Object.entries(u.byChannel||{})){let p=this.cost(y,u.model);T[f]=(T[f]||0)+p}}return{date:o,totalTasks:c,totalInput:a,totalOutput:i,totalCacheRead:k,totalCacheCreate:h,totalCost:n,topAgent:l,topCost:t,agentCosts:g,agentSessionStats:b,byChannel:T}}appendToTokenCosts(e,o){let s=o||S(process.cwd(),".agentx/TOKEN_COSTS.md"),c=n=>n<.01?n.toFixed(4):n.toFixed(2),a=n=>n>=1e6?`${(n/1e6).toFixed(1)}M`:n>=1e3?`${(n/1e3).toFixed(0)}K`:String(n),i=Object.entries(e.agentCosts).sort(([,n],[,l])=>l-n).map(([n,l])=>{let t=e.agentSessionStats[n],g=t&&t.sessions>0?` (${t.sessions}s, ${t.avgTasksPerSession.toFixed(1)}t/s)`:"";return`${n}: $${c(l)}${g}`}).join(", "),k=Object.entries(e.byChannel).sort(([,n],[,l])=>l-n).map(([n,l])=>`${n}: $${c(l)}`).join(", "),h=`| ${e.date} | ${e.totalTasks} | ${a(e.totalInput)} | ${a(e.totalOutput)} | ${a(e.totalCacheRead)} | ${a(e.totalCacheCreate)} | $${c(e.totalCost)} | ${i} | ${k} |`;O(s)?K(s,h+`
|
|
2
|
+
`):v(s,`# Token Costs
|
|
3
|
+
|
|
4
|
+
Daily token cost tracking. Auto-appended at midnight (Africa/Tunis).
|
|
5
|
+
Agent breakdown: \`agent: $cost (Ns, T.Tt/s)\` \u2014 sessions, tasks/session.
|
|
6
|
+
|
|
7
|
+
| Date | Tasks | Input | Output | Cache R | Cache W | Cost | Per Agent | Per Channel |
|
|
8
|
+
|------|-------|-------|--------|---------|---------|------|-----------|-------------|
|
|
9
|
+
`+h+`
|
|
10
|
+
`)}hasTokenCostsEntry(e,o){let s=o||S(process.cwd(),".agentx/TOKEN_COSTS.md");if(!O(s))return!1;try{let c=D(s,"utf-8");return new RegExp(`^\\|\\s*${e}\\s*\\|`,"m").test(c)}catch{return!1}}lastTokenCostsDate(e){let o=e||S(process.cwd(),".agentx/TOKEN_COSTS.md");if(!O(o))return null;try{let c=D(o,"utf-8").match(/^\|\s*(\d{4}-\d{2}-\d{2})\s*\|/gm)??[];if(c.length===0)return null;let a=c.map(i=>i.match(/(\d{4}-\d{2}-\d{2})/)?.[1]).filter(i=>!!i);return a.sort(),a[a.length-1]??null}catch{return null}}catchUpTokenCosts(e,o={}){let s=o.maxDays??30,c=[],a=this.lastTokenCostsDate(o.filePath),i=[],k=new Date(`${e}T00:00:00Z`);for(let h=0;h<s;h++){let n=k.toISOString().slice(0,10);if(a&&n<=a)break;i.push(n),k.setUTCDate(k.getUTCDate()-1)}i.reverse();for(let h of i){if(this.hasTokenCostsEntry(h,o.filePath))continue;let n=this.generateDailyReport(h);!n||n.totalTasks===0||(this.appendToTokenCosts(n,o.filePath),c.push(h))}return c}summary(e=7){let o=0,s=0,c=0,a=0,i=0,k=0,h=0,n={},l={};for(let T=0;T<e;T++){let C=new Date(Date.now()-T*864e5).toISOString().slice(0,10),u=this.getDate(C);if(u)for(let[m,r]of Object.entries(u.agents)){let f=this.cost(r);o+=r.tasks;let y=(r.inputTokens||0)+(r.tier2InputTokens||0),p=(r.outputTokens||0)+(r.tier2OutputTokens||0),I=(r.cacheReadTokens||0)+(r.tier2CacheReadTokens||0),E=(r.cacheCreateTokens||0)+(r.tier2CacheCreateTokens||0);s+=y,c+=p,a+=I,i+=E,k+=r.errors,h+=f;let d=n[m]||{tasks:0,input:0,output:0,cacheRead:0,cacheCreate:0,total:0,avgDuration:0,totalDuration:0,cost:0,sessions:0};d.tasks+=r.tasks,d.input+=y,d.output+=p,d.cacheRead+=I,d.cacheCreate+=E,d.total=d.input+d.output+d.cacheRead+d.cacheCreate,d.totalDuration+=r.totalDuration,d.avgDuration=d.totalDuration/d.tasks,d.cost+=f,d.sessions+=r.sessionKeys?.length||0,r.model&&(d.model=r.model),n[m]=d;for(let[P,x]of Object.entries(r.byChannel||{})){let N=this.cost(x,r.model),A=l[P]||{tasks:0,cost:0};A.tasks+=x.tasks,A.cost+=N,l[P]=A}}}let t=s+c+a+i,g=a+i>0?a/(a+i):0,b={};for(let[T,C]of Object.entries(n))b[T]={sessions:C.sessions,avgTasksPerSession:C.sessions>0?C.tasks/C.sessions:C.tasks};return{totalTasks:o,totalTokens:t,totalInput:s,totalOutput:c,totalCacheRead:a,totalCacheCreate:i,cacheHitRatio:g,totalErrors:k,totalCost:h,byAgent:n,byChannel:l,sessionStats:b}}filePath(e){return S(this.dir,`${e}.json`)}save(e){try{v(this.filePath(e.date),JSON.stringify(e,null,2))}catch{}}};function G(){let R=JSON.parse(JSON.stringify(w));try{let e=S(process.cwd(),W);if(!O(e))return R;let o=JSON.parse(D(e,"utf-8"));for(let[s,c]of Object.entries(o))R[s]||(R[s]={input:0,output:0,cacheRead:0,cacheCreate:0}),Object.assign(R[s],c)}catch{}return R}export{_ as a};
|
|
11
|
+
//# sourceMappingURL=chunk-34NG7UDX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/daemon/token-tracker.ts"],"sourcesContent":["import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync } from \"fs\"\nimport { resolve } from \"path\"\n\n// --- Token usage tracker ---\n// Tracks token usage per agent per day with cache-aware, tier-aware cost\n// calculation. Persisted to .agentx/usage/{YYYY-MM-DD}.json.\n//\n// Design notes:\n// - Pricing is per-model-FAMILY (opus/sonnet/haiku). Exact model name\n// variants map via getModelFamily(). Override the table by dropping\n// .agentx/pricing/custom.json (same shape) — merged over defaults at\n// construction, lets operators adjust without a rebuild.\n// - Anthropic bills 1.5× input/output when a single request's total input\n// (input + cacheCreate + cacheRead) exceeds 200K tokens. We split each\n// recorded task into tier1/tier2 buckets and bill them separately so\n// long-context sessions are not undercounted.\n// - Per-channel breakdowns let operators see \"GitLab drove 70% of today's\n// spend\" — the lever for channel-specific session + context tuning.\n// - Session tracking (unique chatId:channel pairs seen today per agent)\n// gives an avg-tasks-per-session proxy — when it spikes, it's usually\n// retry/clarification chains, a signal the context budget or model\n// capability is under-sized for that traffic.\n\n// Anthropic pricing per million tokens (as of April 2026, 5-min ephemeral).\n// Keys here must match getModelFamily() output.\n//\n// IMPORTANT: Opus 4.6 is 3× cheaper than Opus 4.1. Haiku 4.5 is more expensive\n// than Haiku 3.5. Our getModelFamily() resolves the exact version so the cost\n// report matches what Anthropic actually bills. Source: LiteLLM pricing cache\n// at ~/.cache/codeburn/litellm-pricing.json + codeburn/src/models.ts.\n//\n// Cache multipliers (consistent across all models):\n// cache write (5-min ephemeral) = 1.25 × input\n// cache read = 0.10 × input\nexport const CACHE_AWARE_PRICING: Record<string, { input: number; output: number; cacheRead: number; cacheCreate: number }> = {\n // Opus 4.6 / 4.5 — $5/$25 per MTok\n \"claude-opus-4-6\": { input: 5, output: 25, cacheRead: 0.5, cacheCreate: 6.25 },\n \"claude-opus-4-5\": { input: 5, output: 25, cacheRead: 0.5, cacheCreate: 6.25 },\n // Opus 4.1 and earlier — $15/$75 per MTok (legacy, keep for historical runs)\n \"claude-opus\": { input: 15, output: 75, cacheRead: 1.5, cacheCreate: 18.75 },\n // Sonnet 4.6 / 4.5 — $3/$15 per MTok\n \"claude-sonnet\": { input: 3, output: 15, cacheRead: 0.3, cacheCreate: 3.75 },\n // Haiku 4.5 — $1/$5 per MTok (more expensive than Haiku 3.5)\n \"claude-haiku\": { input: 1, output: 5, cacheRead: 0.1, cacheCreate: 1.25 },\n}\n\n/** Per-request input size (input + cacheCreate + cacheRead) above this\n * threshold triggers Anthropic's extended-context tier: all four rates\n * get multiplied. Applies to Opus 4.x and Sonnet 4.x 1M variants. */\nexport const CONTEXT_TIER_THRESHOLD = 200_000\nexport const TIER2_MULTIPLIER = 1.5\n\nexport function getModelFamily(model: string): string {\n const lower = model.toLowerCase()\n if (lower.includes(\"opus\")) {\n // Opus 4.6 and 4.5 are 3× cheaper than 4.1 — must distinguish.\n if (lower.includes(\"4-6\") || lower.includes(\"4.6\") || lower.includes(\"opus-4-6\")) return \"claude-opus-4-6\"\n if (lower.includes(\"4-5\") || lower.includes(\"4.5\") || lower.includes(\"opus-4-5\")) return \"claude-opus-4-5\"\n return \"claude-opus\" // legacy 4.1 and earlier\n }\n if (lower.includes(\"haiku\")) return \"claude-haiku\"\n return \"claude-sonnet\"\n}\n\nexport interface ChannelUsage {\n tasks: number\n inputTokens: number\n outputTokens: number\n cacheReadTokens: number\n cacheCreateTokens: number\n tier2InputTokens: number\n tier2OutputTokens: number\n tier2CacheReadTokens: number\n tier2CacheCreateTokens: number\n totalDuration: number\n errors: number\n}\n\nexport interface AgentUsage {\n tasks: number\n inputTokens: number\n outputTokens: number\n cacheReadTokens: number\n cacheCreateTokens: number\n /** Tokens from requests whose input crossed CONTEXT_TIER_THRESHOLD (billed at\n * TIER2_MULTIPLIER × normal rate). Kept separate from the tier1 buckets so\n * cost math stays honest when a session accumulates past 200K. */\n tier2InputTokens?: number\n tier2OutputTokens?: number\n tier2CacheReadTokens?: number\n tier2CacheCreateTokens?: number\n totalDuration: number\n errors: number\n model?: string\n /** Per-channel breakdown so we can attribute cost to Telegram vs GitLab vs WhatsApp. */\n byChannel?: Record<string, ChannelUsage>\n /** Unique session identifiers (channel:chatId) this agent participated in\n * today. Used to derive avg tasks/session. Capped at 500 entries to keep\n * the daily JSON small. */\n sessionKeys?: string[]\n}\n\nexport interface DailyUsage {\n date: string\n agents: Record<string, AgentUsage>\n}\n\nexport interface DailyReport {\n date: string\n totalTasks: number\n totalInput: number\n totalOutput: number\n totalCacheRead: number\n totalCacheCreate: number\n totalCost: number\n topAgent: string\n topCost: number\n agentCosts: Record<string, number>\n /** Per-agent avg tasks/session — high values suggest retry chains. */\n agentSessionStats: Record<string, { sessions: number; avgTasksPerSession: number }>\n /** Aggregate cost by channel across all agents. */\n byChannel: Record<string, number>\n}\n\n/** Shape of a per-request usage report coming from the runtime. Keeps the\n * record() signature tidy and lets callers opt into tier2 accounting. */\nexport interface TaskUsage {\n inputTokens: number\n outputTokens: number\n cacheReadTokens: number\n cacheCreateTokens: number\n}\n\nconst SESSION_KEYS_MAX = 500\nconst PRICING_OVERRIDE_PATH = \".agentx/pricing/custom.json\"\n\nexport class TokenTracker {\n private dir: string\n private cache: DailyUsage | null = null\n private pricing: typeof CACHE_AWARE_PRICING\n\n constructor(baseDir: string = resolve(process.cwd(), \".agentx/usage\")) {\n this.dir = baseDir\n mkdirSync(this.dir, { recursive: true })\n this.pricing = loadPricingWithOverrides()\n }\n\n /** Returns the active pricing table (hardcoded defaults with any per-model\n * overrides merged in). Useful for UI / debugging. */\n getPricing(): typeof CACHE_AWARE_PRICING {\n return this.pricing\n }\n\n /**\n * Calculate cost for a slice of usage. Accepts either a full AgentUsage or\n * a narrowed ChannelUsage — both expose the same token buckets we price on.\n * Tier2 tokens are billed at TIER2_MULTIPLIER × the normal rate.\n */\n static calculateCost(usage: AgentUsage | ChannelUsage, model?: string, pricingTable: typeof CACHE_AWARE_PRICING = CACHE_AWARE_PRICING): number {\n const family = getModelFamily(model || (usage as AgentUsage).model || \"claude-sonnet\")\n const p = pricingTable[family] || pricingTable[\"claude-sonnet\"]\n const tier2In = (usage as any).tier2InputTokens || 0\n const tier2Out = (usage as any).tier2OutputTokens || 0\n const tier2CR = (usage as any).tier2CacheReadTokens || 0\n const tier2CW = (usage as any).tier2CacheCreateTokens || 0\n\n const tier1 =\n ((usage.inputTokens || 0) / 1_000_000) * p.input +\n ((usage.outputTokens || 0) / 1_000_000) * p.output +\n ((usage.cacheReadTokens || 0) / 1_000_000) * p.cacheRead +\n ((usage.cacheCreateTokens || 0) / 1_000_000) * p.cacheCreate\n\n const tier2 =\n (tier2In / 1_000_000) * p.input +\n (tier2Out / 1_000_000) * p.output +\n (tier2CR / 1_000_000) * p.cacheRead +\n (tier2CW / 1_000_000) * p.cacheCreate\n\n return tier1 + tier2 * TIER2_MULTIPLIER\n }\n\n /**\n * Instance-level cost calculation — uses the tracker's resolved pricing\n * table so any custom override file is honored. Prefer this over the\n * static variant when available.\n */\n cost(usage: AgentUsage | ChannelUsage, model?: string): number {\n return TokenTracker.calculateCost(usage, model, this.pricing)\n }\n\n /**\n * Record a task execution with real or estimated token counts.\n *\n * `channel` attributes the cost to a surface (telegram/gitlab/whatsapp/cron).\n * `sessionKey` is an opaque \"this chat on this day\" identifier — we use it\n * to count unique sessions so an avg-tasks-per-session metric falls out.\n */\n record(\n agentId: string,\n duration: number,\n realUsage?: TaskUsage,\n messageLength?: number,\n responseLength?: number,\n error?: boolean,\n model?: string,\n channel?: string,\n sessionKey?: string,\n ): void {\n const daily = this.today()\n const agent = daily.agents[agentId] || {\n tasks: 0, inputTokens: 0, outputTokens: 0,\n cacheReadTokens: 0, cacheCreateTokens: 0,\n tier2InputTokens: 0, tier2OutputTokens: 0,\n tier2CacheReadTokens: 0, tier2CacheCreateTokens: 0,\n totalDuration: 0, errors: 0, byChannel: {}, sessionKeys: [],\n }\n\n agent.tasks++\n agent.totalDuration += duration\n if (model) agent.model = model\n\n // Split this request into tier1/tier2 based on its TOTAL input size\n // (input + cacheCreate + cacheRead). Output inherits the tier of its\n // request. Estimated paths (no realUsage) are always tier1 — they're\n // too small to reach the threshold.\n let i1 = 0, o1 = 0, cr1 = 0, cw1 = 0, i2 = 0, o2 = 0, cr2 = 0, cw2 = 0\n if (realUsage) {\n const totalInput = realUsage.inputTokens + realUsage.cacheReadTokens + realUsage.cacheCreateTokens\n if (totalInput > CONTEXT_TIER_THRESHOLD) {\n i2 = realUsage.inputTokens\n o2 = realUsage.outputTokens\n cr2 = realUsage.cacheReadTokens\n cw2 = realUsage.cacheCreateTokens\n } else {\n i1 = realUsage.inputTokens\n o1 = realUsage.outputTokens\n cr1 = realUsage.cacheReadTokens\n cw1 = realUsage.cacheCreateTokens\n }\n } else if (messageLength !== undefined && responseLength !== undefined) {\n i1 = Math.ceil(messageLength / 4)\n o1 = Math.ceil(responseLength / 4)\n }\n\n agent.inputTokens += i1\n agent.outputTokens += o1\n agent.cacheReadTokens += cr1\n agent.cacheCreateTokens += cw1\n agent.tier2InputTokens = (agent.tier2InputTokens || 0) + i2\n agent.tier2OutputTokens = (agent.tier2OutputTokens || 0) + o2\n agent.tier2CacheReadTokens = (agent.tier2CacheReadTokens || 0) + cr2\n agent.tier2CacheCreateTokens = (agent.tier2CacheCreateTokens || 0) + cw2\n\n if (error) agent.errors++\n\n // Per-channel attribution. Unknown channel falls into \"other\".\n if (!agent.byChannel) agent.byChannel = {}\n const chKey = (channel || \"other\").toLowerCase()\n const ch = agent.byChannel[chKey] || {\n tasks: 0, inputTokens: 0, outputTokens: 0,\n cacheReadTokens: 0, cacheCreateTokens: 0,\n tier2InputTokens: 0, tier2OutputTokens: 0,\n tier2CacheReadTokens: 0, tier2CacheCreateTokens: 0,\n totalDuration: 0, errors: 0,\n }\n ch.tasks++\n ch.totalDuration += duration\n ch.inputTokens += i1; ch.outputTokens += o1\n ch.cacheReadTokens += cr1; ch.cacheCreateTokens += cw1\n ch.tier2InputTokens += i2; ch.tier2OutputTokens += o2\n ch.tier2CacheReadTokens += cr2; ch.tier2CacheCreateTokens += cw2\n if (error) ch.errors++\n agent.byChannel[chKey] = ch\n\n // Session tracking — dedup sessions by opaque key (normally channel:chatId).\n if (sessionKey) {\n if (!agent.sessionKeys) agent.sessionKeys = []\n if (!agent.sessionKeys.includes(sessionKey)) {\n agent.sessionKeys.push(sessionKey)\n if (agent.sessionKeys.length > SESSION_KEYS_MAX) {\n agent.sessionKeys.splice(0, agent.sessionKeys.length - SESSION_KEYS_MAX)\n }\n }\n }\n\n daily.agents[agentId] = agent\n this.save(daily)\n }\n\n /**\n * Get today's usage summary.\n */\n today(): DailyUsage {\n const date = new Date().toISOString().slice(0, 10)\n\n if (this.cache?.date === date) return this.cache\n\n const path = this.filePath(date)\n if (existsSync(path)) {\n try {\n this.cache = JSON.parse(readFileSync(path, \"utf-8\"))\n return this.cache!\n } catch {}\n }\n\n this.cache = { date, agents: {} }\n return this.cache\n }\n\n /**\n * Get usage for a specific date.\n */\n getDate(date: string): DailyUsage | null {\n const path = this.filePath(date)\n if (!existsSync(path)) return null\n try {\n return JSON.parse(readFileSync(path, \"utf-8\"))\n } catch {\n return null\n }\n }\n\n /**\n * Generate a daily cost report for a given date. Includes per-agent,\n * per-channel costs and session statistics.\n */\n generateDailyReport(date?: string): DailyReport | null {\n const targetDate = date || new Date().toISOString().slice(0, 10)\n const usage = this.getDate(targetDate)\n if (!usage) return null\n\n let totalTasks = 0, totalInput = 0, totalOutput = 0\n let totalCacheRead = 0, totalCacheCreate = 0, totalCost = 0\n let topAgent = \"\", topCost = 0\n const agentCosts: Record<string, number> = {}\n const agentSessionStats: Record<string, { sessions: number; avgTasksPerSession: number }> = {}\n const byChannel: Record<string, number> = {}\n\n for (const [id, agent] of Object.entries(usage.agents)) {\n const cost = this.cost(agent)\n agentCosts[id] = cost\n totalTasks += agent.tasks\n totalInput += (agent.inputTokens || 0) + (agent.tier2InputTokens || 0)\n totalOutput += (agent.outputTokens || 0) + (agent.tier2OutputTokens || 0)\n totalCacheRead += (agent.cacheReadTokens || 0) + (agent.tier2CacheReadTokens || 0)\n totalCacheCreate += (agent.cacheCreateTokens || 0) + (agent.tier2CacheCreateTokens || 0)\n totalCost += cost\n if (cost > topCost) { topCost = cost; topAgent = id }\n\n const sessions = agent.sessionKeys?.length || 0\n agentSessionStats[id] = {\n sessions,\n avgTasksPerSession: sessions > 0 ? agent.tasks / sessions : agent.tasks,\n }\n\n for (const [chKey, ch] of Object.entries(agent.byChannel || {})) {\n const chCost = this.cost(ch, agent.model)\n byChannel[chKey] = (byChannel[chKey] || 0) + chCost\n }\n }\n\n return {\n date: targetDate, totalTasks, totalInput, totalOutput,\n totalCacheRead, totalCacheCreate, totalCost, topAgent, topCost, agentCosts,\n agentSessionStats, byChannel,\n }\n }\n\n /**\n * Append a daily report row to TOKEN_COSTS.md. Adds channel + session\n * breakdown inline for at-a-glance spend attribution.\n */\n appendToTokenCosts(report: DailyReport, filePath?: string): void {\n const file = filePath || resolve(process.cwd(), \".agentx/TOKEN_COSTS.md\")\n const fmt = (n: number) => n < 0.01 ? n.toFixed(4) : n.toFixed(2)\n const fmtTok = (n: number) =>\n n >= 1_000_000 ? `${(n / 1_000_000).toFixed(1)}M` :\n n >= 1_000 ? `${(n / 1_000).toFixed(0)}K` :\n String(n)\n\n const agentBreakdown = Object.entries(report.agentCosts)\n .sort(([, a], [, b]) => b - a)\n .map(([id, cost]) => {\n const stats = report.agentSessionStats[id]\n const sessTag = stats && stats.sessions > 0 ? ` (${stats.sessions}s, ${stats.avgTasksPerSession.toFixed(1)}t/s)` : \"\"\n return `${id}: $${fmt(cost)}${sessTag}`\n })\n .join(\", \")\n\n const channelBreakdown = Object.entries(report.byChannel)\n .sort(([, a], [, b]) => b - a)\n .map(([ch, cost]) => `${ch}: $${fmt(cost)}`)\n .join(\", \")\n\n const row = `| ${report.date} | ${report.totalTasks} | ${fmtTok(report.totalInput)} | ${fmtTok(report.totalOutput)} | ${fmtTok(report.totalCacheRead)} | ${fmtTok(report.totalCacheCreate)} | $${fmt(report.totalCost)} | ${agentBreakdown} | ${channelBreakdown} |`\n\n if (!existsSync(file)) {\n const header =\n \"# Token Costs\\n\\n\" +\n \"Daily token cost tracking. Auto-appended at midnight (Africa/Tunis).\\n\" +\n \"Agent breakdown: `agent: $cost (Ns, T.Tt/s)` — sessions, tasks/session.\\n\\n\" +\n \"| Date | Tasks | Input | Output | Cache R | Cache W | Cost | Per Agent | Per Channel |\\n\" +\n \"|------|-------|-------|--------|---------|---------|------|-----------|-------------|\\n\"\n writeFileSync(file, header + row + \"\\n\")\n } else {\n appendFileSync(file, row + \"\\n\")\n }\n }\n\n /**\n * Return true if TOKEN_COSTS.md already has a row for `date`. Lets callers\n * skip duplicate appends after daemon restarts or timer-edge double fires.\n */\n hasTokenCostsEntry(date: string, filePath?: string): boolean {\n const file = filePath || resolve(process.cwd(), \".agentx/TOKEN_COSTS.md\")\n if (!existsSync(file)) return false\n try {\n const content = readFileSync(file, \"utf-8\")\n // Rows look like: `| 2026-04-18 | ...`\n return new RegExp(`^\\\\|\\\\s*${date}\\\\s*\\\\|`, \"m\").test(content)\n } catch {\n return false\n }\n }\n\n /**\n * Find the most recent date already logged to TOKEN_COSTS.md (YYYY-MM-DD).\n * Returns null when the file doesn't exist or has no rows.\n */\n lastTokenCostsDate(filePath?: string): string | null {\n const file = filePath || resolve(process.cwd(), \".agentx/TOKEN_COSTS.md\")\n if (!existsSync(file)) return null\n try {\n const content = readFileSync(file, \"utf-8\")\n const dates = content.match(/^\\|\\s*(\\d{4}-\\d{2}-\\d{2})\\s*\\|/gm) ?? []\n if (dates.length === 0) return null\n // Extract the date portion and pick the max — rows can be out of order\n // after a catch-up, so don't rely on file order.\n const parsed = dates\n .map((m) => m.match(/(\\d{4}-\\d{2}-\\d{2})/)?.[1])\n .filter((d): d is string => !!d)\n parsed.sort()\n return parsed[parsed.length - 1] ?? null\n } catch {\n return null\n }\n }\n\n /**\n * Append reports for any days between (lastLogged + 1) and `throughDate`\n * that have usage on disk but no row in TOKEN_COSTS.md. Idempotent —\n * safe to call on every daemon startup. Bounded to `maxDays` to avoid\n * a stalled daemon backfilling a year's worth on first start.\n *\n * Returns the list of dates actually appended, for logging.\n */\n catchUpTokenCosts(\n throughDate: string,\n opts: { maxDays?: number; filePath?: string } = {},\n ): string[] {\n const maxDays = opts.maxDays ?? 30\n const appended: string[] = []\n\n const last = this.lastTokenCostsDate(opts.filePath)\n // Walk backward from throughDate, collecting dates to fill. Stop at the\n // last-logged date (exclusive) or after maxDays, whichever comes first.\n const candidates: string[] = []\n const cursor = new Date(`${throughDate}T00:00:00Z`)\n for (let i = 0; i < maxDays; i++) {\n const date = cursor.toISOString().slice(0, 10)\n if (last && date <= last) break\n candidates.push(date)\n cursor.setUTCDate(cursor.getUTCDate() - 1)\n }\n // Oldest-first so the file stays chronological.\n candidates.reverse()\n\n for (const date of candidates) {\n if (this.hasTokenCostsEntry(date, opts.filePath)) continue\n const report = this.generateDailyReport(date)\n if (!report || report.totalTasks === 0) continue\n this.appendToTokenCosts(report, opts.filePath)\n appended.push(date)\n }\n return appended\n }\n\n /**\n * Get summary across last N days.\n */\n summary(days: number = 7): {\n totalTasks: number\n totalTokens: number\n totalInput: number\n totalOutput: number\n totalCacheRead: number\n totalCacheCreate: number\n cacheHitRatio: number\n totalErrors: number\n totalCost: number\n byAgent: Record<string, { tasks: number; input: number; output: number; cacheRead: number; cacheCreate: number; total: number; avgDuration: number; cost: number }>\n byChannel: Record<string, { tasks: number; cost: number }>\n sessionStats: Record<string, { sessions: number; avgTasksPerSession: number }>\n } {\n let totalTasks = 0\n let totalInput = 0\n let totalOutput = 0\n let totalCacheRead = 0\n let totalCacheCreate = 0\n let totalErrors = 0\n let totalCost = 0\n const byAgent: Record<string, { tasks: number; input: number; output: number; cacheRead: number; cacheCreate: number; total: number; avgDuration: number; totalDuration: number; cost: number; model?: string; sessions: number }> = {}\n const byChannel: Record<string, { tasks: number; cost: number }> = {}\n\n for (let i = 0; i < days; i++) {\n const date = new Date(Date.now() - i * 86400_000).toISOString().slice(0, 10)\n const usage = this.getDate(date)\n if (!usage) continue\n\n for (const [id, agent] of Object.entries(usage.agents)) {\n const cost = this.cost(agent)\n totalTasks += agent.tasks\n const agentInput = (agent.inputTokens || 0) + (agent.tier2InputTokens || 0)\n const agentOutput = (agent.outputTokens || 0) + (agent.tier2OutputTokens || 0)\n const agentCR = (agent.cacheReadTokens || 0) + (agent.tier2CacheReadTokens || 0)\n const agentCW = (agent.cacheCreateTokens || 0) + (agent.tier2CacheCreateTokens || 0)\n totalInput += agentInput\n totalOutput += agentOutput\n totalCacheRead += agentCR\n totalCacheCreate += agentCW\n totalErrors += agent.errors\n totalCost += cost\n\n const existing = byAgent[id] || { tasks: 0, input: 0, output: 0, cacheRead: 0, cacheCreate: 0, total: 0, avgDuration: 0, totalDuration: 0, cost: 0, sessions: 0 }\n existing.tasks += agent.tasks\n existing.input += agentInput\n existing.output += agentOutput\n existing.cacheRead += agentCR\n existing.cacheCreate += agentCW\n existing.total = existing.input + existing.output + existing.cacheRead + existing.cacheCreate\n existing.totalDuration += agent.totalDuration\n existing.avgDuration = existing.totalDuration / existing.tasks\n existing.cost += cost\n existing.sessions += agent.sessionKeys?.length || 0\n if (agent.model) existing.model = agent.model\n byAgent[id] = existing\n\n for (const [chKey, ch] of Object.entries(agent.byChannel || {})) {\n const chCost = this.cost(ch, agent.model)\n const acc = byChannel[chKey] || { tasks: 0, cost: 0 }\n acc.tasks += ch.tasks\n acc.cost += chCost\n byChannel[chKey] = acc\n }\n }\n }\n\n const totalTokens = totalInput + totalOutput + totalCacheRead + totalCacheCreate\n const cacheHitRatio = (totalCacheRead + totalCacheCreate) > 0\n ? totalCacheRead / (totalCacheRead + totalCacheCreate)\n : 0\n\n const sessionStats: Record<string, { sessions: number; avgTasksPerSession: number }> = {}\n for (const [id, a] of Object.entries(byAgent)) {\n sessionStats[id] = {\n sessions: a.sessions,\n avgTasksPerSession: a.sessions > 0 ? a.tasks / a.sessions : a.tasks,\n }\n }\n\n return { totalTasks, totalTokens, totalInput, totalOutput, totalCacheRead, totalCacheCreate, cacheHitRatio, totalErrors, totalCost, byAgent, byChannel, sessionStats }\n }\n\n private filePath(date: string): string {\n return resolve(this.dir, `${date}.json`)\n }\n\n private save(usage: DailyUsage): void {\n try {\n writeFileSync(this.filePath(usage.date), JSON.stringify(usage, null, 2))\n } catch {}\n }\n}\n\n/**\n * Load CACHE_AWARE_PRICING and merge any operator-supplied override. The\n * override file is optional; operators tweak rates (e.g. when Anthropic\n * publishes a price change) without rebuilding the daemon.\n */\nfunction loadPricingWithOverrides(): typeof CACHE_AWARE_PRICING {\n const result = JSON.parse(JSON.stringify(CACHE_AWARE_PRICING))\n try {\n const p = resolve(process.cwd(), PRICING_OVERRIDE_PATH)\n if (!existsSync(p)) return result\n const raw = JSON.parse(readFileSync(p, \"utf-8\"))\n for (const [family, rates] of Object.entries(raw)) {\n if (!result[family]) result[family] = { input: 0, output: 0, cacheRead: 0, cacheCreate: 0 }\n Object.assign(result[family], rates as object)\n }\n } catch {\n // Malformed override file — stay with defaults rather than crash at boot.\n }\n return result\n}\n"],"mappings":"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","names":["readFileSync","writeFileSync","appendFileSync","existsSync","mkdirSync","resolve","CACHE_AWARE_PRICING","CONTEXT_TIER_THRESHOLD","TIER2_MULTIPLIER","getModelFamily","model","lower","SESSION_KEYS_MAX","PRICING_OVERRIDE_PATH","TokenTracker","baseDir","loadPricingWithOverrides","usage","pricingTable","family","p","tier2In","tier2Out","tier2CR","tier2CW","tier1","tier2","agentId","duration","realUsage","messageLength","responseLength","error","channel","sessionKey","daily","agent","i1","o1","cr1","cw1","i2","o2","cr2","cw2","chKey","ch","date","path","targetDate","totalTasks","totalInput","totalOutput","totalCacheRead","totalCacheCreate","totalCost","topAgent","topCost","agentCosts","agentSessionStats","byChannel","id","cost","sessions","chCost","report","filePath","file","fmt","fmtTok","agentBreakdown","a","b","stats","sessTag","channelBreakdown","row","content","dates","parsed","m","d","throughDate","opts","maxDays","appended","last","candidates","cursor","i","days","totalErrors","byAgent","agentInput","agentOutput","agentCR","agentCW","existing","acc","totalTokens","cacheHitRatio","sessionStats","result","raw","rates"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{a as w,b as y,c as x}from"./chunk-SFQUP3BP.js";import{g}from"./chunk-
|
|
1
|
+
import{a as w,b as y,c as x}from"./chunk-SFQUP3BP.js";import{l as g}from"./chunk-JGXE2YNV.js";import{existsSync as d,promises as h}from"fs";import c from"path";import{execa as C}from"execa";var b={enabled:!0,maxAttempts:3,provider:"claude-code"},v=class{constructor(e,r,i){this.cwd=e;this.config={...b,...r},this.memory=i||new x(e)}config;memory;async detectAndHeal(e,r,i){if(!this.config.enabled)return{healed:!1,attempts:0,error:"Heal disabled",filesChanged:[]};let t=await this.runVerification();if(!t)return{healed:!0,attempts:0,error:"",filesChanged:[]};let n=0,s=t,a=[];for(;n<this.config.maxAttempts;){n++;let l=await this.generateFix(s,e,r,n);if(!l.files.length)break;for(let o of l.files){let m=c.isAbsolute(o.path)?o.path:c.resolve(this.cwd,o.path),p=c.dirname(m);await h.mkdir(p,{recursive:!0}),await h.writeFile(m,o.content,"utf8"),a.push(o.path)}let f=await this.runVerification();if(!f)return i&&await this.memory.recordHeal(i,t,!0,a),{healed:!0,attempts:n,error:t,fix:l.explanation,filesChanged:a};s=f}return i&&await this.memory.recordHeal(i,t,!1,a),{healed:!1,attempts:n,error:s,filesChanged:a}}async runVerification(){let e=[this.config.buildCommand,this.config.lintCommand,this.config.testCommand].filter(Boolean);if(!e.length){let r=await this.detectCommands();e.push(...r)}for(let r of e)try{let[i,...t]=r.split(" ");await C(i,t,{cwd:this.cwd,timeout:6e4,reject:!0})}catch(i){let t=i.stderr||"",n=i.stdout||"",s=i.message||"";return k(`Command failed: ${r}
|
|
2
2
|
${t}
|
|
3
3
|
${n}
|
|
4
4
|
${s}`)}return null}async detectCommands(){let e=[],r=c.resolve(this.cwd,"package.json");if(d(r))try{let t=JSON.parse(await h.readFile(r,"utf8")).scripts||{};t.build&&e.push("npm run build"),t.typecheck&&e.push("npm run typecheck"),t.lint&&e.push("npm run lint"),t.test&&e.push("npm run test")}catch{}return d(c.resolve(this.cwd,"pyproject.toml"))&&d(c.resolve(this.cwd,"conftest.py"))&&e.push("python -m pytest --tb=short -q"),e}async generateFix(e,r,i,t){let n=g(this.config.provider,this.config.apiKey),s=[];for(let o of r.slice(0,5)){let m=c.resolve(this.cwd,o);if(d(m)){let p=await h.readFile(m,"utf8");s.push(`## ${o}
|
|
@@ -29,4 +29,4 @@ Fix the error. Use create_files with the corrected file content.`}],f=await n.ge
|
|
|
29
29
|
`)+`
|
|
30
30
|
... (truncated)`:e.join(`
|
|
31
31
|
`)}export{v as a};
|
|
32
|
-
//# sourceMappingURL=chunk-
|
|
32
|
+
//# sourceMappingURL=chunk-4ITQP3XL.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runtime/heal.ts"],"sourcesContent":["import { existsSync, promises as fs } from \"fs\"\nimport path from \"path\"\nimport { execa } from \"execa\"\nimport { createProvider, type ProviderName } from \"@/agent/providers\"\nimport type { GenerationMessage, GeneratedFile } from \"@/agent/providers/types\"\nimport { detectTechStack, formatTechStack } from \"@/agent/context/tech-stack\"\nimport { Memory } from \"./memory\"\n\n// --- Auto-Heal Engine: detect errors, diagnose, fix, verify ---\n// When generated code fails (build error, test failure, runtime crash),\n// the heal engine reads the error, understands the context, generates\n// a fix, and re-verifies it works.\n\nexport interface HealConfig {\n enabled: boolean\n testCommand?: string\n buildCommand?: string\n lintCommand?: string\n maxAttempts: number\n provider: ProviderName\n model?: string\n apiKey?: string\n}\n\nexport interface HealResult {\n healed: boolean\n attempts: number\n error: string\n fix?: string\n filesChanged: string[]\n}\n\nconst DEFAULT_HEAL_CONFIG: HealConfig = {\n enabled: true,\n maxAttempts: 3,\n provider: \"claude-code\",\n}\n\nexport class HealEngine {\n private config: HealConfig\n private memory: Memory\n\n constructor(\n private cwd: string,\n config?: Partial<HealConfig>,\n memory?: Memory\n ) {\n this.config = { ...DEFAULT_HEAL_CONFIG, ...config }\n this.memory = memory || new Memory(cwd)\n }\n\n async detectAndHeal(\n generatedFiles: string[],\n originalTask: string,\n entryId?: string\n ): Promise<HealResult> {\n if (!this.config.enabled) {\n return { healed: false, attempts: 0, error: \"Heal disabled\", filesChanged: [] }\n }\n\n // 1. Run verification commands\n const error = await this.runVerification()\n if (!error) {\n return { healed: true, attempts: 0, error: \"\", filesChanged: [] }\n }\n\n // 2. Attempt to heal\n let attempts = 0\n let currentError = error\n const allChangedFiles: string[] = []\n\n while (attempts < this.config.maxAttempts) {\n attempts++\n\n const fix = await this.generateFix(\n currentError,\n generatedFiles,\n originalTask,\n attempts\n )\n\n if (!fix.files.length) {\n break // Agent couldn't generate a fix\n }\n\n // Apply the fix\n for (const file of fix.files) {\n const filePath = path.isAbsolute(file.path)\n ? file.path\n : path.resolve(this.cwd, file.path)\n\n const dir = path.dirname(filePath)\n await fs.mkdir(dir, { recursive: true })\n await fs.writeFile(filePath, file.content, \"utf8\")\n allChangedFiles.push(file.path)\n }\n\n // Re-verify\n const newError = await this.runVerification()\n if (!newError) {\n // Healed successfully\n if (entryId) {\n await this.memory.recordHeal(entryId, error, true, allChangedFiles)\n }\n return {\n healed: true,\n attempts,\n error,\n fix: fix.explanation,\n filesChanged: allChangedFiles,\n }\n }\n\n currentError = newError\n }\n\n // Failed to heal\n if (entryId) {\n await this.memory.recordHeal(entryId, error, false, allChangedFiles)\n }\n\n return {\n healed: false,\n attempts,\n error: currentError,\n filesChanged: allChangedFiles,\n }\n }\n\n private async runVerification(): Promise<string | null> {\n const commands = [\n this.config.buildCommand,\n this.config.lintCommand,\n this.config.testCommand,\n ].filter(Boolean) as string[]\n\n // Auto-detect commands if not configured\n if (!commands.length) {\n const detected = await this.detectCommands()\n commands.push(...detected)\n }\n\n for (const cmd of commands) {\n try {\n const [bin, ...args] = cmd.split(\" \")\n await execa(bin, args, {\n cwd: this.cwd,\n timeout: 60000,\n reject: true,\n })\n } catch (error: any) {\n const stderr = error.stderr || \"\"\n const stdout = error.stdout || \"\"\n const message = error.message || \"\"\n return truncateError(`Command failed: ${cmd}\\n${stderr}\\n${stdout}\\n${message}`)\n }\n }\n\n return null // All passed\n }\n\n private async detectCommands(): Promise<string[]> {\n const commands: string[] = []\n const pkgPath = path.resolve(this.cwd, \"package.json\")\n\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(await fs.readFile(pkgPath, \"utf8\"))\n const scripts = pkg.scripts || {}\n if (scripts.build) commands.push(\"npm run build\")\n if (scripts.typecheck) commands.push(\"npm run typecheck\")\n if (scripts.lint) commands.push(\"npm run lint\")\n if (scripts.test) commands.push(\"npm run test\")\n } catch {\n // Skip if can't read package.json\n }\n }\n\n // Python projects\n if (existsSync(path.resolve(this.cwd, \"pyproject.toml\"))) {\n if (existsSync(path.resolve(this.cwd, \"conftest.py\"))) {\n commands.push(\"python -m pytest --tb=short -q\")\n }\n }\n\n return commands\n }\n\n private async generateFix(\n error: string,\n affectedFiles: string[],\n originalTask: string,\n attempt: number\n ): Promise<{ files: GeneratedFile[]; explanation: string }> {\n const provider = createProvider(this.config.provider, this.config.apiKey)\n\n // Read affected files\n const fileContents: string[] = []\n for (const file of affectedFiles.slice(0, 5)) {\n const filePath = path.resolve(this.cwd, file)\n if (existsSync(filePath)) {\n const content = await fs.readFile(filePath, \"utf8\")\n fileContents.push(`## ${file}\\n\\`\\`\\`\\n${content.slice(0, 3000)}\\n\\`\\`\\``)\n }\n }\n\n const techStack = await detectTechStack(this.cwd)\n\n const messages: GenerationMessage[] = [\n {\n role: \"system\",\n content: `You are agentx auto-heal. You fix errors in generated code.\n\nProject tech stack: ${formatTechStack(techStack)}\n\nRULES:\n- Fix ONLY the error — do not refactor or add features\n- Output the COMPLETE fixed file content via create_files\n- Be minimal — smallest change that fixes the error\n- This is attempt ${attempt} — ${attempt > 1 ? \"previous fix attempts failed, try a different approach\" : \"analyze carefully\"}`,\n },\n {\n role: \"user\",\n content: `The following code was generated for task: \"${originalTask}\"\n\n## Error\n\\`\\`\\`\n${error}\n\\`\\`\\`\n\n## Affected Files\n${fileContents.join(\"\\n\\n\")}\n\nFix the error. Use create_files with the corrected file content.`,\n },\n ]\n\n const result = await provider.generate(messages, {\n model: this.config.model,\n maxTokens: 8192,\n })\n\n return {\n files: result.files,\n explanation: result.content,\n }\n }\n}\n\nfunction truncateError(error: string): string {\n const lines = error.split(\"\\n\").filter((l) => l.trim())\n if (lines.length > 30) {\n return lines.slice(0, 30).join(\"\\n\") + \"\\n... (truncated)\"\n }\n return lines.join(\"\\n\")\n}\n"],"mappings":"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","names":["existsSync","fs","path","execa","DEFAULT_HEAL_CONFIG","HealEngine","cwd","config","memory","Memory","generatedFiles","originalTask","entryId","error","attempts","currentError","allChangedFiles","fix","file","filePath","path","dir","fs","newError","commands","detected","cmd","bin","args","execa","stderr","stdout","message","truncateError","pkgPath","existsSync","scripts","affectedFiles","attempt","provider","createProvider","fileContents","content","techStack","detectTechStack","messages","formatTechStack","result","lines","l"]}
|
|
1
|
+
{"version":3,"sources":["../src/runtime/heal.ts"],"sourcesContent":["import { existsSync, promises as fs } from \"fs\"\nimport path from \"path\"\nimport { execa } from \"execa\"\nimport { createProvider, type ProviderName } from \"@/agent/providers\"\nimport type { GenerationMessage, GeneratedFile } from \"@/agent/providers/types\"\nimport { detectTechStack, formatTechStack } from \"@/agent/context/tech-stack\"\nimport { Memory } from \"./memory\"\n\n// --- Auto-Heal Engine: detect errors, diagnose, fix, verify ---\n// When generated code fails (build error, test failure, runtime crash),\n// the heal engine reads the error, understands the context, generates\n// a fix, and re-verifies it works.\n\nexport interface HealConfig {\n enabled: boolean\n testCommand?: string\n buildCommand?: string\n lintCommand?: string\n maxAttempts: number\n provider: ProviderName\n model?: string\n apiKey?: string\n}\n\nexport interface HealResult {\n healed: boolean\n attempts: number\n error: string\n fix?: string\n filesChanged: string[]\n}\n\nconst DEFAULT_HEAL_CONFIG: HealConfig = {\n enabled: true,\n maxAttempts: 3,\n provider: \"claude-code\",\n}\n\nexport class HealEngine {\n private config: HealConfig\n private memory: Memory\n\n constructor(\n private cwd: string,\n config?: Partial<HealConfig>,\n memory?: Memory\n ) {\n this.config = { ...DEFAULT_HEAL_CONFIG, ...config }\n this.memory = memory || new Memory(cwd)\n }\n\n async detectAndHeal(\n generatedFiles: string[],\n originalTask: string,\n entryId?: string\n ): Promise<HealResult> {\n if (!this.config.enabled) {\n return { healed: false, attempts: 0, error: \"Heal disabled\", filesChanged: [] }\n }\n\n // 1. Run verification commands\n const error = await this.runVerification()\n if (!error) {\n return { healed: true, attempts: 0, error: \"\", filesChanged: [] }\n }\n\n // 2. Attempt to heal\n let attempts = 0\n let currentError = error\n const allChangedFiles: string[] = []\n\n while (attempts < this.config.maxAttempts) {\n attempts++\n\n const fix = await this.generateFix(\n currentError,\n generatedFiles,\n originalTask,\n attempts\n )\n\n if (!fix.files.length) {\n break // Agent couldn't generate a fix\n }\n\n // Apply the fix\n for (const file of fix.files) {\n const filePath = path.isAbsolute(file.path)\n ? file.path\n : path.resolve(this.cwd, file.path)\n\n const dir = path.dirname(filePath)\n await fs.mkdir(dir, { recursive: true })\n await fs.writeFile(filePath, file.content, \"utf8\")\n allChangedFiles.push(file.path)\n }\n\n // Re-verify\n const newError = await this.runVerification()\n if (!newError) {\n // Healed successfully\n if (entryId) {\n await this.memory.recordHeal(entryId, error, true, allChangedFiles)\n }\n return {\n healed: true,\n attempts,\n error,\n fix: fix.explanation,\n filesChanged: allChangedFiles,\n }\n }\n\n currentError = newError\n }\n\n // Failed to heal\n if (entryId) {\n await this.memory.recordHeal(entryId, error, false, allChangedFiles)\n }\n\n return {\n healed: false,\n attempts,\n error: currentError,\n filesChanged: allChangedFiles,\n }\n }\n\n private async runVerification(): Promise<string | null> {\n const commands = [\n this.config.buildCommand,\n this.config.lintCommand,\n this.config.testCommand,\n ].filter(Boolean) as string[]\n\n // Auto-detect commands if not configured\n if (!commands.length) {\n const detected = await this.detectCommands()\n commands.push(...detected)\n }\n\n for (const cmd of commands) {\n try {\n const [bin, ...args] = cmd.split(\" \")\n await execa(bin, args, {\n cwd: this.cwd,\n timeout: 60000,\n reject: true,\n })\n } catch (error: any) {\n const stderr = error.stderr || \"\"\n const stdout = error.stdout || \"\"\n const message = error.message || \"\"\n return truncateError(`Command failed: ${cmd}\\n${stderr}\\n${stdout}\\n${message}`)\n }\n }\n\n return null // All passed\n }\n\n private async detectCommands(): Promise<string[]> {\n const commands: string[] = []\n const pkgPath = path.resolve(this.cwd, \"package.json\")\n\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(await fs.readFile(pkgPath, \"utf8\"))\n const scripts = pkg.scripts || {}\n if (scripts.build) commands.push(\"npm run build\")\n if (scripts.typecheck) commands.push(\"npm run typecheck\")\n if (scripts.lint) commands.push(\"npm run lint\")\n if (scripts.test) commands.push(\"npm run test\")\n } catch {\n // Skip if can't read package.json\n }\n }\n\n // Python projects\n if (existsSync(path.resolve(this.cwd, \"pyproject.toml\"))) {\n if (existsSync(path.resolve(this.cwd, \"conftest.py\"))) {\n commands.push(\"python -m pytest --tb=short -q\")\n }\n }\n\n return commands\n }\n\n private async generateFix(\n error: string,\n affectedFiles: string[],\n originalTask: string,\n attempt: number\n ): Promise<{ files: GeneratedFile[]; explanation: string }> {\n const provider = createProvider(this.config.provider, this.config.apiKey)\n\n // Read affected files\n const fileContents: string[] = []\n for (const file of affectedFiles.slice(0, 5)) {\n const filePath = path.resolve(this.cwd, file)\n if (existsSync(filePath)) {\n const content = await fs.readFile(filePath, \"utf8\")\n fileContents.push(`## ${file}\\n\\`\\`\\`\\n${content.slice(0, 3000)}\\n\\`\\`\\``)\n }\n }\n\n const techStack = await detectTechStack(this.cwd)\n\n const messages: GenerationMessage[] = [\n {\n role: \"system\",\n content: `You are agentx auto-heal. You fix errors in generated code.\n\nProject tech stack: ${formatTechStack(techStack)}\n\nRULES:\n- Fix ONLY the error — do not refactor or add features\n- Output the COMPLETE fixed file content via create_files\n- Be minimal — smallest change that fixes the error\n- This is attempt ${attempt} — ${attempt > 1 ? \"previous fix attempts failed, try a different approach\" : \"analyze carefully\"}`,\n },\n {\n role: \"user\",\n content: `The following code was generated for task: \"${originalTask}\"\n\n## Error\n\\`\\`\\`\n${error}\n\\`\\`\\`\n\n## Affected Files\n${fileContents.join(\"\\n\\n\")}\n\nFix the error. Use create_files with the corrected file content.`,\n },\n ]\n\n const result = await provider.generate(messages, {\n model: this.config.model,\n maxTokens: 8192,\n })\n\n return {\n files: result.files,\n explanation: result.content,\n }\n }\n}\n\nfunction truncateError(error: string): string {\n const lines = error.split(\"\\n\").filter((l) => l.trim())\n if (lines.length > 30) {\n return lines.slice(0, 30).join(\"\\n\") + \"\\n... (truncated)\"\n }\n return lines.join(\"\\n\")\n}\n"],"mappings":"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","names":["existsSync","fs","path","execa","DEFAULT_HEAL_CONFIG","HealEngine","cwd","config","memory","Memory","generatedFiles","originalTask","entryId","error","attempts","currentError","allChangedFiles","fix","file","filePath","path","dir","fs","newError","commands","detected","cmd","bin","args","execa","stderr","stdout","message","truncateError","pkgPath","existsSync","scripts","affectedFiles","attempt","provider","createProvider","fileContents","content","techStack","detectTechStack","messages","formatTechStack","result","lines","l"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{a as u}from"./chunk-VJUTQAPJ.js";import{existsSync as R,promises as y}from"fs";import m from"path";import x from"js-yaml";import I from"fast-glob";import{z as e}from"zod";var w=e.object({id:e.string(),priority:e.number().default(0),when:e.object({agentIds:e.array(e.string()).optional(),intentTags:e.array(e.string()).optional(),messageRegex:e.array(e.string()).optional(),requireTags:e.array(e.string()).optional()}).default({}),references:e.array(e.string()).default([]),skills:e.array(e.string()).default([]),maxChars:e.number().int().min(100).max(8e3).optional()}),C=e.object({recipes:e.array(w)}),v=["references/recipes",".references/recipes",".agentx/references/recipes",".agentx/recipes"];async function $(o){let t={recipes:[],sourceById:new Map};for(let r of v){let s=m.resolve(o,r);if(!R(s))continue;let n=await I.glob("**/*.{yaml,yml}",{cwd:s,deep:5});for(let d of n){let a=m.resolve(s,d);try{let f=await y.readFile(a,"utf8"),i=x.load(f),c=C.parse(i);for(let p of c.recipes){if(t.sourceById.has(p.id))throw new Error(`recipe id "${p.id}" duplicated in ${a}`);t.recipes.push(p),t.sourceById.set(p.id,a)}}catch(f){u.warn({file:a,error:f?.message},"recipes: failed to load file")}}}return t}function z(o,t,r){let s=[];for(let i of t.recipes)S(i,o)&&s.push(i);s.sort((i,c)=>c.priority-i.priority||i.id.localeCompare(c.id));let n=new Set,d=[],a=new Set,f=[];for(let i of s){for(let c of i.references){let p=b(c,r);p.length===0&&f.push(c);for(let l of p){if(n.has(l))continue;let g=r.byId.get(l);if(!g){f.push(l);continue}i.when.requireTags?.length&&!i.when.requireTags.every(h=>g.tags.includes(h))||(n.add(l),d.push(g))}}for(let c of i.skills)a.add(c)}return{matched:s,cards:d,requiredSkills:[...a],unresolvedIds:[...new Set(f)]}}function S(o,t){let r=o.when;if(r.agentIds?.length&&!r.agentIds.includes(t.agentId))return!1;if(r.intentTags?.length){let s=t.intentTags??[];if(!r.intentTags.some(d=>s.includes(d)))return!1}return!(r.messageRegex?.length&&!r.messageRegex.some(n=>{try{return new RegExp(n,"i").test(t.message)}catch{return t.message.toLowerCase().includes(n.toLowerCase())}}))}function b(o,t){if(!o.endsWith(".*"))return[o];let r=o.slice(0,-2),s=[];for(let n of t.byId.keys())(n===r||n.startsWith(`${r}.`))&&s.push(n);return s}export{$ as a,z as b};
|
|
2
|
+
//# sourceMappingURL=chunk-53NRX2NA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/agents/references/recipes.ts"],"sourcesContent":["import { existsSync, promises as fs } from \"fs\"\nimport path from \"path\"\nimport yaml from \"js-yaml\"\nimport fg from \"fast-glob\"\nimport { z } from \"zod\"\nimport { logger } from \"@/utils/logger\"\nimport type { ReferenceCard, ReferenceIndex } from \"./types\"\n\n// --- Recipes ---\n//\n// Map (agentId, optional intent tag, optional message keyword) to a bundle of\n// reference card ids and skill names. Pure deterministic lookup — no LLM in\n// the hot path. Multiple recipes can match a single turn; the resolver merges\n// their reference sets and de-duplicates.\n\nconst recipeSchema = z.object({\n id: z.string(),\n /** Higher = picked first when budgets bite. Default 0. */\n priority: z.number().default(0),\n when: z\n .object({\n agentIds: z.array(z.string()).optional(),\n intentTags: z.array(z.string()).optional(),\n /** OR'd, case-insensitive regex sources. */\n messageRegex: z.array(z.string()).optional(),\n /** Tags to require on the card pool. Cards must carry every tag. */\n requireTags: z.array(z.string()).optional(),\n })\n .default({}),\n /** Card ids OR \".*\" glob suffixes — \"ksi.contacts.*\" matches all under that prefix. */\n references: z.array(z.string()).default([]),\n /** Skill names that should be considered required for this intent (audit lint\n * flags FAILING when a referenced skill has no SKILL.md). */\n skills: z.array(z.string()).default([]),\n /** Per-recipe char budget. Total is enforced by the layer; this is advisory. */\n maxChars: z.number().int().min(100).max(8000).optional(),\n})\nexport type Recipe = z.infer<typeof recipeSchema>\n\nconst recipeFileSchema = z.object({\n recipes: z.array(recipeSchema),\n})\n\nexport interface RecipeIndex {\n recipes: Recipe[]\n /** Source path per recipe id. */\n sourceById: Map<string, string>\n}\n\nconst RECIPE_DIRS = [\n \"references/recipes\",\n \".references/recipes\",\n \".agentx/references/recipes\",\n \".agentx/recipes\",\n]\n\nexport async function loadRecipes(cwd: string): Promise<RecipeIndex> {\n const index: RecipeIndex = { recipes: [], sourceById: new Map() }\n for (const dir of RECIPE_DIRS) {\n const abs = path.resolve(cwd, dir)\n if (!existsSync(abs)) continue\n const files = await fg.glob(\"**/*.{yaml,yml}\", { cwd: abs, deep: 5 })\n for (const rel of files) {\n const file = path.resolve(abs, rel)\n try {\n const raw = await fs.readFile(file, \"utf8\")\n const parsed = yaml.load(raw)\n const validated = recipeFileSchema.parse(parsed)\n for (const recipe of validated.recipes) {\n if (index.sourceById.has(recipe.id)) {\n throw new Error(`recipe id \"${recipe.id}\" duplicated in ${file}`)\n }\n index.recipes.push(recipe)\n index.sourceById.set(recipe.id, file)\n }\n } catch (e: any) {\n logger.warn({ file, error: e?.message }, \"recipes: failed to load file\")\n }\n }\n }\n return index\n}\n\nexport interface RecipeContext {\n agentId: string\n intentTags?: string[]\n message: string\n}\n\nexport interface ResolvedRecipes {\n matched: Recipe[]\n cards: ReferenceCard[]\n /** Skill names required by the matched recipes. */\n requiredSkills: string[]\n /** Reference ids in the recipes that did NOT resolve — used by audit lint. */\n unresolvedIds: string[]\n}\n\n/**\n * Match recipes deterministically. AND within a `when` clause, OR across\n * recipes. A recipe with no `when` always matches (use sparingly).\n */\nexport function resolveRecipes(\n ctx: RecipeContext,\n recipeIndex: RecipeIndex,\n referenceIndex: ReferenceIndex,\n): ResolvedRecipes {\n const matched: Recipe[] = []\n for (const recipe of recipeIndex.recipes) {\n if (recipeMatches(recipe, ctx)) matched.push(recipe)\n }\n // Highest priority first, then stable by id.\n matched.sort((a, b) => b.priority - a.priority || a.id.localeCompare(b.id))\n\n const seen = new Set<string>()\n const cards: ReferenceCard[] = []\n const skills = new Set<string>()\n const unresolvedIds: string[] = []\n\n for (const recipe of matched) {\n for (const ref of recipe.references) {\n const ids = expandReferenceIds(ref, referenceIndex)\n if (ids.length === 0) unresolvedIds.push(ref)\n for (const id of ids) {\n if (seen.has(id)) continue\n const card = referenceIndex.byId.get(id)\n if (!card) {\n unresolvedIds.push(id)\n continue\n }\n if (recipe.when.requireTags?.length) {\n const has = recipe.when.requireTags.every(t => card.tags.includes(t))\n if (!has) continue\n }\n seen.add(id)\n cards.push(card)\n }\n }\n for (const s of recipe.skills) skills.add(s)\n }\n\n return {\n matched,\n cards,\n requiredSkills: [...skills],\n unresolvedIds: [...new Set(unresolvedIds)],\n }\n}\n\nfunction recipeMatches(recipe: Recipe, ctx: RecipeContext): boolean {\n const w = recipe.when\n if (w.agentIds?.length && !w.agentIds.includes(ctx.agentId)) return false\n if (w.intentTags?.length) {\n const tags = ctx.intentTags ?? []\n const hit = w.intentTags.some(t => tags.includes(t))\n if (!hit) return false\n }\n if (w.messageRegex?.length) {\n const hit = w.messageRegex.some(src => {\n try {\n return new RegExp(src, \"i\").test(ctx.message)\n } catch {\n return ctx.message.toLowerCase().includes(src.toLowerCase())\n }\n })\n if (!hit) return false\n }\n return true\n}\n\n/**\n * Expand a reference id pattern. Plain ids return [id]. A trailing `.*` matches\n * any id with that prefix in the index.\n */\nfunction expandReferenceIds(pattern: string, idx: ReferenceIndex): string[] {\n if (!pattern.endsWith(\".*\")) return [pattern]\n const prefix = pattern.slice(0, -2)\n const out: string[] = []\n for (const id of idx.byId.keys()) {\n if (id === prefix || id.startsWith(`${prefix}.`)) out.push(id)\n }\n return out\n}\n"],"mappings":"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","names":["existsSync","fs","path","yaml","fg","z","recipeSchema","z","recipeFileSchema","RECIPE_DIRS","loadRecipes","cwd","index","dir","abs","path","existsSync","files","fg","rel","file","raw","fs","parsed","yaml","validated","recipe","e","logger","resolveRecipes","ctx","recipeIndex","referenceIndex","matched","recipeMatches","a","b","seen","cards","skills","unresolvedIds","ref","ids","expandReferenceIds","id","card","t","s","w","tags","src","pattern","idx","prefix","out"]}
|