agentix-cli 0.13.0 → 0.15.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.
Files changed (82) hide show
  1. package/README.md +65 -478
  2. package/dist/agent-LRSSVFDR.js +2 -0
  3. package/dist/board-dashboard-DAPHL6W2.js +4508 -0
  4. package/dist/board-dashboard-DAPHL6W2.js.map +1 -0
  5. package/dist/chunk-3XULOWYB.js +199 -0
  6. package/dist/chunk-3XULOWYB.js.map +1 -0
  7. package/dist/chunk-6U2XOQHU.js +7 -0
  8. package/dist/chunk-6U2XOQHU.js.map +1 -0
  9. package/dist/{chunk-ZRYBSI5G.js → chunk-7MBTMCKY.js} +2 -2
  10. package/dist/{chunk-D6EDMQRV.js → chunk-AU37OREL.js} +5 -5
  11. package/dist/chunk-B4Q4X7D5.js +6 -0
  12. package/dist/chunk-B4Q4X7D5.js.map +1 -0
  13. package/dist/chunk-CXY2RYNI.js +11 -0
  14. package/dist/chunk-CXY2RYNI.js.map +1 -0
  15. package/dist/chunk-FIOB3MEX.js +2 -0
  16. package/dist/chunk-HLRVDCNH.js +5 -0
  17. package/dist/chunk-HLRVDCNH.js.map +1 -0
  18. package/dist/chunk-IEVEMKAI.js +168 -0
  19. package/dist/chunk-IEVEMKAI.js.map +1 -0
  20. package/dist/chunk-RB7265LO.js +12 -0
  21. package/dist/chunk-RB7265LO.js.map +1 -0
  22. package/dist/chunk-RGUYBH77.js +7 -0
  23. package/dist/chunk-RGUYBH77.js.map +1 -0
  24. package/dist/chunk-WVNSWBCU.js +138 -0
  25. package/dist/chunk-WVNSWBCU.js.map +1 -0
  26. package/dist/cli.js +64 -48
  27. package/dist/cli.js.map +1 -1
  28. package/dist/{compaction-7I3E5QUO.js → compaction-JBZZ7I7Y.js} +4 -4
  29. package/dist/config-DCJYECL4.js +2 -0
  30. package/dist/config-mutator-6EJHSP3Y.js +2 -0
  31. package/dist/debug-3MEUO2P2.js +2 -0
  32. package/dist/{heal-OTGT5HHJ.js → heal-WKY5I4KB.js} +2 -2
  33. package/dist/index.d.ts +894 -70
  34. package/dist/index.js +11 -11
  35. package/dist/index.js.map +1 -1
  36. package/dist/loader-PTCXLJEX.js +2 -0
  37. package/dist/memory-extract-AD7KMRPC.js +2 -0
  38. package/dist/{providers-G3WZ4RVJ.js → providers-VUIWO3ZK.js} +2 -2
  39. package/dist/registry-5ND7GLFJ.js +2 -0
  40. package/dist/scheduler-FCXOXCN5.js +2 -0
  41. package/dist/scheduler-FCXOXCN5.js.map +1 -0
  42. package/dist/{subagent-WV7QKQ3L.js → subagent-2SJHSSJS.js} +2 -2
  43. package/dist/{usage-dashboard-2TJQBFSH.js → usage-dashboard-QX3CF6KK.js} +8 -8
  44. package/dist/usage-dashboard-QX3CF6KK.js.map +1 -0
  45. package/dist/whatsapp-state-6D6FHHD2.js +2 -0
  46. package/dist/whatsapp-state-6D6FHHD2.js.map +1 -0
  47. package/dist/workspace-setup-ZWTT42XT.js +2 -0
  48. package/dist/workspace-setup-ZWTT42XT.js.map +1 -0
  49. package/package.json +10 -1
  50. package/dist/agent-5KIMJI3F.js +0 -2
  51. package/dist/chunk-34UAFPK4.js +0 -10
  52. package/dist/chunk-34UAFPK4.js.map +0 -1
  53. package/dist/chunk-4YCH6IZV.js +0 -2
  54. package/dist/chunk-CJ45Y2IR.js +0 -12
  55. package/dist/chunk-CJ45Y2IR.js.map +0 -1
  56. package/dist/chunk-T25UEXVK.js +0 -126
  57. package/dist/chunk-T25UEXVK.js.map +0 -1
  58. package/dist/chunk-VH23CDHL.js +0 -157
  59. package/dist/chunk-VH23CDHL.js.map +0 -1
  60. package/dist/chunk-WJ6ZF6EZ.js +0 -6
  61. package/dist/chunk-WJ6ZF6EZ.js.map +0 -1
  62. package/dist/config-HAD5E4YX.js +0 -2
  63. package/dist/debug-N3B5NVJU.js +0 -2
  64. package/dist/loader-YQE6XJBT.js +0 -2
  65. package/dist/memory-extract-RGPUPMKU.js +0 -2
  66. package/dist/registry-CDWY7CLL.js +0 -2
  67. package/dist/usage-dashboard-2TJQBFSH.js.map +0 -1
  68. package/dist/workspace-setup-QQTCJY4B.js +0 -2
  69. /package/dist/{agent-5KIMJI3F.js.map → agent-LRSSVFDR.js.map} +0 -0
  70. /package/dist/{chunk-ZRYBSI5G.js.map → chunk-7MBTMCKY.js.map} +0 -0
  71. /package/dist/{chunk-D6EDMQRV.js.map → chunk-AU37OREL.js.map} +0 -0
  72. /package/dist/{chunk-4YCH6IZV.js.map → chunk-FIOB3MEX.js.map} +0 -0
  73. /package/dist/{compaction-7I3E5QUO.js.map → compaction-JBZZ7I7Y.js.map} +0 -0
  74. /package/dist/{config-HAD5E4YX.js.map → config-DCJYECL4.js.map} +0 -0
  75. /package/dist/{debug-N3B5NVJU.js.map → config-mutator-6EJHSP3Y.js.map} +0 -0
  76. /package/dist/{heal-OTGT5HHJ.js.map → debug-3MEUO2P2.js.map} +0 -0
  77. /package/dist/{loader-YQE6XJBT.js.map → heal-WKY5I4KB.js.map} +0 -0
  78. /package/dist/{memory-extract-RGPUPMKU.js.map → loader-PTCXLJEX.js.map} +0 -0
  79. /package/dist/{providers-G3WZ4RVJ.js.map → memory-extract-AD7KMRPC.js.map} +0 -0
  80. /package/dist/{registry-CDWY7CLL.js.map → providers-VUIWO3ZK.js.map} +0 -0
  81. /package/dist/{workspace-setup-QQTCJY4B.js.map → registry-5ND7GLFJ.js.map} +0 -0
  82. /package/dist/{subagent-WV7QKQ3L.js.map → subagent-2SJHSSJS.js.map} +0 -0
