agentix-cli 0.46.2 → 0.47.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/dist/{App-QTUGMHG7.js → App-RZDJH6MK.js} +2 -2
  2. package/dist/{ChatApp-634FDG3W.js → ChatApp-6TXOFUDH.js} +2 -2
  3. package/dist/{agent-WT4RB5SO.js → agent-I6RXAEXG.js} +2 -2
  4. package/dist/{board-dashboard-IXCW2KIV.js → board-dashboard-4LAQOB74.js} +4 -4
  5. package/dist/{chunk-QJANPP4C.js → chunk-ANLKBCYH.js} +6 -6
  6. package/dist/{chunk-QJANPP4C.js.map → chunk-ANLKBCYH.js.map} +1 -1
  7. package/dist/{chunk-7A7RC3R4.js → chunk-BHPIAG3E.js} +2 -2
  8. package/dist/{chunk-RY2EULHS.js → chunk-BJWK7FPT.js} +2 -2
  9. package/dist/{chunk-6HMZ5TTM.js → chunk-BZLHS3VF.js} +2 -2
  10. package/dist/{chunk-QLTM3F7M.js → chunk-DJQA2CWD.js} +10 -10
  11. package/dist/{chunk-QLTM3F7M.js.map → chunk-DJQA2CWD.js.map} +1 -1
  12. package/dist/{chunk-V5FZJUK4.js → chunk-FNLGY4UV.js} +2 -2
  13. package/dist/{chunk-V5FZJUK4.js.map → chunk-FNLGY4UV.js.map} +1 -1
  14. package/dist/{chunk-LUSFRS7R.js → chunk-FQBPSL36.js} +2 -2
  15. package/dist/{chunk-HA6RYFST.js → chunk-FXGSSZAP.js} +2 -2
  16. package/dist/{chunk-NSYY5NCR.js → chunk-J63MRNXF.js} +2 -2
  17. package/dist/{chunk-SGFXE5FP.js → chunk-KTM7HSEB.js} +2 -2
  18. package/dist/chunk-LYOXXD4T.js +123 -0
  19. package/dist/chunk-LYOXXD4T.js.map +1 -0
  20. package/dist/{chunk-TNFOGJWZ.js → chunk-MDTKQZC6.js} +2 -2
  21. package/dist/{chunk-ICZPZYYR.js → chunk-NXJBAKBR.js} +2 -2
  22. package/dist/{chunk-WLVQDEOT.js → chunk-OSSI6R4K.js} +2 -2
  23. package/dist/{chunk-L3A2LVWC.js → chunk-QURVHYBP.js} +13 -13
  24. package/dist/{chunk-2TJ5L7CB.js → chunk-SFLF64Q3.js} +2 -2
  25. package/dist/{chunk-E6EW42EA.js → chunk-VHTFHGVW.js} +2 -2
  26. package/dist/{chunk-CTCOWVNL.js → chunk-VZDKE2EZ.js} +2 -2
  27. package/dist/{chunk-LGBBKLUO.js → chunk-W544ZCCO.js} +2 -2
  28. package/dist/chunk-WZGABQ72.js +59 -0
  29. package/dist/chunk-WZGABQ72.js.map +1 -0
  30. package/dist/chunk-XMRI32CY.js +160 -0
  31. package/dist/chunk-XMRI32CY.js.map +1 -0
  32. package/dist/{chunk-NWOGPKD7.js → chunk-XMWOV2D5.js} +2 -2
  33. package/dist/{chunk-5ZWNGD5D.js → chunk-YIXQDOPN.js} +130 -126
  34. package/dist/chunk-YIXQDOPN.js.map +1 -0
  35. package/dist/cli.js +1 -1
  36. package/dist/client-LAIP23IP.js +2 -0
  37. package/dist/config-OZ2OOKFA.js +2 -0
  38. package/dist/config-mutator-EUF7QQ4V.js +2 -0
  39. package/dist/doctor-PEE3F5TX.js +2 -0
  40. package/dist/index.d.ts +147 -6
  41. package/dist/index.js +1 -1
  42. package/dist/program-PMHQRD5E.js +2 -0
  43. package/dist/{promote-R6WVBMT7.js → promote-RNSGW4AJ.js} +2 -2
  44. package/dist/query-BOUVRUU4.js +2 -0
  45. package/dist/registry-GGU43EOI.js +2 -0
  46. package/dist/{request-planner-KYQCJDAW.js → request-planner-5354IOYM.js} +2 -2
  47. package/dist/{review-candidates-ROFQR6XE.js → review-candidates-UTWMO7UO.js} +2 -2
  48. package/dist/{routing-OC3QJI3W.js → routing-JGNWB6VJ.js} +2 -2
  49. package/dist/{schedule-tool-VQCNMBJF.js → schedule-tool-6SNLG2NA.js} +2 -2
  50. package/dist/scheduler-N25EAWF6.js +2 -0
  51. package/dist/{seat-3WT3VJC2.js → seat-STLDEYSO.js} +2 -2
  52. package/dist/{select-KPQWIC22.js → select-VP66P6XB.js} +2 -2
  53. package/dist/{surface-usage-UREILT2E.js → surface-usage-FM2ZHGYE.js} +2 -2
  54. package/dist/wiki-SKIIAVCU.js +2 -0
  55. package/package.json +1 -1
  56. package/dist/chunk-5ZWNGD5D.js.map +0 -1
  57. package/dist/chunk-7KED5NB2.js +0 -160
  58. package/dist/chunk-7KED5NB2.js.map +0 -1
  59. package/dist/chunk-BIFJSQB5.js +0 -123
  60. package/dist/chunk-BIFJSQB5.js.map +0 -1
  61. package/dist/chunk-VMDA2SX2.js +0 -43
  62. package/dist/chunk-VMDA2SX2.js.map +0 -1
  63. package/dist/client-NTC667CA.js +0 -2
  64. package/dist/config-5V444ZFW.js +0 -2
  65. package/dist/config-mutator-E6NQLFWR.js +0 -2
  66. package/dist/doctor-7Z4VLXC4.js +0 -2
  67. package/dist/program-AHTQMAKN.js +0 -2
  68. package/dist/query-6EMTLCHQ.js +0 -2
  69. package/dist/registry-FBWGWB5K.js +0 -2
  70. package/dist/scheduler-NRXPVG7P.js +0 -2
  71. package/dist/wiki-LGHX3WPI.js +0 -2
  72. /package/dist/{App-QTUGMHG7.js.map → App-RZDJH6MK.js.map} +0 -0
  73. /package/dist/{ChatApp-634FDG3W.js.map → ChatApp-6TXOFUDH.js.map} +0 -0
  74. /package/dist/{agent-WT4RB5SO.js.map → agent-I6RXAEXG.js.map} +0 -0
  75. /package/dist/{board-dashboard-IXCW2KIV.js.map → board-dashboard-4LAQOB74.js.map} +0 -0
  76. /package/dist/{chunk-7A7RC3R4.js.map → chunk-BHPIAG3E.js.map} +0 -0
  77. /package/dist/{chunk-RY2EULHS.js.map → chunk-BJWK7FPT.js.map} +0 -0
  78. /package/dist/{chunk-6HMZ5TTM.js.map → chunk-BZLHS3VF.js.map} +0 -0
  79. /package/dist/{chunk-LUSFRS7R.js.map → chunk-FQBPSL36.js.map} +0 -0
  80. /package/dist/{chunk-HA6RYFST.js.map → chunk-FXGSSZAP.js.map} +0 -0
  81. /package/dist/{chunk-NSYY5NCR.js.map → chunk-J63MRNXF.js.map} +0 -0
  82. /package/dist/{chunk-SGFXE5FP.js.map → chunk-KTM7HSEB.js.map} +0 -0
  83. /package/dist/{chunk-TNFOGJWZ.js.map → chunk-MDTKQZC6.js.map} +0 -0
  84. /package/dist/{chunk-ICZPZYYR.js.map → chunk-NXJBAKBR.js.map} +0 -0
  85. /package/dist/{chunk-WLVQDEOT.js.map → chunk-OSSI6R4K.js.map} +0 -0
  86. /package/dist/{chunk-L3A2LVWC.js.map → chunk-QURVHYBP.js.map} +0 -0
  87. /package/dist/{chunk-2TJ5L7CB.js.map → chunk-SFLF64Q3.js.map} +0 -0
  88. /package/dist/{chunk-E6EW42EA.js.map → chunk-VHTFHGVW.js.map} +0 -0
  89. /package/dist/{chunk-CTCOWVNL.js.map → chunk-VZDKE2EZ.js.map} +0 -0
  90. /package/dist/{chunk-LGBBKLUO.js.map → chunk-W544ZCCO.js.map} +0 -0
  91. /package/dist/{chunk-NWOGPKD7.js.map → chunk-XMWOV2D5.js.map} +0 -0
  92. /package/dist/{client-NTC667CA.js.map → client-LAIP23IP.js.map} +0 -0
  93. /package/dist/{config-5V444ZFW.js.map → config-OZ2OOKFA.js.map} +0 -0
  94. /package/dist/{config-mutator-E6NQLFWR.js.map → config-mutator-EUF7QQ4V.js.map} +0 -0
  95. /package/dist/{doctor-7Z4VLXC4.js.map → doctor-PEE3F5TX.js.map} +0 -0
  96. /package/dist/{program-AHTQMAKN.js.map → program-PMHQRD5E.js.map} +0 -0
  97. /package/dist/{promote-R6WVBMT7.js.map → promote-RNSGW4AJ.js.map} +0 -0
  98. /package/dist/{query-6EMTLCHQ.js.map → query-BOUVRUU4.js.map} +0 -0
  99. /package/dist/{registry-FBWGWB5K.js.map → registry-GGU43EOI.js.map} +0 -0
  100. /package/dist/{request-planner-KYQCJDAW.js.map → request-planner-5354IOYM.js.map} +0 -0
  101. /package/dist/{review-candidates-ROFQR6XE.js.map → review-candidates-UTWMO7UO.js.map} +0 -0
  102. /package/dist/{routing-OC3QJI3W.js.map → routing-JGNWB6VJ.js.map} +0 -0
  103. /package/dist/{schedule-tool-VQCNMBJF.js.map → schedule-tool-6SNLG2NA.js.map} +0 -0
  104. /package/dist/{scheduler-NRXPVG7P.js.map → scheduler-N25EAWF6.js.map} +0 -0
  105. /package/dist/{seat-3WT3VJC2.js.map → seat-STLDEYSO.js.map} +0 -0
  106. /package/dist/{select-KPQWIC22.js.map → select-VP66P6XB.js.map} +0 -0
  107. /package/dist/{surface-usage-UREILT2E.js.map → surface-usage-FM2ZHGYE.js.map} +0 -0
  108. /package/dist/{wiki-LGHX3WPI.js.map → wiki-SKIIAVCU.js.map} +0 -0
