agentix-cli 0.110.0 → 0.111.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/mac-voice/Sources/AgentXVoice/Panel.swift +77 -30
- package/apps/mac-voice/Sources/AgentXVoice/PanelAnswer.swift +3 -2
- package/apps/mac-voice/Sources/AgentXVoice/PillForm.swift +55 -0
- package/apps/mac-voice/Sources/AgentXVoice/PillMenu.swift +8 -7
- package/apps/mac-voice/Sources/AgentXVoice/SettingsLook.swift +2 -2
- package/apps/mac-voice/Sources/AgentXVoice/StatusMenu.swift +5 -6
- package/apps/mac-voice/Sources/AgentXVoice/main.swift +7 -6
- package/dist/{App-4VWAQOVA.js → App-T4Q7SW6M.js} +2 -2
- package/dist/{ChatApp-KOTGUDZH.js → ChatApp-BKUJUIDL.js} +2 -2
- package/dist/{agent-CGRHC2IS.js → agent-YZVEXFE7.js} +2 -2
- package/dist/{board-dashboard-SAYBNLZW.js → board-dashboard-3TVUCOSI.js} +411 -257
- package/dist/board-dashboard-3TVUCOSI.js.map +1 -0
- package/dist/{chunk-UVMCAZL7.js → chunk-4FU4BFCH.js} +2 -2
- package/dist/{chunk-NIW75USA.js → chunk-4NFDDTGA.js} +2 -2
- package/dist/{chunk-2VGYRETU.js → chunk-53OS4TDU.js} +2 -2
- package/dist/{chunk-J32LSOBJ.js → chunk-7UNS34CN.js} +2 -2
- package/dist/chunk-AV7T3SYT.js +111 -0
- package/dist/chunk-AV7T3SYT.js.map +1 -0
- package/dist/chunk-DTQQ4DAY.js +154 -0
- package/dist/chunk-DTQQ4DAY.js.map +1 -0
- package/dist/{chunk-SFPMS55Z.js → chunk-DUBCOGQB.js} +49 -48
- package/dist/chunk-DUBCOGQB.js.map +1 -0
- package/dist/{chunk-BFOB6ERR.js → chunk-DXWDVKMX.js} +2 -2
- package/dist/{chunk-BZKGQMU2.js → chunk-FBODSJU6.js} +2 -2
- package/dist/{chunk-UTV4DRAW.js → chunk-GQMUFSKB.js} +14 -14
- package/dist/{chunk-UTV4DRAW.js.map → chunk-GQMUFSKB.js.map} +1 -1
- package/dist/{chunk-4UCDWEC4.js → chunk-HCTWKWS3.js} +2 -2
- package/dist/{chunk-EC6DKMR5.js → chunk-KDD6FPQ6.js} +238 -238
- package/dist/chunk-KDD6FPQ6.js.map +1 -0
- package/dist/{chunk-5O6PIO7F.js → chunk-LWYKS5JU.js} +2 -2
- package/dist/{chunk-QQAZ6EJP.js → chunk-M2L6S3Z5.js} +2 -2
- package/dist/{chunk-6F2E5NQA.js → chunk-QJP37VVA.js} +2 -2
- package/dist/{chunk-QFP4YZII.js → chunk-RS2T53EC.js} +2 -2
- package/dist/{chunk-E5OV6RTG.js → chunk-RXLU6HJ6.js} +3 -3
- package/dist/chunk-RXLU6HJ6.js.map +1 -0
- package/dist/{chunk-D7M4WQFU.js → chunk-SIOWVLZG.js} +4 -4
- package/dist/chunk-SIOWVLZG.js.map +1 -0
- package/dist/{chunk-BG6SOGKT.js → chunk-U52IS3AF.js} +2 -2
- package/dist/{chunk-RJ7DASOU.js → chunk-UIBUZ2N3.js} +25 -10
- package/dist/chunk-UIBUZ2N3.js.map +1 -0
- package/dist/{chunk-MJWJ3JVA.js → chunk-WSKQUKCF.js} +2 -2
- package/dist/{chunk-S4NZUPIN.js → chunk-WXQQO2PD.js} +2 -2
- package/dist/{chunk-FHI5WPFO.js → chunk-WYSNRZV4.js} +4 -4
- package/dist/chunk-WYSNRZV4.js.map +1 -0
- package/dist/{chunk-CJR26JUI.js → chunk-XEOT2F4C.js} +2 -2
- package/dist/{chunk-3Z5P2CWS.js → chunk-XF26QNAW.js} +2 -2
- package/dist/{chunk-CJL5OPNQ.js → chunk-Z7L74GGT.js} +2 -2
- package/dist/cli.js +1 -1
- package/dist/{client-VYMTUZKN.js → client-O7SPKO5X.js} +2 -2
- package/dist/config-ZHQYXFOJ.js +2 -0
- package/dist/config-mutator-RS2NTDNO.js +2 -0
- package/dist/{doctor-GHUPUBET.js → doctor-XP7O45RC.js} +2 -2
- package/dist/{grants-Y667GZAU.js → grants-M4K6WXOO.js} +2 -2
- package/dist/index.d.ts +68 -15
- package/dist/index.js +1 -1
- package/dist/{program-V2JEVJAD.js → program-EZLJIVQX.js} +187 -182
- package/dist/program-EZLJIVQX.js.map +1 -0
- package/dist/{promote-KY3Y2PVU.js → promote-UGWANGI2.js} +2 -2
- package/dist/{query-BYCRYDM3.js → query-ZET7Z5FT.js} +2 -2
- package/dist/registry-UXWOY52B.js +2 -0
- package/dist/request-planner-W5YLKCGG.js +6 -0
- package/dist/request-planner-W5YLKCGG.js.map +1 -0
- package/dist/{review-candidates-56ZPJNXW.js → review-candidates-GU4S5VQH.js} +2 -2
- package/dist/{routing-WKS2CZYF.js → routing-GJ4VXKIC.js} +2 -2
- package/dist/schedule-tool-HD4LWKVC.js +2 -0
- package/dist/scheduler-3LEHZHWJ.js +2 -0
- package/dist/seat-L6XJ2BFA.js +2 -0
- package/dist/select-T7CKKLC2.js +2 -0
- package/dist/{select-73JC2YBN.js.map → select-T7CKKLC2.js.map} +1 -1
- package/dist/{set-enabled-WQAO4BFF.js → set-enabled-U2HGNY2O.js} +2 -2
- package/dist/{surface-usage-CH3SHO77.js → surface-usage-HCJTUS6Z.js} +2 -2
- package/dist/{tool-QDU6MY6B.js → tool-2FMWZB4N.js} +2 -2
- package/dist/{tool-GCPMPPUE.js → tool-TX622HEB.js} +2 -2
- package/dist/wiki-MYJBOJSM.js +2 -0
- package/dist/workspace-setup-HAV4CQV7.js +2 -0
- package/package.json +2 -1
- package/dist/board-dashboard-SAYBNLZW.js.map +0 -1
- package/dist/chunk-3TY6YJHO.js +0 -153
- package/dist/chunk-3TY6YJHO.js.map +0 -1
- package/dist/chunk-D7M4WQFU.js.map +0 -1
- package/dist/chunk-E5OV6RTG.js.map +0 -1
- package/dist/chunk-EC6DKMR5.js.map +0 -1
- package/dist/chunk-FHI5WPFO.js.map +0 -1
- package/dist/chunk-RJ7DASOU.js.map +0 -1
- package/dist/chunk-SFPMS55Z.js.map +0 -1
- package/dist/chunk-UDJAODAB.js +0 -100
- package/dist/chunk-UDJAODAB.js.map +0 -1
- package/dist/config-J6CNV23P.js +0 -2
- package/dist/config-mutator-RGC72OEU.js +0 -2
- package/dist/program-V2JEVJAD.js.map +0 -1
- package/dist/registry-NDFYNQG4.js +0 -2
- package/dist/request-planner-YOUVO3BR.js +0 -2
- package/dist/request-planner-YOUVO3BR.js.map +0 -1
- package/dist/schedule-tool-4L6QAILN.js +0 -2
- package/dist/scheduler-65SE3WJJ.js +0 -2
- package/dist/seat-EO3EVBH3.js +0 -2
- package/dist/select-73JC2YBN.js +0 -2
- package/dist/wiki-CQZE5Z5C.js +0 -2
- package/dist/workspace-setup-EA3G42NY.js +0 -2
- /package/dist/{App-4VWAQOVA.js.map → App-T4Q7SW6M.js.map} +0 -0
- /package/dist/{ChatApp-KOTGUDZH.js.map → ChatApp-BKUJUIDL.js.map} +0 -0
- /package/dist/{agent-CGRHC2IS.js.map → agent-YZVEXFE7.js.map} +0 -0
- /package/dist/{chunk-UVMCAZL7.js.map → chunk-4FU4BFCH.js.map} +0 -0
- /package/dist/{chunk-NIW75USA.js.map → chunk-4NFDDTGA.js.map} +0 -0
- /package/dist/{chunk-2VGYRETU.js.map → chunk-53OS4TDU.js.map} +0 -0
- /package/dist/{chunk-J32LSOBJ.js.map → chunk-7UNS34CN.js.map} +0 -0
- /package/dist/{chunk-BFOB6ERR.js.map → chunk-DXWDVKMX.js.map} +0 -0
- /package/dist/{chunk-BZKGQMU2.js.map → chunk-FBODSJU6.js.map} +0 -0
- /package/dist/{chunk-4UCDWEC4.js.map → chunk-HCTWKWS3.js.map} +0 -0
- /package/dist/{chunk-5O6PIO7F.js.map → chunk-LWYKS5JU.js.map} +0 -0
- /package/dist/{chunk-QQAZ6EJP.js.map → chunk-M2L6S3Z5.js.map} +0 -0
- /package/dist/{chunk-6F2E5NQA.js.map → chunk-QJP37VVA.js.map} +0 -0
- /package/dist/{chunk-QFP4YZII.js.map → chunk-RS2T53EC.js.map} +0 -0
- /package/dist/{chunk-BG6SOGKT.js.map → chunk-U52IS3AF.js.map} +0 -0
- /package/dist/{chunk-MJWJ3JVA.js.map → chunk-WSKQUKCF.js.map} +0 -0
- /package/dist/{chunk-S4NZUPIN.js.map → chunk-WXQQO2PD.js.map} +0 -0
- /package/dist/{chunk-CJR26JUI.js.map → chunk-XEOT2F4C.js.map} +0 -0
- /package/dist/{chunk-3Z5P2CWS.js.map → chunk-XF26QNAW.js.map} +0 -0
- /package/dist/{chunk-CJL5OPNQ.js.map → chunk-Z7L74GGT.js.map} +0 -0
- /package/dist/{client-VYMTUZKN.js.map → client-O7SPKO5X.js.map} +0 -0
- /package/dist/{config-J6CNV23P.js.map → config-ZHQYXFOJ.js.map} +0 -0
- /package/dist/{config-mutator-RGC72OEU.js.map → config-mutator-RS2NTDNO.js.map} +0 -0
- /package/dist/{doctor-GHUPUBET.js.map → doctor-XP7O45RC.js.map} +0 -0
- /package/dist/{grants-Y667GZAU.js.map → grants-M4K6WXOO.js.map} +0 -0
- /package/dist/{promote-KY3Y2PVU.js.map → promote-UGWANGI2.js.map} +0 -0
- /package/dist/{query-BYCRYDM3.js.map → query-ZET7Z5FT.js.map} +0 -0
- /package/dist/{registry-NDFYNQG4.js.map → registry-UXWOY52B.js.map} +0 -0
- /package/dist/{review-candidates-56ZPJNXW.js.map → review-candidates-GU4S5VQH.js.map} +0 -0
- /package/dist/{routing-WKS2CZYF.js.map → routing-GJ4VXKIC.js.map} +0 -0
- /package/dist/{schedule-tool-4L6QAILN.js.map → schedule-tool-HD4LWKVC.js.map} +0 -0
- /package/dist/{scheduler-65SE3WJJ.js.map → scheduler-3LEHZHWJ.js.map} +0 -0
- /package/dist/{seat-EO3EVBH3.js.map → seat-L6XJ2BFA.js.map} +0 -0
- /package/dist/{set-enabled-WQAO4BFF.js.map → set-enabled-U2HGNY2O.js.map} +0 -0
- /package/dist/{surface-usage-CH3SHO77.js.map → surface-usage-HCJTUS6Z.js.map} +0 -0
- /package/dist/{tool-QDU6MY6B.js.map → tool-2FMWZB4N.js.map} +0 -0
- /package/dist/{tool-GCPMPPUE.js.map → tool-TX622HEB.js.map} +0 -0
- /package/dist/{wiki-CQZE5Z5C.js.map → wiki-MYJBOJSM.js.map} +0 -0
- /package/dist/{workspace-setup-EA3G42NY.js.map → workspace-setup-HAV4CQV7.js.map} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{f as o,n as u}from"./chunk-UIBUZ2N3.js";u();import{existsSync as s}from"fs";import{resolve as a}from"path";function c(){return!!(process.env.VITEST||process.env.NODE_ENV==="test"||process.env.AGENTX_NO_TELEMETRY)}function d(){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 f(n=new Date){return n.toISOString().slice(0,10)}function g(n,t,e=new Date){try{if(!t||c()||!d())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,f(e),e.toISOString())}catch{}}function E(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,i as u}from"./chunk-RJ7DASOU.js";u();import{existsSync as s}from"f
|
|
|
6
6
|
WHERE ${r.join(" AND ")}
|
|
7
7
|
GROUP BY kind, name
|
|
8
8
|
ORDER BY count DESC`).all(...i)}catch{return[]}}function T(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 l=new Set(["guard check","completion","serve"]);function y(n){return!!n&&!l.has(n)}export{g as a,E as b,T as c,y as d};
|
|
9
|
-
//# sourceMappingURL=chunk-
|
|
9
|
+
//# sourceMappingURL=chunk-4FU4BFCH.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{f as j,i as x,p as O,r as I}from"./chunk-S2GAEH2F.js";import{a as R}from"./chunk-JXF54PT7.js";import{
|
|
1
|
+
import{f as j,i as x,p as O,r as I}from"./chunk-S2GAEH2F.js";import{a as R}from"./chunk-JXF54PT7.js";import{ia as S,la as L}from"./chunk-SIOWVLZG.js";L();import{Command as z}from"commander";import f from"chalk";import{execFileSync as h}from"child_process";import{accessSync as T,constants as H,existsSync as y,readFileSync as b,readdirSync as $,statSync as X}from"fs";import{join as E,resolve as m}from"path";import{homedir as B}from"os";import{createRequire as F}from"module";import{execFile as M}from"child_process";var q=(t,n)=>new Promise(e=>{try{M(t,n,{timeout:5e3},(i,s)=>e(i?null:String(s)))}catch{e(null)}}),N="Open System Settings \u203A Notifications \u203A AgentX Helper, turn on Allow notifications and choose Banners.";async function P(t={}){if((t.platform??process.platform)!=="darwin")return{state:"unsupported",message:"Desktop banners are only shown on a Mac."};let n=t.helper===void 0?I():t.helper;if(!n)return{state:"missing",message:"AgentX Helper is not installed, so banners come from Script Editor and show its icon.",fix:'Run agentx desktop install, then run agentx notify "Hello" --title "Test" and click Allow.'};let e=await(t.run??q)(n,["notify-status"]),i=null;try{i=e?JSON.parse(e):null}catch{i=null}return i?i.authorization==="notDetermined"?{state:"not-asked",path:n,message:"macOS has not asked yet whether AgentX Helper may notify; banners show the Script Editor icon until it does.",fix:'Run agentx notify "Hello" --title "Test" and click Allow when macOS asks.'}:i.authorization==="denied"?{state:"not-allowed",path:n,message:"macOS does not allow AgentX Helper to notify, so banners show the Script Editor icon.",fix:N}:i.alertStyle==="none"?{state:"hidden",path:n,message:"AgentX Helper may notify, but its banner style is None, so nothing appears on screen.",fix:N}:{state:"ready",path:n,message:"AgentX Helper posts banners with the AgentX icon."}:{state:"outdated",path:n,message:"AgentX Helper is too old to report whether it may notify.",fix:"Run agentx desktop install to update it."}}async function J(t={}){let n=[];await Z(n),U(n);let e=K(n);return e&&Q(n,e),e&&Y(n,e),e&&ee(n,e),e&&te(n,e),e&&oe(n,e),e&&await G(n,e),e&&V(n,e),t.running!==!1&&e&&await ie(n,e),{checks:n,summary:W(n)}}var ke=new z().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 t=>{let{checks:n,summary:e}=await J({running:t.running!==!1});t.json?process.stdout.write(JSON.stringify({checks:n,summary:e},null,2)+`
|
|
2
2
|
`):se(n),process.exit(e.errors>0?1:0)});async function G(t,n,e=P){if(!O(n.notifications?.local).banner)return;let i=await e();i.state!=="unsupported"&&t.push({severity:i.state==="ready"?"ok":"warn",group:"Notifications",title:i.message,detail:i.path,fix:i.fix})}function V(t,n,e=process.platform,i=s=>{try{return T(s,H.X_OK),!0}catch{return!1}}){let s=n.reminders;if(!s?.enabled)return;if(e!=="darwin"){t.push({severity:"warn",group:"Reminders",title:"Reminders hand-back unavailable on this machine",detail:"reminders.enabled is set, but Apple Reminders exists only on macOS. The daemon starts without it.",fix:"Set reminders.enabled to false here, or enable it on the Mac node instead."});return}let r=s.command||"remindctl",a=r.includes("/")?i(r)?r:null:x(r,process.env.PATH||"",i);t.push(a?{severity:"ok",group:"Reminders",title:`Watching ${(s.lists||[]).join(", ")} for due reminders`,detail:a}:{severity:"fail",group:"Reminders",title:`${r} not found`,detail:"The daemon reads Apple Reminders through remindctl.",fix:"brew install steipete/tap/remindctl, or set reminders.command to its full path."})}async function Z(t){let n=R(process.versions.node);t.push({severity:n?"ok":"fail",group:"Environment",title:`Node.js ${process.versions.node}`,detail:n?void 0:"AgentX requires Node 22.19 or newer, up to Node 26.",fix:n?void 0:"Install Node 22 (nvm install 22 && nvm use 22)"});let e=l=>{try{return h(process.platform==="win32"?"where":"which",[l],{encoding:"utf-8"}).trim().split(`
|
|
3
3
|
`)[0]}catch{return null}},i=e("npm");t.push({severity:i?"ok":"fail",group:"Environment",title:i?"npm on PATH":"npm not found",detail:i||void 0});let s=e("claude");if(s){let l;try{l=h("claude",["--version"],{encoding:"utf-8",timeout:3e3}).trim()}catch{}t.push({severity:"ok",group:"Environment",title:`claude CLI ${l||"installed"}`,detail:s})}else t.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 l;try{l=h("codex",["--version"],{encoding:"utf-8",timeout:3e3}).trim()}catch{}t.push({severity:"ok",group:"Environment",title:`codex CLI ${l||"installed"}`,detail:r})}else t.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 a=e("opencode");if(a){let l;try{l=h("opencode",["--version"],{encoding:"utf-8",timeout:3e3}).trim()}catch{}t.push({severity:"ok",group:"Environment",title:`opencode CLI ${l||"installed"}`,detail:a})}else t.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{F(import.meta.url)("better-sqlite3"),t.push({severity:"ok",group:"Environment",title:`better-sqlite3 loads for Node ${process.version}`,detail:`NODE_MODULE_VERSION ${process.versions.modules}`})}catch(l){t.push({severity:"fail",group:"Environment",title:"better-sqlite3 native binding does not load",detail:l?.message??String(l),fix:`Run "pnpm rebuild better-sqlite3" with the same Node used by the daemon (${process.version}, modules ${process.versions.modules}).`})}}function U(t){if(process.platform!=="darwin")return;let n=E(B(),"Library/LaunchAgents"),e=[];try{e=$(n).filter(s=>/agentx\.voice.*\.plist$/.test(s))}catch{return}let i=s=>{try{return T(s,H.X_OK),!0}catch{return!1}};for(let s of e){let r=j(b(E(n,s),"utf-8")),a=x("ffmpeg",r,i);t.push(a?{severity:"ok",group:"Desktop",title:"ffmpeg reachable by the desktop app",detail:`${a} (${s})`}:{severity:"warn",group:"Desktop",title:"ffmpeg not reachable by the desktop app",detail:`Local Whisper transcription fails without it. ${s} sets PATH to ${r}.`,fix:"Install ffmpeg (brew install ffmpeg), then rerun agentx desktop install."})}}function K(t){let n=m(process.cwd(),"agentx.json");if(!y(n))return t.push({severity:"fail",group:"Config",title:"agentx.json not found",detail:`Looked in ${n}`,fix:"Run `agentx setup` to create one via the web wizard."}),null;let e;try{e=JSON.parse(b(n,"utf-8"))}catch(a){return t.push({severity:"fail",group:"Config",title:"agentx.json is not valid JSON",detail:a.message}),null}try{S(n),t.push({severity:"ok",group:"Config",title:`agentx.json valid (${Object.keys(e.agents||{}).length} agents, ${Object.keys(e.crons||{}).length} schedules)`})}catch(a){t.push({severity:"fail",group:"Config",title:"agentx.json failed schema validation",detail:a.message,fix:"Open /admin \u2192 Advanced tab to edit and re-save."})}let i=Object.entries(e.agents||{}).filter(([,a])=>a.tier==="claude-code");i.length>0&&((()=>{try{return h(process.platform==="win32"?"where":"which",["claude"],{stdio:"ignore"}),!0}catch{return!1}})()||t.push({severity:"fail",group:"Config",title:`${i.length} agent(s) use claude-code tier but claude CLI is missing`,detail:i.map(([l])=>l).join(", "),fix:"Install Claude Code (https://docs.anthropic.com/en/docs/claude-code) or switch these agents to tier=sdk."}));let s=Object.entries(e.agents||{}).filter(([,a])=>a.tier==="codex-cli");s.length>0&&((()=>{try{return h(process.platform==="win32"?"where":"which",["codex"],{stdio:"ignore"}),!0}catch{return!1}})()||t.push({severity:"fail",group:"Config",title:`${s.length} agent(s) use codex-cli tier but codex CLI is missing`,detail:s.map(([l])=>l).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(([,a])=>a.tier==="opencode");return r.length>0&&((()=>{try{return h(process.platform==="win32"?"where":"which",["opencode"],{stdio:"ignore"}),!0}catch{return!1}})()||t.push({severity:"fail",group:"Config",title:`${r.length} agent(s) use opencode tier but OpenCode CLI is missing`,detail:r.map(([l])=>l).join(", "),fix:"Install OpenCode from https://opencode.ai/docs/ or switch these agents to another tier."})),e}function Q(t,n){let e=new Set;if(C(n,e),e.size===0){t.push({severity:"ok",group:"Secrets",title:"No ${VAR} references in agentx.json"});return}let i=m(process.cwd(),".env"),s={};if(y(i))for(let l of b(i,"utf-8").split(/\r?\n/)){let u=l.match(/^\s*([A-Z_][A-Z0-9_]*)\s*=\s*(.*)$/i);u&&(s[u[1]]=u[2])}let r=[],a=[];for(let l of e){let u=process.env[l]??s[l];u===void 0?r.push(l):(!u.trim()||/^["']?\s*["']?$/.test(u))&&a.push(l)}r.length===0&&a.length===0?t.push({severity:"ok",group:"Secrets",title:`${e.size} env reference(s) resolved`}):(r.length>0&&t.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>`}),a.length>0&&t.push({severity:"warn",group:"Secrets",title:`${a.length} env var(s) set but empty`,detail:a.join(", ")}))}function C(t,n){if(typeof t=="string")for(let e of t.matchAll(/\$\{([A-Z_][A-Z0-9_]*)\}/g))n.add(e[1]);else if(Array.isArray(t))for(let e of t)C(e,n);else if(t&&typeof t=="object")for(let e of Object.values(t))C(e,n)}function Y(t,n){let e=[];for(let[i,s]of Object.entries(n.agents||{})){if(!s.workspace)continue;let r=s.workspace.startsWith("/")?s.workspace:m(process.cwd(),s.workspace);try{X(r).isDirectory()||e.push(`${i}: ${r} (exists but not a directory)`)}catch{e.push(`${i}: ${r}`)}}e.length===0?t.push({severity:"ok",group:"Workspaces",title:"All agent workspace folders exist"}):t.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 ee(t,n){let e=/\$\{[A-Za-z_][A-Za-z0-9_]*\}/g,i=[];for(let[s,r]of Object.entries(n.agents||{})){if(!r.workspace)continue;let a=r.workspace.startsWith("/")?r.workspace:m(process.cwd(),r.workspace);for(let l of["settings.json","settings.local.json"]){let u=m(a,".claude",l);if(!y(u))continue;let w;try{w=JSON.parse(b(u,"utf-8"))}catch(d){i.push({severity:"fail",agent:s,msg:`${l}: 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 g=p.match(e);g&&i.push({severity:"fail",agent:s,msg:`${l} env.${d} contains unexpanded ${[...new Set(g)].join(",")} \u2014 Claude Code does NOT expand \${VAR} in settings.json (use a fully-literal value)`})}let o=k.PATH;if(typeof o=="string"){let d=o.split(":").filter(Boolean);d.includes("/usr/bin")||i.push({severity:"fail",agent:s,msg:`${l} env.PATH missing /usr/bin \u2014 cat/head/sh/which will fail`}),d.includes("/bin")||i.push({severity:"fail",agent:s,msg:`${l} 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&&i.push({severity:"warn",agent:s,msg:`${l} permissions.allow is explicitly [] with ${p.length} deny rules \u2014 agent may be locked out (omit the key to use defaults instead)`})}}}if(i.length===0){t.push({severity:"ok",group:"Workspace settings",title:`All ${Object.keys(n.agents||{}).length} agent .claude/settings.json files clean`});return}for(let s of i)t.push({severity:s.severity,group:"Workspace settings",title:`${s.agent}: ${s.msg}`,fix:s.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 te(t,n){let e=n.webhooks??[],i=[],s=m(process.cwd(),n.workflows?.dir||".agentx/workflows"),r=new Set;try{if(y(s))for(let o of $(s))o.endsWith(".json")&&!o.startsWith("_")&&!o.endsWith(".disabled.json")&&r.add(o.replace(/\.json$/,""))}catch{}let a=new Set(["github","gitlab"]);for(let o of e){if(!o.enabled||!a.has(o.source))continue;!(o.triggers&&Object.keys(o.triggers).length>0)&&!o.defaultWorkflow&&i.push({severity:"warn",title:`webhook "${o.id}" (${o.source} -> ${o.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 o of e)if(!(!o.enabled||!o.triggers)){for(let[c,d]of Object.entries(o.triggers))r.size>0&&!r.has(d)&&i.push({severity:"fail",title:`webhook "${o.id}".triggers["${c}"] -> "${d}" \u2014 no such workflow under ${n.workflows?.dir||".agentx/workflows"}`,fix:"Create the workflow file or fix the id in agentx.json"});o.defaultWorkflow&&r.size>0&&!r.has(o.defaultWorkflow)&&i.push({severity:"fail",title:`webhook "${o.id}".defaultWorkflow -> "${o.defaultWorkflow}" \u2014 no such workflow`,fix:"Create the workflow file or fix the id in agentx.json"})}try{if(y(s)){for(let o of $(s))if(!(!o.endsWith(".json")||o.startsWith("_")||o.endsWith(".disabled.json")))try{let c=JSON.parse(b(m(s,o),"utf-8"));c?.status==="active"&&c?.state==="quarantined"&&i.push({severity:"warn",title:`workflow "${c.id??o}" 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 l=new Set((n.mesh?.peers??[]).map(o=>o.name));for(let o of e)!o.enabled||!o.node||l.has(o.node)||i.push({severity:"fail",title:`webhook "${o.id}" routes to mesh peer "${o.node}" which is not in mesh.peers`,fix:"Add the peer to mesh.peers or remove the node field on the webhook."});let u=new Map;for(let o of e){if(!o.enabled)continue;let c=`${o.source}:${o.agentId}`,d=u.get(c);d?i.push({severity:"warn",title:`webhook entries "${d}" and "${o.id}" both enabled for ${o.source} -> ${o.agentId}; only the first is dispatched`,fix:"Disable one entry, or merge their triggers maps into a single entry."}):u.set(c,o.id)}let w=new Set(Object.keys(n.agents??{}));for(let o of n.channels?.gitlab?.agentMappings??[])if(o?.agentId){if(o.node){l.has(o.node)||i.push({severity:"fail",title:`gitlab.agentMappings entry agentId="${o.agentId}" routes to mesh peer "${o.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(o.agentId)||i.push({severity:"warn",title:`gitlab.agentMappings entry agentId="${o.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[o,c]of Object.entries(n.channels?.telegram?.accounts??{}))c.agentBinding?w.has(c.agentBinding)||i.push({severity:"fail",title:`telegram account "${o}".agentBinding -> "${c.agentBinding}" \u2014 no such agent in agents.*`,fix:"Fix the agentBinding or define the agent in agents.*."}):i.push({severity:"fail",title:`telegram account "${o}" 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[o,c]of Object.entries(n.agents??{})){let d=c.integrations??[];if(!d.length)continue;let p=new Set;for(let g of d){let A=`${g.kind}::${g.label}`;p.has(A)&&i.push({severity:"warn",title:`agents.${o}.integrations: duplicate (${g.kind}, ${g.label})`,fix:"Remove the duplicate or rename one. (kind, label) must be unique per agent."}),p.add(A),k.has(g.kind)||i.push({severity:"warn",title:`agents.${o}.integrations: unknown kind "${g.kind}" on integration "${g.label}"`,fix:'Use a canonical kind from INTEGRATION_KINDS, or "custom" for ad-hoc services.'});let D=g.credentials||{};for(let[_,v]of Object.entries(D))typeof v=="string"&&/^[A-Z][A-Z0-9_]*$/.test(v)&&!process.env[v]&&i.push({severity:"warn",title:`agents.${o}.integrations[${g.kind}/${g.label}].${_} -> $${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(i.length===0){t.push({severity:"ok",group:"Routing",title:`${e.length} webhook(s), ${Object.keys(n.channels?.telegram?.accounts??{}).length} telegram account(s) \u2014 all routing references resolve`});return}for(let o of i)t.push({severity:o.severity,group:"Routing",title:o.title,fix:o.fix})}function ne(t){return typeof t?.canMeasure!="boolean"?null:t.canMeasure?{severity:"ok",group:"Runtime",title:"Daemon finds ffmpeg to measure phone recordings"}:{severity:"warn",group:"Runtime",title:t.allowUnmeasured?"Daemon can't find ffmpeg: phone recordings go unmeasured (voice.allowUnmeasured)":"Daemon can't find ffmpeg: phone voice input is refused",fix:"Install ffmpeg (brew install ffmpeg / sudo apt install ffmpeg), or set AGENTX_FFMPEG in the service's environment. The daemon's PATH is shorter than your shell's."}}async function ie(t,n){let e=n.dashboard?.daemonUrl?.replace(/\/+$/,"")||"http://127.0.0.1:18800",i={};n.dashboard?.token&&(i.Authorization=`Bearer ${n.dashboard.token}`);try{let s=new AbortController,r=setTimeout(()=>s.abort(),2e3),a=await fetch(e+"/health",{headers:i,signal:s.signal});if(clearTimeout(r),!a.ok){t.push({severity:"warn",group:"Runtime",title:`Daemon responded ${a.status}`,detail:e});return}let l=await a.json();t.push({severity:"ok",group:"Runtime",title:`Daemon healthy \u2014 ${l.agents?.length??0} agents live, uptime ${Math.round((l.uptime??0)/60)}m`,detail:e});let u=ne(l.voice);u&&t.push(u)}catch{t.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 oe(t,n){let e=Object.entries(n.agents||{}).filter(([,s])=>s?.codegraph===!0&&(s.tier==="claude-code"||s.tier==="codex-cli"));if(e.length===0)return;let i=null;try{i=h(process.platform==="win32"?"where":"which",["codegraph"],{encoding:"utf-8"}).trim().split(`
|
|
5
5
|
`)[0]||null}catch{i=null}if(i){let s;try{s=h("codegraph",["--version"],{encoding:"utf-8",timeout:3e3}).trim()}catch{}t.push({severity:"ok",group:"CodeGraph",title:`codegraph CLI ${s||"installed"}`,detail:i})}else t.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[s,r]of e){if(!r.workspace||!y(r.workspace)){t.push({severity:"warn",group:"CodeGraph",title:`${s}: workspace missing`,detail:`Workspace path ${r.workspace} does not exist \u2014 codegraph will skip this agent.`});continue}let a=m(r.workspace,".codegraph","codegraph.db");y(a)?t.push({severity:"ok",group:"CodeGraph",title:`${s}: index present`,detail:a}):t.push({severity:"warn",group:"CodeGraph",title:`${s}: 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 W(t){return{errors:t.filter(n=>n.severity==="fail").length,warnings:t.filter(n=>n.severity==="warn").length,ok:t.filter(n=>n.severity==="ok").length}}function se(t){console.log(),console.log(f.bold(" agentx doctor")),console.log();let n=new Map;for(let i of t)n.has(i.group)||n.set(i.group,[]),n.get(i.group).push(i);for(let[i,s]of n){console.log(f.bold(" "+i));for(let r of s){let a=r.severity==="ok"?f.green("\u2713"):r.severity==="warn"?f.yellow("!"):f.red("\u2717");console.log(` ${a} ${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=W(t);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{P as a,J as b,ke as c,G as d,V as e,ne as f};
|
|
7
|
-
//# sourceMappingURL=chunk-
|
|
7
|
+
//# sourceMappingURL=chunk-4NFDDTGA.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{b as tt,c as nt}from"./chunk-GLQLCBJS.js";import{a as Ve}from"./chunk-GV7S5BAX.js";import{c as Ze}from"./chunk-SZDACK6O.js";import{b as ce}from"./chunk-AFRHKA7B.js";import{b as qe,e as Ye,f as Ge,i as Qe,k as Xe,l as et,q as rt}from"./chunk-
|
|
1
|
+
import{b as tt,c as nt}from"./chunk-GLQLCBJS.js";import{a as Ve}from"./chunk-GV7S5BAX.js";import{c as Ze}from"./chunk-SZDACK6O.js";import{b as ce}from"./chunk-AFRHKA7B.js";import{b as qe,e as Ye,f as Ge,i as Qe,k as Xe,l as et,q as rt}from"./chunk-M2L6S3Z5.js";import{c as Ke,e as ze,f as Je}from"./chunk-KA2UIZXG.js";import{e as He,g as Ue,h as Be,i as de}from"./chunk-GQ76J72O.js";import{a as _e,b as oe,c as se,d as Le,e as ae}from"./chunk-QD6IICK7.js";import{y as Ce,z as hn}from"./chunk-GQMUFSKB.js";import{b as Me,f as mn}from"./chunk-72SZI2UW.js";import{c as We,d as je}from"./chunk-QOYAX2FT.js";import{f as ie,n as gn}from"./chunk-UIBUZ2N3.js";import{existsSync as yn,mkdirSync as it,readFileSync as wn,writeFileSync as kn,renameSync as bn}from"fs";import{resolve as ot,dirname as vn}from"path";var st=class{filePath;log;constructor(e={}){let t=e.baseDir??ot(process.cwd(),".agentx");this.filePath=ot(t,"handover.json"),this.log=e.log??console.error.bind(console,"[handover]"),it(t,{recursive:!0})}key(e,t,n){return`${e}::${t}::${n||"-"}`}load(){if(!yn(this.filePath))return{version:1,overrides:{}};try{let e=wn(this.filePath,"utf-8"),t=JSON.parse(e);return!t||typeof t!="object"?{version:1,overrides:{}}:(t.overrides||(t.overrides={}),t)}catch(e){return this.log(`load failed, starting empty: ${e?.message||e}`),{version:1,overrides:{}}}}save(e){it(vn(this.filePath),{recursive:!0});let t=`${this.filePath}.tmp-${process.pid}`;kn(t,JSON.stringify(e,null,2)),bn(t,this.filePath)}get(e,t,n){let o=this.load().overrides[this.key(e,t,n)];if(o&&!(o.expiresAt&&Date.parse(o.expiresAt)<Date.now()))return o}set(e){if(!e.channel||!e.chatId||!e.toAgent)throw new Error("channel, chatId and toAgent are required");let t=this.load(),n={...e,createdAt:e.createdAt??new Date().toISOString()};return t.overrides[this.key(n.channel,n.chatId,n.accountId)]=n,this.save(t),n}remove(e,t,n){let i=this.load(),o=this.key(e,t,n);return o in i.overrides?(delete i.overrides[o],this.save(i),!0):!1}consumeSummary(e,t,n){let i=this.load(),o=i.overrides[this.key(e,t,n)];if(!o||!o.summary||o.summaryConsumedAt)return;let s=o.summary;return o.summaryConsumedAt=new Date().toISOString(),this.save(i),s}listActive(){let e=this.load(),t=Date.now();return Object.values(e.overrides).filter(n=>!n.expiresAt||Date.parse(n.expiresAt)>=t)}listByAgent(e){let t=this.listActive();return{incoming:t.filter(n=>n.toAgent===e),outgoing:t.filter(n=>n.fromAgent===e)}}};import{readFileSync as ut,writeFileSync as le,appendFileSync as Rn,mkdirSync as $n,existsSync as pt,statSync as xn}from"fs";import{resolve as U}from"path";var at="[Agent Memory \u2014 persistent facts from past conversations]",Sn=2400;function dt(r,e){return r?`UNVERIFIED (${e===null?"age unknown":`${e}d old`}) `:""}function In(r,e=Date.now()){let t=ze(r,e),n=r.undated?"UNVERIFIED (never checked) ":dt(t,He(r.verifiedAt,e));return{line:`- [wiki ${r.id}] ${n}${r.subject} \xB7 ${r.attribute}: ${r.value} (${r.source}, checked ${r.verifiedAt.slice(0,10)} by ${r.verifiedBy})`,unverified:t}}function An(r,e){let n=!r.source.chatId.startsWith("-")&&/^\d+$/.test(r.source.chatId)?"DM":r.source.chatId,i=Be(r,e);return{line:`- [${r.category}] ${dt(i,Ue(r,e))}${r.content} (${n}, ${r.source.date})`,unverified:i}}function ct(r,e=[],t=Date.now()){let n=[],i=new Set,o=at.length,s=!1;for(let a of r){let d=e.length>0?Je(a.content,e):null,c,u,p=d&&Date.parse(a.createdAt)>Date.parse(d.verifiedAt)&&!a.content.toLowerCase().includes(d.value.toLowerCase());if(d&&!p){if(i.has(d.id))continue;i.add(d.id),c=In(d,t),u=d.id}else c=An(a,t),d&&(c.line+=` [differs from wiki ${d.id}]`);let f=c.unverified&&!s?de.length:0;if(o+f+c.line.length>Sn)break;n.push({fact:a,...c,...u?{wikiRef:u}:{}}),o+=f+c.line.length,f&&(s=!0)}return n}function lt(r){if(r.length===0)return"";let e=r.some(t=>t.unverified)?[de]:[];return[at,...e,...r.map(t=>t.line),"[End Memory]"].join(`
|
|
2
2
|
`)}var ft=200,Pn=7,gt=class{memoryDir;ledger;constructor(e=process.cwd()){this.memoryDir=U(e,".agentx/memory"),this.ledger=new Ke(U(e,".agentx/wiki")),pt(this.memoryDir)||$n(this.memoryDir,{recursive:!0})}filePath(e){return U(this.memoryDir,`${e}.jsonl`)}addMemory(e,t){if(t.category==="secret"||oe(t.content))return!1;let n=t.source.trust??_e(t.source.channel),i=t.review??Le(n),o={...t,source:{...t.source,trust:n},...i?{review:i}:{},id:`${Date.now().toString(36)}-${Math.random().toString(36).slice(2,6)}`,createdAt:new Date().toISOString()};return Rn(this.filePath(e),JSON.stringify(o)+`
|
|
3
3
|
`),!0}parsedCache=new Map;getAll(e){let t=this.filePath(e);if(!pt(t))return[];try{let{mtimeMs:n,size:i}=xn(t),o=this.parsedCache.get(t);if(o&&o.mtimeMs===n&&o.size===i)return o.facts.slice();let a=ut(t,"utf-8").split(`
|
|
4
4
|
`).filter(d=>d.trim()).map(d=>{try{return JSON.parse(d)}catch{return null}}).filter(d=>d!==null);return this.parsedCache.set(t,{mtimeMs:n,size:i,facts:a}),a.slice()}catch{return[]}}findRelevant(e,t,n=8){let i=this.getAll(t).filter(ae);if(i.length===0)return[];let o=i.map(g=>`${g.keywords.join(" ")} ${g.content}`),s=U(this.memoryDir,`${t}_bm25_cache.json`),a=je(o,s),d=We(e,a),c=new Map(d.map(g=>[g.docIndex,g.score])),p=i.map((g,h)=>{let m=c.get(h)??0;return g.category==="commitment"&&(m+=1),{memory:g,score:m}}).filter(g=>g.score>0);if(p.length===0)return i.slice(-n);let f=p.sort((g,h)=>h.score-g.score).slice(0,n).map(g=>g.memory);if(f.length>0)try{this.recordAccess(t,f.map(g=>g.id))}catch{}return f}buildContext(e){return lt(this.contextLines(e))}contextFacts(e){return this.contextLines(e).map(t=>t.fact)}contextLines(e){let t=[];try{t=this.ledger.list()}catch{}return ct(e,t)}recordAccess(e,t){if(t.length===0)return;let n=this.getAll(e),i=new Set(t),o=!1;for(let s of n)if(i.has(s.id)){s.accessCount=(s.accessCount||0)+1,s.lastAccessed=new Date().toISOString();let a=s.intervalDays||1;s.intervalDays=Math.min(a*2,30),s.nextReview=new Date(Date.now()+s.intervalDays*864e5).toISOString(),o=!0}o&&le(this.filePath(e),n.map(s=>JSON.stringify(s)).join(`
|
|
@@ -25,4 +25,4 @@ import{b as tt,c as nt}from"./chunk-GLQLCBJS.js";import{a as Ve}from"./chunk-GV7
|
|
|
25
25
|
`).filter(Boolean),i=[];for(let o=n.length-1;o>=0&&i.length<t;o--)try{let s=JSON.parse(n[o]);(!e||s.person===e)&&i.push(s)}catch{}return i}load(){if(Oe(this.file))try{let e=JSON.parse(Ne(this.file,"utf-8"));if(e&&Array.isArray(e.devices))return e}catch{}return{devices:[]}}save(e){Gt(Qt(this.file),{recursive:!0});let t=`${this.file}.${process.pid}.${en(4).toString("hex")}.tmp`;Zt(t,JSON.stringify(e,null,2)+`
|
|
26
26
|
`,{encoding:"utf-8",mode:384}),Vt(t,this.file)}};import{extname as ui}from"path";var nn=/<agentx-artifact>([\s\S]*?)<\/agentx-artifact>/gi,rn=/<agentx-artifact>[\s\S]*$/i;function Ps(r,e=1/0,t=!1){let n=[],i=String(r??"");if(!/<agentx-artifact>/i.test(i))return{text:i,artifacts:n};let o=i.replace(nn,(s,a)=>{try{let d=JSON.parse(a.trim());if(d&&typeof d.filename=="string"&&typeof d.mime=="string"&&n.length<e){let c=d.mime,u=c.startsWith("image/")?"image":c==="application/pdf"?"pdf":c.startsWith("text/")?"text":"file";n.push({type:u,filename:d.filename,mime:c})}}catch{}return""});return{text:(t?o.replace(rn,""):o).trim(),artifacts:n}}function Ts(r,e=!1){let t=String(r??""),n=t;if(/<agentx-artifact>/i.test(t)){let i=t.replace(nn,(o,s)=>{try{let a=JSON.parse(s.trim());return a&&typeof a.filename=="string"?a.filename:""}catch{return""}});n=(e?i.replace(rn,""):i).trim()}return n.replace(/```[\s\S]*?```|`[^`\n]*`|!\[([^\]\n]*)\]\(([^)\s]+)\)/g,(i,o,s)=>o===void 0||s===void 0?i:`[${o||s}](${s})`)}var pi={".png":{mime:"image/png",kind:"image"},".jpg":{mime:"image/jpeg",kind:"image"},".jpeg":{mime:"image/jpeg",kind:"image"},".gif":{mime:"image/gif",kind:"image"},".webp":{mime:"image/webp",kind:"image"},".svg":{mime:"image/svg+xml",kind:"file"},".pdf":{mime:"application/pdf",kind:"file"},".txt":{mime:"text/plain; charset=utf-8",kind:"file"},".md":{mime:"text/markdown; charset=utf-8",kind:"file"},".csv":{mime:"text/csv; charset=utf-8",kind:"file"},".json":{mime:"application/json",kind:"file"},".mp3":{mime:"audio/mpeg",kind:"audio"},".m4a":{mime:"audio/mp4",kind:"audio"},".wav":{mime:"audio/wav",kind:"audio"},".mp4":{mime:"video/mp4",kind:"video"},".webm":{mime:"video/webm",kind:"video"}},Os={bytes:20*1024*1024,perMessage:20,pathChars:512};function Ns(r){return pi[ui(r).toLowerCase()]??null}var fi=["[Phone app: showing files]","To show the owner a file you created (a chart, a screenshot, a PDF, a recording), save or copy it into .agentx/outbox/ in your workspace and end your reply with one line per file:",'<agentx-artifact>{"filename":".agentx/outbox/<name.ext>","mime":"<mime/type>"}</agentx-artifact>',"Files outside your workspace (/tmp, your home folder) are refused. Outbox files are removed after 7 days.","The line is removed from the text. Images show in the reply, audio and video get a player, other files an Open link. Allowed: png, jpg, gif, webp, svg, pdf, txt, md, csv, json, mp3, m4a, wav, mp4, webm; up to 20 MB and 20 files per reply.","A picture already on the web can go inline as ."].join(`
|
|
27
27
|
`);function Ds(r,e,t){return r==="app"&&e&&t!==!1?fi:void 0}import{lstatSync as Ee,mkdirSync as gi,readdirSync as mi,realpathSync as De,unlinkSync as hi}from"fs";import{join as ne,resolve as on}from"path";var sn=".agentx/outbox",yi=7*24*60*60*1e3;function wi(r,e){try{let t=De(on(e,r)),n=ne(t,sn);return De(n)===n&&Ee(n).isDirectory()?n:null}catch{return null}}function js(r,e={}){let t=e.cwd??process.cwd();if(e.create!==!1)try{let i=De(on(t,r));Ee(ne(i,".agentx"),{throwIfNoEntry:!1})?.isSymbolicLink()||gi(ne(i,sn),{recursive:!0})}catch{return 0}let n=wi(r,t);return n?an(n,(e.now??Date.now())-(e.maxAgeMs??yi)):0}function an(r,e){let t=0;for(let n of mi(r)){let i=ne(r,n);try{let o=Ee(i);o.isDirectory()?t+=an(i,e):Math.max(o.mtimeMs,o.ctimeMs)<e&&(hi(i),t++)}catch{}}return t}export{st as a,gt as b,mt as c,Fi as d,Nn as e,Wi as f,$ as g,pe as h,v as i,B as j,Bn as k,he as l,we as m,be as n,xe as o,Hr as p,qr as q,Te as r,ar as s,ai as t,tn as u,Ps as v,Ts as w,Os as x,Ns as y,Ds as z,sn as A,js as B};
|
|
28
|
-
//# sourceMappingURL=chunk-
|
|
28
|
+
//# sourceMappingURL=chunk-53OS4TDU.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
2
|
-
//# sourceMappingURL=chunk-
|
|
1
|
+
import{y as l,z as f}from"./chunk-GQMUFSKB.js";import{a,f as p}from"./chunk-72SZI2UW.js";f();p();var d="task-tier";function u(e){return{task:w(e.message,2e3),agent:e.agent,channel:e.channel??null,continuesExistingSession:e.isFollowUp??!1,...e.toolsAvailable?{toolsAvailable:e.toolsAvailable}:{}}}var c={needsFlagship:a("This task needs the strongest available model to be done correctly.",{true:"Doing it well takes real reasoning: writing or reviewing code, diagnosing a failure, planning work across several steps, weighing a judgement, handling money, touching production or anything irreversible, following a long or ambiguous instruction, or work where being subtly wrong would not be noticed. Anything where a weaker model would produce something that LOOKS right belongs here.",false:"It is mechanical and its result is obvious on sight: acknowledging or confirming a message, restating or formatting something already written, a greeting, a status echo, reading one file and reporting what it says, a lookup with one right answer, a scheduled heartbeat with nothing to decide."})};function g(e){return e.needsFlagship.noul}function w(e,n){return e.length>n?`${e.slice(0,n)}\u2026`:e}var h=.2;function T(e){return e==="voice"||e==="desktop"||e==="opencode"}var k=60*60*1e3;function _(e,n){if(e==="claude-code"){let t=n?.cheapModels?.[e]??n?.cheapModel;return t&&/^(claude-|haiku$|sonnet$|opus$)/.test(t)?t:void 0}if(e==="codex-cli"){let t=n?.cheapModels?.[e];return t&&/^(gpt-|o\d)/.test(t)?t:void 0}}async function y(e){let n=(s,r=null)=>({downgraded:!1,needsFlagship:r,reason:s});if(T(e.channel))return n(`${e.channel} requests retain the configured model`);if(!e.cheapModel)return n("no cheaper model configured");if(e.isFollowUp){let s=e.sessionIdleMs??0,r=e.cacheTtlMs??k;if(s<r)return n(`follow-up on a warm cache (idle ${Math.round(s/1e3)}s) \u2014 swapping would cost more than it saves`)}let t;try{t=await l(d,u(e),c,{incumbent:{needsFlagship:1},features:{agent:e.agent,channel:e.channel??"unknown"},timeoutMs:1500})}catch{return n("routing seat threw \u2014 keeping the flagship")}if(!t)return n("routing seat unavailable \u2014 keeping the flagship");let o=g(t.answers);if(t.mode!=="active")return n(`shadow \u2014 would ${o<(e.threshold??h)?"downgrade":"keep"} (${o.toFixed(2)})`,o);let i=e.threshold??h;return o>=i?n(`needs the flagship (${o.toFixed(2)})`,o):{model:e.cheapModel,downgraded:!0,needsFlagship:o,reason:`mechanical task (${o.toFixed(2)} below ${i}) \u2192 ${e.cheapModel}`}}export{h as a,T as b,_ as c,y as d};
|
|
2
|
+
//# sourceMappingURL=chunk-7UNS34CN.js.map
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import{existsSync as f,mkdirSync as H,writeFileSync as k,readFileSync as w}from"fs";import{resolve as m}from"path";import{createHash as ke}from"crypto";import{readFileSync as Q,existsSync as z,unlinkSync as Z,readdirSync as De}from"fs";import{resolve as T}from"path";function Ie(n,t){let s={},r=e=>{let o=T(n,e);if(z(o))try{return Q(o,"utf-8").trim()||void 0}catch{return}};if(t){let e=r(`SOUL.${t}.md`);e?(s.soul=e,s.soulProfile=t):s.soul=r("SOUL.md")}else s.soul=r("SOUL.md");if(s.identity=r("IDENTITY.md"),s.user=r("USER.md"),s.agents=r("AGENTS.md"),s.bootstrap=r("BOOTSTRAP.md"),s.bootstrap)try{Z(T(n,"BOOTSTRAP.md"))}catch{}return s}function Ue(n){let t=[];return n.identity&&t.push(`[Identity]
|
|
2
|
+
${n.identity}`),n.soul&&t.push(`[Personality & Boundaries]
|
|
3
|
+
${n.soul}`),n.user&&t.push(`[User Profile]
|
|
4
|
+
${n.user}`),n.bootstrap&&t.push(`[First-Run Instructions \u2014 one time only]
|
|
5
|
+
${n.bootstrap}`),t.length===0?"":t.join(`
|
|
6
|
+
|
|
7
|
+
`)}function v(n){return`# ${n.name}
|
|
8
|
+
|
|
9
|
+
> Agent ID: \`${n.id}\` | Tier: ${n.tier||"claude-code"}
|
|
10
|
+
|
|
11
|
+
## Role
|
|
12
|
+
|
|
13
|
+
${n.role||"AI agent managed by AgentX orchestrator."}
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
This workspace is managed by AgentX. The agent runs via:
|
|
18
|
+
\`\`\`bash
|
|
19
|
+
# Direct execution
|
|
20
|
+
agentx daemon send ${n.id} "your task here"
|
|
21
|
+
|
|
22
|
+
# Or via any connected channel (Telegram, WhatsApp, GitLab)
|
|
23
|
+
\`\`\`
|
|
24
|
+
|
|
25
|
+
## Code Style
|
|
26
|
+
|
|
27
|
+
- Follow existing patterns in the codebase
|
|
28
|
+
- Use the language and framework conventions already present
|
|
29
|
+
- Keep changes minimal and focused
|
|
30
|
+
|
|
31
|
+
## Testing
|
|
32
|
+
|
|
33
|
+
Run tests before committing:
|
|
34
|
+
\`\`\`bash
|
|
35
|
+
npm test # or the project's test command
|
|
36
|
+
\`\`\`
|
|
37
|
+
|
|
38
|
+
## PR Guidelines
|
|
39
|
+
|
|
40
|
+
- One logical change per commit
|
|
41
|
+
- Reference issue numbers when applicable
|
|
42
|
+
- Keep PR descriptions concise
|
|
43
|
+
|
|
44
|
+
## AgentX Integration
|
|
45
|
+
|
|
46
|
+
This agent is part of an AgentX mesh. It can:
|
|
47
|
+
- Receive tasks from messaging channels (Telegram, WhatsApp, Discord)
|
|
48
|
+
- Respond to GitLab @mentions on issues and MRs
|
|
49
|
+
- Delegate to other agents by mentioning their handles
|
|
50
|
+
- Send cross-channel messages via the /send API
|
|
51
|
+
- Access its wiki knowledge base for context
|
|
52
|
+
`}function Ne(n){let t=n.match(/\/soul\s+(\w+)/i);return t?t[1].toLowerCase():/\/soul\s*$/i.test(n)?"default":null}import{execFile as ee}from"child_process";import{existsSync as B}from"fs";import{resolve as te}from"path";var D=["mcp__codegraph__codegraph_search","mcp__codegraph__codegraph_context","mcp__codegraph__codegraph_callers","mcp__codegraph__codegraph_callees","mcp__codegraph__codegraph_impact","mcp__codegraph__codegraph_node","mcp__codegraph__codegraph_status","mcp__codegraph__codegraph_files"],E={command:"codegraph",args:["serve","--mcp"]};function C(){return["## CodeGraph","","This workspace has CodeGraph initialized \u2014 a semantic code-knowledge-graph indexed via tree-sitter. Use it instead of grep/find/Read for exploration.","","**ALWAYS spawn an Explore subagent for `codegraph_explore` / `codegraph_context`** \u2014 those tools return large source sections that would otherwise bloat the main session. Include this in the Explore prompt:","","> Use `codegraph_explore` as your PRIMARY tool \u2014 one call returns full source from all relevant files. Do not re-Read files codegraph_explore already returned. Fall back to grep/glob/Read only if codegraph returned no results.","","**In the main session, use these lightweight codegraph tools directly:**","","| Tool | Use for |","|---|---|","| `codegraph_search` | Find symbols by name across the codebase |","| `codegraph_callers` / `codegraph_callees` | Trace call flow |","| `codegraph_impact` | Check what's affected before editing a symbol |","| `codegraph_node` | Get a single symbol's details |","| `codegraph_files` | Indexed file structure (faster than ls) |","| `codegraph_status` | Index health |",""].join(`
|
|
53
|
+
`)}function He(n){let t=n.mcp??{};return!n.codegraph||t.codegraph?t:{...t,codegraph:{command:E.command,args:[...E.args]}}}var ne=".codegraph",se=10*60*1e3,L=2;function re(n){let t=Date.now();return new Promise((s,r)=>{ee("codegraph",["init","--index","--quiet",n],{timeout:se,maxBuffer:4*1024*1024},e=>{e?r(e):s(Date.now()-t)})})}async function We(n,t=console.error){let s=[];for(let[o,a]of Object.entries(n))a.codegraph&&(a.tier!=="claude-code"&&a.tier!=="codex-cli"||B(a.workspace)&&(B(te(a.workspace,ne))||s.push({agentId:o,workspace:a.workspace})));if(s.length===0)return;t(`codegraph: indexing ${s.length} workspace(s) in background (max ${L} parallel)`);let r=0,e=[];for(let o=0;o<Math.min(L,s.length);o++)e.push((async()=>{for(;;){let a=r++;if(a>=s.length)return;let{agentId:i,workspace:l}=s[a];try{let u=await re(l);t(` codegraph: indexed ${i} in ${(u/1e3).toFixed(1)}s`)}catch(u){let p=u?.code==="ENOENT"?" (codegraph CLI not on PATH; install with `npm i -g @colbymchenry/codegraph`)":"";t(` codegraph: ${i} index failed: ${u?.message??u}${p}`)}}})());await Promise.allSettled(e)}import{appendFileSync as oe,existsSync as N,mkdirSync as j,readdirSync as I,realpathSync as ie,statSync as ae,unlinkSync as ce,utimesSync as le,writeFileSync as ue}from"fs";import{createHash as M}from"crypto";import{resolve as b,sep as pe}from"path";var A="/observation/pack";function Ye(n=process.cwd()){return b(n,".agentx/observations")}function F(n,t){return b(n,encodeURIComponent(t).replace(/\./g,"%2E"))}function G(n){return n==="bypassPermissions"}function U(n){try{return ie(n)}catch{return b(n)}}function de(n,t){return t.some(s=>{try{return new RegExp(`^(?:${s})$`).test(n)}catch{return s===n}})}var he=2e3;function _(n,t,s){return n.subarray(t,s).toString("utf-8").replace(/^�+|�+$/g,"")}function ge(n,t,s){let r=Buffer.from(n,"utf-8"),e=_(r,0,s.headBytes),o=s.tailBytes>0?_(r,r.length-s.tailBytes,r.length):"",a=r.length-s.headBytes-s.tailBytes,i=n.split(`
|
|
54
|
+
`).length,u=r.length/i>he?"Its lines are very long, so read it by byte range (for example head -c or tail -c, or jq for JSON) for the part you need.":"Read that file with offset and limit (line numbers), or grep it, for the part you need. Do not cat it whole: that is packed again.";return[`[ObservationPack: this result is ${r.length} bytes (${i} lines). Only its first ${s.headBytes} and last ${s.tailBytes} bytes are shown.`,`The exact original is saved at ${t}`,`${u} Do not guess at what is not shown.]`,e,`[... ${a} bytes not shown ...]`,o].join(`
|
|
55
|
+
`)}function me(n){let t=n;return!!t&&typeof t=="object"&&t.type==="text"&&typeof t.file?.content=="string"}function O(n){if(n==="")return 0;let t=1;for(let s=n.indexOf(`
|
|
56
|
+
`);s!==-1;s=n.indexOf(`
|
|
57
|
+
`,s+1))t++;return n.endsWith(`
|
|
58
|
+
`)&&t--,t}function fe(n,t,s){let r=Buffer.from(n,"utf-8"),e=O(n),o=_(r,0,s.headBytes),a=o.lastIndexOf(`
|
|
59
|
+
`);o=a===-1?o:o.slice(0,a+1);let i=s.tailBytes>0?_(r,r.length-s.tailBytes,r.length):"",l=i.indexOf(`
|
|
60
|
+
`);i=l===-1?i:i.slice(l+1);let u=a===-1?0:O(o),p=l===-1?0:O(i),c=t.startLine,d=c+e-1,h=c+u,g=d-p,x=r.length-Buffer.byteLength(o,"utf-8")-Buffer.byteLength(i,"utf-8"),K=typeof t.totalLines=="number"&&t.totalLines>e?` (${t.totalLines} lines in all)`:"",X=h===g?`Line ${h} (${x} bytes) is`:`Lines ${h} to ${g} (${x} bytes) are`,y=[u===0&&o!==""?c:null,p===0&&i!==""?d:null].filter(R=>R!==null),Y=y.length===0?"":y.length===1||y[0]===y[1]?` (line ${y[0]} only in part)`:` (lines ${y[0]} and ${y[1]} only in part)`,V=p>0?` Lines ${g+1} to ${d} follow; the numbers beside them are not the file's.`:"";return[o,`[ObservationPack: this read is ${r.length} bytes, lines ${c} to ${d} of ${t.path}${K}.`,`${X} not shown${Y}.${V}`,"Read the file again with offset and limit for the lines you need, or Grep it. Do not guess at what is not shown.]",i].filter(R=>R!=="").join(`
|
|
61
|
+
`)}function ye(n,t){let s=M("sha256").update(t).digest("hex"),r=b(n,`${s}.txt`);if(N(r)){let e=new Date;le(r,e,e)}else j(n,{recursive:!0,mode:448}),ue(r,t,{mode:384});return{sha:s,path:r}}function be(n,t,s){if(!t.enabled)return null;let r=n.tool_name;if(!r||!de(r,t.tools))return null;let e=n.tool_input?.file_path;if(typeof e=="string"&&U(e).startsWith(U(s)+pe)||t.limitBytes<=t.headBytes+t.tailBytes)return null;if(r==="Read"){let i=n.tool_response;if(!me(i))return null;let l=i.file.content,u=Buffer.byteLength(l,"utf-8");if(u<=t.limitBytes)return null;let p=typeof e=="string"?e:i.file.filePath??"the file you read",c=n.tool_input?.offset,d=typeof i.file.startLine=="number"&&i.file.startLine>=1?i.file.startLine:typeof c=="number"&&c>=1?c:1,h=fe(l,{path:p,startLine:d,totalLines:i.file.totalLines},t),g=M("sha256").update(l).digest("hex");return{updated:{...i,file:{...i.file,content:h}},packs:[{sha:g,path:p,bytes:u,keptBytes:Buffer.byteLength(h,"utf-8")}]}}let o=[],a=i=>{if(typeof i=="string"){let l=Buffer.byteLength(i,"utf-8");if(l<=t.limitBytes)return i;let{sha:u,path:p}=ye(s,i),c=ge(i,p,t);return o.push({sha:u,path:p,bytes:l,keptBytes:Buffer.byteLength(c,"utf-8")}),c}if(Array.isArray(i))return i.map(a);if(i&&typeof i=="object"){let l=i;return l.type==="image"||l.type==="audio"||l.isImage===!0||typeof l.persistedOutputPath=="string"?i:Object.fromEntries(Object.entries(l).map(([u,p])=>[u,u==="blob"?p:a(p)]))}return i};try{let i=a(n.tool_response);return o.length>0?{updated:i,packs:o}:null}catch{return null}}function Ve(n,t,s){let r=n&&typeof n=="object"?be(n,t,s.dir):null;if(!r)return"";try{let e=new Date().toISOString(),o=r.packs.map(a=>JSON.stringify({at:e,agent:s.agentId,session:n.session_id,tool:n.tool_name,sha:a.sha,bytes:a.bytes,keptBytes:a.keptBytes}));j(s.dir,{recursive:!0,mode:448}),oe(b(s.dir,"index.jsonl"),o.join(`
|
|
62
|
+
`)+`
|
|
63
|
+
`,{mode:384})}catch{}return JSON.stringify({hookSpecificOutput:{hookEventName:"PostToolUse",updatedToolOutput:r.updated}})}function Qe(n,t,s=Date.now()){if(t<=0||!N(n))return 0;let r=s-t*864e5,e=0;for(let o of I(n,{withFileTypes:!0})){if(!o.isDirectory())continue;let a=b(n,o.name);for(let i of I(a)){if(!i.endsWith(".txt"))continue;let l=b(a,i);try{ae(l).mtimeMs<r&&(ce(l),e++)}catch{}}}return e}var P="<!-- agentx-managed: hash=",$=" -->";function W(n){return ke("sha256").update(n.systemPrompt??"").update("\0").update(n.codegraph?"cg:1":"cg:0").digest("hex").slice(0,16)}function xe(n){return`${P}${W(n)}${$}`}function we(n){let t=n.split(`
|
|
64
|
+
`,1)[0]??"";return!t.startsWith(P)||!t.endsWith($)?null:t.slice(P.length,t.length-$.length)}function J(n,t){let s=`${n} ${t.systemPrompt||""} ${t.name}`.toLowerCase();return/cod(e|ing|er)|develop|engineer|program/.test(s)?"coding":/pm|project.?manag|product|scrum/.test(s)?"pm":/devops|deploy|infra|ops|sre|ci.?cd/.test(s)?"devops":/qa|test|quality|forensic/.test(s)?"qa":"general"}function _e(n,t,s){let r=J(n,t),e=[];switch(e.push(xe(t)),e.push(`# ${t.name}`),e.push(""),e.push("@AGENTS.md"),e.push(""),t.systemPrompt&&t.systemPrompt.trim().length>0&&(e.push("## Role"),e.push(""),e.push(t.systemPrompt.trimEnd()),e.push("")),e.push("## Commands"),e.push(""),f(m(t.workspace,"package.json"))?(e.push("```bash"),e.push("npm install # install dependencies"),e.push("npm test # run tests"),e.push("npm run build # build project"),e.push("```")):f(m(t.workspace,"requirements.txt"))&&(e.push("```bash"),e.push("pip install -r requirements.txt"),e.push("pytest"),e.push("```")),e.push(""),e.push("## Conventions"),e.push(""),r){case"coding":e.push("- Write tests for new code before committing"),e.push("- Keep functions small and focused"),e.push("- Follow existing patterns in the codebase"),e.push("- Run tests before pushing");break;case"pm":e.push("- Keep responses concise (3-5 lines main message)"),e.push("- Reference issues with #IID and MRs with !IID"),e.push("- Update issue labels and milestones when relevant"),e.push("- Summarize decisions, don't narrate process");break;case"devops":e.push("- Always check service status before making changes"),e.push("- Never run destructive commands without confirmation"),e.push("- Log all deployment actions"),e.push("- Verify changes in staging before production");break;case"qa":e.push("- Document reproduction steps for every bug"),e.push("- Include expected vs actual behavior"),e.push("- Check both happy path and edge cases"),e.push("- Attach screenshots when reporting UI issues");break;default:e.push("- Be concise \u2014 lead with the answer, skip preamble"),e.push("- Follow existing patterns in the codebase")}return e.push(""),e.push("## Cross-Channel Messaging"),e.push(""),e.push("You can send messages to any channel proactively:"),e.push("```bash"),e.push(`curl -X POST http://localhost:${s}/send \\`),e.push(' -H "Content-Type: application/json" \\'),e.push(` -d '{"channel":"telegram","chatId":"<id>","text":"<message>","agentId":"${n}"}'`),e.push("```"),e.push("Channels: telegram, whatsapp, gitlab, discord"),e.push(""),e.push("## Typed actions \u2014 prefer these over free-form Bash"),e.push(""),e.push("Daemon-shipped typed actions cover the common needs. Each call appears in `/traces` as a structured step (input + output schema), unlike opaque `Bash`/`curl`/`Read` calls."),e.push(""),e.push("```bash"),e.push("# List available actions + their schemas"),e.push("agentx actions builtin # \u2192 catalog"),e.push("agentx actions builtin <name> --schema # \u2192 input/output schema"),e.push(""),e.push("# Invoke an action with typed JSON input"),e.push(`agentx actions builtin http.fetch --input '{"url":"https://example.com/data"}'`),e.push(`agentx actions builtin http.post --input '{"url":"https://x.tld/hook","body":{"k":"v"}}'`),e.push(`agentx actions builtin file.read_lines --input '{"path":"data/prices.json"}'`),e.push(`agentx actions builtin file.write_jsonl --input '{"path":"leads.jsonl","record":{"name":"\u2026","email":"\u2026"}}'`),e.push(`agentx actions builtin extract.structured --input '{"prompt":"<text>","schema":{"type":"object",\u2026}}'`),e.push(`agentx actions builtin rag.lexical --input '{"agentId":"${n}","query":"\u2026","limit":5}'`),e.push(`agentx actions builtin agent.call --input '{"agentId":"<local-agent>","message":"\u2026"}'`),e.push(`agentx actions builtin mesh.delegate --input '{"peer":"<peer-name>","agent":"<peer-agent>","message":"\u2026"}'`),e.push("```"),e.push(""),e.push("Rule of thumb:"),e.push("- HTTP fetch/POST \u2192 `http.fetch` / `http.post` (not `Bash curl`)"),e.push("- Reading a file you'll parse \u2192 `file.read_lines` (not `Bash cat`)"),e.push("- Appending a structured record \u2192 `file.write_jsonl` (not `Bash echo >>` or `Write` with model-formatted JSON)"),e.push("- Extracting fields from text into a typed object \u2192 `extract.structured` (not asking the model to format JSON in its reply)"),e.push("- Searching a doc corpus \u2192 `rag.lexical` (not `Read` over every file)"),e.push("- Calling another agent on this daemon \u2192 `agent.call` (fresh session by default; not composing `Bash curl` to /task)"),e.push("- Calling an agent on a peer mesh node \u2192 `mesh.delegate` (cross-machine; same fresh-session default)"),e.push(""),t.codegraph&&e.push(C()),e.join(`
|
|
65
|
+
`)}var S=["Bash(npx prisma migrate reset*)","Bash(prisma migrate reset*)","Bash(npx prisma db push*)","Bash(prisma db push*)","Bash(dropdb*)"];function Se(n,t){return`curl -s --max-time 3 -H 'Content-Type: application/json' --data-binary @- '${`http://127.0.0.1:${t}/guard/check?agent=${encodeURIComponent(n)}`}' 2>/dev/null || true`}function q(n,t){let s=Se(n,t);return[{matcher:"Bash",hooks:[{type:"command",command:s,timeout:10}]},{matcher:"Write|Edit",hooks:[{type:"command",command:s,timeout:10}]}]}function Re(n){return Array.isArray(n?.hooks)&&n.hooks.some(t=>typeof t?.command=="string"&&(t.command.includes("/guard/check")||t.command.includes("guard check")))}function Oe(n,t,s="19900"){let r={autoMemoryEnabled:!0,env:{CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS:"1"}};if(t.permissionMode==="bypassPermissions"?r.permissions={deny:[...S]}:t.permissionMode==="plan"?r.permissions={deny:[...S,"Bash(rm -rf *)","Bash(drop *)","Bash(DELETE *)"]}:r.permissions={deny:[...S,"Bash(rm -rf /)","Bash(> /dev/sda*)","Bash(mkfs*)"]},t.codegraph){let o=r.permissions||{},a=Array.isArray(o.allow)?o.allow:[];o.allow=[...new Set([...a,...D])],r.permissions=o}let e={};return e.PreToolUse=q(n,s),e.Notification=[{matcher:"",hooks:[{type:"command",command:`echo "[${n}] notification: $(jq -r '.type // "unknown"')" >> /tmp/agentx-${n}.log`}]}],e.PostToolUse=[{matcher:"Bash",hooks:[{type:"command",command:`jq -r '.tool_input.command // empty' >> /tmp/agentx-${n}-commands.log`}]}],e.SessionStart=[{matcher:"compact",hooks:[{type:"command",command:`echo "Reminder: You are ${t.name} (${n}). Follow the conventions in CLAUDE.md."`}]}],r.hooks=e,r}var Ae=`# Code Quality
|
|
66
|
+
|
|
67
|
+
- No console.log in production code (use a logger)
|
|
68
|
+
- Handle errors explicitly \u2014 no empty catch blocks
|
|
69
|
+
- Keep files under 300 lines
|
|
70
|
+
- Extract repeated logic into shared utilities
|
|
71
|
+
`;function Pe(n,t){let s=J(n,t),r=[];switch(s){case"coding":r.push({name:"testing.md",content:`---
|
|
72
|
+
paths:
|
|
73
|
+
- "**/*.test.{ts,tsx,js,jsx}"
|
|
74
|
+
- "**/*.spec.{ts,tsx,js,jsx}"
|
|
75
|
+
- "test/**/*"
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
# Testing Rules
|
|
79
|
+
|
|
80
|
+
- Every new function should have a corresponding test
|
|
81
|
+
- Test both happy path and error cases
|
|
82
|
+
- Use descriptive test names that explain what's being tested
|
|
83
|
+
- Mock external dependencies, not internal modules
|
|
84
|
+
`}),r.push({name:"code-quality.md",content:`# Code Quality
|
|
85
|
+
|
|
86
|
+
- No console.log in production code (use a logger)
|
|
87
|
+
- Handle errors explicitly \u2014 no empty catch blocks
|
|
88
|
+
- Extract repeated logic into shared utilities
|
|
89
|
+
`,replaces:[Ae]});break;case"devops":r.push({name:"deployment-safety.md",content:`# Deployment Safety
|
|
90
|
+
|
|
91
|
+
- Always verify staging before production
|
|
92
|
+
- Check disk space and memory before deploying
|
|
93
|
+
- Never expose secrets in logs or commits
|
|
94
|
+
- Use rollback-safe deployment strategies
|
|
95
|
+
`});break;case"pm":r.push({name:"communication.md",content:`# Communication Rules
|
|
96
|
+
|
|
97
|
+
- Keep GitLab comments under 5 lines for the main message
|
|
98
|
+
- Use <details> for verbose output (logs, commands, steps)
|
|
99
|
+
- Reference issues with #IID, merge requests with !IID
|
|
100
|
+
- Never mention Telegram handles on GitLab
|
|
101
|
+
`});break;case"qa":r.push({name:"bug-reports.md",content:`# Bug Report Format
|
|
102
|
+
|
|
103
|
+
Always include:
|
|
104
|
+
1. Steps to reproduce
|
|
105
|
+
2. Expected behavior
|
|
106
|
+
3. Actual behavior
|
|
107
|
+
4. Environment (browser, OS, version)
|
|
108
|
+
5. Screenshots if applicable
|
|
109
|
+
`});break}return r}function $e(n,t,s="19900",r=console.error){let e=[],o=[],a=t.workspace;if(!f(a))return r(`Workspace not found: ${a}`),{created:e,skipped:o};if(t.tier!=="claude-code"&&t.tier!=="codex-cli")return{created:e,skipped:o};let i=(c,d)=>{f(c)?o.push(c):(H(m(c,".."),{recursive:!0}),k(c,d),e.push(c))},l=(c,d,h)=>{if(!f(c)){H(m(c,".."),{recursive:!0}),k(c,d),e.push(c);return}let g;try{g=w(c,"utf8")}catch{o.push(c);return}let x=we(g);if(x===null){o.push(c);return}if(x===h){o.push(c);return}k(c,d),e.push(`${c} (refreshed)`)};if(i(m(a,"AGENTS.md"),v({name:t.name,id:n,role:t.systemPrompt?.split(`
|
|
110
|
+
`)[0],workspace:a,tier:t.tier})),t.tier==="codex-cli")return e.length>0&&r(`[${n}] workspace setup: created ${e.length} file(s)`),{created:e,skipped:o};l(m(a,"CLAUDE.md"),_e(n,t,s),W(t));let u=m(a,".claude/settings.json");i(u,JSON.stringify(Oe(n,t,s),null,2));let p=Pe(n,t);for(let c of p){let d=m(a,".claude/rules",c.name);c.replaces&&f(d)&&c.replaces.includes(w(d,"utf8"))?(k(d,c.content),e.push(d)):i(d,c.content)}return e.length>0&&r(`[${n}] workspace setup: created ${e.length} file(s)`),{created:e,skipped:o}}function Te(n,t){let s=m(n,".claude/settings.json");if(!f(s))return!1;try{let r=JSON.parse(w(s,"utf-8")),e=!1;for(let[o,a]of Object.entries(t))if(typeof a=="object"&&a!==null&&!Array.isArray(a)){r[o]||(r[o]={});for(let[i,l]of Object.entries(a))r[o][i]===void 0&&(r[o][i]=l,e=!0)}else r[o]===void 0&&(r[o]=a,e=!0);return e&&k(s,JSON.stringify(r,null,2)),e}catch{return!1}}function ve(n,t,s="19900"){let r=m(n,".claude/settings.json");if(!f(r))return!1;try{let e=JSON.parse(w(r,"utf-8")),o=!1,a=e.permissions??={},i=Array.isArray(a.deny)?a.deny:a.deny=[];for(let c of S)i.includes(c)||(i.push(c),o=!0);let l=e.hooks??={},p=[...Array.isArray(l.PreToolUse)?l.PreToolUse.filter(c=>!Re(c)):[],...q(t,s)];return JSON.stringify(l.PreToolUse??null)!==JSON.stringify(p)&&(l.PreToolUse=p,o=!0),o&&k(r,JSON.stringify(e,null,2)),o}catch{return!1}}function Be(n){return Array.isArray(n?.hooks)&&n.hooks.some(t=>typeof t?.command=="string"&&t.command.includes(A))}function Ee(n,t,s,r){let e=m(n,".claude/settings.json");if(!f(e))return!1;try{let o=JSON.parse(w(e,"utf-8")),a=JSON.stringify(o),i=r.enabled&&r.tools.length>0,l=`Read(/${F(r.dir,t)}/**)`,u=Array.isArray(o.hooks?.PostToolUse)?o.hooks.PostToolUse:[],p=u.filter(h=>!Be(h)),c=Array.isArray(o.permissions?.allow)?o.permissions.allow:[],d=c.filter(h=>h!==l);if(i){let g=`curl -s --max-time 5 -H 'Content-Type: application/json' --data-binary @- '${`http://127.0.0.1:${s}${A}?agent=${encodeURIComponent(t)}`}' 2>/dev/null || true`;p.push({matcher:r.tools.join("|"),hooks:[{type:"command",command:g,timeout:10}]}),d.push(l)}return(i||u.length>0)&&((o.hooks??={}).PostToolUse=p),(i||c.length>0)&&((o.permissions??={}).allow=d),JSON.stringify(o)===a?!1:(k(e,JSON.stringify(o,null,2)),!0)}catch{return!1}}function ot(n,t="19900",s=console.error,r){let e=0,o=0,a=0,i=0,l=0,u=0;for(let[p,c]of Object.entries(n)){if(c.tier!=="claude-code"&&c.tier!=="codex-cli")continue;let d=$e(p,c,t,s);if(e+=d.created.length,c.tier==="claude-code"&&Te(c.workspace,{env:{CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS:"1"}})&&o++,c.tier==="claude-code"&&ve(c.workspace,p,t)&&a++,c.tier==="claude-code"&&r){let h=G(c.permissionMode);r.enabled&&!h&&u++;let g=r.enabled&&h;Ee(c.workspace,p,t,{...r,enabled:g})&&(g?i++:l++)}}e>0&&s(`Workspace setup: ${e} file(s) created across agent workspaces`),o>0&&s(`Workspace patch: ${o} workspace(s) updated with agent teams support`),a>0&&s(`Guardrails: ${a} workspace(s) backfilled with deny list + PreToolUse guard hook`),i>0&&s(`ObservationPack: PostToolUse hook written to ${i} workspace(s)`),l>0&&s(`ObservationPack: PostToolUse hook removed from ${l} workspace(s)`),u>0&&s(`ObservationPack: ${u} agent(s) not packed: permissionMode is not "bypassPermissions", so they could not read a saved original back`)}export{Ie as a,Ue as b,Ne as c,He as d,We as e,A as f,Ye as g,F as h,G as i,Ve as j,Qe as k,we as l,_e as m,$e as n,ve as o,Ee as p,ot as q};
|
|
111
|
+
//# sourceMappingURL=chunk-AV7T3SYT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/agents/workspace-setup.ts","../src/agents/bootstrap.ts","../src/agents/codegraph-bootstrap.ts","../src/agents/observation-pack.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync, readFileSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { createHash } from \"crypto\"\nimport type { AgentDef } from \"@/daemon/config\"\nimport { generateAgentsMd } from \"./bootstrap\"\nimport { CODEGRAPH_TOOLS, codegraphClaudeMdSection } from \"./codegraph-bootstrap\"\nimport { OBSERVATION_PACK_ROUTE, agentObservationDir, canReadOriginals } from \"./observation-pack\"\n\n// Marker stamped into auto-generated CLAUDE.md so we can tell agentx-managed\n// files apart from user-edited ones. On daemon start, files with the marker\n// are silently regenerated when the systemPrompt hash changes; files without\n// the marker are treated as user-owned and never touched.\nconst MANAGED_MARKER_PREFIX = \"<!-- agentx-managed: hash=\"\nconst MANAGED_MARKER_SUFFIX = \" -->\"\n\n/** Hash inputs that drive the managed-CLAUDE.md content. Anything that\n * affects what `generateClaudeMd` emits should feed into this so flipping\n * the input also refreshes the file on next boot. Currently:\n * systemPrompt + codegraph flag (the codegraph instruction block is\n * conditional on it). */\nfunction managedHashInputs(def: Pick<AgentDef, \"systemPrompt\" | \"codegraph\">): string {\n return createHash(\"sha256\")\n .update(def.systemPrompt ?? \"\")\n .update(\"\\0\")\n .update(def.codegraph ? \"cg:1\" : \"cg:0\")\n .digest(\"hex\")\n .slice(0, 16)\n}\n\nfunction buildManagedMarker(def: Pick<AgentDef, \"systemPrompt\" | \"codegraph\">): string {\n return `${MANAGED_MARKER_PREFIX}${managedHashInputs(def)}${MANAGED_MARKER_SUFFIX}`\n}\n\n/**\n * Read the managed marker from an existing file's first line, if present.\n * Returns the hash (truncated sha256 hex) when found, null otherwise.\n * Files without the marker are user-managed and untouchable.\n */\nexport function readManagedHash(content: string): string | null {\n const firstLine = content.split(\"\\n\", 1)[0] ?? \"\"\n if (!firstLine.startsWith(MANAGED_MARKER_PREFIX)) return null\n if (!firstLine.endsWith(MANAGED_MARKER_SUFFIX)) return null\n return firstLine.slice(MANAGED_MARKER_PREFIX.length, firstLine.length - MANAGED_MARKER_SUFFIX.length)\n}\n\n// --- Workspace Setup: align agent workspaces with Claude Code best practices ---\n//\n// Generates on daemon start (if missing):\n// CLAUDE.md — project conventions, under 200 lines, @AGENTS.md import\n// AGENTS.md — agents.md spec for cross-tool compatibility\n// .claude/settings.json — hooks, permissions, auto memory\n// .claude/rules/ — path-specific rules based on agent role\n\n/** Detect agent role from systemPrompt or agent ID */\nfunction detectRole(agentId: string, def: AgentDef): \"coding\" | \"pm\" | \"devops\" | \"qa\" | \"general\" {\n const text = `${agentId} ${def.systemPrompt || \"\"} ${def.name}`.toLowerCase()\n if (/cod(e|ing|er)|develop|engineer|program/.test(text)) return \"coding\"\n if (/pm|project.?manag|product|scrum/.test(text)) return \"pm\"\n if (/devops|deploy|infra|ops|sre|ci.?cd/.test(text)) return \"devops\"\n if (/qa|test|quality|forensic/.test(text)) return \"qa\"\n return \"general\"\n}\n\n/** Generate CLAUDE.md for an agent workspace */\nexport function generateClaudeMd(agentId: string, def: AgentDef, daemonPort: string): string {\n const role = detectRole(agentId, def)\n const lines: string[] = []\n\n // Managed-marker on line 1 — used by setupWorkspace to detect stale\n // auto-generated files and regenerate them when systemPrompt changes.\n // User-edited files (no marker) are left alone forever.\n lines.push(buildManagedMarker(def))\n lines.push(`# ${def.name}`)\n lines.push(\"\")\n lines.push(\"@AGENTS.md\")\n lines.push(\"\")\n\n // Agent identity from systemPrompt — emitted verbatim. Truncating here would\n // silently drop instructions (e.g. multi-paragraph protocols), so we trade\n // a few extra tokens in CLAUDE.md for fidelity. The runtime path already\n // forwards the full systemPrompt via --append-system-prompt; this section\n // mirrors it for tools that read CLAUDE.md as workspace context.\n if (def.systemPrompt && def.systemPrompt.trim().length > 0) {\n lines.push(\"## Role\")\n lines.push(\"\")\n lines.push(def.systemPrompt.trimEnd())\n lines.push(\"\")\n }\n\n // Build/test commands\n lines.push(\"## Commands\")\n lines.push(\"\")\n if (existsSync(resolve(def.workspace, \"package.json\"))) {\n lines.push(\"```bash\")\n lines.push(\"npm install # install dependencies\")\n lines.push(\"npm test # run tests\")\n lines.push(\"npm run build # build project\")\n lines.push(\"```\")\n } else if (existsSync(resolve(def.workspace, \"requirements.txt\"))) {\n lines.push(\"```bash\")\n lines.push(\"pip install -r requirements.txt\")\n lines.push(\"pytest\")\n lines.push(\"```\")\n }\n lines.push(\"\")\n\n // Role-specific conventions\n lines.push(\"## Conventions\")\n lines.push(\"\")\n switch (role) {\n case \"coding\":\n lines.push(\"- Write tests for new code before committing\")\n lines.push(\"- Keep functions small and focused\")\n lines.push(\"- Follow existing patterns in the codebase\")\n lines.push(\"- Run tests before pushing\")\n break\n case \"pm\":\n lines.push(\"- Keep responses concise (3-5 lines main message)\")\n lines.push(\"- Reference issues with #IID and MRs with !IID\")\n lines.push(\"- Update issue labels and milestones when relevant\")\n lines.push(\"- Summarize decisions, don't narrate process\")\n break\n case \"devops\":\n lines.push(\"- Always check service status before making changes\")\n lines.push(\"- Never run destructive commands without confirmation\")\n lines.push(\"- Log all deployment actions\")\n lines.push(\"- Verify changes in staging before production\")\n break\n case \"qa\":\n lines.push(\"- Document reproduction steps for every bug\")\n lines.push(\"- Include expected vs actual behavior\")\n lines.push(\"- Check both happy path and edge cases\")\n lines.push(\"- Attach screenshots when reporting UI issues\")\n break\n default:\n lines.push(\"- Be concise — lead with the answer, skip preamble\")\n lines.push(\"- Follow existing patterns in the codebase\")\n }\n lines.push(\"\")\n\n // Cross-channel messaging capability\n lines.push(\"## Cross-Channel Messaging\")\n lines.push(\"\")\n lines.push(\"You can send messages to any channel proactively:\")\n lines.push(\"```bash\")\n lines.push(`curl -X POST http://localhost:${daemonPort}/send \\\\`)\n lines.push(` -H \"Content-Type: application/json\" \\\\`)\n lines.push(` -d '{\"channel\":\"telegram\",\"chatId\":\"<id>\",\"text\":\"<message>\",\"agentId\":\"${agentId}\"}'`)\n lines.push(\"```\")\n lines.push('Channels: telegram, whatsapp, gitlab, discord')\n lines.push(\"\")\n\n // Typed actions catalog — prefer these over free-form Bash/Read/Write/curl\n // when applicable. Each action's call lands in /traces as a structured\n // step (action: <name>, input: …, output: …) which is how observability\n // works in this codebase. Free-form Bash gets recorded as opaque shell\n // text, which is harder to debug and harder to compose into workflows.\n lines.push(\"## Typed actions — prefer these over free-form Bash\")\n lines.push(\"\")\n lines.push(\"Daemon-shipped typed actions cover the common needs. Each call appears in `/traces` as a structured step (input + output schema), unlike opaque `Bash`/`curl`/`Read` calls.\")\n lines.push(\"\")\n lines.push(\"```bash\")\n lines.push(\"# List available actions + their schemas\")\n lines.push(`agentx actions builtin # → catalog`)\n lines.push(`agentx actions builtin <name> --schema # → input/output schema`)\n lines.push(\"\")\n lines.push(\"# Invoke an action with typed JSON input\")\n lines.push(`agentx actions builtin http.fetch --input '{\"url\":\"https://example.com/data\"}'`)\n lines.push(`agentx actions builtin http.post --input '{\"url\":\"https://x.tld/hook\",\"body\":{\"k\":\"v\"}}'`)\n lines.push(`agentx actions builtin file.read_lines --input '{\"path\":\"data/prices.json\"}'`)\n lines.push(`agentx actions builtin file.write_jsonl --input '{\"path\":\"leads.jsonl\",\"record\":{\"name\":\"…\",\"email\":\"…\"}}'`)\n lines.push(`agentx actions builtin extract.structured --input '{\"prompt\":\"<text>\",\"schema\":{\"type\":\"object\",…}}'`)\n lines.push(`agentx actions builtin rag.lexical --input '{\"agentId\":\"${agentId}\",\"query\":\"…\",\"limit\":5}'`)\n lines.push(`agentx actions builtin agent.call --input '{\"agentId\":\"<local-agent>\",\"message\":\"…\"}'`)\n lines.push(`agentx actions builtin mesh.delegate --input '{\"peer\":\"<peer-name>\",\"agent\":\"<peer-agent>\",\"message\":\"…\"}'`)\n lines.push(\"```\")\n lines.push(\"\")\n lines.push(\"Rule of thumb:\")\n lines.push(\"- HTTP fetch/POST → `http.fetch` / `http.post` (not `Bash curl`)\")\n lines.push(\"- Reading a file you'll parse → `file.read_lines` (not `Bash cat`)\")\n lines.push(\"- Appending a structured record → `file.write_jsonl` (not `Bash echo >>` or `Write` with model-formatted JSON)\")\n lines.push(\"- Extracting fields from text into a typed object → `extract.structured` (not asking the model to format JSON in its reply)\")\n lines.push(\"- Searching a doc corpus → `rag.lexical` (not `Read` over every file)\")\n lines.push(\"- Calling another agent on this daemon → `agent.call` (fresh session by default; not composing `Bash curl` to /task)\")\n lines.push(\"- Calling an agent on a peer mesh node → `mesh.delegate` (cross-machine; same fresh-session default)\")\n lines.push(\"\")\n\n // CodeGraph instruction block — only when the agent opted in. See\n // src/agents/codegraph-bootstrap.ts for the section content; sourcing it\n // from one place keeps the wording consistent if upstream changes.\n if (def.codegraph) {\n lines.push(codegraphClaudeMdSection())\n }\n\n return lines.join(\"\\n\")\n}\n\n// Phase 0 stop-gap deny list for the destructive-action guardrails. Claude\n// Code deny globs are coarse (single trailing wildcard), so this is a blunt\n// backstop for the clearly-always-destructive prisma/dropdb classes that\n// caused the 2026-07-21 prod-wipe incident. The env-var-resolved, nuanced\n// matching (diff --shadow-database-url pointed at prod, pg_restore --clean,\n// migrate deploy) is done by the `agentx guard` PreToolUse hook, which runs\n// even under bypassPermissions (where deny rules may be skipped).\nconst GUARDRAIL_DENY = [\n \"Bash(npx prisma migrate reset*)\",\n \"Bash(prisma migrate reset*)\",\n \"Bash(npx prisma db push*)\",\n \"Bash(prisma db push*)\",\n \"Bash(dropdb*)\",\n]\n\n/**\n * The command the PreToolUse hook runs. This fires on EVERY Bash/Write/Edit\n * call, so it must be cheap: spawning `node dist/cli.js guard check` cost\n * ~300ms of interpreter + bundle boot per tool call. Instead we POST the\n * payload to the already-running daemon over loopback (~1ms) and pipe its\n * response straight back to Claude Code. `/guard/check` is loopback-only.\n *\n * The trailing `|| true` is load-bearing: a PreToolUse hook exiting 2 BLOCKS\n * the tool call, and curl uses exit 2 for its own init failures. Forcing\n * exit 0 means a curl/daemon problem can never masquerade as a policy deny.\n * If the daemon is unreachable we fail open — acceptable because the daemon\n * is what spawns the agents in the first place, so \"daemon down\" means there\n * are no agent tool calls to guard.\n */\nfunction guardHookCommand(agentId: string, daemonPort: string): string {\n const url = `http://127.0.0.1:${daemonPort}/guard/check?agent=${encodeURIComponent(agentId)}`\n return `curl -s --max-time 3 -H 'Content-Type: application/json' --data-binary @- '${url}' 2>/dev/null || true`\n}\n\n/** PreToolUse hook entries wiring the guard onto Bash + Write/Edit. */\nfunction guardPreToolUseHooks(agentId: string, daemonPort: string): unknown[] {\n const command = guardHookCommand(agentId, daemonPort)\n return [\n { matcher: \"Bash\", hooks: [{ type: \"command\", command, timeout: 10 }] },\n { matcher: \"Write|Edit\", hooks: [{ type: \"command\", command, timeout: 10 }] },\n ]\n}\n\n/** Is this hook entry one of ours? Matches both the current curl form and\n * the earlier `guard check` CLI form, so stale entries get replaced rather\n * than duplicated. */\nfunction isGuardEntry(entry: any): boolean {\n return (\n Array.isArray(entry?.hooks) &&\n entry.hooks.some(\n (h: any) =>\n typeof h?.command === \"string\" && (h.command.includes(\"/guard/check\") || h.command.includes(\"guard check\")),\n )\n )\n}\n\n/** Generate .claude/settings.json with hooks and permissions */\nfunction generateSettings(agentId: string, def: AgentDef, daemonPort: string = \"19900\"): Record<string, unknown> {\n const settings: Record<string, unknown> = {\n autoMemoryEnabled: true,\n env: {\n CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: \"1\",\n },\n }\n\n // Permission mode. The guardrail deny list is injected in ALL modes —\n // including bypassPermissions, which previously had none — as a stop-gap\n // backstop beneath the PreToolUse guard hook.\n if (def.permissionMode === \"bypassPermissions\") {\n settings[\"permissions\"] = { deny: [...GUARDRAIL_DENY] }\n } else if (def.permissionMode === \"plan\") {\n settings[\"permissions\"] = {\n deny: [...GUARDRAIL_DENY, \"Bash(rm -rf *)\", \"Bash(drop *)\", \"Bash(DELETE *)\"],\n }\n } else {\n // Default: deny destructive operations\n settings[\"permissions\"] = {\n deny: [\n ...GUARDRAIL_DENY,\n \"Bash(rm -rf /)\",\n \"Bash(> /dev/sda*)\",\n \"Bash(mkfs*)\",\n ],\n }\n }\n\n // Pre-authorize codegraph MCP tools so claude doesn't prompt for each\n // call. Even under bypassPermissions there's no harm — an explicit allow\n // list survives if the agent later switches to a stricter mode.\n if (def.codegraph) {\n const perms = (settings[\"permissions\"] as Record<string, unknown>) || {}\n const existing = Array.isArray(perms[\"allow\"]) ? (perms[\"allow\"] as string[]) : []\n perms[\"allow\"] = [...new Set([...existing, ...CODEGRAPH_TOOLS])]\n settings[\"permissions\"] = perms\n }\n\n // Hooks\n const hooks: Record<string, unknown[]> = {}\n\n // PreToolUse — destructive-action guardrails. Runs the guard on every Bash\n // and Write/Edit call; in warn mode it audits without blocking. This is the\n // primary enforcement chokepoint (works even under bypassPermissions).\n hooks[\"PreToolUse\"] = guardPreToolUseHooks(agentId, daemonPort)\n\n // Notification hook — log when agent needs input\n hooks[\"Notification\"] = [{\n matcher: \"\",\n hooks: [{\n type: \"command\",\n command: `echo \"[${agentId}] notification: $(jq -r '.type // \\\"unknown\\\"')\" >> /tmp/agentx-${agentId}.log`,\n }],\n }]\n\n // PostToolUse — log tool activity\n hooks[\"PostToolUse\"] = [{\n matcher: \"Bash\",\n hooks: [{\n type: \"command\",\n command: `jq -r '.tool_input.command // empty' >> /tmp/agentx-${agentId}-commands.log`,\n }],\n }]\n\n // SessionStart after compaction — re-inject key context\n hooks[\"SessionStart\"] = [{\n matcher: \"compact\",\n hooks: [{\n type: \"command\",\n command: `echo \"Reminder: You are ${def.name} (${agentId}). Follow the conventions in CLAUDE.md.\"`,\n }],\n }]\n\n settings[\"hooks\"] = hooks\n return settings\n}\n\n/** The code-quality rule as written before #455. Its \"Keep files under 300\n * lines\" made a coding agent open a task by counting lines (`wc -l`)\n * instead of reading the files, one extra model call: in a 10-run test on\n * a six-file bug hunt, 10 of 10 first calls counted lines with it and 2 of\n * 10 without it, and median tokens fell from 160k to 124k. */\nconst CODE_QUALITY_RULE_BEFORE_455 = `# Code Quality\n\n- No console.log in production code (use a logger)\n- Handle errors explicitly — no empty catch blocks\n- Keep files under 300 lines\n- Extract repeated logic into shared utilities\n`\n\ninterface RuleFile {\n name: string\n content: string\n /** Earlier generated versions. A workspace file still identical to one\n * of them was never edited by hand, so it is brought up to date. */\n replaces?: string[]\n}\n\n/** Generate .claude/rules/ files based on agent role */\nfunction generateRules(agentId: string, def: AgentDef): RuleFile[] {\n const role = detectRole(agentId, def)\n const rules: RuleFile[] = []\n\n switch (role) {\n case \"coding\":\n rules.push({\n name: \"testing.md\",\n content: `---\npaths:\n - \"**/*.test.{ts,tsx,js,jsx}\"\n - \"**/*.spec.{ts,tsx,js,jsx}\"\n - \"test/**/*\"\n---\n\n# Testing Rules\n\n- Every new function should have a corresponding test\n- Test both happy path and error cases\n- Use descriptive test names that explain what's being tested\n- Mock external dependencies, not internal modules\n`,\n })\n rules.push({\n name: \"code-quality.md\",\n content: `# Code Quality\n\n- No console.log in production code (use a logger)\n- Handle errors explicitly — no empty catch blocks\n- Extract repeated logic into shared utilities\n`,\n replaces: [CODE_QUALITY_RULE_BEFORE_455],\n })\n break\n\n case \"devops\":\n rules.push({\n name: \"deployment-safety.md\",\n content: `# Deployment Safety\n\n- Always verify staging before production\n- Check disk space and memory before deploying\n- Never expose secrets in logs or commits\n- Use rollback-safe deployment strategies\n`,\n })\n break\n\n case \"pm\":\n rules.push({\n name: \"communication.md\",\n content: `# Communication Rules\n\n- Keep GitLab comments under 5 lines for the main message\n- Use <details> for verbose output (logs, commands, steps)\n- Reference issues with #IID, merge requests with !IID\n- Never mention Telegram handles on GitLab\n`,\n })\n break\n\n case \"qa\":\n rules.push({\n name: \"bug-reports.md\",\n content: `# Bug Report Format\n\nAlways include:\n1. Steps to reproduce\n2. Expected behavior\n3. Actual behavior\n4. Environment (browser, OS, version)\n5. Screenshots if applicable\n`,\n })\n break\n }\n\n return rules\n}\n\n/**\n * Set up an agent workspace with CLI-agent best practices.\n * Only creates files that don't already exist (non-destructive).\n */\nexport function setupWorkspace(\n agentId: string,\n def: AgentDef,\n daemonPort: string = \"19900\",\n log: (...args: unknown[]) => void = console.error,\n): { created: string[]; skipped: string[] } {\n const created: string[] = []\n const skipped: string[] = []\n const workspace = def.workspace\n\n if (!existsSync(workspace)) {\n log(`Workspace not found: ${workspace}`)\n return { created, skipped }\n }\n\n // Only set up CLI-backed tier agents.\n if (def.tier !== \"claude-code\" && def.tier !== \"codex-cli\") {\n return { created, skipped }\n }\n\n const writeIfMissing = (path: string, content: string) => {\n if (existsSync(path)) {\n skipped.push(path)\n } else {\n mkdirSync(resolve(path, \"..\"), { recursive: true })\n writeFileSync(path, content)\n created.push(path)\n }\n }\n\n /**\n * Write a managed file with the agentx-managed marker. If the file already\n * exists and starts with our marker, regenerate when the embedded hash\n * doesn't match the current expected hash. Files without the marker are\n * treated as user-owned and left alone.\n */\n const writeManaged = (path: string, content: string, expectedHash: string) => {\n if (!existsSync(path)) {\n mkdirSync(resolve(path, \"..\"), { recursive: true })\n writeFileSync(path, content)\n created.push(path)\n return\n }\n let existing: string\n try {\n existing = readFileSync(path, \"utf8\")\n } catch {\n skipped.push(path)\n return\n }\n const existingHash = readManagedHash(existing)\n if (existingHash === null) {\n // User-edited (no marker) — never overwrite.\n skipped.push(path)\n return\n }\n if (existingHash === expectedHash) {\n skipped.push(path)\n return\n }\n writeFileSync(path, content)\n created.push(`${path} (refreshed)`)\n }\n\n // AGENTS.md (agents.md spec) — first-line of systemPrompt only, currently\n // not managed-marker'd. Left as writeIfMissing for now.\n writeIfMissing(\n resolve(workspace, \"AGENTS.md\"),\n generateAgentsMd({ name: def.name, id: agentId, role: def.systemPrompt?.split(\"\\n\")[0], workspace, tier: def.tier }),\n )\n\n if (def.tier === \"codex-cli\") {\n if (created.length > 0) {\n log(`[${agentId}] workspace setup: created ${created.length} file(s)`)\n }\n return { created, skipped }\n }\n\n // CLAUDE.md — managed: silently refreshed when systemPrompt OR codegraph\n // flag changes (both feed into the marker hash via managedHashInputs).\n writeManaged(\n resolve(workspace, \"CLAUDE.md\"),\n generateClaudeMd(agentId, def, daemonPort),\n managedHashInputs(def),\n )\n\n // .claude/settings.json\n const settingsPath = resolve(workspace, \".claude/settings.json\")\n writeIfMissing(settingsPath, JSON.stringify(generateSettings(agentId, def, daemonPort), null, 2))\n\n // .claude/rules/\n const rules = generateRules(agentId, def)\n for (const rule of rules) {\n const path = resolve(workspace, \".claude/rules\", rule.name)\n if (rule.replaces && existsSync(path) && rule.replaces.includes(readFileSync(path, \"utf8\"))) {\n writeFileSync(path, rule.content)\n created.push(path)\n } else {\n writeIfMissing(path, rule.content)\n }\n }\n\n if (created.length > 0) {\n log(`[${agentId}] workspace setup: created ${created.length} file(s)`)\n }\n\n return { created, skipped }\n}\n\n/**\n * Patch existing .claude/settings.json to add missing keys (non-destructive).\n * Used to enable new features (like agent teams) on workspaces that already have settings.\n */\nfunction patchSettings(workspace: string, patches: Record<string, unknown>): boolean {\n const settingsPath = resolve(workspace, \".claude/settings.json\")\n if (!existsSync(settingsPath)) return false\n\n try {\n const existing = JSON.parse(readFileSync(settingsPath, \"utf-8\"))\n let changed = false\n\n for (const [key, value] of Object.entries(patches)) {\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n // Merge objects (e.g. env: { KEY: \"value\" })\n if (!existing[key]) existing[key] = {}\n for (const [subKey, subValue] of Object.entries(value as Record<string, unknown>)) {\n if (existing[key][subKey] === undefined) {\n existing[key][subKey] = subValue\n changed = true\n }\n }\n } else if (existing[key] === undefined) {\n existing[key] = value\n changed = true\n }\n }\n\n if (changed) {\n writeFileSync(settingsPath, JSON.stringify(existing, null, 2))\n }\n return changed\n } catch {\n return false\n }\n}\n\n/**\n * Backfill the destructive-action guardrails into an EXISTING settings.json:\n * merge the stop-gap deny list into permissions.deny (dedup, no clobber) and\n * add the PreToolUse guard hook if absent. writeIfMissing never touches\n * existing files, so live workspaces need this explicit patch. Idempotent —\n * re-running is a no-op once both are present. Returns true if it changed.\n */\nexport function patchGuardrails(workspace: string, agentId: string, daemonPort: string = \"19900\"): boolean {\n const settingsPath = resolve(workspace, \".claude/settings.json\")\n if (!existsSync(settingsPath)) return false\n\n try {\n const existing = JSON.parse(readFileSync(settingsPath, \"utf-8\"))\n let changed = false\n\n // 1. deny list\n const perms = (existing.permissions ??= {})\n const deny: string[] = Array.isArray(perms.deny) ? perms.deny : (perms.deny = [])\n for (const rule of GUARDRAIL_DENY) {\n if (!deny.includes(rule)) {\n deny.push(rule)\n changed = true\n }\n }\n\n // 2. PreToolUse guard hook. Drop any previous guard entries (including\n // the older node-CLI form) and re-add the current one, so a changed\n // port or invocation style self-heals instead of duplicating.\n const hooks = (existing.hooks ??= {})\n const others = Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse.filter((e: any) => !isGuardEntry(e)) : []\n const desired = [...others, ...guardPreToolUseHooks(agentId, daemonPort)]\n if (JSON.stringify(hooks.PreToolUse ?? null) !== JSON.stringify(desired)) {\n hooks.PreToolUse = desired\n changed = true\n }\n\n if (changed) writeFileSync(settingsPath, JSON.stringify(existing, null, 2))\n return changed\n } catch {\n return false\n }\n}\n\n/** What `patchObservationPack` needs from `session.observationPack`. */\nexport interface ObservationPackHook {\n enabled: boolean\n tools: string[]\n /** The store of saved originals; the agent is allowed to read its own\n * folder in it and no other agent's. */\n dir: string\n}\n\nfunction isObservationPackEntry(entry: any): boolean {\n return (\n Array.isArray(entry?.hooks) &&\n entry.hooks.some((h: any) => typeof h?.command === \"string\" && h.command.includes(OBSERVATION_PACK_ROUTE))\n )\n}\n\n/**\n * Add the ObservationPack PostToolUse hook (#621) to an existing\n * settings.json when the pack is on, and take it out when it is off. The\n * hook pipes the tool result to the daemon's loopback route and hands the\n * answer back to Claude Code; with the daemon down it prints nothing and the\n * result is left as it was. Also lets the agent Read its own saved originals.\n * Idempotent. Returns true if it changed the file.\n */\nexport function patchObservationPack(\n workspace: string,\n agentId: string,\n daemonPort: string,\n pack: ObservationPackHook,\n): boolean {\n const settingsPath = resolve(workspace, \".claude/settings.json\")\n if (!existsSync(settingsPath)) return false\n\n try {\n const existing = JSON.parse(readFileSync(settingsPath, \"utf-8\"))\n const before = JSON.stringify(existing)\n\n const on = pack.enabled && pack.tools.length > 0\n const readRule = `Read(/${agentObservationDir(pack.dir, agentId)}/**)`\n const posted: unknown[] = Array.isArray(existing.hooks?.PostToolUse) ? existing.hooks.PostToolUse : []\n const others = posted.filter((e) => !isObservationPackEntry(e))\n const allowed: string[] = Array.isArray(existing.permissions?.allow) ? existing.permissions.allow : []\n const allow = allowed.filter((r) => r !== readRule)\n\n if (on) {\n const url = `http://127.0.0.1:${daemonPort}${OBSERVATION_PACK_ROUTE}?agent=${encodeURIComponent(agentId)}`\n const command = `curl -s --max-time 5 -H 'Content-Type: application/json' --data-binary @- '${url}' 2>/dev/null || true`\n others.push({ matcher: pack.tools.join(\"|\"), hooks: [{ type: \"command\", command, timeout: 10 }] })\n allow.push(readRule)\n }\n\n // Only touch the keys the pack owns, and only when it has something there.\n if (on || posted.length > 0) (existing.hooks ??= {}).PostToolUse = others\n if (on || allowed.length > 0) (existing.permissions ??= {}).allow = allow\n\n if (JSON.stringify(existing) === before) return false\n writeFileSync(settingsPath, JSON.stringify(existing, null, 2))\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Set up all agent workspaces on daemon start.\n */\nexport function setupAllWorkspaces(\n agents: Record<string, AgentDef>,\n daemonPort: string = \"19900\",\n log: (...args: unknown[]) => void = console.error,\n observationPack?: ObservationPackHook,\n): void {\n let totalCreated = 0\n let totalPatched = 0\n let totalGuarded = 0\n let packWritten = 0\n let packRemoved = 0\n let packSkipped = 0\n for (const [id, def] of Object.entries(agents)) {\n if (def.tier !== \"claude-code\" && def.tier !== \"codex-cli\") continue\n const result = setupWorkspace(id, def, daemonPort, log)\n totalCreated += result.created.length\n\n // Patch existing settings with new features (agent teams, etc.)\n if (def.tier === \"claude-code\" && patchSettings(def.workspace, {\n env: { CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: \"1\" },\n })) {\n totalPatched++\n }\n\n // Backfill destructive-action guardrails (deny list + PreToolUse hook)\n // onto existing workspaces.\n if (def.tier === \"claude-code\" && patchGuardrails(def.workspace, id, daemonPort)) {\n totalGuarded++\n }\n\n if (def.tier === \"claude-code\" && observationPack) {\n // An agent that cannot read an original back is not packed, and loses\n // the hook if an earlier start wrote it.\n const readable = canReadOriginals(def.permissionMode)\n if (observationPack.enabled && !readable) packSkipped++\n const on = observationPack.enabled && readable\n if (patchObservationPack(def.workspace, id, daemonPort, { ...observationPack, enabled: on })) {\n if (on) packWritten++\n else packRemoved++\n }\n }\n }\n if (totalCreated > 0) {\n log(`Workspace setup: ${totalCreated} file(s) created across agent workspaces`)\n }\n if (totalPatched > 0) {\n log(`Workspace patch: ${totalPatched} workspace(s) updated with agent teams support`)\n }\n if (totalGuarded > 0) {\n log(`Guardrails: ${totalGuarded} workspace(s) backfilled with deny list + PreToolUse guard hook`)\n }\n if (packWritten > 0) {\n log(`ObservationPack: PostToolUse hook written to ${packWritten} workspace(s)`)\n }\n if (packRemoved > 0) {\n log(`ObservationPack: PostToolUse hook removed from ${packRemoved} workspace(s)`)\n }\n if (packSkipped > 0) {\n log(`ObservationPack: ${packSkipped} agent(s) not packed: permissionMode is not \"bypassPermissions\", so they could not read a saved original back`)\n }\n}\n","import { readFileSync, existsSync, unlinkSync, readdirSync } from \"fs\"\nimport { resolve } from \"path\"\n\n// --- Bootstrap Identity Files ---\n//\n// Support for structured workspace identity files (inspired by OpenClaw).\n// These files live in the agent's workspace and are auto-loaded into context.\n//\n// Supported files:\n// SOUL.md — default personality, tone, boundaries\n// SOUL.{profile}.md — named soul profiles (e.g. SOUL.finance.md, SOUL.legal.md)\n// IDENTITY.md — name, role, emoji, tagline (persistent)\n// USER.md — user profile, preferences (persistent)\n// AGENTS.md — operating rules, standing orders (persistent)\n// BOOTSTRAP.md — one-time first-run ritual (auto-deleted after first load)\n//\n// Soul switching: send \"/soul finance\" to swap to SOUL.finance.md mid-session.\n\nexport interface BootstrapFiles {\n soul?: string\n /** Active soul profile name (undefined = default SOUL.md) */\n soulProfile?: string\n identity?: string\n user?: string\n agents?: string\n bootstrap?: string\n}\n\nconst BOOTSTRAP_FILE_NAMES = [\n \"SOUL.md\",\n \"IDENTITY.md\",\n \"USER.md\",\n \"AGENTS.md\",\n \"BOOTSTRAP.md\",\n] as const\n\n/**\n * Load bootstrap identity files from an agent's workspace.\n * @param soulProfile — optional soul profile name (e.g. \"finance\" loads SOUL.finance.md)\n */\nexport function loadBootstrapFiles(workspace: string, soulProfile?: string): BootstrapFiles {\n const result: BootstrapFiles = {}\n\n const tryLoad = (filename: string): string | undefined => {\n const filePath = resolve(workspace, filename)\n if (!existsSync(filePath)) return undefined\n try {\n const content = readFileSync(filePath, \"utf-8\").trim()\n return content || undefined\n } catch {\n return undefined\n }\n }\n\n // Load soul: profile-specific if requested, fallback to default\n if (soulProfile) {\n const profileSoul = tryLoad(`SOUL.${soulProfile}.md`)\n if (profileSoul) {\n result.soul = profileSoul\n result.soulProfile = soulProfile\n } else {\n // Profile not found — fall back to default and note it\n result.soul = tryLoad(\"SOUL.md\")\n }\n } else {\n result.soul = tryLoad(\"SOUL.md\")\n }\n\n result.identity = tryLoad(\"IDENTITY.md\")\n result.user = tryLoad(\"USER.md\")\n result.agents = tryLoad(\"AGENTS.md\")\n result.bootstrap = tryLoad(\"BOOTSTRAP.md\")\n\n // BOOTSTRAP.md is one-time: delete after loading\n if (result.bootstrap) {\n try {\n unlinkSync(resolve(workspace, \"BOOTSTRAP.md\"))\n } catch {\n // Best-effort deletion\n }\n }\n\n return result\n}\n\n/**\n * Build context string from bootstrap files.\n * Returns empty string if no files found.\n */\nexport function buildBootstrapContext(files: BootstrapFiles): string {\n const sections: string[] = []\n\n if (files.identity) {\n sections.push(`[Identity]\\n${files.identity}`)\n }\n\n if (files.soul) {\n sections.push(`[Personality & Boundaries]\\n${files.soul}`)\n }\n\n // AGENTS.md is intentionally NOT injected into AgentX's --append-system-prompt\n // anymore. It's loaded natively by Claude Code via `@AGENTS.md` in the\n // workspace's CLAUDE.md (generated by workspace-setup.ts for new workspaces;\n // added to existing workspaces by the 2026-04-24 distribution). Injecting it\n // here on top of the native CLAUDE.md path would double the bytes. Keeping\n // the load in BootstrapFiles for diagnostic/listing purposes only.\n\n if (files.user) {\n sections.push(`[User Profile]\\n${files.user}`)\n }\n\n if (files.bootstrap) {\n sections.push(`[First-Run Instructions — one time only]\\n${files.bootstrap}`)\n }\n\n if (sections.length === 0) return \"\"\n\n return sections.join(\"\\n\\n\")\n}\n\n/**\n * Check which bootstrap files exist in a workspace (for status/diagnostics).\n */\nexport function listBootstrapFiles(workspace: string): string[] {\n return BOOTSTRAP_FILE_NAMES.filter((name) =>\n existsSync(resolve(workspace, name))\n )\n}\n\n/**\n * List available soul profiles in a workspace.\n * Returns profile names (e.g. [\"finance\", \"legal\", \"creative\"]).\n */\nexport function listSoulProfiles(workspace: string): string[] {\n try {\n return readdirSync(workspace)\n .filter(f => f.startsWith(\"SOUL.\") && f.endsWith(\".md\") && f !== \"SOUL.md\")\n .map(f => f.slice(5, -3)) // \"SOUL.finance.md\" → \"finance\"\n } catch {\n return []\n }\n}\n\n/**\n * Generate a standard AGENTS.md for a new agent workspace.\n * Follows the agents.md community convention (https://agents.md/).\n * Readable by Claude Code, Codex, Cursor, Amp, Gemini CLI, etc.\n */\nexport function generateAgentsMd(agent: {\n name: string\n id: string\n role?: string\n workspace: string\n tier?: string\n}): string {\n return `# ${agent.name}\n\n> Agent ID: \\`${agent.id}\\` | Tier: ${agent.tier || \"claude-code\"}\n\n## Role\n\n${agent.role || \"AI agent managed by AgentX orchestrator.\"}\n\n## Setup\n\nThis workspace is managed by AgentX. The agent runs via:\n\\`\\`\\`bash\n# Direct execution\nagentx daemon send ${agent.id} \"your task here\"\n\n# Or via any connected channel (Telegram, WhatsApp, GitLab)\n\\`\\`\\`\n\n## Code Style\n\n- Follow existing patterns in the codebase\n- Use the language and framework conventions already present\n- Keep changes minimal and focused\n\n## Testing\n\nRun tests before committing:\n\\`\\`\\`bash\nnpm test # or the project's test command\n\\`\\`\\`\n\n## PR Guidelines\n\n- One logical change per commit\n- Reference issue numbers when applicable\n- Keep PR descriptions concise\n\n## AgentX Integration\n\nThis agent is part of an AgentX mesh. It can:\n- Receive tasks from messaging channels (Telegram, WhatsApp, Discord)\n- Respond to GitLab @mentions on issues and MRs\n- Delegate to other agents by mentioning their handles\n- Send cross-channel messages via the /send API\n- Access its wiki knowledge base for context\n`\n}\n\n/**\n * Detect a /soul command in a message.\n * Returns the requested profile name, or null if no /soul command found.\n * \"/soul finance\" → \"finance\"\n * \"/soul\" or \"/soul default\" → \"default\" (reset to SOUL.md)\n */\nexport function detectSoulSwitch(message: string): string | null {\n const match = message.match(/\\/soul\\s+(\\w+)/i)\n if (match) return match[1].toLowerCase()\n if (/\\/soul\\s*$/i.test(message)) return \"default\"\n return null\n}\n","import { execFile } from \"child_process\"\nimport { existsSync } from \"fs\"\nimport { resolve } from \"path\"\nimport type { AgentDef } from \"@/daemon/config\"\nimport type { McpServerMap } from \"./agent-mcp\"\n\n// --- CodeGraph integration: per-agent semantic code-graph index ---\n//\n// CodeGraph (https://github.com/colbymchenry/codegraph) is a local\n// tree-sitter → SQLite knowledge graph exposed to Claude Code / Codex via\n// MCP. Opt-in per agent with `codegraph: true` on the agent's config.\n//\n// This module owns three responsibilities:\n// 1. `effectiveMcpConfig(def)` — synthesize the codegraph MCP server\n// entry into the agent's MCP map. Used by the daemon's\n// installAgentMcpConfig pass so the workspace's .mcp.json picks\n// up `codegraph serve --mcp` automatically.\n// 2. `bootstrapCodegraphIndexes(agents, log)` — at daemon boot, fire\n// off `codegraph init --index --quiet` in the background for any\n// opted-in workspace whose `.codegraph/` is missing. Throttled to\n// 2 parallel jobs so a large fleet doesn't thrash the host.\n// Returns immediately; the indexes finish on their own.\n// 3. Export the constant tool/CLI names that workspace-setup needs\n// to keep CLAUDE.md and settings.json in sync.\n\n/** MCP tool names exposed by `codegraph serve --mcp`. Used to extend the\n * per-workspace `.claude/settings.json` permissions.allow so claude\n * doesn't prompt for permission on every call. Keep in sync with the\n * upstream tool surface — additions are harmless, removals leave a\n * stale allow entry (also harmless). */\nexport const CODEGRAPH_TOOLS = [\n \"mcp__codegraph__codegraph_search\",\n \"mcp__codegraph__codegraph_context\",\n \"mcp__codegraph__codegraph_callers\",\n \"mcp__codegraph__codegraph_callees\",\n \"mcp__codegraph__codegraph_impact\",\n \"mcp__codegraph__codegraph_node\",\n \"mcp__codegraph__codegraph_status\",\n \"mcp__codegraph__codegraph_files\",\n] as const\n\n/** MCP server stanza we inject into `.mcp.json` for opted-in agents. */\nexport const CODEGRAPH_MCP_SERVER: { command: string; args: string[] } = {\n command: \"codegraph\",\n args: [\"serve\", \"--mcp\"],\n}\n\n/** Markdown block appended to managed CLAUDE.md when codegraph is on.\n * Tells the agent to prefer codegraph_* over grep, and to always spawn\n * an Explore subagent for codegraph_explore so the large source-dump\n * doesn't bloat the main session context. Mirrors the upstream-\n * recommended wording from the README's \"Global Instructions\" block. */\nexport function codegraphClaudeMdSection(): string {\n return [\n \"## CodeGraph\",\n \"\",\n \"This workspace has CodeGraph initialized — a semantic code-knowledge-graph indexed via tree-sitter. Use it instead of grep/find/Read for exploration.\",\n \"\",\n \"**ALWAYS spawn an Explore subagent for `codegraph_explore` / `codegraph_context`** — those tools return large source sections that would otherwise bloat the main session. Include this in the Explore prompt:\",\n \"\",\n \"> Use `codegraph_explore` as your PRIMARY tool — one call returns full source from all relevant files. Do not re-Read files codegraph_explore already returned. Fall back to grep/glob/Read only if codegraph returned no results.\",\n \"\",\n \"**In the main session, use these lightweight codegraph tools directly:**\",\n \"\",\n \"| Tool | Use for |\",\n \"|---|---|\",\n \"| `codegraph_search` | Find symbols by name across the codebase |\",\n \"| `codegraph_callers` / `codegraph_callees` | Trace call flow |\",\n \"| `codegraph_impact` | Check what's affected before editing a symbol |\",\n \"| `codegraph_node` | Get a single symbol's details |\",\n \"| `codegraph_files` | Indexed file structure (faster than ls) |\",\n \"| `codegraph_status` | Index health |\",\n \"\",\n ].join(\"\\n\")\n}\n\n/** Return the MCP server map an agent should expose to .mcp.json,\n * layering codegraph on top of the operator-declared `def.mcp` when\n * `def.codegraph === true`. Operator-declared entries win on key\n * collision (operator can override by declaring their own\n * `mcp.codegraph` block with different args). */\nexport function effectiveMcpConfig(def: AgentDef): McpServerMap {\n const base: McpServerMap = ((def as any).mcp ?? {}) as McpServerMap\n if (!def.codegraph) return base\n if (base.codegraph) return base\n return { ...base, codegraph: { command: CODEGRAPH_MCP_SERVER.command, args: [...CODEGRAPH_MCP_SERVER.args] } }\n}\n\nconst INDEX_DIR = \".codegraph\"\nconst INIT_TIMEOUT_MS = 10 * 60 * 1000 // 10 min — Swift Compiler benchmark indexed 25k files in ~4 min\nconst MAX_PARALLEL = 2\n\n/** Spawn `codegraph init --index --quiet` for one workspace. Returns the\n * duration in ms on success, throws on failure. Caller decides whether\n * to surface the error. */\nfunction indexWorkspace(workspace: string): Promise<number> {\n const started = Date.now()\n return new Promise<number>((res, rej) => {\n execFile(\n \"codegraph\",\n [\"init\", \"--index\", \"--quiet\", workspace],\n { timeout: INIT_TIMEOUT_MS, maxBuffer: 4 * 1024 * 1024 },\n (err) => {\n if (err) rej(err)\n else res(Date.now() - started)\n },\n )\n })\n}\n\n/** Boot-time helper: kick off `codegraph init --index` for every\n * opted-in workspace whose `.codegraph/` is missing. Fire-and-forget —\n * the returned promise resolves once all background jobs settle, but\n * callers (daemon start) don't need to await it. Indexing happens\n * while the daemon is already serving traffic; cold dispatches before\n * the index lands gracefully fall through to grep (the MCP server\n * returns an empty result, and CLAUDE.md tells the agent to fall back).\n *\n * Throttled to MAX_PARALLEL concurrent jobs so a fleet boot doesn't\n * saturate CPU/disk. */\nexport async function bootstrapCodegraphIndexes(\n agents: Record<string, AgentDef>,\n log: (...args: unknown[]) => void = console.error,\n): Promise<void> {\n const pending: Array<{ agentId: string; workspace: string }> = []\n for (const [agentId, def] of Object.entries(agents)) {\n if (!def.codegraph) continue\n if (def.tier !== \"claude-code\" && def.tier !== \"codex-cli\") continue\n if (!existsSync(def.workspace)) continue\n if (existsSync(resolve(def.workspace, INDEX_DIR))) continue\n pending.push({ agentId, workspace: def.workspace })\n }\n\n if (pending.length === 0) return\n\n log(`codegraph: indexing ${pending.length} workspace(s) in background (max ${MAX_PARALLEL} parallel)`)\n\n let i = 0\n const runners: Promise<void>[] = []\n for (let slot = 0; slot < Math.min(MAX_PARALLEL, pending.length); slot++) {\n runners.push((async () => {\n while (true) {\n const idx = i++\n if (idx >= pending.length) return\n const { agentId, workspace } = pending[idx]\n try {\n const ms = await indexWorkspace(workspace)\n log(` codegraph: indexed ${agentId} in ${(ms / 1000).toFixed(1)}s`)\n } catch (err: any) {\n // codegraph binary missing surfaces as ENOENT — recoverable hint\n const hint = err?.code === \"ENOENT\"\n ? \" (codegraph CLI not on PATH; install with `npm i -g @colbymchenry/codegraph`)\"\n : \"\"\n log(` codegraph: ${agentId} index failed: ${err?.message ?? err}${hint}`)\n }\n }\n })())\n }\n await Promise.allSettled(runners)\n}\n","// ObservationPack (#621): keep large tool results out of the conversation.\n//\n// A tool result stays in a session's context and is re-read on every later\n// request. Most of a 40 KB build log is never looked at again. The pack\n// answers Claude Code's PostToolUse hook: a text result over the size limit\n// is written to disk in full and the model is given its first and last bytes\n// plus the path of the file, which it can Read or grep for the exact original.\n//\n// Claude Code does the same on its own for Bash output over 30,000 characters\n// (a 2 KB preview and a saved file); those results are left to it. The pack\n// covers what is between its limit and that one, and the tools Claude Code\n// does not preview.\n//\n// A file read (the Read tool) is packed too, since the owner's decision of\n// 2026-10-05 on #621. The file is its own original, so nothing is copied:\n// the model keeps the first and last lines, with the file's path and the\n// line numbers of what was left out, and reads those again with offset and\n// limit when it needs them.\n//\n// The hook replaces the result before the model sees it for the first time.\n// The paper this follows (arXiv 2609.20519) shows the full result for two\n// requests first; a hook cannot do that, because it runs once per tool call.\n//\n// Nothing here calls a model, and a result is never dropped: if the original\n// cannot be written, the result is left as it was.\n\nimport { appendFileSync, existsSync, mkdirSync, readdirSync, realpathSync, statSync, unlinkSync, utimesSync, writeFileSync } from \"fs\"\nimport { createHash } from \"crypto\"\nimport { resolve, sep } from \"path\"\n\nexport interface ObservationPackConfig {\n enabled: boolean\n /** A text result larger than this many bytes is packed. */\n limitBytes: number\n /** Bytes of the start of the original shown to the model. */\n headBytes: number\n /** Bytes of the end of the original shown to the model. */\n tailBytes: number\n /** Tool names the pack applies to. Each entry is a regular expression that\n * must match the whole name, as in a Claude Code hook matcher. */\n tools: string[]\n /** Days an original is kept on disk. 0 keeps every original. */\n retentionDays: number\n}\n\n/** The part of Claude Code's PostToolUse payload the pack reads. */\nexport interface PostToolUsePayload {\n session_id?: string\n tool_name?: string\n tool_input?: Record<string, unknown>\n tool_response?: unknown\n}\n\nexport interface PackRecord {\n sha: string\n path: string\n bytes: number\n keptBytes: number\n}\n\n/** Path marker that tells one of our hook entries from anybody else's. */\nexport const OBSERVATION_PACK_ROUTE = \"/observation/pack\"\n\n/** The store: one folder per agent under it. */\nexport function observationDir(root: string = process.cwd()): string {\n return resolve(root, \".agentx/observations\")\n}\n\n/**\n * One agent's folder in the store. An agent is allowed to read its own\n * folder only: an original holds whatever its command printed. The id is\n * encoded so that no id can name a folder outside the store.\n */\nexport function agentObservationDir(storeDir: string, agentId: string): string {\n return resolve(storeDir, encodeURIComponent(agentId).replace(/\\./g, \"%2E\"))\n}\n\n/**\n * Whether an agent can read a saved original back. Seen live on Claude Code\n * 2.1.289: outside `bypassPermissions` the Read of an original is refused in\n * a session nobody answers for, the allow rule notwithstanding, and the model\n * then guesses at the part it was not shown. Such an agent is not packed.\n */\nexport function canReadOriginals(permissionMode: string | undefined): boolean {\n return permissionMode === \"bypassPermissions\"\n}\n\n/** The same path with symlinks followed, or as given when it does not exist. */\nfunction realPath(path: string): string {\n try {\n return realpathSync(path)\n } catch {\n return resolve(path)\n }\n}\n\nexport function toolIsPacked(toolName: string, tools: string[]): boolean {\n return tools.some((pattern) => {\n try {\n return new RegExp(`^(?:${pattern})$`).test(toolName)\n } catch {\n return pattern === toolName\n }\n })\n}\n\n/** Average line size above which the excerpt points at byte ranges, not lines. */\nconst LONG_LINE_BYTES = 2000\n\n/** Cut on a byte count without leaving half a character at the cut. */\nfunction decodeSlice(buf: Buffer, start: number, end: number): string {\n return buf.subarray(start, end).toString(\"utf-8\").replace(/^�+|�+$/g, \"\")\n}\n\n/** The text the model gets in place of `text`. */\nexport function buildExcerpt(text: string, path: string, config: Pick<ObservationPackConfig, \"headBytes\" | \"tailBytes\">): string {\n const buf = Buffer.from(text, \"utf-8\")\n const head = decodeSlice(buf, 0, config.headBytes)\n const tail = config.tailBytes > 0 ? decodeSlice(buf, buf.length - config.tailBytes, buf.length) : \"\"\n const withheld = buf.length - config.headBytes - config.tailBytes\n const lines = text.split(\"\\n\").length\n // A line read or a grep match on very long lines (minified JSON) returns\n // as much as the original, which is then packed again.\n const longLines = buf.length / lines > LONG_LINE_BYTES\n const how = longLines\n ? `Its lines are very long, so read it by byte range (for example head -c or tail -c, or jq for JSON) for the part you need.`\n : `Read that file with offset and limit (line numbers), or grep it, for the part you need. Do not cat it whole: that is packed again.`\n return [\n `[ObservationPack: this result is ${buf.length} bytes (${lines} lines). Only its first ${config.headBytes} and last ${config.tailBytes} bytes are shown.`,\n `The exact original is saved at ${path}`,\n `${how} Do not guess at what is not shown.]`,\n head,\n `[... ${withheld} bytes not shown ...]`,\n tail,\n ].join(\"\\n\")\n}\n\n/** The text shape of a Read result in Claude Code's PostToolUse payload. */\ninterface ReadTextResult {\n type: \"text\"\n file: { filePath?: string; content: string; numLines?: number; startLine?: number; totalLines?: number }\n}\n\nfunction isReadTextResult(value: unknown): value is ReadTextResult {\n const obj = value as Partial<ReadTextResult> | null\n return !!obj && typeof obj === \"object\" && obj.type === \"text\" && typeof obj.file?.content === \"string\"\n}\n\n/** How many lines `text` spans, counting a final unterminated line. */\nfunction lineCount(text: string): number {\n if (text === \"\") return 0\n let n = 1\n for (let i = text.indexOf(\"\\n\"); i !== -1; i = text.indexOf(\"\\n\", i + 1)) n++\n if (text.endsWith(\"\\n\")) n--\n return n\n}\n\n/**\n * The text the model gets in place of a file read. The file itself is the\n * original, so nothing is copied: the excerpt keeps whole lines from both\n * ends and names the file and the exact line numbers of what is not shown,\n * so the model asks for the part it needs with offset and limit.\n *\n * Claude Code prints its line numbers beside the content it is given, from\n * `startLine` on. The head comes first and ends on a line break, so its\n * numbers are the file's; the notice and the tail that follow are numbered\n * on from there, so the notice states the tail's real first line.\n */\nexport function buildReadExcerpt(\n content: string,\n file: { path: string; startLine: number; totalLines?: number },\n config: Pick<ObservationPackConfig, \"headBytes\" | \"tailBytes\">,\n): string {\n const buf = Buffer.from(content, \"utf-8\")\n const lines = lineCount(content)\n\n // Whole lines within the budget. A line longer than the budget is cut,\n // and then counts as not shown, since part of it is not.\n let head = decodeSlice(buf, 0, config.headBytes)\n const headBreak = head.lastIndexOf(\"\\n\")\n head = headBreak === -1 ? head : head.slice(0, headBreak + 1)\n let tail = config.tailBytes > 0 ? decodeSlice(buf, buf.length - config.tailBytes, buf.length) : \"\"\n const tailBreak = tail.indexOf(\"\\n\")\n tail = tailBreak === -1 ? tail : tail.slice(tailBreak + 1)\n\n const headLines = headBreak === -1 ? 0 : lineCount(head)\n const tailLines = tailBreak === -1 ? 0 : lineCount(tail)\n const first = file.startLine\n const last = first + lines - 1\n const hiddenFrom = first + headLines\n const hiddenTo = last - tailLines\n const withheld = buf.length - Buffer.byteLength(head, \"utf-8\") - Buffer.byteLength(tail, \"utf-8\")\n const whole = typeof file.totalLines === \"number\" && file.totalLines > lines ? ` (${file.totalLines} lines in all)` : \"\"\n const hidden = hiddenFrom === hiddenTo ? `Line ${hiddenFrom} (${withheld} bytes) is` : `Lines ${hiddenFrom} to ${hiddenTo} (${withheld} bytes) are`\n const cut = [headLines === 0 && head !== \"\" ? first : null, tailLines === 0 && tail !== \"\" ? last : null].filter((n) => n !== null)\n const partly = cut.length === 0 ? \"\" : cut.length === 1 || cut[0] === cut[1] ? ` (line ${cut[0]} only in part)` : ` (lines ${cut[0]} and ${cut[1]} only in part)`\n const tailNote = tailLines > 0 ? ` Lines ${hiddenTo + 1} to ${last} follow; the numbers beside them are not the file's.` : \"\"\n\n return [\n head,\n `[ObservationPack: this read is ${buf.length} bytes, lines ${first} to ${last} of ${file.path}${whole}.`,\n `${hidden} not shown${partly}.${tailNote}`,\n `Read the file again with offset and limit for the lines you need, or Grep it. Do not guess at what is not shown.]`,\n tail,\n ]\n .filter((part) => part !== \"\")\n .join(\"\\n\")\n}\n\n/** Write the original where the model can read it back. Same text, same file. */\nfunction storeOriginal(dir: string, text: string): { sha: string; path: string } {\n const sha = createHash(\"sha256\").update(text).digest(\"hex\")\n const path = resolve(dir, `${sha}.txt`)\n if (existsSync(path)) {\n // Seen again: keep it clear of the retention sweep.\n const now = new Date()\n utimesSync(path, now, now)\n } else {\n mkdirSync(dir, { recursive: true, mode: 0o700 })\n writeFileSync(path, text, { mode: 0o600 })\n }\n return { sha, path }\n}\n\n/**\n * Pack one tool result. Returns the replacement for `tool_response`, in the\n * tool's own shape, and one record per packed text; or null when nothing is\n * to change (pack off, tool not listed, nothing over the limit, a picture,\n * sound or other base64 item, a result Claude Code already saved to a file,\n * or a read of an original we saved ourselves). For a file read the record's\n * `path` is the file itself.\n */\nexport function packToolOutput(\n payload: PostToolUsePayload,\n config: ObservationPackConfig,\n dir: string,\n): { updated: unknown; packs: PackRecord[] } | null {\n if (!config.enabled) return null\n const tool = payload.tool_name\n if (!tool || !toolIsPacked(tool, config.tools)) return null\n\n // Reading a saved original back must return it whole, or it could never be\n // retrieved.\n const filePath = payload.tool_input?.[\"file_path\"]\n if (typeof filePath === \"string\" && realPath(filePath).startsWith(realPath(dir) + sep)) return null\n\n // An excerpt has to be smaller than what it replaces.\n if (config.limitBytes <= config.headBytes + config.tailBytes) return null\n\n // A file read: the file is the original, so none is copied. Only the text\n // shape is cut; a notebook, a PDF or a picture is left whole.\n if (tool === \"Read\") {\n const response = payload.tool_response\n if (!isReadTextResult(response)) return null\n const content = response.file.content\n const bytes = Buffer.byteLength(content, \"utf-8\")\n if (bytes <= config.limitBytes) return null\n const path = typeof filePath === \"string\" ? filePath : (response.file.filePath ?? \"the file you read\")\n const offset = payload.tool_input?.[\"offset\"]\n const startLine =\n typeof response.file.startLine === \"number\" && response.file.startLine >= 1\n ? response.file.startLine\n : typeof offset === \"number\" && offset >= 1\n ? offset\n : 1\n const excerpt = buildReadExcerpt(content, { path, startLine, totalLines: response.file.totalLines }, config)\n const sha = createHash(\"sha256\").update(content).digest(\"hex\")\n return {\n updated: { ...response, file: { ...response.file, content: excerpt } },\n packs: [{ sha, path, bytes, keptBytes: Buffer.byteLength(excerpt, \"utf-8\") }],\n }\n }\n\n const packs: PackRecord[] = []\n const walk = (value: unknown): unknown => {\n if (typeof value === \"string\") {\n const bytes = Buffer.byteLength(value, \"utf-8\")\n if (bytes <= config.limitBytes) return value\n const { sha, path } = storeOriginal(dir, value)\n const excerpt = buildExcerpt(value, path, config)\n packs.push({ sha, path, bytes, keptBytes: Buffer.byteLength(excerpt, \"utf-8\") })\n return excerpt\n }\n if (Array.isArray(value)) return value.map(walk)\n if (value && typeof value === \"object\") {\n const obj = value as Record<string, unknown>\n // Pictures and sound are not text; base64 cut in the middle is noise.\n if (obj[\"type\"] === \"image\" || obj[\"type\"] === \"audio\" || obj[\"isImage\"] === true) return value\n // Claude Code already saved this one to a file and shows a preview\n // (Bash output over 30,000 characters). What we get here is cut short,\n // so it is not the original; leave its own handle in place.\n if (typeof obj[\"persistedOutputPath\"] === \"string\") return value\n // `blob` is the base64 body of an MCP resource; its `text` twin is packed.\n return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, k === \"blob\" ? v : walk(v)]))\n }\n return value\n }\n\n try {\n const updated = walk(payload.tool_response)\n return packs.length > 0 ? { updated, packs } : null\n } catch {\n // Could not save an original: the model keeps the full result.\n return null\n }\n}\n\n/**\n * Answer a PostToolUse hook call. Returns the JSON Claude Code expects on the\n * hook's stdout, or \"\" to leave the result alone. `dir` is the calling\n * agent's own folder. Each pack is appended to `index.jsonl` there, so what\n * was withheld can be counted later.\n */\nexport function answerPackHook(\n payload: PostToolUsePayload,\n config: ObservationPackConfig,\n opts: { dir: string; agentId?: string },\n): string {\n // A body that is not an object (JSON `null`) is no tool result.\n const result = payload && typeof payload === \"object\" ? packToolOutput(payload, config, opts.dir) : null\n if (!result) return \"\"\n try {\n const at = new Date().toISOString()\n const rows = result.packs.map((p) =>\n JSON.stringify({ at, agent: opts.agentId, session: payload.session_id, tool: payload.tool_name, sha: p.sha, bytes: p.bytes, keptBytes: p.keptBytes }),\n )\n // A packed file read copies nothing, so the folder may not exist yet.\n mkdirSync(opts.dir, { recursive: true, mode: 0o700 })\n appendFileSync(resolve(opts.dir, \"index.jsonl\"), rows.join(\"\\n\") + \"\\n\", { mode: 0o600 })\n } catch {\n // The index is a count, not the evidence. The original is already saved.\n }\n return JSON.stringify({\n hookSpecificOutput: { hookEventName: \"PostToolUse\", updatedToolOutput: result.updated },\n })\n}\n\n/**\n * Delete originals not written or seen again for `retentionDays`, in every\n * agent's folder of the store. Returns how many.\n */\nexport function pruneObservations(storeDir: string, retentionDays: number, now: number = Date.now()): number {\n if (retentionDays <= 0 || !existsSync(storeDir)) return 0\n const cutoff = now - retentionDays * 86_400_000\n let removed = 0\n for (const agent of readdirSync(storeDir, { withFileTypes: true })) {\n if (!agent.isDirectory()) continue\n const dir = resolve(storeDir, agent.name)\n for (const name of readdirSync(dir)) {\n if (!name.endsWith(\".txt\")) continue\n const path = resolve(dir, name)\n try {\n if (statSync(path).mtimeMs < cutoff) {\n unlinkSync(path)\n removed++\n }\n } catch {\n // Gone or unreadable: nothing to prune.\n }\n }\n }\n return removed\n}\n"],"mappings":"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","names":["existsSync","mkdirSync","writeFileSync","readFileSync","resolve","createHash","readFileSync","existsSync","unlinkSync","readdirSync","resolve","loadBootstrapFiles","workspace","soulProfile","result","tryLoad","filename","filePath","resolve","existsSync","readFileSync","profileSoul","unlinkSync","buildBootstrapContext","files","sections","generateAgentsMd","agent","detectSoulSwitch","message","match","execFile","existsSync","resolve","CODEGRAPH_TOOLS","CODEGRAPH_MCP_SERVER","codegraphClaudeMdSection","effectiveMcpConfig","def","base","INDEX_DIR","INIT_TIMEOUT_MS","MAX_PARALLEL","indexWorkspace","workspace","started","res","rej","err","bootstrapCodegraphIndexes","agents","log","pending","agentId","i","runners","slot","idx","ms","hint","appendFileSync","existsSync","mkdirSync","readdirSync","realpathSync","statSync","unlinkSync","utimesSync","writeFileSync","createHash","resolve","sep","OBSERVATION_PACK_ROUTE","observationDir","root","agentObservationDir","storeDir","agentId","canReadOriginals","permissionMode","realPath","path","toolIsPacked","toolName","tools","pattern","LONG_LINE_BYTES","decodeSlice","buf","start","end","buildExcerpt","text","config","head","tail","withheld","lines","how","isReadTextResult","value","obj","lineCount","n","i","buildReadExcerpt","content","file","headBreak","tailBreak","headLines","tailLines","first","last","hiddenFrom","hiddenTo","whole","hidden","cut","partly","tailNote","part","storeOriginal","dir","sha","now","packToolOutput","payload","tool","filePath","response","bytes","offset","startLine","excerpt","packs","walk","k","v","updated","answerPackHook","opts","result","at","rows","p","pruneObservations","retentionDays","cutoff","removed","agent","name","MANAGED_MARKER_PREFIX","MANAGED_MARKER_SUFFIX","managedHashInputs","def","createHash","buildManagedMarker","readManagedHash","content","firstLine","detectRole","agentId","text","generateClaudeMd","daemonPort","role","lines","existsSync","resolve","codegraphClaudeMdSection","GUARDRAIL_DENY","guardHookCommand","guardPreToolUseHooks","command","isGuardEntry","entry","h","generateSettings","settings","perms","existing","CODEGRAPH_TOOLS","hooks","CODE_QUALITY_RULE_BEFORE_455","generateRules","rules","setupWorkspace","log","created","skipped","workspace","writeIfMissing","path","mkdirSync","writeFileSync","writeManaged","expectedHash","readFileSync","existingHash","generateAgentsMd","settingsPath","rule","patchSettings","patches","changed","key","value","subKey","subValue","patchGuardrails","deny","desired","e","isObservationPackEntry","OBSERVATION_PACK_ROUTE","patchObservationPack","pack","before","on","readRule","agentObservationDir","posted","others","allowed","allow","r","setupAllWorkspaces","agents","observationPack","totalCreated","totalPatched","totalGuarded","packWritten","packRemoved","packSkipped","id","result","readable","canReadOriginals"]}
|