agentix-cli 0.15.0 → 0.17.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/README.md +12 -6
- package/dist/agent-4AOMVXRJ.js +2 -0
- package/dist/board-dashboard-CXIVJKDP.js +7282 -0
- package/dist/board-dashboard-CXIVJKDP.js.map +1 -0
- package/dist/chunk-34NG7UDX.js +11 -0
- package/dist/chunk-34NG7UDX.js.map +1 -0
- package/dist/chunk-4HP6UC2X.js +13 -0
- package/dist/chunk-4HP6UC2X.js.map +1 -0
- package/dist/chunk-5F3J55LG.js +2 -0
- package/dist/chunk-5F3J55LG.js.map +1 -0
- package/dist/chunk-5WNGHDFC.js +164 -0
- package/dist/chunk-5WNGHDFC.js.map +1 -0
- package/dist/{chunk-7MBTMCKY.js → chunk-63UYRJIC.js} +2 -2
- package/dist/chunk-64P4QHSZ.js +2 -0
- package/dist/chunk-64P4QHSZ.js.map +1 -0
- package/dist/chunk-6PPIFDOQ.js +1742 -0
- package/dist/chunk-6PPIFDOQ.js.map +1 -0
- package/dist/chunk-ABCHHNG6.js +625 -0
- package/dist/chunk-ABCHHNG6.js.map +1 -0
- package/dist/chunk-AFRHKA7B.js +2 -0
- package/dist/chunk-AFRHKA7B.js.map +1 -0
- package/dist/chunk-BFZUG66X.js +45 -0
- package/dist/chunk-BFZUG66X.js.map +1 -0
- package/dist/chunk-CDNWCYF7.js +2 -0
- package/dist/chunk-CDNWCYF7.js.map +1 -0
- package/dist/{chunk-6MKAXEE5.js → chunk-EKTPCSN4.js} +2 -2
- package/dist/{chunk-6MKAXEE5.js.map → chunk-EKTPCSN4.js.map} +1 -1
- package/dist/chunk-EW27SCCC.js +2 -0
- package/dist/chunk-EW27SCCC.js.map +1 -0
- package/dist/chunk-HCFSHGK4.js +46 -0
- package/dist/chunk-HCFSHGK4.js.map +1 -0
- package/dist/chunk-HHJGKD5B.js +71 -0
- package/dist/chunk-HHJGKD5B.js.map +1 -0
- package/dist/chunk-LEIYC3BF.js +104 -0
- package/dist/chunk-LEIYC3BF.js.map +1 -0
- package/dist/{chunk-HLRVDCNH.js → chunk-MBEQASP3.js} +2 -2
- package/dist/chunk-N67JZAXU.js +2 -0
- package/dist/chunk-N67JZAXU.js.map +1 -0
- package/dist/{chunk-RGUYBH77.js → chunk-SRHLXZRF.js} +2 -2
- package/dist/chunk-TT2Q7YMZ.js +118 -0
- package/dist/chunk-TT2Q7YMZ.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-WZEYII2S.js +6 -0
- package/dist/chunk-WZEYII2S.js.map +1 -0
- package/dist/chunk-XDJSYQST.js +6 -0
- package/dist/chunk-XDJSYQST.js.map +1 -0
- package/dist/chunk-YPOELN6Z.js +798 -0
- package/dist/chunk-YPOELN6Z.js.map +1 -0
- package/dist/{chunk-QZP65I2H.js → chunk-ZE2YFX4K.js} +2 -2
- package/dist/cli.js +1 -302
- package/dist/cli.js.map +1 -1
- package/dist/{compaction-JBZZ7I7Y.js → compaction-X46EQEPS.js} +6 -6
- package/dist/compaction-X46EQEPS.js.map +1 -0
- package/dist/{config-DCJYECL4.js → config-KCYHMKCT.js} +2 -2
- package/dist/config-mutator-QP4GLBOE.js +2 -0
- package/dist/context-planner-G2J7FNNU.js +17 -0
- package/dist/context-planner-G2J7FNNU.js.map +1 -0
- package/dist/editor-chat-R54E65W5.js +137 -0
- package/dist/editor-chat-R54E65W5.js.map +1 -0
- package/dist/heal-JDXUPRET.js +2 -0
- package/dist/inbox-L5ZZ623Y.js +315 -0
- package/dist/inbox-L5ZZ623Y.js.map +1 -0
- package/dist/index.d.ts +1996 -18
- package/dist/index.js +22 -49
- package/dist/index.js.map +1 -1
- package/dist/ingest-whatsapp-NS4OWDOU.js +6 -0
- package/dist/ingest-whatsapp-NS4OWDOU.js.map +1 -0
- package/dist/memory-extract-WH4FZ456.js +2 -0
- package/dist/processes-XF5CV4LK.js +256 -0
- package/dist/processes-XF5CV4LK.js.map +1 -0
- package/dist/program-FFWEILAS.js +2 -0
- package/dist/providers-AKQ4HTB4.js +2 -0
- package/dist/query-PKGYE5HR.js +2 -0
- package/dist/registry-IFMDJWQ7.js +2 -0
- package/dist/scheduler-63D5KFO7.js +2 -0
- package/dist/scheduler-63D5KFO7.js.map +1 -0
- package/dist/slack-PSQLVTGB.js +3 -0
- package/dist/slack-PSQLVTGB.js.map +1 -0
- package/dist/store-5MJOO74U.js +2 -0
- package/dist/store-5MJOO74U.js.map +1 -0
- package/dist/task-store-NM7VKNLE.js +2 -0
- package/dist/task-store-NM7VKNLE.js.map +1 -0
- package/dist/telegram-W4SLFMNY.js +3 -0
- package/dist/telegram-W4SLFMNY.js.map +1 -0
- package/dist/timers-PCB4ZIBD.js +2 -0
- package/dist/timers-PCB4ZIBD.js.map +1 -0
- package/dist/types-C7IFH42Z.js +2 -0
- package/dist/types-C7IFH42Z.js.map +1 -0
- package/dist/types-OIFU3KSQ.js +2 -0
- package/dist/types-OIFU3KSQ.js.map +1 -0
- package/dist/usage-dashboard-AXPXB2KP.js +803 -0
- package/dist/usage-dashboard-AXPXB2KP.js.map +1 -0
- package/dist/web/workflow-editor.global.js +1427 -0
- package/dist/web/workflow-editor.global.js.map +1 -0
- package/dist/whatsapp-DCHRBRVV.js +3 -0
- package/dist/whatsapp-DCHRBRVV.js.map +1 -0
- package/dist/wiki-UDPYFJBL.js +2 -0
- package/dist/wiki-UDPYFJBL.js.map +1 -0
- package/dist/workspace-setup-C4VB3QZV.js +2 -0
- package/dist/workspace-setup-C4VB3QZV.js.map +1 -0
- package/package.json +10 -3
- package/dist/agent-LRSSVFDR.js +0 -2
- package/dist/board-dashboard-DAPHL6W2.js +0 -4508
- package/dist/board-dashboard-DAPHL6W2.js.map +0 -1
- package/dist/chunk-3XULOWYB.js +0 -199
- package/dist/chunk-3XULOWYB.js.map +0 -1
- package/dist/chunk-6U2XOQHU.js +0 -7
- package/dist/chunk-6U2XOQHU.js.map +0 -1
- package/dist/chunk-AU37OREL.js +0 -108
- package/dist/chunk-AU37OREL.js.map +0 -1
- package/dist/chunk-B4Q4X7D5.js +0 -6
- package/dist/chunk-B4Q4X7D5.js.map +0 -1
- package/dist/chunk-CXY2RYNI.js +0 -11
- package/dist/chunk-CXY2RYNI.js.map +0 -1
- package/dist/chunk-IEVEMKAI.js +0 -168
- package/dist/chunk-IEVEMKAI.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-RB7265LO.js +0 -12
- package/dist/chunk-RB7265LO.js.map +0 -1
- package/dist/chunk-WVNSWBCU.js +0 -138
- package/dist/chunk-WVNSWBCU.js.map +0 -1
- package/dist/compaction-JBZZ7I7Y.js.map +0 -1
- package/dist/config-mutator-6EJHSP3Y.js +0 -2
- package/dist/heal-WKY5I4KB.js +0 -2
- package/dist/memory-extract-AD7KMRPC.js +0 -2
- package/dist/providers-VUIWO3ZK.js +0 -2
- package/dist/registry-5ND7GLFJ.js +0 -2
- package/dist/scheduler-FCXOXCN5.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-LRSSVFDR.js.map → agent-4AOMVXRJ.js.map} +0 -0
- /package/dist/{chunk-7MBTMCKY.js.map → chunk-63UYRJIC.js.map} +0 -0
- /package/dist/{chunk-HLRVDCNH.js.map → chunk-MBEQASP3.js.map} +0 -0
- /package/dist/{chunk-RGUYBH77.js.map → chunk-SRHLXZRF.js.map} +0 -0
- /package/dist/{chunk-QZP65I2H.js.map → chunk-ZE2YFX4K.js.map} +0 -0
- /package/dist/{config-DCJYECL4.js.map → config-KCYHMKCT.js.map} +0 -0
- /package/dist/{config-mutator-6EJHSP3Y.js.map → config-mutator-QP4GLBOE.js.map} +0 -0
- /package/dist/{heal-WKY5I4KB.js.map → heal-JDXUPRET.js.map} +0 -0
- /package/dist/{memory-extract-AD7KMRPC.js.map → memory-extract-WH4FZ456.js.map} +0 -0
- /package/dist/{providers-VUIWO3ZK.js.map → program-FFWEILAS.js.map} +0 -0
- /package/dist/{registry-5ND7GLFJ.js.map → providers-AKQ4HTB4.js.map} +0 -0
- /package/dist/{scheduler-FCXOXCN5.js.map → query-PKGYE5HR.js.map} +0 -0
- /package/dist/{workspace-setup-ZWTT42XT.js.map → registry-IFMDJWQ7.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"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import{a as le}from"./chunk-SRHLXZRF.js";import{c as U}from"./chunk-N67JZAXU.js";import{e as ce}from"./chunk-CDNWCYF7.js";import{b as L}from"./chunk-AFRHKA7B.js";import{a as ue,b as pe}from"./chunk-EW27SCCC.js";import{existsSync as N,mkdirSync as ze,readdirSync as he,readFileSync as H,unlinkSync as Be,writeFileSync as Je}from"fs";import{resolve as j,relative as Ge}from"path";import{z as c}from"zod";var Oe=c.enum(["trigger.channel","trigger.manual","trigger.cron","trigger.hook","trigger.form","agent","transform","branch","gateway.parallel","rule","action.send","action.createIssue","action.setLabel","action.readLabel","action.react","action.editMessage","action.logTime","action.callHTTP","userTask","subProcess","signal.emit","signal.wait","timer.boundary","checkpoint","end"]),_e=c.enum(["equals","contains","matches","exists"]),Ce=c.object({kind:_e,params:c.record(c.unknown()).default({})}),ge=c.object({id:c.string().min(1).regex(/^[a-zA-Z_][a-zA-Z0-9_-]*$/,"node id must be identifier-safe"),type:Oe,config:c.record(c.unknown()).default({}),position:c.object({x:c.number(),y:c.number()}).optional()}),fe=c.object({from:c.string(),fromPort:c.string().optional(),to:c.string(),toPort:c.string().optional(),label:c.string().optional()}),v=c.object({id:c.string().regex(/^[a-z0-9][a-z0-9_-]*$/,"workflow id must be lower-kebab"),version:c.literal(2).default(2),title:c.string().min(1),description:c.string().optional(),priority:c.number().int().default(0),fanOut:c.boolean().default(!1),nodes:c.array(ge).refine(s=>s.length>0,{message:"workflow must have at least one node"}),edges:c.array(fe).default([]),envAllow:c.array(c.string()).default([]),retention:c.object({maxRuns:c.number().int().positive().default(500),maxDays:c.number().int().positive().default(90)}).default({maxRuns:500,maxDays:90}),maxChildDepth:c.number().int().positive().default(5),mesh:c.object({allowRemote:c.boolean().default(!1),peers:c.array(c.string()).optional()}).default({allowRemote:!1}),created:c.string().optional(),updated:c.string().optional()});function M(s){let e=[],t=new Map(s.nodes.map(o=>[o.id,o])),r=s.nodes.filter(o=>o.type.startsWith("trigger."));r.length===0&&e.push("workflow must have exactly one trigger.* node (found 0)"),r.length>1&&e.push(`workflow has ${r.length} trigger.* nodes; v1 supports exactly one`);for(let o of s.edges)t.has(o.from)||e.push(`edge references missing node: from="${o.from}"`),t.has(o.to)||e.push(`edge references missing node: to="${o.to}"`);let n=r[0];if(n){let o=new Map;for(let l of s.edges)o.has(l.from)||o.set(l.from,[]),o.get(l.from).push(l.to);let a=new Set,d=[n.id];for(;d.length;){let l=d.pop();if(!a.has(l)){a.add(l);for(let u of o.get(l)??[])d.push(u)}}for(let l of s.nodes)a.has(l.id)||e.push(`node "${l.id}" is unreachable from trigger "${n.id}"`);s.nodes.some(l=>a.has(l.id)&&De(l.type))||e.push("no reachable `end`, `checkpoint`, `userTask`, `subProcess`, `signal.wait`, or `timer.boundary` node \u2014 the run cannot terminate or pause")}for(let o of s.nodes){if(o.type!=="branch"&&o.type!=="rule")continue;let a=o.type==="branch"?o.config.cases??[]:(o.config.rules??[]).filter(p=>typeof p.to=="string"),d=o.type==="branch"?o.config.default:o.config.default?.to,l=new Set(a.map(p=>p.to));d&&l.add(d);let u=s.edges.filter(p=>p.from===o.id);for(let p of u){let m=p.fromPort??"";l.has(m)||e.push(`${o.type} "${o.id}" has outgoing edge with port "${m}" not declared in cases or default`)}}let i=Ue(s);for(let o of i)e.push(`cycle without agent/checkpoint: ${o.join(" -> ")}`);return e}function De(s){return s==="end"||s==="checkpoint"||s==="userTask"||s==="subProcess"||s==="signal.wait"||s==="timer.boundary"}function Fe(s){return s==="agent"||s==="checkpoint"||s==="userTask"||s==="subProcess"||s==="signal.wait"||s==="timer.boundary"}function Ue(s){let e=new Map;for(let a of s.edges)e.has(a.from)||e.set(a.from,[]),e.get(a.from).push(a.to);let t=new Map(s.nodes.map(a=>[a.id,a])),r=[],n=new Map,i=[],o=a=>{if(n.get(a)===1){let d=i.indexOf(a);if(d>=0){let l=i.slice(d).concat(a);l.some(p=>{let m=t.get(p)?.type;return m?Fe(m):!1})||r.push(l)}return}if(n.get(a)!==2){n.set(a,1),i.push(a);for(let d of e.get(a)??[])o(d);i.pop(),n.set(a,2)}};for(let a of s.nodes)n.get(a.id)!==2&&o(a.id);return r}var me=c.enum(["running","paused","completed","failed","canceled"]),Le=c.enum(["ok","failed","timeout","skipped","paused","resumed"]),Me=c.object({at:c.string(),nodeId:c.string(),inputKeys:c.array(c.string()).default([]),status:Le,output:c.record(c.unknown()).optional(),idempotencyKey:c.string(),note:c.string().optional()}),He=c.object({backend:c.string(),id:c.string()}),Ke=c.discriminatedUnion("kind",[c.object({kind:c.literal("checkpoint"),nodeId:c.string(),checkpointName:c.string(),resumeMatch:c.record(c.unknown()).default({})}),c.object({kind:c.literal("userTask"),nodeId:c.string(),taskId:c.string(),assignee:c.string(),assignedTo:c.array(c.string()).default([])}),c.object({kind:c.literal("subProcess"),nodeId:c.string(),childRunId:c.string(),childWorkflowId:c.string()}),c.object({kind:c.literal("signalWait"),nodeId:c.string(),signalName:c.string(),match:c.record(c.unknown()).default({}),scope:c.enum(["workflow","global"]).default("workflow")}),c.object({kind:c.literal("timerWait"),nodeId:c.string(),fireAt:c.string()})]),qe=c.object({id:c.string(),workflowId:c.string(),workflowVersion:c.literal(2),homeNode:c.string(),status:me,pausedAt:Ke.optional(),context:c.record(c.record(c.unknown())).default({}),pending:c.array(c.string()).default([]),joinCounters:c.record(c.array(c.string())).default({}),entityRef:He,history:c.array(Me).default([]),parentRunId:c.string().nullable().default(null),parentNodeId:c.string().nullable().default(null),rootRunId:c.string().nullable().default(null),depth:c.number().int().min(0).default(0),createdAt:c.string(),updatedAt:c.string()});var K=class{baseDir;constructor(e={}){this.baseDir=e.baseDir??j(process.cwd(),".agentx/workflows"),ze(this.baseDir,{recursive:!0})}pathFor(e){return j(this.baseDir,`${e}.json`)}list(){if(!N(this.baseDir))return[];let e=[];for(let t of he(this.baseDir,{withFileTypes:!0})){if(!t.isFile()||!t.name.endsWith(".json")||t.name.startsWith("_"))continue;let r=j(this.baseDir,t.name);try{let n=JSON.parse(H(r,"utf-8")),i=v.safeParse(n);i.success&&e.push(i.data)}catch{}}return e.sort((t,r)=>t.id.localeCompare(r.id))}validateAll(){let e=[];if(!N(this.baseDir))return e;for(let t of he(this.baseDir,{withFileTypes:!0})){if(!t.isFile()||!t.name.endsWith(".json")||t.name.startsWith("_"))continue;let r=j(this.baseDir,t.name),n=Ge(this.baseDir,r),i;try{i=JSON.parse(H(r,"utf-8"))}catch(d){e.push({id:t.name.replace(/\.json$/,""),path:n,issues:[`parse error: ${d.message}`],isValid:!1});continue}let o=v.safeParse(i);if(!o.success){let d=o.error.issues.map(l=>`${l.path.join(".")||"<root>"}: ${l.message}`);e.push({id:String(i?.id??t.name.replace(/\.json$/,"")),path:n,issues:d,isValid:!1});continue}let a=M(o.data);e.push({workflow:o.data,issues:a,isValid:a.length===0})}return e}get(e){let t=this.pathFor(e);if(!N(t))return null;try{let r=JSON.parse(H(t,"utf-8")),n=v.safeParse(r);return n.success?n.data:null}catch{return null}}save(e){let t=new Date().toISOString().slice(0,10),r=v.parse({...e,created:e.created||t,updated:t});return Je(this.pathFor(r.id),JSON.stringify(r,null,2)+`
|
|
2
|
+
`),r}delete(e){let t=this.pathFor(e);return N(t)?(Be(t),!0):!1}};import{createHash as Ve,randomUUID as Ze}from"crypto";import{appendFileSync as ye,existsSync as R,mkdirSync as we,readdirSync as ke,readFileSync as q,statSync as be,unlinkSync as Ie,writeFileSync as Ye}from"fs";import{resolve as I}from"path";function Qe(s){return s.replace(/[^a-zA-Z0-9._:#@-]/g,"_").slice(0,200)}function w(s,e,t){return Ve("sha1").update(`${s}|${e}|${t}`).digest("hex").slice(0,16)}var z=class{baseDir;runsDir;indexDir;nodeId;constructor(e){this.baseDir=e.baseDir??I(process.cwd(),".agentx/workflows"),this.runsDir=I(this.baseDir,"_runs"),this.indexDir=I(this.baseDir,"_index"),this.nodeId=e.nodeId,we(this.runsDir,{recursive:!0}),we(this.indexDir,{recursive:!0})}runPath(e){return I(this.runsDir,`${e}.jsonl`)}indexPath(e){return I(this.indexDir,`${e.backend}__${Qe(e.id)}.json`)}create(e){let t=new Date().toISOString(),r=Ze(),n={id:r,workflowId:e.workflowId,workflowVersion:2,homeNode:this.nodeId,status:"running",pausedAt:void 0,context:e.initialContext??{},pending:e.initialPending,entityRef:e.entityRef,history:[],parentRunId:e.parentRunId??null,parentNodeId:e.parentNodeId??null,rootRunId:e.rootRunId??r,depth:e.depth??0,joinCounters:{},createdAt:t,updatedAt:t};return this.appendSnapshot(n),this.writeIndex(e.entityRef,n.id),n}get(e){let t=this.runPath(e);if(!R(t))return null;let r=q(t,"utf-8").trim();if(!r)return null;let n=null;for(let i of r.split(`
|
|
3
|
+
`)){let o;try{o=JSON.parse(i)}catch{continue}if(o.v===2){if(o.kind==="snapshot")n=o.run;else if(o.kind==="exec"&&n&&n.id===o.runId){let a=n;n={...a,history:[...a.history,o.entry],pending:o.pending,status:o.status??a.status,pausedAt:o.pausedAt===null?void 0:o.pausedAt??a.pausedAt,context:o.context??a.context,joinCounters:o.joinCounters??a.joinCounters,updatedAt:o.entry.at}}}}return n}getActiveByEntity(e){let t=this.indexPath(e);if(!R(t))return null;try{return JSON.parse(q(t,"utf-8")).runId}catch{return null}}getHomeNodeByEntity(e){let t=this.indexPath(e);if(!R(t))return null;try{return JSON.parse(q(t,"utf-8")).homeNode}catch{return null}}recordExecution(e){let t=this.get(e.runId);if(!t)return null;if(t.history.some(i=>i.idempotencyKey===e.entry.idempotencyKey))return t;let r={v:2,kind:"exec",runId:e.runId,entry:e.entry,pending:e.nextPending,status:e.status,pausedAt:e.pausedAt===null?null:e.pausedAt,context:e.context,joinCounters:e.joinCounters};ye(this.runPath(e.runId),JSON.stringify(r)+`
|
|
4
|
+
`);let n=e.status??t.status;return n==="completed"||n==="failed"||n==="canceled"?this.clearIndex(t.entityRef):this.writeIndex(t.entityRef,t.id),this.get(e.runId)}setStatus(e,t){let r=this.get(e);if(!r)return null;let n={...r,status:t,updatedAt:new Date().toISOString()};return this.appendSnapshot(n),t!=="running"&&t!=="paused"&&this.clearIndex(n.entityRef),n}list(e={}){if(!R(this.runsDir))return[];let t=ke(this.runsDir).filter(n=>n.endsWith(".jsonl")).map(n=>({name:n,mtime:be(I(this.runsDir,n)).mtimeMs})).sort((n,i)=>i.mtime-n.mtime),r=[];for(let n of t){let i=this.get(n.name.replace(/\.jsonl$/,""));if(i&&!(e.workflowId&&i.workflowId!==e.workflowId)&&(r.push(i),e.limit&&r.length>=e.limit))break}return r}prune(e){if(!R(this.runsDir))return 0;let t=Date.now()-e.maxDays*24*60*60*1e3,r=[];for(let i of ke(this.runsDir)){if(!i.endsWith(".jsonl"))continue;let o=I(this.runsDir,i),a=be(o).mtimeMs,d=this.get(i.replace(/\.jsonl$/,""));d&&(d.status==="running"||d.status==="paused"||r.push({file:o,run:d,mtime:a}))}r.sort((i,o)=>o.mtime-i.mtime);let n=0;for(let i=0;i<r.length;i++){let o=r[i],a=o.mtime<t,d=i>=e.maxRuns;if(a||d)try{Ie(o.file),n++}catch{}}return n}appendSnapshot(e){let t={v:2,kind:"snapshot",run:e};ye(this.runPath(e.id),JSON.stringify(t)+`
|
|
5
|
+
`)}writeIndex(e,t){Ye(this.indexPath(e),JSON.stringify({runId:t,homeNode:this.nodeId,updatedAt:new Date().toISOString()},null,2)+`
|
|
6
|
+
`)}clearIndex(e){let t=this.indexPath(e);if(R(t))try{Ie(t)}catch{}}};import{existsSync as Se,mkdirSync as Xe,readFileSync as et,unlinkSync as tt,writeFileSync as nt}from"fs";import{resolve as B}from"path";import{z as $}from"zod";var ve=$.object({version:$.literal(1),nodes:$.record($.object({x:$.number(),y:$.number()}))}),J=class{baseDir;layoutsDir;constructor(e={}){this.baseDir=e.baseDir??B(process.cwd(),".agentx/workflows"),this.layoutsDir=B(this.baseDir,"_layouts"),Xe(this.layoutsDir,{recursive:!0})}pathFor(e){let t=e.replace(/[^a-zA-Z0-9._-]/g,"_");return B(this.layoutsDir,`${t}.json`)}get(e){let t=this.pathFor(e);if(!Se(t))return null;try{let r=ve.safeParse(JSON.parse(et(t,"utf-8")));return r.success?r.data:null}catch{return null}}save(e,t){let r=ve.parse(t);return nt(this.pathFor(e),JSON.stringify(r,null,2)+`
|
|
7
|
+
`),r}sync(e,t){let r=this.get(e);if(!r)return null;let n={};for(let o of t)r.nodes[o]&&(n[o]=r.nodes[o]);let i={version:1,nodes:n};return this.save(e,i)}delete(e){let t=this.pathFor(e);return Se(t)?(tt(t),!0):!1}};function G(s){let{workflow:e,fromNodeId:t,selectedPort:r}=s,n=e.nodes.find(a=>a.id===t);if(!n)return{nextPending:[]};let i=e.edges.filter(a=>a.from===t);return{nextPending:(n.type==="branch"?i.filter(a=>(a.fromPort??"")===(r??"")):i).map(a=>a.to)}}function V(s,e){let t=s.config.cases??[],r=s.config.default??"";for(let n of t)if(rt(n.when,e))return n.to;return r}function rt(s,e){let t=s.params??{},r=String(t.path??""),n=W(e,r);switch(s.kind){case"equals":return n===t.value;case"contains":return typeof n=="string"?n.includes(String(t.value??"")):Array.isArray(n)?n.some(i=>i===t.value):!1;case"matches":{if(typeof n!="string")return!1;try{return new RegExp(String(t.regex??"")).test(n)}catch{return!1}}case"exists":return n!=null&&n!=="";default:return!1}}function W(s,e){if(!e)return;let t=e.split("."),r=s;for(let n of t){if(r==null||typeof r!="object")return;r=r[n]}return r}function E(s){let e=s.nodes.find(r=>r.type.startsWith("trigger."));if(!e)return null;let t=s.edges.filter(r=>r.from===e.id).map(r=>r.to);return{triggerId:e.id,pending:t}}function k(s,e){return s.nodes.find(t=>t.id===e)}function st(s,e,t){let r=s.split(".");if(r[0]==="env"){if(r.length!==2)return"";let i=r[1];if(!t.has(i))return"";let o=process.env[i];return typeof o=="string"?o:""}let n=e;for(let i of r){if(n==null||typeof n!="object")return"";n=n[i]}return n==null?"":typeof n=="object"?JSON.stringify(n):String(n)}function S(s,e,t={}){let r=new Set(t.envAllow||[]);return s.replace(/\{\{\s*([a-zA-Z_][a-zA-Z0-9_.-]*)\s*\}\}/g,(n,i)=>st(i,e,r))}function f(s,e,t={}){let r={};for(let[n,i]of Object.entries(s))typeof i=="string"?r[n]=S(i,e,t):Array.isArray(i)?r[n]=i.map(o=>typeof o=="string"?S(o,e,t):o):i&&typeof i=="object"?r[n]=f(i,e,t):r[n]=i;return r}var T=async s=>({output:s.run.context[s.node.id]??{}}),ot=async s=>{let e=s.node.config,t=String(e.agentId??""),r=String(e.prompt??"");if(!t)return{error:`agent node "${s.node.id}" missing config.agentId`};let n=S(r,s.run.context,{envAllow:s.workflow.envAllow}),i=typeof e.timeoutMinutes=="number"?e.timeoutMinutes:void 0,o=Date.now();try{let a=await s.agents.execute({agentId:t,message:n,workflowRunId:s.run.id,timeoutMinutes:i}),d=Date.now()-o;if(a.error)return s.log(`[node:${s.node.id}] agent "${t}" failed: ${a.error}`),{error:a.error};let u=String(e.resultParser??"noqta-result-token")==="json"?xt(a.content):Ae(a.content);return{output:{reply:a.content,result:u.result,json:u.json,taskId:a.taskId,durationMs:d}}}catch(a){return s.log(`[node:${s.node.id}] agent "${t}" threw: ${a.message}`),{error:a.message}}},it=async s=>{let e=V(s.node,s.run.context);return e?{output:{port:e},port:e}:(s.log(`[node:${s.node.id}] branch matched no case and no default port`),{output:{port:null}})},at=async s=>{let e=f(s.node.config,s.run.context,{envAllow:s.workflow.envAllow}),t=String(e.channel??""),r=String(e.chatId??""),n=String(e.text??"");if(!t||!r){let a=String(s.node.config.chatId??""),d=String(s.node.config.channel??""),l=[];return t||l.push(`channel config="${d}" rendered=""`),r||l.push(`chatId config="${a}" rendered=""`),(/\{\{\s*env\./.test(a)||/\{\{\s*env\./.test(d))&&l.push("(env.* templates only resolve for names listed in workflow.envAllow and actually set in process.env)"),{error:`action.send "${s.node.id}" needs channel + chatId \u2014 ${l.join("; ")}`}}let i=Y(s,e.accountId),o=s.channels[t];if(o?.send)try{return{output:{messageId:await o.send({channel:t,chatId:r,text:n,...i?{accountId:i}:{}})??null}}}catch(a){return{error:`action.send failed: ${a.message}`}}if(s.forwardChannelSend)try{return{output:{messageId:(await s.forwardChannelSend({channel:t,chatId:r,text:n,accountId:i})).messageId,viaMesh:!0}}}catch(a){return s.log(`[node:${s.node.id}] mesh-forward of "${t}" send failed: ${a.message}`),{error:`action.send via mesh failed: ${a.message}`}}return s.log(`[node:${s.node.id}] adapter "${t}" not available locally and no mesh forwarder`),{error:`channel "${t}" not available (no local adapter, no mesh route)`}},dt=async s=>{let e=f(s.node.config,s.run.context,{envAllow:s.workflow.envAllow}),t=String(e.channel??"gitlab"),r=String(e.project??""),n=String(e.title??""),i=String(e.description??""),o=Array.isArray(e.labels)?e.labels.map(String):[],a=Array.isArray(e.assignees)?e.assignees.map(String):[];if(!r||!n)return{error:`action.createIssue "${s.node.id}" needs project + title`};let d=Q(s),l=s.channels[t];if(!l?.createIssue)return{error:`channel "${t}" does not support createIssue`};try{let u=await l.createIssue({project:r,title:n,description:i,labels:o,assignees:a,agentId:d});return u?{output:{issue:{iid:u.iid,url:u.url,webUrl:u.url}}}:{error:"createIssue returned null (see adapter log)"}}catch(u){return{error:`action.createIssue failed: ${u.message}`}}},lt=async()=>({output:{}}),ct=async s=>{let e=s.node.config,t=String(e.name??s.node.id),r=e.waitFor??{},n=e.resumeMatch??{};return{paused:!0,pausedAt:{kind:"checkpoint",nodeId:s.node.id,checkpointName:t,resumeMatch:{...r,...n}}}},ut=async s=>{let e=f(s.node.config,s.run.context,{envAllow:s.workflow.envAllow}),t=String(e.channel??"gitlab"),r=String(e.project??""),n=String(e.iid??""),i=Array.isArray(e.add)?e.add.map(String):[],o=Array.isArray(e.remove)?e.remove.map(String):[];if(!r||!n)return{error:`action.setLabel "${s.node.id}" needs project + iid (try {{trigger.project}} / {{trigger.issue.iid}})`};if(i.length===0&&o.length===0)return{output:{skipped:!0,reason:"no labels to add or remove"}};let a=s.channels[t];if(!a?.setLabels)return{error:`channel "${t}" does not support setLabels`};let d=Q(s),l=e.kind==="merge_request"?"merge_request":"issue";return{output:{labels:await a.setLabels({project:r,kind:l,iid:n,add:i,remove:o,agentId:d})??[],add:i,remove:o}}},pt=async s=>{let e=f(s.node.config,s.run.context,{envAllow:s.workflow.envAllow}),t=String(e.channel??"gitlab"),r=String(e.project??""),n=String(e.iid??"");if(!r||!n)return{error:`action.readLabel "${s.node.id}" needs project + iid`};let i=s.channels[t];if(!i?.getLabels)return{error:`channel "${t}" does not support getLabels`};let o=e.kind==="merge_request"?"merge_request":"issue";return{output:{labels:await i.getLabels({project:r,kind:o,iid:n})??[]}}},gt=async s=>{let e=f(s.node.config,s.run.context,{envAllow:s.workflow.envAllow}),t=String(e.channel??""),r=String(e.chatId??""),n=String(e.messageId??""),i=String(e.emoji??"\u{1F440}");if(!t||!r||!n)return{error:`action.react "${s.node.id}" needs channel + chatId + messageId`};let o=Y(s,e.accountId),a=s.channels[t];if(!a?.react)return{error:`channel "${t}" does not support react`};try{return await a.react(r,n,i,o),{output:{emoji:i}}}catch(d){return{error:`action.react failed: ${d.message}`}}},ft=async s=>{let e=f(s.node.config,s.run.context,{envAllow:s.workflow.envAllow}),t=String(e.channel??""),r=String(e.chatId??""),n=String(e.messageId??""),i=String(e.text??""),o=e.parseMode?String(e.parseMode):void 0;if(!t||!r||!n||!i)return{error:`action.editMessage "${s.node.id}" needs channel + chatId + messageId + text`};let a=Y(s,e.accountId),d=s.channels[t];if(!d?.editMessage)return{error:`channel "${t}" does not support editMessage`};try{return{output:{edited:await d.editMessage(r,n,i,o,a)}}}catch(l){return{error:`action.editMessage failed: ${l.message}`}}},mt=async s=>{let e=f(s.node.config,s.run.context,{envAllow:s.workflow.envAllow}),t=String(e.channel??"gitlab"),r=String(e.chatId??""),n=Number(e.durationMs??0);if(!r||!n)return{error:`action.logTime "${s.node.id}" needs chatId + durationMs`};let i=s.channels[t];if(!i?.logTimeSpent)return{error:`channel "${t}" does not support logTimeSpent`};let o=Q(s);try{return await i.logTimeSpent(r,n,o),{output:{durationMs:n}}}catch(a){return{error:`action.logTime failed: ${a.message}`}}},ht=async s=>{let e=f(s.node.config,s.run.context,{envAllow:s.workflow.envAllow}),t=String(e.url??"");if(!t)return{error:`action.callHTTP "${s.node.id}" needs url`};let r=String(e.method??"POST").toUpperCase(),n=e.headers&&typeof e.headers=="object"?e.headers:{"Content-Type":"application/json"},i;e.body!==void 0&&(i=typeof e.body=="string"?e.body:JSON.stringify(e.body));try{let o=await fetch(t,{method:r,headers:n,body:i}),a=await o.text(),d=a;try{d=JSON.parse(a)}catch{}return{output:{ok:o.ok,status:o.status,body:d}}}catch(o){return{error:`action.callHTTP failed: ${o.message}`}}},yt=async s=>{let e=s.node.config,t=s.run.context;return typeof e.path=="string"&&e.path?{output:{value:W(t,e.path)}}:e.template&&typeof e.template=="object"?{output:f(e.template,t,{envAllow:s.workflow.envAllow})}:{output:{}}},wt=async s=>{let e=s.node.config,t=f(e,s.run.context,{envAllow:s.workflow.envAllow}),r=String(t.assignTo??"");if(!r)return{error:`userTask "${s.node.id}" missing config.assignTo`};let n=ue(r);if(!n)return{error:`userTask "${s.node.id}" assignTo must be "actor:<id>" or "role:<id>", got "${r}"`};let i=e.form??t.form,o=kt(i);if(!o)return{error:`userTask "${s.node.id}" missing or invalid config.form`};let a=String(t.title??o.title??s.node.id),d=typeof t.description=="string"?t.description:void 0;if(!s.actors)return{error:`userTask "${s.node.id}" requires ActorStore in context (engine misconfigured)`};if(!s.tasks)return{error:`userTask "${s.node.id}" requires TaskStore in context (engine misconfigured)`};let l=s.actors.pickAssignees(n);if(l.length===0)return{error:`userTask "${s.node.id}" assignee "${r}" resolved to zero actors`};let u=Re(t.dueIn),p=s.tasks.create({runId:s.run.id,workflowId:s.workflow.id,nodeId:s.node.id,title:a,description:d,assignee:r,assignedTo:l,form:o,dueAt:u});return s.log(`[node:${s.node.id}] userTask created ${p.id} for ${r} \u2192 actors [${l.join(", ")}]`),{paused:!0,pausedAt:{kind:"userTask",nodeId:s.node.id,taskId:p.id,assignee:r,assignedTo:l}}};function kt(s){if(!s||typeof s!="object")return null;let e=ce.safeParse(s);return e.success?e.data:null}function Re(s){if(s!=null){if(typeof s=="number")return new Date(Date.now()+s*6e4).toISOString();if(typeof s=="object"&&s&&"minutes"in s){let e=Number(s.minutes);if(Number.isFinite(e))return new Date(Date.now()+e*6e4).toISOString()}if(typeof s=="string"){let e=/^P(?:([0-9]+)D)?(?:T(?:([0-9]+)H)?(?:([0-9]+)M)?(?:([0-9]+)S)?)?$/.exec(s);if(e){let t=Number(e[1]??0),r=Number(e[2]??0),n=Number(e[3]??0),i=Number(e[4]??0),o=((t*24+r)*60+n)*6e4+i*1e3;if(o>0)return new Date(Date.now()+o).toISOString()}}}}var bt=async s=>{let e=s.node.config,t=f(e,s.run.context,{envAllow:s.workflow.envAllow}),r=String(t.workflowId??"");if(!r)return{error:`subProcess "${s.node.id}" missing config.workflowId`};let n=s.run.depth??0,i=s.workflow.maxChildDepth??5;if(n+1>=i)return{error:`max child workflow depth ${i} exceeded at ${s.workflow.id} \u2192 ${r} (parent depth ${n})`};let o=e.inputMap,a={};if(o==="*")a={...s.run.context};else if(o&&typeof o=="object")for(let[d,l]of Object.entries(o))l&&typeof l=="object"&&!Array.isArray(l)?a[d]=f(l,s.run.context,{envAllow:s.workflow.envAllow}):typeof l=="string"&&(a[d]={value:S(l,s.run.context,{envAllow:s.workflow.envAllow})});return{paused:!0,pausedAt:{kind:"subProcess",nodeId:s.node.id,childRunId:"",childWorkflowId:r},spawnChild:{workflowId:r,input:a}}},It=T,St=async s=>{let e=s.node.config,t=f(e,s.run.context,{envAllow:s.workflow.envAllow}),r=String(t.name??"");if(!r)return{error:`signal.emit "${s.node.id}" missing config.name`};let n=t.scope==="global"?"global":"workflow",i=t.payload&&typeof t.payload=="object"?t.payload:{};return{output:{emittedAt:new Date().toISOString(),name:r,scope:n,payload:i},emitSignal:{name:r,scope:n,payload:i}}},vt=async s=>{let e=s.node.config,t=f(e,s.run.context,{envAllow:s.workflow.envAllow}),r=String(t.name??"");if(!r)return{error:`signal.wait "${s.node.id}" missing config.name`};let n=t.scope==="global"?"global":"workflow",i=e.match&&typeof e.match=="object"?e.match:{};return{paused:!0,pausedAt:{kind:"signalWait",nodeId:s.node.id,signalName:r,match:i,scope:n}}},Rt=async s=>{let e=s.node.config,t=f(e,s.run.context,{envAllow:s.workflow.envAllow}),r=Re(t.after??e.after);return r?{paused:!0,pausedAt:{kind:"timerWait",nodeId:s.node.id,fireAt:r}}:{error:`timer.boundary "${s.node.id}" needs config.after (duration or minutes)`}},$t=async()=>({output:{}}),At=async s=>{let e=s.node.config,r=(Array.isArray(e.inputs)?e.inputs:[]).map(o=>typeof o=="string"?S(o,s.run.context,{envAllow:s.workflow.envAllow}):o),n=Array.isArray(e.rules)?e.rules:[];for(let o of n){let a=Array.isArray(o.when)?o.when:[];if(a.length!==r.length)continue;let d=!0;for(let l=0;l<a.length;l++)if(!Tt(a[l],r[l])){d=!1;break}if(d)return{output:{...o.output??{},matchedPort:o.to??""},port:o.to}}let i=e.default;return{output:{...i?.output??{},matchedPort:i?.to??""},port:i?.to}};function Tt(s,e){if(s==null||s==="*")return!0;let t=String(s),r=typeof e=="number"?e:String(e??""),n=/^(>=|<=|>|<)\s*(-?\d+(?:\.\d+)?)$/.exec(t);if(n){let i=n[1],o=Number(n[2]),a=typeof r=="number"?r:Number(r);if(Number.isNaN(a))return!1;switch(i){case">":return a>o;case"<":return a<o;case">=":return a>=o;case"<=":return a<=o}}if(t.startsWith("!="))return String(r)!==t.slice(2);if(t.startsWith("/")&&t.endsWith("/")&&t.length>2)try{return new RegExp(t.slice(1,-1)).test(String(r))}catch{return!1}return String(r)===t}var $e={"trigger.channel":T,"trigger.manual":T,"trigger.cron":T,"trigger.hook":T,"trigger.form":It,agent:ot,branch:it,transform:yt,"gateway.parallel":$t,rule:At,"action.send":at,"action.createIssue":dt,"action.setLabel":ut,"action.readLabel":pt,"action.react":gt,"action.editMessage":ft,"action.logTime":mt,"action.callHTTP":ht,userTask:wt,subProcess:bt,"signal.emit":St,"signal.wait":vt,"timer.boundary":Rt,checkpoint:ct,end:lt};function Z(s){return $e[s]}function Y(s,e){if(typeof e=="string"&&e)return e;let t=s.workflow.nodes.find(i=>i.type.startsWith("trigger."));if(!t)return;let n=s.run.context[t.id]?.accountId;return typeof n=="string"&&n?n:void 0}function Q(s){for(let e=s.run.history.length-1;e>=0;e--){let t=s.run.history[e],r=s.workflow.nodes.find(n=>n.id===t.nodeId);if(r?.type==="agent")return r.config.agentId??void 0}}function Ae(s){if(!s)return{};let e=s.match(/\bRESULT:\s*([a-z][a-z0-9_-]*)/i);if(e)return{result:e[1].toLowerCase()};let t=s.match(/\[(APPROVED|REJECTED|CHANGES[-_]REQUESTED|DONE|FAILED|SKIPPED)\]/i);return t?{result:t[1].toLowerCase().replace("_","-")}:{}}function xt(s){let{result:e}=Ae(s);if(!s)return{result:e};let t=s.match(/```(?:json)?\s*([\s\S]*?)```/);if(!t)return{result:e};try{return{result:e,json:JSON.parse(t[1])}}catch{return{result:e}}}var x=class{handlers=new Set;subscribe(e){return this.handlers.add(e),()=>this.handlers.delete(e)}emit(e){for(let t of this.handlers)try{Promise.resolve(t(e)).catch(()=>{})}catch{}}};function X(s,e){if(s.name!==e.name||s.scope==="workflow"&&s.workflowId!==e.workflowId)return!1;for(let[t,r]of Object.entries(s.match??{}))if(!(r===void 0||r===""||r==="*")&&e.payload[t]!==r)return!1;return!0}function Te(s,e){let t=[],r={};for(let n of s.fields){let i=e.values[n.key];if(!(i!=null&&i!=="")){n.required?t.push({field:n.key,message:`"${n.label}" is required`}):n.defaultValue!==void 0&&(r[n.key]=n.defaultValue);continue}let a=Pt(n,i);if(a.error){t.push({field:n.key,message:a.error});continue}r[n.key]=a.value}return{ok:t.length===0,errors:t,values:r}}function Pt(s,e){let t=s.label;switch(s.type){case"text":case"long-text":{let r=String(e);return s.validate?.min!==void 0&&r.length<s.validate.min?{error:`"${t}" must be at least ${s.validate.min} characters`}:s.validate?.max!==void 0&&r.length>s.validate.max?{error:`"${t}" must be at most ${s.validate.max} characters`}:s.validate?.pattern&&!new RegExp(s.validate.pattern).test(r)?{error:`"${t}" does not match the expected format`}:{value:r}}case"number":{let r=typeof e=="number"?e:Number(String(e));return Number.isFinite(r)?s.validate?.min!==void 0&&r<s.validate.min?{error:`"${t}" must be at least ${s.validate.min}`}:s.validate?.max!==void 0&&r>s.validate.max?{error:`"${t}" must be at most ${s.validate.max}`}:{value:r}:{error:`"${t}" must be a number`}}case"boolean":{if(typeof e=="boolean")return{value:e};let r=String(e).toLowerCase();return["true","yes","y","1"].includes(r)?{value:!0}:["false","no","n","0"].includes(r)?{value:!1}:{error:`"${t}" must be yes/no`}}case"date":{let r=typeof e=="string"||typeof e=="number"?new Date(e):null;return!r||Number.isNaN(r.getTime())?{error:`"${t}" is not a valid date`}:{value:r.toISOString()}}case"select":{let r=String(e),n=s.options??[];return n.length&&!n.includes(r)?{error:`"${t}" must be one of: ${n.join(", ")}`}:{value:r}}case"multi-select":{let r=Array.isArray(e)?e.map(String):String(e).split(",").map(i=>i.trim()).filter(Boolean),n=s.options??[];if(n.length){let i=r.filter(o=>!n.includes(o));if(i.length)return{error:`"${t}" contains invalid values: ${i.join(", ")}`}}return{value:r}}case"file":return{value:String(e)}}return{error:`unknown field type: ${s.type}`}}var Nt=4e3,ee=class{store;runs;nodeId;forwarder;channels;agents;log;actors;tasks;timers;signals;renderUserTask;ackTypingTimers=new Map;constructor(e){this.store=e.store,this.runs=e.runs,this.nodeId=e.nodeId,this.forwarder=e.forwarder,this.channels=e.channels,this.agents=e.agents,this.log=e.log??(()=>{}),this.actors=e.actors??new pe,this.tasks=e.tasks??new U,this.timers=e.timers??new L({log:t=>this.log(t)}),this.signals=e.signals??new x,this.renderUserTask=e.renderUserTask,this.timers.onFire(t=>this.resumeFromTimer(t)),this.signals.subscribe(t=>this.resumeFromSignal(t))}emitSignal(e){let t={name:e.name,scope:e.scope??"global",workflowId:e.workflowId??"",payload:e.payload??{},emittedAt:new Date().toISOString()};return this.signals.emit(t),t}async resumeFromSignal(e){let t=this.runs.list({limit:500});for(let r of t){if(r.status!=="paused"||!r.pausedAt||r.pausedAt.kind!=="signalWait")continue;let n={name:r.pausedAt.signalName,scope:r.pausedAt.scope,workflowId:r.workflowId,match:r.pausedAt.match??{}};if(!X(n,e))continue;let i=this.store.list().find(p=>p.id===r.workflowId);if(!i)continue;let o=k(i,r.pausedAt.nodeId),a=o?i.edges.filter(p=>p.from===o.id).map(p=>p.to):[],d={receivedAt:new Date().toISOString(),name:e.name,payload:e.payload},l={at:d.receivedAt,nodeId:r.pausedAt.nodeId,inputKeys:[],status:"resumed",output:d,idempotencyKey:w(r.id,r.pausedAt.nodeId,`signal:${e.name}:${e.emittedAt}`)},u={...r.context,[r.pausedAt.nodeId]:d};this.runs.recordExecution({runId:r.id,entry:l,nextPending:a,status:"running",pausedAt:null,context:u}),this.log(`[workflow:${i.id}] run ${r.id} resumed from signal "${e.name}"`),this.walk(i,r.id,`signal:${e.name}`).catch(p=>this.log(`[workflow:${i.id}] walk-after-signal failed: ${p.message}`))}}async resumeFromTimer(e){let t=this.runs.get(e.runId);if(!t||t.status!=="paused"||!t.pausedAt||t.pausedAt.kind!=="timerWait"||t.pausedAt.nodeId!==e.nodeId)return;let r=this.store.list().find(u=>u.id===t.workflowId);if(!r)return;let n=k(r,e.nodeId),i=n?r.edges.filter(u=>u.from===n.id).map(u=>u.to):[],o=new Date().toISOString(),a={firedAt:o,scheduledFor:e.fireAt},d={at:o,nodeId:e.nodeId,inputKeys:[],status:"resumed",output:a,idempotencyKey:w(t.id,e.nodeId,`timer:${e.id}`)},l={...t.context,[e.nodeId]:a};this.runs.recordExecution({runId:t.id,entry:d,nextPending:i,status:"running",pausedAt:null,context:l}),this.log(`[workflow:${r.id}] run ${t.id} resumed from timer "${e.nodeId}" (fired ${o})`),this.walk(r,t.id,`timer:${e.id}`).catch(u=>this.log(`[workflow:${r.id}] walk-after-timer failed: ${u.message}`))}async dispatchWorkflow(e){let t=this.store.list().find(o=>o.id===e.workflowId);if(!t)return this.log(`[workflows] dispatchWorkflow: workflow "${e.workflowId}" not found`),{claimed:!1,run:null};let n=t.nodes.find(o=>o.type.startsWith("trigger."))?.config??{},i={source:e.trigger?.source??n.source??"hook",project:e.trigger?.project,repo:e.trigger?.repo,chat:e.trigger?.chat,labels:e.trigger?.labels};return this.dispatchOne(t,e.entityRef,e.event,i)}startChannelAck(e,t){let r=String(t.channel??""),n=String(t.chatId??"");if(!r||!n)return;let i=this.channels[r];if(!i)return;let o=typeof t.accountId=="string"?t.accountId:void 0,a=t.event,d=typeof a?.id=="string"?a.id:a?.id!=null?String(a.id):void 0;if(d&&i.react)try{Promise.resolve(i.react(n,d,"\u{1F440}",o)).catch(()=>{})}catch{}if(this.ackTypingTimers.has(e)||!i.sendTyping)return;let l=()=>{try{Promise.resolve(i.sendTyping(n,o)).catch(()=>{})}catch{}};l();let u=setInterval(l,Nt);this.ackTypingTimers.set(e,u)}stopChannelAck(e){let t=this.ackTypingTimers.get(e);t&&(clearInterval(t),this.ackTypingTimers.delete(e))}async dispatch(e){let t=this.matchByTrigger(e.trigger,e.fromRemote),n=t.some(a=>a.fanOut)?t:t.slice(0,1),i=[],o=[];for(let a of n){let d=await this.dispatchOne(a,e.entityRef,e.event,e.trigger);d.claimed&&i.push(a),d.run&&o.push(d.run)}return!e.fromRemote&&this.forwarder?.broadcastTrigger&&Promise.resolve(this.forwarder.broadcastTrigger({trigger:e.trigger,entityRef:e.entityRef,event:e.event})).catch(a=>this.log(`[mesh-broadcast] ${a?.message??a}`)),{claimed:i,runs:o}}matchByTrigger(e,t){let r=this.store.list(),n=[];for(let i of r){if(t){if(!i.mesh?.allowRemote)continue;let l=i.mesh.peers;if(l&&l.length>0&&!l.includes(t.peer))continue}let o=i.nodes.find(l=>l.type.startsWith("trigger."));if(!o)continue;let a=o.config;if(a.source!==e.source)continue;let d=a.filter;if(d){if(d.project&&d.project!=="*"&&d.project!==e.project||d.repo&&d.repo!=="*"&&d.repo!==e.repo||d.chat&&d.chat!=="*"&&O(e.source,d.chat)!==O(e.source,e.chat))continue;if(d.labels?.length){let l=new Set(e.labels??[]);if(!d.labels.some(p=>l.has(p)))continue}}n.push(i)}return n.sort((i,o)=>o.priority-i.priority||i.id.localeCompare(o.id))}async dispatchOne(e,t,r,n){let i=this.runs.getActiveByEntity(t),o=i?this.runs.getHomeNodeByEntity(t):null;if(i&&o&&o!==this.nodeId){if(!this.forwarder)return this.log(`[workflow:${e.id}] run ${i} home'd on "${o}" but no forwarder \u2014 dropping`),{claimed:!1,run:null};try{await this.forwarder.forwardTransition(o,{workflowId:e.id,event:r,entityRef:t})}catch(d){this.log(`[workflow:${e.id}] forward to "${o}" failed: ${d.message}`)}return{claimed:!0,run:null}}let a=i?this.runs.get(i):null;if(a)if(a.status==="paused"&&a.pausedAt){if(a.pausedAt.kind!=="checkpoint")return this.log(`[workflow:${e.id}] run ${a.id} paused on ${a.pausedAt.kind} \u2014 channel event bypasses workflow`),{claimed:!1,run:null};if(!Wt(a.pausedAt.resumeMatch,n,r))return this.log(`[workflow:${e.id}] run ${a.id} paused but event doesn't match resumeMatch \u2014 dropping`),{claimed:!1,run:null};let d=k(e,a.pausedAt.nodeId),l=d?e.edges.filter(A=>A.from===d.id).map(A=>A.to):[],u={at:new Date().toISOString(),nodeId:a.pausedAt.nodeId,inputKeys:[],status:"resumed",output:{event:r.payload},idempotencyKey:w(a.id,a.pausedAt.nodeId,`resume:${r.id}`)},p={...a.context,[a.pausedAt.nodeId]:{event:r.payload}},m=a.pausedAt.checkpointName,h=this.runs.recordExecution({runId:a.id,entry:u,nextPending:l,status:"running",pausedAt:null,context:p});h&&(a=h),this.log(`[workflow:${e.id}] run ${a.id} resumed from checkpoint "${m}"`),n.source.endsWith("-message")&&this.startChannelAck(a.id,r.payload)}else return a.status==="running"?(this.log(`[workflow:${e.id}] run ${a.id} already running; dropping concurrent event ${r.id}`),{claimed:!0,run:null}):(this.log(`[workflow:${e.id}] run ${a.id} is ${a.status}; dropping event ${r.id}`),{claimed:!1,run:null});else{let d=E(e);if(!d)return this.log(`[workflow:${e.id}] no trigger node found`),{claimed:!1,run:null};a=this.runs.create({workflowId:e.id,initialPending:d.pending,entityRef:t,initialContext:{[d.triggerId]:r.payload}}),this.log(`[workflow:${e.id}] run ${a.id} created from trigger "${d.triggerId}" for ${t.id}`),n.source.endsWith("-message")&&this.startChannelAck(a.id,r.payload)}return this.walk(e,a.id,r.id),{claimed:!0,run:a}}async walk(e,t,r){try{await this.walkInner(e,t,r)}finally{this.stopChannelAck(t)}}async walkInner(e,t,r){let n=this.runs.get(t);if(n)for(;n.status==="running"&&n.pending.length>0;){let i=n.pending[0],o=n.pending.slice(1),a=k(e,i);if(!a){this.log(`[workflow:${e.id}] missing node "${i}" \u2014 marking run failed`),this.runs.recordExecution({runId:n.id,entry:{at:new Date().toISOString(),nodeId:i,inputKeys:[],status:"failed",idempotencyKey:w(n.id,i,r),note:"node definition missing from workflow"},nextPending:o,status:"failed"});return}let d=Z(a.type);if(!d){this.log(`[workflow:${e.id}] no handler for node type "${a.type}"`),this.runs.recordExecution({runId:n.id,entry:{at:new Date().toISOString(),nodeId:i,inputKeys:[],status:"failed",idempotencyKey:w(n.id,i,r),note:`no handler for node type "${a.type}"`},nextPending:o,status:"failed"});return}let l=w(n.id,i,r);if(n.history.some(g=>g.idempotencyKey===l)){n=this.runs.recordExecution({runId:n.id,entry:{at:new Date().toISOString(),nodeId:i,inputKeys:[],status:"skipped",idempotencyKey:l+"_dedup",note:"duplicate execution dropped"},nextPending:o})??n;continue}let u=e.edges.filter(g=>g.to===i).map(g=>g.from),p;try{p=await d({workflow:e,run:n,node:a,channels:this.channels,agents:this.agents,actors:this.actors,tasks:this.tasks,forwardChannelSend:this.forwarder?.forwardChannelSend?.bind(this.forwarder),log:this.log})}catch(g){this.log(`[workflow:${e.id}] handler "${a.type}" threw: ${g.message}`),p={error:g.message}}let m=new Date().toISOString();if(p.error){let g={at:m,nodeId:i,inputKeys:u,status:"failed",idempotencyKey:l,note:p.error.slice(0,200)};n=this.runs.recordExecution({runId:n.id,entry:g,nextPending:o,status:"failed"})??n;return}if(p.paused&&p.pausedAt){let g=p.pausedAt,b=null;if(p.spawnChild&&g.kind==="subProcess"){if(b=await this.spawnChild({parent:n,parentNodeId:i,childWorkflowId:p.spawnChild.workflowId,input:p.spawnChild.input}),!b){let y={at:m,nodeId:i,inputKeys:u,status:"failed",idempotencyKey:l,note:`subProcess: child workflow "${p.spawnChild.workflowId}" not found`};n=this.runs.recordExecution({runId:n.id,entry:y,nextPending:o,status:"failed"})??n;return}g={...g,childRunId:b.id}}let de={at:m,nodeId:i,inputKeys:u,status:"paused",idempotencyKey:l};if(n=this.runs.recordExecution({runId:n.id,entry:de,nextPending:o,status:"paused",pausedAt:g})??n,b&&this.kickChildWalk(b),g.kind==="timerWait")try{this.timers.schedule({runId:n.id,workflowId:e.id,nodeId:g.nodeId,fireAt:g.fireAt,cancelKey:`${n.id}:${g.nodeId}`})}catch(y){this.log(`[workflow:${e.id}] timer schedule failed: ${y.message}`)}if(g.kind==="userTask"&&this.renderUserTask){let y=this.tasks.get(g.taskId);if(y)try{await this.renderUserTask(y)}catch(F){this.log(`[workflow:${e.id}] userTask render failed: ${F.message}`)}}return}let h=p.output??{},A={...n.context,[i]:h},{nextPending:Ne}=G({workflow:e,fromNodeId:i,selectedPort:p.port}),C=[],P={...n.joinCounters??{}};for(let g of Ne){let b=k(e,g);if(!(b?.type==="gateway.parallel"&&String(b.config?.mode??"fanOut")==="join")){C.push(g);continue}let y=P[g]??[];y.includes(i)||y.push(i),P[g]=y;let F=e.edges.filter(Ee=>Ee.to===g).length;y.length>=F&&(C.push(g),delete P[g])}let je=[...o,...C],D=a.type==="end",We={at:m,nodeId:i,inputKeys:u,status:"ok",output:h,idempotencyKey:l};if(n=this.runs.recordExecution({runId:n.id,entry:We,nextPending:D?[]:je,status:D?String(a.config.status??"completed"):"running",context:A,joinCounters:P})??n,p.emitSignal&&this.emitSignal({name:p.emitSignal.name,scope:p.emitSignal.scope,workflowId:e.id,payload:p.emitSignal.payload}),D){n.parentRunId&&n.parentNodeId&&this.resumeParent({parentRunId:n.parentRunId,parentNodeId:n.parentNodeId,childRun:n}).catch(g=>this.log(`[workflow:${e.id}] resumeParent failed: ${g.message}`));return}}}async spawnChild(e){let t=this.store.list().find(a=>a.id===e.childWorkflowId);if(!t)return null;let r=E(t);if(!r)return this.log(`[workflow:${t.id}] no trigger node \u2014 cannot spawn as child`),null;let n={...e.input};n[r.triggerId]={...n[r.triggerId]??{},parentRunId:e.parent.id,parentNodeId:e.parentNodeId};let i={backend:"agentx-internal",id:`subprocess:${e.parent.id}:${e.parentNodeId}`},o=this.runs.create({workflowId:t.id,initialPending:r.pending,entityRef:i,initialContext:n,parentRunId:e.parent.id,parentNodeId:e.parentNodeId,rootRunId:e.parent.rootRunId??e.parent.id,depth:(e.parent.depth??0)+1});return this.log(`[workflow:${t.id}] child run ${o.id} spawned from ${e.parent.id}/${e.parentNodeId} (depth ${o.depth})`),o}kickChildWalk(e){let t=this.store.list().find(r=>r.id===e.workflowId);t&&this.walk(t,e.id,`spawn:${e.parentRunId}:${e.parentNodeId}`).catch(r=>this.log(`[workflow:${t.id}] child walk failed: ${r.message}`))}async resumeParent(e){let t=this.runs.get(e.parentRunId);if(!t||t.status!=="paused"||!t.pausedAt||t.pausedAt.kind!=="subProcess"||t.pausedAt.nodeId!==e.parentNodeId||t.pausedAt.childRunId!==e.childRun.id)return;let r=this.store.list().find(l=>l.id===t.workflowId);if(!r)return;let n=k(r,e.parentNodeId),i=n?r.edges.filter(l=>l.from===n.id).map(l=>l.to):[],o={childRunId:e.childRun.id,status:e.childRun.status,output:jt(e.childRun)},a={at:new Date().toISOString(),nodeId:e.parentNodeId,inputKeys:[],status:"resumed",output:o,idempotencyKey:w(t.id,e.parentNodeId,`child:${e.childRun.id}`)},d={...t.context,[e.parentNodeId]:o};this.runs.recordExecution({runId:t.id,entry:a,nextPending:i,status:"running",pausedAt:null,context:d}),this.log(`[workflow:${r.id}] parent run ${t.id} resumed from subProcess child ${e.childRun.id}`),this.walk(r,t.id,`child:${e.childRun.id}`)}async submitTask(e,t,r){let n=this.tasks.get(e);if(!n)return{ok:!1,error:`task not found: ${e}`};if(n.status!=="open")return{ok:!1,error:`task is ${n.status}`};let i=Te(n.form,t);if(!i.ok)return{ok:!1,error:"form validation failed",fieldErrors:i.errors};let o=this.runs.get(n.runId);if(!o||o.status!=="paused")return{ok:!1,error:`run ${n.runId} is not paused`};if(!o.pausedAt||o.pausedAt.kind!=="userTask"||o.pausedAt.taskId!==e)return{ok:!1,error:`run ${n.runId} is not waiting on task ${e}`};let a=this.store.list().find(h=>h.id===o.workflowId);if(!a)return{ok:!1,error:`workflow ${o.workflowId} not found`};let d=k(a,n.nodeId),l=d?a.edges.filter(h=>h.from===d.id).map(h=>h.to):[],u={submittedBy:r,submittedAt:new Date().toISOString(),values:i.values,action:t.action},p={at:new Date().toISOString(),nodeId:n.nodeId,inputKeys:[],status:"resumed",output:u,idempotencyKey:w(o.id,n.nodeId,`task:${e}:${t.action}`)},m={...o.context,[n.nodeId]:u};return this.runs.recordExecution({runId:o.id,entry:p,nextPending:l,status:"running",pausedAt:null,context:m}),this.tasks.save({...n,status:"completed",submittedBy:r,submittedAt:u.submittedAt,submittedValues:i.values,submittedAction:t.action}),this.log(`[workflow:${a.id}] run ${o.id} resumed from userTask ${e}`),this.walk(a,o.id,`task:${e}`),{ok:!0,runId:o.id}}};function jt(s){for(let e=s.history.length-1;e>=0;e--){let t=s.history[e];if(t.status==="ok"&&t.output&&Object.keys(t.output).length>0)return t.output}return{}}function Wt(s,e,t){let r=i=>{let o=s[i];if(o===void 0||o===""||o==="*")return!0;if(Array.isArray(o)){let a=new Set(e.labels??[]);return o.some(d=>a.has(String(d)))}return i==="chat"?O(e.source,String(o))===O(e.source,e.chat):e[i]===o};if(!r("source")||!r("project")||!r("repo")||!r("chat"))return!1;let n=s.eventIdLike;return!(typeof n=="string"&&n&&!t.id.includes(n))}function O(s,e){return e==null?"":s==="whatsapp-message"?e.replace(/@s\.whatsapp\.net$|@g\.us$/i,"").replace(/[\s+()]/g,""):e}function Et(s){return{"pre:channel-message":async e=>{let t=e.msg;if(!t)return{};let r=Ot(t.channel);if(!r)return{};let n=t.group?.id??t.sender.id,i={backend:t.channel,id:`${t.channel}:${n}`},{claimed:o}=await s.dispatch({trigger:{source:r,chat:n},entityRef:i,event:{id:`${t.channel}:${t.accountId}:${t.id}`,payload:_t(t)}});return o.length===0?{}:o.every(d=>d.nodes.find(u=>u.type.startsWith("trigger."))?.config?.passthrough===!0)?{}:{blocked:!0,message:`handled by workflow${o.length>1?"s":""}: ${o.map(d=>d.id).join(", ")}`}},"on:gitlab-issue":async e=>{let t=e.project,r=e.iid,n=e.issueEvent;if(!t||r===void 0)return{};let i=Array.isArray(n?.object_attributes?.labels)?n.object_attributes.labels.map(d=>typeof d=="string"?d:d?.title).filter(Boolean):n?.labels?.map(d=>d?.title)??[],o=n?.assignees??n?.object_attributes?.assignee_ids??[],a={backend:"gitlab",id:`${t}#${r}`};return await s.dispatch({trigger:{source:"gitlab-issue",project:t,labels:i},entityRef:a,event:{id:`gitlab-issue:${t}:${r}:${n?.object_attributes?.updated_at||n?.object_attributes?.action||"evt"}`,payload:{issue:{iid:r,title:n?.object_attributes?.title,description:n?.object_attributes?.description,url:n?.object_attributes?.url,action:n?.object_attributes?.action,labels:i,assignees:o},project:t,channel:"gitlab",chatId:`${t}:issue:${r}`}}}),{}},"on:gitlab-pipeline":async e=>{let t=e.project,r=e.pipelineId,n=e.status,i=e.ref;if(!t||!n)return{};let a=e.raw?.merge_request?.iid;if(!a)return{};let d={backend:"gitlab",id:`${t}!${a}`};return await s.dispatch({trigger:{source:"gitlab-pipeline",project:t},entityRef:d,event:{id:`gitlab-pipeline:${t}:${r}:${n}`,payload:{pipeline:{id:r,status:n,ref:i},project:t,channel:"gitlab",chatId:`${t}:merge_request:${a}`}}}),{}}}}function Ot(s){switch(s){case"telegram":return"telegram-message";case"whatsapp":return"whatsapp-message";case"discord":return"discord-message";case"slack":return"slack-message";default:return null}}function _t(s){let e=s.group?.id??s.sender.id;return{channel:s.channel,chatId:e,accountId:s.accountId,text:s.text,fromJid:s.sender.id,sender:{id:s.sender.id,name:s.sender.name,username:s.sender.username},contact:s.channelMeta?.facts?{facts:s.channelMeta.facts}:void 0,group:s.group?{id:s.group.id,name:s.group.name}:void 0,channelMeta:s.channelMeta,replyTo:s.replyTo,replyToText:s.replyToText,media:s.media,event:{id:s.id,timestamp:s.timestamp?.toISOString?.()??new Date().toISOString()}}}function Ct(s){let e=s.store.list(),t=0,r=0;for(let n of e){let i=n.nodes.find(o=>o.type.startsWith("trigger."));if(i){if(i.type==="trigger.cron"){let o=i.config;if(!o.spec){s.log(`[workflows] ${n.id} trigger.cron missing config.spec \u2014 skipping`);continue}Dt({workflowId:n.id,triggerNodeId:i.id,spec:o.spec,timezone:o.timezone??"UTC"},s.dispatcher,s.log),t++}else if(i.type==="trigger.hook"){let o=i.config;if(!o.event||!o.event.startsWith("on:")){s.log(`[workflows] ${n.id} trigger.hook config.event must start with "on:" \u2014 skipping`);continue}s.hooks.registerHandler(o.event,`workflows:${n.id}`,async a=>{try{await s.dispatcher.dispatchWorkflow({workflowId:n.id,entityRef:{backend:"hook",id:`${n.id}:${o.event}`},event:{id:`hook:${o.event}:${Date.now().toString(36)}`,payload:{hookEvent:o.event,...a}}})}catch(d){s.log(`[workflows] ${n.id} hook dispatch failed: ${d.message}`)}return{}},60),r++}}}return{cronTimers:t,hookSubscribers:r}}function Dt(s,e,t){let r=()=>{let n;try{n=le(s.spec,new Date,s.timezone)}catch(o){t(`[workflows] ${s.workflowId} cron spec invalid: ${o.message}`);return}let i=Math.max(1e3,n.getTime()-Date.now());setTimeout(()=>{(async()=>{let o=`cron:${s.workflowId}:${n.toISOString()}`;try{await e.dispatchWorkflow({workflowId:s.workflowId,trigger:{source:"cron"},entityRef:{backend:"cron",id:`${s.workflowId}@${n.toISOString()}`},event:{id:o,payload:{now:n.toISOString(),spec:s.spec,workflowId:s.workflowId}}})}catch(a){t(`[workflows] ${s.workflowId} cron dispatch failed: ${a.message}`)}finally{r()}})()},i)};r()}import{readFileSync as te,writeFileSync as Pe,existsSync as ne,readdirSync as Ut,statSync as Lt}from"fs";import{resolve as re,join as Mt}from"path";var se=/^(\s*)-\s*\[( |x|X)\]\s*(.*)$/,_=/@([a-z0-9_-]+)/i,xe=/\[time:\s*([0-9hm\s]+)\]/i;function Ht(s){let e=s.match(/(\d+)\s*h/i),t=s.match(/(\d+)\s*m/i);return(e?parseInt(e[1],10)*3600:0)+(t?parseInt(t[1],10)*60:0)}var oe=class{constructor(e){this.path=e}type="backlog";capabilities={listAll:!1,create:!1,transition:!1,labels:!1,search:!1};read(){let e=re(process.cwd(),this.path);return ne(e)?te(e,"utf-8").split(`
|
|
8
|
+
`):[]}write(e){let t=re(process.cwd(),this.path);Pe(t,e.join(`
|
|
9
|
+
`))}async listOpen(e){let t=this.read(),r=[];for(let n=0;n<t.length;n++){let i=t[n].match(se);if(!i||i[2].toLowerCase()==="x")continue;let a=i[3],d=a.match(_);if(d&&d[1].toLowerCase()!==e.toLowerCase())continue;let l=a.match(xe),u=a.replace(_,"").replace(xe,"").trim();r.push({id:`backlog:${n}`,title:u||`Task at line ${n+1}`,assignee:d?d[1]:void 0,estimatedSeconds:l?Ht(l[1]):void 0,priority:n})}return r}async claim(e,t){}async report(e,t){if(!e.startsWith("backlog:"))throw new Error(`not a backlog id: ${e}`);let r=parseInt(e.slice(8),10),n=this.read(),i=n[r];if(!i)throw new Error(`backlog line ${r} missing`);if(!i.match(se))throw new Error(`backlog line ${r} not a checkbox`);if(t.status==="done"){n[r]=i.replace(/- \[ \]/,"- [x]");let a=t.timeSeconds?` <!-- done by agent, ${Math.round(t.timeSeconds/60)}m -->`:" <!-- done by agent -->";n.splice(r+1,0,a)}else t.status==="blocked"?n.splice(r+1,0,` <!-- BLOCKED: ${t.blocker||t.note||"?"} -->`):t.note&&n.splice(r+1,0,` <!-- ${t.note.replace(/-->/g,"\u2014 >")} -->`);this.write(n)}},ie=class{constructor(e,t){this.root=e;this.glob=t}type="wiki";capabilities={listAll:!1,create:!1,transition:!1,labels:!1,search:!1};walk(e,t){if(ne(e))for(let r of Ut(e)){let n=Mt(e,r);Lt(n).isDirectory()?this.walk(n,t):r.endsWith(".md")&&t.push(n)}}async listOpen(e){let t=[];this.walk(re(process.cwd(),this.root),t);let r=[];for(let n of t){let i=te(n,"utf-8").split(`
|
|
10
|
+
`);for(let o=0;o<i.length;o++){let a=i[o].match(se);if(!a||a[2].toLowerCase()==="x")continue;let d=a[3].match(_);!d||d[1].toLowerCase()!==e.toLowerCase()||r.push({id:`wiki:${n}:${o}`,title:a[3].replace(_,"").trim(),assignee:d[1],priority:o})}}return r}async claim(){}async report(e,t){let r=e.split(":"),n=parseInt(r[r.length-1],10),i=r.slice(1,-1).join(":");if(!ne(i))return;let o=te(i,"utf-8").split(`
|
|
11
|
+
`);t.status==="done"&&o[n]&&(o[n]=o[n].replace(/- \[ \]/,"- [x]"),Pe(i,o.join(`
|
|
12
|
+
`)))}},ae=class{constructor(e,t,r,n,i){this.host=e;this.token=t;this.projects=r;this.agentUsernames=n;this.log=i}type="gitlab";capabilities={listAll:!0,create:!0,transition:!0,labels:!0,search:!0};usernameToAgent(e){for(let[t,r]of Object.entries(this.agentUsernames))if(r.some(n=>n.toLowerCase()===e.toLowerCase()))return t}mapIssue(e,t){let r=e.references?.full?.split("#")[0]||t,n=[],i;Array.isArray(e.labels)&&(e.labels.length&&typeof e.labels[0]=="object"?(i=e.labels.map(l=>({name:l.name,color:l.color,text_color:l.text_color})),n=i.map(l=>l.name)):n=e.labels);let a=(Array.isArray(e.assignees)&&e.assignees.length?e.assignees:e.assignee?[e.assignee]:[]).map(l=>({username:l.username,name:l.name,avatarUrl:l.avatar_url})),d=a[0]?.username;return{id:`gitlab:${r}:issue:${e.iid}`,title:e.title,description:e.description||void 0,assignee:d?this.usernameToAgent(d)||d:void 0,assignees:a,url:e.web_url,priority:e.weight??99,estimatedSeconds:e.time_stats?.time_estimate||void 0,labels:n,labelDetails:i,updatedAt:e.updated_at,state:e.state==="closed"?"closed":"opened",milestone:e.milestone?.title||void 0}}async api(e,t={}){let r=await fetch(`${this.host}/api/v4${e}`,{...t,headers:{"PRIVATE-TOKEN":this.token,"Content-Type":"application/json",...t.headers||{}}});if(!r.ok)throw new Error(`GitLab ${r.status}: ${await r.text().catch(()=>"")}`);return r.json()}async listOpen(e){let t=this.agentUsernames[e]||[];if(!t.length)return[];let r=[],n=this.projects.length?this.projects:["__ALL__"];for(let i of t)for(let o of n)try{let a=`assignee_username=${encodeURIComponent(i)}&state=opened&scope=all&per_page=20`,d=o==="__ALL__"?`/issues?${a}`:`/projects/${encodeURIComponent(o)}/issues?${a}`,l=await this.api(d);for(let u of l){let p=this.mapIssue(u,o);p.assignee=e,r.push(p)}}catch(a){this.log(`[business] GitLab listOpen failed for ${i} on ${o}: ${a.message}`)}return r}async claim(e,t){let{project:r,iid:n,type:i}=this.parseId(t),o=`/projects/${encodeURIComponent(r)}/${i==="issue"?"issues":"merge_requests"}/${n}?add_labels=Doing&remove_labels=To Do`;try{await this.api(o,{method:"PUT"})}catch(a){this.log(`[business] claim label update failed: ${a.message}`)}}async report(e,t){let{project:r,iid:n,type:i}=this.parseId(e),o=i==="issue"?"issues":"merge_requests";if(t.timeSeconds&&t.timeSeconds>0){let a=Math.max(1,Math.round(t.timeSeconds/60)),d=a>=60?`${Math.floor(a/60)}h${a%60?` ${a%60}m`:""}`:`${a}m`;try{await this.api(`/projects/${encodeURIComponent(r)}/${o}/${n}/add_spent_time?duration=${encodeURIComponent(d)}`,{method:"POST"})}catch(l){this.log(`[business] add_spent_time failed: ${l.message}`)}}if(t.status==="done")try{await this.api(`/projects/${encodeURIComponent(r)}/${o}/${n}?add_labels=Done&remove_labels=Doing`,{method:"PUT"})}catch(a){this.log(`[business] done label update failed: ${a.message}`)}else if(t.status==="blocked")try{await this.api(`/projects/${encodeURIComponent(r)}/${o}/${n}?add_labels=Blocked`,{method:"PUT"})}catch(a){this.log(`[business] blocked label update failed: ${a.message}`)}if(t.note)try{await this.api(`/projects/${encodeURIComponent(r)}/${o}/${n}/notes`,{method:"POST",body:JSON.stringify({body:t.note})})}catch(a){this.log(`[business] report note failed: ${a.message}`)}}async listAll(e={}){let t=e.sinceDays??30,r=new Date(Date.now()-t*24*36e5).toISOString(),n=new URLSearchParams,i=e.state??"opened";n.set("state",i),n.set("updated_after",r),n.set("scope","all"),n.set("per_page","100"),n.set("with_labels_details","true"),n.set("order_by","updated_at"),n.set("sort","desc"),e.labels?.length&&n.set("labels",e.labels.join(",")),e.milestone&&n.set("milestone",e.milestone),e.search&&n.set("search",e.search);let o=this.projects.length?this.projects:["__ALL__"],a=[];for(let l of o)try{let u=l==="__ALL__"?`/issues?${n.toString()}`:`/projects/${encodeURIComponent(l)}/issues?${n.toString()}`,p=await this.api(u);for(let m of p)a.push(this.mapIssue(m,l))}catch(u){this.log(`[business] GitLab listAll failed on ${l}: ${u.message}`)}let d=a;if(e.notLabels?.length){let l=new Set(e.notLabels);d=d.filter(u=>!(u.labels||[]).some(p=>l.has(p)))}if(e.withoutScopedPrefix){let l=e.withoutScopedPrefix+"::";d=d.filter(u=>!(u.labels||[]).some(p=>p.startsWith(l)))}return d}async setState(e,t){let{project:r,iid:n,type:i}=this.parseId(e),o=i==="issue"?"issues":"merge_requests";await this.api(`/projects/${encodeURIComponent(r)}/${o}/${n}?state_event=${t}`,{method:"PUT"})}async getItem(e){let{project:t,iid:r}=this.parseId(e);try{let n=await this.api(`/projects/${encodeURIComponent(t)}/issues/${r}?with_labels_details=true`);return{...this.mapIssue(n,t),descriptionHtml:n.description_html||void 0,createdAt:n.created_at,author:n.author?{username:n.author.username,name:n.author.name,avatarUrl:n.author.avatar_url}:void 0,allLabels:Array.isArray(n.labels)?typeof n.labels[0]=="object"?n.labels.map(o=>o.name):n.labels:[]}}catch(n){return this.log(`[board] GitLab getItem failed for ${e}: ${n.message}`),null}}async updateItem(e,t){let{project:r,iid:n}=this.parseId(e),i;t.assigneeUsernames&&(i=(await Promise.all(t.assigneeUsernames.map(async u=>{try{let p=await this.api(`/users?username=${encodeURIComponent(u)}`);return Array.isArray(p)&&p[0]?.id?p[0].id:null}catch{return null}}))).filter(u=>typeof u=="number"));let o;if(t.milestoneTitle!==void 0)if(t.milestoneTitle===null||t.milestoneTitle==="")o=0;else try{let l=await this.api(`/projects/${encodeURIComponent(r)}/milestones?title=${encodeURIComponent(t.milestoneTitle)}`);Array.isArray(l)&&l[0]?.id&&(o=l[0].id)}catch(l){this.log(`[board] milestone lookup failed for "${t.milestoneTitle}": ${l.message}`)}let a={};t.title!==void 0&&(a.title=t.title),t.description!==void 0&&(a.description=t.description),t.labels!==void 0&&(a.labels=t.labels.join(",")),t.addLabels?.length&&(a.add_labels=t.addLabels.join(",")),t.removeLabels?.length&&(a.remove_labels=t.removeLabels.join(",")),i!==void 0&&(a.assignee_ids=i),o!==void 0&&(a.milestone_id=o);let d=await this.api(`/projects/${encodeURIComponent(r)}/issues/${n}`,{method:"PUT",body:JSON.stringify(a)});return this.mapIssue(d,r)}async listMembers(){let e=new Map;for(let t of this.projects)try{let r=await this.api(`/projects/${encodeURIComponent(t)}/members/all?per_page=100`);for(let n of r)n.username&&!e.has(n.username)&&e.set(n.username,{username:n.username,name:n.name,avatarUrl:n.avatar_url})}catch(r){this.log(`[board] listMembers failed for ${t}: ${r.message}`)}return[...e.values()].sort((t,r)=>t.username.localeCompare(r.username))}async listMilestones(){let e=new Map;for(let r of this.projects)try{let n=await this.api(`/projects/${encodeURIComponent(r)}/milestones?per_page=100`);for(let i of n)i.title&&!e.has(i.title)&&e.set(i.title,{id:i.id,title:i.title,state:i.state,dueDate:i.due_date})}catch(n){this.log(`[board] listMilestones failed for ${r}: ${n.message}`)}let t=[...e.values()];return t.sort((r,n)=>r.state!==n.state?r.state==="active"?-1:1:r.title.localeCompare(n.title)),t}async create(e){let t=e.projectHint||this.projects[0];if(!t)throw new Error("GitLabWorkSource.create requires projectHint or configured projects");let r,n=e.assigneeUsernames?.length?e.assigneeUsernames:e.assignee?(this.agentUsernames[e.assignee]||[]).slice(0,1):[];n.length&&(r=(await Promise.all(n.map(async l=>{try{let u=await this.api(`/users?username=${encodeURIComponent(l)}`);return Array.isArray(u)&&u[0]?.id?u[0].id:null}catch(u){return this.log(`[board] user lookup failed for ${l}: ${u.message}`),null}}))).filter(l=>typeof l=="number"));let i;if(e.milestoneTitle)try{let d=await this.api(`/projects/${encodeURIComponent(t)}/milestones?title=${encodeURIComponent(e.milestoneTitle)}`);Array.isArray(d)&&d[0]?.id&&(i=d[0].id)}catch(d){this.log(`[board] milestone lookup failed for "${e.milestoneTitle}": ${d.message}`)}let o={title:e.title,description:e.description,labels:(e.labels||[]).join(",")};r?.length&&(o.assignee_ids=r),i!==void 0&&(o.milestone_id=i);let a=await this.api(`/projects/${encodeURIComponent(t)}/issues`,{method:"POST",body:JSON.stringify(o)});return this.mapIssue(a,t)}async transition(e,t,r){await this.transitionMany(e,t?[t]:[],r?[r]:[])}async transitionMany(e,t,r){let{project:n,iid:i,type:o}=this.parseId(e),a=o==="issue"?"issues":"merge_requests",d=new URLSearchParams;t.length&&d.set("add_labels",t.join(",")),r.length&&d.set("remove_labels",r.join(",")),d.toString()&&await this.api(`/projects/${encodeURIComponent(n)}/${a}/${i}?${d.toString()}`,{method:"PUT"})}parseId(e){let r=e.replace(/^gitlab:/,"").split(":"),n=r.pop(),i=r.pop()==="mr"?"mr":"issue";return{project:r.join(":"),type:i,iid:n}}};function Qn(s,e,t){let r=s.workSource;switch(r.type){case"backlog":return new oe(r.path);case"wiki":return new ie(r.path,r.glob);case"gitlab":{let n=e.channels.gitlab;if(!n?.token)throw new Error("business.workSource.type=gitlab requires channels.gitlab.token");let i={};for(let a of n.agentMappings)a.gitlabUsernames.length&&(i[a.agentId]=a.gitlabUsernames);let o=r.projects.length?r.projects:n.routes.map(a=>a.project);return new ae(n.host,n.token,o,i,t)}}}export{v as a,M as b,K as c,z as d,J as e,ee as f,Et as g,Ct as h,ae as i,Qn as j};
|
|
13
|
+
//# sourceMappingURL=chunk-4HP6UC2X.js.map
|