@@ -1,4 +1,4 @@
1
- import{a as Ce,b as Se,c as ge}from"./chunk-SFQUP3BP.js";import{d as X,h as p}from"./chunk-CJ45Y2IR.js";import{a as de,c as ue,g as me}from"./chunk-KXZMYLHQ.js";import{a as F,b as ce,f as pe}from"./chunk-M7HKBG3V.js";import{a as Te,d as fe}from"./chunk-MCJUAE6V.js";import{e as v}from"./chunk-DSYKYZMT.js";import{b as he,c as ye}from"./chunk-QOYAX2FT.js";import{z as R}from"zod";var Re=R.object({provider:R.enum(["claude-code","claude","openai","ollama","custom"]).default("claude-code"),model:R.string().optional(),apiKey:R.string().optional(),skills:R.array(R.string()).default([]),output:R.object({dir:R.string().default("./generated")}).default({}),context7:R.object({enabled:R.boolean().default(!0),apiKey:R.string().optional()}).default({}),agentic:R.object({maxIterations:R.number().default(20),enabledTools:R.array(R.string()).default(["create_files","ask_user","read_file","search_files","list_directory","run_command","edit_file"]),disabledTools:R.array(R.string()).default([])}).default({})}),dt=["component","page","api","website","document","script","config","skill","media","report","test","workflow","schema","email","diagram","auto"],ut={component:"UI component (any framework)",page:"Full page or screen",api:"API endpoint, route handler, or service",website:"Multi-page website or app",document:"Markdown, documentation, or specification",script:"Standalone script or utility",config:"Configuration file or setup",skill:"Agent skill (SKILL.md format for skills.sh)",media:"Media generation prompt (image/audio/video description)",report:"Analysis report or audit",test:"Test suite, test fixtures, or test data",workflow:"CI/CD pipeline, GitHub Actions, or automation",schema:"Database schema, Zod validators, or GraphQL types",email:"Email template (React Email, MJML, HTML)",diagram:"Mermaid, D2, or PlantUML diagram",auto:"Auto-detect the best output type"};import{existsSync as Ye,promises as Je}from"fs";import B from"path";import Qe from"fast-glob";var Ae={"prisma/schema.prisma":{type:"prisma",category:"database"},"drizzle/schema.ts":{type:"drizzle",category:"database"},"schema.graphql":{type:"graphql",category:"api"},"schema.gql":{type:"graphql",category:"api"},"openapi.yaml":{type:"openapi",category:"api"},"openapi.json":{type:"openapi",category:"api"},"swagger.yaml":{type:"openapi",category:"api"},"swagger.json":{type:"openapi",category:"api"}};async function Me(o){let e={},t=await Qe.glob("**/*",{cwd:o,deep:4,ignore:["**/node_modules/**","**/dist/**","**/build/**","**/.next/**","**/target/**","**/__pycache__/**","**/vendor/**","**/.git/**"],onlyFiles:!0});for(let[s,i]of Object.entries(Ae)){let l=t.find(a=>a.endsWith(s)||a===s);if(l&&i.category==="database"){let a=await K(B.resolve(o,l));if(a){e.database={type:i.type,content:Z(a,3e3),tables:Ve(a,i.type)};break}}}for(let[s,i]of Object.entries(Ae)){let l=t.find(a=>a.endsWith(s)||a===s);if(l&&i.category==="api"){let a=await K(B.resolve(o,l));if(a){e.api={type:i.type,content:Z(a,3e3)};break}}}if(!e.api){let s=t.find(i=>i.includes("trpc")&&(i.endsWith("router.ts")||i.endsWith("router.js")));if(s){let i=await K(B.resolve(o,s));i&&(e.api={type:"trpc",content:Z(i,3e3)})}}let n=t.find(s=>s===".env.example"||s===".env.local.example"||s===".env.template");if(n){let s=await K(B.resolve(o,n));s&&(e.env=Xe(s))}let r=t.filter(s=>(s.includes("models")||s.includes("types")||s.includes("schemas"))&&(s.endsWith(".ts")||s.endsWith(".py")||s.endsWith(".rs")||s.endsWith(".go")));if(r.length){e.models=[];for(let s of r.slice(0,5)){let i=await K(B.resolve(o,s));i&&e.models.push({path:s,content:Z(i,2e3),type:B.extname(s).slice(1)})}}return e}function Ve(o,e){if(e==="prisma"){let t=o.match(/model\s+(\w+)\s*\{/g);return t?t.map(n=>n.replace(/model\s+/,"").replace(/\s*\{/,"")):[]}return[]}function Xe(o){return{variables:o.split(`
1
+ import{a as Ce,b as Se,c as ge}from"./chunk-SFQUP3BP.js";import{d as X,h as p}from"./chunk-RB7265LO.js";import{a as de,c as ue,g as me}from"./chunk-KXZMYLHQ.js";import{a as F,b as ce,f as pe}from"./chunk-M7HKBG3V.js";import{a as Te,d as fe}from"./chunk-MCJUAE6V.js";import{e as v}from"./chunk-DSYKYZMT.js";import{b as he,c as ye}from"./chunk-QOYAX2FT.js";import{z as R}from"zod";var Re=R.object({provider:R.enum(["claude-code","claude","openai","ollama","custom"]).default("claude-code"),model:R.string().optional(),apiKey:R.string().optional(),skills:R.array(R.string()).default([]),output:R.object({dir:R.string().default("./generated")}).default({}),context7:R.object({enabled:R.boolean().default(!0),apiKey:R.string().optional()}).default({}),agentic:R.object({maxIterations:R.number().default(20),enabledTools:R.array(R.string()).default(["create_files","ask_user","read_file","search_files","list_directory","run_command","edit_file"]),disabledTools:R.array(R.string()).default([])}).default({})}),dt=["component","page","api","website","document","script","config","skill","media","report","test","workflow","schema","email","diagram","auto"],ut={component:"UI component (any framework)",page:"Full page or screen",api:"API endpoint, route handler, or service",website:"Multi-page website or app",document:"Markdown, documentation, or specification",script:"Standalone script or utility",config:"Configuration file or setup",skill:"Agent skill (SKILL.md format for skills.sh)",media:"Media generation prompt (image/audio/video description)",report:"Analysis report or audit",test:"Test suite, test fixtures, or test data",workflow:"CI/CD pipeline, GitHub Actions, or automation",schema:"Database schema, Zod validators, or GraphQL types",email:"Email template (React Email, MJML, HTML)",diagram:"Mermaid, D2, or PlantUML diagram",auto:"Auto-detect the best output type"};import{existsSync as Ye,promises as Je}from"fs";import B from"path";import Qe from"fast-glob";var Ae={"prisma/schema.prisma":{type:"prisma",category:"database"},"drizzle/schema.ts":{type:"drizzle",category:"database"},"schema.graphql":{type:"graphql",category:"api"},"schema.gql":{type:"graphql",category:"api"},"openapi.yaml":{type:"openapi",category:"api"},"openapi.json":{type:"openapi",category:"api"},"swagger.yaml":{type:"openapi",category:"api"},"swagger.json":{type:"openapi",category:"api"}};async function Me(o){let e={},t=await Qe.glob("**/*",{cwd:o,deep:4,ignore:["**/node_modules/**","**/dist/**","**/build/**","**/.next/**","**/target/**","**/__pycache__/**","**/vendor/**","**/.git/**"],onlyFiles:!0});for(let[s,i]of Object.entries(Ae)){let l=t.find(a=>a.endsWith(s)||a===s);if(l&&i.category==="database"){let a=await K(B.resolve(o,l));if(a){e.database={type:i.type,content:Z(a,3e3),tables:Ve(a,i.type)};break}}}for(let[s,i]of Object.entries(Ae)){let l=t.find(a=>a.endsWith(s)||a===s);if(l&&i.category==="api"){let a=await K(B.resolve(o,l));if(a){e.api={type:i.type,content:Z(a,3e3)};break}}}if(!e.api){let s=t.find(i=>i.includes("trpc")&&(i.endsWith("router.ts")||i.endsWith("router.js")));if(s){let i=await K(B.resolve(o,s));i&&(e.api={type:"trpc",content:Z(i,3e3)})}}let n=t.find(s=>s===".env.example"||s===".env.local.example"||s===".env.template");if(n){let s=await K(B.resolve(o,n));s&&(e.env=Xe(s))}let r=t.filter(s=>(s.includes("models")||s.includes("types")||s.includes("schemas"))&&(s.endsWith(".ts")||s.endsWith(".py")||s.endsWith(".rs")||s.endsWith(".go")));if(r.length){e.models=[];for(let s of r.slice(0,5)){let i=await K(B.resolve(o,s));i&&e.models.push({path:s,content:Z(i,2e3),type:B.extname(s).slice(1)})}}return e}function Ve(o,e){if(e==="prisma"){let t=o.match(/model\s+(\w+)\s*\{/g);return t?t.map(n=>n.replace(/model\s+/,"").replace(/\s*\{/,"")):[]}return[]}function Xe(o){return{variables:o.split(`
2
2
  `).filter(n=>n.trim()&&!n.trim().startsWith("#")).map(n=>{let[r]=n.split("="),s=r.trim(),i=n.includes("=")&&n.split("=")[1]?.trim().length>0;return{key:s,required:!i}})}}async function K(o){try{return Ye(o)?await Je.readFile(o,"utf8"):null}catch{return null}}function Z(o,e){return o.length<=e?o:o.slice(0,e)+`
3
3
  ... (truncated)`}function Ie(o){let e=[];if(o.database&&e.push(`## Database Schema (${o.database.type})
4
4
  `+(o.database.tables?.length?`Tables: ${o.database.tables.join(", ")}
@@ -38,7 +38,7 @@ ${s.join(`
38
38
  ... (output truncated)`:i;return s.exitCode!==0?{tool_use_id:e.id,content:`Command exited with code ${s.exitCode}:
39
39
  ${l}`,is_error:!0}:{tool_use_id:e.id,content:l||"(no output)"}}async editFile(e){let t=String(e.input.path||""),n=e.input.edits,r=re.resolve(this.cwd,t);if(!n||n.length===0)return{tool_use_id:e.id,content:"No edits provided.",is_error:!0};let s=await N.checkFileWrite(t);if(s==="deny")return{tool_use_id:e.id,content:`File write blocked by permissions: ${t}`,is_error:!0};if(s==="skip")return{tool_use_id:e.id,content:`File write skipped (plan mode): ${t}`};if(p.has("pre:file-write")){let a=await p.execute("pre:file-write",{event:"pre:file-write",file:r,cwd:this.cwd});if(a.blocked)return{tool_use_id:e.id,content:a.message||`File edit blocked by pre:file-write hook: ${t}`,is_error:!0}}let i=await J.readFile(r,"utf8"),l=[];for(let a of n)i.includes(a.old_text)?(i=i.replace(a.old_text,a.new_text),l.push(`Replaced: "${a.old_text.slice(0,40)}..."`)):l.push(`Not found: "${a.old_text.slice(0,40)}..."`);return this.options.dryRun||await J.writeFile(r,i,"utf8"),p.has("post:file-write")&&await p.execute("post:file-write",{event:"post:file-write",file:r,fileContent:i,cwd:this.cwd}),{tool_use_id:e.id,content:`Edited ${t}:
40
40
  ${l.join(`
41
- `)}`}}async createFiles(e){let t=e.input,n=t.files||[];return{tool_use_id:e.id,content:t.summary||`Queued ${n.length} file(s) for creation.`,files:n}}async spawnAgent(e){let t=String(e.input.agent_id||""),n=String(e.input.prompt||""),r=Number(e.input.timeout_seconds)||300;if(!t||!n)return{tool_use_id:e.id,content:"agent_id and prompt are required.",is_error:!0};try{let{SubAgentManager:s}=await import("./subagent-WV7QKQ3L.js"),{getGlobalRegistry:i}=await import("./registry-CDWY7CLL.js"),l=i();if(!l)return{tool_use_id:e.id,content:"Sub-agent spawning requires the daemon to be running.",is_error:!0};let h=await new s(l).spawn({targetAgentId:t,prompt:n,parentAgentId:"orchestrator",timeout:r*1e3});return h.success?{tool_use_id:e.id,content:`Sub-agent "${t}" completed (${Math.round(h.duration/1e3)}s):
41
+ `)}`}}async createFiles(e){let t=e.input,n=t.files||[];return{tool_use_id:e.id,content:t.summary||`Queued ${n.length} file(s) for creation.`,files:n}}async spawnAgent(e){let t=String(e.input.agent_id||""),n=String(e.input.prompt||""),r=Number(e.input.timeout_seconds)||300;if(!t||!n)return{tool_use_id:e.id,content:"agent_id and prompt are required.",is_error:!0};try{let{SubAgentManager:s}=await import("./subagent-2SJHSSJS.js"),{getGlobalRegistry:i}=await import("./registry-5ND7GLFJ.js"),l=i();if(!l)return{tool_use_id:e.id,content:"Sub-agent spawning requires the daemon to be running.",is_error:!0};let h=await new s(l).spawn({targetAgentId:t,prompt:n,parentAgentId:"orchestrator",timeout:r*1e3});return h.success?{tool_use_id:e.id,content:`Sub-agent "${t}" completed (${Math.round(h.duration/1e3)}s):
42
42
 
43
43
  ${h.content}`}:{tool_use_id:e.id,content:`Sub-agent "${t}" failed: ${h.error}`,is_error:!0}}catch(s){return{tool_use_id:e.id,content:`Failed to spawn sub-agent: ${s.message}`,is_error:!0}}}async askUser(e){let t=String(e.input.question||""),n=e.input.options,r=t;return n?.length&&(r+=`
44
44
  Options: ${n.join(", ")}`),{tool_use_id:e.id,content:"Question sent to user.",followUp:r}}};async function ve(o){let{provider:e,systemPrompt:t,messages:n,providerOptions:r,cwd:s,maxIterations:i=20,enabledTools:l,interactive:a=!0,overwrite:h=!1,dryRun:c=!1,onProgress:f}=o;if(!e.generateRaw)return We(o);let x=new Q(s,{interactive:a,overwrite:h,dryRun:c}),b=de(l),$=n.filter(k=>k.role!=="system").map(k=>({role:k.role,content:k.content})),G=[],C=0,S="",O,M=0;for(;M<i;){M++,f?.({type:"iteration_start",iteration:M}),v.step(M,`Agentic loop iteration (${b.length} tools available)`);let k;try{k=await e.generateRaw($,t,b,r)}catch(y){if(y.message?.includes("not available"))return We(o);throw y}C+=k.usage.input_tokens+k.usage.output_tokens;for(let y of k.content)y.type==="text"&&(S+=y.text,f?.({type:"text_delta",text:y.text}));if(k.stop_reason==="end_turn"||k.stop_reason==="max_tokens")break;if(k.stop_reason==="tool_use"){let y=k.content.filter(_=>_.type==="tool_use");if(y.length===0)break;$.push({role:"assistant",content:k.content});let I=[];for(let _ of y){f?.({type:"tool_call",name:_.name,id:_.id,input:_.input}),v.step(M,`Tool call: ${_.name}`);let w=await x.execute({name:_.name,id:_.id,input:_.input});f?.({type:"tool_result",name:_.name,id:_.id,content:w.content.slice(0,200),is_error:w.is_error}),w.files?.length&&(G.push(...w.files),f?.({type:"files_created",files:w.files})),w.followUp&&(O=w.followUp),I.push({type:"tool_result",tool_use_id:w.tool_use_id,content:w.content,is_error:w.is_error})}if($.push({role:"user",content:I}),O)break;continue}break}return f?.({type:"complete",iterations:M,totalTokens:C}),{files:G,content:S,followUp:O,tokensUsed:C,iterations:M}}async function We(o){let{provider:e,systemPrompt:t,messages:n,providerOptions:r,maxIterations:s=5}=o,l=[{role:"system",content:t+`
@@ -47,7 +47,7 @@ Options: ${n.join(", ")}`),{tool_use_id:e.id,content:"Question sent to user.",fo
47
47
  `);l.push({role:"assistant",content:b.content+(G?`
48
48
 
49
49
  Files created:
50
- ${G}`:"")}),l.push({role:"user",content:"Continue generating the remaining files. Build on what you've already created. When finished, do not include [CONTINUE] in your response."})}return{files:a,content:c,followUp:f,tokensUsed:h,iterations:x}}function _e(o){return typeof o.generateRaw=="function"}var se=class{queue=[];resolvers=[];closed=!1;push(e){if(this.closed)return;let t=this.resolvers.shift();if(t){t({value:e,done:!1});return}this.queue.push(e)}close(){if(!this.closed){this.closed=!0;for(let e of this.resolvers.splice(0))e({value:void 0,done:!0})}}async next(){return this.queue.length?{value:this.queue.shift(),done:!1}:this.closed?{value:void 0,done:!0}:new Promise(e=>{this.resolvers.push(e)})}async*[Symbol.asyncIterator](){for(;;){let{value:e,done:t}=await this.next();if(t)return;yield e}}};async function ze(o,e,t){let n=Re.parse(t||{}),r=new Y(o);await r.load();let[s,i,l]=await Promise.all([Ce(o),Me(o),Te(o)]),a="";if(n.context7.enabled)try{a=await Oe(s,e,n.context7.apiKey)}catch{}let h=r.buildMemoryContext(e),c=ke(o);return v.context("memory",h?"loaded":"empty"),v.context("instructions",c?"loaded from project":"none"),{techStack:s,schemas:i,skills:l,docs:a,config:n,memoryContext:h,projectInstructions:c}}async function Uo(o){let{task:e,cwd:t,overwrite:n=!1,dryRun:r=!1,provider:s="claude-code",model:i,apiKey:l,context7:a=!0,interactive:h=!0}=o,c=e;if(p.has("pre:prompt")){let u=await p.execute("pre:prompt",{event:"pre:prompt",task:e,cwd:t});if(u.blocked)throw new Error(u.message||"Blocked by pre:prompt hook");u.modified?.task&&(c=String(u.modified.task))}if(p.has("pre:generate")){let u=await p.execute("pre:generate",{event:"pre:generate",task:c,cwd:t});if(u.blocked)throw new Error(u.message||"Blocked by pre:generate hook")}F.info("Analyzing project...");let f=await ze(t,c,{provider:s,context7:{enabled:a,apiKey:l}}),x=we(o.outputType,c);F.info(`Output type: ${x}`);let b=fe(f.skills,c,x);b.length&&F.info(`Loaded ${b.length} relevant skill(s): ${b.map(u=>u.skill.frontmatter.name).join(", ")}`);let $=He(f,x,b.map(u=>u.skill));if(!await pe(l))throw new Error("No credentials configured. Run `agentx model` to set up.");let C=me(s,l),S=i||ce()?.model,O=[...o.sessionMessages||[],{role:"user",content:c}];F.info("Generating...");let M=_e(C),k=o.maxSteps??(M?20:5),y=await ve({provider:C,systemPrompt:$,messages:[{role:"system",content:$},...O],providerOptions:{model:S,maxTokens:8192},cwd:t,maxIterations:k,enabledTools:f.config.agentic.enabledTools.filter(u=>!f.config.agentic.disabledTools.includes(u)),interactive:h,overwrite:n,dryRun:r,onProgress:u=>{u.type==="iteration_start"&&u.iteration>1&&F.info(`Step ${u.iteration}/${k}...`),u.type==="tool_call"&&v.step(0,`Tool: ${u.name}`)}});if(y.tokensUsed){let u=S||"claude-sonnet-4-20250514",L=Math.round(y.tokensUsed*.3),ie=y.tokensUsed-L;X.recordStep(1,u,L,ie)}let{content:I}=y,{followUp:_,tokensUsed:w}=y;if(y.iterations>1&&F.info(`Completed in ${y.iterations} step(s)`),p.has("post:response")){let u=await p.execute("post:response",{event:"post:response",content:I,task:c,cwd:t});if(u.blocked)throw new Error(u.message||"Blocked by post:response hook");u.modified?.content&&(I=String(u.modified.content))}if(_&&h)return{files:{written:[],skipped:[],errors:[]},content:I,outputType:x,followUp:_,tokensUsed:w};let U=new Map;for(let u of y.files)U.set(u.path,u);let A=ne(x,f.techStack,o.outputDir),T=await oe(Array.from(U.values()),{cwd:t,overwrite:n,dryRun:r,outputDir:A}),j;if(o.heal!==!1&&!r&&T.written.length>0){let{HealEngine:u}=await import("./heal-OTGT5HHJ.js");j=await new u(t,{enabled:!0,testCommand:o.healConfig?.testCommand,buildCommand:o.healConfig?.buildCommand,lintCommand:o.healConfig?.lintCommand,maxAttempts:o.healConfig?.maxAttempts??3,provider:s,model:S,apiKey:l}).detectAndHeal(T.written,c),!j.healed&&j.error&&await p.execute("on:error",{event:"on:error",error:new Error(j.error),task:c,cwd:t})}return p.has("post:generate")&&await p.execute("post:generate",{event:"post:generate",task:c,content:I,cwd:t}),{files:T,content:I,outputType:x,tokensUsed:w,healResult:j}}async function*Eo(o){let{task:e,cwd:t,overwrite:n=!1,dryRun:r=!1,provider:s="claude-code",model:i,apiKey:l,context7:a=!0,interactive:h=!0}=o,c=e;if(p.has("pre:prompt")){let d=await p.execute("pre:prompt",{event:"pre:prompt",task:e,cwd:t});if(d.blocked){yield{type:"error",error:d.message||"Blocked by pre:prompt hook"};return}d.modified?.task&&(c=String(d.modified.task))}if(p.has("pre:generate")){let d=await p.execute("pre:generate",{event:"pre:generate",task:c,cwd:t});if(d.blocked){yield{type:"error",error:d.message||"Blocked by pre:generate hook"};return}}let f=await ze(t,c,{provider:s,context7:{enabled:a,apiKey:l}}),x=we(o.outputType,c);yield{type:"context_ready",outputType:x};let b=fe(f.skills,c,x),$=He(f,x,b.map(d=>d.skill));if(!await pe(l)){yield{type:"error",error:"No credentials configured. Run `agentx model` to set up."};return}let C=me(s,l),S=i||ce()?.model,O=_e(C),M=o.maxSteps??(O?20:5),k=[...o.sessionMessages||[],{role:"user",content:c}];if(O){let d=new se,P,g,q=(async()=>{try{P=await ve({provider:C,systemPrompt:$,messages:[{role:"system",content:$},...k],providerOptions:{model:S,maxTokens:8192},cwd:t,maxIterations:M,enabledTools:f.config.agentic.enabledTools.filter(m=>!f.config.agentic.disabledTools.includes(m)),interactive:h,overwrite:n,dryRun:r,onProgress:m=>{m.type==="text_delta"&&d.push({type:"text_delta",text:m.text}),m.type==="iteration_start"&&d.push({type:"iteration",iteration:m.iteration}),m.type==="tool_call"&&d.push({type:"tool_call",name:m.name,id:m.id}),m.type==="tool_result"&&d.push({type:"tool_result",name:m.name,id:m.id,is_error:m.is_error}),m.type==="files_created"&&d.push({type:"step_complete",step:0,filesCount:m.files.length})}})}catch(m){g=m}finally{d.close()}})();for await(let m of d)yield m;if(await q,g){yield{type:"error",error:g instanceof Error?g.message:String(g)};return}if(!P){yield{type:"error",error:"Agentic loop failed without a result"};return}yield{type:"done",result:{content:P.content,files:P.files,tokensUsed:P.tokensUsed,followUp:P.followUp}};let{content:E}=P;if(p.has("post:response")){let m=await p.execute("post:response",{event:"post:response",content:E,task:c,cwd:t});if(m.blocked){yield{type:"error",error:m.message||"Blocked by post:response hook"};return}m.modified?.content&&(E=String(m.modified.content))}if(P.followUp&&h){yield{type:"generate_result",result:{files:{written:[],skipped:[],errors:[]},content:E,outputType:x,followUp:P.followUp,tokensUsed:P.tokensUsed}};return}let z=new Map;for(let m of P.files)z.set(m.path,m);let le=ne(x,f.techStack,o.outputDir),V=await oe(Array.from(z.values()),{cwd:t,overwrite:n,dryRun:r,outputDir:le}),H;if(o.heal!==!1&&!r&&V.written.length>0){let{HealEngine:m}=await import("./heal-OTGT5HHJ.js");H=await new m(t,{enabled:!0,testCommand:o.healConfig?.testCommand,buildCommand:o.healConfig?.buildCommand,lintCommand:o.healConfig?.lintCommand,maxAttempts:o.healConfig?.maxAttempts??3,provider:s,model:S,apiKey:l}).detectAndHeal(V.written,c)}p.has("post:generate")&&await p.execute("post:generate",{event:"post:generate",task:c,content:E,cwd:t}),yield{type:"generate_result",result:{files:V,content:E,outputType:x,tokensUsed:P.tokensUsed,healResult:H}};return}let y=[{role:"system",content:$},...k],I=[],_=0,w="",U,A=0;A++,v.step(A,`Starting generation (model: ${S||"default"})`);let T;if(C.stream){let d="",P;for await(let g of C.stream(y,{model:S,maxTokens:8192}))yield g,g.type==="text_delta"&&(d+=g.text),g.type==="done"&&(P=g.result);T=P||{content:d,files:[],tokensUsed:0}}else{let d=await C.generate(y,{model:S,maxTokens:8192});d.content&&(yield{type:"text_delta",text:d.content}),yield{type:"done",result:d},T=d}_+=T.tokensUsed||0,w=T.content;let j=T.tokensUsed||0,u=S||"claude-sonnet-4-20250514",L=Math.round(j*.3),ie=j-L;if(X.recordStep(A,u,L,ie),v.api("generate",u,j),v.step(A,`Generated ${T.files.length} file(s), ${j} tokens`),T.files.length&&I.push(...T.files),T.followUp&&(U=T.followUp),yield{type:"step_complete",step:A,filesCount:T.files.length},!U&&(w.includes("[CONTINUE]")||w.includes("Next, I'll")||w.includes("Now let me")||w.includes("I'll also generate"))&&T.files.length>0){let P=T.files.map(g=>`Created: ${g.path}${g.description?` \u2014 ${g.description}`:""}`).join(`
50
+ ${G}`:"")}),l.push({role:"user",content:"Continue generating the remaining files. Build on what you've already created. When finished, do not include [CONTINUE] in your response."})}return{files:a,content:c,followUp:f,tokensUsed:h,iterations:x}}function _e(o){return typeof o.generateRaw=="function"}var se=class{queue=[];resolvers=[];closed=!1;push(e){if(this.closed)return;let t=this.resolvers.shift();if(t){t({value:e,done:!1});return}this.queue.push(e)}close(){if(!this.closed){this.closed=!0;for(let e of this.resolvers.splice(0))e({value:void 0,done:!0})}}async next(){return this.queue.length?{value:this.queue.shift(),done:!1}:this.closed?{value:void 0,done:!0}:new Promise(e=>{this.resolvers.push(e)})}async*[Symbol.asyncIterator](){for(;;){let{value:e,done:t}=await this.next();if(t)return;yield e}}};async function ze(o,e,t){let n=Re.parse(t||{}),r=new Y(o);await r.load();let[s,i,l]=await Promise.all([Ce(o),Me(o),Te(o)]),a="";if(n.context7.enabled)try{a=await Oe(s,e,n.context7.apiKey)}catch{}let h=r.buildMemoryContext(e),c=ke(o);return v.context("memory",h?"loaded":"empty"),v.context("instructions",c?"loaded from project":"none"),{techStack:s,schemas:i,skills:l,docs:a,config:n,memoryContext:h,projectInstructions:c}}async function Uo(o){let{task:e,cwd:t,overwrite:n=!1,dryRun:r=!1,provider:s="claude-code",model:i,apiKey:l,context7:a=!0,interactive:h=!0}=o,c=e;if(p.has("pre:prompt")){let u=await p.execute("pre:prompt",{event:"pre:prompt",task:e,cwd:t});if(u.blocked)throw new Error(u.message||"Blocked by pre:prompt hook");u.modified?.task&&(c=String(u.modified.task))}if(p.has("pre:generate")){let u=await p.execute("pre:generate",{event:"pre:generate",task:c,cwd:t});if(u.blocked)throw new Error(u.message||"Blocked by pre:generate hook")}F.info("Analyzing project...");let f=await ze(t,c,{provider:s,context7:{enabled:a,apiKey:l}}),x=we(o.outputType,c);F.info(`Output type: ${x}`);let b=fe(f.skills,c,x);b.length&&F.info(`Loaded ${b.length} relevant skill(s): ${b.map(u=>u.skill.frontmatter.name).join(", ")}`);let $=He(f,x,b.map(u=>u.skill));if(!await pe(l))throw new Error("No credentials configured. Run `agentx model` to set up.");let C=me(s,l),S=i||ce()?.model,O=[...o.sessionMessages||[],{role:"user",content:c}];F.info("Generating...");let M=_e(C),k=o.maxSteps??(M?20:5),y=await ve({provider:C,systemPrompt:$,messages:[{role:"system",content:$},...O],providerOptions:{model:S,maxTokens:8192},cwd:t,maxIterations:k,enabledTools:f.config.agentic.enabledTools.filter(u=>!f.config.agentic.disabledTools.includes(u)),interactive:h,overwrite:n,dryRun:r,onProgress:u=>{u.type==="iteration_start"&&u.iteration>1&&F.info(`Step ${u.iteration}/${k}...`),u.type==="tool_call"&&v.step(0,`Tool: ${u.name}`)}});if(y.tokensUsed){let u=S||"claude-sonnet-4-20250514",L=Math.round(y.tokensUsed*.3),ie=y.tokensUsed-L;X.recordStep(1,u,L,ie)}let{content:I}=y,{followUp:_,tokensUsed:w}=y;if(y.iterations>1&&F.info(`Completed in ${y.iterations} step(s)`),p.has("post:response")){let u=await p.execute("post:response",{event:"post:response",content:I,task:c,cwd:t});if(u.blocked)throw new Error(u.message||"Blocked by post:response hook");u.modified?.content&&(I=String(u.modified.content))}if(_&&h)return{files:{written:[],skipped:[],errors:[]},content:I,outputType:x,followUp:_,tokensUsed:w};let U=new Map;for(let u of y.files)U.set(u.path,u);let A=ne(x,f.techStack,o.outputDir),T=await oe(Array.from(U.values()),{cwd:t,overwrite:n,dryRun:r,outputDir:A}),j;if(o.heal!==!1&&!r&&T.written.length>0){let{HealEngine:u}=await import("./heal-WKY5I4KB.js");j=await new u(t,{enabled:!0,testCommand:o.healConfig?.testCommand,buildCommand:o.healConfig?.buildCommand,lintCommand:o.healConfig?.lintCommand,maxAttempts:o.healConfig?.maxAttempts??3,provider:s,model:S,apiKey:l}).detectAndHeal(T.written,c),!j.healed&&j.error&&await p.execute("on:error",{event:"on:error",error:new Error(j.error),task:c,cwd:t})}return p.has("post:generate")&&await p.execute("post:generate",{event:"post:generate",task:c,content:I,cwd:t}),{files:T,content:I,outputType:x,tokensUsed:w,healResult:j}}async function*Eo(o){let{task:e,cwd:t,overwrite:n=!1,dryRun:r=!1,provider:s="claude-code",model:i,apiKey:l,context7:a=!0,interactive:h=!0}=o,c=e;if(p.has("pre:prompt")){let d=await p.execute("pre:prompt",{event:"pre:prompt",task:e,cwd:t});if(d.blocked){yield{type:"error",error:d.message||"Blocked by pre:prompt hook"};return}d.modified?.task&&(c=String(d.modified.task))}if(p.has("pre:generate")){let d=await p.execute("pre:generate",{event:"pre:generate",task:c,cwd:t});if(d.blocked){yield{type:"error",error:d.message||"Blocked by pre:generate hook"};return}}let f=await ze(t,c,{provider:s,context7:{enabled:a,apiKey:l}}),x=we(o.outputType,c);yield{type:"context_ready",outputType:x};let b=fe(f.skills,c,x),$=He(f,x,b.map(d=>d.skill));if(!await pe(l)){yield{type:"error",error:"No credentials configured. Run `agentx model` to set up."};return}let C=me(s,l),S=i||ce()?.model,O=_e(C),M=o.maxSteps??(O?20:5),k=[...o.sessionMessages||[],{role:"user",content:c}];if(O){let d=new se,P,g,q=(async()=>{try{P=await ve({provider:C,systemPrompt:$,messages:[{role:"system",content:$},...k],providerOptions:{model:S,maxTokens:8192},cwd:t,maxIterations:M,enabledTools:f.config.agentic.enabledTools.filter(m=>!f.config.agentic.disabledTools.includes(m)),interactive:h,overwrite:n,dryRun:r,onProgress:m=>{m.type==="text_delta"&&d.push({type:"text_delta",text:m.text}),m.type==="iteration_start"&&d.push({type:"iteration",iteration:m.iteration}),m.type==="tool_call"&&d.push({type:"tool_call",name:m.name,id:m.id}),m.type==="tool_result"&&d.push({type:"tool_result",name:m.name,id:m.id,is_error:m.is_error}),m.type==="files_created"&&d.push({type:"step_complete",step:0,filesCount:m.files.length})}})}catch(m){g=m}finally{d.close()}})();for await(let m of d)yield m;if(await q,g){yield{type:"error",error:g instanceof Error?g.message:String(g)};return}if(!P){yield{type:"error",error:"Agentic loop failed without a result"};return}yield{type:"done",result:{content:P.content,files:P.files,tokensUsed:P.tokensUsed,followUp:P.followUp}};let{content:E}=P;if(p.has("post:response")){let m=await p.execute("post:response",{event:"post:response",content:E,task:c,cwd:t});if(m.blocked){yield{type:"error",error:m.message||"Blocked by post:response hook"};return}m.modified?.content&&(E=String(m.modified.content))}if(P.followUp&&h){yield{type:"generate_result",result:{files:{written:[],skipped:[],errors:[]},content:E,outputType:x,followUp:P.followUp,tokensUsed:P.tokensUsed}};return}let z=new Map;for(let m of P.files)z.set(m.path,m);let le=ne(x,f.techStack,o.outputDir),V=await oe(Array.from(z.values()),{cwd:t,overwrite:n,dryRun:r,outputDir:le}),H;if(o.heal!==!1&&!r&&V.written.length>0){let{HealEngine:m}=await import("./heal-WKY5I4KB.js");H=await new m(t,{enabled:!0,testCommand:o.healConfig?.testCommand,buildCommand:o.healConfig?.buildCommand,lintCommand:o.healConfig?.lintCommand,maxAttempts:o.healConfig?.maxAttempts??3,provider:s,model:S,apiKey:l}).detectAndHeal(V.written,c)}p.has("post:generate")&&await p.execute("post:generate",{event:"post:generate",task:c,content:E,cwd:t}),yield{type:"generate_result",result:{files:V,content:E,outputType:x,tokensUsed:P.tokensUsed,healResult:H}};return}let y=[{role:"system",content:$},...k],I=[],_=0,w="",U,A=0;A++,v.step(A,`Starting generation (model: ${S||"default"})`);let T;if(C.stream){let d="",P;for await(let g of C.stream(y,{model:S,maxTokens:8192}))yield g,g.type==="text_delta"&&(d+=g.text),g.type==="done"&&(P=g.result);T=P||{content:d,files:[],tokensUsed:0}}else{let d=await C.generate(y,{model:S,maxTokens:8192});d.content&&(yield{type:"text_delta",text:d.content}),yield{type:"done",result:d},T=d}_+=T.tokensUsed||0,w=T.content;let j=T.tokensUsed||0,u=S||"claude-sonnet-4-20250514",L=Math.round(j*.3),ie=j-L;if(X.recordStep(A,u,L,ie),v.api("generate",u,j),v.step(A,`Generated ${T.files.length} file(s), ${j} tokens`),T.files.length&&I.push(...T.files),T.followUp&&(U=T.followUp),yield{type:"step_complete",step:A,filesCount:T.files.length},!U&&(w.includes("[CONTINUE]")||w.includes("Next, I'll")||w.includes("Now let me")||w.includes("I'll also generate"))&&T.files.length>0){let P=T.files.map(g=>`Created: ${g.path}${g.description?` \u2014 ${g.description}`:""}`).join(`
51
51
  `);for(y.push({role:"assistant",content:w+(P?`
52
52
 
53
53
  Files created:
@@ -55,7 +55,7 @@ ${P}`:"")}),y.push({role:"user",content:"Continue generating the remaining files
55
55
  `);y.push({role:"assistant",content:g.content+(H?`
56
56
 
57
57
  Files created:
58
- ${H}`:"")}),y.push({role:"user",content:"Continue generating the remaining files. Build on what you've already created. When finished, do not include [CONTINUE] in your response."})}}if(p.has("post:response")){let d=await p.execute("post:response",{event:"post:response",content:w,task:c,cwd:t});if(d.blocked){yield{type:"error",error:d.message||"Blocked by post:response hook"};return}d.modified?.content&&(w=String(d.modified.content))}if(U&&h){yield{type:"generate_result",result:{files:{written:[],skipped:[],errors:[]},content:w,outputType:x,followUp:U,tokensUsed:_}};return}let Pe=new Map;for(let d of I)Pe.set(d.path,d);let Ke=ne(x,f.techStack,o.outputDir),ae=await oe(Array.from(Pe.values()),{cwd:t,overwrite:n,dryRun:r,outputDir:Ke}),W;if(o.heal!==!1&&!r&&ae.written.length>0){let{HealEngine:d}=await import("./heal-OTGT5HHJ.js");W=await new d(t,{enabled:!0,testCommand:o.healConfig?.testCommand,buildCommand:o.healConfig?.buildCommand,lintCommand:o.healConfig?.lintCommand,maxAttempts:o.healConfig?.maxAttempts??3,provider:s,model:S,apiKey:l}).detectAndHeal(ae.written,c),!W.healed&&W.error&&await p.execute("on:error",{event:"on:error",error:new Error(W.error),task:c,cwd:t})}p.has("post:generate")&&await p.execute("post:generate",{event:"post:generate",task:c,content:w,cwd:t}),yield{type:"generate_result",result:{files:ae,content:w,outputType:x,tokensUsed:_,healResult:W}}}function He(o,e,t){let n=[];n.push(`You are agentx, an agentic code generation tool. You generate high-quality, production-ready output for any tech stack.
58
+ ${H}`:"")}),y.push({role:"user",content:"Continue generating the remaining files. Build on what you've already created. When finished, do not include [CONTINUE] in your response."})}}if(p.has("post:response")){let d=await p.execute("post:response",{event:"post:response",content:w,task:c,cwd:t});if(d.blocked){yield{type:"error",error:d.message||"Blocked by post:response hook"};return}d.modified?.content&&(w=String(d.modified.content))}if(U&&h){yield{type:"generate_result",result:{files:{written:[],skipped:[],errors:[]},content:w,outputType:x,followUp:U,tokensUsed:_}};return}let Pe=new Map;for(let d of I)Pe.set(d.path,d);let Ke=ne(x,f.techStack,o.outputDir),ae=await oe(Array.from(Pe.values()),{cwd:t,overwrite:n,dryRun:r,outputDir:Ke}),W;if(o.heal!==!1&&!r&&ae.written.length>0){let{HealEngine:d}=await import("./heal-WKY5I4KB.js");W=await new d(t,{enabled:!0,testCommand:o.healConfig?.testCommand,buildCommand:o.healConfig?.buildCommand,lintCommand:o.healConfig?.lintCommand,maxAttempts:o.healConfig?.maxAttempts??3,provider:s,model:S,apiKey:l}).detectAndHeal(ae.written,c),!W.healed&&W.error&&await p.execute("on:error",{event:"on:error",error:new Error(W.error),task:c,cwd:t})}p.has("post:generate")&&await p.execute("post:generate",{event:"post:generate",task:c,content:w,cwd:t}),yield{type:"generate_result",result:{files:ae,content:w,outputType:x,tokensUsed:_,healResult:W}}}function He(o,e,t){let n=[];n.push(`You are agentx, an agentic code generation tool. You generate high-quality, production-ready output for any tech stack.
59
59
 
60
60
  Your primary tool is \`create_files\` \u2014 use it to output all generated code, documents, and configs as files.
61
61
  If the request is ambiguous or you need critical information to proceed correctly, use \`ask_user\` to ask a clarifying question.
@@ -105,4 +105,4 @@ ${o.projectInstructions}`),o.memoryContext&&n.push(o.memoryContext),n.push(`# Ou
105
105
  Generate output appropriate for: ${e}. Use the \`create_files\` tool to output all files.`),n.join(`
106
106
 
107
107
  `)}export{Re as a,dt as b,ut as c,Me as d,Ie as e,Oe as f,Ue as g,we as h,Ee as i,De as j,be as k,ee as l,N as m,Y as n,qe as o,ke as p,xe as q,Q as r,ze as s,Uo as t,Eo as u};
108
- //# sourceMappingURL=chunk-D6EDMQRV.js.map
108
+ //# sourceMappingURL=chunk-AU37OREL.js.map
@@ -0,0 +1,6 @@
1
+ import{b as g,c as w}from"./chunk-FIOB3MEX.js";import{z as n}from"zod";var C,v,L,z,T,$,m,h=g(()=>{"use strict";C=n.enum(["mon","tue","wed","thu","fri","sat","sun"]),v=n.object({days:n.array(C).default(["mon","tue","wed","thu","fri"]),start:n.string().regex(/^\d{2}:\d{2}$/),end:n.string().regex(/^\d{2}:\d{2}$/),lunch:n.object({start:n.string().regex(/^\d{2}:\d{2}$/),end:n.string().regex(/^\d{2}:\d{2}$/)}).optional()}),L=n.object({title:n.string(),responsibilities:n.array(n.string()).default([]),sopPath:n.string().optional(),kpis:n.array(n.string()).default([])}),z=n.object({role:n.string(),reportsTo:n.string().optional(),schedule:v,utilizationTarget:n.number().min(0).max(1).default(.8)}),T=n.object({channel:n.string(),chatId:n.string(),accountId:n.string().optional()}),$=n.discriminatedUnion("type",[n.object({type:n.literal("backlog"),path:n.string().default(".agentx/backlog.md")}),n.object({type:n.literal("gitlab"),projects:n.array(n.string()).default([])}),n.object({type:n.literal("wiki"),path:n.string(),glob:n.string().default("**/*.md")})]),m=n.object({enabled:n.boolean().default(!1),timezone:n.string().default("UTC"),mainChannel:T,workSource:$,roles:n.record(n.string(),L).default({}),orgChart:n.record(n.string(),z).default({}),workTickMinutes:n.number().int().min(1).max(60).default(15),idleQueueThreshold:n.number().int().min(0).default(0)})});import{z as t}from"zod";function ee(o,r){let s=new Set(o.labels||[]);if(o.state==="closed"){let a=r.find(l=>l.kind==="closed");if(a)return a.id}for(let a of r)if(a.kind==="scoped-label"&&a.scopedLabel&&s.has(a.scopedLabel))return a.id;for(let a of r)if(a.kind==="label"&&a.mapsToLabel&&s.has(a.mapsToLabel))return a.id;let i=r.find(a=>a.kind==="open-backlog");if(i){let a=i.scopedPrefix+"::";return[...s].some(d=>d.startsWith(a))?null:i.id}return r[0]?.id??null}function te(o,r){let s=[],i=[],a=!1,l=!1;return r.kind==="scoped-label"&&r.scopedLabel?s.push(r.scopedLabel):r.kind==="label"&&r.mapsToLabel?s.push(r.mapsToLabel):r.kind==="closed"&&(a=!0),o&&(o.kind==="scoped-label"&&o.scopedLabel?i.push(o.scopedLabel):o.kind==="label"&&o.mapsToLabel?i.push(o.mapsToLabel):o.kind==="closed"&&r.kind!=="closed"&&(l=!0)),{addLabels:s.length?s:void 0,removeLabels:i.length?i:void 0,closeIssue:a||void 0,reopen:l||void 0}}var B,D,I,P,A,E,y,O,k,S=g(()=>{"use strict";B=t.object({name:t.string(),color:t.string().regex(/^#[0-9a-f]{6}$/i).default("#6366f1"),description:t.string().optional()}),D=t.object({id:t.string().regex(/^[a-z0-9_-]+$/,"column id must be lowercase slug"),title:t.string(),kind:t.enum(["open-backlog","scoped-label","closed","label"]).default("label"),mapsToLabel:t.string().optional(),scopedLabel:t.string().optional(),scopedPrefix:t.string().default("Status"),accent:t.string().optional()}),I=t.object({enabled:t.boolean().default(!0),staleDoingMinutes:t.number().int().min(1).default(45),respectLunchBreak:t.boolean().default(!0),respectSchedule:t.boolean().default(!0),action:t.enum(["badge","notify"]).default("badge")}),P=t.discriminatedUnion("type",[t.object({type:t.literal("gitlab"),projects:t.array(t.string()).min(1)})]),A=[{id:"open",title:"Open",kind:"open-backlog",scopedPrefix:"Status",accent:"#6b7280"},{id:"todo",title:"To Do",kind:"scoped-label",scopedLabel:"Status::To Do",scopedPrefix:"Status",accent:"#fb7a35"},{id:"doing",title:"Doing",kind:"scoped-label",scopedLabel:"Status::Doing",scopedPrefix:"Status",accent:"#22c55e"},{id:"onhold",title:"On Hold",kind:"scoped-label",scopedLabel:"Status::On Hold",scopedPrefix:"Status",accent:"#f59e0b"},{id:"review",title:"Review",kind:"scoped-label",scopedLabel:"Status::Review",scopedPrefix:"Status",accent:"#3b82f6"},{id:"closed",title:"Closed",kind:"closed",scopedPrefix:"Status",accent:"#6b7280"}],E=t.object({id:t.string().regex(/^[a-z0-9_-]+$/,"board id must be lowercase slug"),name:t.string(),source:P,primaryToolLabel:t.string().optional(),labels:t.array(B).default([]),columns:t.array(D).min(1).default(A),timeRangeDays:t.number().int().min(1).max(365).default(30),closedWindowDays:t.number().int().min(1).max(365).default(30),reconciliation:I.default({})}),y=t.array(E).default([]),O=t.object({name:t.string(),url:t.string(),dashboardUrl:t.string().optional(),token:t.string().optional(),dashboardToken:t.string().optional()}),k=t.object({enabled:t.boolean().default(!1),port:t.number().int().default(4202),bind:t.string().default("127.0.0.1"),token:t.string().optional(),daemonUrl:t.string().default("http://localhost:18800"),daemons:t.array(O).default([]),draftAgent:t.string().optional()}).default({})});var V={};w(V,{daemonConfigSchema:()=>j,expandEnvVars:()=>p,loadDaemonConfig:()=>H,validateWorkspaces:()=>Q});import{z as e}from"zod";import{readFileSync as x,existsSync as u}from"fs";import{resolve as f}from"path";function R(o){let r=f(o,".env");if(!u(r))return;let s=x(r,"utf-8");for(let i of s.split(`
2
+ `)){let a=i.trim();if(!a||a.startsWith("#"))continue;let l=a.indexOf("=");if(l===-1)continue;let d=a.slice(0,l).trim(),c=a.slice(l+1).trim();process.env[d]||(process.env[d]=c)}}function p(o){if(typeof o=="string")return o.replace(/\$\{(\w+)\}/g,(r,s)=>process.env[s]||"");if(Array.isArray(o))return o.map(p);if(o!==null&&typeof o=="object"){let r={};for(let[s,i]of Object.entries(o))r[s]=p(i);return r}return o}function H(o){let r=o?[o]:[f(process.cwd(),"agentx.json"),f(process.cwd(),".agentx/config.json")];R(process.cwd());let s,i;for(let c of r)if(u(c)){s=x(c,"utf-8"),i=c;break}if(!s||!i)throw new Error(`No config found. Create agentx.json or .agentx/config.json
3
+ Searched: ${r.join(", ")}`);let a;try{a=JSON.parse(s)}catch(c){throw new Error(`Invalid JSON in ${i}: ${c.message}`)}let l=p(a),d=j.safeParse(l);if(!d.success){let c=d.error.issues.map(b=>` ${b.path.join(".")}: ${b.message}`).join(`
4
+ `);throw new Error(`Config validation failed (${i}):
5
+ ${c}`)}return d.data}function Q(o){let r=[];for(let[s,i]of Object.entries(o.agents)){if(!u(i.workspace)){r.push(`Agent "${s}": workspace not found at ${i.workspace}`);continue}if(i.tier==="claude-code"){let d=f(i.workspace,".claude");u(d)||r.push(`Agent "${s}": no .claude/ directory in workspace ${i.workspace}. Claude Code native features (hooks, MCP, skills) won't be available.`)}let a=i.provider||"claude",l=o.providers[a];i.tier!=="claude-code"&&(!l||!l.apiKey)&&r.push(`Agent "${s}": provider "${a}" has no API key configured. Set providers.${a}.apiKey in config or use tier "claude-code" for subscription.`)}for(let[s,i]of Object.entries(o.crons))o.agents[i.agent]||r.push(`Cron "${s}": references unknown agent "${i.agent}"`);if(o.channels.telegram.enabled)for(let[s,i]of Object.entries(o.channels.telegram.accounts))o.agents[i.agentBinding]||r.push(`Telegram account "${s}": references unknown agent "${i.agentBinding}"`);return r}var M,U,J,N,W,_,q,F,K,j,G=g(()=>{h();S();M=e.object({apiKey:e.string().optional(),defaultModel:e.string().optional(),baseUrl:e.string().optional()}),U=e.object({name:e.string(),workspace:e.string(),tier:e.enum(["claude-code","sdk","orchestrator"]).default("claude-code"),provider:e.string().optional(),model:e.string().optional(),systemPrompt:e.string().optional(),mentions:e.array(e.string()).default([]),maxConcurrent:e.number().default(1),maxExecutionMinutes:e.number().int().min(1).max(240).default(20),permissionMode:e.string().default("default"),queueMode:e.enum(["collect","followup","drop"]).default("collect"),heartbeat:e.object({enabled:e.boolean().default(!1),intervalMinutes:e.number().default(30),prompt:e.string().default("Check inbox, pending tasks, and system health. Report anything that needs attention."),channel:e.string().default("heartbeat")}).default({}),access:e.enum(["private","public"]).default("private")}),J=e.object({token:e.string(),agentBinding:e.string()}),N=e.object({telegram:e.object({enabled:e.boolean().default(!1),accounts:e.record(e.string(),J).default({}),policy:e.object({dm:e.enum(["pair","block"]).default("pair"),group:e.enum(["mention-required","all"]).default("mention-required")}).default({})}).default({}),whatsapp:e.object({enabled:e.boolean().default(!1),sessionDir:e.string().default(".agentx/whatsapp-sessions"),defaultAgent:e.string().optional(),allowFrom:e.array(e.string()).optional(),routes:e.array(e.object({contact:e.string().optional(),group:e.string().optional(),agent:e.string()})).default([])}).default({}),discord:e.object({enabled:e.boolean().default(!1),token:e.string().optional(),agentBinding:e.string().optional()}).default({}),slack:e.object({enabled:e.boolean().default(!1),botToken:e.string().optional(),appToken:e.string().optional(),agentBinding:e.string().optional()}).default({}),gitlab:e.object({enabled:e.boolean().default(!1),webhookPort:e.number().default(18810),webhookSecret:e.string().optional(),host:e.string().default("https://gitlab.com"),token:e.string().optional(),routes:e.array(e.object({project:e.string(),agent:e.string()})).default([]),agentMappings:e.array(e.object({agentId:e.string(),gitlabUsernames:e.array(e.string()).default([]),keywords:e.array(e.string()).default([]),token:e.string().optional(),node:e.string().optional()})).default([])}).default({})}),W=e.object({enabled:e.boolean().default(!0),schedule:e.string(),timezone:e.string().default("UTC"),agent:e.string(),prompt:e.string(),timeout:e.number().default(600),model:e.string().optional(),maxOutputTokens:e.number().int().min(50).max(8e3).optional(),onError:e.union([e.enum(["log","notify","disable"]),e.array(e.enum(["log","notify","disable"]))]).default("log").transform(o=>Array.isArray(o)?o:[o]),notify:e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()}).optional()}),_=e.object({name:e.string(),triggers:e.array(e.object({pattern:e.string(),channel:e.string().optional()})),allowedContacts:e.array(e.string()).optional(),agent:e.string(),prompt:e.string(),schedule:e.string().optional(),timezone:e.string().default("UTC"),notify:e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()}).optional()}),q=e.object({url:e.string(),name:e.string(),token:e.string().optional()}),F=e.object({enabled:e.boolean().default(!1),peers:e.array(q).default([]),discovery:e.enum(["static","mdns"]).default("static"),healthCheck:e.object({interval:e.number().default(60),timeout:e.number().default(10)}).default({})}),K=e.object({longTaskThreshold:e.number().default(30),destination:e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()}).optional(),on:e.object({taskComplete:e.boolean().default(!0),taskError:e.boolean().default(!0),taskQueued:e.boolean().default(!1)}).default({})}).default({}),j=e.object({node:e.object({id:e.string(),name:e.string(),bind:e.string().default("127.0.0.1:18800"),defaultAgent:e.string().optional()}),providers:e.record(e.string(),M).default({}),agents:e.record(e.string(),U).default({}),channels:N.default({}),crons:e.record(e.string(),W).default({}),services:e.record(e.string(),_).default({}),notifications:K,mesh:F.default({}),business:m.optional(),boards:y,dashboard:k,webhooks:e.array(e.object({id:e.string().regex(/^[a-z0-9][a-z0-9_-]*$/,"webhook id must be lowercase slug"),source:e.enum(["gitlab","github","sentry","stripe","discord","slack","custom"]),agentId:e.string(),secretEnv:e.string().optional(),description:e.string().optional(),enabled:e.boolean().default(!0)})).default([]),session:e.object({staleMinutes:e.number().int().min(1).max(1440).default(120)}).default({})})});export{ee as a,te as b,S as c,j as d,p as e,H as f,Q as g,V as h,G as i};
6
+ //# sourceMappingURL=chunk-B4Q4X7D5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/business/config.ts","../src/boards/config.ts","../src/daemon/config.ts"],"sourcesContent":["import { z } from \"zod\"\n\n// --- Business layer configuration schema ---\n// Optional top-level `business` key in agentx.json.\n// When enabled, AgentX simulates a team with roles, schedules, and a work pool.\n\nconst dayEnum = z.enum([\"mon\", \"tue\", \"wed\", \"thu\", \"fri\", \"sat\", \"sun\"])\n\nconst scheduleSchema = z.object({\n days: z.array(dayEnum).default([\"mon\", \"tue\", \"wed\", \"thu\", \"fri\"]),\n start: z.string().regex(/^\\d{2}:\\d{2}$/), // \"09:00\"\n end: z.string().regex(/^\\d{2}:\\d{2}$/), // \"17:00\"\n lunch: z.object({\n start: z.string().regex(/^\\d{2}:\\d{2}$/),\n end: z.string().regex(/^\\d{2}:\\d{2}$/),\n }).optional(),\n})\n\nconst roleSchema = z.object({\n title: z.string(),\n responsibilities: z.array(z.string()).default([]),\n sopPath: z.string().optional(),\n kpis: z.array(z.string()).default([]),\n})\n\nconst orgEntrySchema = z.object({\n role: z.string(),\n reportsTo: z.string().optional(),\n schedule: scheduleSchema,\n utilizationTarget: z.number().min(0).max(1).default(0.8),\n})\n\nconst channelRefSchema = z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n})\n\nconst workSourceSchema = z.discriminatedUnion(\"type\", [\n z.object({\n type: z.literal(\"backlog\"),\n path: z.string().default(\".agentx/backlog.md\"),\n }),\n z.object({\n type: z.literal(\"gitlab\"),\n projects: z.array(z.string()).default([]), // empty = all configured gitlab projects\n }),\n z.object({\n type: z.literal(\"wiki\"),\n path: z.string(),\n glob: z.string().default(\"**/*.md\"),\n }),\n])\n\nexport const businessConfigSchema = z.object({\n enabled: z.boolean().default(false),\n timezone: z.string().default(\"UTC\"),\n mainChannel: channelRefSchema,\n workSource: workSourceSchema,\n roles: z.record(z.string(), roleSchema).default({}),\n orgChart: z.record(z.string(), orgEntrySchema).default({}),\n /** Work-tick cadence in minutes during business hours. Default 15. */\n workTickMinutes: z.number().int().min(1).max(60).default(15),\n /** Max queue depth for an idle agent before skipping the work tick (avoids piling up). */\n idleQueueThreshold: z.number().int().min(0).default(0),\n})\n\nexport type BusinessConfig = z.infer<typeof businessConfigSchema>\nexport type BusinessRole = z.infer<typeof roleSchema>\nexport type BusinessOrgEntry = z.infer<typeof orgEntrySchema>\nexport type BusinessSchedule = z.infer<typeof scheduleSchema>\nexport type BusinessWorkSource = z.infer<typeof workSourceSchema>\nexport type BusinessChannelRef = z.infer<typeof channelRefSchema>\n","import { z } from \"zod\"\n\n// --- Kanban board configuration ---\n//\n// Top-level optional keys in agentx.json, independent of the business layer:\n//\n// boards: array of kanban boards, each backed by a WorkSource\n// dashboard: HTTP server controlling whether the board UI serves\n//\n// Both default to empty / disabled so existing installs see no change.\n\nexport const boardLabelSchema = z.object({\n /** Label name — used verbatim as the GitLab label string. */\n name: z.string(),\n color: z.string().regex(/^#[0-9a-f]{6}$/i).default(\"#6366f1\"),\n description: z.string().optional(),\n})\n\n/**\n * A board column. `kind` controls what issues land here and how drag-to-this-column\n * mutates the issue:\n *\n * - \"open-backlog\": opened issues that have NO scoped label matching `scopedPrefix`\n * (default \"Status\"). Dragging INTO this column removes any existing scoped label.\n * - \"scoped-label\": opened issues carrying `scopedLabel` (e.g. \"Status::Doing\").\n * Scoped labels are mutually exclusive by prefix, so adding the new one lets\n * GitLab auto-remove the old one (Premium), but we still explicitly strip the\n * previous column's label for compatibility.\n * - \"closed\": all closed issues. Dragging here closes the issue; dragging away\n * reopens it.\n * - \"label\" (default, legacy): opened issues with `mapsToLabel`; flat add/remove.\n */\nexport const boardColumnSchema = z.object({\n id: z.string().regex(/^[a-z0-9_-]+$/, \"column id must be lowercase slug\"),\n title: z.string(),\n kind: z.enum([\"open-backlog\", \"scoped-label\", \"closed\", \"label\"]).default(\"label\"),\n /** For kind=\"label\": label written on entry, removed on exit. */\n mapsToLabel: z.string().optional(),\n /** For kind=\"scoped-label\": the full scoped label (e.g. \"Status::Doing\"). */\n scopedLabel: z.string().optional(),\n /** For kind=\"open-backlog\": scoped prefix that marks \"claimed\" cards to exclude. */\n scopedPrefix: z.string().default(\"Status\"),\n /** CSS color or hex — shown as the column's top accent bar. */\n accent: z.string().optional(),\n})\nexport type BoardColumnId = string\n\nexport const boardReconcileSchema = z.object({\n enabled: z.boolean().default(true),\n staleDoingMinutes: z.number().int().min(1).default(45),\n respectLunchBreak: z.boolean().default(true),\n respectSchedule: z.boolean().default(true),\n /** Badge only for P1/P3; auto-notify + auto-demote deferred. */\n action: z.enum([\"badge\", \"notify\"]).default(\"badge\"),\n})\n\nexport const boardSourceSchema = z.discriminatedUnion(\"type\", [\n z.object({\n type: z.literal(\"gitlab\"),\n /** GitLab project paths (e.g. \"mtgl/mtgl-system-v2\"). At least one required. */\n projects: z.array(z.string()).min(1),\n }),\n // future: backlog, wiki\n])\n\n/**\n * GitLab-style default board — six columns:\n * Open (backlog) → To Do → Doing → On Hold → Review → Closed\n *\n * Drives the `Status::*` scoped label taxonomy. Moving a card adds/removes the\n * column's scoped label; moving INTO Closed closes the issue; moving OUT of\n * Closed reopens it; moving INTO Open strips any Status::* label.\n */\nexport const DEFAULT_COLUMNS: Array<z.infer<typeof boardColumnSchema>> = [\n { id: \"open\", title: \"Open\", kind: \"open-backlog\", scopedPrefix: \"Status\", accent: \"#6b7280\" },\n { id: \"todo\", title: \"To Do\", kind: \"scoped-label\", scopedLabel: \"Status::To Do\", scopedPrefix: \"Status\", accent: \"#fb7a35\" },\n { id: \"doing\", title: \"Doing\", kind: \"scoped-label\", scopedLabel: \"Status::Doing\", scopedPrefix: \"Status\", accent: \"#22c55e\" },\n { id: \"onhold\", title: \"On Hold\", kind: \"scoped-label\", scopedLabel: \"Status::On Hold\", scopedPrefix: \"Status\", accent: \"#f59e0b\" },\n { id: \"review\", title: \"Review\", kind: \"scoped-label\", scopedLabel: \"Status::Review\", scopedPrefix: \"Status\", accent: \"#3b82f6\" },\n { id: \"closed\", title: \"Closed\", kind: \"closed\", scopedPrefix: \"Status\", accent: \"#6b7280\" },\n]\n\nexport const boardSchema = z.object({\n id: z.string().regex(/^[a-z0-9_-]+$/, \"board id must be lowercase slug\"),\n name: z.string(),\n source: boardSourceSchema,\n /** If set, ANDed into every listAll query and shown as a baseline chip in the UI. */\n primaryToolLabel: z.string().optional(),\n labels: z.array(boardLabelSchema).default([]),\n columns: z.array(boardColumnSchema).min(1).default(DEFAULT_COLUMNS),\n timeRangeDays: z.number().int().min(1).max(365).default(30),\n /** Maximum closed-issue window (days) shown in the Closed column. */\n closedWindowDays: z.number().int().min(1).max(365).default(30),\n reconciliation: boardReconcileSchema.default({}),\n})\n\nexport const boardsConfigSchema = z.array(boardSchema).default([])\n\nexport const dashboardDaemonSchema = z.object({\n name: z.string(),\n /** Daemon HTTP URL (the one serving /agents, /health, /task). */\n url: z.string(),\n /** Optional board-dashboard URL if we want to proxy admin writes to this\n * peer. Defaults to `url` with port :18800/:19900 rewritten to :4202,\n * matching our convention that the dashboard runs alongside the daemon. */\n dashboardUrl: z.string().optional(),\n /** Bearer for the daemon's HTTP API (live snapshot, task stream). */\n token: z.string().optional(),\n /** Bearer for the peer's dashboard when proxying admin calls. Defaults to\n * `token` when unset. */\n dashboardToken: z.string().optional(),\n})\n\nexport const dashboardConfigSchema = z.object({\n enabled: z.boolean().default(false),\n port: z.number().int().default(4202),\n bind: z.string().default(\"127.0.0.1\"),\n /** Opt-in bearer token for writes. If unset, writes are unauthenticated. */\n token: z.string().optional(),\n /** Primary daemon the dashboard talks to for live view + A2A. */\n daemonUrl: z.string().default(\"http://localhost:18800\"),\n /** Additional daemons to poll (beyond peers discovered via primary's /mesh). */\n daemons: z.array(dashboardDaemonSchema).default([]),\n /** Default agent used for AI-assisted drafting in the create-issue flow. */\n draftAgent: z.string().optional(),\n}).default({})\n\nexport type BoardConfig = z.infer<typeof boardSchema>\nexport type BoardLabel = z.infer<typeof boardLabelSchema>\nexport type BoardColumn = z.infer<typeof boardColumnSchema>\nexport type BoardSource = z.infer<typeof boardSourceSchema>\nexport type BoardReconcile = z.infer<typeof boardReconcileSchema>\nexport type DashboardConfig = z.infer<typeof dashboardConfigSchema>\n\n/**\n * Decide which column an item lives in. Walks columns in order; the first\n * matching column wins. Items with `state: \"closed\"` always land in the\n * first `kind: \"closed\"` column; otherwise the first `scoped-label` whose\n * `scopedLabel` is present wins; otherwise the first `label` column whose\n * `mapsToLabel` is present; otherwise the first `open-backlog` column.\n *\n * Returns `null` when the item carries a scoped label matching an\n * `open-backlog` column's `scopedPrefix` but no `scoped-label` column claims\n * it — those are off-board workflow states (e.g. `Status::Done` when no\n * Done column exists) and must not leak into the backlog.\n */\nexport function deriveStage(\n item: { state?: \"opened\" | \"closed\"; labels?: string[] },\n columns: BoardColumn[],\n): BoardColumnId | null {\n const set = new Set(item.labels || [])\n if (item.state === \"closed\") {\n const closed = columns.find((c) => c.kind === \"closed\")\n if (closed) return closed.id\n }\n for (const c of columns) {\n if (c.kind === \"scoped-label\" && c.scopedLabel && set.has(c.scopedLabel)) return c.id\n }\n for (const c of columns) {\n if (c.kind === \"label\" && c.mapsToLabel && set.has(c.mapsToLabel)) return c.id\n }\n const backlog = columns.find((c) => c.kind === \"open-backlog\")\n if (backlog) {\n // If the item has any scoped-prefix label, it's not an open-backlog candidate.\n const prefix = backlog.scopedPrefix + \"::\"\n const hasScoped = [...set].some((l) => l.startsWith(prefix))\n return hasScoped ? null : backlog.id\n }\n return columns[0]?.id ?? null\n}\n\n/**\n * Describes how to mutate an issue to move it between two columns.\n * `closeIssue` / `reopen` take precedence over label mutations on the same action.\n */\nexport interface ColumnTransition {\n addLabels?: string[]\n removeLabels?: string[]\n closeIssue?: boolean\n reopen?: boolean\n}\n\nexport function transitionDiff(\n fromColumn: BoardColumn | undefined,\n toColumn: BoardColumn,\n): ColumnTransition {\n const add: string[] = []\n const remove: string[] = []\n let closeIssue = false\n let reopen = false\n\n // Handle target column.\n if (toColumn.kind === \"scoped-label\" && toColumn.scopedLabel) {\n add.push(toColumn.scopedLabel)\n } else if (toColumn.kind === \"label\" && toColumn.mapsToLabel) {\n add.push(toColumn.mapsToLabel)\n } else if (toColumn.kind === \"closed\") {\n closeIssue = true\n }\n\n // Handle source column — strip its label; if it was \"closed\", reopen.\n if (fromColumn) {\n if (fromColumn.kind === \"scoped-label\" && fromColumn.scopedLabel) {\n remove.push(fromColumn.scopedLabel)\n } else if (fromColumn.kind === \"label\" && fromColumn.mapsToLabel) {\n remove.push(fromColumn.mapsToLabel)\n } else if (fromColumn.kind === \"closed\" && toColumn.kind !== \"closed\") {\n reopen = true\n }\n }\n\n return {\n addLabels: add.length ? add : undefined,\n removeLabels: remove.length ? remove : undefined,\n closeIssue: closeIssue || undefined,\n reopen: reopen || undefined,\n }\n}\n","import { z } from \"zod\"\nimport { readFileSync, existsSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { businessConfigSchema } from \"@/business/config\"\nimport { boardsConfigSchema, dashboardConfigSchema } from \"@/boards/config\"\n\n/**\n * Load .env file into process.env (simple, no dependency).\n */\nfunction loadDotEnv(dir: string): void {\n const envPath = resolve(dir, \".env\")\n if (!existsSync(envPath)) return\n\n const content = readFileSync(envPath, \"utf-8\")\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim()\n if (!trimmed || trimmed.startsWith(\"#\")) continue\n const eqIdx = trimmed.indexOf(\"=\")\n if (eqIdx === -1) continue\n const key = trimmed.slice(0, eqIdx).trim()\n const value = trimmed.slice(eqIdx + 1).trim()\n if (!process.env[key]) {\n process.env[key] = value\n }\n }\n}\n\n// --- Daemon configuration schema & loader ---\n\nconst providerConfigSchema = z.object({\n apiKey: z.string().optional(),\n defaultModel: z.string().optional(),\n baseUrl: z.string().optional(),\n})\n\nconst agentConfigSchema = z.object({\n name: z.string(),\n workspace: z.string(),\n tier: z.enum([\"claude-code\", \"sdk\", \"orchestrator\"]).default(\"claude-code\"),\n provider: z.string().optional(),\n model: z.string().optional(),\n systemPrompt: z.string().optional(),\n mentions: z.array(z.string()).default([]),\n maxConcurrent: z.number().default(1),\n /** Hard wall-clock cap on a single Claude Code invocation. Exceeding the\n * cap sends SIGTERM (exit 143). Default 20 min — bump for devops/coder\n * agents that do long investigations or multi-file refactors. */\n maxExecutionMinutes: z.number().int().min(1).max(240).default(20),\n permissionMode: z.string().default(\"default\"),\n queueMode: z.enum([\"collect\", \"followup\", \"drop\"]).default(\"collect\"),\n heartbeat: z.object({\n enabled: z.boolean().default(false),\n intervalMinutes: z.number().default(30),\n prompt: z.string().default(\"Check inbox, pending tasks, and system health. Report anything that needs attention.\"),\n channel: z.string().default(\"heartbeat\"),\n }).default({}),\n /** Reachability via the external public API.\n * - \"private\" (default): this agent is daemon-internal + channel-bound only.\n * - \"public\": external apps can POST /api/public/agents/<id>/messages\n * with a scoped token (agent:<id> or agent:*). */\n access: z.enum([\"private\", \"public\"]).default(\"private\"),\n})\n\nconst telegramAccountSchema = z.object({\n token: z.string(),\n agentBinding: z.string(),\n})\n\nconst channelsConfigSchema = z.object({\n telegram: z.object({\n enabled: z.boolean().default(false),\n accounts: z.record(z.string(), telegramAccountSchema).default({}),\n policy: z.object({\n dm: z.enum([\"pair\", \"block\"]).default(\"pair\"),\n group: z.enum([\"mention-required\", \"all\"]).default(\"mention-required\"),\n }).default({}),\n }).default({}),\n whatsapp: z.object({\n enabled: z.boolean().default(false),\n sessionDir: z.string().default(\".agentx/whatsapp-sessions\"),\n defaultAgent: z.string().optional(),\n allowFrom: z.array(z.string()).optional(),\n routes: z.array(z.object({\n contact: z.string().optional(),\n group: z.string().optional(),\n agent: z.string(),\n })).default([]),\n }).default({}),\n discord: z.object({\n enabled: z.boolean().default(false),\n token: z.string().optional(),\n agentBinding: z.string().optional(),\n }).default({}),\n slack: z.object({\n enabled: z.boolean().default(false),\n /** xoxb-... — bot user OAuth token from the Slack app's \"OAuth & Permissions\" tab. */\n botToken: z.string().optional(),\n /** xapp-... — app-level token with connections:write scope (for Socket Mode). */\n appToken: z.string().optional(),\n agentBinding: z.string().optional(),\n }).default({}),\n gitlab: z.object({\n enabled: z.boolean().default(false),\n webhookPort: z.number().default(18810),\n webhookSecret: z.string().optional(),\n host: z.string().default(\"https://gitlab.com\"),\n token: z.string().optional(),\n routes: z.array(z.object({\n project: z.string(),\n agent: z.string(),\n })).default([]),\n agentMappings: z.array(z.object({\n agentId: z.string(),\n gitlabUsernames: z.array(z.string()).default([]),\n keywords: z.array(z.string()).default([]),\n token: z.string().optional(),\n /** If set, the agent lives on a remote mesh peer (node id). Forces the\n * username→agent map to resolve to this mapping even when a local\n * agent's token resolves to the same GitLab user — prevents collisions\n * like two agents both claiming @devops-noqta. */\n node: z.string().optional(),\n })).default([]),\n }).default({}),\n})\n\nconst cronJobSchema = z.object({\n enabled: z.boolean().default(true),\n schedule: z.string(),\n timezone: z.string().default(\"UTC\"),\n agent: z.string(),\n prompt: z.string(),\n timeout: z.number().default(600),\n model: z.string().optional(),\n /** Soft cap on output length. Claude Code CLI has no hard flag for this,\n * but appending an instruction to the prompt is reliably honored. Use\n * 1500 for briefs, 500 for status pings, 300 for pure classifiers. */\n maxOutputTokens: z.number().int().min(50).max(8000).optional(),\n onError: z.union([\n z.enum([\"log\", \"notify\", \"disable\"]),\n z.array(z.enum([\"log\", \"notify\", \"disable\"])),\n ]).default(\"log\").transform(v => Array.isArray(v) ? v : [v]),\n notify: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n})\n\nconst serviceSchema = z.object({\n name: z.string(),\n triggers: z.array(z.object({\n pattern: z.string(),\n channel: z.string().optional(),\n })),\n allowedContacts: z.array(z.string()).optional(),\n agent: z.string(),\n prompt: z.string(),\n schedule: z.string().optional(),\n timezone: z.string().default(\"UTC\"),\n notify: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n})\n\nconst meshPeerSchema = z.object({\n url: z.string(),\n name: z.string(),\n token: z.string().optional(),\n})\n\nconst meshConfigSchema = z.object({\n enabled: z.boolean().default(false),\n peers: z.array(meshPeerSchema).default([]),\n discovery: z.enum([\"static\", \"mdns\"]).default(\"static\"),\n healthCheck: z.object({\n interval: z.number().default(60),\n timeout: z.number().default(10),\n }).default({}),\n})\n\nconst notificationsSchema = z.object({\n /** Send notification when task takes longer than this (seconds). 0 = disabled. */\n longTaskThreshold: z.number().default(30),\n /** Where to send notifications */\n destination: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n /** Notify on these events */\n on: z.object({\n taskComplete: z.boolean().default(true),\n taskError: z.boolean().default(true),\n taskQueued: z.boolean().default(false),\n }).default({}),\n}).default({})\n\nexport const daemonConfigSchema = z.object({\n node: z.object({\n id: z.string(),\n name: z.string(),\n bind: z.string().default(\"127.0.0.1:18800\"),\n /** Default agent for voice/API calls that don't specify one (e.g. Siri) */\n defaultAgent: z.string().optional(),\n }),\n providers: z.record(z.string(), providerConfigSchema).default({}),\n agents: z.record(z.string(), agentConfigSchema).default({}),\n channels: channelsConfigSchema.default({}),\n crons: z.record(z.string(), cronJobSchema).default({}),\n services: z.record(z.string(), serviceSchema).default({}),\n notifications: notificationsSchema,\n mesh: meshConfigSchema.default({}),\n business: businessConfigSchema.optional(),\n boards: boardsConfigSchema,\n dashboard: dashboardConfigSchema,\n /** Registered inbound webhooks — an inventory the dashboard manages. Each\n * entry binds an (agent, source) pair to an optional signing secret. The\n * actual inbound URL is always POST /webhook/<agentId>/<source>. */\n webhooks: z.array(z.object({\n id: z.string().regex(/^[a-z0-9][a-z0-9_-]*$/, \"webhook id must be lowercase slug\"),\n source: z.enum([\"gitlab\", \"github\", \"sentry\", \"stripe\", \"discord\", \"slack\", \"custom\"]),\n agentId: z.string(),\n secretEnv: z.string().optional(),\n description: z.string().optional(),\n enabled: z.boolean().default(true),\n })).default([]),\n /** Session cache-reuse policy. `staleMinutes` controls how long a Claude\n * session can idle before we rebuild the prompt from scratch (paying the\n * full cache-create tax again). Longer is cheaper at Opus rates but means\n * an agent may resume with mildly outdated context. */\n session: z.object({\n staleMinutes: z.number().int().min(1).max(1440).default(120),\n }).default({}),\n})\n\nexport type DaemonConfig = z.infer<typeof daemonConfigSchema>\nexport type AgentDef = z.infer<typeof agentConfigSchema>\nexport type CronJobDef = z.infer<typeof cronJobSchema>\nexport type MeshPeer = z.infer<typeof meshPeerSchema>\n\n/**\n * Expand environment variables in strings: ${VAR_NAME} -> process.env.VAR_NAME\n */\nexport function expandEnvVars(obj: unknown): unknown {\n if (typeof obj === \"string\") {\n return obj.replace(/\\$\\{(\\w+)\\}/g, (_match, name) => process.env[name] || \"\")\n }\n if (Array.isArray(obj)) {\n return obj.map(expandEnvVars)\n }\n if (obj !== null && typeof obj === \"object\") {\n const result: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n result[key] = expandEnvVars(value)\n }\n return result\n }\n return obj\n}\n\n/**\n * Load daemon config from agentx.json, with env var expansion and validation.\n */\nexport function loadDaemonConfig(configPath?: string): DaemonConfig {\n const paths = configPath\n ? [configPath]\n : [\n resolve(process.cwd(), \"agentx.json\"),\n resolve(process.cwd(), \".agentx/config.json\"),\n ]\n\n // Load .env from same directory as config search\n loadDotEnv(process.cwd())\n\n let raw: string | undefined\n let foundPath: string | undefined\n\n for (const p of paths) {\n if (existsSync(p)) {\n raw = readFileSync(p, \"utf-8\")\n foundPath = p\n break\n }\n }\n\n if (!raw || !foundPath) {\n throw new Error(\n `No config found. Create agentx.json or .agentx/config.json\\n` +\n `Searched: ${paths.join(\", \")}`\n )\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch (e: any) {\n throw new Error(`Invalid JSON in ${foundPath}: ${e.message}`)\n }\n\n // Expand environment variables\n const expanded = expandEnvVars(parsed)\n\n // Validate\n const result = daemonConfigSchema.safeParse(expanded)\n if (!result.success) {\n const issues = result.error.issues\n .map((i) => ` ${i.path.join(\".\")}: ${i.message}`)\n .join(\"\\n\")\n throw new Error(`Config validation failed (${foundPath}):\\n${issues}`)\n }\n\n return result.data\n}\n\n/**\n * Validate agent workspace directories exist and have .claude/ setup.\n */\nexport function validateWorkspaces(config: DaemonConfig): string[] {\n const warnings: string[] = []\n\n for (const [id, agent] of Object.entries(config.agents)) {\n if (!existsSync(agent.workspace)) {\n warnings.push(`Agent \"${id}\": workspace not found at ${agent.workspace}`)\n continue\n }\n\n if (agent.tier === \"claude-code\") {\n const claudeDir = resolve(agent.workspace, \".claude\")\n if (!existsSync(claudeDir)) {\n warnings.push(\n `Agent \"${id}\": no .claude/ directory in workspace ${agent.workspace}. ` +\n `Claude Code native features (hooks, MCP, skills) won't be available.`\n )\n }\n }\n\n // Check provider availability\n const providerName = agent.provider || \"claude\"\n const providerConfig = config.providers[providerName]\n if (agent.tier !== \"claude-code\" && (!providerConfig || !providerConfig.apiKey)) {\n warnings.push(\n `Agent \"${id}\": provider \"${providerName}\" has no API key configured. ` +\n `Set providers.${providerName}.apiKey in config or use tier \"claude-code\" for subscription.`\n )\n }\n }\n\n // Validate cron agent bindings\n for (const [id, cron] of Object.entries(config.crons)) {\n if (!config.agents[cron.agent]) {\n warnings.push(`Cron \"${id}\": references unknown agent \"${cron.agent}\"`)\n }\n }\n\n // Validate channel agent bindings\n if (config.channels.telegram.enabled) {\n for (const [name, account] of Object.entries(config.channels.telegram.accounts)) {\n if (!config.agents[account.agentBinding]) {\n warnings.push(\n `Telegram account \"${name}\": references unknown agent \"${account.agentBinding}\"`\n )\n }\n }\n }\n\n return warnings\n}\n"],"mappings":"+CAAA,OAAS,KAAAA,MAAS,MAAlB,IAMMC,EAEAC,EAUAC,EAOAC,EAOAC,EAMAC,EAgBOC,EAtDbC,EAAAC,EAAA,kBAMMR,EAAUD,EAAE,KAAK,CAAC,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,KAAK,CAAC,EAElEE,EAAiBF,EAAE,OAAO,CAC9B,KAAMA,EAAE,MAAMC,CAAO,EAAE,QAAQ,CAAC,MAAO,MAAO,MAAO,MAAO,KAAK,CAAC,EAClE,MAAOD,EAAE,OAAO,EAAE,MAAM,eAAe,EACvC,IAAKA,EAAE,OAAO,EAAE,MAAM,eAAe,EACrC,MAAOA,EAAE,OAAO,CACd,MAAOA,EAAE,OAAO,EAAE,MAAM,eAAe,EACvC,IAAKA,EAAE,OAAO,EAAE,MAAM,eAAe,CACvC,CAAC,EAAE,SAAS,CACd,CAAC,EAEKG,EAAaH,EAAE,OAAO,CAC1B,MAAOA,EAAE,OAAO,EAChB,iBAAkBA,EAAE,MAAMA,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC,EAChD,QAASA,EAAE,OAAO,EAAE,SAAS,EAC7B,KAAMA,EAAE,MAAMA,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC,CACtC,CAAC,EAEKI,EAAiBJ,EAAE,OAAO,CAC9B,KAAMA,EAAE,OAAO,EACf,UAAWA,EAAE,OAAO,EAAE,SAAS,EAC/B,SAAUE,EACV,kBAAmBF,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,QAAQ,EAAG,CACzD,CAAC,EAEKK,EAAmBL,EAAE,OAAO,CAChC,QAASA,EAAE,OAAO,EAClB,OAAQA,EAAE,OAAO,EACjB,UAAWA,EAAE,OAAO,EAAE,SAAS,CACjC,CAAC,EAEKM,EAAmBN,EAAE,mBAAmB,OAAQ,CACpDA,EAAE,OAAO,CACP,KAAMA,EAAE,QAAQ,SAAS,EACzB,KAAMA,EAAE,OAAO,EAAE,QAAQ,oBAAoB,CAC/C,CAAC,EACDA,EAAE,OAAO,CACP,KAAMA,EAAE,QAAQ,QAAQ,EACxB,SAAUA,EAAE,MAAMA,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC,CAC1C,CAAC,EACDA,EAAE,OAAO,CACP,KAAMA,EAAE,QAAQ,MAAM,EACtB,KAAMA,EAAE,OAAO,EACf,KAAMA,EAAE,OAAO,EAAE,QAAQ,SAAS,CACpC,CAAC,CACH,CAAC,EAEYO,EAAuBP,EAAE,OAAO,CAC3C,QAASA,EAAE,QAAQ,EAAE,QAAQ,EAAK,EAClC,SAAUA,EAAE,OAAO,EAAE,QAAQ,KAAK,EAClC,YAAaK,EACb,WAAYC,EACZ,MAAON,EAAE,OAAOA,EAAE,OAAO,EAAGG,CAAU,EAAE,QAAQ,CAAC,CAAC,EAClD,SAAUH,EAAE,OAAOA,EAAE,OAAO,EAAGI,CAAc,EAAE,QAAQ,CAAC,CAAC,EAEzD,gBAAiBJ,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,EAE3D,mBAAoBA,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC,CACvD,CAAC,ICjED,OAAS,KAAAU,MAAS,MAkJX,SAASC,GACdC,EACAC,EACsB,CACtB,IAAMC,EAAM,IAAI,IAAIF,EAAK,QAAU,CAAC,CAAC,EACrC,GAAIA,EAAK,QAAU,SAAU,CAC3B,IAAMG,EAASF,EAAQ,KAAMG,GAAMA,EAAE,OAAS,QAAQ,EACtD,GAAID,EAAQ,OAAOA,EAAO,GAE5B,QAAWC,KAAKH,EACd,GAAIG,EAAE,OAAS,gBAAkBA,EAAE,aAAeF,EAAI,IAAIE,EAAE,WAAW,EAAG,OAAOA,EAAE,GAErF,QAAWA,KAAKH,EACd,GAAIG,EAAE,OAAS,SAAWA,EAAE,aAAeF,EAAI,IAAIE,EAAE,WAAW,EAAG,OAAOA,EAAE,GAE9E,IAAMC,EAAUJ,EAAQ,KAAMG,GAAMA,EAAE,OAAS,cAAc,EAC7D,GAAIC,EAAS,CAEX,IAAMC,EAASD,EAAQ,aAAe,KAEtC,MADkB,CAAC,GAAGH,CAAG,EAAE,KAAMK,GAAMA,EAAE,WAAWD,CAAM,CAAC,EACxC,KAAOD,EAAQ,GAEpC,OAAOJ,EAAQ,CAAC,GAAG,IAAM,IAC3B,CAaO,SAASO,GACdC,EACAC,EACkB,CAClB,IAAMC,EAAgB,CAAC,EACjBC,EAAmB,CAAC,EACtBC,EAAa,GACbC,EAAS,GAGb,OAAIJ,EAAS,OAAS,gBAAkBA,EAAS,YAC/CC,EAAI,KAAKD,EAAS,WAAW,EACpBA,EAAS,OAAS,SAAWA,EAAS,YAC/CC,EAAI,KAAKD,EAAS,WAAW,EACpBA,EAAS,OAAS,WAC3BG,EAAa,IAIXJ,IACEA,EAAW,OAAS,gBAAkBA,EAAW,YACnDG,EAAO,KAAKH,EAAW,WAAW,EACzBA,EAAW,OAAS,SAAWA,EAAW,YACnDG,EAAO,KAAKH,EAAW,WAAW,EACzBA,EAAW,OAAS,UAAYC,EAAS,OAAS,WAC3DI,EAAS,KAIN,CACL,UAAWH,EAAI,OAASA,EAAM,OAC9B,aAAcC,EAAO,OAASA,EAAS,OACvC,WAAYC,GAAc,OAC1B,OAAQC,GAAU,MACpB,CACF,CAzNA,IAWaC,EAqBAC,EAeAC,EASAC,EAiBAC,EASAC,EAcAC,EAEAC,EAeAC,EAjHbC,EAAAC,EAAA,kBAWaV,EAAmBjB,EAAE,OAAO,CAEvC,KAAMA,EAAE,OAAO,EACf,MAAOA,EAAE,OAAO,EAAE,MAAM,iBAAiB,EAAE,QAAQ,SAAS,EAC5D,YAAaA,EAAE,OAAO,EAAE,SAAS,CACnC,CAAC,EAgBYkB,EAAoBlB,EAAE,OAAO,CACxC,GAAIA,EAAE,OAAO,EAAE,MAAM,gBAAiB,kCAAkC,EACxE,MAAOA,EAAE,OAAO,EAChB,KAAMA,EAAE,KAAK,CAAC,eAAgB,eAAgB,SAAU,OAAO,CAAC,EAAE,QAAQ,OAAO,EAEjF,YAAaA,EAAE,OAAO,EAAE,SAAS,EAEjC,YAAaA,EAAE,OAAO,EAAE,SAAS,EAEjC,aAAcA,EAAE,OAAO,EAAE,QAAQ,QAAQ,EAEzC,OAAQA,EAAE,OAAO,EAAE,SAAS,CAC9B,CAAC,EAGYmB,EAAuBnB,EAAE,OAAO,CAC3C,QAASA,EAAE,QAAQ,EAAE,QAAQ,EAAI,EACjC,kBAAmBA,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,QAAQ,EAAE,EACrD,kBAAmBA,EAAE,QAAQ,EAAE,QAAQ,EAAI,EAC3C,gBAAiBA,EAAE,QAAQ,EAAE,QAAQ,EAAI,EAEzC,OAAQA,EAAE,KAAK,CAAC,QAAS,QAAQ,CAAC,EAAE,QAAQ,OAAO,CACrD,CAAC,EAEYoB,EAAoBpB,EAAE,mBAAmB,OAAQ,CAC5DA,EAAE,OAAO,CACP,KAAMA,EAAE,QAAQ,QAAQ,EAExB,SAAUA,EAAE,MAAMA,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,CACrC,CAAC,CAEH,CAAC,EAUYqB,EAA4D,CACvE,CAAE,GAAI,OAAU,MAAO,OAAW,KAAM,eAAgB,aAAc,SAAU,OAAQ,SAAU,EAClG,CAAE,GAAI,OAAU,MAAO,QAAW,KAAM,eAAgB,YAAa,gBAAmB,aAAc,SAAU,OAAQ,SAAU,EAClI,CAAE,GAAI,QAAU,MAAO,QAAW,KAAM,eAAgB,YAAa,gBAAmB,aAAc,SAAU,OAAQ,SAAU,EAClI,CAAE,GAAI,SAAU,MAAO,UAAW,KAAM,eAAgB,YAAa,kBAAmB,aAAc,SAAU,OAAQ,SAAU,EAClI,CAAE,GAAI,SAAU,MAAO,SAAW,KAAM,eAAgB,YAAa,iBAAmB,aAAc,SAAU,OAAQ,SAAU,EAClI,CAAE,GAAI,SAAU,MAAO,SAAW,KAAM,SAAgB,aAAc,SAAU,OAAQ,SAAU,CACpG,EAEaC,EAActB,EAAE,OAAO,CAClC,GAAIA,EAAE,OAAO,EAAE,MAAM,gBAAiB,iCAAiC,EACvE,KAAMA,EAAE,OAAO,EACf,OAAQoB,EAER,iBAAkBpB,EAAE,OAAO,EAAE,SAAS,EACtC,OAAQA,EAAE,MAAMiB,CAAgB,EAAE,QAAQ,CAAC,CAAC,EAC5C,QAASjB,EAAE,MAAMkB,CAAiB,EAAE,IAAI,CAAC,EAAE,QAAQG,CAAe,EAClE,cAAerB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE,EAE1D,iBAAkBA,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE,EAC7D,eAAgBmB,EAAqB,QAAQ,CAAC,CAAC,CACjD,CAAC,EAEYI,EAAqBvB,EAAE,MAAMsB,CAAW,EAAE,QAAQ,CAAC,CAAC,EAEpDE,EAAwBxB,EAAE,OAAO,CAC5C,KAAMA,EAAE,OAAO,EAEf,IAAKA,EAAE,OAAO,EAId,aAAcA,EAAE,OAAO,EAAE,SAAS,EAElC,MAAOA,EAAE,OAAO,EAAE,SAAS,EAG3B,eAAgBA,EAAE,OAAO,EAAE,SAAS,CACtC,CAAC,EAEYyB,EAAwBzB,EAAE,OAAO,CAC5C,QAASA,EAAE,QAAQ,EAAE,QAAQ,EAAK,EAClC,KAAMA,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,IAAI,EACnC,KAAMA,EAAE,OAAO,EAAE,QAAQ,WAAW,EAEpC,MAAOA,EAAE,OAAO,EAAE,SAAS,EAE3B,UAAWA,EAAE,OAAO,EAAE,QAAQ,wBAAwB,EAEtD,QAASA,EAAE,MAAMwB,CAAqB,EAAE,QAAQ,CAAC,CAAC,EAElD,WAAYxB,EAAE,OAAO,EAAE,SAAS,CAClC,CAAC,EAAE,QAAQ,CAAC,CAAC,IC7Hb,IAAA4B,EAAA,GAAAC,EAAAD,EAAA,wBAAAE,EAAA,kBAAAC,EAAA,qBAAAC,EAAA,uBAAAC,IAAA,OAAS,KAAAC,MAAS,MAClB,OAAS,gBAAAC,EAAc,cAAAC,MAAkB,KACzC,OAAS,WAAAC,MAAe,OAOxB,SAASC,EAAWC,EAAmB,CACrC,IAAMC,EAAUH,EAAQE,EAAK,MAAM,EACnC,GAAI,CAACH,EAAWI,CAAO,EAAG,OAE1B,IAAMC,EAAUN,EAAaK,EAAS,OAAO,EAC7C,QAAWE,KAAQD,EAAQ,MAAM;AAAA,CAAI,EAAG,CACtC,IAAME,EAAUD,EAAK,KAAK,EAC1B,GAAI,CAACC,GAAWA,EAAQ,WAAW,GAAG,EAAG,SACzC,IAAMC,EAAQD,EAAQ,QAAQ,GAAG,EACjC,GAAIC,IAAU,GAAI,SAClB,IAAMC,EAAMF,EAAQ,MAAM,EAAGC,CAAK,EAAE,KAAK,EACnCE,EAAQH,EAAQ,MAAMC,EAAQ,CAAC,EAAE,KAAK,EACvC,QAAQ,IAAIC,CAAG,IAClB,QAAQ,IAAIA,CAAG,EAAIC,GAGzB,CA4NO,SAASf,EAAcgB,EAAuB,CACnD,GAAI,OAAOA,GAAQ,SACjB,OAAOA,EAAI,QAAQ,eAAgB,CAACC,EAAQC,IAAS,QAAQ,IAAIA,CAAI,GAAK,EAAE,EAE9E,GAAI,MAAM,QAAQF,CAAG,EACnB,OAAOA,EAAI,IAAIhB,CAAa,EAE9B,GAAIgB,IAAQ,MAAQ,OAAOA,GAAQ,SAAU,CAC3C,IAAMG,EAAkC,CAAC,EACzC,OAAW,CAACL,EAAKC,CAAK,IAAK,OAAO,QAAQC,CAA8B,EACtEG,EAAOL,CAAG,EAAId,EAAce,CAAK,EAEnC,OAAOI,EAET,OAAOH,CACT,CAKO,SAASf,EAAiBmB,EAAmC,CAClE,IAAMC,EAAQD,EACV,CAACA,CAAU,EACX,CACEd,EAAQ,QAAQ,IAAI,EAAG,aAAa,EACpCA,EAAQ,QAAQ,IAAI,EAAG,qBAAqB,CAC9C,EAGJC,EAAW,QAAQ,IAAI,CAAC,EAExB,IAAIe,EACAC,EAEJ,QAAWC,KAAKH,EACd,GAAIhB,EAAWmB,CAAC,EAAG,CACjBF,EAAMlB,EAAaoB,EAAG,OAAO,EAC7BD,EAAYC,EACZ,MAIJ,GAAI,CAACF,GAAO,CAACC,EACX,MAAM,IAAI,MACR;AAAA,YACeF,EAAM,KAAK,IAAI,GAChC,EAGF,IAAII,EACJ,GAAI,CACFA,EAAS,KAAK,MAAMH,CAAG,CACzB,OAASI,EAAP,CACA,MAAM,IAAI,MAAM,mBAAmBH,MAAcG,EAAE,SAAS,CAC9D,CAGA,IAAMC,EAAW3B,EAAcyB,CAAM,EAG/BN,EAASpB,EAAmB,UAAU4B,CAAQ,EACpD,GAAI,CAACR,EAAO,QAAS,CACnB,IAAMS,EAAST,EAAO,MAAM,OACzB,IAAKU,GAAM,KAAKA,EAAE,KAAK,KAAK,GAAG,MAAMA,EAAE,SAAS,EAChD,KAAK;AAAA,CAAI,EACZ,MAAM,IAAI,MAAM,6BAA6BN;AAAA,EAAgBK,GAAQ,EAGvE,OAAOT,EAAO,IAChB,CAKO,SAASjB,EAAmB4B,EAAgC,CACjE,IAAMC,EAAqB,CAAC,EAE5B,OAAW,CAACC,EAAIC,CAAK,IAAK,OAAO,QAAQH,EAAO,MAAM,EAAG,CACvD,GAAI,CAACzB,EAAW4B,EAAM,SAAS,EAAG,CAChCF,EAAS,KAAK,UAAUC,8BAA+BC,EAAM,WAAW,EACxE,SAGF,GAAIA,EAAM,OAAS,cAAe,CAChC,IAAMC,EAAY5B,EAAQ2B,EAAM,UAAW,SAAS,EAC/C5B,EAAW6B,CAAS,GACvBH,EAAS,KACP,UAAUC,0CAA2CC,EAAM,iFAE7D,EAKJ,IAAME,EAAeF,EAAM,UAAY,SACjCG,EAAiBN,EAAO,UAAUK,CAAY,EAChDF,EAAM,OAAS,gBAAkB,CAACG,GAAkB,CAACA,EAAe,SACtEL,EAAS,KACP,UAAUC,iBAAkBG,+CACTA,gEACrB,EAKJ,OAAW,CAACH,EAAIK,CAAI,IAAK,OAAO,QAAQP,EAAO,KAAK,EAC7CA,EAAO,OAAOO,EAAK,KAAK,GAC3BN,EAAS,KAAK,SAASC,iCAAkCK,EAAK,QAAQ,EAK1E,GAAIP,EAAO,SAAS,SAAS,QAC3B,OAAW,CAACZ,EAAMoB,CAAO,IAAK,OAAO,QAAQR,EAAO,SAAS,SAAS,QAAQ,EACvEA,EAAO,OAAOQ,EAAQ,YAAY,GACrCP,EAAS,KACP,qBAAqBb,iCAAoCoB,EAAQ,eACnE,EAKN,OAAOP,CACT,CAhXA,IA6BMQ,EAMAC,EA4BAC,EAKAC,EAyDAC,EAuBAC,EAkBAC,EAMAC,EAUAC,EAiBOhD,EAvMbiD,EAAAC,EAAA,KAGAD,IACAA,IAyBMT,EAAuBpC,EAAE,OAAO,CACpC,OAAQA,EAAE,OAAO,EAAE,SAAS,EAC5B,aAAcA,EAAE,OAAO,EAAE,SAAS,EAClC,QAASA,EAAE,OAAO,EAAE,SAAS,CAC/B,CAAC,EAEKqC,EAAoBrC,EAAE,OAAO,CACjC,KAAMA,EAAE,OAAO,EACf,UAAWA,EAAE,OAAO,EACpB,KAAMA,EAAE,KAAK,CAAC,cAAe,MAAO,cAAc,CAAC,EAAE,QAAQ,aAAa,EAC1E,SAAUA,EAAE,OAAO,EAAE,SAAS,EAC9B,MAAOA,EAAE,OAAO,EAAE,SAAS,EAC3B,aAAcA,EAAE,OAAO,EAAE,SAAS,EAClC,SAAUA,EAAE,MAAMA,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC,EACxC,cAAeA,EAAE,OAAO,EAAE,QAAQ,CAAC,EAInC,oBAAqBA,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE,EAChE,eAAgBA,EAAE,OAAO,EAAE,QAAQ,SAAS,EAC5C,UAAWA,EAAE,KAAK,CAAC,UAAW,WAAY,MAAM,CAAC,EAAE,QAAQ,SAAS,EACpE,UAAWA,EAAE,OAAO,CAClB,QAASA,EAAE,QAAQ,EAAE,QAAQ,EAAK,EAClC,gBAAiBA,EAAE,OAAO,EAAE,QAAQ,EAAE,EACtC,OAAQA,EAAE,OAAO,EAAE,QAAQ,sFAAsF,EACjH,QAASA,EAAE,OAAO,EAAE,QAAQ,WAAW,CACzC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAKb,OAAQA,EAAE,KAAK,CAAC,UAAW,QAAQ,CAAC,EAAE,QAAQ,SAAS,CACzD,CAAC,EAEKsC,EAAwBtC,EAAE,OAAO,CACrC,MAAOA,EAAE,OAAO,EAChB,aAAcA,EAAE,OAAO,CACzB,CAAC,EAEKuC,EAAuBvC,EAAE,OAAO,CACpC,SAAUA,EAAE,OAAO,CACjB,QAASA,EAAE,QAAQ,EAAE,QAAQ,EAAK,EAClC,SAAUA,EAAE,OAAOA,EAAE,OAAO,EAAGsC,CAAqB,EAAE,QAAQ,CAAC,CAAC,EAChE,OAAQtC,EAAE,OAAO,CACf,GAAIA,EAAE,KAAK,CAAC,OAAQ,OAAO,CAAC,EAAE,QAAQ,MAAM,EAC5C,MAAOA,EAAE,KAAK,CAAC,mBAAoB,KAAK,CAAC,EAAE,QAAQ,kBAAkB,CACvE,CAAC,EAAE,QAAQ,CAAC,CAAC,CACf,CAAC,EAAE,QAAQ,CAAC,CAAC,EACb,SAAUA,EAAE,OAAO,CACjB,QAASA,EAAE,QAAQ,EAAE,QAAQ,EAAK,EAClC,WAAYA,EAAE,OAAO,EAAE,QAAQ,2BAA2B,EAC1D,aAAcA,EAAE,OAAO,EAAE,SAAS,EAClC,UAAWA,EAAE,MAAMA,EAAE,OAAO,CAAC,EAAE,SAAS,EACxC,OAAQA,EAAE,MAAMA,EAAE,OAAO,CACvB,QAASA,EAAE,OAAO,EAAE,SAAS,EAC7B,MAAOA,EAAE,OAAO,EAAE,SAAS,EAC3B,MAAOA,EAAE,OAAO,CAClB,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAChB,CAAC,EAAE,QAAQ,CAAC,CAAC,EACb,QAASA,EAAE,OAAO,CAChB,QAASA,EAAE,QAAQ,EAAE,QAAQ,EAAK,EAClC,MAAOA,EAAE,OAAO,EAAE,SAAS,EAC3B,aAAcA,EAAE,OAAO,EAAE,SAAS,CACpC,CAAC,EAAE,QAAQ,CAAC,CAAC,EACb,MAAOA,EAAE,OAAO,CACd,QAASA,EAAE,QAAQ,EAAE,QAAQ,EAAK,EAElC,SAAUA,EAAE,OAAO,EAAE,SAAS,EAE9B,SAAUA,EAAE,OAAO,EAAE,SAAS,EAC9B,aAAcA,EAAE,OAAO,EAAE,SAAS,CACpC,CAAC,EAAE,QAAQ,CAAC,CAAC,EACb,OAAQA,EAAE,OAAO,CACf,QAASA,EAAE,QAAQ,EAAE,QAAQ,EAAK,EAClC,YAAaA,EAAE,OAAO,EAAE,QAAQ,KAAK,EACrC,cAAeA,EAAE,OAAO,EAAE,SAAS,EACnC,KAAMA,EAAE,OAAO,EAAE,QAAQ,oBAAoB,EAC7C,MAAOA,EAAE,OAAO,EAAE,SAAS,EAC3B,OAAQA,EAAE,MAAMA,EAAE,OAAO,CACvB,QAASA,EAAE,OAAO,EAClB,MAAOA,EAAE,OAAO,CAClB,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EACd,cAAeA,EAAE,MAAMA,EAAE,OAAO,CAC9B,QAASA,EAAE,OAAO,EAClB,gBAAiBA,EAAE,MAAMA,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC,EAC/C,SAAUA,EAAE,MAAMA,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC,EACxC,MAAOA,EAAE,OAAO,EAAE,SAAS,EAK3B,KAAMA,EAAE,OAAO,EAAE,SAAS,CAC5B,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAChB,CAAC,EAAE,QAAQ,CAAC,CAAC,CACf,CAAC,EAEKwC,EAAgBxC,EAAE,OAAO,CAC7B,QAASA,EAAE,QAAQ,EAAE,QAAQ,EAAI,EACjC,SAAUA,EAAE,OAAO,EACnB,SAAUA,EAAE,OAAO,EAAE,QAAQ,KAAK,EAClC,MAAOA,EAAE,OAAO,EAChB,OAAQA,EAAE,OAAO,EACjB,QAASA,EAAE,OAAO,EAAE,QAAQ,GAAG,EAC/B,MAAOA,EAAE,OAAO,EAAE,SAAS,EAI3B,gBAAiBA,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,GAAI,EAAE,SAAS,EAC7D,QAASA,EAAE,MAAM,CACfA,EAAE,KAAK,CAAC,MAAO,SAAU,SAAS,CAAC,EACnCA,EAAE,MAAMA,EAAE,KAAK,CAAC,MAAO,SAAU,SAAS,CAAC,CAAC,CAC9C,CAAC,EAAE,QAAQ,KAAK,EAAE,UAAU+C,GAAK,MAAM,QAAQA,CAAC,EAAIA,EAAI,CAACA,CAAC,CAAC,EAC3D,OAAQ/C,EAAE,OAAO,CACf,QAASA,EAAE,OAAO,EAClB,OAAQA,EAAE,OAAO,EACjB,UAAWA,EAAE,OAAO,EAAE,SAAS,CACjC,CAAC,EAAE,SAAS,CACd,CAAC,EAEKyC,EAAgBzC,EAAE,OAAO,CAC7B,KAAMA,EAAE,OAAO,EACf,SAAUA,EAAE,MAAMA,EAAE,OAAO,CACzB,QAASA,EAAE,OAAO,EAClB,QAASA,EAAE,OAAO,EAAE,SAAS,CAC/B,CAAC,CAAC,EACF,gBAAiBA,EAAE,MAAMA,EAAE,OAAO,CAAC,EAAE,SAAS,EAC9C,MAAOA,EAAE,OAAO,EAChB,OAAQA,EAAE,OAAO,EACjB,SAAUA,EAAE,OAAO,EAAE,SAAS,EAC9B,SAAUA,EAAE,OAAO,EAAE,QAAQ,KAAK,EAClC,OAAQA,EAAE,OAAO,CACf,QAASA,EAAE,OAAO,EAClB,OAAQA,EAAE,OAAO,EACjB,UAAWA,EAAE,OAAO,EAAE,SAAS,CACjC,CAAC,EAAE,SAAS,CACd,CAAC,EAEK0C,EAAiB1C,EAAE,OAAO,CAC9B,IAAKA,EAAE,OAAO,EACd,KAAMA,EAAE,OAAO,EACf,MAAOA,EAAE,OAAO,EAAE,SAAS,CAC7B,CAAC,EAEK2C,EAAmB3C,EAAE,OAAO,CAChC,QAASA,EAAE,QAAQ,EAAE,QAAQ,EAAK,EAClC,MAAOA,EAAE,MAAM0C,CAAc,EAAE,QAAQ,CAAC,CAAC,EACzC,UAAW1C,EAAE,KAAK,CAAC,SAAU,MAAM,CAAC,EAAE,QAAQ,QAAQ,EACtD,YAAaA,EAAE,OAAO,CACpB,SAAUA,EAAE,OAAO,EAAE,QAAQ,EAAE,EAC/B,QAASA,EAAE,OAAO,EAAE,QAAQ,EAAE,CAChC,CAAC,EAAE,QAAQ,CAAC,CAAC,CACf,CAAC,EAEK4C,EAAsB5C,EAAE,OAAO,CAEnC,kBAAmBA,EAAE,OAAO,EAAE,QAAQ,EAAE,EAExC,YAAaA,EAAE,OAAO,CACpB,QAASA,EAAE,OAAO,EAClB,OAAQA,EAAE,OAAO,EACjB,UAAWA,EAAE,OAAO,EAAE,SAAS,CACjC,CAAC,EAAE,SAAS,EAEZ,GAAIA,EAAE,OAAO,CACX,aAAcA,EAAE,QAAQ,EAAE,QAAQ,EAAI,EACtC,UAAWA,EAAE,QAAQ,EAAE,QAAQ,EAAI,EACnC,WAAYA,EAAE,QAAQ,EAAE,QAAQ,EAAK,CACvC,CAAC,EAAE,QAAQ,CAAC,CAAC,CACf,CAAC,EAAE,QAAQ,CAAC,CAAC,EAEAJ,EAAqBI,EAAE,OAAO,CACzC,KAAMA,EAAE,OAAO,CACb,GAAIA,EAAE,OAAO,EACb,KAAMA,EAAE,OAAO,EACf,KAAMA,EAAE,OAAO,EAAE,QAAQ,iBAAiB,EAE1C,aAAcA,EAAE,OAAO,EAAE,SAAS,CACpC,CAAC,EACD,UAAWA,EAAE,OAAOA,EAAE,OAAO,EAAGoC,CAAoB,EAAE,QAAQ,CAAC,CAAC,EAChE,OAAQpC,EAAE,OAAOA,EAAE,OAAO,EAAGqC,CAAiB,EAAE,QAAQ,CAAC,CAAC,EAC1D,SAAUE,EAAqB,QAAQ,CAAC,CAAC,EACzC,MAAOvC,EAAE,OAAOA,EAAE,OAAO,EAAGwC,CAAa,EAAE,QAAQ,CAAC,CAAC,EACrD,SAAUxC,EAAE,OAAOA,EAAE,OAAO,EAAGyC,CAAa,EAAE,QAAQ,CAAC,CAAC,EACxD,cAAeG,EACf,KAAMD,EAAiB,QAAQ,CAAC,CAAC,EACjC,SAAUK,EAAqB,SAAS,EACxC,OAAQC,EACR,UAAWC,EAIX,SAAUlD,EAAE,MAAMA,EAAE,OAAO,CACzB,GAAIA,EAAE,OAAO,EAAE,MAAM,wBAAyB,mCAAmC,EACjF,OAAQA,EAAE,KAAK,CAAC,SAAU,SAAU,SAAU,SAAU,UAAW,QAAS,QAAQ,CAAC,EACrF,QAASA,EAAE,OAAO,EAClB,UAAWA,EAAE,OAAO,EAAE,SAAS,EAC/B,YAAaA,EAAE,OAAO,EAAE,SAAS,EACjC,QAASA,EAAE,QAAQ,EAAE,QAAQ,EAAI,CACnC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAKd,QAASA,EAAE,OAAO,CAChB,aAAcA,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE,QAAQ,GAAG,CAC7D,CAAC,EAAE,QAAQ,CAAC,CAAC,CACf,CAAC","names":["z","dayEnum","scheduleSchema","roleSchema","orgEntrySchema","channelRefSchema","workSourceSchema","businessConfigSchema","init_config","__esmMin","z","deriveStage","item","columns","set","closed","c","backlog","prefix","l","transitionDiff","fromColumn","toColumn","add","remove","closeIssue","reopen","boardLabelSchema","boardColumnSchema","boardReconcileSchema","boardSourceSchema","DEFAULT_COLUMNS","boardSchema","boardsConfigSchema","dashboardDaemonSchema","dashboardConfigSchema","init_config","__esmMin","config_exports","__export","daemonConfigSchema","expandEnvVars","loadDaemonConfig","validateWorkspaces","z","readFileSync","existsSync","resolve","loadDotEnv","dir","envPath","content","line","trimmed","eqIdx","key","value","obj","_match","name","result","configPath","paths","raw","foundPath","p","parsed","e","expanded","issues","i","config","warnings","id","agent","claudeDir","providerName","providerConfig","cron","account","providerConfigSchema","agentConfigSchema","telegramAccountSchema","channelsConfigSchema","cronJobSchema","serviceSchema","meshPeerSchema","meshConfigSchema","notificationsSchema","init_config","__esmMin","v","businessConfigSchema","boardsConfigSchema","dashboardConfigSchema"]}
@@ -0,0 +1,11 @@
1
+ import{readFileSync as A,writeFileSync as E,appendFileSync as K,existsSync as O,mkdirSync as j}from"fs";import{resolve as _}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 W(f){let e=f.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 x=500,G=".agentx/pricing/custom.json",I=class{dir;cache=null;pricing;constructor(e=_(process.cwd(),".agentx/usage")){this.dir=e,j(this.dir,{recursive:!0}),this.pricing=J()}getPricing(){return this.pricing}static calculateCost(e,r,s=w){let h=W(r||e.model||"claude-sonnet"),a=s[h]||s["claude-sonnet"],p=e.tier2InputTokens||0,C=e.tier2OutputTokens||0,T=e.tier2CacheReadTokens||0,n=e.tier2CacheCreateTokens||0,i=(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=p/1e6*a.input+C/1e6*a.output+T/1e6*a.cacheRead+n/1e6*a.cacheCreate;return i+t*F}cost(e,r){return I.calculateCost(e,r,this.pricing)}record(e,r,s,h,a,p,C,T,n){let i=this.today(),t=i.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+=r,C&&(t.model=C);let b=0,g=0,d=0,k=0,c=0,m=0,o=0,R=0;s?s.inputTokens+s.cacheReadTokens+s.cacheCreateTokens>H?(c=s.inputTokens,m=s.outputTokens,o=s.cacheReadTokens,R=s.cacheCreateTokens):(b=s.inputTokens,g=s.outputTokens,d=s.cacheReadTokens,k=s.cacheCreateTokens):h!==void 0&&a!==void 0&&(b=Math.ceil(h/4),g=Math.ceil(a/4)),t.inputTokens+=b,t.outputTokens+=g,t.cacheReadTokens+=d,t.cacheCreateTokens+=k,t.tier2InputTokens=(t.tier2InputTokens||0)+c,t.tier2OutputTokens=(t.tier2OutputTokens||0)+m,t.tier2CacheReadTokens=(t.tier2CacheReadTokens||0)+o,t.tier2CacheCreateTokens=(t.tier2CacheCreateTokens||0)+R,p&&t.errors++,t.byChannel||(t.byChannel={});let y=(T||"other").toLowerCase(),l=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};l.tasks++,l.totalDuration+=r,l.inputTokens+=b,l.outputTokens+=g,l.cacheReadTokens+=d,l.cacheCreateTokens+=k,l.tier2InputTokens+=c,l.tier2OutputTokens+=m,l.tier2CacheReadTokens+=o,l.tier2CacheCreateTokens+=R,p&&l.errors++,t.byChannel[y]=l,n&&(t.sessionKeys||(t.sessionKeys=[]),t.sessionKeys.includes(n)||(t.sessionKeys.push(n),t.sessionKeys.length>x&&t.sessionKeys.splice(0,t.sessionKeys.length-x))),i.agents[e]=t,this.save(i)}today(){let e=new Date().toISOString().slice(0,10);if(this.cache?.date===e)return this.cache;let r=this.filePath(e);if(O(r))try{return this.cache=JSON.parse(A(r,"utf-8")),this.cache}catch{}return this.cache={date:e,agents:{}},this.cache}getDate(e){let r=this.filePath(e);if(!O(r))return null;try{return JSON.parse(A(r,"utf-8"))}catch{return null}}generateDailyReport(e){let r=e||new Date().toISOString().slice(0,10),s=this.getDate(r);if(!s)return null;let h=0,a=0,p=0,C=0,T=0,n=0,i="",t=0,b={},g={},d={};for(let[k,c]of Object.entries(s.agents)){let m=this.cost(c);b[k]=m,h+=c.tasks,a+=(c.inputTokens||0)+(c.tier2InputTokens||0),p+=(c.outputTokens||0)+(c.tier2OutputTokens||0),C+=(c.cacheReadTokens||0)+(c.tier2CacheReadTokens||0),T+=(c.cacheCreateTokens||0)+(c.tier2CacheCreateTokens||0),n+=m,m>t&&(t=m,i=k);let o=c.sessionKeys?.length||0;g[k]={sessions:o,avgTasksPerSession:o>0?c.tasks/o:c.tasks};for(let[R,y]of Object.entries(c.byChannel||{})){let l=this.cost(y,c.model);d[R]=(d[R]||0)+l}}return{date:r,totalTasks:h,totalInput:a,totalOutput:p,totalCacheRead:C,totalCacheCreate:T,totalCost:n,topAgent:i,topCost:t,agentCosts:b,agentSessionStats:g,byChannel:d}}appendToTokenCosts(e,r){let s=r||_(process.cwd(),".agentx/TOKEN_COSTS.md"),h=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),p=Object.entries(e.agentCosts).sort(([,n],[,i])=>i-n).map(([n,i])=>{let t=e.agentSessionStats[n],b=t&&t.sessions>0?` (${t.sessions}s, ${t.avgTasksPerSession.toFixed(1)}t/s)`:"";return`${n}: $${h(i)}${b}`}).join(", "),C=Object.entries(e.byChannel).sort(([,n],[,i])=>i-n).map(([n,i])=>`${n}: $${h(i)}`).join(", "),T=`| ${e.date} | ${e.totalTasks} | ${a(e.totalInput)} | ${a(e.totalOutput)} | ${a(e.totalCacheRead)} | ${a(e.totalCacheCreate)} | $${h(e.totalCost)} | ${p} | ${C} |`;O(s)?K(s,T+`
2
+ `):E(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
+ `+T+`
10
+ `)}summary(e=7){let r=0,s=0,h=0,a=0,p=0,C=0,T=0,n={},i={};for(let d=0;d<e;d++){let k=new Date(Date.now()-d*864e5).toISOString().slice(0,10),c=this.getDate(k);if(c)for(let[m,o]of Object.entries(c.agents)){let R=this.cost(o);r+=o.tasks;let y=(o.inputTokens||0)+(o.tier2InputTokens||0),l=(o.outputTokens||0)+(o.tier2OutputTokens||0),S=(o.cacheReadTokens||0)+(o.tier2CacheReadTokens||0),v=(o.cacheCreateTokens||0)+(o.tier2CacheCreateTokens||0);s+=y,h+=l,a+=S,p+=v,C+=o.errors,T+=R;let u=n[m]||{tasks:0,input:0,output:0,cacheRead:0,cacheCreate:0,total:0,avgDuration:0,totalDuration:0,cost:0,sessions:0};u.tasks+=o.tasks,u.input+=y,u.output+=l,u.cacheRead+=S,u.cacheCreate+=v,u.total=u.input+u.output+u.cacheRead+u.cacheCreate,u.totalDuration+=o.totalDuration,u.avgDuration=u.totalDuration/u.tasks,u.cost+=R,u.sessions+=o.sessionKeys?.length||0,o.model&&(u.model=o.model),n[m]=u;for(let[P,$]of Object.entries(o.byChannel||{})){let N=this.cost($,o.model),D=i[P]||{tasks:0,cost:0};D.tasks+=$.tasks,D.cost+=N,i[P]=D}}}let t=s+h+a+p,b=a+p>0?a/(a+p):0,g={};for(let[d,k]of Object.entries(n))g[d]={sessions:k.sessions,avgTasksPerSession:k.sessions>0?k.tasks/k.sessions:k.tasks};return{totalTasks:r,totalTokens:t,totalInput:s,totalOutput:h,totalCacheRead:a,totalCacheCreate:p,cacheHitRatio:b,totalErrors:C,totalCost:T,byAgent:n,byChannel:i,sessionStats:g}}filePath(e){return _(this.dir,`${e}.json`)}save(e){try{E(this.filePath(e.date),JSON.stringify(e,null,2))}catch{}}};function J(){let f=JSON.parse(JSON.stringify(w));try{let e=_(process.cwd(),G);if(!O(e))return f;let r=JSON.parse(A(e,"utf-8"));for(let[s,h]of Object.entries(r))f[s]||(f[s]={input:0,output:0,cacheRead:0,cacheCreate:0}),Object.assign(f[s],h)}catch{}return f}export{I as a};
11
+ //# sourceMappingURL=chunk-CXY2RYNI.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 * 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","days","totalErrors","byAgent","i","agentInput","agentOutput","agentCR","agentCW","existing","acc","totalTokens","cacheHitRatio","sessionStats","result","raw","rates"]}
@@ -0,0 +1,2 @@
1
+ var e=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var h=Object.getOwnPropertyNames;var i=Object.prototype.hasOwnProperty;var j=(a,b,c)=>b in a?e(a,b,{enumerable:!0,configurable:!0,writable:!0,value:c}):a[b]=c;var l=(a=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(a,{get:(b,c)=>(typeof require<"u"?require:b)[c]}):a)(function(a){if(typeof require<"u")return require.apply(this,arguments);throw new Error('Dynamic require of "'+a+'" is not supported')});var m=(a,b)=>()=>(a&&(b=a(a=0)),b);var n=(a,b)=>{for(var c in b)e(a,c,{get:b[c],enumerable:!0})},k=(a,b,c,f)=>{if(b&&typeof b=="object"||typeof b=="function")for(let d of h(b))!i.call(a,d)&&d!==c&&e(a,d,{get:()=>b[d],enumerable:!(f=g(b,d))||f.enumerable});return a};var o=a=>k(e({},"__esModule",{value:!0}),a);var p=(a,b,c)=>(j(a,typeof b!="symbol"?b+"":b,c),c);export{l as a,m as b,n as c,o as d,p as e};
2
+ //# sourceMappingURL=chunk-FIOB3MEX.js.map
@@ -0,0 +1,5 @@
1
+ import{d as m,e as $,i as S}from"./chunk-B4Q4X7D5.js";S();import{readFileSync as x,writeFileSync as P,existsSync as b}from"fs";import{resolve as f}from"path";function w(r){if(r)return f(r);let e=[f(process.cwd(),"agentx.json"),f(process.cwd(),".agentx/config.json")];for(let n of e)if(b(n))return n;return e[0]}function v(r){return JSON.parse(JSON.stringify(r))}async function O(r){try{let e=await fetch(`${r.replace(/\/$/,"")}/reload`,{method:"POST",signal:AbortSignal.timeout(3e3)});return e.ok?{ok:!0}:{ok:!1,reason:`HTTP ${e.status}`}}catch(e){return{ok:!1,reason:e?.cause?.code||e?.message||"unreachable"}}}async function J(r,e={}){let{validate:n=!0,dryRun:t=!1,reload:s=!0,configPath:u,daemonUrl:c}=e,o=w(u);if(!b(o))return{success:!1,path:o,error:`Config not found at ${o}. Run \`agentx init\` first.`};let d;try{d=x(o,"utf-8")}catch(a){return{success:!1,path:o,error:`Cannot read ${o}: ${a.message}`}}let g;try{g=JSON.parse(d)}catch(a){return{success:!1,path:o,error:`Invalid JSON in ${o}: ${a.message}`}}let i=v(g);try{await r(i)}catch(a){return{success:!1,path:o,error:`Mutator threw: ${a.message}`}}if(n){let a=$(i),l=m.safeParse(a);if(!l.success){let k=l.error.issues.map(y=>` ${y.path.join(".")||"<root>"}: ${y.message}`).join(`
2
+ `);return{success:!1,path:o,error:`Validation failed:
3
+ ${k}`,after:i}}}if(t)return{success:!0,path:o,after:i,dryRun:!0};try{P(o,JSON.stringify(i,null,2)+`
4
+ `)}catch(a){return{success:!1,path:o,error:`Write failed: ${a.message}`,after:i}}let p=!1,h;if(s){let a=c||R(i),l=await O(a);l.ok?p=!0:h=l.reason}return{success:!0,path:o,after:i,reloaded:p,reloadSkipped:h}}function R(r){let e=r?.node?.bind;if(!e)return"http://127.0.0.1:18800";let[n,t]=e.split(":");return`http://${n==="0.0.0.0"||n==="::"?"127.0.0.1":n}:${t}`}function N(r,e,n){let t=e.split("."),s=r;for(let u=0;u<t.length-1;u++){let c=t[u];(s[c]===void 0||s[c]===null)&&(s[c]={}),s=s[c]}s[t[t.length-1]]=n}function j(r,e){let n=e.split("."),t=r;for(let s of n){if(t==null)return;t=t[s]}return t}function A(r,e){let n=e.split("."),t=r;for(let s=0;s<n.length-1;s++){if(t==null)return;t=t[n[s]]}t!=null&&delete t[n[n.length-1]]}export{J as a,N as b,j as c,A as d};
5
+ //# sourceMappingURL=chunk-HLRVDCNH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/daemon/config-mutator.ts"],"sourcesContent":["import { readFileSync, writeFileSync, existsSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { daemonConfigSchema, expandEnvVars } from \"./config\"\n\n// --- Config mutator ---\n//\n// Single helper for every \"edit agentx.json\" code path. Handles:\n// 1. Read raw JSON (preserves ${VAR} tokens)\n// 2. Call user-supplied mutator on a deep clone\n// 3. Validate the env-expanded clone through the Zod schema\n// 4. Pretty-print and write back (raw, unexpanded form)\n// 5. Signal the running daemon via POST /reload if reachable\n//\n// Commands that previously called saveConfig() now go through this,\n// so every add-wizard gets pre-save validation and auto-reload for free.\n\nexport interface MutationOptions {\n /** Run the Zod schema against the env-expanded mutated config. Default: true. */\n validate?: boolean\n /** Compute + print the diff but do NOT write. Default: false. */\n dryRun?: boolean\n /** POST /reload to the running daemon if reachable. Default: true. */\n reload?: boolean\n /** Override the config file path. Default: ./agentx.json then .agentx/config.json. */\n configPath?: string\n /** Override daemon URL for the reload signal. Default: inferred from config. */\n daemonUrl?: string\n}\n\nexport interface MutationResult {\n success: boolean\n /** Path that was (or would have been) written. */\n path?: string\n /** The mutated raw config (before write, after mutator fn). */\n after?: unknown\n /** Validation error if the Zod parse failed. */\n error?: string\n /** True if the daemon acknowledged the reload. */\n reloaded?: boolean\n /** Reload failure reason — daemon unreachable, 5xx, etc. Not a hard error. */\n reloadSkipped?: string\n /** True when opts.dryRun was set. */\n dryRun?: boolean\n}\n\nfunction findConfigPath(override?: string): string {\n if (override) return resolve(override)\n const candidates = [\n resolve(process.cwd(), \"agentx.json\"),\n resolve(process.cwd(), \".agentx/config.json\"),\n ]\n for (const c of candidates) if (existsSync(c)) return c\n return candidates[0]\n}\n\nfunction deepClone<T>(v: T): T {\n return JSON.parse(JSON.stringify(v))\n}\n\nasync function signalReload(daemonUrl: string): Promise<{ ok: boolean; reason?: string }> {\n try {\n const res = await fetch(`${daemonUrl.replace(/\\/$/, \"\")}/reload`, {\n method: \"POST\",\n signal: AbortSignal.timeout(3000),\n })\n if (!res.ok) return { ok: false, reason: `HTTP ${res.status}` }\n return { ok: true }\n } catch (e: any) {\n // Daemon not running is fine — user is editing config before start.\n return { ok: false, reason: e?.cause?.code || e?.message || \"unreachable\" }\n }\n}\n\n/**\n * Read current config, apply `mutator(cfg)` in place, validate, write, reload.\n *\n * The mutator receives the raw parsed JSON (with `${VAR}` tokens intact). Mutate\n * the object directly — no need to return anything. Validation runs against an\n * env-expanded clone so schema errors are caught before we overwrite the file.\n */\nexport async function applyConfigMutation(\n mutator: (cfg: any) => void | Promise<void>,\n opts: MutationOptions = {},\n): Promise<MutationResult> {\n const {\n validate = true,\n dryRun = false,\n reload = true,\n configPath,\n daemonUrl,\n } = opts\n\n const path = findConfigPath(configPath)\n\n if (!existsSync(path)) {\n return { success: false, path, error: `Config not found at ${path}. Run \\`agentx init\\` first.` }\n }\n\n let raw: string\n try {\n raw = readFileSync(path, \"utf-8\")\n } catch (e: any) {\n return { success: false, path, error: `Cannot read ${path}: ${e.message}` }\n }\n\n let current: any\n try {\n current = JSON.parse(raw)\n } catch (e: any) {\n return { success: false, path, error: `Invalid JSON in ${path}: ${e.message}` }\n }\n\n const mutated = deepClone(current)\n try {\n await mutator(mutated)\n } catch (e: any) {\n return { success: false, path, error: `Mutator threw: ${e.message}` }\n }\n\n if (validate) {\n const expanded = expandEnvVars(mutated)\n const parsed = daemonConfigSchema.safeParse(expanded)\n if (!parsed.success) {\n const issues = parsed.error.issues\n .map((i) => ` ${i.path.join(\".\") || \"<root>\"}: ${i.message}`)\n .join(\"\\n\")\n return { success: false, path, error: `Validation failed:\\n${issues}`, after: mutated }\n }\n }\n\n if (dryRun) {\n return { success: true, path, after: mutated, dryRun: true }\n }\n\n try {\n writeFileSync(path, JSON.stringify(mutated, null, 2) + \"\\n\")\n } catch (e: any) {\n return { success: false, path, error: `Write failed: ${e.message}`, after: mutated }\n }\n\n let reloaded = false\n let reloadSkipped: string | undefined\n\n if (reload) {\n const url = daemonUrl || inferDaemonUrl(mutated)\n const res = await signalReload(url)\n if (res.ok) reloaded = true\n else reloadSkipped = res.reason\n }\n\n return { success: true, path, after: mutated, reloaded, reloadSkipped }\n}\n\nfunction inferDaemonUrl(cfg: any): string {\n const bind: string | undefined = cfg?.node?.bind\n if (!bind) return \"http://127.0.0.1:18800\"\n // bind is \"host:port\" (host may be 0.0.0.0)\n const [host, port] = bind.split(\":\")\n const reachable = host === \"0.0.0.0\" || host === \"::\" ? \"127.0.0.1\" : host\n return `http://${reachable}:${port}`\n}\n\n/**\n * Convenience wrapper for dot-path set: `agents.devops.model` = value.\n * Creates intermediate objects as needed. Array indices (`crons.foo.onError.0`)\n * supported.\n */\nexport function setAtPath(obj: any, path: string, value: unknown): void {\n const parts = path.split(\".\")\n let cur = obj\n for (let i = 0; i < parts.length - 1; i++) {\n const k = parts[i]\n if (cur[k] === undefined || cur[k] === null) cur[k] = {}\n cur = cur[k]\n }\n cur[parts[parts.length - 1]] = value\n}\n\nexport function getAtPath(obj: any, path: string): unknown {\n const parts = path.split(\".\")\n let cur = obj\n for (const k of parts) {\n if (cur == null) return undefined\n cur = cur[k]\n }\n return cur\n}\n\nexport function unsetAtPath(obj: any, path: string): void {\n const parts = path.split(\".\")\n let cur = obj\n for (let i = 0; i < parts.length - 1; i++) {\n if (cur == null) return\n cur = cur[parts[i]]\n }\n if (cur != null) delete cur[parts[parts.length - 1]]\n}\n"],"mappings":"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","names":["init_config","readFileSync","writeFileSync","existsSync","resolve","findConfigPath","override","candidates","c","deepClone","v","signalReload","daemonUrl","res","applyConfigMutation","mutator","opts","validate","dryRun","reload","configPath","path","raw","e","current","mutated","expanded","expandEnvVars","parsed","daemonConfigSchema","issues","i","reloaded","reloadSkipped","url","inferDaemonUrl","cfg","bind","host","port","setAtPath","obj","value","parts","cur","k","getAtPath","unsetAtPath"]}