@@ -1,4 +1,4 @@
1
- import{a as o}from"./chunk-QLTM3F7M.js";import{existsSync as s}from"fs";import{resolve as a}from"path";function u(){return!!(process.env.VITEST||process.env.NODE_ENV==="test"||process.env.AGENTX_NO_TELEMETRY)}function c(){let n=process.cwd();for(let t=0;t<5;t++){if(s(a(n,".agentx"))||s(a(n,"agentx.json")))return n;let e=a(n,"..");if(e===n)break;n=e}return null}function d(n=new Date){return n.toISOString().slice(0,10)}function m(n,t,e=new Date){try{if(!t||u()||!c())return;let r=o({quiet:!0});if(!r)return;r.prepare(`INSERT INTO surface_usage (kind, name, day, count, last_at)
1
+ import{a as o}from"./chunk-DJQA2CWD.js";import{existsSync as s}from"fs";import{resolve as a}from"path";function u(){return!!(process.env.VITEST||process.env.NODE_ENV==="test"||process.env.AGENTX_NO_TELEMETRY)}function c(){let n=process.cwd();for(let t=0;t<5;t++){if(s(a(n,".agentx"))||s(a(n,"agentx.json")))return n;let e=a(n,"..");if(e===n)break;n=e}return null}function d(n=new Date){return n.toISOString().slice(0,10)}function m(n,t,e=new Date){try{if(!t||u()||!c())return;let r=o({quiet:!0});if(!r)return;r.prepare(`INSERT INTO surface_usage (kind, name, day, count, last_at)
2
2
  VALUES (?, ?, ?, 1, ?)
3
3
  ON CONFLICT(kind, name, day)
4
4
  DO UPDATE SET count = count + 1, last_at = excluded.last_at`).run(n,t,d(e),e.toISOString())}catch{}}function g(n={}){try{let t=o({quiet:!0});if(!t)return[];let e=new Date(Date.now()-(n.sinceDays??30)*864e5).toISOString().slice(0,10),r=["day >= ?"],i=[e];return n.kind&&(r.push("kind = ?"),i.push(n.kind)),t.prepare(`SELECT kind, name, SUM(count) AS count, COUNT(DISTINCT day) AS days, MAX(last_at) AS last_at
@@ -6,4 +6,4 @@ import{a as o}from"./chunk-QLTM3F7M.js";import{existsSync as s}from"fs";import{r
6
6
  WHERE ${r.join(" AND ")}
7
7
  GROUP BY kind, name
8
8
  ORDER BY count DESC`).all(...i)}catch{return[]}}function E(n){let t=[],e=n;for(;e&&typeof e.name=="function";){let r=e.name();e.parent&&t.unshift(r),e=e.parent}return t.join(" ")}var f=new Set(["guard check","completion","serve"]);function T(n){return!!n&&!f.has(n)}export{m as a,g as b,E as c,T as d};
9
- //# sourceMappingURL=chunk-2TJ5L7CB.js.map
9
+ //# sourceMappingURL=chunk-SFLF64Q3.js.map
@@ -1,4 +1,4 @@
1
- import{b as Y,c as X,d as G,e as D,f as I,g as M,h as W}from"./chunk-7CVJ4JOE.js";import{a as K}from"./chunk-RY2EULHS.js";import{l as B,o as q}from"./chunk-QJANPP4C.js";import{existsSync as _,readFileSync as re,writeFileSync as oe,appendFileSync as be,mkdirSync as ye}from"fs";import{dirname as ke,resolve as R}from"path";import{fileURLToPath as we}from"url";import{hostname as $e}from"os";import{spawn as Ae}from"child_process";q();import{existsSync as Q,readFileSync as V,writeFileSync as F,copyFileSync as he}from"fs";import{resolve as Z}from"path";function ee(t,e={}){let n=e.configPath||Z(process.cwd(),"agentx.json");if(!Q(n))throw new Error(`agentx.json not found at ${n}`);let a=JSON.parse(V(n,"utf-8")),r=t(a),o=typeof r=="string"?{summary:r}:r,c=JSON.stringify(a,null,2)+`
1
+ import{b as Y,c as X,d as G,e as D,f as I,g as M,h as W}from"./chunk-7CVJ4JOE.js";import{a as K}from"./chunk-BJWK7FPT.js";import{l as B,o as q}from"./chunk-ANLKBCYH.js";import{existsSync as _,readFileSync as re,writeFileSync as oe,appendFileSync as be,mkdirSync as ye}from"fs";import{dirname as ke,resolve as R}from"path";import{fileURLToPath as we}from"url";import{hostname as $e}from"os";import{spawn as Ae}from"child_process";q();import{existsSync as Q,readFileSync as V,writeFileSync as F,copyFileSync as he}from"fs";import{resolve as Z}from"path";function ee(t,e={}){let n=e.configPath||Z(process.cwd(),"agentx.json");if(!Q(n))throw new Error(`agentx.json not found at ${n}`);let a=JSON.parse(V(n,"utf-8")),r=t(a),o=typeof r=="string"?{summary:r}:r,c=JSON.stringify(a,null,2)+`
2
2
  `,p=n+".tmp";F(p,c,"utf-8");try{JSON.parse(V(p,"utf-8"))}catch(d){throw new Error(`config did not round-trip cleanly: ${d.message}`)}let i;return e.skipBackup||(i=`${n}.bak.${Date.now()}`,he(n,i)),F(n,c,"utf-8"),e.skipReload||fe().catch(()=>{}),{summary:o.summary,payload:o.payload,backupPath:i}}function te(t,e={}){let n=e.configPath||Z(process.cwd(),"agentx.json");if(Q(n)&&!e.overwrite)throw new Error(`agentx.json already exists at ${n}. Pass overwrite=true to replace it.`);return F(n,JSON.stringify(t,null,2)+`
3
3
  `,"utf-8"),{file:n}}async function fe(){try{let e=B().dashboard.daemonUrl?.replace(/\/+$/,"")||"http://127.0.0.1:18800";await fetch(`${e}/reload`,{method:"POST"}).catch(()=>null)}catch{}}function ne(t,e){let n=t.configExists?t.agentCount===0?"Add your first agent":"Add another agent":"Set up your team",a=`<header class="ax-topbar">
4
4
  <div class="ax-topbar__left">
@@ -737,4 +737,4 @@ tr:hover td { background: var(--bg-hover); }
737
737
  <div class="meta-line">${U((d.meta.tags||[]).slice(0,6),e)}</div>
738
738
  <h3><a href="${p}/article/${encodeURIComponent(d.path)}">${s(d.meta.title)}</a></h3>
739
739
  </div>`;return $(`${e} \xB7 ${n}`,C(r),i)}function P(t,e,n){let a=t.rebuildIndex(),r=L(a.articles.map(c=>({title:c.title,path:c.path,tags:c.tags}))),o='<div class="nav-section">';o+=`<div class="nav-section-title">${s(e)}</div>`,o+=`<a class="nav-item${n?"":" active"}" href="/">Home</a>`,o+='<a class="nav-item" href="/lint">Health check</a>',o+="</div>";for(let[c,p]of r){o+='<div class="nav-section">',o+=`<div class="nav-section-title">${J(c)}</div>`;for(let i of p){let d=i.path===n;o+=`<a class="nav-item${d?" active":""}" href="/article/${encodeURIComponent(i.path)}">${s(i.title)}</a>`}o+="</div>"}return o}function Ge(t){let{wikiDir:e,agentFilter:n,peerUrls:a=[],mode:r="graph",pathPrefix:o=""}=t,c=o.replace(/\/+$/,""),p=new K(e,void 0,r),i=a.length>0?new H(a):null;return async(d,g)=>{let x=new URL(d.url||"/","http://localhost"),l=decodeURIComponent(x.pathname),h=c&&l.startsWith(c)?l.slice(c.length)||"/":l,f=null,E=200;try{if(h.startsWith("/remote/")&&i){let y=h.slice(8).split("/article/");if(y.length===2){let[b,v]=y,m=b.indexOf("/"),k=decodeURIComponent(b.slice(0,m)),u=b.slice(m+1),w=await i.getRemoteArticle(k,u,v);if(w){let z=await i.getRemoteAgents(),N=[...p.summary(),...z.map(S=>({...S,agentId:`${S.agentId} @${S.nodeId}`}))],A=`<div class="breadcrumb"><a href="/">Hub</a> / <span class="tag">remote</span> ${s(u)} @ ${s(k)}</div>`;A+=`<h1>${s(w.meta.title)}</h1>`,A+='<dl class="meta">',A+=`<dt>Tags</dt><dd>${(w.meta.tags||[]).map(S=>`<span class="tag">${S}</span>`).join(" ")}</dd>`,A+=`<dt>Owner</dt><dd>${s(w.meta.owner)}</dd>`,A+=`<dt>Source</dt><dd><span class="tag">remote</span> ${s(k)}</dd>`,A+=`<dt>Updated</dt><dd>${w.meta.lastUpdated}</dd>`,A+="</dl>";let T=new Map;A+=O(w.content,T),f=$(w.meta.title,C(N),A)}}}if(!f&&n){let y=p.getAgentWiki(n);if(h==="/"||h===""){let b=y.rebuildIndex(),v=p.getAgentEntries(n),m=`<h1>${s(n)}</h1>`;if(m+='<div class="stats">',m+=`<div class="stat-card"><div class="number">${b.articles.length}</div><div class="label">Articles</div></div>`,m+=`<div class="stat-card"><div class="number">${v.length}</div><div class="label">Entries</div></div>`,m+="</div>",b.articles.length>0){m+="<h2>Articles</h2><table><tr><th>Title</th><th>Type</th><th>Updated</th></tr>";for(let k of b.articles)m+=`<tr><td><a href="/article/${encodeURIComponent(k.path)}">${s(k.title)}</a></td><td><span class="tag">${(k.tags||[]).slice(0,3).join(", ")}</span></td><td>${k.lastUpdated||""}</td></tr>`;m+="</table>"}f=$(n,P(y,n),m)}else if(h.startsWith("/article/")){let b=h.slice(9),v=y.readArticle(b);if(v){let m=y.rebuildIndex(),k=new Map;for(let w of m.articles)k.set(w.title,w.path);let u=`<h1>${s(v.meta.title)}</h1>`;u+='<dl class="meta">',u+=`<dt>Tags</dt><dd>${(v.meta.tags||[]).map(w=>`<span class="tag">${w}</span>`).join(" ")}</dd>`,u+=`<dt>Updated</dt><dd>${v.meta.lastUpdated}</dd>`,v.meta.sources?.length&&(u+=`<dt>Sources</dt><dd>${v.meta.sources.length} entries</dd>`),u+="</dl>",u+=O(v.content,k),f=$(v.meta.title,P(y,n,b),u)}}else if(h==="/lint"){let b=y.lint(),v="<h1>Health Check</h1>";if(b.length===0)v+='<p style="color:green;">No issues.</p>';else{v+="<table><tr><th>Type</th><th>Article</th><th>Issue</th></tr>";for(let m of b)v+=`<tr><td><span class="tag">${m.type}</span></td><td>${s(m.article)}</td><td>${s(m.message)}</td></tr>`;v+="</table>"}f=$("Health Check",P(y,n),v)}else if(h==="/search"){let b=x.searchParams.get("q")||"",v=await y.findRelevantReranked(b,void 0,20),m=`<h1>Search: "${s(b)}"</h1>`;for(let k of v)m+=`<div class="entry-card"><h3><a href="/article/${encodeURIComponent(k.path)}">${s(k.meta.title)}</a></h3><p>${s(k.content.slice(0,200))}...</p></div>`;v.length===0&&(m+="<p>No results.</p>"),f=$(`Search: ${b}`,P(y,n),m)}else if(h.startsWith("/tag/")){let b=decodeURIComponent(h.slice(5)),v=y.findByTags([b],void 0,100),m=`<h1>Tag: <a class="tag" href="#">${s(b)}</a></h1>`;m+=`<p>${v.length} article${v.length===1?"":"s"} tagged <code>${s(b)}</code></p>`;for(let k of v)m+=`<div class="entry-card"><h3><a href="/article/${encodeURIComponent(k.path)}">${s(k.meta.title)}</a></h3><div class="meta-line">${U((k.meta.tags||[]).slice(0,6))}</div></div>`;v.length===0&&(m+="<p>No articles with this tag.</p>"),f=$(`Tag: ${b}`,P(y,n),m)}}else{let y=i?await i.getRemoteAgents():[],b=p.summary(),v=new Map(b.map(u=>[u.agentId,u])),m=[],k=new Set;for(let u of b){let w=y.find(z=>z.agentId===u.agentId);w&&w.totalArticles>u.totalArticles?m.push({...w,totalEntries:Math.max(u.totalEntries,w.totalEntries),unabsorbed:Math.max(u.unabsorbed,w.unabsorbed)}):m.push(u),k.add(u.agentId)}for(let u of y)k.has(u.agentId)||(m.push(u),k.add(u.agentId));if(h==="/"||h==="")f=je(p,m,y);else if(h==="/entries")f=qe(p,m);else if(h==="/search")f=Ke(p,x.searchParams.get("q")||"",m);else if(h.startsWith("/tag/"))f=Ye(p,decodeURIComponent(h.slice(5)),m);else if(h.match(/^\/agent\/[^/]+\/tag\/.+/)){let u=h.match(/^\/agent\/([^/]+)\/tag\/(.+)$/);f=Xe(p,decodeURIComponent(u[1]),decodeURIComponent(u[2]),m)}else if(h.startsWith("/agent/")){let u=h.slice(7),w=u.indexOf("/article/");if(w===-1)f=Le(p,u,m);else{let z=u.slice(0,w),N=u.slice(w+9);if(f=Je(p,z,N,m),!f&&i){let A=y.find(T=>T.agentId===z);if(A){let T=await i.getRemoteArticle(A.peerUrl,z,N);if(T){let S=`<div class="breadcrumb"><a href="/">Hub</a> / <span class="tag">remote @ ${s(A.nodeId)}</span> / ${s(z)}</div>`;S+=`<h1>${s(T.meta.title)}</h1>`,S+='<dl class="meta">',S+=`<dt>Tags</dt><dd>${(T.meta.tags||[]).map(ue=>`<span class="tag">${ue}</span>`).join(" ")}</dd>`,S+=`<dt>Source</dt><dd><span class="tag">remote</span> ${s(A.nodeId)}</dd>`,S+=`<dt>Updated</dt><dd>${T.meta.lastUpdated}</dd>`,S+="</dl>",S+=O(T.content,new Map),f=$(T.meta.title,C(m),S)}}}}}}if(!f){E=404;let y=n?P(p.getAgentWiki(n),n):C(p.summary());f=$("Not Found",y,"<h1>Page not found</h1>")}}catch(y){E=500,f=$("Error","",`<h1>Error</h1><pre>${s(y.message)}</pre>`)}g.writeHead(E,{"Content-Type":"text/html; charset=utf-8"}),g.end(c?Ve(f,c):f)}}function Ve(t,e){t=t.replace(/(\s(?:href|action)=")\/(?!\/)/g,`$1${e}/`),t=t.replace(new RegExp(`(${e})${e}/`,"g"),`${e}/`);let n=`<a href="/admin" style="position:fixed;top:10px;right:14px;z-index:9999;padding:5px 11px;background:rgba(0,0,0,0.55);color:#fff;font:12px/1.2 'IBM Plex Sans',system-ui,sans-serif;text-decoration:none;border-radius:6px;border:1px solid rgba(255,255,255,0.18);backdrop-filter:blur(6px)">\u2190 Dashboard</a>`;return t=t.replace(/<body([^>]*)>/,`<body$1>${n}`),t}function Tt(t,e=4200,n,a=[],r="graph"){let o=Ge({wikiDir:t,agentFilter:n,peerUrls:a,mode:r});He((p,i)=>{o(p,i)}).listen(e)}var Qe="https://ntfy.sh";function zt(t){return{enabled:!!t?.enabled,server:t?.server||Qe,topicSet:!!t?.topic,tokenSet:!!t?.token}}function Rt(t,e){let n={...t??{}};if("enabled"in e&&(n.enabled=!!e.enabled),"server"in e){let a=String(e.server).trim().replace(/\/+$/,"");if(!/^https?:\/\/\S+$/.test(a))throw new Error("ntfy server must be an http(s) URL");n.server=a}if("topic"in e){let a=String(e.topic).trim();if(!a)throw new Error("ntfy topic cannot be empty");n.topic=a}if("token"in e){let a=String(e.token??"").trim();a?n.token=a:delete n.token}if(n.enabled&&!n.topic)throw new Error("set a topic before enabling ntfy");return n}export{ee as a,ie as b,mt as c,ut as d,ht as e,pe as f,yt as g,kt as h,zt as i,Rt as j,Ge as k,Tt as l};
740
- //# sourceMappingURL=chunk-E6EW42EA.js.map
740
+ //# sourceMappingURL=chunk-VHTFHGVW.js.map
@@ -1,4 +1,4 @@
1
- import{q as L,r as F}from"./chunk-3SCPRYJB.js";import{b as _,c as $,d as E}from"./chunk-FRHSQVFN.js";import{b as C}from"./chunk-DP7RTNDM.js";import{e as S}from"./chunk-SGFXE5FP.js";import{b as j}from"./chunk-64P4QHSZ.js";import{createHash as Z}from"crypto";import{existsSync as J,readdirSync as Q,readFileSync as ee,writeFileSync as te}from"fs";import{resolve as A}from"path";function he(n,t,o,e,s,i,a=""){return V(t,o,e,s,i,a)}function V(n,t,o,e,s,i=""){let a=t?`
1
+ import{q as L,r as F}from"./chunk-3SCPRYJB.js";import{b as _,c as $,d as E}from"./chunk-FRHSQVFN.js";import{b as C}from"./chunk-DP7RTNDM.js";import{e as S}from"./chunk-KTM7HSEB.js";import{b as j}from"./chunk-64P4QHSZ.js";import{createHash as Z}from"crypto";import{existsSync as J,readdirSync as Q,readFileSync as ee,writeFileSync as te}from"fs";import{resolve as A}from"path";function he(n,t,o,e,s,i,a=""){return V(t,o,e,s,i,a)}function V(n,t,o,e,s,i=""){let a=t?`
2
2
  ## Worldview
3
3
 
4
4
  ${t}
@@ -238,4 +238,4 @@ ${g}`}var v="memory-promoter",re=["project","reference","feedback"],B="_memory-p
238
238
  Re-emit the FULL corrected JSON object \u2014 same structure, fixed issues, valid JSON only.`:n,y=await e(`${d}/task`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({agent:t.viaAgent,message:h,freshSession:o===null,context:{channel:"api",chatId:t.chatId??"memory-promote"}}),signal:i.signal});if(!y.ok){let f=await y.text().catch(()=>"");throw new Error(`agent /task ${y.status}: ${f.slice(0,500)}`)}let w=await y.json();if(w.error)throw new Error(`agent reported error: ${w.error}`);let b=String(w.content??"").trim();if(!b)throw new Error("agent returned empty content");return b}if(!t.model)throw new Error("callPromotionLlm: viaAgent or model is required");let l=process.env.ANTHROPIC_API_KEY;if(!l)throw new Error("ANTHROPIC_API_KEY not set; use --via <agentId> instead");let u=[{role:"user",content:n}];o&&(u.push({role:"assistant",content:"(previous attempt; will retry with corrections)"}),u.push({role:"user",content:`Your previous response failed validation: ${o}
239
239
  Re-emit the FULL corrected JSON object.`}));let g=await e(le,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":l,"anthropic-version":de},body:JSON.stringify({model:t.model,max_tokens:8e3,messages:u}),signal:i.signal});if(!g.ok){let d=await g.text().catch(()=>"");throw new Error(`anthropic API ${g.status}: ${d.slice(0,500)}`)}let p=await g.json(),r=(Array.isArray(p.content)?p.content:[]).filter(d=>d?.type==="text").map(d=>d.text).join(`
240
240
  `).trim();if(!r)throw new Error("anthropic API returned no text content");return r}finally{clearTimeout(a)}}async function Me(n={}){let t=n.wikiDir??A(process.cwd(),".agentx","wiki"),o=n.memoryRoot??A(process.cwd(),".agentx"),e=n.log??(()=>{}),s=n.now??Date.now(),i={candidates:[],clusters:[],proposed:[],skipped:[],gaps:[],warnings:[],errors:[],dryRun:!n.commit},a=new S(t,(...m)=>e(m.map(String).join(" "))),l=a.rebuildIndex(),u=H(t),g=[...ie(o),...n.extraCandidates??[]];if(i.candidates=pe(g,l,u,{types:n.types,sinceMs:n.sinceMs,agentFilter:n.agentFilter,now:s,max:n.max}),i.candidates.length===0)return e("no unpromoted memories in window"),i;if(i.clusters=ce(i.candidates),i.dryRun)return i;let p=l.articles.map(m=>({title:m.title,path:m.path,type:m.type})),c=a.getWorldview()??"",r=n.budgetTokens??me,d=i.clusters,h=O(v,d,p,c);for(;D(h)>r&&d.length>1;)d=d.slice(0,-1),h=O(v,d,p,c);if(D(h)>r)return i.errors.push(`judge prompt (~${D(h)} tokens) exceeds the ${r}-token budget even with one cluster`),i;d.length<i.clusters.length&&i.warnings.push(`token budget: judged ${d.length} of ${i.clusters.length} clusters; the rest wait for the next run`);let y=d.flatMap(m=>m.candidates),w=new Set(y.map(m=>m.stamp)),b=n.chatId??`memory-promote-${new Date(s).toISOString().slice(0,10)}`,f=W(await U(h,{...n,chatId:b}),w);if("error"in f&&(e(`parse failed (${f.error}) \u2014 retrying with feedback`),f=W(await U(h,{...n,chatId:b},f.error),w)),"error"in f)return i.errors.push(`LLM response unusable after retry: ${f.error}`),i;i.warnings.push(...f.warnings),i.gaps=f.gaps;let M=new Date(s).toISOString(),P=[],q=new Map(y.map(m=>[m.stamp,m]));for(let m of f.articles){let k=a.readArticle(m.path);if(k&&!a.canWrite(k.meta,v)){i.errors.push(`write denied for "${m.path}" (owned by ${k.meta.owner??"?"})`);continue}let T=ue(m.promotedFrom,q),I={id:_(s,m.path),createdAt:M,status:"pending",article:m,evidence:T,...k?{replaces:{lastUpdated:k.meta.lastUpdated,contentHash:K(k.content)}}:{}};$(t,I),i.proposed.push({id:I.id,path:m.path,title:m.title,type:m.type,stamps:m.promotedFrom,agents:T.agents,occurrences:T.occurrences});for(let z of m.promotedFrom)P.push({stamp:z,decision:"proposed",article:m.path,proposal:I.id,at:M})}for(let m of f.skipped)P.push({stamp:m.stamp,decision:"skipped",reason:m.reason,at:M}),i.skipped.push(m);return P.length&&N(t,P),i}var me=6e4;function D(n){return Math.ceil(n.length/4)}function K(n){return Z("sha256").update(n).digest("hex").slice(0,16)}var ge=600;function ue(n,t){let o=[];for(let e of n){let s=t.get(e);s&&o.push({stamp:e,kind:e.startsWith("review:")?"review":"memory",agentId:s.agentId,type:s.memory.type,name:s.memory.name,description:s.memory.description,...s.occurrences?{occurrences:s.occurrences}:{},...s.sessions?.length?{sessions:s.sessions}:{},...s.memory.author?{author:s.memory.author}:{},...s.memory.taskId?{taskId:s.memory.taskId}:{},updatedAt:s.memory.updatedAt,excerpt:s.memory.body.slice(0,ge)})}return{agents:[...new Set(o.map(e=>e.agentId))].sort(),occurrences:Math.max(1,...o.map(e=>e.occurrences??1)),sources:o}}function Te(n,t,o={}){let e=E(n,t);if(!e)return{ok:!1,error:`no proposal "${t}"`};if(e.status!=="pending")return{ok:!1,error:`proposal "${t}" is already ${e.status}`};let s=o.log??(()=>{}),i=new S(n,(...d)=>s(d.map(String).join(" "))),{article:a}=e,l=i.readArticle(a.path);if(!o.force){if(l&&!e.replaces)return{ok:!1,error:`"${a.path}" was created after this proposal; review it, then approve with --force`};if(l&&e.replaces&&K(l.content)!==e.replaces.contentHash)return{ok:!1,error:`"${a.path}" changed since this proposal; review it, then approve with --force`}}let u=o.now??Date.now(),g=new Date(u).toISOString().slice(0,10);if(!i.writeArticle(a.path,{title:a.title,type:j(a.type)?a.type:void 0,related:a.related,tags:a.tags,owner:v,access:"public",created:l?.meta.created??g,lastUpdated:g,sources:se(l?.meta.sources,a.promotedFrom)},a.content,v))return{ok:!1,error:`write denied for "${a.path}" (owned by ${l?.meta.owner??"?"})`};let c=new Date(u).toISOString();N(n,a.promotedFrom.map(d=>({stamp:d,decision:"promoted",article:a.path,proposal:t,at:c}))),i.rebuildIndex();let r={...e,status:"approved",decidedBy:o.by??"operator",decidedAt:c};return $(n,r),{ok:!0,proposal:r}}function Ie(n,t,o={}){let e=E(n,t);if(!e)return{ok:!1,error:`no proposal "${t}"`};if(e.status!=="pending")return{ok:!1,error:`proposal "${t}" is already ${e.status}`};let s=new Date(o.now??Date.now()).toISOString();N(n,e.article.promotedFrom.map(a=>({stamp:a,decision:"rejected",article:e.article.path,proposal:t,reason:o.reason,at:s})));let i={...e,status:"rejected",decidedBy:o.by??"operator",decidedAt:s,...o.reason?{reason:o.reason}:{}};return $(n,i),{ok:!0,proposal:i}}export{he as a,v as b,re as c,B as d,G as e,oe as f,x as g,se as h,ie as i,H as j,N as k,ce as l,W as m,pe as n,U as o,Me as p,D as q,ue as r,Te as s,Ie as t};
241
- //# sourceMappingURL=chunk-CTCOWVNL.js.map
241
+ //# sourceMappingURL=chunk-VZDKE2EZ.js.map
@@ -1,7 +1,7 @@
1
- import{g as A,h as I}from"./chunk-7L6M3P2N.js";import{l as j,o as E}from"./chunk-QJANPP4C.js";E();import{Command as N}from"commander";import f from"chalk";import{execFileSync as h}from"child_process";import{accessSync as P,constants as T,existsSync as y,readFileSync as b,readdirSync as $,statSync as L}from"fs";import{join as O,resolve as m}from"path";import{homedir as D}from"os";import{createRequire as _}from"module";async function q(n={}){let i=[];await z(i),B(i);let e=H(i);return e&&M(i,e),e&&Z(i,e),e&&F(i,e),e&&J(i,e),e&&V(i,e),n.running!==!1&&e&&await G(i,e),{checks:i,summary:S(i)}}var se=new N().name("doctor").description("pre-flight health check: Node, config, credentials, daemon").option("--no-running","skip the live daemon probe").option("--json","emit machine-readable JSON (stable shape for CI)").action(async n=>{let{checks:i,summary:e}=await q({running:n.running!==!1});n.json?process.stdout.write(JSON.stringify({checks:i,summary:e},null,2)+`
1
+ import{g as A,h as I}from"./chunk-7L6M3P2N.js";import{l as j,o as E}from"./chunk-ANLKBCYH.js";E();import{Command as N}from"commander";import f from"chalk";import{execFileSync as h}from"child_process";import{accessSync as P,constants as T,existsSync as y,readFileSync as b,readdirSync as $,statSync as L}from"fs";import{join as O,resolve as m}from"path";import{homedir as D}from"os";import{createRequire as _}from"module";async function q(n={}){let i=[];await z(i),B(i);let e=H(i);return e&&M(i,e),e&&Z(i,e),e&&F(i,e),e&&J(i,e),e&&V(i,e),n.running!==!1&&e&&await G(i,e),{checks:i,summary:S(i)}}var se=new N().name("doctor").description("pre-flight health check: Node, config, credentials, daemon").option("--no-running","skip the live daemon probe").option("--json","emit machine-readable JSON (stable shape for CI)").action(async n=>{let{checks:i,summary:e}=await q({running:n.running!==!1});n.json?process.stdout.write(JSON.stringify({checks:i,summary:e},null,2)+`
2
2
  `):K(i),process.exit(e.errors>0?1:0)});async function z(n){let i=Number(process.versions.node.split(".")[0]);n.push({severity:i==22?"ok":"fail",group:"Environment",title:`Node.js ${process.versions.node}`,detail:i==22?void 0:"AgentX requires Node 22.x.",fix:i==22?void 0:"Install Node 22 (nvm install 22 && nvm use 22)"});let e=a=>{try{return h(process.platform==="win32"?"where":"which",[a],{encoding:"utf-8"}).trim().split(`
3
3
  `)[0]}catch{return null}},s=e("npm");n.push({severity:s?"ok":"fail",group:"Environment",title:s?"npm on PATH":"npm not found",detail:s||void 0});let o=e("claude");if(o){let a;try{a=h("claude",["--version"],{encoding:"utf-8",timeout:3e3}).trim()}catch{}n.push({severity:"ok",group:"Environment",title:`claude CLI ${a||"installed"}`,detail:o})}else n.push({severity:"warn",group:"Environment",title:"claude CLI not on PATH",detail:"Only required for agents on the claude-code tier.",fix:"See https://docs.anthropic.com/en/docs/claude-code"});let r=e("codex");if(r){let a;try{a=h("codex",["--version"],{encoding:"utf-8",timeout:3e3}).trim()}catch{}n.push({severity:"ok",group:"Environment",title:`codex CLI ${a||"installed"}`,detail:r})}else n.push({severity:"warn",group:"Environment",title:"codex CLI not on PATH",detail:"Only required for agents on the codex-cli tier.",fix:"Install with `npm i -g @openai/codex`."});let l=e("opencode");if(l){let a;try{a=h("opencode",["--version"],{encoding:"utf-8",timeout:3e3}).trim()}catch{}n.push({severity:"ok",group:"Environment",title:`opencode CLI ${a||"installed"}`,detail:l})}else n.push({severity:"warn",group:"Environment",title:"opencode CLI not on PATH",detail:"Only required for agents on the opencode tier.",fix:"Install from https://opencode.ai/docs/."});try{_(import.meta.url)("better-sqlite3"),n.push({severity:"ok",group:"Environment",title:`better-sqlite3 loads for Node ${process.version}`,detail:`NODE_MODULE_VERSION ${process.versions.modules}`})}catch(a){n.push({severity:"fail",group:"Environment",title:"better-sqlite3 native binding does not load",detail:a?.message??String(a),fix:`Run "pnpm rebuild better-sqlite3" with the same Node used by the daemon (${process.version}, modules ${process.versions.modules}).`})}}function B(n){if(process.platform!=="darwin")return;let i=O(D(),"Library/LaunchAgents"),e=[];try{e=$(i).filter(o=>/agentx\.voice.*\.plist$/.test(o))}catch{return}let s=o=>{try{return P(o,T.X_OK),!0}catch{return!1}};for(let o of e){let r=A(b(O(i,o),"utf-8")),l=I("ffmpeg",r,s);n.push(l?{severity:"ok",group:"Desktop",title:"ffmpeg reachable by the desktop app",detail:`${l} (${o})`}:{severity:"warn",group:"Desktop",title:"ffmpeg not reachable by the desktop app",detail:`Local Whisper transcription fails without it. ${o} sets PATH to ${r}.`,fix:"Install ffmpeg (brew install ffmpeg), then rerun agentx desktop install."})}}function H(n){let i=m(process.cwd(),"agentx.json");if(!y(i))return n.push({severity:"fail",group:"Config",title:"agentx.json not found",detail:`Looked in ${i}`,fix:"Run `agentx setup` to create one via the web wizard."}),null;let e;try{e=JSON.parse(b(i,"utf-8"))}catch(l){return n.push({severity:"fail",group:"Config",title:"agentx.json is not valid JSON",detail:l.message}),null}try{j(i),n.push({severity:"ok",group:"Config",title:`agentx.json valid (${Object.keys(e.agents||{}).length} agents, ${Object.keys(e.crons||{}).length} schedules)`})}catch(l){n.push({severity:"fail",group:"Config",title:"agentx.json failed schema validation",detail:l.message,fix:"Open /admin \u2192 Advanced tab to edit and re-save."})}let s=Object.entries(e.agents||{}).filter(([,l])=>l.tier==="claude-code");s.length>0&&((()=>{try{return h(process.platform==="win32"?"where":"which",["claude"],{stdio:"ignore"}),!0}catch{return!1}})()||n.push({severity:"fail",group:"Config",title:`${s.length} agent(s) use claude-code tier but claude CLI is missing`,detail:s.map(([a])=>a).join(", "),fix:"Install Claude Code (https://docs.anthropic.com/en/docs/claude-code) or switch these agents to tier=sdk."}));let o=Object.entries(e.agents||{}).filter(([,l])=>l.tier==="codex-cli");o.length>0&&((()=>{try{return h(process.platform==="win32"?"where":"which",["codex"],{stdio:"ignore"}),!0}catch{return!1}})()||n.push({severity:"fail",group:"Config",title:`${o.length} agent(s) use codex-cli tier but codex CLI is missing`,detail:o.map(([a])=>a).join(", "),fix:"Install Codex CLI with `npm i -g @openai/codex` or switch these agents to tier=claude-code."}));let r=Object.entries(e.agents||{}).filter(([,l])=>l.tier==="opencode");return r.length>0&&((()=>{try{return h(process.platform==="win32"?"where":"which",["opencode"],{stdio:"ignore"}),!0}catch{return!1}})()||n.push({severity:"fail",group:"Config",title:`${r.length} agent(s) use opencode tier but OpenCode CLI is missing`,detail:r.map(([a])=>a).join(", "),fix:"Install OpenCode from https://opencode.ai/docs/ or switch these agents to another tier."})),e}function M(n,i){let e=new Set;if(x(i,e),e.size===0){n.push({severity:"ok",group:"Secrets",title:"No ${VAR} references in agentx.json"});return}let s=m(process.cwd(),".env"),o={};if(y(s))for(let a of b(s,"utf-8").split(/\r?\n/)){let g=a.match(/^\s*([A-Z_][A-Z0-9_]*)\s*=\s*(.*)$/i);g&&(o[g[1]]=g[2])}let r=[],l=[];for(let a of e){let g=process.env[a]??o[a];g===void 0?r.push(a):(!g.trim()||/^["']?\s*["']?$/.test(g))&&l.push(a)}r.length===0&&l.length===0?n.push({severity:"ok",group:"Secrets",title:`${e.size} env reference(s) resolved`}):(r.length>0&&n.push({severity:"fail",group:"Secrets",title:`${r.length} env var(s) referenced in agentx.json but missing`,detail:r.join(", "),fix:`Add to .env \u2014 e.g. ${r[0]}=<value>`}),l.length>0&&n.push({severity:"warn",group:"Secrets",title:`${l.length} env var(s) set but empty`,detail:l.join(", ")}))}function x(n,i){if(typeof n=="string")for(let e of n.matchAll(/\$\{([A-Z_][A-Z0-9_]*)\}/g))i.add(e[1]);else if(Array.isArray(n))for(let e of n)x(e,i);else if(n&&typeof n=="object")for(let e of Object.values(n))x(e,i)}function Z(n,i){let e=[];for(let[s,o]of Object.entries(i.agents||{})){if(!o.workspace)continue;let r=o.workspace.startsWith("/")?o.workspace:m(process.cwd(),o.workspace);try{L(r).isDirectory()||e.push(`${s}: ${r} (exists but not a directory)`)}catch{e.push(`${s}: ${r}`)}}e.length===0?n.push({severity:"ok",group:"Workspaces",title:"All agent workspace folders exist"}):n.push({severity:"warn",group:"Workspaces",title:`${e.length} agent workspace(s) missing`,detail:e.join(`
4
4
  `),fix:"The daemon will create missing folders on first task, but you won't see CLAUDE.md / skills until you populate them."})}function F(n,i){let e=/\$\{[A-Za-z_][A-Za-z0-9_]*\}/g,s=[];for(let[o,r]of Object.entries(i.agents||{})){if(!r.workspace)continue;let l=r.workspace.startsWith("/")?r.workspace:m(process.cwd(),r.workspace);for(let a of["settings.json","settings.local.json"]){let g=m(l,".claude",a);if(!y(g))continue;let w;try{w=JSON.parse(b(g,"utf-8"))}catch(d){s.push({severity:"fail",agent:o,msg:`${a}: JSON parse error \u2014 ${d.message}`});continue}let k=w.env||{};for(let[d,p]of Object.entries(k)){if(typeof p!="string")continue;let u=p.match(e);u&&s.push({severity:"fail",agent:o,msg:`${a} env.${d} contains unexpanded ${[...new Set(u)].join(",")} \u2014 Claude Code does NOT expand \${VAR} in settings.json (use a fully-literal value)`})}let t=k.PATH;if(typeof t=="string"){let d=t.split(":").filter(Boolean);d.includes("/usr/bin")||s.push({severity:"fail",agent:o,msg:`${a} env.PATH missing /usr/bin \u2014 cat/head/sh/which will fail`}),d.includes("/bin")||s.push({severity:"fail",agent:o,msg:`${a} env.PATH missing /bin \u2014 ls/cp/mv will fail`})}let c=w.permissions;if(c&&typeof c=="object"&&"allow"in c){let d=c.allow,p=c.deny;Array.isArray(d)&&d.length===0&&Array.isArray(p)&&p.length>0&&s.push({severity:"warn",agent:o,msg:`${a} permissions.allow is explicitly [] with ${p.length} deny rules \u2014 agent may be locked out (omit the key to use defaults instead)`})}}}if(s.length===0){n.push({severity:"ok",group:"Workspace settings",title:`All ${Object.keys(i.agents||{}).length} agent .claude/settings.json files clean`});return}for(let o of s)n.push({severity:o.severity,group:"Workspace settings",title:`${o.agent}: ${o.msg}`,fix:o.severity==="fail"?"Edit .claude/settings.json \u2014 replace ${VAR} with the literal value or omit the key to inherit.":"Either remove the empty allow list or populate it."})}function J(n,i){let e=i.webhooks??[],s=[],o=m(process.cwd(),i.workflows?.dir||".agentx/workflows"),r=new Set;try{if(y(o))for(let t of $(o))t.endsWith(".json")&&!t.startsWith("_")&&!t.endsWith(".disabled.json")&&r.add(t.replace(/\.json$/,""))}catch{}let l=new Set(["github","gitlab"]);for(let t of e){if(!t.enabled||!l.has(t.source))continue;!(t.triggers&&Object.keys(t.triggers).length>0)&&!t.defaultWorkflow&&s.push({severity:"warn",title:`webhook "${t.id}" (${t.source} -> ${t.agentId}) has no triggers map; every event-type collapses to the bound agent`,fix:'Add a triggers map: { "issues.opened": "wf-id", "pull_request.synchronize": "wf-id" } \u2014 or set defaultWorkflow.'})}for(let t of e)if(!(!t.enabled||!t.triggers)){for(let[c,d]of Object.entries(t.triggers))r.size>0&&!r.has(d)&&s.push({severity:"fail",title:`webhook "${t.id}".triggers["${c}"] -> "${d}" \u2014 no such workflow under ${i.workflows?.dir||".agentx/workflows"}`,fix:"Create the workflow file or fix the id in agentx.json"});t.defaultWorkflow&&r.size>0&&!r.has(t.defaultWorkflow)&&s.push({severity:"fail",title:`webhook "${t.id}".defaultWorkflow -> "${t.defaultWorkflow}" \u2014 no such workflow`,fix:"Create the workflow file or fix the id in agentx.json"})}try{if(y(o)){for(let t of $(o))if(!(!t.endsWith(".json")||t.startsWith("_")||t.endsWith(".disabled.json")))try{let c=JSON.parse(b(m(o,t),"utf-8"));c?.status==="active"&&c?.state==="quarantined"&&s.push({severity:"warn",title:`workflow "${c.id??t}" is status=active but state=quarantined \u2014 it looks live but will never dispatch`,fix:'Resolve the conflict the detector flagged, then set state back to "active" (or set status to "review" while parked).'})}catch{}}}catch{}let a=new Set((i.mesh?.peers??[]).map(t=>t.name));for(let t of e)!t.enabled||!t.node||a.has(t.node)||s.push({severity:"fail",title:`webhook "${t.id}" routes to mesh peer "${t.node}" which is not in mesh.peers`,fix:"Add the peer to mesh.peers or remove the node field on the webhook."});let g=new Map;for(let t of e){if(!t.enabled)continue;let c=`${t.source}:${t.agentId}`,d=g.get(c);d?s.push({severity:"warn",title:`webhook entries "${d}" and "${t.id}" both enabled for ${t.source} -> ${t.agentId}; only the first is dispatched`,fix:"Disable one entry, or merge their triggers maps into a single entry."}):g.set(c,t.id)}let w=new Set(Object.keys(i.agents??{}));for(let t of i.channels?.gitlab?.agentMappings??[])if(t?.agentId){if(t.node){a.has(t.node)||s.push({severity:"fail",title:`gitlab.agentMappings entry agentId="${t.agentId}" routes to mesh peer "${t.node}" which is not in mesh.peers`,fix:"Add the peer to mesh.peers or drop the node field on the mapping."});continue}w.has(t.agentId)||s.push({severity:"warn",title:`gitlab.agentMappings entry agentId="${t.agentId}" \u2014 no such agent in agents.* (and no node field, so it's not a cross-mesh route)`,fix:'Add the agent to agents.*, set node:"<peer>" if the agent lives remotely, or remove the mapping.'})}for(let[t,c]of Object.entries(i.channels?.telegram?.accounts??{}))c.agentBinding?w.has(c.agentBinding)||s.push({severity:"fail",title:`telegram account "${t}".agentBinding -> "${c.agentBinding}" \u2014 no such agent in agents.*`,fix:"Fix the agentBinding or define the agent in agents.*."}):s.push({severity:"fail",title:`telegram account "${t}" has no agentBinding; every DM to this bot will drop`,fix:"Set agentBinding to a valid agent id."});let k=new Set(["telegram-bot","whatsapp","slack-bot","discord-bot","gitlab-user","github-user","gmail","hotmail","smtp","hubspot","salesforce","pipedrive","odoo","trello","linear","asana","jira","notion","stripe","sentry","vercel","twilio","custom"]);for(let[t,c]of Object.entries(i.agents??{})){let d=c.integrations??[];if(!d.length)continue;let p=new Set;for(let u of d){let C=`${u.kind}::${u.label}`;p.has(C)&&s.push({severity:"warn",title:`agents.${t}.integrations: duplicate (${u.kind}, ${u.label})`,fix:"Remove the duplicate or rename one. (kind, label) must be unique per agent."}),p.add(C),k.has(u.kind)||s.push({severity:"warn",title:`agents.${t}.integrations: unknown kind "${u.kind}" on integration "${u.label}"`,fix:'Use a canonical kind from INTEGRATION_KINDS, or "custom" for ad-hoc services.'});let R=u.credentials||{};for(let[W,v]of Object.entries(R))typeof v=="string"&&/^[A-Z][A-Z0-9_]*$/.test(v)&&!process.env[v]&&s.push({severity:"warn",title:`agents.${t}.integrations[${u.kind}/${u.label}].${W} -> $${v} but env var is unset (in this shell)`,fix:`Set ${v} on the daemon environment. The daemon's process.env is what matters; this warning fires from your local shell.`})}}if(s.length===0){n.push({severity:"ok",group:"Routing",title:`${e.length} webhook(s), ${Object.keys(i.channels?.telegram?.accounts??{}).length} telegram account(s) \u2014 all routing references resolve`});return}for(let t of s)n.push({severity:t.severity,group:"Routing",title:t.title,fix:t.fix})}async function G(n,i){let e=i.dashboard?.daemonUrl?.replace(/\/+$/,"")||"http://127.0.0.1:18800",s={};i.dashboard?.token&&(s.Authorization=`Bearer ${i.dashboard.token}`);try{let o=new AbortController,r=setTimeout(()=>o.abort(),2e3),l=await fetch(e+"/health",{headers:s,signal:o.signal});if(clearTimeout(r),!l.ok){n.push({severity:"warn",group:"Runtime",title:`Daemon responded ${l.status}`,detail:e});return}let a=await l.json();n.push({severity:"ok",group:"Runtime",title:`Daemon healthy \u2014 ${a.agents?.length??0} agents live, uptime ${Math.round((a.uptime??0)/60)}m`,detail:e})}catch{n.push({severity:"warn",group:"Runtime",title:"Daemon not reachable (not running?)",detail:e,fix:"Run `agentx daemon start`. Skip this check with `agentx doctor --no-running`."})}}function V(n,i){let e=Object.entries(i.agents||{}).filter(([,o])=>o?.codegraph===!0&&(o.tier==="claude-code"||o.tier==="codex-cli"));if(e.length===0)return;let s=null;try{s=h(process.platform==="win32"?"where":"which",["codegraph"],{encoding:"utf-8"}).trim().split(`
5
5
  `)[0]||null}catch{s=null}if(s){let o;try{o=h("codegraph",["--version"],{encoding:"utf-8",timeout:3e3}).trim()}catch{}n.push({severity:"ok",group:"CodeGraph",title:`codegraph CLI ${o||"installed"}`,detail:s})}else n.push({severity:"fail",group:"CodeGraph",title:"codegraph CLI not on PATH",detail:`${e.length} agent(s) have codegraph:true but the binary isn't available.`,fix:"Install with `npm i -g @colbymchenry/codegraph`."});for(let[o,r]of e){if(!r.workspace||!y(r.workspace)){n.push({severity:"warn",group:"CodeGraph",title:`${o}: workspace missing`,detail:`Workspace path ${r.workspace} does not exist \u2014 codegraph will skip this agent.`});continue}let l=m(r.workspace,".codegraph","codegraph.db");y(l)?n.push({severity:"ok",group:"CodeGraph",title:`${o}: index present`,detail:l}):n.push({severity:"warn",group:"CodeGraph",title:`${o}: index not built yet`,detail:".codegraph/ missing \u2014 daemon will background-index this workspace on next boot.",fix:`Run \`codegraph init --index ${r.workspace}\` to build it now.`})}}function S(n){return{errors:n.filter(i=>i.severity==="fail").length,warnings:n.filter(i=>i.severity==="warn").length,ok:n.filter(i=>i.severity==="ok").length}}function K(n){console.log(),console.log(f.bold(" agentx doctor")),console.log();let i=new Map;for(let s of n)i.has(s.group)||i.set(s.group,[]),i.get(s.group).push(s);for(let[s,o]of i){console.log(f.bold(" "+s));for(let r of o){let l=r.severity==="ok"?f.green("\u2713"):r.severity==="warn"?f.yellow("!"):f.red("\u2717");console.log(` ${l} ${r.title}`),r.detail&&console.log(f.dim(" "+r.detail.replace(/\n/g,`
6
6
  `))),r.fix&&console.log(f.dim(" \u2192 "+r.fix))}console.log()}let e=S(n);e.errors>0?console.log(f.red.bold(` ${e.errors} error${e.errors===1?"":"s"}`)+(e.warnings?f.yellow(`, ${e.warnings} warning${e.warnings===1?"":"s"}`):"")+`. Fix the error${e.errors===1?"":"s"} and rerun.`):e.warnings>0?console.log(f.yellow.bold(` ${e.warnings} warning${e.warnings===1?"":"s"}.`)+f.dim(" Review but not blocking.")):console.log(f.green.bold(` All checks passed (${e.ok}).`)),console.log()}export{q as a,se as b};
7
- //# sourceMappingURL=chunk-LGBBKLUO.js.map
7
+ //# sourceMappingURL=chunk-W544ZCCO.js.map
@@ -0,0 +1,59 @@
1
+ import{a as u,b as o}from"./chunk-ZTEU5JVQ.js";o();function m(e,n,t=u()){return e.prepare(`
2
+ INSERT INTO task_traces (
3
+ task_id, agent_id, channel, chat_id, workflow_run_id, workflow_id,
4
+ workflow_node_id, intent_event_id, intent_decided_by, resume_session_id,
5
+ model, status, started_at, message_preview, original_message,
6
+ resume_origin, resume_attempt, resumed_from
7
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'in-flight', ?, ?, ?, ?, ?, ?)
8
+ `).run(t,n.agentId,n.channel??null,n.chatId??null,n.workflowRunId??null,n.workflowId??null,n.workflowNodeId??null,n.intentEventId??null,n.intentDecidedBy??null,n.resumeSessionId??null,n.model??null,Date.now(),n.messagePreview??null,n.originalMessage??null,n.resumeOrigin??null,n.resumeAttempt??0,n.resumedFrom??null),t}function g(e,n,t){let s=Date.now();e.prepare(`
9
+ UPDATE task_traces SET
10
+ status = ?,
11
+ final_session_id = ?,
12
+ input_tokens = ?,
13
+ output_tokens = ?,
14
+ cache_read_tokens = ?,
15
+ cache_create_tokens = ?,
16
+ tier2_input_tokens = ?,
17
+ tier2_output_tokens = ?,
18
+ tier2_cache_read_tokens = ?,
19
+ tier2_cache_create_tokens = ?,
20
+ resumed = COALESCE(?, resumed),
21
+ resume_session_id = COALESCE(?, resume_session_id),
22
+ jev_arm = COALESCE(?, jev_arm),
23
+ error = ?,
24
+ final_response = COALESCE(?, final_response),
25
+ finished_at = ?,
26
+ duration_ms = ? - started_at
27
+ WHERE task_id = ?
28
+ `).run(t.status,t.finalSessionId??null,t.inputTokens??null,t.outputTokens??null,t.cacheReadTokens??null,t.cacheCreateTokens??null,t.tier2InputTokens??null,t.tier2OutputTokens??null,t.tier2CacheReadTokens??null,t.tier2CacheCreateTokens??null,t.resumed==null?null:t.resumed?1:0,t.resumeSessionId??null,t.jevArm??null,t.error??null,t.finalResponse??null,s,s,n)}function _(e,n,t){let s=Date.now();return e.transaction(()=>{let l=(e.prepare("SELECT MAX(seq) AS max_seq FROM task_trace_steps WHERE task_id = ?").get(n)?.max_seq??-1)+1;return e.prepare(`
29
+ INSERT INTO task_trace_steps (
30
+ task_id, seq, name, action, status, input_summary, output_summary, error, ms, started_at
31
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
32
+ `).run(n,l,t.name,t.action??null,t.status??null,t.inputSummary??null,t.outputSummary??null,t.error??null,t.ms??null,s),l})()}function i(e){return{taskId:e.task_id,agentId:e.agent_id,channel:e.channel??null,chatId:e.chat_id??null,workflowRunId:e.workflow_run_id??null,workflowId:e.workflow_id??null,workflowNodeId:e.workflow_node_id??null,intentEventId:e.intent_event_id??null,intentDecidedBy:e.intent_decided_by??null,resumeSessionId:e.resume_session_id??null,finalSessionId:e.final_session_id??null,model:e.model??null,status:e.status,startedAt:e.started_at,finishedAt:e.finished_at??null,durationMs:e.duration_ms??null,inputTokens:e.input_tokens??null,outputTokens:e.output_tokens??null,cacheReadTokens:e.cache_read_tokens??null,cacheCreateTokens:e.cache_create_tokens??null,tier2InputTokens:e.tier2_input_tokens??null,tier2OutputTokens:e.tier2_output_tokens??null,tier2CacheReadTokens:e.tier2_cache_read_tokens??null,tier2CacheCreateTokens:e.tier2_cache_create_tokens??null,resumed:e.resumed==null?null:e.resumed===1,jevArm:e.jev_arm??null,error:e.error??null,messagePreview:e.message_preview??null,originalMessage:e.original_message??null,finalResponse:e.final_response??null}}function d(e){return{taskId:e.task_id,seq:e.seq,name:e.name,action:e.action??null,status:e.status??null,inputSummary:e.input_summary??null,outputSummary:e.output_summary??null,error:e.error??null,ms:e.ms??null,startedAt:e.started_at}}function p(e,n){let t=e.prepare("SELECT * FROM task_traces WHERE task_id = ?").get(n);if(!t)return null;let s=e.prepare("SELECT * FROM task_trace_steps WHERE task_id = ? ORDER BY seq").all(n);return{task:i(t),steps:s.map(d)}}function k(e){let n=Date.now();return e.prepare(`
33
+ UPDATE task_traces
34
+ SET status = 'canceled',
35
+ error = 'daemon-restart (orphaned in-flight)',
36
+ finished_at = ?
37
+ WHERE status = 'in-flight'
38
+ `).run(n).changes??0}function h(e,n=Date.now()){let t=e.transaction(()=>{let r=e.prepare(`
39
+ SELECT task_id, agent_id, channel, chat_id, workflow_run_id, started_at,
40
+ original_message, resume_origin, resume_attempt
41
+ FROM task_traces WHERE status = 'in-flight' ORDER BY started_at
42
+ `).all();return e.prepare(`
43
+ UPDATE task_traces
44
+ SET status = 'canceled', error = 'daemon-restart (interrupted)',
45
+ finished_at = ?, resume_decision = 'interrupted'
46
+ WHERE status = 'in-flight'
47
+ `).run(n),r}),s=e.prepare(`
48
+ SELECT action, input_summary FROM task_trace_steps
49
+ WHERE task_id = ? AND name = 'tool_use' ORDER BY seq
50
+ `);return t().map(r=>({taskId:r.task_id,agentId:r.agent_id,channel:r.channel??null,chatId:r.chat_id??null,workflowRunId:r.workflow_run_id??null,startedAt:r.started_at,originalMessage:r.original_message??null,resumeOrigin:r.resume_origin??null,resumeAttempt:r.resume_attempt??0,toolCalls:s.all(r.task_id).map(a=>({action:a.action??null,inputSummary:a.input_summary??null}))}))}function T(e,n){return(e.prepare(`
51
+ UPDATE task_traces SET resume_decision = 'deciding'
52
+ WHERE task_id = ? AND resume_decision = 'interrupted'
53
+ `).run(n).changes??0)===1}function I(e,n,t,s){e.prepare("UPDATE task_traces SET resume_decision = ?, resume_reason = ? WHERE task_id = ?").run(t,s.slice(0,500),n)}function f(e,n={}){let t=[],s=[];n.agentId&&(t.push("agent_id = ?"),s.push(n.agentId)),n.channel&&(t.push("channel = ?"),s.push(n.channel)),n.chatId&&(t.push("chat_id = ?"),s.push(n.chatId)),n.workflowRunId&&(t.push("workflow_run_id = ?"),s.push(n.workflowRunId)),n.status&&(t.push("status = ?"),s.push(n.status)),n.since!==void 0&&(t.push("started_at >= ?"),s.push(n.since)),n.until!==void 0&&(t.push("started_at <= ?"),s.push(n.until));let r=Math.max(1,Math.min(n.limit??100,1e3)),a=`
54
+ SELECT * FROM task_traces
55
+ ${t.length>0?"WHERE "+t.join(" AND "):""}
56
+ ORDER BY started_at DESC
57
+ LIMIT ?
58
+ `;return s.push(r),e.prepare(a).all(...s).map(i)}export{m as a,g as b,_ as c,p as d,k as e,h as f,T as g,I as h,f as i};
59
+ //# sourceMappingURL=chunk-WZGABQ72.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/storage/traces.ts"],"sourcesContent":["import type Database from \"better-sqlite3\"\nimport { newEventId } from \"@/intent/ulid\"\n\n// --- Task trace store ---\n//\n// Improvement plan #2 — per-task observability backed by SQLite. One\n// task_traces row per executeTask invocation, an append-only stream of\n// task_trace_steps inside it. The store is intentionally dumb: it stores\n// what callers give it. Callers (capture sites in src/agents/runtime.ts)\n// are responsible for byte-capping large step payloads so the daemon\n// memory stays bounded under tool-heavy turns.\n//\n// Why a separate store layer (vs writing inline in runtime.ts)?\n// - Tests can exercise the trace lifecycle against a temp SQLite without\n// spinning up the agent runtime.\n// - The HTTP endpoints (GET /task/:id/trace) read through the same\n// functions producers write through, so there's a single source of\n// truth on field names + semantics.\n// - Future capture sites (sdk tier, orchestrator tier) reuse this API\n// without copying SQL.\n//\n// Why ULID for task_id?\n// - URL-safe in HTTP routes (GET /task/:id/trace).\n// - Time-sortable, so listTraces ORDER BY started_at and ORDER BY\n// task_id agree without an explicit timestamp column index.\n// - Already in-tree (src/intent/ulid.ts); no new dep.\n//\n// Cross-DB FK note:\n// intent_event_id and intent_decided_by are logical foreign keys into\n// the intent ledger at .agentx/intent/ledger.sqlite. better-sqlite3\n// doesn't enforce cross-database FKs and we don't ATTACH the ledger\n// here on purpose — the rescue plan's append-only contract on the\n// ledger is honoured by routing all writes through src/intent/ledger.ts.\n// Joins happen at the HTTP / CLI layer when needed.\n\nexport interface TraceStartInput {\n agentId: string\n channel?: string | null\n chatId?: string | null\n messagePreview?: string | null\n /** Full untruncated user message — used by `agentx trace replay <taskId>`\n * to re-fire a recorded task against the current agent config. NULL on\n * rows from before migration v8, in which case replay falls back to\n * messagePreview (which is capped at 200 chars). */\n originalMessage?: string | null\n workflowRunId?: string | null\n workflowId?: string | null\n workflowNodeId?: string | null\n intentEventId?: string | null\n intentDecidedBy?: string | null\n resumeSessionId?: string | null\n model?: string | null\n /** How to re-enter this run after a restart (JSON, see agents/resume). */\n resumeOrigin?: string | null\n /** 0 for a fresh run; n for the n-th resume of a cut-off run. */\n resumeAttempt?: number | null\n /** The cut-off run this one continues. */\n resumedFrom?: string | null\n}\n\nexport interface TraceEndInput {\n status: \"ok\" | \"error\" | \"timeout\"\n finalSessionId?: string | null\n inputTokens?: number | null\n outputTokens?: number | null\n cacheReadTokens?: number | null\n cacheCreateTokens?: number | null\n tier2InputTokens?: number | null\n tier2OutputTokens?: number | null\n tier2CacheReadTokens?: number | null\n tier2CacheCreateTokens?: number | null\n /** Whether the turn continued a provider session. The resume decision\n * is made after task:started, so it is recorded at the end. Omitted\n * leaves both resumed and resume_session_id untouched. */\n resumed?: boolean | null\n resumeSessionId?: string | null\n /** Request-gate experiment arm: \"treatment\" | \"holdout\". */\n jevArm?: string | null\n error?: string | null\n /** The agent's final reply text. Stored verbatim so `replay --diff`\n * can compare original output vs the new run's output without\n * reconstructing it from the step ledger. NULL when the response\n * wasn't captured (older rows or non-text outputs). */\n finalResponse?: string | null\n}\n\nexport interface TraceStepInput {\n /** Coarse step kind. Conventional values: \"tool_use\" | \"tool_result\" |\n * \"llm_message\" | \"session_rotation\" | \"error\" | \"preflight\". Free-form\n * strings allowed so future capture sites don't need a schema bump. */\n name: string\n /** Fine-grained name within the kind. For \"tool_use\", this is the tool\n * name (e.g. \"Bash\", \"Edit\"). Optional. */\n action?: string | null\n status?: \"ok\" | \"error\" | \"in-flight\" | null\n /** JSON-stringified input or human-readable summary. Caller-byte-capped. */\n inputSummary?: string | null\n outputSummary?: string | null\n error?: string | null\n /** Step duration when known. Optional — many step kinds are point-in-time. */\n ms?: number | null\n}\n\nexport interface TraceRecord {\n taskId: string\n agentId: string\n channel: string | null\n chatId: string | null\n workflowRunId: string | null\n workflowId: string | null\n workflowNodeId: string | null\n intentEventId: string | null\n intentDecidedBy: string | null\n resumeSessionId: string | null\n finalSessionId: string | null\n model: string | null\n status: string\n startedAt: number\n finishedAt: number | null\n durationMs: number | null\n inputTokens: number | null\n outputTokens: number | null\n cacheReadTokens: number | null\n cacheCreateTokens: number | null\n tier2InputTokens: number | null\n tier2OutputTokens: number | null\n tier2CacheReadTokens: number | null\n tier2CacheCreateTokens: number | null\n /** NULL on rows recorded before migration v12. */\n resumed: boolean | null\n /** NULL when the request gate was not active (or before migration v13). */\n jevArm: string | null\n error: string | null\n messagePreview: string | null\n /** Full untruncated user message — populated for traces from migration v8\n * onward. NULL on older rows; consumers should fall back to messagePreview. */\n originalMessage: string | null\n /** Agent's final reply text — populated for traces from migration v8\n * onward when the executor records it on end. */\n finalResponse: string | null\n}\n\nexport interface TraceStepRecord {\n taskId: string\n seq: number\n name: string\n action: string | null\n status: string | null\n inputSummary: string | null\n outputSummary: string | null\n error: string | null\n ms: number | null\n startedAt: number\n}\n\nexport interface ListTracesFilters {\n agentId?: string\n channel?: string\n chatId?: string\n workflowRunId?: string\n status?: string\n /** ms epoch */\n since?: number\n /** ms epoch */\n until?: number\n /** Default 100, capped at 1000. */\n limit?: number\n}\n\n/**\n * Insert an in-flight trace row and return its ULID. Callers thread the\n * returned id through the executor and call recordTraceEnd at finish time.\n *\n * `taskId` is optional — when supplied, the caller has already allocated\n * a ULID upstream (e.g. registry.execute generating it before the bus\n * event so the runtime can capture per-step rows under the same id).\n * When omitted, a fresh ULID is allocated here.\n */\nexport function recordTraceStart(\n db: Database.Database,\n input: TraceStartInput,\n taskId: string = newEventId(),\n): string {\n db.prepare(`\n INSERT INTO task_traces (\n task_id, agent_id, channel, chat_id, workflow_run_id, workflow_id,\n workflow_node_id, intent_event_id, intent_decided_by, resume_session_id,\n model, status, started_at, message_preview, original_message,\n resume_origin, resume_attempt, resumed_from\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'in-flight', ?, ?, ?, ?, ?, ?)\n `).run(\n taskId,\n input.agentId,\n input.channel ?? null,\n input.chatId ?? null,\n input.workflowRunId ?? null,\n input.workflowId ?? null,\n input.workflowNodeId ?? null,\n input.intentEventId ?? null,\n input.intentDecidedBy ?? null,\n input.resumeSessionId ?? null,\n input.model ?? null,\n Date.now(),\n input.messagePreview ?? null,\n input.originalMessage ?? null,\n input.resumeOrigin ?? null,\n input.resumeAttempt ?? 0,\n input.resumedFrom ?? null,\n )\n return taskId\n}\n\n/**\n * Finalize a trace. duration_ms is computed from finished_at - started_at\n * inside the UPDATE so the two columns can never disagree. Idempotent —\n * calling twice on the same task_id overwrites the prior end values; the\n * second call's status wins. (Soft-overwrite is intentional: timeout\n * handlers may write before a late stream-completion arrives.)\n */\nexport function recordTraceEnd(db: Database.Database, taskId: string, input: TraceEndInput): void {\n const finishedAt = Date.now()\n db.prepare(`\n UPDATE task_traces SET\n status = ?,\n final_session_id = ?,\n input_tokens = ?,\n output_tokens = ?,\n cache_read_tokens = ?,\n cache_create_tokens = ?,\n tier2_input_tokens = ?,\n tier2_output_tokens = ?,\n tier2_cache_read_tokens = ?,\n tier2_cache_create_tokens = ?,\n resumed = COALESCE(?, resumed),\n resume_session_id = COALESCE(?, resume_session_id),\n jev_arm = COALESCE(?, jev_arm),\n error = ?,\n final_response = COALESCE(?, final_response),\n finished_at = ?,\n duration_ms = ? - started_at\n WHERE task_id = ?\n `).run(\n input.status,\n input.finalSessionId ?? null,\n input.inputTokens ?? null,\n input.outputTokens ?? null,\n input.cacheReadTokens ?? null,\n input.cacheCreateTokens ?? null,\n input.tier2InputTokens ?? null,\n input.tier2OutputTokens ?? null,\n input.tier2CacheReadTokens ?? null,\n input.tier2CacheCreateTokens ?? null,\n input.resumed == null ? null : input.resumed ? 1 : 0,\n input.resumeSessionId ?? null,\n input.jevArm ?? null,\n input.error ?? null,\n input.finalResponse ?? null,\n finishedAt,\n finishedAt,\n taskId,\n )\n}\n\n/**\n * Append a step. seq is auto-allocated as MAX(seq)+1 within a transaction\n * so concurrent step writes for the same task_id can't collide. Returns\n * the assigned seq, useful for downstream correlation.\n */\nexport function recordTraceStep(db: Database.Database, taskId: string, input: TraceStepInput): number {\n const startedAt = Date.now()\n const allocateAndInsert = db.transaction((): number => {\n const row = db\n .prepare(\"SELECT MAX(seq) AS max_seq FROM task_trace_steps WHERE task_id = ?\")\n .get(taskId) as { max_seq: number | null } | undefined\n const seq = ((row?.max_seq ?? -1) as number) + 1\n db.prepare(`\n INSERT INTO task_trace_steps (\n task_id, seq, name, action, status, input_summary, output_summary, error, ms, started_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `).run(\n taskId,\n seq,\n input.name,\n input.action ?? null,\n input.status ?? null,\n input.inputSummary ?? null,\n input.outputSummary ?? null,\n input.error ?? null,\n input.ms ?? null,\n startedAt,\n )\n return seq\n })\n return allocateAndInsert()\n}\n\nfunction rowToTrace(row: Record<string, unknown>): TraceRecord {\n return {\n taskId: row.task_id as string,\n agentId: row.agent_id as string,\n channel: (row.channel as string) ?? null,\n chatId: (row.chat_id as string) ?? null,\n workflowRunId: (row.workflow_run_id as string) ?? null,\n workflowId: (row.workflow_id as string) ?? null,\n workflowNodeId: (row.workflow_node_id as string) ?? null,\n intentEventId: (row.intent_event_id as string) ?? null,\n intentDecidedBy: (row.intent_decided_by as string) ?? null,\n resumeSessionId: (row.resume_session_id as string) ?? null,\n finalSessionId: (row.final_session_id as string) ?? null,\n model: (row.model as string) ?? null,\n status: row.status as string,\n startedAt: row.started_at as number,\n finishedAt: (row.finished_at as number) ?? null,\n durationMs: (row.duration_ms as number) ?? null,\n inputTokens: (row.input_tokens as number) ?? null,\n outputTokens: (row.output_tokens as number) ?? null,\n cacheReadTokens: (row.cache_read_tokens as number) ?? null,\n cacheCreateTokens: (row.cache_create_tokens as number) ?? null,\n tier2InputTokens: (row.tier2_input_tokens as number) ?? null,\n tier2OutputTokens: (row.tier2_output_tokens as number) ?? null,\n tier2CacheReadTokens: (row.tier2_cache_read_tokens as number) ?? null,\n tier2CacheCreateTokens: (row.tier2_cache_create_tokens as number) ?? null,\n resumed: row.resumed == null ? null : row.resumed === 1,\n jevArm: (row.jev_arm as string) ?? null,\n error: (row.error as string) ?? null,\n messagePreview: (row.message_preview as string) ?? null,\n originalMessage: (row.original_message as string) ?? null,\n finalResponse: (row.final_response as string) ?? null,\n }\n}\n\nfunction rowToStep(row: Record<string, unknown>): TraceStepRecord {\n return {\n taskId: row.task_id as string,\n seq: row.seq as number,\n name: row.name as string,\n action: (row.action as string) ?? null,\n status: (row.status as string) ?? null,\n inputSummary: (row.input_summary as string) ?? null,\n outputSummary: (row.output_summary as string) ?? null,\n error: (row.error as string) ?? null,\n ms: (row.ms as number) ?? null,\n startedAt: row.started_at as number,\n }\n}\n\n/** Fetch a trace + its ordered steps. Returns null if no such task_id. */\nexport function getTrace(\n db: Database.Database,\n taskId: string,\n): { task: TraceRecord; steps: TraceStepRecord[] } | null {\n const row = db.prepare(\"SELECT * FROM task_traces WHERE task_id = ?\").get(taskId) as\n | Record<string, unknown>\n | undefined\n if (!row) return null\n const stepRows = db\n .prepare(\"SELECT * FROM task_trace_steps WHERE task_id = ? ORDER BY seq\")\n .all(taskId) as Record<string, unknown>[]\n return { task: rowToTrace(row), steps: stepRows.map(rowToStep) }\n}\n\n/**\n * Cancel any in-flight trace rows left over from a previous daemon\n * lifetime. Mirrors the intent ledger's startup cleanup (rescue plan\n * Phase 1 commit a8514d9): a hard-killed daemon never gets to fire\n * task:completed, so the row sits \"in-flight\" forever and contaminates\n * triage queries (`agentx trace list --status in-flight`) with stale\n * orphans. Run once at daemon boot before subscribers attach.\n *\n * Returns the number of rows updated. duration_ms is intentionally NOT\n * computed — we don't know when the task actually ended; preserving\n * NULL there is more honest than backdating to \"now\".\n */\nexport function cleanupOrphanedTraces(db: Database.Database): number {\n const now = Date.now()\n const r = db.prepare(`\n UPDATE task_traces\n SET status = 'canceled',\n error = 'daemon-restart (orphaned in-flight)',\n finished_at = ?\n WHERE status = 'in-flight'\n `).run(now)\n return (r.changes ?? 0) as number\n}\n\n// --- Resume after restart (#103) -------------------------------------------\n\n/** A run that was still going when the previous daemon stopped. */\nexport interface InterruptedRun {\n taskId: string\n agentId: string\n channel: string | null\n chatId: string | null\n workflowRunId: string | null\n startedAt: number\n originalMessage: string | null\n resumeOrigin: string | null\n resumeAttempt: number\n /** Tool calls it had made, in order, for the resume note. */\n toolCalls: Array<{ action: string | null; inputSummary: string | null }>\n}\n\n/** Close every run left in flight by the previous daemon, marking it\n * `interrupted` for the resume planner, and return them. One transaction,\n * so a second process booting on the same database gets nothing. */\nexport function takeInterruptedRuns(db: Database.Database, now = Date.now()): InterruptedRun[] {\n const take = db.transaction(() => {\n const rows = db.prepare(`\n SELECT task_id, agent_id, channel, chat_id, workflow_run_id, started_at,\n original_message, resume_origin, resume_attempt\n FROM task_traces WHERE status = 'in-flight' ORDER BY started_at\n `).all() as Array<Record<string, any>>\n db.prepare(`\n UPDATE task_traces\n SET status = 'canceled', error = 'daemon-restart (interrupted)',\n finished_at = ?, resume_decision = 'interrupted'\n WHERE status = 'in-flight'\n `).run(now)\n return rows\n })\n const steps = db.prepare(`\n SELECT action, input_summary FROM task_trace_steps\n WHERE task_id = ? AND name = 'tool_use' ORDER BY seq\n `)\n return take().map((r) => ({\n taskId: r.task_id,\n agentId: r.agent_id,\n channel: r.channel ?? null,\n chatId: r.chat_id ?? null,\n workflowRunId: r.workflow_run_id ?? null,\n startedAt: r.started_at,\n originalMessage: r.original_message ?? null,\n resumeOrigin: r.resume_origin ?? null,\n resumeAttempt: r.resume_attempt ?? 0,\n toolCalls: (steps.all(r.task_id) as Array<Record<string, any>>).map((s) => ({\n action: s.action ?? null, inputSummary: s.input_summary ?? null,\n })),\n }))\n}\n\n/** Claim an interrupted run before deciding what to do with it. Returns\n * false when another process already claimed or decided it, so a run is\n * never resumed — or its chat told — twice. */\nexport function claimResume(db: Database.Database, taskId: string): boolean {\n const r = db.prepare(`\n UPDATE task_traces SET resume_decision = 'deciding'\n WHERE task_id = ? AND resume_decision = 'interrupted'\n `).run(taskId)\n return (r.changes ?? 0) === 1\n}\n\nexport type ResumeDecision = \"resumed\" | \"reported\" | \"skipped\" | \"resume-failed\"\n\nexport function recordResumeDecision(\n db: Database.Database, taskId: string, decision: ResumeDecision, reason: string,\n): void {\n db.prepare(\"UPDATE task_traces SET resume_decision = ?, resume_reason = ? WHERE task_id = ?\")\n .run(decision, reason.slice(0, 500), taskId)\n}\n\n/** Triage / dashboard surface — list traces newest-first, filtered. */\nexport function listTraces(db: Database.Database, filters: ListTracesFilters = {}): TraceRecord[] {\n const where: string[] = []\n const params: unknown[] = []\n if (filters.agentId) { where.push(\"agent_id = ?\"); params.push(filters.agentId) }\n if (filters.channel) { where.push(\"channel = ?\"); params.push(filters.channel) }\n if (filters.chatId) { where.push(\"chat_id = ?\"); params.push(filters.chatId) }\n if (filters.workflowRunId) { where.push(\"workflow_run_id = ?\"); params.push(filters.workflowRunId) }\n if (filters.status) { where.push(\"status = ?\"); params.push(filters.status) }\n if (filters.since !== undefined) { where.push(\"started_at >= ?\"); params.push(filters.since) }\n if (filters.until !== undefined) { where.push(\"started_at <= ?\"); params.push(filters.until) }\n\n const limit = Math.max(1, Math.min(filters.limit ?? 100, 1000))\n const sql = `\n SELECT * FROM task_traces\n ${where.length > 0 ? \"WHERE \" + where.join(\" AND \") : \"\"}\n ORDER BY started_at DESC\n LIMIT ?\n `\n params.push(limit)\n return (db.prepare(sql).all(...params) as Record<string, unknown>[]).map(rowToTrace)\n}\n"],"mappings":"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","names":["init_ulid","recordTraceStart","db","input","taskId","newEventId","recordTraceEnd","finishedAt","recordTraceStep","startedAt","seq","rowToTrace","row","rowToStep","getTrace","stepRows","cleanupOrphanedTraces","now","takeInterruptedRuns","take","rows","steps","s","claimResume","recordResumeDecision","decision","reason","listTraces","filters","where","params","limit","sql"]}